diff --git a/.github/ISSUE_TEMPLATE/bug_template.md b/.github/ISSUE_TEMPLATE/bug_template.md index d197ca1312..5a6275b394 100644 --- a/.github/ISSUE_TEMPLATE/bug_template.md +++ b/.github/ISSUE_TEMPLATE/bug_template.md @@ -7,20 +7,30 @@ labels: 'needs-triage,needs-sig,kind/bug' --- **Describe the bug** -A clear and concise description of what the bug is. +A clear and concise description of what the bug is. Try to isolate the issue to help the community to reproduce it easily and increase chances for a fast fix. -**To Reproduce** +**Steps to reproduce** Steps to reproduce the behavior: 1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +2. Click on '...' +3. Select attached asset '...' +4. Scroll down to '...' +5. See error **Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. +**Actual behavior** +A clear and concise description of what actually happened. + +**Assets required** +Provide sample assets needed to reproduce the issue. + +**Screenshots/Video** +If applicable, add screenshots and/or a video to help explain your problem. + +**Found in Branch** +Name of or link to the branch where the issue occurs. **Desktop/Device (please complete the following information):** - Device: [e.g. PC, Mac, iPhone, Samsung] diff --git a/Assets/Editor/MapScreenshotSettings.xml b/Assets/Editor/MapScreenshotSettings.xml index 01f90db4b3..88a8a49aef 100644 --- a/Assets/Editor/MapScreenshotSettings.xml +++ b/Assets/Editor/MapScreenshotSettings.xml @@ -1,18 +1,5 @@ - - - - - - - - - - - - - diff --git a/Assets/Editor/Materials/Stripes.tif.exportsettings b/Assets/Editor/Materials/Stripes.tif.exportsettings deleted file mode 100644 index 0653bb85eb..0000000000 --- a/Assets/Editor/Materials/Stripes.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Diffuse_lowQ \ No newline at end of file diff --git a/Assets/Editor/Materials/voxel_editor.png.exportsettings b/Assets/Editor/Materials/voxel_editor.png.exportsettings deleted file mode 100644 index d19ae00148..0000000000 --- a/Assets/Editor/Materials/voxel_editor.png.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /mipmaps=0 /preset=AlbedoWithGenericAlpha /reduce=-1 \ No newline at end of file diff --git a/Assets/Editor/UserTools.xml b/Assets/Editor/UserTools.xml index 8f971cbd5c..4cc197afbe 100644 --- a/Assets/Editor/UserTools.xml +++ b/Assets/Editor/UserTools.xml @@ -7,8 +7,6 @@ - - diff --git a/Assets/Engine/Config/AutoTestChain.cfg b/Assets/Engine/Config/AutoTestChain.cfg index 8c6fae9020..4164acb271 100644 --- a/Assets/Engine/Config/AutoTestChain.cfg +++ b/Assets/Engine/Config/AutoTestChain.cfg @@ -1,16 +1,5 @@ ConsoleHide -g_godMode=1 sys_warnings=0 con_showonload=1 -i_forcefeedback=0 -g_infiniteammo=1 -e_ObjectLayersActivation=0 -e_ObjectLayersActivationPhysics=0 -g_flashrenderingduringloading=0 sys_maxfps=0 r_vsync=0 -p_max_substeps=1 -demo_file=autotest -demo_num_runs=0 -demo_quit=1 -demo_ai=1 diff --git a/Assets/Engine/Config/AutoTestTimeDemo.cfg b/Assets/Engine/Config/AutoTestTimeDemo.cfg index 687158eda9..664ea94939 100644 --- a/Assets/Engine/Config/AutoTestTimeDemo.cfg +++ b/Assets/Engine/Config/AutoTestTimeDemo.cfg @@ -1,15 +1,5 @@ ConsoleHide -g_godMode=1 -g_infiniteammo=1 r_displayinfo=1 s_profiling=1 sys_maxfps=0 -e_ObjectLayersActivation=0 -e_ObjectLayersActivationPhysics=0 - -demo_file=autotest -demo_num_runs=2 -demo_quit=1 -demo_savestats=1 -demo_profile=-1 demo diff --git a/Assets/Engine/Config/AutotestPlaythrough.cfg b/Assets/Engine/Config/AutotestPlaythrough.cfg index f453097972..664ea94939 100644 --- a/Assets/Engine/Config/AutotestPlaythrough.cfg +++ b/Assets/Engine/Config/AutotestPlaythrough.cfg @@ -1,12 +1,5 @@ ConsoleHide -g_godMode=1 -g_infiniteammo=1 r_displayinfo=1 s_profiling=1 sys_maxfps=0 -demo_file=playthru -demo_num_runs=2 -demo_quit=1 -demo_savestats=1 -demo_profile=-1 -demo \ No newline at end of file +demo diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg index 626dce3ee4..04fb56f3ca 100644 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg +++ b/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg @@ -11,113 +11,16 @@ ; default of this CVarGroup = 7 -sys_spec_ObjectDetail=7 -sys_spec_Shading=7 -sys_spec_VolumetricEffects=7 -sys_spec_Shadows=7 -sys_spec_Texture=7 -sys_spec_Physics=7 -sys_spec_PostProcessing=7 -sys_spec_Particles=7 -sys_spec_Sound=7 -sys_spec_Water=7 -sys_spec_GameEffects=7 -sys_spec_light=7 - [1] -sys_spec_ObjectDetail=1 -sys_spec_Shading=1 -sys_spec_VolumetricEffects=1 -sys_spec_Shadows=1 -sys_spec_Texture=1 -sys_spec_Physics=1 -sys_spec_PostProcessing=1 -sys_spec_Particles=1 -sys_spec_Sound=1 -sys_spec_Water=1 -sys_spec_GameEffects=1 -sys_spec_light=1 [2] -sys_spec_ObjectDetail=2 -sys_spec_Shading=2 -sys_spec_VolumetricEffects=2 -sys_spec_Shadows=2 -sys_spec_Texture=2 -sys_spec_Physics=2 -sys_spec_PostProcessing=2 -sys_spec_Particles=2 -sys_spec_Sound=2 -sys_spec_Water=2 -sys_spec_GameEffects=2 -sys_spec_light=2 [3] -sys_spec_ObjectDetail=3 -sys_spec_Shading=3 -sys_spec_VolumetricEffects=3 -sys_spec_Shadows=3 -sys_spec_Texture=3 -sys_spec_Physics=3 -sys_spec_PostProcessing=3 -sys_spec_Particles=3 -sys_spec_Sound=3 -sys_spec_Water=3 -sys_spec_GameEffects=3 -sys_spec_light=3 [4] -sys_spec_ObjectDetail=4 -sys_spec_Shading=4 -sys_spec_VolumetricEffects=4 -sys_spec_Shadows=4 -sys_spec_Texture=4 -sys_spec_Physics=4 -sys_spec_PostProcessing=4 -sys_spec_Particles=4 -sys_spec_Sound=4 -sys_spec_Water=4 -sys_spec_GameEffects=4 -sys_spec_light=4 [5] -sys_spec_ObjectDetail=5 -sys_spec_Shading=5 -sys_spec_VolumetricEffects=5 -sys_spec_Shadows=5 -sys_spec_Texture=5 -sys_spec_Physics=5 -sys_spec_PostProcessing=5 -sys_spec_Particles=5 -sys_spec_Sound=5 -sys_spec_Water=5 -sys_spec_GameEffects=5 -sys_spec_light=5 [6] -sys_spec_ObjectDetail=6 -sys_spec_Shading=6 -sys_spec_VolumetricEffects=6 -sys_spec_Shadows=6 -sys_spec_Texture=6 -sys_spec_Physics=6 -sys_spec_PostProcessing=6 -sys_spec_Particles=6 -sys_spec_Sound=6 -sys_spec_Water=6 -sys_spec_GameEffects=6 -sys_spec_light=6 [8] -sys_spec_ObjectDetail=8 -sys_spec_Shading=8 -sys_spec_VolumetricEffects=8 -sys_spec_Shadows=8 -sys_spec_Texture=8 -sys_spec_Physics=8 -sys_spec_PostProcessing=8 -sys_spec_Particles=8 -sys_spec_Sound=8 -sys_spec_Water=8 -sys_spec_GameEffects=8 -sys_spec_light=8 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg deleted file mode 100644 index 34e058d47d..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -mfx_Timeout = 0.01 - - - diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg deleted file mode 100644 index a2996f7ded..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg +++ /dev/null @@ -1,160 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -ca_AttachmentCullingRation=360 -es_DebrisLifetimeScale=1 -e_CoverageBufferReproj=6 -e_DecalsAllowGameDecals=1 -e_DecalsLifeTimeScale=2 -e_DecalsOverlapping=1 -e_Dissolve=2 -e_LightQuality=3 -e_LodMin=0 -e_LodRatio=20 -e_MaxViewDistSpecLerp=1 -e_MergedMeshesInstanceDist=1.0 -e_MergedMeshesPool=8192 -e_ObjQuality=3 -e_OcclusionCullingViewDistRatio=1 -e_ProcVegetation=1 -e_StatObjBufferRenderTasks=1 -e_StreamCgf=0 -e_TerrainLodRatio=1 -e_TerrainOcclusionCullingMaxDist=200 -e_Tessellation=0 -e_VegetationMinSize=0 -e_ViewDistMin=10 -e_ViewDistRatio=100 -e_ViewDistRatioCustom=100 -e_ViewDistRatioDetail=100 -e_ViewDistRatioLights=50 -e_ViewDistRatioVegetation=100 -r_DrawNearZRange=0.12 -r_FlaresTessellationRatio=1 -r_SilhouettePOM=0 -r_usezpass=2 - -[1] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=1 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=1 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=21 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=0 -e_ViewDistRatio=50 - -[2] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=2 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=2 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=1 -e_ViewDistRatio=50 - -[3] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=3 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=3 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=1 -e_ViewDistRatio=75 - -[4] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=4 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=4 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=1 -e_ViewDistRatio=75 - -[5] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_LightQuality=1 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=1 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatio=50 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 - -[6] -ca_AttachmentCullingRation=300 -es_DebrisLifetimeScale=0.8 -e_LightQuality=2 -e_LodRatio=15 -e_ObjQuality=2 -e_ViewDistRatio=75 -e_ViewDistRatioDetail=35 -e_ViewDistRatioVegetation=75 - -[8] -ca_AttachmentCullingRation=400 -e_LightQuality=4 -e_LodRatio=40 -e_MergedMeshesInstanceDist=2.0 -e_MergedMeshesPool=16384 -e_ObjQuality=4 -e_TerrainLodRatio=0.5 -e_Tessellation=1 -e_ViewDistRatio=125 -e_ViewDistRatioCustom=125 -e_ViewDistRatioDetail=125 -e_ViewDistRatioLights=75 -e_ViewDistRatioVegetation=125 -r_DrawNearZRange = 0.08 -r_SilhouettePOM=1 \ No newline at end of file diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg deleted file mode 100644 index 0a02170b97..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg +++ /dev/null @@ -1,94 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_ParticlesGI=1 -e_ParticlesPreload=0 -e_ParticlesMaxScreenFill=128 -e_ParticlesMinDrawPixels=1 -e_ParticlesMotionBlur=0 -e_ParticlesObjectCollisions=2 -e_ParticlesQuality=3 -e_ParticlesSortQuality=0 -e_ParticlesPoolSize=16384 -r_ParticlesHalfRes=0 -r_ParticlesTessellation=1 -r_ParticlesInstanceVertices=1 -r_ParticlesGpuMaxEmitCount=10000 - -[1] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=10 - - -[2] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=100 - - -[3] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=500 - - -[4] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=1000 - - -[5] -e_ParticlesMaxScreenFill=32 -e_ParticlesMinDrawPixels=1.5 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=1 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesGpuMaxEmitCount=3000 - -[6] -e_ParticlesMaxScreenFill=64 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -r_ParticlesGpuMaxEmitCount=5000 - -[8] -e_ParticlesMaxScreenFill=160 -e_ParticlesMotionBlur=1 -e_ParticlesQuality=4 -r_ParticlesGpuMaxEmitCount=1048576 \ No newline at end of file diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg deleted file mode 100644 index 15d9fc61e3..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg +++ /dev/null @@ -1,100 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -es_MaxPhysDist=100 -es_MaxPhysDistInvisible=25 -e_CullVegActivation=50 -e_FoliageWindActivationDist=25 -e_PhysMinCellSize=4 -e_PhysOceanCell=0.5 -e_PhysProxyTriLimit=10000 -g_breakage_mem_limit=0 -g_breakage_particles_limit=160 -g_no_secondary_breaking=0 -g_tree_cut_reuse_dist=0 -p_gravity_z=-13 -p_max_entity_cells=300000 -p_max_MC_iters=6000 -p_max_object_splashes=3 -p_max_substeps=5 -p_max_substeps_large_group=5 -p_num_bodies_large_group=100 -p_splash_dist0=7 -p_splash_dist1=30 -p_splash_force0=10 -p_splash_force1=100 -p_splash_vel0=4.5 -p_splash_vel1=10 -v_vehicle_quality=4 - -[1] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[2] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[3] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[4] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[5] -es_MaxPhysDist=50 -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysOceanCell=1 -g_breakage_particles_limit=80 -g_tree_cut_reuse_dist=0.35 -p_max_MC_iters=4000 -p_max_substeps=2 - -[6] - -[8] diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg deleted file mode 100644 index 9d2cd3754d..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg +++ /dev/null @@ -1,114 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -r_PostProcessEffects=1 -q_ShaderHDR=2 -q_ShaderPostProcess=2 -r_ChromaticAberration=0 -r_ColorGradingChartsCache=0 -r_DepthOfField=2 -r_Flares=1 -r_HDRBloomQuality=2 -r_MotionBlur=2 -r_MotionBlurMaxViewDist=100000 -r_MotionBlurQuality=1 -r_MotionBlurShutterSpeed=125 -r_Rain=2 -r_RainMaxViewDist_Deferred=150 -r_Sharpening=0 -r_Snow=2 -r_SunShafts=2 -r_TranspDepthFixup=1 -r_ToneMapTechnique=0 -r_ToneMapExposureType=0 -r_HDRBloom=1 -r_ColorGrading=1 -r_ColorSpace=0 - -[1] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_ColorGrading=0 -r_DepthOfField=0 -r_Flares=0 -r_HDRBloom=0 -r_HDRBloomQuality=0 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=0 -r_TranspDepthFixup=0 -r_ToneMapTechnique=3 -r_ToneMapExposureType=1 -r_ColorSpace=2 - - -[2] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_ColorGradingChartsCache=4 -r_DepthOfField=1 -r_Flares=0 -r_HDRBloomQuality=0 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=1 -r_TranspDepthFixup=0 - - -[3] -q_ShaderHDR=1 -q_ShaderPostProcess=2 -r_ColorGradingChartsCache=4 -r_DepthOfField=1 -r_HDRBloomQuality=1 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=1 -r_TranspDepthFixup=0 - -[4] -q_ShaderHDR=1 -q_ShaderPostProcess=2 -r_ColorGradingChartsCache=4 -r_DepthOfField=1 -r_HDRBloomQuality=1 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=1 -r_TranspDepthFixup=0 - -[5] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_ColorGradingChartsCache=4 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_RainMaxViewDist_Deferred=40 -r_TranspDepthFixup=0 - -[6] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_RainMaxViewDist_Deferred=100 - -[8] -q_ShaderHDR=3 -q_ShaderPostProcess=3 -r_MotionBlurQuality=2 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg deleted file mode 100644 index cf1bdf5184..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg +++ /dev/null @@ -1,98 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -q_ShaderGeneral=2 -q_ShaderMetal=2 -q_ShaderGlass=2 -q_ShaderVegetation=2 -q_ShaderIce=2 -q_ShaderTerrain=2 -q_ShaderShadow=2 -q_ShaderFX=2 -q_ShaderSky=2 -q_Renderer=2 - -[1] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[2] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[3] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=2 - -[4] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=2 - -[5] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[6] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[8] -q_ShaderGeneral=3 -q_ShaderMetal=3 -q_ShaderGlass=3 -q_ShaderVegetation=3 -q_ShaderIce=3 -q_ShaderTerrain=3 -q_ShaderShadow=3 -q_ShaderFX=3 -q_ShaderSky=3 -q_Renderer=3 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg deleted file mode 100644 index eb379a8583..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg +++ /dev/null @@ -1,120 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_CacheNearestCubePicking=1 -e_DynamicLightsMaxEntityLights=16 -e_GI=1 -e_LightVolumes=1 -e_SkyUpdateRate=1 -e_TerrainAo=0 -e_VegetationUseTerrainColor=1 -r_DeferredShadingDepthBoundsTest=1 -r_DeferredShadingTiled=2 -r_DeferredShadingTiledHairQuality=1 -r_deferredShadingFilterGBuffer=0 -r_DeferredShadingSSS=1 -r_AntialiasingMode=3 -r_DetailDistance=8 -r_EnvTexUpdateInterval=0.05 -r_Refraction=1 -r_RefractionPartialResolves=2 -r_ssdo=1 -r_ssdoHalfRes=2 -r_ssdoColorBleeding=1 -r_SSReflections=1 -r_SSReflHalfRes=1 -r_VisAreaClipLightsPerPixel=1 -sys_spec_Quality=7 - -[1] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_Refraction=0 -r_RefractionPartialResolves=0 -r_ssdo=0 -r_ssdoHalfRes=1 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=1 - -[2] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_RefractionPartialResolves=0 -r_ssdo=0 -r_ssdoHalfRes=1 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=2 - -[3] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_RefractionPartialResolves=0 -r_ssdo=0 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=3 - -[4] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_RefractionPartialResolves=0 -r_ssdo=1 -r_ssdoHalfRes=1 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=4 - -[5] -e_DynamicLightsMaxEntityLights=7 -e_GI=0 -e_SkyUpdateRate=0.5 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_SSReflections=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -sys_spec_Quality=5 - -[6] -e_DynamicLightsMaxEntityLights=11 -sys_spec_Quality=6 - -[8] -r_DeferredShadingTiledHairQuality=2 -r_SSReflHalfRes=0 -sys_spec_Quality=8 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg deleted file mode 100644 index d43c28c09d..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg +++ /dev/null @@ -1,116 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_GsmLodsNum=5 -e_GsmRange=3 -e_ParticlesShadows=1 -e_Shadows=1 -e_ShadowsBlendCascades=1 -e_ShadowsClouds=1 -e_ShadowsCastViewDistRatio=1 -e_ShadowsLodBiasFixed=0 -e_ShadowsMaxTexRes=1024 -e_ShadowsOnAlphaBlend=0 -e_ShadowsPoolSize=4096 -e_ShadowsResScale=4 -e_ShadowsTessellateCascades=1 -e_ShadowsTessellateDLights=0 -e_ShadowsUpdateViewDistRatio=256 -r_DrawNearShadows=1 -r_FogShadows=2 -r_FogShadowsWater=0 -r_ShadowJittering=2.5 -r_ShadowPoolMaxFrames=30 -r_ShadowPoolMaxTimeslicedUpdatesPerFrame=100 -r_ShadowsPCFiltering=1 -r_ShadowsCache=4 -r_ShadowsCacheFormat=1 -r_ShadowsCacheResolutions=6324,4214 -r_ShadowsUseClipVolume=1 -e_ObjShadowCastSpec=3 - -[1] -e_GsmLodsNum=3 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[2] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[3] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[4] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[5] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_FogShadows=0 -r_ShadowJittering=1 -e_ObjShadowCastSpec=1 -r_ShadowsCacheResolutions=3162,2107 - -[6] -r_ShadowJittering=1 -e_ObjShadowCastSpec=2 - -[8] -r_FogShadows=1 -r_FogShadowsWater=1 -r_ShadowPoolMaxFrames=0 -r_ShadowPoolMaxTimeslicedUpdatesPerFrame=100 -e_ObjShadowCastSpec=4 -r_ShadowsCache=5 -r_ShadowsCacheResolutions=4214 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg deleted file mode 100644 index 42bd4c7f02..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -[1] - -[2] - -[3] - -[4] - -[5] - -[6] - -[8] diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg deleted file mode 100644 index 0e02c41ab2..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg +++ /dev/null @@ -1,94 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_TerrainTextureStreamingPoolItemsNum=64 -r_DynTexAtlasCloudsMaxSize=32 -r_DynTexAtlasSpritesMaxSize=32 -r_DynTexMaxSize=80 -r_EnvCMResolution=2 -r_EnvTexResolution=3 -r_ImposterRatio=1 -r_TexAtlasSize=2048 -r_TexMaxAnisotropy=4 -r_TexMinAnisotropy=4 -r_TexNoAnisoAlphaTest=0 - -[1] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[2] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[3] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[4] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[5] -r_DynTexAtlasCloudsMaxSize=24 -r_DynTexAtlasSpritesMaxSize=16 -r_DynTexMaxSize=50 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[6] -r_DynTexAtlasCloudsMaxSize=24 -r_DynTexAtlasSpritesMaxSize=16 -r_DynTexMaxSize=60 -r_EnvCMResolution=1 -r_EnvTexResolution=2 -r_ImposterRatio=1.5 -r_TexMaxAnisotropy=8 -r_TexMinAnisotropy=8 -r_TexNoAnisoAlphaTest=1 - -[8] -r_TexMaxAnisotropy=16 -r_TexMinAnisotropy=16 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg deleted file mode 100644 index 6b6c11ddf6..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg +++ /dev/null @@ -1,52 +0,0 @@ -[default] -; dummy default for this CVarGroup (will be auto initialized during streaming system init or overridden by user via system.cfg) -= 0 - -; VRAM 1.0 GB -r_TexturesStreaming=1 -r_TexturesStreamingMipBias=0 -r_TexturesstreamingMinUsableMips=8 -r_TexturesStreamingSkipMips=2 -r_TexturesStreamPoolSize=256 - -[1] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[2] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[3] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[4] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[5] -; VRAM 1.0 GB - -[6] -; VRAM 1.5 GB -r_TexturesStreamingSkipMips=1 -r_TexturesStreamPoolSize=512 - -[7] -; VRAM 2.0 GB -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=640 - -[8] -; VRAM 3.0 GB -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=1536 \ No newline at end of file diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg deleted file mode 100644 index b6fdfec33c..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg +++ /dev/null @@ -1,26 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_Clouds=1 -r_Beams=1 - -[1] -r_Beams=0 - -[2] -r_Beams=0 - -[3] -r_Beams=0 - -[4] -r_Beams=0 - -[5] -r_Beams=0 - -[6] -r_Beams=0 - -[8] diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg deleted file mode 100644 index 720339dc98..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg +++ /dev/null @@ -1,81 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_WaterOceanFFT=1 -e_WaterTessellationAmount=10 -e_WaterTessellationSwathWidth=10 -q_ShaderWater=2 -r_WaterCaustics=1 -r_WaterReflections=1 -r_WaterReflectionsQuality=4 -r_WaterReflectionsMinVisiblePixelsUpdate=0.05 -r_WaterTessellationHW=0 -r_WaterUpdateDistance=0.2 -r_WaterUpdateFactor=0.0 -r_WaterVolumeCaustics=0 -r_WaterVolumeCausticsDensity=256 -r_WaterVolumeCausticsMaxDist=35 -r_WaterVolumeCausticsRes=1024 -r_WaterVolumeCausticsSnapFactor=1 - -[1] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=0 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.1 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[2] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=0 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.1 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[3] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=0 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.05 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[4] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=4 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.01 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[5] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.1 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[6] -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.05 -r_WaterVolumeCausticsDensity=128 -r_WaterVolumeCausticsMaxDist=25 -r_WaterVolumeCausticsRes=512 - -[8] -e_WaterTessellationAmount=85 -r_WaterTessellationHW=1 -r_WaterVolumeCaustics=1 diff --git a/Assets/Engine/Config/aidebug.cfg b/Assets/Engine/Config/aidebug.cfg index f8134c7461..b0eafaaa45 100644 --- a/Assets/Engine/Config/aidebug.cfg +++ b/Assets/Engine/Config/aidebug.cfg @@ -1,4 +1 @@ ai_DebugDraw = 1 -ai_DebugDrawNavigation = 1 -ai_DrawPath all -ai_debugMNMAgentType MediumSizedCharacters \ No newline at end of file diff --git a/Assets/Engine/Config/artprof.cfg b/Assets/Engine/Config/artprof.cfg index 2e1cf55a54..92b91623bf 100644 --- a/Assets/Engine/Config/artprof.cfg +++ b/Assets/Engine/Config/artprof.cfg @@ -1,7 +1,6 @@ ; Setup useful cvars for artists profiling GPU cost ; Once in level (e.g. map c3mp_rooftop_gardens from the frontend/cmdline), in the console: ; exec artprof.cfg -; Be sure also to set r_shadersAsyncActivation=0 in your user.cfg (or copy artprof_user.cfg -> user.cfg) ; used to allow loading of loose shaders sys_pakPriority=0 @@ -9,19 +8,8 @@ sys_pakPriority=0 ; because it's annoying and not relevant for artists sys_pakLogInvalidFileAccess=0 -; disable fog volumes and particles as they can be misleading with r_measureOverdraw 4 -e_fogVolumes=0 -e_particles=0 - ; for convenience -g_infiniteSuitEnergy=1 -g_infiniteAmmo=1 -g_timelimit=0 + bind o "r_measureOverdraw 0" bind p "r_measureOverdraw 4" -bind k "r_artProfile 0" -bind l "r_artProfile 1" - -; loading into an MP level with the map command stops you looking up and down unless you have a weapon -i_giveitem scar diff --git a/Assets/Engine/Config/artprof_user.cfg b/Assets/Engine/Config/artprof_user.cfg index 673436f6f3..265021e120 100644 --- a/Assets/Engine/Config/artprof_user.cfg +++ b/Assets/Engine/Config/artprof_user.cfg @@ -1,6 +1,3 @@ -; disable async shader activation as it crashes when r_shadersAllowCompilation=1 -r_shadersAsyncActivation=0 - ; enable shader compiliation for r_measureOverdraw 4 r_shadersAllowCompilation=1 diff --git a/Assets/Engine/Config/benchmark_cpu.cfg b/Assets/Engine/Config/benchmark_cpu.cfg index c75619107f..bbb3c9db95 100644 --- a/Assets/Engine/Config/benchmark_cpu.cfg +++ b/Assets/Engine/Config/benchmark_cpu.cfg @@ -1,13 +1,3 @@ -demo_restart_level = 2 -g_godMode=1 -g_infiniteammo=1 r_displayinfo=1 -demo_file = autotest -demo_ai = 1 -demo_num_runs = 4 -demo_quit = 1 -hud_startPaused=0 sys_maxfps=0 -e_ObjectLayersActivation=0 -sys_flash = 0 r_vsync = 0 diff --git a/Assets/Engine/Config/benchmark_gpu.cfg b/Assets/Engine/Config/benchmark_gpu.cfg index 7653b32d86..f0242d64d1 100644 --- a/Assets/Engine/Config/benchmark_gpu.cfg +++ b/Assets/Engine/Config/benchmark_gpu.cfg @@ -1,13 +1,3 @@ -demo_restart_level = 1 -g_godMode = 1 -g_infiniteammo = 1 r_displayinfo = 2 -demo_file = timedemo_short -demo_ai = 0 -demo_num_runs = 2 -demo_quit = 1 --- hud_startPaused = 0 sys_maxfps = -1 --- e_ObjectLayersActivation = 0 --- sys_flash = 0 -r_vsync = 0 \ No newline at end of file +r_vsync = 0 diff --git a/Assets/Engine/Config/mgpu.cfg b/Assets/Engine/Config/mgpu.cfg index 93da7fdfd0..e69de29bb2 100644 --- a/Assets/Engine/Config/mgpu.cfg +++ b/Assets/Engine/Config/mgpu.cfg @@ -1,6 +0,0 @@ -r_ColorGradingChartsCache = 0 -r_waterupdateFactor = 0 -r_PostProcessHUD3DCache = 0 -e_gsmcache = 0 -r_ConditionalRendering = 0 -e_gicache = 0 \ No newline at end of file diff --git a/Assets/Engine/Config/multiplayer.cfg b/Assets/Engine/Config/multiplayer.cfg index eafa434f8e..e69de29bb2 100644 --- a/Assets/Engine/Config/multiplayer.cfg +++ b/Assets/Engine/Config/multiplayer.cfg @@ -1,77 +0,0 @@ -ag_turnSpeedParamScale=0.0 -aim_assistFalloffDistance=200 -aim_assistInputForFullFollow_Ironsight=0.20 -aim_assistMaxDistance=255 -aim_assistMaxDistance_ironsight=255 -aim_assistMinTurnScale=0.5 -aim_assistMinTurnScale_ironsight=0.5 -aim_assistSlowDisableDistance=255 -aim_assistSlowFalloffStartDistance=200 -aim_assistSlowThresholdOuter=2.5 -aim_assiststrength=0.7 -aim_assiststrength_ironsight=0.75 -br_breakmaxworldsize=511 -cl_sensitivityControllerMP=0.6 -cl_shallowWaterSpeedMulPlayer=1.0 -controller_multiplier_x=3 -controller_multiplier_z=4 -g_actorViewDistRatio=255 - --- This forces broken trees to have spherical inertia, which makes them harder to rotate around their vertical axis. -g_breakageMinAxisInertia=1.0 -g_glassAutoShatterMinArea=0.5 - -g_distanceForceNoIk=35 -g_fpDbaManagementEnable=0 -g_godMode=0 -g_highlightingMaxDistanceToHighlightSquared=625 -g_hitDeathReactions_streaming=2 -g_mp_as_DefendersMaxHealth=150 -g_multiplayerDefault=1 - --- Overriden in GameSDK\Difficulty\*.cfg -g_playerLowHealthThreshold=20 -g_playerMidHealthThreshold=60 - -g_spawn_vistable_numLineTestsPerFrame=3 -g_telemetryConfig="MP" -g_telemetrySampleRateBandwidth=3 -g_telemetrySampleRateMemory=2 -g_telemetrySampleRatePerformance=1 -g_VTOLInsideBoundsScaleX=0.6 -g_VTOLInsideBoundsScaleY=1 -net_breakage_sync_entities=0 -net_enable_tfrc=0 -net_log=1 - --- Make consoles match the PC gravity -p_gravity_z="-13" - --- Sanity check for physics RepositionEntity -p_max_entity_cells=10000 - -pl_impulseEnabled=1 -pl_jump_maxTimerValue=0.0 -pl_jump_quickPressThresh=0.12 -pl_melee.angle_limit_from_behind=70 -pl_melee.impulses_enable=1 -pl_melee.melee_snap_angle_limit=45 -pl_melee.melee_snap_end_position_range=1.5 -pl_melee.melee_snap_move_speed_multiplier=10 -pl_melee.melee_snap_target_select_range=3.5 -pl_melee.mp_knockback_strength_hor=2 -pl_melee.mp_melee_system=1 -pl_melee.mp_victim_screenfx_blendout_duration=0.25 -pl_melee.mp_victim_screenfx_duration=0.1 -pl_nanovision_timetodrain=8 -pl_nanovision_timetorecharge=16 -pl_pickAndThrow.chargedThrowAutoAimConeSize=10 -pl_pickAndThrow.complexMelee_snap_angle_limit=25 -pl_sliding_control_mp.deceleration_speed=4 -pl_sliding_control_mp.max_downhill_acceleration=15 -pl_sliding_control_mp.min_speed=4 -pl_sliding_control_mp.min_speed_threshold=5 -pl_stealthKill_aimVsSpineLerp=0.65 -pl_stealthKill_useExtendedRange=1 -p_splash_vel0=0.5 -sv_bandwidth=2147483647 \ No newline at end of file diff --git a/Assets/Engine/Config/multiplayer_console.cfg b/Assets/Engine/Config/multiplayer_console.cfg index 0bed002497..e69de29bb2 100644 --- a/Assets/Engine/Config/multiplayer_console.cfg +++ b/Assets/Engine/Config/multiplayer_console.cfg @@ -1,29 +0,0 @@ -e_GI=0 -r_DeferredShadingIndexedAmbient=0 -e_ParticlesObjectCollisions=0 -g_distanceForceNoLegRaycasts=0.00001 -g_telemetryDisplaySessionId=1 -g_breakageNoDebrisCollisions=1 - -; Breakage throttling -; These are explained in ActionGame.cpp -; -g_glassAutoShatterOnExplosions=1 -g_glassNoDecals=1 -g_glassMaxPanesToBreakPerFrame=2 -g_breakageTreeMax=100 -g_breakageTreeInc=101 -g_breakageTreeDec=25 -g_breakageTreeIncGlass=51 - -sys_PakInMemoryPakSizeLimit=25 - -r_TexturesStreamPoolSecondarySize=35 -r_MotionBlur=1 - -e_CoverageBufferReproj=2 - -osm_enabled = 1 - -g_waterHitOnly = 1 -p_max_object_splashes=1 diff --git a/Assets/Engine/Config/recording.cfg b/Assets/Engine/Config/recording.cfg index 42a0dfd9bd..931dbf0858 100644 --- a/Assets/Engine/Config/recording.cfg +++ b/Assets/Engine/Config/recording.cfg @@ -1,63 +1,3 @@ --- added this lines for proper 360 deg panorama renderings - -e_ScreenShotFileFormat = jpg -demo_fixed_timestep = 60 -s_SoundEnable = 0 r_DisplayInfo = 0 -e_PanoramaScreenShotHeight = 720 -e_PanoramaScreenShotWidth = 10053 c_shakeMult = 0 -demo_ai = 1 -r_MotionBlur = 0 - --- enables full water reflection --- e_DebugMask = 2 -- e_DebugMask not allowed here - --- set weapong lighting effect to 0 to prevent flickering bug because of too much dynamic lights in the scene - - sys_spec = 2 -r_WaterRefractions = 1 -r_WaterReflections = 1 -r_WaterUpdateFactor = 0.01 -r_WaterReflections_ForceParticles = 0 - -r_EnvCMResolution = 2 -r_EnvTexResolution = 3 -r_EnvTexUpdateInterval = 0.05 -e_Decals = 1 -e_DecalsLifeTimeScale = 2 -ca_EnableDecals = 1 -e_LodRatio = 10 -e_ViewDistRatio = 55 -e_Lods = 1 -e_VegetationMinSize = 0 -r_CloudsUpdateAlways = 0 - -e_DynamicLightsMaxEntityLights = 3 -r_DepthOfField = 1 ---r_MotionBlur = 1 -r_Flares = 1 -r_checkSunVis = 1 -r_Coronas = 1 -r_CoronaFade = 0.1625 -r_UseEdgeAA = 1 -e_Clouds = 1 - -r_TexResolution = 0 -r_TexBumpResolution = 0 -r_DetailTextures = 1 -r_DetailNumLayers = 1 -r_DetailDistance = 8 - -e_ParticlesLod = 0.9 - -r_ShadowBlur = 3 -e_ShadowsMaxTexRes = 1024 -r_ShadowJittering = 1 -e_Shadows = 1 -e_VegetationBending = 1 -ai_UpdateAllAlways = 1 -r_refraction = 1 -r_sunshafts = 1 -r_ImposterRatio = 1 diff --git a/Assets/Engine/Config/singleplayer.cfg b/Assets/Engine/Config/singleplayer.cfg index fc7a4f4c05..e69de29bb2 100644 --- a/Assets/Engine/Config/singleplayer.cfg +++ b/Assets/Engine/Config/singleplayer.cfg @@ -1,15 +0,0 @@ -ai_CompatibilityMode=crysis2 -ai_BurstWhileMovingDestinationRange=9999.0 - -g_telemetryConfig=SP - -net_inactivitytimeout=3600 -net_inactivitytimeoutDevmode=3600 - -pl_movement.nonCombat_heavy_weapon_speed_scale=1.0 - --- BLM - Don't override game rules. The template project only has DummyRules. --- Furthermore, multiplayer.cfg doesn't set sv_gamerules, so the inconsistency --- is likely to create bugs. ---sv_gamerules=SinglePlayer -ca_StreamCHR=1 \ No newline at end of file diff --git a/Assets/Engine/Config/sketch_off.cfg b/Assets/Engine/Config/sketch_off.cfg deleted file mode 100644 index 6b0562daab..0000000000 --- a/Assets/Engine/Config/sketch_off.cfg +++ /dev/null @@ -1,24 +0,0 @@ -r_UseZPass = 1 -r_GeomInstancing = 1 - -e_Fog = 1 -e_Clouds = 1 -e_Decals = 1 -e_TerrainDetailMaterials = 1 -e_Dissolve = 1 -e_TerrainAo = 1 - -r_WaterReflections = 1 - -e_Shadows = 1 -e_VegetationBending = 1 - -r_PostProcessEffects = 1 -r_Flares = 1 -r_Beams = 1 -r_Glow = 1 - -r_DetailTextures = 1 - -r_refraction = 1 -r_sunshafts = 1 \ No newline at end of file diff --git a/Assets/Engine/Config/sketch_on.cfg b/Assets/Engine/Config/sketch_on.cfg deleted file mode 100644 index 6759b5d4c8..0000000000 --- a/Assets/Engine/Config/sketch_on.cfg +++ /dev/null @@ -1,24 +0,0 @@ -r_UseZPass = 0 -r_GeomInstancing = 0 - -e_Fog = 0 -e_Clouds = 0 -e_Decals = 0 -e_TerrainDetailMaterials = 0 -e_Dissolve = 0 -e_TerrainAo = 0 - -r_WaterReflections = 0 - -e_Shadows = 0 -e_VegetationBending = 0 - -r_PostProcessEffects = 0 -r_Flares = 0 -r_Beams = 0 -r_Glow = 0 - -r_DetailTextures = 0 - -r_refraction = 0 -r_sunshafts = 0 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_MaliT760.cfg b/Assets/Engine/Config/spec/android_MaliT760.cfg index f951e61eef..fd7c0a349d 100644 --- a/Assets/Engine/Config/spec/android_MaliT760.cfg +++ b/Assets/Engine/Config/spec/android_MaliT760.cfg @@ -1,76 +1,8 @@ -sys_spec_Full=2 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Disable gmem for this device because it causes a crash -r_EnableGMEMPath=0 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 --- This allows the generation of reflections for the ocean water. Without it, the water looks really dark. -e_recursion=1 -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 -e_ViewDistRatioVegetation=21 - - ------------------------- --- sys_spec_postprocessing ------------------------- -r_HDRBloom=0 -r_SunShafts=0 -r_ToneMapTechnique=3 -r_ToneMapExposureType=1 -r_ColorSpace=2 - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - ------------------------- --- sys_spec_textureresolution ------------------------- -r_TexturesstreamingMinUsableMips=7 - - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_high.cfg b/Assets/Engine/Config/spec/android_high.cfg index fc46adc491..0ab8ebc37b 100644 --- a/Assets/Engine/Config/spec/android_high.cfg +++ b/Assets/Engine/Config/spec/android_high.cfg @@ -1,65 +1,7 @@ -sys_spec_Full=3 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=2 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 - -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - -r_ClearGMEMGBuffer=1 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_high_nogmem.cfg b/Assets/Engine/Config/spec/android_high_nogmem.cfg index ac718a57ee..fd7c0a349d 100644 --- a/Assets/Engine/Config/spec/android_high_nogmem.cfg +++ b/Assets/Engine/Config/spec/android_high_nogmem.cfg @@ -1,61 +1,8 @@ -sys_spec_Full=3 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Disabling gmem for this configuration -r_EnableGMEMPath=0 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 - diff --git a/Assets/Engine/Config/spec/android_low.cfg b/Assets/Engine/Config/spec/android_low.cfg index 6f2a876877..0ab8ebc37b 100644 --- a/Assets/Engine/Config/spec/android_low.cfg +++ b/Assets/Engine/Config/spec/android_low.cfg @@ -1,76 +1,7 @@ -sys_spec_Full=1 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 - --- This allows the generation of reflections for the ocean water. Without it, the water looks really dark. -e_recursion=0 -e_CheckOcclusion=1 - -r_Fur=0 - --- Water occlusion queries crash in some OpenGL ES 3.0 devices -e_HwOcclusionCullingWater = 0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - - ------------------------- --- sys_spec_textureresolution ------------------------- -r_TexturesstreamingMinUsableMips=6 - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 -r_ClearGMEMGBuffer=1 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_medium.cfg b/Assets/Engine/Config/spec/android_medium.cfg index f9d776efa7..0ab8ebc37b 100644 --- a/Assets/Engine/Config/spec/android_medium.cfg +++ b/Assets/Engine/Config/spec/android_medium.cfg @@ -1,81 +1,7 @@ -sys_spec_Full=2 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 - --- This allows the generation of reflections for the ocean water. Without it, the water looks really dark. -e_recursion=1 -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 -e_ViewDistRatioVegetation=21 - - ------------------------- --- sys_spec_postprocessing ------------------------- -r_HDRBloom=0 -r_SunShafts=0 -r_ToneMapExposureType=1 -r_ColorSpace=2 - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - ------------------------- --- sys_spec_textureresolution ------------------------- -r_TexturesstreamingMinUsableMips=7 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - -r_ClearGMEMGBuffer=1 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_veryhigh.cfg b/Assets/Engine/Config/spec/android_veryhigh.cfg index d4f8e9cd47..d58e319706 100644 --- a/Assets/Engine/Config/spec/android_veryhigh.cfg +++ b/Assets/Engine/Config/spec/android_veryhigh.cfg @@ -1,60 +1,5 @@ -sys_spec_Full=4 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 - --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 - sys_streaming_memory_budget=512 - -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - -r_ClearGMEMGBuffer=1 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_high.cfg b/Assets/Engine/Config/spec/ios_high.cfg index 67fa99ca04..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_high.cfg +++ b/Assets/Engine/Config/spec/ios_high.cfg @@ -1,100 +1,10 @@ -sys_spec_Full=3 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=2048 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_low.cfg b/Assets/Engine/Config/spec/ios_low.cfg index 7870d65bd5..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_low.cfg +++ b/Assets/Engine/Config/spec/ios_low.cfg @@ -1,103 +1,10 @@ -sys_spec_Full=1 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_medium.cfg b/Assets/Engine/Config/spec/ios_medium.cfg index 2eaecdef16..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_medium.cfg +++ b/Assets/Engine/Config/spec/ios_medium.cfg @@ -1,101 +1,10 @@ -sys_spec_Full=2 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_veryhigh.cfg b/Assets/Engine/Config/spec/ios_veryhigh.cfg index 53937a77bf..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_veryhigh.cfg +++ b/Assets/Engine/Config/spec/ios_veryhigh.cfg @@ -1,102 +1,10 @@ -sys_spec_Full=4 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=2048 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/osx_metal_high.cfg b/Assets/Engine/Config/spec/osx_metal_high.cfg deleted file mode 100644 index 8689e23c51..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_high.cfg +++ /dev/null @@ -1,38 +0,0 @@ - -sys_spec_Full=7 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI = 0 - - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/osx_metal_low.cfg b/Assets/Engine/Config/spec/osx_metal_low.cfg deleted file mode 100644 index a3084d48c5..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_low.cfg +++ /dev/null @@ -1,37 +0,0 @@ - -sys_spec_Full=5 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI=0 - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/osx_metal_medium.cfg b/Assets/Engine/Config/spec/osx_metal_medium.cfg deleted file mode 100644 index 6477bbb217..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_medium.cfg +++ /dev/null @@ -1,37 +0,0 @@ - -sys_spec_Full=6 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI = 0 - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/osx_metal_veryhigh.cfg b/Assets/Engine/Config/spec/osx_metal_veryhigh.cfg deleted file mode 100644 index 899c198f31..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_veryhigh.cfg +++ /dev/null @@ -1,37 +0,0 @@ - -sys_spec_Full=8 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI = 0 - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/pc_high.cfg b/Assets/Engine/Config/spec/pc_high.cfg index a62ed25a6a..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_high.cfg +++ b/Assets/Engine/Config/spec/pc_high.cfg @@ -1 +0,0 @@ -sys_spec_Full = 7 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/pc_low.cfg b/Assets/Engine/Config/spec/pc_low.cfg index 068446e151..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_low.cfg +++ b/Assets/Engine/Config/spec/pc_low.cfg @@ -1 +0,0 @@ -sys_spec_Full = 5 diff --git a/Assets/Engine/Config/spec/pc_medium.cfg b/Assets/Engine/Config/spec/pc_medium.cfg index 0d08b956b4..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_medium.cfg +++ b/Assets/Engine/Config/spec/pc_medium.cfg @@ -1 +0,0 @@ -sys_spec_Full = 6 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/pc_veryhigh.cfg b/Assets/Engine/Config/spec/pc_veryhigh.cfg index 33959937e8..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_veryhigh.cfg +++ b/Assets/Engine/Config/spec/pc_veryhigh.cfg @@ -1 +0,0 @@ -sys_spec_Full = 8 diff --git a/Assets/Engine/Config/statoscope.cfg b/Assets/Engine/Config/statoscope.cfg index d1d18c813c..ed2089bd67 100644 --- a/Assets/Engine/Config/statoscope.cfg +++ b/Assets/Engine/Config/statoscope.cfg @@ -1,12 +1,3 @@ profile=-1 -profile_allthreads=1 -i_forcefeedback=0 -g_godmode=1 log_verbosity=-1 sys_pakLogInvalidFileAccess=1 -e_StatoscopeDataGroups=fgmrtudlipny -e_StatoscopeFilenameUseBuildInfo=0 -e_StatoscopeFilenameUseMap=1 -e_StatoscopeMinFuncLengthMs=0.1 -e_StatoscopeMaxNumFuncsPerFrame=60 -e_StatoscopeScreenshotCapturePeriod=5 diff --git a/Assets/Engine/Config/vr.cfg b/Assets/Engine/Config/vr.cfg index 989f55f345..80d4b7ce81 100644 --- a/Assets/Engine/Config/vr.cfg +++ b/Assets/Engine/Config/vr.cfg @@ -3,39 +3,13 @@ ----------------------------------------------- r_width = 960 r_height = 1080 -r_backbufferWidth = 1920 -r_backbufferHeight = 1080 - ------------------------------------------- -- Set the system Spec (Medium) ------------------------------------------- sys_spec = 2 - -------------------------------------------- --- HMD related -------------------------------------------- -r_overrideDXGIoutput = 0 -r_stereodevice = 100 -r_stereomode = 1 -r_stereooutput = 7 -r_minimizeLatency = 1 -hmd_low_persistence = 1 -r_stereoScaleCoefficient = 1 - - ------------------------------------------- -- Set some video optimisations ------------------------------------------- r_vsync = 0 -r_MotionBlur = 0 -r_ssdoHalfRes = 3 -r_Refraction = 0 -r_DeferredShadingTiled = 0 -r_CBufferUseNativeDepth = 0 - -------------------------------------------- --- Hide the hud -------------------------------------------- ---hud_hide = 1 diff --git a/Assets/Engine/EngineAssets/Icons/AverageMemoryUsage.TIF.exportsettings b/Assets/Engine/EngineAssets/Icons/AverageMemoryUsage.TIF.exportsettings deleted file mode 100644 index c48fb8632a..0000000000 --- a/Assets/Engine/EngineAssets/Icons/AverageMemoryUsage.TIF.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/HighMemoryUsage.TIF.exportsettings b/Assets/Engine/EngineAssets/Icons/HighMemoryUsage.TIF.exportsettings deleted file mode 100644 index c48fb8632a..0000000000 --- a/Assets/Engine/EngineAssets/Icons/HighMemoryUsage.TIF.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/LevelShaderCacheMiss.tif.exportsettings b/Assets/Engine/EngineAssets/Icons/LevelShaderCacheMiss.tif.exportsettings deleted file mode 100644 index c48fb8632a..0000000000 --- a/Assets/Engine/EngineAssets/Icons/LevelShaderCacheMiss.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/LivePreview.TIF.exportsettings b/Assets/Engine/EngineAssets/Icons/LivePreview.TIF.exportsettings deleted file mode 100644 index 8da27c31a4..0000000000 --- a/Assets/Engine/EngineAssets/Icons/LivePreview.TIF.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/LowMemoryUsage.TIF.exportsettings b/Assets/Engine/EngineAssets/Icons/LowMemoryUsage.TIF.exportsettings deleted file mode 100644 index c48fb8632a..0000000000 --- a/Assets/Engine/EngineAssets/Icons/LowMemoryUsage.TIF.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/NavigationProcessing.tif.exportsettings b/Assets/Engine/EngineAssets/Icons/NavigationProcessing.tif.exportsettings deleted file mode 100644 index 8da27c31a4..0000000000 --- a/Assets/Engine/EngineAssets/Icons/NavigationProcessing.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/NullSoundSystem.tif.exportsettings b/Assets/Engine/EngineAssets/Icons/NullSoundSystem.tif.exportsettings deleted file mode 100644 index 8da27c31a4..0000000000 --- a/Assets/Engine/EngineAssets/Icons/NullSoundSystem.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/ShaderCompiling.tif.exportsettings b/Assets/Engine/EngineAssets/Icons/ShaderCompiling.tif.exportsettings deleted file mode 100644 index 8da27c31a4..0000000000 --- a/Assets/Engine/EngineAssets/Icons/ShaderCompiling.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/Streaming.tif.exportsettings b/Assets/Engine/EngineAssets/Icons/Streaming.tif.exportsettings deleted file mode 100644 index 8da27c31a4..0000000000 --- a/Assets/Engine/EngineAssets/Icons/Streaming.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/EngineAssets/Icons/StreamingTerrain.tif.exportsettings b/Assets/Engine/EngineAssets/Icons/StreamingTerrain.tif.exportsettings deleted file mode 100644 index 8da27c31a4..0000000000 --- a/Assets/Engine/EngineAssets/Icons/StreamingTerrain.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=Uncompressed \ No newline at end of file diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed index 579fd3c444..aafbffbe8f 100644 --- a/Assets/Engine/SeedAssetList.seed +++ b/Assets/Engine/SeedAssetList.seed @@ -48,22 +48,6 @@ - - - - - - - - - - - - - - - - @@ -432,110 +416,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -616,38 +496,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Assets/Engine/textures/default_icon.png.exportsettings b/Assets/Engine/textures/default_icon.png.exportsettings deleted file mode 100644 index 89ea1ac93f..0000000000 --- a/Assets/Engine/textures/default_icon.png.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /mipmaps=0 /preset=Albedo /reduce=-1 /ser=1 \ No newline at end of file diff --git a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings deleted file mode 100644 index b65133fbb0..0000000000 --- a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=AlbedoWithGenericAlpha /reduce="android:2,ios:2,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings deleted file mode 100644 index e8da408b36..0000000000 --- a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Albedo /reduce="android:3,ios:3,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt index 0d6fe4c8fa..ff3cd5c465 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt @@ -6,12 +6,7 @@ # # -################################################################################ -# Atom Renderer: Automated Tests -# Runs EditorPythonBindings (hydra) scripts inside the Editor to verify test results for the Atom renderer. -################################################################################ - -if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS) +if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_pytest( NAME AutomatedTesting::Atom_TestSuite_Main TEST_SUITE main diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 4c5716d365..3403c938a8 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -3,8 +3,6 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Main suite tests for the Atom renderer. """ import logging import os @@ -25,20 +23,20 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") class TestAtomEditorComponentsMain(object): """Holds tests for Atom components.""" + @pytest.mark.test_case_id("C32078118") # Decal + @pytest.mark.test_case_id("C32078119") # DepthOfField + @pytest.mark.test_case_id("C32078120") # Directional Light + @pytest.mark.test_case_id("C32078121") # Exposure Control + @pytest.mark.test_case_id("C32078115") # Global Skylight (IBL) + @pytest.mark.test_case_id("C32078125") # Physical Sky + @pytest.mark.test_case_id("C32078127") # PostFX Layer + @pytest.mark.test_case_id("C32078131") # PostFX Radius Weight Modifier + @pytest.mark.test_case_id("C32078117") # Light + @pytest.mark.test_case_id("C36525660") # Display Mapper def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform): """ Please review the hydra script run by this test for more specific test info. - Tests the following Atom components and verifies all "expected_lines" appear in Editor.log: - 1. Display Mapper - 2. Light - 3. PostFX Radius Weight Modifier - 4. PostFX Layer - 5. Physical Sky - 6. Global Skylight (IBL) - 7. Exposure Control - 8. Directional Light - 9. DepthOfField - 10. Decal + Tests the Atom components & verifies all "expected_lines" appear in Editor.log """ cfg_args = [level] @@ -71,6 +69,18 @@ class TestAtomEditorComponentsMain(object): "DepthOfField_test: Entity deleted: True", "DepthOfField_test: UNDO entity deletion works: True", "DepthOfField_test: REDO entity deletion works: True", + # Directional Light Component + "Directional Light Entity successfully created", + "Directional Light_test: Component added to the entity: True", + "Directional Light_test: Component removed after UNDO: True", + "Directional Light_test: Component added after REDO: True", + "Directional Light_test: Entered game mode: True", + "Directional Light_test: Exit game mode: True", + "Directional Light_test: Entity is hidden: True", + "Directional Light_test: Entity is shown: True", + "Directional Light_test: Entity deleted: True", + "Directional Light_test: UNDO entity deletion works: True", + "Directional Light_test: REDO entity deletion works: True", # Exposure Control Component "Exposure Control Entity successfully created", "Exposure Control_test: Component added to the entity: True", @@ -161,21 +171,6 @@ class TestAtomEditorComponentsMain(object): "Display Mapper_test: Entity deleted: True", "Display Mapper_test: UNDO entity deletion works: True", "Display Mapper_test: REDO entity deletion works: True", - # Reflection Probe Component - "Reflection Probe Entity successfully created", - "Reflection Probe_test: Component added to the entity: True", - "Reflection Probe_test: Component removed after UNDO: True", - "Reflection Probe_test: Component added after REDO: True", - "Reflection Probe_test: Entered game mode: True", - "Reflection Probe_test: Exit game mode: True", - "Reflection Probe_test: Entity disabled initially: True", - "Reflection Probe_test: Entity enabled after adding required components: True", - "Reflection Probe_test: Cubemap is generated: True", - "Reflection Probe_test: Entity is hidden: True", - "Reflection Probe_test: Entity is shown: True", - "Reflection Probe_test: Entity deleted: True", - "Reflection Probe_test: UNDO entity deletion works: True", - "Reflection Probe_test: REDO entity deletion works: True", ] unexpected_lines = [ @@ -197,6 +192,7 @@ class TestAtomEditorComponentsMain(object): cfg_args=cfg_args, ) + @pytest.mark.test_case_id("C34525095") def test_AtomEditorComponents_LightComponent( self, request, editor, workspace, project, launcher_platform, level): """ @@ -283,6 +279,15 @@ class TestMaterialEditorBasicTests(object): request.addfinalizer(teardown) @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) + @pytest.mark.test_case_id("C34448113") # Creating a New Asset. + @pytest.mark.test_case_id("C34448114") # Opening an Existing Asset. + @pytest.mark.test_case_id("C34448115") # Closing Selected Material. + @pytest.mark.test_case_id("C34448116") # Closing All Materials. + @pytest.mark.test_case_id("C34448117") # Closing all but Selected Material. + @pytest.mark.test_case_id("C34448118") # Saving Material. + @pytest.mark.test_case_id("C34448119") # Saving as a New Material. + @pytest.mark.test_case_id("C34448120") # Saving as a Child Material. + @pytest.mark.test_case_id("C34448121") # Saving all Open Materials. def test_MaterialEditorBasicTests( self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index 3403d8e9b1..381f266fab 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -73,6 +73,7 @@ def create_screenshots_archive(screenshot_path): class TestAllComponentsIndepthTests(object): @pytest.mark.parametrize("screenshot_name", ["AtomBasicLevelSetup.ppm"]) + @pytest.mark.test_case_id("C34603773") def test_BasicLevelSetup_SetsUpLevel( self, request, editor, workspace, project, launcher_platform, level, screenshot_name): """ @@ -115,6 +116,7 @@ class TestAllComponentsIndepthTests(object): create_screenshots_archive(screenshot_directory) + @pytest.mark.test_case_id("C34525095") def test_LightComponent_ScreenshotMatchesGoldenImage( self, request, editor, workspace, project, launcher_platform, level): """ @@ -146,7 +148,7 @@ class TestAllComponentsIndepthTests(object): golden_image_path = os.path.join(golden_images_directory(), golden_image) golden_images.append(golden_image_path) - expected_lines = ["Light component tests completed."] + expected_lines = ["spot_light Controller|Configuration|Shadows|Shadowmap size: SUCCESS"] unexpected_lines = [ "Trace::Assert", "Trace::Error", @@ -220,20 +222,20 @@ class TestPerformanceBenchmarkSuite(object): @pytest.mark.system class TestMaterialEditor(object): - @pytest.mark.parametrize("cfg_args", ["-rhi=dx12", "-rhi=Vulkan"]) + @pytest.mark.parametrize("cfg_args,expected_lines", [ + pytest.param("-rhi=dx12", ["Registering dx12 RHI"]), + pytest.param("-rhi=Vulkan", ["Registering vulkan RHI"]) + ]) @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) + @pytest.mark.test_case_id("C30973986") # Material Editor Launching in Dx12 + @pytest.mark.test_case_id("C30973987") # Material Editor Launching in Vulkan def test_MaterialEditorLaunch_AllRHIOptionsSucceed( - self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args): + self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args, + expected_lines): """ Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor. - Checks for the "Finished loading viewport configurations." success message post launch. + Checks for the specific expected_lines messaging for each RHI type. """ - expected_lines = ["Finished loading viewport configurations."] - unexpected_lines = [ - # "Trace::Assert", - # "Trace::Error", - "Traceback (most recent call last):", - ] hydra.launch_and_validate_results( request, @@ -243,7 +245,7 @@ class TestMaterialEditor(object): run_python="--runpython", timeout=60, expected_lines=expected_lines, - unexpected_lines=unexpected_lines, + unexpected_lines=[], halt_on_unexpected=False, null_renderer=False, cfg_args=[cfg_args], diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py index ef572d6e5c..568768e12e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py @@ -23,6 +23,7 @@ class TestAutomation(EditorTestSuite): # Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests. global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"] + @pytest.mark.test_case_id("C34603773") class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest): use_null_renderer = False # Default is True screenshot_name = "AtomBasicLevelSetup.ppm" diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py index 47b2204d56..447a4ebac9 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -14,33 +14,62 @@ from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite @pytest.mark.parametrize("launcher_platform", ['windows_editor']) class TestAutomation(EditorTestSuite): + @pytest.mark.test_case_id("C32078118") class AtomEditorComponents_DecalAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module + @pytest.mark.test_case_id("C32078119") class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module + @pytest.mark.test_case_id("C32078120") class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module + @pytest.mark.test_case_id("C32078121") class AtomEditorComponents_ExposureControlAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module + @pytest.mark.test_case_id("C32078115") class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module + @pytest.mark.test_case_id("C32078125") class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module + @pytest.mark.test_case_id("C32078131") class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest): from Atom.tests import ( hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module) + @pytest.mark.test_case_id("C32078117") class AtomEditorComponents_LightAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module + @pytest.mark.test_case_id("C36525660") class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module + @pytest.mark.test_case_id("C32078128") + class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module + + @pytest.mark.test_case_id("C32078124") + class AtomEditorComponents_MeshAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module + + @pytest.mark.test_case_id("C32078123") + class AtomEditorComponents_MaterialAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_MaterialAdded as test_module + + @pytest.mark.test_case_id("C32078127") + class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module + + @pytest.mark.test_case_id("C36525665") + class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index 05401c0059..58e5d00ff2 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -3,12 +3,17 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Sandbox suite tests for the Atom renderer. """ +import logging +import os import pytest +import editor_python_test_tools.hydra_test_utils as hydra + +logger = logging.getLogger(__name__) +TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") + @pytest.mark.parametrize("project", ["AutomatedTesting"]) @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @@ -18,3 +23,50 @@ class TestAtomEditorComponentsSandbox(object): # It requires at least one test def test_Dummy(self, request, editor, level, workspace, project, launcher_platform): pass + + @pytest.mark.parametrize("project", ["AutomatedTesting"]) + @pytest.mark.parametrize("launcher_platform", ['windows_editor']) + @pytest.mark.parametrize("level", ["auto_test"]) + class TestAtomEditorComponentsMain(object): + """Holds tests for Atom components.""" + + @pytest.mark.test_case_id("C32078128") + def test_AtomEditorComponents_ReflectionProbeAddedToEntity( + self, request, editor, level, workspace, project, launcher_platform): + """ + Please review the hydra script run by this test for more specific test info. + Tests the following Atom components and verifies all "expected_lines" appear in Editor.log: + 1. Reflection Probe + """ + cfg_args = [level] + + expected_lines = [ + # Reflection Probe Component + "Reflection Probe Entity successfully created", + "Reflection Probe_test: Component added to the entity: True", + "Reflection Probe_test: Component removed after UNDO: True", + "Reflection Probe_test: Component added after REDO: True", + "Reflection Probe_test: Entered game mode: True", + "Reflection Probe_test: Exit game mode: True", + "Reflection Probe_test: Entity disabled initially: True", + "Reflection Probe_test: Entity enabled after adding required components: True", + "Reflection Probe_test: Cubemap is generated: True", + "Reflection Probe_test: Entity is hidden: True", + "Reflection Probe_test: Entity is shown: True", + "Reflection Probe_test: Entity deleted: True", + "Reflection Probe_test: UNDO entity deletion works: True", + "Reflection Probe_test: REDO entity deletion works: True", + ] + + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "hydra_AtomEditorComponents_AddedToEntity.py", + timeout=120, + expected_lines=expected_lines, + unexpected_lines=[], + halt_on_unexpected=True, + null_renderer=True, + cfg_args=cfg_args, + ) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py index 821e0acfdb..11832f8846 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py @@ -113,7 +113,6 @@ def create_basic_atom_level(level_name): general.close_pane("Error Log") general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) # Delete all existing entities & create default_level entity diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py new file mode 100644 index 0000000000..32cd5471f2 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py @@ -0,0 +1,205 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + material_creation = ( + "Material Entity successfully created", + "Material Entity failed to be created") + material_component = ( + "Entity has a Material component", + "Entity failed to find Material component") + material_disabled = ( + "Material component disabled", + "Material component was not disabled.") + actor_component = ( + "Entity has an Actor component", + "Entity did not have an Actor component") + actor_undo = ( + "Entity Actor component gone", + "Entity Actor component add failed to undo") + mesh_component = ( + "Entity has a Mesh component", + "Entity did not have a Mesh component") + material_enabled = ( + "Material component enabled", + "Material component was not enabled.") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_Material_AddedToEntity(): + """ + Summary: + Tests the Material component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Material entity with no components. + 2) Add a Material component to Material entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify Material component not enabled. + 6) Add Actor component since it is required by the Material component. + 7) Verify Material component is enabled. + 8) UNDO add Actor component + 9) Verify Material component not enabled. + 10) Add Mesh component since it is required by the Material component. + 11) Verify Material component is enabled. + 12) Enter/Exit game mode. + 13) Test IsHidden. + 14) Test IsVisible. + 15) Delete Material entity. + 16) UNDO deletion. + 17) REDO deletion. + 18) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Material entity with no components. + material_name = "Material" + material_entity = EditorEntity.create_editor_entity(material_name) + Report.critical_result(Tests.material_creation, material_entity.exists()) + + # 2. Add a Material component to Material entity. + material_component = material_entity.add_component(material_name) + Report.critical_result( + Tests.material_component, + material_entity.has_component(material_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not material_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, material_entity.exists()) + + # 5. Verify Material component not enabled. + Report.result(Tests.material_disabled, not material_component.is_enabled()) + + # 6. Add Actor component since it is required by the Material component. + actor_name = "Actor" + material_entity.add_component(actor_name) + Report.result(Tests.actor_component, material_entity.has_component(actor_name)) + + # 7. Verify Material component is enabled. + Report.result(Tests.material_enabled, material_component.is_enabled()) + + # 8. UNDO component addition. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.actor_undo, not material_entity.has_component(actor_name)) + + # 9. Verify Material component not enabled. + Report.result(Tests.material_disabled, not material_component.is_enabled()) + + # 10. Add Mesh component since it is required by the Material component. + mesh_name = "Mesh" + material_entity.add_component(mesh_name) + Report.result(Tests.mesh_component, material_entity.has_component(mesh_name)) + + # 11. Verify Material component is enabled. + Report.result(Tests.material_enabled, material_component.is_enabled()) + + # 12. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 13. Test IsHidden. + material_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, material_entity.is_hidden() is True) + + # 14. Test IsVisible. + material_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, material_entity.is_visible() is True) + + # 15. Delete Material entity. + material_entity.delete() + Report.result(Tests.entity_deleted, not material_entity.exists()) + + # 16. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, material_entity.exists()) + + # 17. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not material_entity.exists()) + + # 18. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_Material_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py new file mode 100644 index 0000000000..fbf5c987d1 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py @@ -0,0 +1,171 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + mesh_entity_creation = ( + "Mesh Entity successfully created", + "Mesh Entity failed to be created") + mesh_component_added = ( + "Entity has a Mesh component", + "Entity failed to find Mesh component") + mesh_asset_specified = ( + "Mesh asset set", + "Mesh asset not set") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_Mesh_AddedToEntity(): + """ + Summary: + Tests the Mesh component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Mesh entity with no components. + 2) Add a Mesh component to Mesh entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Specify the Mesh component asset + 6) Enter/Exit game mode. + 7) Test IsHidden. + 8) Test IsVisible. + 9) Delete Mesh entity. + 10) UNDO deletion. + 11) REDO deletion. + 12) Look for errors. + + :return: None + """ + + import os + + import azlmbr.legacy.general as general + + from editor_python_test_tools.asset_utils import Asset + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Mesh entity with no components. + mesh_name = "Mesh" + mesh_entity = EditorEntity.create_editor_entity(mesh_name) + Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists()) + + # 2. Add a Mesh component to Mesh entity. + mesh_component = mesh_entity.add_component(mesh_name) + Report.critical_result( + Tests.mesh_component_added, + mesh_entity.has_component(mesh_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not mesh_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, mesh_entity.exists()) + + # 5. Set Mesh component asset property + mesh_property_asset = 'Controller|Configuration|Mesh Asset' + model_path = os.path.join('Objects', 'shaderball', 'shaderball_default_1m.azmodel') + model = Asset.find_asset_by_path(model_path) + mesh_component.set_component_property_value(mesh_property_asset, model.id) + Report.result(Tests.mesh_asset_specified, + mesh_component.get_component_property_value(mesh_property_asset) == model.id) + + # 6. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 7. Test IsHidden. + mesh_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, mesh_entity.is_hidden() is True) + + # 8. Test IsVisible. + mesh_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, mesh_entity.is_visible() is True) + + # 9. Delete Mesh entity. + mesh_entity.delete() + Report.result(Tests.entity_deleted, not mesh_entity.exists()) + + # 10. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, mesh_entity.exists()) + + # 11. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not mesh_entity.exists()) + + # 12. Look for errors or asserts. + helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_Mesh_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py new file mode 100644 index 0000000000..8c2dee416b --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py @@ -0,0 +1,156 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + postfx_layer_entity_creation = ( + "PostFX Layer Entity successfully created", + "PostFX Layer Entity failed to be created") + postfx_layer_component_added = ( + "Entity has a PostFX Layer component", + "Entity failed to find PostFX Layer component") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_postfx_layer_AddedToEntity(): + """ + Summary: + Tests the PostFX Layer component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a PostFX Layer entity with no components. + 2) Add a PostFX Layer component to PostFX Layer entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete PostFX Layer entity. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import os + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a PostFX Layer entity with no components. + postfx_layer_name = "PostFX Layer" + postfx_layer_entity = EditorEntity.create_editor_entity(postfx_layer_name) + Report.critical_result(Tests.postfx_layer_entity_creation, postfx_layer_entity.exists()) + + # 2. Add a PostFX Layer component to PostFX Layer entity. + postfx_layer_component = postfx_layer_entity.add_component(postfx_layer_name) + Report.critical_result(Tests.postfx_layer_component_added, postfx_layer_entity.has_component(postfx_layer_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not postfx_layer_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, postfx_layer_entity.exists()) + + # 5. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + postfx_layer_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, postfx_layer_entity.is_hidden() is True) + + # 7. Test IsVisible. + postfx_layer_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, postfx_layer_entity.is_visible() is True) + + # 8. Delete PostFX Layer entity. + postfx_layer_entity.delete() + Report.result(Tests.entity_deleted, not postfx_layer_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, postfx_layer_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not postfx_layer_entity.exists()) + + # 11. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_postfx_layer_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py new file mode 100644 index 0000000000..4c98bcd130 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -0,0 +1,207 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + postfx_shape_weight_creation = ( + "PostFx Shape Weight Modifier Entity successfully created", + "PostFx Shape Weight Modifier Entity failed to be created") + postfx_shape_weight_component = ( + "Entity has a PostFx Shape Weight Modifier component", + "Entity failed to find PostFx Shape Weight Modifier component") + postfx_shape_weight_disabled = ( + "PostFx Shape Weight Modifier component disabled", + "PostFx Shape Weight Modifier component was not disabled.") + postfx_layer_component = ( + "Entity has a PostFX Layer component", + "Entity did not have an PostFX Layer component") + tube_shape_component = ( + "Entity has a Tube Shape component", + "Entity did not have a Tube Shape component") + postfx_shape_weight_enabled = ( + "PostFx Shape Weight Modifier component enabled", + "PostFx Shape Weight Modifier component was not enabled.") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): + """ + Summary: + Tests the PostFx Shape Weight Modifier component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a PostFx Shape Weight Modifier entity with no components. + 2) Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify PostFx Shape Weight Modifier component not enabled. + 6) Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. + 7) Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. + 8) Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. + 9) Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled. + 10) Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component. + 11) Enter/Exit game mode. + 12) Test IsHidden. + 13) Test IsVisible. + 14) Delete PostFx Shape Weight Modifier entity. + 15) UNDO deletion. + 16) REDO deletion. + 17) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a PostFx Shape Weight Modifier entity with no components. + postfx_shape_weight_name = "PostFX Shape Weight Modifier" + postfx_shape_weight_entity = EditorEntity.create_editor_entity(postfx_shape_weight_name) + Report.critical_result(Tests.postfx_shape_weight_creation, postfx_shape_weight_entity.exists()) + + # 2. Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity. + postfx_shape_weight_component = postfx_shape_weight_entity.add_component(postfx_shape_weight_name) + Report.critical_result( + Tests.postfx_shape_weight_component, + postfx_shape_weight_entity.has_component(postfx_shape_weight_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not postfx_shape_weight_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, postfx_shape_weight_entity.exists()) + + # 5. Verify PostFx Shape Weight Modifier component not enabled. + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 6. Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. + postfx_layer_name = "PostFX Layer" + postfx_shape_weight_entity.add_component(postfx_layer_name) + Report.result(Tests.postfx_layer_component, postfx_shape_weight_entity.has_component(postfx_layer_name)) + + # 7. Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 8. Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. + for shape in ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape', + 'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape']: + postfx_shape_weight_entity.add_component(shape) + test_shape = ( + f"Entity has a {shape} component", + f"Entity did not have a {shape} component") + Report.result(test_shape, postfx_shape_weight_entity.has_component(shape)) + + # Check if required shape allows PostFX Shape Weight Modifier to be enabled + Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) + + # 9. Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled. + general.undo() + TestHelper.wait_for_condition(lambda: not postfx_shape_weight_entity.has_component(shape), 1.0) + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 10. Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component. + postfx_shape_weight_entity.add_components(['Spline', 'Tube Shape']) + Report.result(Tests.tube_shape_component, postfx_shape_weight_entity.has_component('Tube Shape')) + Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) + + # 11. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 12. Test IsHidden. + postfx_shape_weight_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, postfx_shape_weight_entity.is_hidden() is True) + + # 13. Test IsVisible. + postfx_shape_weight_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, postfx_shape_weight_entity.is_visible() is True) + + # 14. Delete PostFx Shape Weight Modifier entity. + postfx_shape_weight_entity.delete() + Report.result(Tests.entity_deleted, not postfx_shape_weight_entity.exists()) + + # 15. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists()) + + # 16. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists()) + + # 17. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_postfx_shape_weight_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py new file mode 100644 index 0000000000..9b13eb2c7e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py @@ -0,0 +1,194 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + reflection_probe_creation = ( + "Reflection Probe Entity successfully created", + "Reflection Probe Entity failed to be created") + reflection_probe_component = ( + "Entity has a Reflection Probe component", + "Entity failed to find Reflection Probe component") + reflection_probe_disabled = ( + "Reflection Probe component disabled", + "Reflection Probe component was not disabled.") + reflection_map_generated = ( + "Reflection Probe cubemap generated", + "Reflection Probe cubemap not generated") + box_shape_component = ( + "Entity has a Box Shape component", + "Entity did not have a Box Shape component") + reflection_probe_enabled = ( + "Reflection Probe component enabled", + "Reflection Probe component was not enabled.") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_ReflectionProbe_AddedToEntity(): + """ + Summary: + Tests the Reflection Probe component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Reflection Probe entity with no components. + 2) Add a Reflection Probe component to Reflection Probe entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify Reflection Probe component not enabled. + 6) Add Shape component since it is required by the Reflection Probe component. + 7) Verify Reflection Probe component is enabled. + 8) Enter/Exit game mode. + 9) Test IsHidden. + 10) Test IsVisible. + 11) Verify cubemap generation + 12) Delete Reflection Probe entity. + 13) UNDO deletion. + 14) REDO deletion. + 15) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.render as render + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Reflection Probe entity with no components. + reflection_probe_name = "Reflection Probe" + reflection_probe_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), reflection_probe_name) + Report.critical_result(Tests.reflection_probe_creation, reflection_probe_entity.exists()) + + # 2. Add a Reflection Probe component to Reflection Probe entity. + reflection_probe_component = reflection_probe_entity.add_component(reflection_probe_name) + Report.critical_result( + Tests.reflection_probe_component, + reflection_probe_entity.has_component(reflection_probe_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not reflection_probe_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, reflection_probe_entity.exists()) + + # 5. Verify Reflection Probe component not enabled. + Report.result(Tests.reflection_probe_disabled, not reflection_probe_component.is_enabled()) + + # 6. Add Box Shape component since it is required by the Reflection Probe component. + box_shape = "Box Shape" + reflection_probe_entity.add_component(box_shape) + Report.result(Tests.box_shape_component, reflection_probe_entity.has_component(box_shape)) + + # 7. Verify Reflection Probe component is enabled. + Report.result(Tests.reflection_probe_enabled, reflection_probe_component.is_enabled()) + + # 8. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 9. Test IsHidden. + reflection_probe_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, reflection_probe_entity.is_hidden() is True) + + # 10. Test IsVisible. + reflection_probe_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, reflection_probe_entity.is_visible() is True) + + # 11. Verify cubemap generation + render.EditorReflectionProbeBus(azlmbr.bus.Event, "BakeReflectionProbe", reflection_probe_entity.id) + Report.result( + Tests.reflection_map_generated, + helper.wait_for_condition( + lambda: reflection_probe_component.get_component_property_value("Cubemap|Baked Cubemap Path") != "", + 20.0)) + + # 12. Delete Reflection Probe entity. + reflection_probe_entity.delete() + Report.result(Tests.entity_deleted, not reflection_probe_entity.exists()) + + # 13. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, reflection_probe_entity.exists()) + + # 14. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not reflection_probe_entity.exists()) + + # 15. Look for errors or asserts. + helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_ReflectionProbe_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py index 92199bf196..fbd9f3459a 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py @@ -66,7 +66,6 @@ def run(): general.close_pane("Error Log") general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) return True diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py index ac28e67fa1..93e78a7c0a 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py @@ -82,7 +82,6 @@ def run(): general.close_pane("Error Log") general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) return True diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py index ef3048d8d4..1b094b1cfa 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -149,7 +149,6 @@ class TestHelper: general.idle_wait(1.0) general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py index da89316993..e5dd9adbb9 100755 --- a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py @@ -525,10 +525,6 @@ class TestAutomation(TestAutomationBase): from .tests.joints import Joints_BallNoLimitsConstrained as test_module self._run_test(request, workspace, editor, test_module) - def test_Joints_FixedLeadFollowerCollide(self, request, workspace, editor, launcher_platform): - from .tests.joints import Joints_FixedLeadFollowerCollide as test_module - self._run_test(request, workspace, editor, test_module) - def test_Joints_GlobalFrameConstrained(self, request, workspace, editor, launcher_platform): from .tests.joints import Joints_GlobalFrameConstrained as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py index abc58779a2..e949d2d8f9 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py @@ -82,7 +82,7 @@ def ForceRegion_SplineRegionWithModifiedTransform(): import azlmbr.bus as bus # region Constants - TIMEOUT = 5.0 + TIMEOUT = 10.0 MIN_TRIGGER_DISTANCE = 2.0 # endregion diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py index 91190f83e5..6582628ff9 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py @@ -77,7 +77,7 @@ def ForceRegion_ZeroPointForceDoesNothing(): helper.init_idle() - TIMEOUT_SECONDS = 3.0 + TIMEOUT_SECONDS = 5.0 X_Y_Z_TOLERANCE = 1.5 REGION_HEIGHT = 3.0 TERRAIN_HEIGHT = 32.0 diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py index 6226f42534..a85e460659 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py @@ -45,8 +45,13 @@ class JointEntity: # Entity class that sets a flag when an instance receives collision events. class JointEntityCollisionAware(JointEntity): def on_collision_begin(self, args): - if not self.collided: - self.collided = True + self.collided = True + + def on_collision_persist(self, args): + self.collided = True + + def on_collision_end(self, args): + self.collided = True def __init__(self, name): self.id = general.find_game_entity(name) @@ -58,3 +63,5 @@ class JointEntityCollisionAware(JointEntity): self.handler = azlmbr.physics.CollisionNotificationBusHandler() self.handler.connect(self.id) self.handler.add_callback("OnCollisionBegin", self.on_collision_begin) + self.handler.add_callback("OnCollisionPresist", self.on_collision_persist) + self.handler.add_callback("OnCollisionEnd", self.on_collision_end) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py index 0e8d7ea255..1209cb6caf 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py @@ -52,9 +52,6 @@ def Joints_BallLeadFollowerCollide(): from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper - import azlmbr.legacy.general as general - import azlmbr.bus - from JointsHelper import JointEntityCollisionAware # Helper Entity class - self.collided flag is set when instance receives collision event. @@ -75,17 +72,12 @@ def Joints_BallLeadFollowerCollide(): lead = Entity("lead") follower = Entity("follower") - # 4) Wait for several seconds - general.idle_wait(2.0) # wait for lead and follower to move + # 4) Wait for collision between lead and follower or timeout + Report.critical_result(Tests.check_collision_happened, helper.wait_for_condition(lambda: lead.collided and follower.collided, 10.0)) - # 5) Check to see if lead and follower behaved as expected - Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided) - - # 6) Exit Game Mode + # 5) Exit Game Mode helper.exit_game_mode(Tests.exit_game_mode) - - if __name__ == "__main__": from editor_python_test_tools.utils import Report Report.start_test(Joints_BallLeadFollowerCollide) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_FixedLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_FixedLeadFollowerCollide.py deleted file mode 100644 index e142941c5a..0000000000 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_FixedLeadFollowerCollide.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT -""" - -# Test case ID : C18243582 -# Test Case Title : Check that fixed joint allows lead-follower collision - - -# fmt: off -class Tests: - enter_game_mode = ("Entered game mode", "Failed to enter game mode") - exit_game_mode = ("Exited game mode", "Couldn't exit game mode") - lead_found = ("Found lead", "Did not find lead") - follower_found = ("Found follower", "Did not find follower") - check_collision_happened = ("Lead and follower collided", "Lead and follower did not collide") -# fmt: on - - -def Joints_FixedLeadFollowerCollide(): - """ - Summary: Check that fixed joint allows lead-follower collision - - Level Description: - lead - Starts above follower entity - follower - Starts below lead entity. Constrained to lead entity with fixed joint. Starts with initial velocity of (5, 0, 0) in positive X direction. - - Expected Behavior: - The follower entity moves in the positive X direction and the lead entity is dragged along towards the positive X direction. - The x position of the lead entity is incremented from its original. - The lead and follower entities are kept apart at a distance of approximately 1.0 due to collision. - - Test Steps: - 1) Open Level - 2) Enter Game Mode - 3) Create and Validate Entities - 4) Wait for several seconds - 5) Check to see if lead and follower behaved as expected. - 6) Exit Game Mode - 7) Close Editor - - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. - - :return: None - """ - import os - import sys - from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper - - import azlmbr.legacy.general as general - import azlmbr.bus - - from JointsHelper import JointEntityCollisionAware - - # Helper Entity class - self.collided flag is set when instance receives collision event. - class Entity(JointEntityCollisionAware): - def criticalEntityFound(self): # Override function to use local Test dictionary - Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid()) - - # Main Script - helper.init_idle() - - # 1) Open Level - helper.open_level("Physics", "Joints_FixedLeadFollowerCollide") - - # 2) Enter Game Mode - helper.enter_game_mode(Tests.enter_game_mode) - - # 3) Create and Validate Entities - lead = Entity("lead") - follower = Entity("follower") - - # 4) Wait for several seconds - general.idle_wait(2.0) # wait for lead and follower to move - - # 5) Check to see if lead entity and follower collided - Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided) - - # 6) Exit Game Mode - helper.exit_game_mode(Tests.exit_game_mode) - - - -if __name__ == "__main__": - from editor_python_test_tools.utils import Report - Report.start_test(Joints_FixedLeadFollowerCollide) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py index 0870a807fc..0b3f88b4f6 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py @@ -52,11 +52,13 @@ def Joints_HingeNoLimitsConstrained(): """ import os import sys + import math from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper import azlmbr.legacy.general as general import azlmbr.bus + import azlmbr.math as azmath import JointsHelper from JointsHelper import JointEntity @@ -84,28 +86,65 @@ def Joints_HingeNoLimitsConstrained(): Report.info_vector3(lead.position, "lead initial position:") Report.info_vector3(follower.position, "follower initial position:") leadInitialPosition = lead.position - followerInitialPosition = follower.position - # 4) Wait for several seconds - general.idle_wait(4.0) # wait for lead and follower to move + # 4) Wait for the follower to move above and over the lead or Timeout + normalizedStartPos = JointsHelper.getRelativeVector(lead.position, follower.position) + normalizedStartPos = normalizedStartPos.GetNormalizedSafe() + + class WaitCondition: + ANGLE_CHECKPOINT_1 = math.radians(90) + ANGLE_CHECKPOINT_2 = math.radians(200) + + angleAchieved = 0.0 + followerMovedAbove90Deg = False #this is expected to be true to pass the test + followerMovedAbove200Deg = False #this is expected to be true to pass the test + + jointNormal = azmath.Vector3(0.0, -1.0, 0.0) # the joint rotates around the y axis + def checkConditionMet(self): + #calculate the current follower-lead vector + normalVec = JointsHelper.getRelativeVector(lead.position, follower.position) + normalVec = normalVec.GetNormalizedSafe() + + #triple product and get the angle + tripleProduct = normalizedStartPos.dot(normalVec.cross(self.jointNormal)) + currentAngle = math.acos(normalizedStartPos.Dot(normalVec)) + if tripleProduct < 0: + currentAngle = (2*math.pi) - currentAngle + + #if the angle is now less then last time, it is no longer rising, so end the test. + if currentAngle < self.angleAchieved: + return True + + #once we're passed the final check point, end the test + if currentAngle > self.ANGLE_CHECKPOINT_2: + self.followerMovedAbove200Deg = True + return True + + self.angleAchieved = currentAngle + self.followerMovedAbove90Deg = currentAngle > self.ANGLE_CHECKPOINT_1 + return False + + def isFollowerPositionCorrect(self): + return self.followerMovedAbove90Deg and self.followerMovedAbove200Deg + + waitCondition = WaitCondition() + + MAX_WAIT_TIME = 10.0 #seconds + conditionMet = helper.wait_for_condition(lambda: waitCondition.checkConditionMet(), MAX_WAIT_TIME) # 5) Check to see if lead and follower behaved as expected - Report.info_vector3(lead.position, "lead position after 1 second:") - Report.info_vector3(follower.position, "follower position after 1 second:") + Report.info_vector3(lead.position, "lead position after test run:") + Report.info_vector3(follower.position, "follower position after test run:") leadPositionDelta = lead.position.Subtract(leadInitialPosition) leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON) Report.critical_result(Tests.check_lead_position, leadRemainedStill) - followerSwingedOverLead = (follower.position.x < leadInitialPosition.x and - follower.position.z > leadInitialPosition.z) - Report.critical_result(Tests.check_follower_position, followerSwingedOverLead) + Report.critical_result(Tests.check_follower_position, conditionMet and waitCondition.isFollowerPositionCorrect()) # 6) Exit Game Mode helper.exit_game_mode(Tests.exit_game_mode) - - if __name__ == "__main__": from editor_python_test_tools.utils import Report Report.start_test(Joints_HingeNoLimitsConstrained) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py index 1c53854bab..05671389c2 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py @@ -80,6 +80,20 @@ def RigidBody_KinematicModeWorks(): ramp_id = general.find_game_entity("Ramp") Report.result(Tests.find_ramp, ramp_id.IsValid()) + # 2.1) setup collision handler + class RampTouched: + value = False + + def on_collision_begin(args): + other_id = args[0] + if other_id.Equal(ramp_id): + Report.info("Box touched ramp") + RampTouched.value = True + + handler = azlmbr.physics.CollisionNotificationBusHandler() + handler.connect(box_id) + handler.add_callback("OnCollisionBegin", on_collision_begin) + # 3) Check for kinematic ramp and not kinematic box box_kinematic = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", box_id) Report.result(Tests.box_is_not_kinematic, not box_kinematic) @@ -98,21 +112,7 @@ def RigidBody_KinematicModeWorks(): ramp_pos_start = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", ramp_id) Report.info("Ramp's initial position: {}".format(ramp_pos_start)) - # 6) Check to see that the box hits the ramp - class RampTouched: - value = False - - def on_collision_begin(args): - other_id = args[0] - if other_id.Equal(ramp_id): - Report.info("Box touched ramp") - RampTouched.value = True - - handler = azlmbr.physics.CollisionNotificationBusHandler() - handler.connect(box_id) - handler.add_callback("OnCollisionBegin", on_collision_begin) - - # 6.5) Wait for the box to touch the ramp or timeout + # 6) Wait for the box to touch the ramp or timeout helper.wait_for_condition(lambda: RampTouched.value, TIME_OUT) Report.result(Tests.box_touched_ramp, RampTouched.value) diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C30936451/test_texture_sequence000.png.exportsettings b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C30936451/test_texture_sequence000.png.exportsettings deleted file mode 100644 index a8fbf72992..0000000000 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C30936451/test_texture_sequence000.png.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Terrain_Albedo_HighPassed /reduce=0 \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py index 49cf17855f..22fa3aca0d 100755 --- a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py +++ b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py @@ -187,7 +187,3 @@ def prepare_for_screenshot_compare(remote_console_instance): """ wait_for(lambda: _retry_command(remote_console_instance, 'r_displayinfo 0', '$3r_DisplayInfo = $60 $5[DUMPTODISK, RESTRICTEDMODE]$4')) - wait_for(lambda: _retry_command(remote_console_instance, 'r_antialiasingmode 0', - '$3r_AntialiasingMode = $60 $5[]$4')) - wait_for(lambda: _retry_command(remote_console_instance, 'e_WaterOcean 0', - '$3e_WaterOcean = $60 $5[]$4')) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py new file mode 100644 index 0000000000..39cacf9af5 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py @@ -0,0 +1,101 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + + +class Tests: + load_level = ( + "Level loaded successfully", + "Failed to load the level" + ) + create_entity = ( + "Parent entity created successfully", + "Failed to create a parent entity" + ) + set_entity_name = ( + "Entity name set successfully", + "Failed to set entity name" + ) + delete_entity = ( + "Parent Entity deleted successfully", + "Failed to delete parent entity" + ) + create_child_entity = ( + "Child entity created successfully", + "Failed to create a child entity" + ) + delete_child_entity = ( + "Child entity deleted successfully", + "Failed to delete child entity" + ) + add_mesh_component = ( + "Mesh component added successfully", + "Failed to add mesh component" + ) + found_component_typeId = ( + "Found component typeId", + "Unable to find component TypeId" + ) + remove_mesh_component = ( + "Mesh component removed successfully", + "Failed to remove mesh component" + ) + + +def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): + """ + Performing basic test in editor + 01. Load exiting level + 02. create parent entity and set name + 03. create child entity and set a name + 04. delete child entity + 05. add mesh component to parent entity + 06. delete parent entity + """ + + from editor_python_test_tools.utils import Report + from editor_python_test_tools.editor_entity_utils import EditorEntity + + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.entity as entity + import azlmbr.legacy.general as general + import azlmbr.object + + # 01. load an existing level + test_level = 'Simple' + general.open_level_no_prompt(test_level) + Report.result(Tests.load_level, general.get_current_level_name() == test_level) + + # 02. create parent entity and set name + # Delete any exiting entity and Create a new Entity at the root level + search_filter = azlmbr.entity.SearchFilter() + all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) + editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) + parent_entity = EditorEntity.create_editor_entity("Parent_1") + Report.result(Tests.create_entity, parent_entity.exists()) + + # 03. Create child Entity to above created parent entity and set a name + child_1_entity = EditorEntity.create_editor_entity("Child_1", parent_entity.id ) + Report.result(Tests.create_child_entity, child_1_entity.exists()) + + # 04. delete_Child_entity + child_1_entity.delete() + Report.result(Tests.delete_child_entity, not child_1_entity.exists()) + + # 05. add mesh component to parent entity + parent_entity.add_component("Mesh") + Report.result(Tests.add_mesh_component, parent_entity.has_component("Mesh")) + + # 06. delete parent entity + parent_entity.delete() + Report.result(Tests.delete_entity, not parent_entity.exists()) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + + Report.start_test(BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index 9c0b99daff..afc52f962d 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -73,3 +73,7 @@ class TestAutomationAutoTestMode(EditorTestSuite): @pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208") class test_Menus_FileMenuOptions_Work(EditorSharedTest): from .EditorScripts import Menus_FileMenuOptions as test_module + + + class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest): + from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt index d5c7fd5109..c2123d683c 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt @@ -55,32 +55,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ ## LandscapeCanvas ## - ly_add_pytest( - NAME AutomatedTesting::LandscapeCanvasTests_Main - TEST_SERIAL - TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/TestSuite_Main.py - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - - ly_add_pytest( - NAME AutomatedTesting::LandscapeCanvasTests_Periodic - TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/TestSuite_Periodic.py - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - ly_add_pytest( NAME AutomatedTesting::LandscapeCanvasTests_Main_Optimized TEST_SERIAL diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py index 9cc572ad69..0461ff2647 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py @@ -12,7 +12,6 @@ import ly_test_tools.environment.file_system as file_system from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite -@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/Joints_FixedLeadFollowerCollide.ly b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/Joints_FixedLeadFollowerCollide.ly deleted file mode 100644 index 0c3ac9e668..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/Joints_FixedLeadFollowerCollide.ly +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3093fee5317ba6fb353a435c09f43f13c5e722421aae62a297f699c202d6129e -size 6699 diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/filelist.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/filelist.xml deleted file mode 100644 index d0960f8154..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/filelist.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/level.pak b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/level.pak deleted file mode 100644 index 532be2c1bd..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/level.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b1daa050e732ff0594601bbfca8ac9ed05972c2f9fa3e43dbc8645e802ed2730 -size 7959 diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/Environment.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/Environment.xml deleted file mode 100644 index 4ba36f66ae..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/Environment.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TerrainTexture.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TerrainTexture.xml deleted file mode 100644 index f43df05b22..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TerrainTexture.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TimeOfDay.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TimeOfDay.xml deleted file mode 100644 index 456d609b8a..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TimeOfDay.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/VegetationMap.dat b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/VegetationMap.dat deleted file mode 100644 index dce5631cd0..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/VegetationMap.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9 -size 63 diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/tags.txt b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/tags.txt deleted file mode 100644 index 0d6c1880e7..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/tags.txt +++ /dev/null @@ -1,12 +0,0 @@ -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/terraintexture.pak b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/terraintexture.pak deleted file mode 100644 index fe3604a050..0000000000 --- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/terraintexture.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d85 -size 22 diff --git a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly index 3d17ae7ccc..9babf84ba4 100644 --- a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly +++ b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:012caffa9354f6253d2e626ba183b44eb02a74eba286fde0430227ef024411e2 -size 8961 +oid sha256:60276c07b45a734e4f71d695278167ea61e884f8b513906168c9642078ad5954 +size 6045 diff --git a/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly b/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly index 92e6788bf0..d6052ffa7c 100644 --- a/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly +++ b/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:619bcf0c2a2b08f4ffe05e60858c479828fe39d5530f4e488b9c0ec8a585ccb7 -size 7735 +oid sha256:cb97ada674d123c67d7a32eb65e81fa66472cf51f748054c4a4297649f2a0f40 +size 5568 diff --git a/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings b/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings deleted file mode 100644 index a4e1a9a3c5..0000000000 --- a/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material b/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material new file mode 100644 index 0000000000..878b3ac39f --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material @@ -0,0 +1,13 @@ +{ + "description": "", + "materialType": "Materials/Types/Skin.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "wrinkleLayers": { + "count": 3, + "enable": true, + "showBlendValues": true + } + } +} diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor.fbx b/AutomatedTesting/Objects/MorphTargets/morphActor.fbx new file mode 100644 index 0000000000..ffb75e680e --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53e17ec8155911c8b42e85436130f600bd6dddd8931a8ccb1b2f8a9f8674cc85 +size 45104 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif new file mode 100644 index 0000000000..3bc18cf450 --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0da56a05daa0ec1c476cfe25ca6d3b65267c98886cf33408f6e852fb325a8e2c +size 198084 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif new file mode 100644 index 0000000000..39e70f5acf --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3537fbe9205731a242251c525a67bbb5f3b8f5307537f1dc0c318b5b885ce52 +size 198112 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif new file mode 100644 index 0000000000..43e19f0d5f --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd794d5dd4b749c3275bfab79b9b5ae3f8e007d3e6741c0566c9c2d3931123bf +size 198112 diff --git a/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx b/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx new file mode 100644 index 0000000000..c0dcc007dc --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45ded862987a64061deffd8e4c9aa1dff4eec3bcff5f7b505679f1959e8ae137 +size 51440 diff --git a/AutomatedTesting/Registry/assets_scan_folders.setreg b/AutomatedTesting/Registry/assets_scan_folders.setreg index c74ba6703e..3043533b59 100644 --- a/AutomatedTesting/Registry/assets_scan_folders.setreg +++ b/AutomatedTesting/Registry/assets_scan_folders.setreg @@ -10,13 +10,6 @@ "Gems/DevTextures" ] }, - "PBSreferenceMaterials": - { - "SourcePaths": - [ - "Gems/PBSreferenceMaterials" - ] - }, "PhysicsEntities": { "SourcePaths": diff --git a/AutomatedTesting/textures/lights/flare01.tif.exportsettings b/AutomatedTesting/textures/lights/flare01.tif.exportsettings deleted file mode 100644 index 4fee4cfc4b..0000000000 --- a/AutomatedTesting/textures/lights/flare01.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=LensOptics /reduce=-1 \ No newline at end of file diff --git a/AutomatedTesting/textures/milestone2/AMA_Grey_01.tif.exportsettings b/AutomatedTesting/textures/milestone2/AMA_Grey_01.tif.exportsettings deleted file mode 100644 index 2d1dccbf99..0000000000 --- a/AutomatedTesting/textures/milestone2/AMA_Grey_01.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Albedo /reduce=0 \ No newline at end of file diff --git a/AutomatedTesting/textures/milestone2/AMA_Grey_02.tif.exportsettings b/AutomatedTesting/textures/milestone2/AMA_Grey_02.tif.exportsettings deleted file mode 100644 index a8fbf72992..0000000000 --- a/AutomatedTesting/textures/milestone2/AMA_Grey_02.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Terrain_Albedo_HighPassed /reduce=0 \ No newline at end of file diff --git a/AutomatedTesting/textures/milestone2/AMA_Grey_03.tif.exportsettings b/AutomatedTesting/textures/milestone2/AMA_Grey_03.tif.exportsettings deleted file mode 100644 index a8fbf72992..0000000000 --- a/AutomatedTesting/textures/milestone2/AMA_Grey_03.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Terrain_Albedo_HighPassed /reduce=0 \ No newline at end of file diff --git a/AutomatedTesting/textures/milestone2/particles/FX_LauncherMuzzleFront_01.tif.exportsettings b/AutomatedTesting/textures/milestone2/particles/FX_LauncherMuzzleFront_01.tif.exportsettings deleted file mode 100644 index be29bd9bc0..0000000000 --- a/AutomatedTesting/textures/milestone2/particles/FX_LauncherMuzzleFront_01.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=AlbedoWithOpacity /reduce=0 \ No newline at end of file diff --git a/AutomatedTesting/textures/milestone2/particles/FX_LauncherMuzzleRing_01.tif.exportsettings b/AutomatedTesting/textures/milestone2/particles/FX_LauncherMuzzleRing_01.tif.exportsettings deleted file mode 100644 index be29bd9bc0..0000000000 --- a/AutomatedTesting/textures/milestone2/particles/FX_LauncherMuzzleRing_01.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=AlbedoWithOpacity /reduce=0 \ No newline at end of file diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 2ea0aa1a74..bdfac373eb 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -102,7 +102,7 @@ ly_add_target( 3rdParty::Qt::Gui 3rdParty::Qt::Widgets 3rdParty::Qt::Concurrent - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::squish-ccr 3rdParty::AWSNativeSDK::STS Legacy::CryCommon diff --git a/Code/Editor/Controls/FolderTreeCtrl.cpp b/Code/Editor/Controls/FolderTreeCtrl.cpp index 4088ab976f..1464580ffc 100644 --- a/Code/Editor/Controls/FolderTreeCtrl.cpp +++ b/Code/Editor/Controls/FolderTreeCtrl.cpp @@ -279,7 +279,7 @@ void CFolderTreeCtrl::LoadTreeRec(const QString& currentFolder) void CFolderTreeCtrl::AddItem(const QString& path) { AZ::IO::FixedMaxPath folder{ AZ::IO::PathView(path.toUtf8().constData()) }; - AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Extension(); + AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Stem(); folder = folder.ParentPath(); auto regex = QRegExp(m_fileNameSpec, Qt::CaseInsensitive, QRegExp::Wildcard); diff --git a/Code/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Editor/Core/LevelEditorMenuHandler.cpp index e568f05167..fdde1ac305 100644 --- a/Code/Editor/Core/LevelEditorMenuHandler.cpp +++ b/Code/Editor/Core/LevelEditorMenuHandler.cpp @@ -32,6 +32,7 @@ #include // AzToolsFramework +#include #include // AzQtComponents @@ -166,15 +167,14 @@ LevelEditorMenuHandler::LevelEditorMenuHandler(MainWindow* mainWindow, QtViewPan m_mainWindow->menuBar()->setNativeMenuBar(true); #endif - ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( - AzToolsFramework::GetEntityContextId()); + ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId()); EditorMenuRequestBus::Handler::BusConnect(); } LevelEditorMenuHandler::~LevelEditorMenuHandler() { EditorMenuRequestBus::Handler::BusDisconnect(); - ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); + ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); } void LevelEditorMenuHandler::Initialize() @@ -487,8 +487,6 @@ void LevelEditorMenuHandler::PopulateEditMenu(ActionManager::MenuWrapper& editMe editMenu.AddAction(AzToolsFramework::EditPivot); editMenu.AddAction(AzToolsFramework::EditReset); editMenu.AddAction(AzToolsFramework::EditResetManipulator); - editMenu.AddAction(AzToolsFramework::EditResetLocal); - editMenu.AddAction(AzToolsFramework::EditResetWorld); // Hide Selection editMenu.AddAction(AzToolsFramework::HideSelection); @@ -1186,36 +1184,44 @@ void LevelEditorMenuHandler::AddDisableActionInSimModeListener(QAction* action) })); } -void LevelEditorMenuHandler::EnteredComponentMode(const AZStd::vector& /*componentModeTypes*/) +void LevelEditorMenuHandler::OnEditorModeActivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - auto menuWrapper = m_actionManager->FindMenu(s_editMenuId); - if (!menuWrapper.isNull()) + if (mode == ViewportEditorMode::Component) { - // copy of menu actions - auto actions = menuWrapper.Get()->actions(); - // remove all non-reserved edit menu options - actions.erase( - std::remove_if(actions.begin(), actions.end(), [](QAction* action) - { - return !action->property("Reserved").toBool(); - }), - actions.end()); + if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId); + !menuWrapper.isNull()) + { + // copy of menu actions + auto actions = menuWrapper.Get()->actions(); + // remove all non-reserved edit menu options + actions.erase( + std::remove_if(actions.begin(), actions.end(), [](QAction* action) + { + return !action->property("Reserved").toBool(); + }), + actions.end()); - // clear and update the menu with new actions - menuWrapper.Get()->clear(); - menuWrapper.Get()->addActions(actions); + // clear and update the menu with new actions + menuWrapper.Get()->clear(); + menuWrapper.Get()->addActions(actions); + } } } -void LevelEditorMenuHandler::LeftComponentMode(const AZStd::vector& /*componentModeTypes*/) +void LevelEditorMenuHandler::OnEditorModeDeactivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - RestoreEditMenuToDefault(); + if (mode == ViewportEditorMode::Component) + { + RestoreEditMenuToDefault(); + } } void LevelEditorMenuHandler::AddEditMenuAction(QAction* action) { - auto menuWrapper = m_actionManager->FindMenu(s_editMenuId); - if (!menuWrapper.isNull()) + if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId); + !menuWrapper.isNull()) { menuWrapper.Get()->addAction(action); } @@ -1239,8 +1245,8 @@ void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QActio void LevelEditorMenuHandler::RestoreEditMenuToDefault() { - auto menuWrapper = m_actionManager->FindMenu(s_editMenuId); - if (!menuWrapper.isNull()) + if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId); + !menuWrapper.isNull()) { menuWrapper.Get()->clear(); PopulateEditMenu(menuWrapper); diff --git a/Code/Editor/Core/LevelEditorMenuHandler.h b/Code/Editor/Core/LevelEditorMenuHandler.h index 5ac8e63786..ff03c7748c 100644 --- a/Code/Editor/Core/LevelEditorMenuHandler.h +++ b/Code/Editor/Core/LevelEditorMenuHandler.h @@ -18,7 +18,7 @@ #include #include "ActionManager.h" #include "QtViewPaneManager.h" -#include +#include #endif class MainWindow; @@ -28,7 +28,7 @@ struct QtViewPane; class LevelEditorMenuHandler : public QObject - , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler + , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler , private AzToolsFramework::EditorMenuRequestBus::Handler { Q_OBJECT @@ -88,9 +88,11 @@ private: void AddDisableActionInSimModeListener(QAction* action); - // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; // EditorMenuRequestBus void AddEditMenuAction(QAction* action) override; diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index d2a448bd34..4f2e995ac4 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -2125,6 +2125,8 @@ bool CCryEditApp::FixDanglingSharedMemory(const QString& sharedMemName) const int CCryEditApp::ExitInstance(int exitCode) { + AZ_TracePrintf("Exit", "Called ExitInstance() with exit code: 0x%x", exitCode); + if (m_pEditor) { m_pEditor->OnBeginShutdownSequence(); diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp index 75bd0ad270..4944c3bff5 100644 --- a/Code/Editor/CryEditDoc.cpp +++ b/Code/Editor/CryEditDoc.cpp @@ -1273,7 +1273,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) if (savedEntities) { AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Updated PakFile levelEntities.editor_xml"); - pakFile.UpdateFile("LevelEntities.editor_xml", entitySaveBuffer.begin(), static_cast(entitySaveBuffer.size())); + pakFile.UpdateFile("levelentities.editor_xml", entitySaveBuffer.begin(), static_cast(entitySaveBuffer.size())); // Save XML archive to pak file. bool bSaved = xmlAr.SaveToPak(Path::GetPath(tempSaveFile), pakFile); @@ -1501,7 +1501,7 @@ bool CCryEditDoc::LoadEntitiesFromLevel(const QString& levelPakFile) bool pakOpened = pakSystem->OpenPack(levelPakFile.toUtf8().data()); if (pakOpened) { - const QString entityFilename = Path::GetPath(levelPakFile) + "LevelEntities.editor_xml"; + const QString entityFilename = Path::GetPath(levelPakFile) + "levelentities.editor_xml"; CCryFile entitiesFile; if (entitiesFile.Open(entityFilename.toUtf8().data(), "rt")) diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index c492010336..ce4a0a2e33 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -96,7 +96,7 @@ namespace SandboxEditor cameras.AddCamera(m_firstPersonTranslateCamera); cameras.AddCamera(m_firstPersonScrollCamera); cameras.AddCamera(m_firstPersonFocusCamera); - cameras.AddCamera(m_pivotCamera); + cameras.AddCamera(m_orbitCamera); }); return controller; @@ -135,7 +135,7 @@ namespace SandboxEditor m_firstPersonRotateCamera->SetActivationEndedFn(showCursor); m_firstPersonPanCamera = AZStd::make_shared( - SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivot); + SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivotLook); m_firstPersonPanCamera->m_panSpeedFn = [] { @@ -155,7 +155,7 @@ namespace SandboxEditor const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds(); m_firstPersonTranslateCamera = AZStd::make_shared( - translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot); + translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook); m_firstPersonTranslateCamera->m_translateSpeedFn = [] { @@ -167,7 +167,7 @@ namespace SandboxEditor return SandboxEditor::CameraBoostMultiplier(); }; - m_firstPersonScrollCamera = AZStd::make_shared(); + m_firstPersonScrollCamera = AZStd::make_shared(); m_firstPersonScrollCamera->m_scrollSpeedFn = [] { @@ -196,82 +196,82 @@ namespace SandboxEditor m_firstPersonFocusCamera->SetPivotFn(pivotFn); - m_pivotCamera = AZStd::make_shared(SandboxEditor::CameraPivotChannelId()); + m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId()); - m_pivotCamera->SetPivotFn( + m_orbitCamera->SetPivotFn( [pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { return pivotFn(); }); - m_pivotRotateCamera = AZStd::make_shared(SandboxEditor::CameraPivotLookChannelId()); + m_orbitRotateCamera = AZStd::make_shared(SandboxEditor::CameraOrbitLookChannelId()); - m_pivotRotateCamera->m_rotateSpeedFn = [] + m_orbitRotateCamera->m_rotateSpeedFn = [] { return SandboxEditor::CameraRotateSpeed(); }; - m_pivotRotateCamera->m_invertYawFn = [] + m_orbitRotateCamera->m_invertYawFn = [] { - return SandboxEditor::CameraPivotYawRotationInverted(); + return SandboxEditor::CameraOrbitYawRotationInverted(); }; - m_pivotTranslateCamera = AZStd::make_shared( - translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffset); + m_orbitTranslateCamera = AZStd::make_shared( + translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffsetOrbit); - m_pivotTranslateCamera->m_translateSpeedFn = [] + m_orbitTranslateCamera->m_translateSpeedFn = [] { return SandboxEditor::CameraTranslateSpeed(); }; - m_pivotTranslateCamera->m_boostMultiplierFn = [] + m_orbitTranslateCamera->m_boostMultiplierFn = [] { return SandboxEditor::CameraBoostMultiplier(); }; - m_pivotDollyScrollCamera = AZStd::make_shared(); + m_orbitDollyScrollCamera = AZStd::make_shared(); - m_pivotDollyScrollCamera->m_scrollSpeedFn = [] + m_orbitDollyScrollCamera->m_scrollSpeedFn = [] { return SandboxEditor::CameraScrollSpeed(); }; - m_pivotDollyMoveCamera = AZStd::make_shared(SandboxEditor::CameraPivotDollyChannelId()); + m_orbitDollyMoveCamera = AZStd::make_shared(SandboxEditor::CameraOrbitDollyChannelId()); - m_pivotDollyMoveCamera->m_motionSpeedFn = [] + m_orbitDollyMoveCamera->m_motionSpeedFn = [] { return SandboxEditor::CameraDollyMotionSpeed(); }; - m_pivotPanCamera = AZStd::make_shared( - SandboxEditor::CameraPivotPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffset); + m_orbitPanCamera = AZStd::make_shared( + SandboxEditor::CameraOrbitPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffsetOrbit); - m_pivotPanCamera->m_panSpeedFn = [] + m_orbitPanCamera->m_panSpeedFn = [] { return SandboxEditor::CameraPanSpeed(); }; - m_pivotPanCamera->m_invertPanXFn = [] + m_orbitPanCamera->m_invertPanXFn = [] { return SandboxEditor::CameraPanInvertedX(); }; - m_pivotPanCamera->m_invertPanYFn = [] + m_orbitPanCamera->m_invertPanYFn = [] { return SandboxEditor::CameraPanInvertedY(); }; - m_pivotFocusCamera = - AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusPivot); + m_orbitFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusOrbit); - m_pivotFocusCamera->SetPivotFn(pivotFn); + m_orbitFocusCamera->SetPivotFn(pivotFn); - m_pivotCamera->m_pivotCameras.AddCamera(m_pivotRotateCamera); - m_pivotCamera->m_pivotCameras.AddCamera(m_pivotTranslateCamera); - m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyScrollCamera); - m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyMoveCamera); - m_pivotCamera->m_pivotCameras.AddCamera(m_pivotPanCamera); - m_pivotCamera->m_pivotCameras.AddCamera(m_pivotFocusCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyMoveCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitFocusCamera); } void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged() @@ -282,12 +282,12 @@ namespace SandboxEditor m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId()); m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); - m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId()); - m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); - m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId()); - m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId()); - m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId()); - m_pivotFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); + m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId()); + m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); + m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId()); + m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId()); + m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId()); + m_orbitFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); } void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index 90103dba43..9cfd6f3554 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -41,15 +41,15 @@ namespace SandboxEditor AZStd::shared_ptr m_firstPersonRotateCamera; AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; - AZStd::shared_ptr m_firstPersonScrollCamera; + AZStd::shared_ptr m_firstPersonScrollCamera; AZStd::shared_ptr m_firstPersonFocusCamera; - AZStd::shared_ptr m_pivotCamera; - AZStd::shared_ptr m_pivotRotateCamera; - AZStd::shared_ptr m_pivotTranslateCamera; - AZStd::shared_ptr m_pivotDollyScrollCamera; - AZStd::shared_ptr m_pivotDollyMoveCamera; - AZStd::shared_ptr m_pivotPanCamera; - AZStd::shared_ptr m_pivotFocusCamera; + AZStd::shared_ptr m_orbitCamera; + AZStd::shared_ptr m_orbitRotateCamera; + AZStd::shared_ptr m_orbitTranslateCamera; + AZStd::shared_ptr m_orbitDollyScrollCamera; + AZStd::shared_ptr m_orbitDollyMoveCamera; + AZStd::shared_ptr m_orbitPanCamera; + AZStd::shared_ptr m_orbitFocusCamera; AzFramework::ViewportId m_viewportId; }; diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index b632163186..16176bc241 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp +++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp @@ -73,7 +73,7 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Field("TranslateSmoothing", &CameraMovementSettings::m_translateSmoothing) ->Field("TranslateSmoothness", &CameraMovementSettings::m_translateSmoothness) ->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook) - ->Field("PivotYawRotationInverted", &CameraMovementSettings::m_pivotYawRotationInverted) + ->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted) ->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX) ->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY); @@ -86,12 +86,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Field("TranslateUp", &CameraInputSettings::m_translateUpChannelId) ->Field("TranslateDown", &CameraInputSettings::m_translateDownChannelId) ->Field("Boost", &CameraInputSettings::m_boostChannelId) - ->Field("Pivot", &CameraInputSettings::m_pivotChannelId) + ->Field("Orbit", &CameraInputSettings::m_orbitChannelId) ->Field("FreeLook", &CameraInputSettings::m_freeLookChannelId) ->Field("FreePan", &CameraInputSettings::m_freePanChannelId) - ->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId) - ->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId) - ->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId) + ->Field("OrbitLook", &CameraInputSettings::m_orbitLookChannelId) + ->Field("OrbitDolly", &CameraInputSettings::m_orbitDollyChannelId) + ->Field("OrbitPan", &CameraInputSettings::m_orbitPanChannelId) ->Field("Focus", &CameraInputSettings::m_focusChannelId); serialize.Class() @@ -144,8 +144,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Attribute(AZ::Edit::Attributes::Min, minValue) ->Attribute(AZ::Edit::Attributes::Visibility, &CameraMovementSettings::TranslateSmoothingVisibility) ->DataElement( - AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_pivotYawRotationInverted, "Camera Pivot Yaw Inverted", - "Inverted yaw rotation while pivoting") + AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_orbitYawRotationInverted, "Camera Orbit Yaw Inverted", + "Inverted yaw rotation while orbiting") ->DataElement( AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_panInvertedX, "Invert Pan X", "Invert direction of pan in local X axis") @@ -186,8 +186,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial "Key/button to move the camera more quickly") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) ->DataElement( - AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotChannelId, "Pivot", - "Key/button to begin the camera pivot behavior") + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitChannelId, "Orbit", + "Key/button to begin the camera orbit behavior") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) ->DataElement( AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freeLookChannelId, "Free Look", @@ -197,19 +197,19 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freePanChannelId, "Free Pan", "Key/button to begin camera free pan") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) ->DataElement( - AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotLookChannelId, "Pivot Look", - "Key/button to begin camera pivot look") + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitLookChannelId, "Orbit Look", + "Key/button to begin camera orbit look") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) ->DataElement( - AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotDollyChannelId, "Pivot Dolly", - "Key/button to begin camera pivot dolly") + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitDollyChannelId, "Orbit Dolly", + "Key/button to begin camera orbit dolly") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) ->DataElement( - AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan", - "Key/button to begin camera pivot pan") + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitPanChannelId, "Orbit Pan", + "Key/button to begin camera orbit pan") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) ->DataElement( - AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera pivot") + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera orbit") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames); editContext->Class("Viewport Preferences", "Viewport Preferences") @@ -268,7 +268,7 @@ void CEditorPreferencesPage_ViewportCamera::OnApply() SandboxEditor::SetCameraTranslateSmoothness(m_cameraMovementSettings.m_translateSmoothness); SandboxEditor::SetCameraTranslateSmoothingEnabled(m_cameraMovementSettings.m_translateSmoothing); SandboxEditor::SetCameraCaptureCursorForLook(m_cameraMovementSettings.m_captureCursorLook); - SandboxEditor::SetCameraPivotYawRotationInverted(m_cameraMovementSettings.m_pivotYawRotationInverted); + SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted); SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX); SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY); @@ -279,12 +279,12 @@ void CEditorPreferencesPage_ViewportCamera::OnApply() SandboxEditor::SetCameraTranslateUpChannelId(m_cameraInputSettings.m_translateUpChannelId); SandboxEditor::SetCameraTranslateDownChannelId(m_cameraInputSettings.m_translateDownChannelId); SandboxEditor::SetCameraTranslateBoostChannelId(m_cameraInputSettings.m_boostChannelId); - SandboxEditor::SetCameraPivotChannelId(m_cameraInputSettings.m_pivotChannelId); + SandboxEditor::SetCameraOrbitChannelId(m_cameraInputSettings.m_orbitChannelId); SandboxEditor::SetCameraFreeLookChannelId(m_cameraInputSettings.m_freeLookChannelId); SandboxEditor::SetCameraFreePanChannelId(m_cameraInputSettings.m_freePanChannelId); - SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId); - SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId); - SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId); + SandboxEditor::SetCameraOrbitLookChannelId(m_cameraInputSettings.m_orbitLookChannelId); + SandboxEditor::SetCameraOrbitDollyChannelId(m_cameraInputSettings.m_orbitDollyChannelId); + SandboxEditor::SetCameraOrbitPanChannelId(m_cameraInputSettings.m_orbitPanChannelId); SandboxEditor::SetCameraFocusChannelId(m_cameraInputSettings.m_focusChannelId); SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Broadcast( @@ -304,7 +304,7 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings() m_cameraMovementSettings.m_translateSmoothness = SandboxEditor::CameraTranslateSmoothness(); m_cameraMovementSettings.m_translateSmoothing = SandboxEditor::CameraTranslateSmoothingEnabled(); m_cameraMovementSettings.m_captureCursorLook = SandboxEditor::CameraCaptureCursorForLook(); - m_cameraMovementSettings.m_pivotYawRotationInverted = SandboxEditor::CameraPivotYawRotationInverted(); + m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted(); m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX(); m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY(); @@ -315,11 +315,11 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings() m_cameraInputSettings.m_translateUpChannelId = SandboxEditor::CameraTranslateUpChannelId().GetName(); m_cameraInputSettings.m_translateDownChannelId = SandboxEditor::CameraTranslateDownChannelId().GetName(); m_cameraInputSettings.m_boostChannelId = SandboxEditor::CameraTranslateBoostChannelId().GetName(); - m_cameraInputSettings.m_pivotChannelId = SandboxEditor::CameraPivotChannelId().GetName(); + m_cameraInputSettings.m_orbitChannelId = SandboxEditor::CameraOrbitChannelId().GetName(); m_cameraInputSettings.m_freeLookChannelId = SandboxEditor::CameraFreeLookChannelId().GetName(); m_cameraInputSettings.m_freePanChannelId = SandboxEditor::CameraFreePanChannelId().GetName(); - m_cameraInputSettings.m_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName(); - m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName(); - m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName(); + m_cameraInputSettings.m_orbitLookChannelId = SandboxEditor::CameraOrbitLookChannelId().GetName(); + m_cameraInputSettings.m_orbitDollyChannelId = SandboxEditor::CameraOrbitDollyChannelId().GetName(); + m_cameraInputSettings.m_orbitPanChannelId = SandboxEditor::CameraOrbitPanChannelId().GetName(); m_cameraInputSettings.m_focusChannelId = SandboxEditor::CameraFocusChannelId().GetName(); } diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h index cd31141c4a..fdc86b0f89 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.h +++ b/Code/Editor/EditorPreferencesPageViewportCamera.h @@ -54,7 +54,7 @@ private: float m_translateSmoothness; bool m_translateSmoothing; bool m_captureCursorLook; - bool m_pivotYawRotationInverted; + bool m_orbitYawRotationInverted; bool m_panInvertedX; bool m_panInvertedY; @@ -80,12 +80,12 @@ private: AZStd::string m_translateUpChannelId; AZStd::string m_translateDownChannelId; AZStd::string m_boostChannelId; - AZStd::string m_pivotChannelId; + AZStd::string m_orbitChannelId; AZStd::string m_freeLookChannelId; AZStd::string m_freePanChannelId; - AZStd::string m_pivotLookChannelId; - AZStd::string m_pivotDollyChannelId; - AZStd::string m_pivotPanChannelId; + AZStd::string m_orbitLookChannelId; + AZStd::string m_orbitDollyChannelId; + AZStd::string m_orbitPanChannelId; AZStd::string m_focusChannelId; }; diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index c2539b008c..2354c6d63a 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -28,7 +28,7 @@ namespace SandboxEditor constexpr AZStd::string_view CameraRotateSpeedSetting = "/Amazon/Preferences/Editor/Camera/RotateSpeed"; constexpr AZStd::string_view CameraScrollSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyScrollSpeed"; constexpr AZStd::string_view CameraDollyMotionSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyMotionSpeed"; - constexpr AZStd::string_view CameraPivotYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted"; + constexpr AZStd::string_view CameraOrbitYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted"; constexpr AZStd::string_view CameraPanInvertedXSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedX"; constexpr AZStd::string_view CameraPanInvertedYSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedY"; constexpr AZStd::string_view CameraPanSpeedSetting = "/Amazon/Preferences/Editor/Camera/PanSpeed"; @@ -44,12 +44,12 @@ namespace SandboxEditor constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId"; constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId"; constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId"; - constexpr AZStd::string_view CameraPivotIdSetting = "/Amazon/Preferences/Editor/Camera/PivotId"; + constexpr AZStd::string_view CameraOrbitIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitId"; constexpr AZStd::string_view CameraFreeLookIdSetting = "/Amazon/Preferences/Editor/Camera/FreeLookId"; constexpr AZStd::string_view CameraFreePanIdSetting = "/Amazon/Preferences/Editor/Camera/FreePanId"; - constexpr AZStd::string_view CameraPivotLookIdSetting = "/Amazon/Preferences/Editor/Camera/PivotLookId"; - constexpr AZStd::string_view CameraPivotDollyIdSetting = "/Amazon/Preferences/Editor/Camera/PivotDollyId"; - constexpr AZStd::string_view CameraPivotPanIdSetting = "/Amazon/Preferences/Editor/Camera/PivotPanId"; + constexpr AZStd::string_view CameraOrbitLookIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitLookId"; + constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId"; + constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId"; constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId"; template @@ -240,14 +240,14 @@ namespace SandboxEditor SetRegistry(CameraDollyMotionSpeedSetting, speed); } - bool CameraPivotYawRotationInverted() + bool CameraOrbitYawRotationInverted() { - return GetRegistry(CameraPivotYawRotationInvertedSetting, false); + return GetRegistry(CameraOrbitYawRotationInvertedSetting, false); } - void SetCameraPivotYawRotationInverted(const bool inverted) + void SetCameraOrbitYawRotationInverted(const bool inverted) { - SetRegistry(CameraPivotYawRotationInvertedSetting, inverted); + SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted); } bool CameraPanInvertedX() @@ -404,14 +404,14 @@ namespace SandboxEditor SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId); } - AzFramework::InputChannelId CameraPivotChannelId() + AzFramework::InputChannelId CameraOrbitChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str()); } - void SetCameraPivotChannelId(AZStd::string_view cameraPivotId) + void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId) { - SetRegistry(CameraPivotIdSetting, cameraPivotId); + SetRegistry(CameraOrbitIdSetting, cameraOrbitId); } AzFramework::InputChannelId CameraFreeLookChannelId() @@ -434,34 +434,34 @@ namespace SandboxEditor SetRegistry(CameraFreePanIdSetting, cameraFreePanId); } - AzFramework::InputChannelId CameraPivotLookChannelId() + AzFramework::InputChannelId CameraOrbitLookChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotLookIdSetting, AZStd::string("mouse_button_left")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str()); } - void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId) + void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId) { - SetRegistry(CameraPivotLookIdSetting, cameraPivotLookId); + SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId); } - AzFramework::InputChannelId CameraPivotDollyChannelId() + AzFramework::InputChannelId CameraOrbitDollyChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotDollyIdSetting, AZStd::string("mouse_button_right")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str()); } - void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId) + void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId) { - SetRegistry(CameraPivotDollyIdSetting, cameraPivotDollyId); + SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId); } - AzFramework::InputChannelId CameraPivotPanChannelId() + AzFramework::InputChannelId CameraOrbitPanChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotPanIdSetting, AZStd::string("mouse_button_middle")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str()); } - void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId) + void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId) { - SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId); + SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId); } AzFramework::InputChannelId CameraFocusChannelId() diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index 0253b01621..c1394f7404 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -71,8 +71,8 @@ namespace SandboxEditor SANDBOX_API float CameraDollyMotionSpeed(); SANDBOX_API void SetCameraDollyMotionSpeed(float speed); - SANDBOX_API bool CameraPivotYawRotationInverted(); - SANDBOX_API void SetCameraPivotYawRotationInverted(bool inverted); + SANDBOX_API bool CameraOrbitYawRotationInverted(); + SANDBOX_API void SetCameraOrbitYawRotationInverted(bool inverted); SANDBOX_API bool CameraPanInvertedX(); SANDBOX_API void SetCameraPanInvertedX(bool inverted); @@ -119,8 +119,8 @@ namespace SandboxEditor SANDBOX_API AzFramework::InputChannelId CameraTranslateBoostChannelId(); SANDBOX_API void SetCameraTranslateBoostChannelId(AZStd::string_view cameraTranslateBoostId); - SANDBOX_API AzFramework::InputChannelId CameraPivotChannelId(); - SANDBOX_API void SetCameraPivotChannelId(AZStd::string_view cameraPivotId); + SANDBOX_API AzFramework::InputChannelId CameraOrbitChannelId(); + SANDBOX_API void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId); SANDBOX_API AzFramework::InputChannelId CameraFreeLookChannelId(); SANDBOX_API void SetCameraFreeLookChannelId(AZStd::string_view cameraFreeLookId); @@ -128,14 +128,14 @@ namespace SandboxEditor SANDBOX_API AzFramework::InputChannelId CameraFreePanChannelId(); SANDBOX_API void SetCameraFreePanChannelId(AZStd::string_view cameraFreePanId); - SANDBOX_API AzFramework::InputChannelId CameraPivotLookChannelId(); - SANDBOX_API void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId); + SANDBOX_API AzFramework::InputChannelId CameraOrbitLookChannelId(); + SANDBOX_API void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId); - SANDBOX_API AzFramework::InputChannelId CameraPivotDollyChannelId(); - SANDBOX_API void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId); + SANDBOX_API AzFramework::InputChannelId CameraOrbitDollyChannelId(); + SANDBOX_API void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId); - SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId(); - SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId); + SANDBOX_API AzFramework::InputChannelId CameraOrbitPanChannelId(); + SANDBOX_API void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId); SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId(); SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId); diff --git a/Code/Editor/GameExporter.cpp b/Code/Editor/GameExporter.cpp index 9315505e08..0324629877 100644 --- a/Code/Editor/GameExporter.cpp +++ b/Code/Editor/GameExporter.cpp @@ -28,6 +28,7 @@ #include "Objects/EntityObject.h" #include +#include ////////////////////////////////////////////////////////////////////////// #define MUSIC_LEVEL_LIBRARY_FILE "Music.xml" diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index 157291cfc2..ea3653f663 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -69,6 +69,7 @@ namespace UnitTest m_rootWidget = AZStd::make_unique(); m_rootWidget->setFixedSize(WidgetSize); + m_rootWidget->move(0, 0); // explicitly set the widget to be in the upper left corner m_controllerList = AZStd::make_shared(); m_controllerList->RegisterViewportContext(TestViewportId); @@ -344,4 +345,51 @@ namespace UnitTest // Clean-up HaltCollaborators(); } + + // test to verify deltas and cursor positions are handled correctly when the widget is moved + TEST_F(ModularViewportCameraControllerFixture, CameraDoesNotStutterAfterWidgetIsMoved) + { + // Given + PrepareCollaborators(); + SandboxEditor::SetCameraCaptureCursorForLook(true); + + const float deltaTime = 1.0f / 60.0f; + + // When + // move cursor to the center of the screen + auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2); + MouseMove(m_rootWidget.get(), start, QPoint(0, 0)); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + // move camera right + const auto mouseDelta = QPoint(200, 0); + MousePressAndMove(m_rootWidget.get(), start, mouseDelta, Qt::MouseButton::RightButton); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::NoModifier, start + mouseDelta); + + // update the position of the widget + const auto offset = QPoint(500, 500); + m_rootWidget->move(offset); + + // move cursor back to widget center + MouseMove(m_rootWidget.get(), start, QPoint(0, 0)); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + // move camera left + MousePressAndMove(m_rootWidget.get(), start, -mouseDelta, Qt::MouseButton::RightButton); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + // Then + // ensure the camera rotation has returned to the identity + const AZ::Quaternion cameraRotation = m_cameraViewportContextView->GetCameraTransform().GetRotation(); + const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(cameraRotation)); + + using ::testing::FloatNear; + EXPECT_THAT(eulerAngles.GetX(), FloatNear(0.0f, 0.001f)); + EXPECT_THAT(eulerAngles.GetZ(), FloatNear(0.0f, 0.001f)); + + // Clean-up + HaltCollaborators(); + } } // namespace UnitTest diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp index 05477bb0c7..ed72cd9170 100644 --- a/Code/Editor/MainWindow.cpp +++ b/Code/Editor/MainWindow.cpp @@ -98,6 +98,7 @@ AZ_POP_DISABLE_WARNING #include "ActionManager.h" #include +#include #include using namespace AZ; diff --git a/Code/Editor/Objects/ObjectManager.cpp b/Code/Editor/Objects/ObjectManager.cpp index f67cf24553..7057cc5b7b 100644 --- a/Code/Editor/Objects/ObjectManager.cpp +++ b/Code/Editor/Objects/ObjectManager.cpp @@ -30,6 +30,8 @@ #include "Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h" #include +#include +#include AZ_CVAR_EXTERNED(bool, ed_visibility_logTiming); @@ -107,14 +109,13 @@ CObjectManager::CObjectManager() m_objectsByName.reserve(1024); LoadRegistry(); - AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( - AzToolsFramework::GetEntityContextId()); + AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); } ////////////////////////////////////////////////////////////////////////// CObjectManager::~CObjectManager() { - AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); + AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); m_bExiting = true; SaveRegistry(); @@ -2306,25 +2307,33 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC } } -void CObjectManager::EnteredComponentMode(const AZStd::vector& /*componentModeTypes*/) +void CObjectManager::OnEditorModeActivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - // hide current gizmo for entity (translate/rotate/scale) - IGizmoManager* gizmoManager = GetGizmoManager(); - const size_t gizmoCount = static_cast(gizmoManager->GetGizmoCount()); - for (size_t i = 0; i < gizmoCount; ++i) + if (mode == AzToolsFramework::ViewportEditorMode::Component) { - gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast(i))); + // hide current gizmo for entity (translate/rotate/scale) + IGizmoManager* gizmoManager = GetGizmoManager(); + const size_t gizmoCount = static_cast(gizmoManager->GetGizmoCount()); + for (size_t i = 0; i < gizmoCount; ++i) + { + gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast(i))); + } } } -void CObjectManager::LeftComponentMode(const AZStd::vector& /*componentModeTypes*/) +void CObjectManager::OnEditorModeDeactivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - // show translate/rotate/scale gizmo again - if (IGizmoManager* gizmoManager = GetGizmoManager()) + if (mode == AzToolsFramework::ViewportEditorMode::Component) { - if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject()) + // show translate/rotate/scale gizmo again + if (IGizmoManager* gizmoManager = GetGizmoManager()) { - gizmoManager->AddGizmo(new CAxisGizmo(selectedObject)); + if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject()) + { + gizmoManager->AddGizmo(new CAxisGizmo(selectedObject)); + } } } } diff --git a/Code/Editor/Objects/ObjectManager.h b/Code/Editor/Objects/ObjectManager.h index 0ad5d8323e..7fb2342e40 100644 --- a/Code/Editor/Objects/ObjectManager.h +++ b/Code/Editor/Objects/ObjectManager.h @@ -20,8 +20,9 @@ #include "ObjectManagerEventBus.h" #include -#include +#include #include +#include #include // forward declarations. @@ -58,7 +59,7 @@ public: */ class CObjectManager : public IObjectManager - , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler + , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler { public: //! Selection functor callback. @@ -329,9 +330,11 @@ private: void FindDisplayableObjects(DisplayContext& dc, bool bDisplay); - // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; private: typedef std::map Objects; diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp index 9ed7c4a144..803deb3509 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -267,8 +268,7 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags) ToolsApplicationEvents::Bus::Handler::BusConnect(); AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusConnect(); - AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( - AzToolsFramework::GetEntityContextId()); + AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect(); AzToolsFramework::EditorWindowUIRequestBus::Handler::BusConnect(); } @@ -276,7 +276,7 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags) OutlinerWidget::~OutlinerWidget() { AzToolsFramework::EditorWindowUIRequestBus::Handler::BusDisconnect(); - AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); + AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect(); EntityHighlightMessages::Bus::Handler::BusDisconnect(); @@ -1335,14 +1335,22 @@ void OutlinerWidget::SetEditorUiEnabled(bool enable) EnableUi(enable); } -void OutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) +void OutlinerWidget::OnEditorModeActivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - EnableUi(false); + if (mode == AzToolsFramework::ViewportEditorMode::Component) + { + EnableUi(false); + } } -void OutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) +void OutlinerWidget::OnEditorModeDeactivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - EnableUi(true); + if (mode == AzToolsFramework::ViewportEditorMode::Component) + { + EnableUi(true); + } } void OutlinerWidget::OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, AZ::SliceComponent::SliceInstanceAddress& sliceAddress, const AzFramework::SliceInstantiationTicket& /*ticket*/) diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx index b29032a760..364ab05eb7 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -58,7 +58,7 @@ class OutlinerWidget , private AzToolsFramework::EditorEntityContextNotificationBus::Handler , private AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler , private AzToolsFramework::EditorEntityInfoNotificationBus::Handler - , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler + , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler , private AzToolsFramework::EditorWindowUIRequestBus::Handler { Q_OBJECT; @@ -105,9 +105,11 @@ private: void OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId /*childId*/) override; void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& /*name*/) override; - // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; // EditorWindowUIRequestBus overrides void SetEditorUiEnabled(bool enable) override; diff --git a/Code/Editor/QtViewPaneManager.cpp b/Code/Editor/QtViewPaneManager.cpp index eff3a7331e..1ac4b9cfe3 100644 --- a/Code/Editor/QtViewPaneManager.cpp +++ b/Code/Editor/QtViewPaneManager.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -44,11 +45,54 @@ #include #include #include - #include #include "ShortcutDispatcher.h" +// Helper for EditorComponentModeNotifications to be used +// as a member instead of inheriting from EBus directly. +class ViewportEditorModeNotificationsBusImpl + : public AzToolsFramework::ViewportEditorModeNotificationsBus::Handler +{ + public: + // Set the function to be called when entering ComponentMode. + void SetEnteredComponentModeFunc( + const AZStd::function& enteredComponentModeFunc) + { + m_enteredComponentModeFunc = enteredComponentModeFunc; + } + + // Set the function to be called when leaving ComponentMode. + void SetLeftComponentModeFunc( + const AZStd::function& leftComponentModeFunc) + { + m_leftComponentModeFunc = leftComponentModeFunc; + } + + private: + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override + { + if (mode == AzToolsFramework::ViewportEditorMode::Component) + { + m_enteredComponentModeFunc(editorModeState); + } + } + + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override + { + if (mode == AzToolsFramework::ViewportEditorMode::Component) + { + m_leftComponentModeFunc(editorModeState); + } + } + + AZStd::function m_enteredComponentModeFunc; ///< Function to call when entering ComponentMode. + AZStd::function m_leftComponentModeFunc; ///< Function to call when leaving ComponentMode. +}; + struct ViewLayoutState { QVector viewPanes; @@ -519,16 +563,17 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent) , m_settings(nullptr) , m_restoreInProgress(false) , m_advancedDockManager(nullptr) + , m_componentModeNotifications(AZStd::make_unique()) { qRegisterMetaTypeStreamOperators("ViewLayoutState"); qRegisterMetaTypeStreamOperators >("QVector"); // view pane manager is interested when we enter/exit ComponentMode - m_componentModeNotifications.BusConnect(AzToolsFramework::GetEntityContextId()); + m_componentModeNotifications->BusConnect(AzToolsFramework::GetEntityContextId()); m_windowRequest.BusConnect(); - m_componentModeNotifications.SetEnteredComponentModeFunc( - [this](const AZStd::vector& /*componentModeTypes*/) + m_componentModeNotifications->SetEnteredComponentModeFunc( + [this](const AzToolsFramework::ViewportEditorModesInterface&) { // gray out panels when entering ComponentMode SetDefaultActionsEnabled(false, m_registeredPanes, [](QWidget* widget, bool on) @@ -537,8 +582,8 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent) }); }); - m_componentModeNotifications.SetLeftComponentModeFunc( - [this](const AZStd::vector& /*componentModeTypes*/) + m_componentModeNotifications->SetLeftComponentModeFunc( + [this](const AzToolsFramework::ViewportEditorModesInterface&) { // enable panels again when leaving ComponentMode SetDefaultActionsEnabled(true, m_registeredPanes, [](QWidget* widget, bool on) @@ -563,7 +608,7 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent) QtViewPaneManager::~QtViewPaneManager() { m_windowRequest.BusDisconnect(); - m_componentModeNotifications.BusDisconnect(); + m_componentModeNotifications->BusDisconnect(); } static bool lessThan(const QtViewPane& v1, const QtViewPane& v2) diff --git a/Code/Editor/QtViewPaneManager.h b/Code/Editor/QtViewPaneManager.h index 3ad1cc9cf7..0515ae726e 100644 --- a/Code/Editor/QtViewPaneManager.h +++ b/Code/Editor/QtViewPaneManager.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -34,6 +33,7 @@ #endif class QMainWindow; +class ViewportEditorModeNotificationsBusImpl; struct ViewLayoutState; namespace AzQtComponents @@ -245,9 +245,9 @@ private: QPointer m_advancedDockManager; - using EditorComponentModeNotificationBusImpl = AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBusImpl; - EditorComponentModeNotificationBusImpl m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so - //!< QtViewPaneManager does not need to inherit directly from it. */ + AZStd::unique_ptr + m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so + //!< QtViewPaneManager does not need to inherit directly from it. */ using EditorWindowRequestBusImpl = AzToolsFramework::EditorWindowRequestBusImpl; EditorWindowRequestBusImpl m_windowRequest; //!< Helper for EditorWindowRequestBus so diff --git a/Code/Editor/Util/XmlArchive.cpp b/Code/Editor/Util/XmlArchive.cpp index 18c3fc8e63..3a965fb239 100644 --- a/Code/Editor/Util/XmlArchive.cpp +++ b/Code/Editor/Util/XmlArchive.cpp @@ -119,7 +119,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile& _smart_ptr pXmlStrData = root->getXMLData(5000000); // Save xml file. - QString xmlFilename = "Level.editor_xml"; + QString xmlFilename = "level.editor_xml"; pakFile.UpdateFile(xmlFilename.toUtf8().data(), (void*)pXmlStrData->GetString(), static_cast(pXmlStrData->GetStringLength())); if (pakFile.GetArchive()) @@ -134,7 +134,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile& ////////////////////////////////////////////////////////////////////////// bool CXmlArchive::LoadFromPak(const QString& levelPath, CPakFile& pakFile) { - QString xmlFilename = QDir(levelPath).absoluteFilePath("Level.editor_xml"); + QString xmlFilename = QDir(levelPath).absoluteFilePath("level.editor_xml"); root = XmlHelpers::LoadXmlFromFile(xmlFilename.toUtf8().data()); if (!root) { diff --git a/Code/Editor/Viewport.cpp b/Code/Editor/Viewport.cpp index 5b4f4a4df3..c8f2e268d9 100644 --- a/Code/Editor/Viewport.cpp +++ b/Code/Editor/Viewport.cpp @@ -19,6 +19,7 @@ #include #include +#include // Editor #include "ViewManager.h" diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp index 3a83aa5d1c..3fa3b39ca5 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp @@ -173,6 +173,7 @@ namespace AZ if (assetTracker) { + assetTracker->FixUpAsset(*instance); assetTracker->AddAsset(*instance); } @@ -185,7 +186,20 @@ namespace AZ return context.Report(result, message); } - void SerializedAssetTracker::AddAsset(Asset& asset) + void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback) + { + m_assetFixUpCallback = AZStd::move(assetFixUpCallback); + } + + void SerializedAssetTracker::FixUpAsset(Asset& asset) + { + if (m_assetFixUpCallback) + { + m_assetFixUpCallback(asset); + } + } + + void SerializedAssetTracker::AddAsset(Asset asset) { m_serializedAssets.emplace_back(asset); } @@ -199,5 +213,6 @@ namespace AZ { return m_serializedAssets; } + } // namespace Data } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h index 879952c82d..e1b8cda00d 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h @@ -39,13 +39,18 @@ namespace AZ { public: AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}"); + using AssetFixUp = AZStd::function& asset)>; - void AddAsset(Asset& asset); + void SetAssetFixUp(AssetFixUp assetFixUpCallback); + void FixUpAsset(Asset& asset); + + void AddAsset(Asset asset); AZStd::vector>& GetTrackedAssets(); const AZStd::vector>& GetTrackedAssets() const; private: AZStd::vector> m_serializedAssets; + AssetFixUp m_assetFixUpCallback; }; } // namespace Data } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Console/Console.cpp b/Code/Framework/AzCore/AzCore/Console/Console.cpp index a1b8a1759d..9f207d9afd 100644 --- a/Code/Framework/AzCore/AzCore/Console/Console.cpp +++ b/Code/Framework/AzCore/AzCore/Console/Console.cpp @@ -476,15 +476,16 @@ namespace AZ // Responsible for using the Json Serialization Issue Callback system // to determine when a JSON Patch or JSON Merge Patch modifies a value - // at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer + // at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer JsonSerializationResult::ResultCode operator()(AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view path) { - AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator }; + constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator }; + constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator }; AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator }; if (result.GetTask() == JsonSerializationResult::Tasks::Merge && result.GetProcessing() == JsonSerializationResult::Processing::Completed - && inputKey.IsRelativeTo(consoleRootCommandKey)) + && (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey))) { if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType) { @@ -510,12 +511,24 @@ namespace AZ { using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; - AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator }; + constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator }; + constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator }; AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator }; - // The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined - if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey) + + // Abuses the IsRelativeToFuncton function of the path class to extract the console + // command from the settings registry objects + FixedValueString command; + if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey)) + { + command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native(); + } + else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey)) + { + command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native(); + } + + if (!command.empty()) { - FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native(); ConsoleCommandContainer commandArgs; // Argument string which stores the value from the Settings Registry long enough // to pass into the PerformCommand. The ConsoleCommandContainer stores string_views @@ -603,9 +616,10 @@ namespace AZ void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry) { - // Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey + // Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey // So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects) - settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})", + settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })" + R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })", SettingsRegistryInterface::Format::JsonMergePatch); m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this }); diff --git a/Code/Framework/AzCore/AzCore/Console/IConsole.h b/Code/Framework/AzCore/AzCore/Console/IConsole.h index dafc284ce3..73d17ac65a 100644 --- a/Code/Framework/AzCore/AzCore/Console/IConsole.h +++ b/Code/Framework/AzCore/AzCore/Console/IConsole.h @@ -31,7 +31,8 @@ namespace AZ using FunctorVisitor = AZStd::function; - inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands"; + inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands"; + inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands"; IConsole() = default; virtual ~IConsole() = default; diff --git a/Code/Framework/AzCore/AzCore/Debug/Timer.h b/Code/Framework/AzCore/AzCore/Debug/Timer.h index 47bd3e1b95..6585fe7468 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Timer.h +++ b/Code/Framework/AzCore/AzCore/Debug/Timer.h @@ -46,5 +46,23 @@ namespace AZ private: AZStd::sys_time_t m_timeStamp; }; + + //! Utility type that updates the given variable with the lifetime of the object in cycles. + //! Useful for quick scope based timing. + struct ScopedTimer + { + explicit ScopedTimer(AZStd::sys_time_t& variable) + : m_variable(variable) + { + m_timer.Stamp(); + } + ~ScopedTimer() + { + m_variable = m_timer.GetDeltaTimeInTicks(); + } + + AZStd::sys_time_t& m_variable; + Timer m_timer; + }; } } diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp index 14504cc282..3edcbaa273 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp @@ -224,6 +224,8 @@ namespace AZ void Debug::Trace::Terminate(int exitCode) { + AZ_TracePrintf("Exit", "Called Terminate() with exit code: 0x%x", exitCode); + AZ::Debug::Trace::PrintCallstack("Exit"); Platform::Terminate(exitCode); } diff --git a/Code/Framework/AzCore/AzCore/EBus/BusImpl.h b/Code/Framework/AzCore/AzCore/EBus/BusImpl.h index 8e655c0525..e2b1c2e92b 100644 --- a/Code/Framework/AzCore/AzCore/EBus/BusImpl.h +++ b/Code/Framework/AzCore/AzCore/EBus/BusImpl.h @@ -160,8 +160,8 @@ namespace AZ /** * Locking primitive that is used when executing events in the event queue. */ - using EventQueueMutexType = typename AZStd::Utils::if_c::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType - MutexType, typename Traits::EventQueueMutexType>::type; + using EventQueueMutexType = AZStd::conditional_t::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType + MutexType, typename Traits::EventQueueMutexType>; /** * Pointer to an address on the bus. @@ -180,14 +180,22 @@ namespace AZ * `::ExecuteQueuedEvents()`. * By default, the event queue is disabled. */ - static const bool EnableEventQueue = Traits::EnableEventQueue; - static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault; - static const bool EnableQueuedReferences = Traits::EnableQueuedReferences; + static constexpr bool EnableEventQueue = Traits::EnableEventQueue; + static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault; + static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences; /** * True if the EBus supports more than one address. Otherwise, false. */ - static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single; + static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single; + + /** + * Template Lock Guard class that wraps around the Mutex + * The EBus uses for Dispatching Events. + * This is not the EBus Context Mutex if LocklessDispatch is true + */ + template + using DispatchLockGuard = typename Traits::template DispatchLockGuard; }; /** @@ -460,7 +468,7 @@ namespace AZ using BusPtr = typename Traits::BusPtr; /** - * Helper to queue an event by BusIdType only when function queueing is enabled + * Helper to queue an event by BusIdType only when function queueing is enabled * @param id Address ID. Handlers that are connected to this ID will receive the event. * @param func Function pointer of the event to dispatch. * @param args Function arguments that are passed to each handler. @@ -581,7 +589,7 @@ namespace AZ , public EBusBroadcaster , public EBusEventer , public EBusEventEnumerator - , public AZStd::Utils::if_c, EBusNullQueue>::type + , public AZStd::conditional_t, EBusNullQueue> { }; @@ -599,7 +607,7 @@ namespace AZ : public EventDispatcher , public EBusBroadcaster , public EBusBroadcastEnumerator - , public AZStd::Utils::if_c, EBusNullQueue>::type + , public AZStd::conditional_t, EBusNullQueue> { }; diff --git a/Code/Framework/AzCore/AzCore/EBus/EBus.h b/Code/Framework/AzCore/AzCore/EBus/EBus.h index da8c880963..67cffb4e41 100644 --- a/Code/Framework/AzCore/AzCore/EBus/EBus.h +++ b/Code/Framework/AzCore/AzCore/EBus/EBus.h @@ -77,9 +77,11 @@ namespace AZ public: /** * Allocator used by the EBus. - * The default setting is AZStd::allocator, which uses AZ::SystemAllocator. + * The default setting is Internal EBusEnvironmentAllocator + * EBus code stores their Context instances in static memory + * Therfore the configured allocator must last as long as the EBus in a module */ - using AllocatorType = AZStd::allocator; + using AllocatorType = AZ::Internal::EBusEnvironmentAllocator; /** * Defines how many handlers can connect to an address on the EBus @@ -236,6 +238,17 @@ namespace AZ * code before or after an event. */ using EventProcessingPolicy = EBusEventProcessingPolicy; + + /** + * Template Lock Guard class that wraps around the Mutex + * The EBus Context uses the LockGuard when dispatching + * (either AZStd::scoped_lock or NullLockGuard) + * The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant + * Otherwise the value above in EBusTraits.h is always used and not the value + * that the derived trait class sets. + */ + template + using DispatchLockGuard = AZStd::conditional_t, AZStd::scoped_lock>; }; namespace Internal @@ -496,6 +509,14 @@ namespace AZ */ static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single; + /** + * Template Lock Guard class that wraps around the Mutex + * The EBus uses for Dispatching Events. + * This is not EBus Context Mutex when LocklessDispatch is set + */ + template + using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard; + ////////////////////////////////////////////////////////////////////////// // Check to help identify common mistakes /// @cond EXCLUDE_DOCS @@ -620,11 +641,11 @@ namespace AZ using ContextMutexType = AZStd::conditional_t, AZStd::shared_mutex, MutexType>; /** - * The scoped lock guard to use (either AZStd::scoped_lock or NullLockGuard + * The scoped lock guard to use * during broadcast/event dispatch. * @see EBusTraits::LocklessDispatch */ - using DispatchLockGuard = AZStd::conditional_t, AZStd::scoped_lock>; + using DispatchLockGuard = DispatchLockGuard; /** * The scoped lock guard to use during connection. Some specialized policies execute handler methods which @@ -704,6 +725,11 @@ namespace AZ static Context& GetOrCreateContext(bool trackCallstack=true); static bool IsInDispatch(Context* context = GetContext(false)); + + /** + * Returns whether the EBus context is in the middle of a dispatch on the current thread + */ + static bool IsInDispatchThisThread(Context* context = GetContext(false)); /// @cond EXCLUDE_DOCS struct RouterCallstackEntry : public CallstackEntry @@ -1208,6 +1234,13 @@ AZ_POP_DISABLE_WARNING return context != nullptr && context->m_dispatches > 0; } + template + bool EBus::IsInDispatchThisThread(Context* context) + { + return context != nullptr && context->s_callstack != nullptr + && context->s_callstack->m_prev != nullptr; + } + //========================================================================= template EBus::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse) diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp index 25dfc7a493..6f5ccc93e4 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp @@ -18,6 +18,14 @@ #include #include +#include + +#include + +AZ_CVAR(float, cl_jobThreadsConcurrencyRatio, 0.6f, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system multiplier on the number of hw threads the machine creates at initialization"); +AZ_CVAR(uint32_t, cl_jobThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system number of hardware threads that are reserved for O3DE system threads"); +AZ_CVAR(uint32_t, cl_jobThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system minimum number of worker threads to create after scaling the number of hw threads"); + namespace AZ { //========================================================================= @@ -46,9 +54,10 @@ namespace AZ JobManagerThreadDesc threadDesc; int numberOfWorkerThreads = m_numberOfWorkerThreads; - if (numberOfWorkerThreads <= 0) + if (numberOfWorkerThreads <= 0) // spawn default number of threads { - numberOfWorkerThreads = AZ::GetMin(static_cast(desc.m_workerThreads.capacity()), AZStd::thread::hardware_concurrency()); + uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved); + numberOfWorkerThreads = AZ::GetMin(static_cast(desc.m_workerThreads.capacity()), scaledHardwareThreads); #if (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS) numberOfWorkerThreads = AZ::GetMin(numberOfWorkerThreads, AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS); #endif // (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS) diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h b/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h index 5594d7aa15..94f84f27b3 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h @@ -36,7 +36,7 @@ namespace AZ */ int m_stackSize; - JobManagerThreadDesc(int cpuId = -1, int priority = -100000, int stackSize = -1) + JobManagerThreadDesc(int cpuId = -1, int priority = 0, int stackSize = -1) : m_cpuId(cpuId) , m_priority(priority) , m_stackSize(stackSize) diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp index a7a0d5197e..5d13acc34c 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp @@ -15,7 +15,7 @@ using namespace Intersect; // IntersectSegmentTriangleCCW // [10/21/2009] //========================================================================= -int Intersect::IntersectSegmentTriangleCCW( +bool Intersect::IntersectSegmentTriangleCCW( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, /*float &u, float &v, float &w,*/ Vector3& normal, float& t) { @@ -34,7 +34,7 @@ int Intersect::IntersectSegmentTriangleCCW( float d = qp.Dot(normal); if (d <= 0.0f) { - return 0; + return false; } // Compute intersection t value of pq with plane of triangle. A ray @@ -46,7 +46,7 @@ int Intersect::IntersectSegmentTriangleCCW( // range segment check t[0,1] (it this case [0,d]) if (t < 0.0f || t > d) { - return 0; + return false; } // Compute barycentric coordinate components and test if within bounds @@ -54,12 +54,12 @@ int Intersect::IntersectSegmentTriangleCCW( v = ac.Dot(e); if (v < 0.0f || v > d) { - return 0; + return false; } w = -ab.Dot(e); if (w < 0.0f || v + w > d) { - return 0; + return false; } // Segment/ray intersects triangle. Perform delayed division and @@ -72,14 +72,14 @@ int Intersect::IntersectSegmentTriangleCCW( normal.Normalize(); - return 1; + return true; } //========================================================================= // IntersectSegmentTriangle // [10/21/2009] //========================================================================= -int +bool Intersect::IntersectSegmentTriangle( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, /*float &u, float &v, float &w,*/ Vector3& normal, float& t) @@ -111,7 +111,7 @@ Intersect::IntersectSegmentTriangle( // so either have a parallel ray or our normal is flipped if (d >= -Constants::FloatEpsilon) { - return 0; // parallel + return false; // parallel } d = -d; e = ap.Cross(qp); @@ -125,19 +125,19 @@ Intersect::IntersectSegmentTriangle( // range segment check t[0,1] (it this case [0,d]) if (t < 0.0f || t > d) { - return 0; + return false; } // Compute barycentric coordinate components and test if within bounds v = ac.Dot(e); if (v < 0.0f || v > d) { - return 0; + return false; } w = -ab.Dot(e); if (w < 0.0f || v + w > d) { - return 0; + return false; } // Segment/ray intersects the triangle. Perform delayed division and @@ -150,14 +150,14 @@ Intersect::IntersectSegmentTriangle( normal.Normalize(); - return 1; + return true; } //========================================================================= // TestSegmentAABBOrigin // [10/21/2009] //========================================================================= -int +bool AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends) { const Vector3 EPSILON(0.001f); // \todo this is slow load move to a const @@ -168,7 +168,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal // Try world coordinate axes as separating axes if (!absMidpoint.IsLessEqualThan(absHalfMidpoint)) { - return 0; + return false; } // Add in an epsilon term to counteract arithmetic errors when segment is @@ -188,11 +188,11 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal Vector3 ead(ey * adz + ez * ady, ex * adz + ez * adx, ex * ady + ey * adx); if (!absMDCross.IsLessEqualThan(ead)) { - return 0; + return false; } // No separating axis found; segment must be overlapping AABB - return 1; + return true; } @@ -200,7 +200,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal // IntersectRayAABB // [10/21/2009] //========================================================================= -int +RayAABBIsectTypes AZ::Intersect::IntersectRayAABB( const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb, float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/) @@ -356,7 +356,7 @@ AZ::Intersect::IntersectRayAABB( // IntersectRayAABB2 // [2/18/2011] //========================================================================= -int +RayAABBIsectTypes AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end) { float tmin, tmax, tymin, tymax, tzmin, tzmax; @@ -408,7 +408,7 @@ AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, return ISECT_RAY_AABB_ISECT; } -int AZ::Intersect::IntersectRayDisk( +bool AZ::Intersect::IntersectRayDisk( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const Vector3& diskNormal, float& t) { // First intersect with the plane of the disk @@ -421,10 +421,10 @@ int AZ::Intersect::IntersectRayDisk( if (pointOnPlane.GetDistance(diskCenter) < diskRadius) { t = planeIntersectionDistance; - return 1; + return true; } } - return 0; + return false; } // Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder, and the book's errata. @@ -1012,7 +1012,7 @@ int AZ::Intersect::IntersectRayQuad( } // reference: Real-Time Collision Detection, 5.3.3 Intersecting Ray or Segment Against Box -int AZ::Intersect::IntersectRayBox( +bool AZ::Intersect::IntersectRayBox( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1, const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, float& t) { @@ -1044,7 +1044,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1065,7 +1065,7 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } @@ -1085,7 +1085,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1106,7 +1106,7 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } @@ -1126,7 +1126,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1147,15 +1147,15 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } t = (isRayOriginInsideBox ? tmax : tmin); - return 1; + return true; } -int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t) +bool AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t) { return AZ::Intersect::IntersectRayBox(rayOrigin, rayDir, obb.GetPosition(), obb.GetAxisX(), obb.GetAxisY(), obb.GetAxisZ(), @@ -1166,7 +1166,7 @@ int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayD // IntersectSegmentCylinder // [10/21/2009] //========================================================================= -int +CylinderIsectTypes AZ::Intersect::IntersectSegmentCylinder( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t) { @@ -1225,7 +1225,7 @@ AZ::Intersect::IntersectSegmentCylinder( return RR_ISECT_RAY_CYL_NONE; // No real roots; no intersection } t = (-b - Sqrt(discr)) / a; - int result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment + CylinderIsectTypes result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment if (md + t * nd < 0.0f) { @@ -1294,7 +1294,7 @@ AZ::Intersect::IntersectSegmentCylinder( // IntersectSegmentCapsule // [10/21/2009] //========================================================================= -int +CapsuleIsectTypes AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t) { int result = IntersectSegmentCylinder(sa, dir, p, q, r, t); @@ -1361,13 +1361,13 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co // IntersectSegmentPolyhedron // [10/21/2009] //========================================================================= -int +bool AZ::Intersect::IntersectSegmentPolyhedron( - const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, + const Vector3& sa, const Vector3& dir, const Plane p[], int numPlanes, float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane) { // Compute direction vector for the segment - Vector3 d = /*b - a*/ sBA; + Vector3 d = /*b - a*/ dir; // Set initial interval to being the whole segment. For a ray, tlast should be // set to +RR_FLT_MAX. For a line, additionally tfirst should be set to -RR_FLT_MAX tfirst = 0.0f; @@ -1388,7 +1388,7 @@ AZ::Intersect::IntersectSegmentPolyhedron( // If so, return "no intersection" if segment lies outside plane if (dist < 0.0f) { - return 0; + return false; } } else @@ -1417,7 +1417,7 @@ AZ::Intersect::IntersectSegmentPolyhedron( // Exit with "no intersection" if intersection becomes empty if (tfirst > tlast) { - return 0; + return false; } } } @@ -1425,11 +1425,11 @@ AZ::Intersect::IntersectSegmentPolyhedron( //DBG_Assert(iFirstPlane!=-1&&iLastPlane!=-1,("We have some bad border case to have only one plane, fix this function!")); if (iFirstPlane == -1 && iLastPlane == -1) { - return 0; + return false; } // A nonzero logical intersection, so the segment intersects the polyhedron - return 1; + return true; } //========================================================================= @@ -1442,7 +1442,7 @@ AZ::Intersect::ClosestSegmentSegment( const Vector3& segment2Start, const Vector3& segment2End, float& segment1Proportion, float& segment2Proportion, Vector3& closestPointSegment1, Vector3& closestPointSegment2, - float epsilon /*= 1e-4f*/ ) + float epsilon) { const Vector3 segment1 = segment1End - segment1Start; const Vector3 segment2 = segment2End - segment2Start; diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h index df3d5e10fb..523069987f 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h @@ -5,363 +5,398 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_MATH_SEGMENT_INTERSECTION_H -#define AZCORE_MATH_SEGMENT_INTERSECTION_H +#pragma once -#include #include #include #include - -/// \file isect_segment.h +#include namespace AZ { namespace Intersect { //! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). - //! To calculate the point of intersection: - //! P = s1 + u (s2 - s1) - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - inline float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p) - { - // so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2 - return s21.Dot(p - s1) / s21.Dot(s21); - } + //! To calculate the point of intersection: P = s1 + u (s2 - s1) + //! @param s1 Segment start point. + //! @param s2 Segment end point. + //! @param p Point to find the closest time to. + //! @return Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p); //! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - //! @return the closest point - inline Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u) - { - const Vector3 s21 = s2 - s1; - // we assume seg1 and seg2 are NOT coincident - AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)"); - - u = LineToPointDistanceTime(s1, s21, p); - - return s1 + u * s21; - } + //! @param s1 Segment start point + //! @param s2 Segment end point + //! @param p Point to find the closest time to. + //! @param u Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + //! @return The closest point + Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u); //! Given segment pq and triangle abc (CCW), returns whether segment intersects //! triangle and if so, also returns the barycentric coordinates (u,v,w) //! of the intersection point. - //! @param p segment start point - //! @param q segment end point - //! @param a triangle point 1 - //! @param b triangle point 2 - //! @param c triangle point 3 - //! @param normal at the intersection point. - //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - //! @return 1 if the segment intersects the triangle otherwise 0 - int IntersectSegmentTriangleCCW( - const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, - /*float &u, float &v, float &w,*/ Vector3& normal, float& t); + //! @param p Segment start point. + //! @param q Segment end point. + //! @param a Triangle point 1. + //! @param b Triangle point 2. + //! @param c Triangle point 3. + //! @param normal At the intersection point. + //! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)]. + //! @return true if the segments intersects the triangle otherwise false. + bool IntersectSegmentTriangleCCW( + const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). - int IntersectSegmentTriangle( - const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, - /*float &u, float &v, float &w,*/ Vector3& normal, float& t); + //! @param p Segment start point. + //! @param q Segment end point. + //! @param a Triangle point 1. + //! @param b Triangle point 2. + //! @param c Triangle point 3. + //! @param normal At the intersection point. + //! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)]. + //! @return True if the segments intersects the triangle otherwise false. + bool IntersectSegmentTriangle( + const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Ray aabb intersection result types. - enum RayAABBIsectTypes + enum RayAABBIsectTypes : AZ::s32 { - ISECT_RAY_AABB_NONE = 0, ///< no intersection - ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb - ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment + ISECT_RAY_AABB_NONE = 0, ///< no intersection + ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb + ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment }; //! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, //! return intersection distance tmin and point q of intersection. - //! @param rayStart ray starting point - //! @param dir ray direction and length (dir = rayEnd - rayStart) - //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, otherwise just use dir.GetReciprocal()) + //! @param rayStart Ray starting point + //! @param dir Ray direction and length (dir = rayEnd - rayStart) + //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, + //! otherwise just use dir.GetReciprocal()) //! @param aabb Axis aligned bounding box to intersect against - //! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value - //! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) - //! @param startNormal normal at the start point. + //! @param tStart Time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value + //! @param tEnd Time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) + //! @param startNormal Normal at the start point. //! @return \ref RayAABBIsectTypes - int IntersectRayAABB( - const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb, - float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/); + RayAABBIsectTypes IntersectRayAABB( + const Vector3& rayStart, + const Vector3& dir, + const Vector3& dirRCP, + const Aabb& aabb, + float& tStart, + float& tEnd, + Vector3& startNormal); //! Intersect ray against AABB. - //! @param rayStart ray starting point. - //! @param dir ray reciprocal direction. + //! @param rayStart Ray starting point. + //! @param dir Ray reciprocal direction. //! @param aabb Axis aligned bounding box to intersect against. - //! @param start length on ray of the first intersection. - //! @param end length of the of the second intersection. - //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. - //! You can check yourself for that case. - int IntersectRayAABB2( - const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, - float& start, float& end); + //! @param start Length on ray of the first intersection. + //! @param end Length of the of the second intersection. + //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and + //! ISECT_RAY_AABB_ISECT. You can check yourself for that case. + RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end); //! Clip a ray to an aabb. return true if ray was clipped. The ray //! can be inside so don't use the result if the ray intersect the box. - inline int ClipRayWithAabb( - const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd) - { - Vector3 startNormal; - float tStart, tEnd; - Vector3 dirLen = rayEnd - rayStart; - if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE) - { - // clip the ray with the box - if (tStart > 0.0f) - { - rayStart = rayStart + tStart * dirLen; - tClipStart = tStart; - } - if (tEnd < 1.0f) - { - rayEnd = rayStart + tEnd * dirLen; - tClipEnd = tEnd; - } - - return 1; - } - - return 0; - } + //! @param aabb Bounds to test against. + //! @param rayStart The start of the ray. + //! @param rayEnd The end of the ray. + //! @param[out] tClipStart The proportion where the ray enters the \ref Aabb. + //! @param[out] tClipEnd The proportion where the ray exits the \ref Aabb. + //! @return True if the ray was clipped, otherwise false. + bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd); //! Test segment and aabb where the segment is defined by midpoint //! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. //! the aabb is at the origin and defined by half extents only. - //! @return 1 if the intersect, otherwise 0. - int TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); + //! @param midPoint Midpoint of a line segment. + //! @param halfVector Half vector of an aabb. + //! @param aabbExtends The extends of a bounded box. + //! @return True if the segment and AABB intersect, otherwise false + bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); - //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin - //! @return 1 if the segment and AABB intersect, otherwise 0. - inline int TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb) - { - Vector3 e = aabb.GetExtents(); - Vector3 d = p1 - p0; - Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax(); - - return TestSegmentAABBOrigin(m, d, e); - } + //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin. + //! @param p0 Segment start point. + //! @param p1 Segment end point. + //! @param aabb Bounded box to test against. + //! @return True if the segment and AABB intersect, otherwise false. + bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb); //! Ray sphere intersection result types. - enum SphereIsectTypes + enum SphereIsectTypes : AZ::s32 { - ISECT_RAY_SPHERE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_SPHERE_NONE, // no intersection - ISECT_RAY_SPHERE_ISECT, // along the PQ segment + ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< The ray starts inside the cylinder + ISECT_RAY_SPHERE_NONE, //!< No intersection + ISECT_RAY_SPHERE_ISECT, //!< Along the PQ segment }; //! IntersectRaySphereOrigin //! return time t>=0 but not limited, so if you check a segment make sure - //! t <= segmentLen - //! @param rayStart ray start point + //! t <= segmentLen. + //! @param rayStart ray start point. //! @param rayDirNormalized ray direction normalized. - //! @param shereRadius sphere radius + //! @param shereRadius Radius of sphere at origin. //! @param time of closest intersection [0,+INF] in relation to the normalized direction. - //! @return \ref SphereIsectTypes - AZ_INLINE int IntersectRaySphereOrigin( - const Vector3& rayStart, const Vector3& rayDirNormalized, - const float sphereRadius, float& t) - { - Vector3 m = rayStart; - float b = m.Dot(rayDirNormalized); - float c = m.Dot(m) - sphereRadius * sphereRadius; - - // Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0) - if (c > 0.0f && b > 0.0f) - { - return ISECT_RAY_SPHERE_NONE; - } - float discr = b * b - c; - // A negative discriminant corresponds to ray missing sphere - if (discr < 0.0f) - { - return ISECT_RAY_SPHERE_NONE; - } - - // Ray now found to intersect sphere, compute smallest t value of intersection - t = -b - Sqrt(discr); - - // If t is negative, ray started inside sphere so clamp t to zero - if (t < 0.0f) - { - // t = 0.0f; - return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside - } - //q = p + t * d; - return ISECT_RAY_SPHERE_ISECT; - } + //! @return \ref SphereIsectTypes. + SphereIsectTypes IntersectRaySphereOrigin( + const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t); //! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin - inline int IntersectRaySphere( - const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t) - { - return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t); - } + //! @param rayStart The start of the ray. + //! @param rayDirNormalized The direction of the ray normalized. + //! @param sphereCenter The center of the sphere. + //! @param sphereRadius Radius of the sphere. + //! @param[out] t Coefficient in the ray's explicit equation from which an + //! intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @return SphereIsectTypes + SphereIsectTypes IntersectRaySphere( + const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t); - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param diskCenter Center point of the disk - //! @param diskRadius Radius of the disk - //! @param diskNormal A normal perpendicular to the disk - //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir that the hit occured at. - //! @return The number of intersecting points. - int IntersectRayDisk( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const AZ::Vector3& diskNormal, float& t); + //! Intersect ray (rayStarty, rayDirNormalized) and disk (center, radius, normal) + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param diskCenter Center point of the disk. + //! @param diskRadius Radius of the disk. + //! @param diskNormal A normal perpendicular to the disk. + //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir + //! that the hit occured at. + //! @return False if not interesecting and true if intersecting + bool IntersectRayDisk( + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& diskCenter, + const float diskRadius, + const AZ::Vector3& diskNormal, + float& t); //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param cylinderEnd1 The center of the circle on one end of the cylinder. - //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length. - //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param cylinderEnd1 The center of the circle on one end of the cylinder. + //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length. + //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCappedCylinder( - const Vector3& rayOrigin, const Vector3& rayDir, - const Vector3& cylinderEnd1, const Vector3& cylinderDir, float cylinderHeight, float cylinderRadius, - float& t1, float& t2); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& cylinderEnd1, + const Vector3& cylinderDir, + float cylinderHeight, + float cylinderRadius, + float& t1, + float& t2); //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param coneApex The apex of the cone. - //! @param coneDir The unit-length direction from the apex to the base. - //! @param coneHeight The height of the cone, from the apex to the base. - //! @param coneBaseRadius The radius of the cone base circle. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param coneApex The apex of the cone. + //! @param coneDir The unit-length direction from the apex to the base. + //! @param coneHeight The height of the cone, from the apex to the base. + //! @param coneBaseRadius The radius of the cone base circle. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCone( - const Vector3& rayOrigin, const Vector3& rayDir, - const Vector3& coneApex, const Vector3& coneDir, float coneHeight, float coneBaseRadius, - float& t1, float& t2); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& coneApex, + const Vector3& coneDir, + float coneHeight, + float coneBaseRadius, + float& t1, + float& t2); //! Test intersection between a ray and a plane in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param planePos A point on the plane to test intersection with. - //! @param planeNormal The normal of the plane to test intersection with. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param planePos A point on the plane to test intersection with. + //! @param planeNormal The normal of the plane to test intersection with. + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return The number of intersection point. int IntersectRayPlane( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, - const Vector3& planeNormal, float& t); + const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t); //! Test intersection between a ray and a two-sided quadrilateral defined by four points in 3D. - //! The four points that define the quadrilateral could be passed in with either counter clock-wise + //! The four points that define the quadrilateral could be passed in with either counter clock-wise //! winding or clock-wise winding. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param vertexA One of the four points that define the quadrilateral. - //! @param vertexB One of the four points that define the quadrilateral. - //! @param vertexC One of the four points that define the quadrilateral. - //! @param vertexD One of the four points that define the quadrilateral. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param vertexA One of the four points that define the quadrilateral. + //! @param vertexB One of the four points that define the quadrilateral. + //! @param vertexC One of the four points that define the quadrilateral. + //! @param vertexD One of the four points that define the quadrilateral. + //! @param[out] t The coefficient in the ray's explicit equation from which the + //! intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return The number of intersection point. int IntersectRayQuad( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& vertexA, - const Vector3& vertexB, const Vector3& vertexC, const Vector3& vertexD, float& t); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& vertexA, + const Vector3& vertexB, + const Vector3& vertexC, + const Vector3& vertexD, + float& t); - //! Test intersection between a ray and an oriented box in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param boxCenter The position of the center of the box. - //! @param boxAxis1 An axis along one dimension of the oriented box. - //! @param boxAxis2 An axis along one dimension of the oriented box. - //! @param boxAxis3 An axis along one dimension of the oriented box. - //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. - //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. - //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayBox( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1, - const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, + //! Test intersection between a ray and an oriented box in 3D. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param boxCenter The position of the center of the box. + //! @param boxAxis1 An axis along one dimension of the oriented box. + //! @param boxAxis2 An axis along one dimension of the oriented box. + //! @param boxAxis3 An axis along one dimension of the oriented box. + //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. + //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. + //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return true if there is an intersection, false otherwise. + bool IntersectRayBox( + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& boxCenter, + const Vector3& boxAxis1, + const Vector3& boxAxis2, + const Vector3& boxAxis3, + float boxHalfExtent1, + float boxHalfExtent2, + float boxHalfExtent3, float& t); //! Test intersection between a ray and an OBB. //! @param rayOrigin The origin of the ray to test intersection with. //! @param rayDir The direction of the ray to test intersection with. //! @param obb The OBB to test for intersection with the ray. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return True if there is an intersection, false otherwise. + bool IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); //! Ray cylinder intersection types. - enum CylinderIsectTypes + enum CylinderIsectTypes : AZ::s32 { - RR_ISECT_RAY_CYL_SA_INSIDE = -1, // the ray starts inside the cylinder - RR_ISECT_RAY_CYL_NONE, // no intersection - RR_ISECT_RAY_CYL_PQ, // along the PQ segment - RR_ISECT_RAY_CYL_P_SIDE, // on the P side - RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side + RR_ISECT_RAY_CYL_SA_INSIDE = -1, //!< the ray starts inside the cylinder + RR_ISECT_RAY_CYL_NONE, //!< no intersection + RR_ISECT_RAY_CYL_PQ, //!< along the PQ segment + RR_ISECT_RAY_CYL_P_SIDE, //!< on the P side + RR_ISECT_RAY_CYL_Q_SIDE, //!< on the Q side }; + //! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder //! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. - int IntersectSegmentCylinder( - const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, - const float r, float& t); + //! @param sa The initial point. + //! @param dir Magnitude and direction for sa. + //! @param p Center point of side 1 cylinder. + //! @param q Center point of side 2 cylinder. + //! @param r Radius of cylinder. + //! @param[out] t Proporition along line segment. + //! @return CylinderIsectTypes + CylinderIsectTypes IntersectSegmentCylinder( + const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); //! Capsule ray intersect types. enum CapsuleIsectTypes { - ISECT_RAY_CAPSULE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_CAPSULE_NONE, // no intersection - ISECT_RAY_CAPSULE_PQ, // along the PQ segment - ISECT_RAY_CAPSULE_P_SIDE, // on the P side - ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side + ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< The ray starts inside the cylinder + ISECT_RAY_CAPSULE_NONE, //!< No intersection + ISECT_RAY_CAPSULE_PQ, //!< Along the PQ segment + ISECT_RAY_CAPSULE_P_SIDE, //!< On the P side + ISECT_RAY_CAPSULE_Q_SIDE, //!< On the Q side }; //! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder //! segment sphere intersection. We can optimize it a lot once we fix the ray //! cylinder intersection. - int IntersectSegmentCapsule( - const Vector3& sa, const Vector3& dir, const Vector3& p, - const Vector3& q, const float r, float& t); + //! @param sa The beginning of the line segment. + //! @param dir The direction and length of the segment. + //! @param p Center point of side 1 capsule. + //! @param q Center point of side 1 capsule. + //! @param r The radius of the capsule. + //! @param[out] t Proporition along line segment. + //! @return CapsuleIsectTypes + CapsuleIsectTypes IntersectSegmentCapsule( + const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); //! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified //! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast //! define the intersection, if any. - int IntersectSegmentPolyhedron( - const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, - float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane); + //! @param sa The beggining of the line segment. + //! @param dir The direction and length of the segment. + //! @param p Planes that compose a convex ponvex polyhedron. + //! @param numPlanes number of planes. + //! @param[out] tfirst Proportion along the line segment where the line enters. + //! @param[out] tlast Proportion along the line segment where the line exits. + //! @param[out] iFirstPlane The plane where the line enters. + //! @param[out] iLastPlane The plane where the line exits. + //! @return True if intersects else false. + bool IntersectSegmentPolyhedron( + const Vector3& sa, + const Vector3& dir, + const Plane p[], + int numPlanes, + float& tfirst, + float& tlast, + int& iFirstPlane, + int& iLastPlane); //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between - //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and segment2Proportion where - //! closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) + //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and + //! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) //! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) //! If segments are parallel returns a solution. + //! @param segment1Start Start of segment 1. + //! @param segment1End End of segment 1. + //! @param segment2Start Start of segment 2. + //! @param segment2End End of segment 2. + //! @param[out] segment1Proportion The proporition along segment 1 [0..1] + //! @param[out] segment2Proportion The proporition along segment 2 [0..1] + //! @param[out] closestPointSegment1 Closest point on segment 1. + //! @param[out] closestPointSegment2 Closest point on segment 2. + //! @param epsilon The minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( - const Vector3& segment1Start, const Vector3& segment1End, - const Vector3& segment2Start, const Vector3& segment2End, - float& segment1Proportion, float& segment2Proportion, - Vector3& closestPointSegment1, Vector3& closestPointSegment2, + const Vector3& segment1Start, + const Vector3& segment1End, + const Vector3& segment2Start, + const Vector3& segment2End, + float& segment1Proportion, + float& segment2Proportion, + Vector3& closestPointSegment1, + Vector3& closestPointSegment2, float epsilon = 1e-4f); //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between //! two segments segment1Start<->segment1End and segment2Start<->segment2End. //! If segments are parallel returns a solution. + //! @param segment1Start Start of segment 1. + //! @param segment1End End of segment 1. + //! @param segment2Start Start of segment 2. + //! @param segment2End End of segment 2. + //! @param[out] closestPointSegment1 Closest point on segment 1. + //! @param[out] closestPointSegment2 Closest point on segment 2. + //! @param epsilon The minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( - const Vector3& segment1Start, const Vector3& segment1End, - const Vector3& segment2Start, const Vector3& segment2End, - Vector3& closestPointSegment1, Vector3& closestPointSegment2, + const Vector3& segment1Start, + const Vector3& segment1End, + const Vector3& segment2Start, + const Vector3& segment2End, + Vector3& closestPointSegment1, + Vector3& closestPointSegment2, float epsilon = 1e-4f); //! Calculate the point (closestPointOnSegment) that is the closest point on //! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where //! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) + //! @param point The point to test + //! @param segmentStart The start of the segment + //! @param segmentEnd The end of the segment + //! @param[out] proportion The proportion of the segment L(t) = (end - start) * t + //! @param[out] closestPointOnSegment The point along the line segment void ClosestPointSegment( - const Vector3& point, const Vector3& segmentStart, const Vector3& segmentEnd, - float& proportion, Vector3& closestPointOnSegment); - } -} + const Vector3& point, + const Vector3& segmentStart, + const Vector3& segmentEnd, + float& proportion, + Vector3& closestPointOnSegment); + } // namespace Intersect +} // namespace AZ -#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H -#pragma once +#include diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl new file mode 100644 index 0000000000..b570b6a182 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl @@ -0,0 +1,101 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +namespace AZ +{ + namespace Intersect + { + AZ_MATH_INLINE bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd) + { + Vector3 startNormal; + float tStart, tEnd; + Vector3 dirLen = rayEnd - rayStart; + if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE) + { + // clip the ray with the box + if (tStart > 0.0f) + { + rayStart = rayStart + tStart * dirLen; + tClipStart = tStart; + } + if (tEnd < 1.0f) + { + rayEnd = rayStart + tEnd * dirLen; + tClipEnd = tEnd; + } + + return true; + } + + return false; + } + + AZ_MATH_INLINE SphereIsectTypes + IntersectRaySphereOrigin(const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t) + { + Vector3 m = rayStart; + float b = m.Dot(rayDirNormalized); + float c = m.Dot(m) - sphereRadius * sphereRadius; + + // Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0) + if (c > 0.0f && b > 0.0f) + { + return ISECT_RAY_SPHERE_NONE; + } + float discr = b * b - c; + // A negative discriminant corresponds to ray missing sphere + if (discr < 0.0f) + { + return ISECT_RAY_SPHERE_NONE; + } + + // Ray now found to intersect sphere, compute smallest t value of intersection + t = -b - Sqrt(discr); + + // If t is negative, ray started inside sphere so clamp t to zero + if (t < 0.0f) + { + // t = 0.0f; + return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside + } + // q = p + t * d; + return ISECT_RAY_SPHERE_ISECT; + } + + AZ_MATH_INLINE SphereIsectTypes IntersectRaySphere(const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t) + { + return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t); + } + + AZ_MATH_INLINE Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u) + { + const Vector3 s21 = s2 - s1; + // we assume seg1 and seg2 are NOT coincident + AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)"); + + u = LineToPointDistanceTime(s1, s21, p); + + return s1 + u * s21; + } + + AZ_MATH_INLINE float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p) + { + // so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2 + return s21.Dot(p - s1) / s21.Dot(s21); + } + + AZ_MATH_INLINE bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb) + { + Vector3 e = aabb.GetExtents(); + Vector3 d = p1 - p0; + Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax(); + + return TestSegmentAABBOrigin(m, d, e); + } + } // namespace Intersect +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h b/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h index be0ab4a0a0..14dec68ad1 100644 --- a/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h +++ b/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h @@ -34,7 +34,9 @@ namespace AZ friend IAllocator; friend class AllocatorBase; friend class Debug::AllocationRecords; - friend class AZ::Internal::EnvironmentVariableHolder; + template friend constexpr auto AZStd::construct_at(T*, Args&&... args) + ->AZStd::enable_if_t()) T(AZStd::forward(args)...))>>, T*>; + template constexpr friend void AZStd::destroy_at(T*); public: typedef AZStd::function OutOfMemoryCBType; diff --git a/Code/Framework/AzCore/AzCore/Module/Environment.h b/Code/Framework/AzCore/AzCore/Module/Environment.h index e87ff81446..a2dcfbd8fa 100644 --- a/Code/Framework/AzCore/AzCore/Module/Environment.h +++ b/Code/Framework/AzCore/AzCore/Module/Environment.h @@ -251,16 +251,15 @@ namespace AZ class EnvironmentVariableHolder : public EnvironmentVariableHolderBase { - void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor */) - { - memset(&m_value, 0, sizeof(T)); - } - template - void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor */, Args&&... args) + void ConstructImpl(Args&&... args) { - // Construction of non-trivial types is left up to the type's constructor. - new(&m_value) T(AZStd::forward(args)...); + // Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object + #if __cpp_lib_launder + AZStd::construct_at(std::launder(reinterpret_cast(&m_value)), AZStd::forward(args)...); + #else + AZStd::construct_at(reinterpret_cast(&m_value), AZStd::forward(args)...); + #endif } static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct) { @@ -274,10 +273,12 @@ namespace AZ AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!"); self->m_isConstructed = false; self->m_moduleOwner = nullptr; - if constexpr(!AZStd::is_trivially_destructible_v) - { - reinterpret_cast(&self->m_value)->~T(); - } + // Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object + #if __cpp_lib_launder + AZStd::destroy_at(std::launder(reinterpret_cast(&self->m_value))); + #else + AZStd::destroy_at(reinterpret_cast(&self->m_value)); + #endif } public: EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator) @@ -303,24 +304,13 @@ namespace AZ UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease); } - void Construct() - { - AZStd::lock_guard lock(m_mutex); - if (!m_isConstructed) - { - ConstructImpl(AZStd::is_trivially_constructible{}); - m_isConstructed = true; - m_moduleOwner = Environment::GetModuleId(); - } - } - template void Construct(Args&&... args) { AZStd::lock_guard lock(m_mutex); if (!m_isConstructed) { - ConstructImpl(typename AZStd::false_type(), AZStd::forward(args)...); + ConstructImpl(AZStd::forward(args)...); m_isConstructed = true; m_moduleOwner = Environment::GetModuleId(); } @@ -333,7 +323,7 @@ namespace AZ } // variable storage - typename AZStd::aligned_storage::value>::type m_value; + AZStd::aligned_storage_for_t m_value; static int s_moduleUseCount; }; @@ -468,6 +458,11 @@ namespace AZ Get() = value; } + void Set(T&& value) + { + Get() = AZStd::move(value); + } + explicit operator bool() const { return IsValid(); diff --git a/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp b/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp index 0086e68c6d..574b0bcc7e 100644 --- a/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp +++ b/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp @@ -42,7 +42,10 @@ namespace AZ AZ_Assert(m_useCount > 0, "m_useCount is already 0!"); if (m_useCount.fetch_sub(1) == 1) { - AZ::NameDictionary::Instance().TryReleaseName(hash); + if (AZ::NameDictionary::IsReady()) + { + AZ::NameDictionary::Instance().TryReleaseName(hash); + } } } } diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp index 8e8011add9..3047a2894e 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp +++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp @@ -21,23 +21,18 @@ namespace AZ namespace NameDictionaryInternal { - static AZ::EnvironmentVariable s_instance = nullptr; + static AZ::EnvironmentVariable s_instance = nullptr; } void NameDictionary::Create() { using namespace NameDictionaryInternal; - AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!"); + AZ_Assert(!s_instance, "NameDictionary already created!"); if (!s_instance) { - s_instance = AZ::Environment::CreateVariable(NameDictionaryInstanceName); - } - - if (!s_instance.Get()) - { - s_instance.Set(aznew NameDictionary()); + s_instance = AZ::Environment::CreateVariable(NameDictionaryInstanceName); } } @@ -46,8 +41,7 @@ namespace AZ using namespace NameDictionaryInternal; AZ_Assert(s_instance, "NameDictionary not created!"); - delete (*s_instance); - *s_instance = nullptr; + s_instance.Reset(); } bool NameDictionary::IsReady() @@ -56,10 +50,10 @@ namespace AZ if (!s_instance) { - s_instance = Environment::FindVariable(NameDictionaryInstanceName); + s_instance = Environment::FindVariable(NameDictionaryInstanceName); } - return s_instance && *s_instance; + return s_instance.IsConstructed(); } NameDictionary& NameDictionary::Instance() @@ -68,12 +62,12 @@ namespace AZ if (!s_instance) { - s_instance = Environment::FindVariable(NameDictionaryInstanceName); + s_instance = Environment::FindVariable(NameDictionaryInstanceName); } - AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet."); + AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet."); - return *(*s_instance); + return *s_instance; } NameDictionary::NameDictionary() diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.h b/Code/Framework/AzCore/AzCore/Name/NameDictionary.h index fa13dbd682..8f9af4be3a 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.h +++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.h @@ -16,7 +16,7 @@ #include #include -namespace MaterialEditor +namespace MaterialEditor { class MaterialEditorCoreComponent; } @@ -34,14 +34,14 @@ namespace AZ { class NameData; }; - + //! Maintains a list of unique strings for Name objects. //! The main benefit of the Name system is very fast string equality comparison, because every - //! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not + //! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not //! collide. It also saves memory by removing duplicate strings. //! - //! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't - //! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names + //! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't + //! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names //! that already exist. class NameDictionary final { @@ -51,7 +51,10 @@ namespace AZ friend Name; friend Internal::NameData; friend UnitTest::NameDictionaryTester; - + template friend constexpr auto AZStd::construct_at(T*, Args&&... args) + -> AZStd::enable_if_t()) T(AZStd::forward(args)...))>>, T*>; + template constexpr friend void AZStd::destroy_at(T*); + public: static void Create(); @@ -62,7 +65,7 @@ namespace AZ //! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared. //! Otherwise, it is added to the internal dictionary. - //! + //! //! @param name The name to resolve against the dictionary. //! @return A Name instance holding a dictionary entry associated with the provided raw string. Name MakeName(AZStd::string_view name); @@ -84,13 +87,13 @@ namespace AZ // Attempts to release the name from the dictionary, but checks to make sure // a reference wasn't taken by another thread. void TryReleaseName(Name::Hash hash); - + ////////////////////////////////////////////////////////////////////////// // Calculates a hash for the provided name string. // Does not attempt to resolve hash collisions; that is handled elsewhere. Name::Hash CalcHash(AZStd::string_view name); - + AZStd::unordered_map m_dictionary; mutable AZStd::shared_mutex m_sharedMutex; }; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h index 3dc1be87c5..40022bdc04 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h @@ -204,14 +204,14 @@ namespace AZ [[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) = 0; //! Register a function that will be called before a file is merged. //! @callback The function to call before a file is merged. - [[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent (PreMergeEventCallback&& callback) = 0; + [[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) = 0; //! Register a function that will be called after a file is merged. //! @callback The function to call after a file is merged. [[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) = 0; //! Register a function that will be called after a file is merged. //! @callback The function to call after a file is merged. - [[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent (PostMergeEventCallback&& callback) = 0; + [[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0; //! Gets the boolean value at the provided path. //! @param result The target to write the result to. diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.cpp new file mode 100644 index 0000000000..9456616024 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + + +namespace AZ::SettingsRegistryVisitorUtils +{ + // Field Visitor implementation + FieldVisitor::FieldVisitor() = default; + FieldVisitor::FieldVisitor(VisitFieldType visitFieldType) + : m_visitFieldType{ visitFieldType } + { + } + + auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName, + VisitAction action, Type type) -> VisitResponse + { + // A default response skip prevents visiting grand children(depth 2 or lower) + VisitResponse visitResponse = VisitResponse::Skip; + if (action == VisitAction::Begin) + { + // Invoke FieldVisitor override if the root path has been set + if (m_rootPath.has_value()) + { + Visit(path, valueName, type); + } + // To make sure only the direct children are visited(depth 1) + // set the root path once and set the VisitReponsoe + // to Continue to recurse into is fields + if (!m_rootPath.has_value()) + { + bool visitableFieldType{}; + switch (m_visitFieldType) + { + case VisitFieldType::Array: + visitableFieldType = type == Type::Array; + break; + case VisitFieldType::Object: + visitableFieldType = type == Type::Object; + break; + case VisitFieldType::ArrayOrObject: + visitableFieldType = type == Type::Array || type ==Type::Object; + break; + default: + AZ_Error("FieldVisitor", false, "The field visitation type value is invalid"); + break; + } + + if (visitableFieldType) + { + m_rootPath = path; + visitResponse = VisitResponse::Continue; + } + } + } + else if (action == VisitAction::Value) + { + // Invoke FieldVisitor override if the root path has been set + if (m_rootPath.has_value()) + { + Visit(path, valueName, type); + } + } + else if (action == VisitAction::End) + { + // Reset m_rootPath back to null when the root path has finished being visited + if (m_rootPath.has_value() && *m_rootPath == path) + { + m_rootPath = AZStd::nullopt; + } + } + + + return visitResponse; + } + + // Array Visitor implementation + ArrayVisitor::ArrayVisitor() + : FieldVisitor(VisitFieldType::Array) + { + } + + // Object Visitor implementation + ObjectVisitor::ObjectVisitor() + : FieldVisitor(VisitFieldType::Object) + { + } + + // Generic VisitField Callback implemention + template + bool VisitFieldCallback(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path) + { + struct VisitFieldVisitor + : BaseVisitor + { + using BaseVisitor::Visit; + VisitFieldVisitor(const VisitorCallback& visitCallback) + : m_visitCallback{ visitCallback } + {} + + void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override + { + m_visitCallback(path, fieldIndex, type); + } + + const VisitorCallback& m_visitCallback; + }; + + VisitFieldVisitor visitor{ visitCallback }; + return settingsRegistry.Visit(visitor, path); + } + + // VisitField implementation + bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path) + { + return VisitFieldCallback(settingsRegistry, visitCallback, path); + } + + // VisitArray implementation + bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path) + { + return VisitFieldCallback(settingsRegistry, visitCallback, path); + } + + // VisitObject implementation + bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path) + { + return VisitFieldCallback(settingsRegistry, visitCallback, path); + } +} diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.h new file mode 100644 index 0000000000..a45712521f --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryVisitorUtils.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + + +namespace AZ::SettingsRegistryVisitorUtils +{ + //! Interface for visiting the fields of an array or object + //! To access the values, use the SettingsRegistryInterface Get/GetObject methods + struct FieldVisitor + : public AZ::SettingsRegistryInterface::Visitor + { + using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse; + using VisitAction = AZ::SettingsRegistryInterface::VisitAction; + using Type = AZ::SettingsRegistryInterface::Type; + + FieldVisitor(); + + // Bring the base class visitor functions into scope + using AZ::SettingsRegistryInterface::Visitor::Visit; + virtual void Visit(AZStd::string_view path, AZStd::string_view arrayIndex, Type type) = 0; + + protected: + // VisitFieldType is used for filtering the type of referenced by the root path + enum class VisitFieldType + { + Array, + Object, + ArrayOrObject + }; + FieldVisitor(const VisitFieldType visitFieldType); + private: + VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName, + VisitAction action, Type type) override; + + VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject }; + AZStd::optional m_rootPath; + }; + + //! Interface for visiting the fields of an array + //! To access the values, use the SettingsRegistryInterface Get/GetObject methods + struct ArrayVisitor + : public FieldVisitor + { + ArrayVisitor(); + }; + + //! Interface for visiting the fields of an object + //! To access the values, use the SettingsRegistryInterface Get/GetObject methods + struct ObjectVisitor + : public FieldVisitor + { + ObjectVisitor(); + }; + + //! Signature of callback funcition invoked when visiting an element of an array or object + using VisitorCallback = AZStd::function; + + //! Invokes the visitor callback for each element of either the array or object at @path + //! If @path is not an array or object, then no elements are visited + //! This function will not recurse into children of elements + //! @visitCallback functor that is invoked for each array or object element found + bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path); + //! Invokes the visitor callback for each element of the array at @path + //! If @path is not an array, then no elements are visited + //! This function will not recurse into children of elements + //! @visitCallback functor that is invoked for each array element found + bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path); + //! Invokes the visitor callback for each element of the object at @path + //! If @path is not an object, then no elements are visited + //! This function will not recurse into children of elements + //! @visitCallback functor that is invoked for each object element found + bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path); +} diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index 7da04d7301..4097348798 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -308,11 +308,11 @@ namespace AZ void TaskExecutor::SetInstance(TaskExecutor* executor) { - if (!executor) + if (!executor) // allow unsetting the executor { s_executor.Reset(); } - else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities) + else if (!s_executor) // ignore any extra executors after the first (this happens during unit tests) { s_executor = AZ::Environment::CreateVariable(s_executorName, executor); } diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp index eed461ecb4..56b56e96a1 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp @@ -11,9 +11,15 @@ #include #include #include +#include +#include // Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system. AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)"); +AZ_CVAR(float, cl_taskGraphThreadsConcurrencyRatio, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph calculate the number of worker threads to spawn by scaling the number of hw threads, value is clamped between 0.0f and 1.0f"); +AZ_CVAR(uint32_t, cl_taskGraphThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph number of hardware threads that are reserved for O3DE system threads. Value is clamped between 0 and the number of logical cores in the system"); +AZ_CVAR(uint32_t, cl_taskGraphThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph minimum number of worker threads to create after scaling the number of hw threads"); + static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService"); namespace AZ @@ -24,8 +30,8 @@ namespace AZ if (Interface::Get() == nullptr) { - Interface::Register(this); - m_taskExecutor = aznew TaskExecutor(); + Interface::Register(this); // small window that another thread can try to use taskgraph between this line and the set instance. + m_taskExecutor = aznew TaskExecutor(Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved)); TaskExecutor::SetInstance(m_taskExecutor); } } diff --git a/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.cpp b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.cpp new file mode 100644 index 0000000000..93eba9cc3f --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ::Threading +{ + uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads) + { + const uint32_t maxHardwareThreads = AZStd::thread::hardware_concurrency(); + const uint32_t numReservedThreads = AZ::GetMin(reservedNumThreads, maxHardwareThreads); // protect against num reserved being bigger than the number of hw threads + const uint32_t maxWorkerThreads = maxHardwareThreads - numReservedThreads; + const float requestedWorkerThreads = AZ::GetClamp(workerThreadsRatio, 0.0f, 1.0f) * static_cast(maxWorkerThreads); + const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads); + const uint32_t numWorkerThreads = AZ::GetMax(minNumWorkerThreads, requestedWorkerThreadsRounded); + return numWorkerThreads; + } +}; diff --git a/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.h b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.h new file mode 100644 index 0000000000..505d900289 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace AZ::Threading +{ + //! Calculates the number of worker threads a system should use based on the number of hardware threads a device has. + //! result = max (minNumWorkerThreads, workerThreadsRatio * (num_hardware_threads - reservedNumThreads)) + //! @param workerThreadsRatio scale applied to the calculated maximum number of threads available after reserved threads have been accounted for. Clamped between 0 and 1. + //! @param minNumWorkerThreads minimum value that will be returned. Value is unclamped and can be more than num_hardware_threads. + //! @param reservedNumThreads number of hardware threads to reserve for O3DE system threads. Value clamped to num_hardware_threads. + //! @return number of worker threads for the calling system to allocate + uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads); +}; diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 14579cbf33..4d95ddf098 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -282,6 +282,7 @@ set(FILES Math/Internal/VertexContainer.inl Math/InterpolationSample.h Math/IntersectPoint.h + Math/IntersectSegment.inl Math/IntersectSegment.cpp Math/IntersectSegment.h Math/MathIntrinsics.h @@ -566,6 +567,8 @@ set(FILES Settings/SettingsRegistryMergeUtils.h Settings/SettingsRegistryScriptUtils.cpp Settings/SettingsRegistryScriptUtils.h + Settings/SettingsRegistryVisitorUtils.cpp + Settings/SettingsRegistryVisitorUtils.h State/HSM.cpp State/HSM.h Statistics/NamedRunningStatistic.h @@ -639,6 +642,8 @@ set(FILES Threading/ThreadSafeDeque.inl Threading/ThreadSafeObject.h Threading/ThreadSafeObject.inl + Threading/ThreadUtils.h + Threading/ThreadUtils.cpp Time/ITime.h Time/TimeSystemComponent.cpp Time/TimeSystemComponent.h diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp new file mode 100644 index 0000000000..5806cc485c --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace AZStd +{ + stateless_allocator::stateless_allocator(const char* name) + : m_name(name) {} + + const char* stateless_allocator::get_name() const + { + return m_name; + } + + void stateless_allocator::set_name(const char* name) + { + m_name = name; + } + + auto stateless_allocator::allocate(size_type byteSize) -> pointer_type + { + return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0); + } + + auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type + { + pointer_type address = AZ_OS_MALLOC(byteSize, alignment); + + if (address == nullptr) + { + AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n"); + } + + return address; + } + + void stateless_allocator::deallocate(pointer_type ptr, size_type) + { + AZ_OS_FREE(ptr); + } + + void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type) + { + AZ_OS_FREE(ptr); + } + + auto stateless_allocator::max_size() const -> size_type + { + return AZ_CORE_MAX_ALLOCATOR_SIZE; + } + + stateless_allocator stateless_allocator::select_on_container_copy_construction() const + { + return *this; + } + + auto stateless_allocator::resize(pointer_type, size_type) -> size_type + { + return 0; + } + + bool stateless_allocator::is_lock_free() + { + return false; + } + + bool stateless_allocator::is_stale_read_allowed() + { + return false; + } + + bool stateless_allocator::is_delayed_recycling() + { + return false; + } + + // comparison operators + bool operator==(const stateless_allocator&, const stateless_allocator&) + { + return true; + } + + bool operator!=(const stateless_allocator&, const stateless_allocator&) + { + return false; + } +} diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.h b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h new file mode 100644 index 0000000000..b73c680c32 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AZStd +{ + class stateless_allocator + { + public: + + AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}"); + + using value_type = uint8_t; + using pointer_type = void*; + using size_type = size_t; + using difference_type = ptrdiff_t; + using allow_memory_leaks = AZStd::true_type; + + stateless_allocator(const char* name = "AZStd::stateless_allocator"); + stateless_allocator(const stateless_allocator& rhs) = default; + + stateless_allocator& operator=(const stateless_allocator& rhs) = default; + + const char* get_name() const; + void set_name(const char* name); + + pointer_type allocate(size_type byteSize); + pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0); + void deallocate(pointer_type ptr, size_type alignment); + void deallocate(pointer_type ptr, size_type byteSize, size_type alignment); + + // max_size actually returns the true maximum size of a single allocation + size_type max_size() const; + + // Returns a copy of the allocator + stateless_allocator select_on_container_copy_construction() const; + + //! extensions + size_type resize(pointer_type ptr, size_type newSize); + + bool is_lock_free(); + bool is_stale_read_allowed(); + bool is_delayed_recycling(); + + private: + const char* m_name; + }; + + bool operator==(const stateless_allocator& left, const stateless_allocator& right); + bool operator!=(const stateless_allocator& left, const stateless_allocator& right); +} diff --git a/Code/Framework/AzCore/AzCore/std/azstd_files.cmake b/Code/Framework/AzCore/AzCore/std/azstd_files.cmake index 08e72c5649..2746489f8c 100644 --- a/Code/Framework/AzCore/AzCore/std/azstd_files.cmake +++ b/Code/Framework/AzCore/AzCore/std/azstd_files.cmake @@ -12,6 +12,8 @@ set(FILES allocator.h allocator_ref.h allocator_stack.h + allocator_stateless.cpp + allocator_stateless.h allocator_static.h allocator_traits.h any.h diff --git a/Code/Framework/AzCore/AzCore/std/createdestroy.h b/Code/Framework/AzCore/AzCore/std/createdestroy.h index 0fa6e4ed40..355374a13a 100644 --- a/Code/Framework/AzCore/AzCore/std/createdestroy.h +++ b/Code/Framework/AzCore/AzCore/std/createdestroy.h @@ -20,7 +20,7 @@ namespace AZStd { - // alias std::pointer_traits into the AZStd::namespace + // alias std::pointer_traits into the AZStd::namespace using std::pointer_traits; //! Bring the names of uninitialized_default_construct and @@ -229,7 +229,7 @@ namespace AZStd //! `new (declval()) T(declval()...)` is well-formed template constexpr auto construct_at(T* ptr, Args&&... args) - -> enable_if_t()) T(AZStd::forward(args)...))>>, T*> + -> enable_if_t()) T(AZStd::forward(args)...))>>, T*> { return ::new (ptr) T(AZStd::forward(args)...); } @@ -487,7 +487,7 @@ namespace AZStd { //! Implements the C++17 uninitialized_move function //! The functions accepts two input iterators and an output iterator - //! It performs an AZStd::move on each in in the range of the input iterator + //! It performs an AZStd::move on each in in the range of the input iterator //! and stores the result in location pointed by the output iterator template ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result) diff --git a/Code/Framework/AzCore/AzCore/std/math.h b/Code/Framework/AzCore/AzCore/std/math.h index 03f12e6e08..042dcca657 100644 --- a/Code/Framework/AzCore/AzCore/std/math.h +++ b/Code/Framework/AzCore/AzCore/std/math.h @@ -30,4 +30,49 @@ namespace AZStd using std::sqrt; using std::tan; using std::trunc; + +} // namespace AZStd + +// from c++20 standard +namespace AZStd::Internal +{ + template + constexpr T lerp(T a, T b, T t) noexcept + { + if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0)) + { + return t * b + (1 - t) * a; + } + if (t == 1) + { + return b; + } + const T x = a + t * (b - a); + if ((t > 1) == (b > a)) + { + return b < x ? x : b; + } + else + { + return x < b ? x : b; + } + } +} // namespace AZStd::Internal + +namespace AZStd +{ + constexpr float lerp(float a, float b, float t) noexcept + { + return Internal::lerp(a, b, t); + } + + constexpr double lerp(double a, double b, double t) noexcept + { + return Internal::lerp(a, b, t); + } + + constexpr long double lerp(long double a, long double b, long double t) noexcept + { + return Internal::lerp(a, b, t); + } } // namespace AZStd diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp index 079f167408..b615f677f1 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp @@ -59,6 +59,10 @@ namespace AZStd { priority = desc->m_priority; } + else + { + priority = SCHED_OTHER; + } if (desc->m_name) { name = desc->m_name; diff --git a/Code/Framework/AzCore/Tests/AZStd/Math.cpp b/Code/Framework/AzCore/Tests/AZStd/Math.cpp new file mode 100644 index 0000000000..5379c9946b --- /dev/null +++ b/Code/Framework/AzCore/Tests/AZStd/Math.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace UnitTest +{ + template + class StdMathTest : public ::testing::Test + { + }; + + using MathTestConfigs = ::testing::Types; + TYPED_TEST_CASE(StdMathTest, MathTestConfigs); + + TYPED_TEST(StdMathTest, LerpOperations) + { + using AZStd::lerp; + using ::testing::Eq; + using T = TypeParam; + + constexpr T maxNumber = AZStd::numeric_limits::max(); + constexpr T eps = AZStd::numeric_limits::epsilon(); + constexpr T a{ 42 }; + + // exactness: lerp(a,b,0)==a && lerp(a,b,1)==b + EXPECT_THAT(lerp(eps, maxNumber, T(0)), Eq(eps)); + EXPECT_THAT(lerp(eps, maxNumber, T(1)), Eq(maxNumber)); + + // consistency: lerp(a,a,t)==a + EXPECT_THAT(lerp(a, a, T(0.5)), Eq(a)); + EXPECT_THAT(lerp(eps, eps, T(0.5)), Eq(eps)); + + // a few generic tests taken from MathUtilTests.cpp + EXPECT_EQ(T(2.5), lerp(T(2), T(4), T(0.25))); + EXPECT_EQ(T(6.0), lerp(T(2), T(4), T(2.0))); + EXPECT_EQ(T(3.5), lerp(T(2), T(4), T(0.75))); + EXPECT_EQ(T(0.0), lerp(T(2), T(4), T(-1.0))); + } +} // namespace UnitTest diff --git a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp index e04c5190f9..e3a2c5c23b 100644 --- a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp @@ -64,6 +64,91 @@ namespace JsonSerializationTests }; + class TestSerializedAssetTracker + : public BaseJsonSerializerFixture + { + public: + void SetUp() override + { + BaseJsonSerializerFixture::SetUp(); + + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + + // Set up the Job Manager with 1 thread so that the Asset Manager is able to load assets. + AZ::JobManagerDesc jobDesc; + AZ::JobManagerThreadDesc threadDesc; + jobDesc.m_workerThreads.push_back(threadDesc); + m_jobManager = aznew AZ::JobManager(jobDesc); + m_jobContext = aznew AZ::JobContext(*m_jobManager); + AZ::JobContext::SetGlobalContext(m_jobContext); + + AZ::Data::AssetManager::Descriptor descriptor; + AZ::Data::AssetManager::Create(descriptor); + AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, azrtti_typeid()); + + m_serializeContext->RegisterGenericType>(); + m_jsonRegistrationContext->Serializer()->HandlesType(); + } + + void TearDown() override + { + m_jsonRegistrationContext->EnableRemoveReflection(); + m_jsonRegistrationContext->Serializer()->HandlesType(); + m_jsonRegistrationContext->DisableRemoveReflection(); + + AZ::Data::AssetManager::Instance().UnregisterHandler(&m_assetHandler); + AZ::Data::AssetManager::Destroy(); + + AZ::JobContext::SetGlobalContext(nullptr); + delete m_jobContext; + delete m_jobManager; + + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + + BaseJsonSerializerFixture::TearDown(); + } + + private: + TestAssetHandler m_assetHandler; + AZ::JobManager* m_jobManager{ nullptr }; + AZ::JobContext* m_jobContext{ nullptr }; + }; + + TEST_F(TestSerializedAssetTracker, AssetTracker_Callback_Works) + { + auto assetCallback = [](AZ::Data::Asset& asset) + { + if (!asset.GetId().IsValid() && !asset.GetHint().empty()) + { + if (asset.GetHint() == "test/path/foo.asset") + { + asset.SetHint("passed"); + } + } + }; + auto tracker = AZ::Data::SerializedAssetTracker{}; + tracker.SetAssetFixUp(assetCallback); + + AZ::JsonDeserializerSettings settings; + settings.m_metadata.Add(tracker); + settings.m_registrationContext = this->m_jsonRegistrationContext.get(); + settings.m_serializeContext = this->m_serializeContext.get(); + + AZStd::string_view assetHintOnlyTestAsset = R"( + { + "assetHint" : "test/path/foo.asset" + })"; + rapidjson::Document jsonDom; + jsonDom.Parse(assetHintOnlyTestAsset.data()); + + AZ::Data::Asset instance; + auto result = AZ::JsonSerialization::Load(instance, jsonDom, settings); + EXPECT_NE(result.GetProcessing(), AZ::JsonSerializationResult::Processing::Halted); + EXPECT_STREQ(instance.GetHint().c_str(), "passed"); + } + class AssetSerializerTestDescription final : public JsonSerializerConformityTestDescriptor> { diff --git a/Code/Framework/AzCore/Tests/EBus.cpp b/Code/Framework/AzCore/Tests/EBus.cpp index 10216a0485..9acf0f8a05 100644 --- a/Code/Framework/AzCore/Tests/EBus.cpp +++ b/Code/Framework/AzCore/Tests/EBus.cpp @@ -2088,7 +2088,7 @@ namespace UnitTest DisconnectNextHandlerByIdImpl multiHandler2; multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress); multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress); - + // Set the first handler m_nextHandler field to point to the second handler multiHandler1.m_nextHandler = &multiHandler2; @@ -2807,7 +2807,7 @@ namespace UnitTest AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_val % m_maxSleep)); } } - + void DoConnect() override { MyEventGroupBus::Handler::BusConnect(m_id); @@ -2854,7 +2854,7 @@ namespace UnitTest } MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4); - + LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect); bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end); @@ -2891,7 +2891,7 @@ namespace UnitTest { MyEventGroupImpl() { - + } ~MyEventGroupImpl() override @@ -3614,7 +3614,7 @@ namespace UnitTest { AZStd::this_thread::yield(); } - + EXPECT_GE(AZStd::chrono::system_clock::now(), endTime); }; AZStd::thread connectThread([&connectHandler, &waitHandler]() @@ -3813,7 +3813,7 @@ namespace UnitTest struct LastHandlerDisconnectHandler : public LastHandlerDisconnectBus::Handler { - void OnEvent() override + void OnEvent() override { ++m_numOnEvents; BusDisconnect(); @@ -3854,7 +3854,7 @@ namespace UnitTest struct DisconnectAssertHandler : public DisconnectAssertBus::Handler { - + }; TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts) @@ -3995,6 +3995,191 @@ namespace UnitTest idTestRequest.Disconnect(); } + + // IsInDispatchThisThread + struct IsInThreadDispatchRequests + : AZ::EBusTraits + { + using MutexType = AZStd::recursive_mutex; + }; + + using IsInThreadDispatchBus = AZ::EBus; + + class IsInThreadDispatchHandler + : public IsInThreadDispatchBus::Handler + {}; + + TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch) + { + IsInThreadDispatchHandler handler; + handler.BusConnect(); + + auto ThreadDispatcher = [](IsInThreadDispatchRequests*) + { + EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread()); + auto PerThreadBusDispatch = []() + { + EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread()); + }; + AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) }; + for (AZStd::thread& thread : threads) + { + thread.join(); + } + }; + + static constexpr size_t ThreadDispatcherIterations = 4; + for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration) + { + EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread()); + IsInThreadDispatchBus::Broadcast(ThreadDispatcher); + EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread()); + } + } + + // Thread Dispatch Policy + struct ThreadDispatchTestBusTraits + : AZ::EBusTraits + { + using MutexType = AZStd::recursive_mutex; + + struct PostThreadDispatchTestInvoker + { + ~PostThreadDispatchTestInvoker(); + }; + + template + struct ThreadDispatchTestLockGuard + { + ThreadDispatchTestLockGuard(DispatchMutex& contextMutex) + : m_lock{ contextMutex } + {} + ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock) + : m_lock{ contextMutex, adopt_lock } + {} + ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete; + ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete; + private: + PostThreadDispatchTestInvoker m_threadPolicyInvoker; + using LockType = AZStd::conditional_t, AZStd::scoped_lock>; + LockType m_lock; + }; + + template + using DispatchLockGuard = ThreadDispatchTestLockGuard; + + static inline AZStd::atomic s_threadPostDispatchCalls; + }; + + class ThreadDispatchTestRequests + { + public: + virtual void FirstCall() = 0; + virtual void SecondCall() = 0; + virtual void ThirdCall() = 0; + }; + + using ThreadDispatchTestBus = AZ::EBus; + + ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker() + { + if (!ThreadDispatchTestBus::IsInDispatchThisThread()) + { + ++s_threadPostDispatchCalls; + } + } + + class ThreadDispatchTestHandler + : public ThreadDispatchTestBus::Handler + { + public: + void Connect() + { + ThreadDispatchTestBus::Handler::BusConnect(); + } + void Disconnect() + { + ThreadDispatchTestBus::Handler::BusDisconnect(); + } + + void FirstCall() override + { + ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall); + } + void SecondCall() override + { + ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall); + } + void ThirdCall() override + { + } + }; + + template + class EBusParamFixture + : public ScopedAllocatorSetupFixture + , public ::testing::WithParamInterface + {}; + + struct ThreadDispatchParams + { + size_t m_threadCount{}; + size_t m_handlerCount{}; + }; + + using ThreadDispatchParamFixture = EBusParamFixture; + + INSTANTIATE_TEST_CASE_P( + ThreadDispatch, + ThreadDispatchParamFixture, + ::testing::Values( + ThreadDispatchParams{ 1, 1 }, + ThreadDispatchParams{ 2, 1 }, + ThreadDispatchParams{ 1, 2 }, + ThreadDispatchParams{ 2, 2 }, + ThreadDispatchParams{ 16, 8 } + ) + ); + + TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch) + { + ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0; + ThreadDispatchParams threadDispatchParams = GetParam(); + AZStd::vector testThreads; + AZStd::vector testHandlers(threadDispatchParams.m_handlerCount); + for (ThreadDispatchTestHandler& testHandler : testHandlers) + { + testHandler.Connect(); + } + + static constexpr size_t DispatchThreadCalls = 3; + const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls; + + auto DispatchThreadWorker = []() + { + ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall); + ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall); + ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall); + }; + + for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex) + { + testThreads.emplace_back(DispatchThreadWorker); + } + + for (AZStd::thread& thread : testThreads) + { + thread.join(); + } + + for (ThreadDispatchTestHandler& testHandler : testHandlers) + { + testHandler.Disconnect(); + } + + EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls); + ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0; + } } // namespace UnitTest #if defined(HAVE_BENCHMARK) @@ -4370,7 +4555,7 @@ namespace Benchmark Bus::ExecuteQueuedEvents(); } s_benchmarkEBusEnv.Disconnect(state); - + } BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast); diff --git a/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp b/Code/Framework/AzCore/Tests/Settings/SettingsRegistryMergeUtilsTests.cpp similarity index 100% rename from Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp rename to Code/Framework/AzCore/Tests/Settings/SettingsRegistryMergeUtilsTests.cpp diff --git a/Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp b/Code/Framework/AzCore/Tests/Settings/SettingsRegistryTests.cpp similarity index 100% rename from Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp rename to Code/Framework/AzCore/Tests/Settings/SettingsRegistryTests.cpp diff --git a/Code/Framework/AzCore/Tests/Settings/SettingsRegistryVisitorUtilsTests.cpp b/Code/Framework/AzCore/Tests/Settings/SettingsRegistryVisitorUtilsTests.cpp new file mode 100644 index 0000000000..15f346ee93 --- /dev/null +++ b/Code/Framework/AzCore/Tests/Settings/SettingsRegistryVisitorUtilsTests.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + + +#include +#include +#include +#include +#include +#include + +namespace SettingsRegistryVisitorUtilsTests +{ + struct VisitCallbackParams + { + AZStd::string_view m_inputJsonDocument; + using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&, + const AZ::SettingsRegistryVisitorUtils::VisitorCallback&, + AZStd::string_view); + + static inline constexpr size_t MaxFieldCount = 10; + using ObjectFields = AZStd::fixed_vector, MaxFieldCount>; + using ArrayFields = AZStd::fixed_vector; + ObjectFields m_objectFields; + ArrayFields m_arrayFields; + }; + + template + class SettingsRegistryVisitorUtilsParamFixture + : public UnitTest::ScopedAllocatorSetupFixture + , public ::testing::WithParamInterface + { + public: + + void SetUp() override + { + m_registry = AZStd::make_unique(); + } + + void TearDown() override + { + m_registry.reset(); + } + + AZStd::unique_ptr m_registry; + }; + + using SettingsRegistryVisitCallbackFixture = SettingsRegistryVisitorUtilsParamFixture; + + TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfArrayType_ReturnsFields) + { + const VisitCallbackParams& visitParams = GetParam(); + + ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch)); + + AZStd::fixed_vector testArrayFields; + auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type) + { + AZStd::string fieldValue; + EXPECT_TRUE(m_registry->Get(fieldValue, path)); + testArrayFields.emplace_back(AZStd::move(fieldValue)); + }; + + AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array"); + + const AZStd::fixed_vector expectedFields{ + visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() }; + EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedFields)); + } + + TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfObjectType_ReturnsFields) + { + const VisitCallbackParams& visitParams = GetParam(); + + ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch)); + + AZStd::fixed_vector, VisitCallbackParams::MaxFieldCount> testObjectFields; + auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type) + { + AZStd::string fieldValue; + EXPECT_TRUE(m_registry->Get(fieldValue, path)); + testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue)); + }; + + AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object"); + + const AZStd::fixed_vector, VisitCallbackParams::MaxFieldCount> expectedFields{ + visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() }; + EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedFields)); + } + + TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfArrayType_ReturnsFields) + { + const VisitCallbackParams& visitParams = GetParam(); + + ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch)); + + AZStd::fixed_vector testArrayFields; + auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type) + { + AZStd::string fieldValue; + EXPECT_TRUE(m_registry->Get(fieldValue, path)); + testArrayFields.emplace_back(AZStd::move(fieldValue)); + }; + + AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array"); + + const AZStd::fixed_vector expectedArrayFields{ + visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() }; + EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedArrayFields)); + } + + TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfObjectType_ReturnsEmpty) + { + const VisitCallbackParams& visitParams = GetParam(); + + ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch)); + + AZStd::fixed_vector testArrayFields; + auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type) + { + AZStd::string fieldValue; + EXPECT_TRUE(m_registry->Get(fieldValue, path)); + testArrayFields.emplace_back(AZStd::move(fieldValue)); + }; + + AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object"); + + EXPECT_TRUE(testArrayFields.empty()); + } + + TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfArrayType_ReturnsEmpty) + { + const VisitCallbackParams& visitParams = GetParam(); + + ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch)); + + AZStd::fixed_vector, VisitCallbackParams::MaxFieldCount> testObjectFields; + auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type) + { + AZStd::string fieldValue; + EXPECT_TRUE(m_registry->Get(fieldValue, path)); + testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue)); + }; + + AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array"); + + EXPECT_TRUE(testObjectFields.empty()); + } + + TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfObjectType_ReturnsFields) + { + const VisitCallbackParams& visitParams = GetParam(); + + ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch)); + + AZStd::fixed_vector, VisitCallbackParams::MaxFieldCount> testObjectFields; + auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type) + { + AZStd::string fieldValue; + EXPECT_TRUE(m_registry->Get(fieldValue, path)); + testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue)); + }; + + AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object"); + + const AZStd::fixed_vector, VisitCallbackParams::MaxFieldCount> expectedObjectFields{ + visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() }; + EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedObjectFields)); + } + + + INSTANTIATE_TEST_CASE_P( + VisitField, + SettingsRegistryVisitCallbackFixture, + ::testing::Values( + VisitCallbackParams + { + R"({)" "\n" + R"( "Test":)" "\n" + R"( {)" "\n" + R"( "Array": [ "Hello", "World" ],)" "\n" + R"( "Object": { "Foo": "Hello", "Bar": "World"})" "\n" + R"( })" "\n" + R"(})" "\n", + VisitCallbackParams::ObjectFields{{"Foo", "Hello"}, {"Bar", "World"}}, + VisitCallbackParams::ArrayFields{"Hello", "World"} + } + ) + ); +} diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index e155594aa0..6ba9944f7d 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -75,11 +75,12 @@ set(FILES Name/NameJsonSerializerTests.cpp Name/NameTests.cpp RTTI/TypeSafeIntegralTests.cpp - SettingsRegistryTests.cpp - SettingsRegistryMergeUtilsTests.cpp Settings/CommandLineTests.cpp + Settings/SettingsRegistryTests.cpp Settings/SettingsRegistryConsoleUtilsTests.cpp + Settings/SettingsRegistryMergeUtilsTests.cpp Settings/SettingsRegistryScriptUtilsTests.cpp + Settings/SettingsRegistryVisitorUtilsTests.cpp Streamer/BlockCacheTests.cpp Streamer/DedicatedCacheTests.cpp Streamer/FullDecompressorTests.cpp @@ -194,6 +195,7 @@ set(FILES AZStd/LockFreeQueues.cpp AZStd/LockFreeStacks.cpp AZStd/LockTests.cpp + AZStd/Math.cpp AZStd/Numeric.cpp AZStd/Ordered.cpp AZStd/Optional.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h index 1f300af770..3435d810fd 100644 --- a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h +++ b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h @@ -29,6 +29,10 @@ namespace AzFramework AZStd::vector m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path) static constexpr const char* GetGemAssetFolder() { return "Assets"; } + static constexpr const char* GetGemRegistryFolder() + { + return "Registry"; + } }; //! Returns a list of GemInfo of all the gems that are active for the for the specified game project. diff --git a/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp index 33690f2246..14fb7ea5eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp @@ -20,6 +20,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -190,6 +191,25 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////////// void InputSystemComponent::Activate() { + const auto* settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + AZ::u64 value = 0; + if (settingsRegistry->Get(value, "/O3DE/InputSystem/MouseMovementSampleRateHertz")) + { + m_mouseMovementSampleRateHertz = aznumeric_caster(value); + } + if (settingsRegistry->Get(value, "/O3DE/InputSystem/GamepadsEnabled")) + { + m_gamepadsEnabled = aznumeric_caster(value); + } + settingsRegistry->Get(m_keyboardEnabled, "/O3DE/InputSystem/KeyboardEnabled"); + settingsRegistry->Get(m_motionEnabled, "/O3DE/InputSystem/MotionEnabled"); + settingsRegistry->Get(m_mouseEnabled, "/O3DE/InputSystem/MouseEnabled"); + settingsRegistry->Get(m_touchEnabled, "/O3DE/InputSystem/TouchEnabled"); + settingsRegistry->Get(m_virtualKeyboardEnabled, "/O3DE/InputSystem/VirtualKeyboardEnabled"); + } + // Create all enabled input devices CreateEnabledInputDevices(); diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h index 58e335696f..9169a83588 100644 --- a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include namespace AZ diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp index 0c879a930f..0856ddeed6 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp @@ -22,6 +22,7 @@ namespace AzFramework ->Field("terminationTime", &SessionConfig::m_terminationTime) ->Field("creatorId", &SessionConfig::m_creatorId) ->Field("sessionProperties", &SessionConfig::m_sessionProperties) + ->Field("matchmakingData", &SessionConfig::m_matchmakingData) ->Field("sessionId", &SessionConfig::m_sessionId) ->Field("sessionName", &SessionConfig::m_sessionName) ->Field("dnsName", &SessionConfig::m_dnsName) @@ -46,6 +47,8 @@ namespace AzFramework "CreatorId", "A unique identifier for a player or entity creating the session.") ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionProperties, "SessionProperties", "A collection of custom properties for a session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_matchmakingData, + "MatchmakingData", "The matchmaking process information that was used to create the session.") ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionId, "SessionId", "A unique identifier for the session.") ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionName, diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h index cfd6aa7c8b..45e40c2f29 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h @@ -35,6 +35,9 @@ namespace AzFramework // A collection of custom properties for a session. AZStd::unordered_map m_sessionProperties; + + // The matchmaking process information that was used to create the session. + AZStd::string m_matchmakingData; // A unique identifier for the session. AZStd::string m_sessionId; diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h index 7788c2d030..902500fe9a 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h @@ -41,6 +41,11 @@ namespace AzFramework // OnDestroySessionBegin is fired at the beginning of session termination // @return The result of all OnDestroySessionBegin notifications virtual bool OnDestroySessionBegin() = 0; + + // OnUpdateSessionBegin is fired at the beginning of session update + // @param sessionConfig The properties to describe a session + // @param updateReason The reason for session update + virtual void OnUpdateSessionBegin(const SessionConfig& sessionConfig, const AZStd::string& updateReason) = 0; }; using SessionNotificationBus = AZ::EBus; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index 13f5303eaf..ddee63e191 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -533,8 +533,8 @@ namespace AzFramework m_translateCameraInputChannelIds = translateCameraInputChannelIds; } - PivotCameraInput::PivotCameraInput(const InputChannelId& pivotChannelId) - : m_pivotChannelId(pivotChannelId) + OrbitCameraInput::OrbitCameraInput(const InputChannelId& orbitChannelId) + : m_orbitChannelId(orbitChannelId) { m_pivotFn = []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { @@ -542,11 +542,11 @@ namespace AzFramework }; } - bool PivotCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta) + bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta) { if (const auto* input = AZStd::get_if(&event)) { - if (input->m_channelId == m_pivotChannelId) + if (input->m_channelId == m_orbitChannelId) { if (input->m_state == InputChannel::State::Began) { @@ -561,13 +561,13 @@ namespace AzFramework if (Active()) { - return m_pivotCameras.HandleEvents(event, cursorDelta, scrollDelta); + return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta); } return !Idle(); } - Camera PivotCameraInput::StepCamera( + Camera OrbitCameraInput::StepCamera( const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime) { Camera nextCamera = targetCamera; @@ -581,12 +581,12 @@ namespace AzFramework if (Active()) { MovePivotDetached(nextCamera, m_pivotFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY())); - nextCamera = m_pivotCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime); + nextCamera = m_orbitCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime); } if (Ending()) { - m_pivotCameras.Reset(); + m_orbitCameras.Reset(); nextCamera.m_pivot = nextCamera.Translation(); nextCamera.m_offset = AZ::Vector3::CreateZero(); @@ -595,12 +595,12 @@ namespace AzFramework return nextCamera; } - void PivotCameraInput::SetPivotInputChannelId(const InputChannelId& pivotChanneId) + void OrbitCameraInput::SetOrbitInputChannelId(const InputChannelId& orbitChanneId) { - m_pivotChannelId = pivotChanneId; + m_orbitChannelId = orbitChanneId; } - PivotDollyScrollCameraInput::PivotDollyScrollCameraInput() + OrbitDollyScrollCameraInput::OrbitDollyScrollCameraInput() { m_scrollSpeedFn = []() constexpr { @@ -608,7 +608,7 @@ namespace AzFramework }; } - bool PivotDollyScrollCameraInput::HandleEvents( + bool OrbitDollyScrollCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta) { if (const auto* scroll = AZStd::get_if(&event)) @@ -619,7 +619,7 @@ namespace AzFramework return !Idle(); } - static Camera PivotDolly(const Camera& targetCamera, const float delta) + static Camera OrbitDolly(const Camera& targetCamera, const float delta) { Camera nextCamera = targetCamera; @@ -646,18 +646,18 @@ namespace AzFramework return nextCamera; } - Camera PivotDollyScrollCameraInput::StepCamera( + Camera OrbitDollyScrollCameraInput::StepCamera( const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta, [[maybe_unused]] const float deltaTime) { - const auto nextCamera = PivotDolly(targetCamera, aznumeric_cast(scrollDelta) * m_scrollSpeedFn()); + const auto nextCamera = OrbitDolly(targetCamera, aznumeric_cast(scrollDelta) * m_scrollSpeedFn()); EndActivation(); return nextCamera; } - PivotDollyMotionCameraInput::PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId) + OrbitDollyMotionCameraInput::OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId) : m_dollyChannelId(dollyChannelId) { m_motionSpeedFn = []() constexpr @@ -666,28 +666,28 @@ namespace AzFramework }; } - bool PivotDollyMotionCameraInput::HandleEvents( + bool OrbitDollyMotionCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta) { HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this); return CameraInputUpdatingAfterMotion(*this); } - Camera PivotDollyMotionCameraInput::StepCamera( + Camera OrbitDollyMotionCameraInput::StepCamera( const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta, [[maybe_unused]] const float deltaTime) { - return PivotDolly(targetCamera, aznumeric_cast(cursorDelta.m_y) * m_motionSpeedFn()); + return OrbitDolly(targetCamera, aznumeric_cast(cursorDelta.m_y) * m_motionSpeedFn()); } - void PivotDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId) + void OrbitDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId) { m_dollyChannelId = dollyChannelId; } - ScrollTranslationCameraInput::ScrollTranslationCameraInput() + LookScrollTranslationCameraInput::LookScrollTranslationCameraInput() { m_scrollSpeedFn = []() constexpr { @@ -695,7 +695,7 @@ namespace AzFramework }; } - bool ScrollTranslationCameraInput::HandleEvents( + bool LookScrollTranslationCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta) { if (const auto* scroll = AZStd::get_if(&event)) @@ -706,7 +706,7 @@ namespace AzFramework return !Idle(); } - Camera ScrollTranslationCameraInput::StepCamera( + Camera LookScrollTranslationCameraInput::StepCamera( const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta, diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index 9b1988f5ec..2b7cc3ea9e 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -30,8 +30,11 @@ namespace AzFramework AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation); //! A simple camera representation using spherical coordinates as input (pitch, yaw, pivot and offset). - //! The cameras transform and view can be obtained through accessor functions that use the internal + //! The camera's transform and view can be obtained through accessor functions that use the internal //! spherical coordinates to calculate the position and orientation. + //! @note Modifying m_pivot directly and leaving m_offset as zero will produce a free look camera effect, giving + //! m_offset a value (e.g. in negative Y only) will produce an orbit camera effect, modifying X and Z of m_offset + //! will further alter the camera translation in relation to m_pivot so it appears off center. struct Camera { AZ::Vector3 m_pivot = AZ::Vector3::CreateZero(); //!< Pivot point to rotate about (modified in world space). @@ -291,7 +294,7 @@ namespace AzFramework Cameras m_cameras; //!< Represents a collection of camera inputs that together provide a camera controller. private: - ScreenVector m_motionDelta; //!< The delta used for look/pivot/pan (rotation + translation) - two dimensional. + ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional. CursorState m_cursorState; //!< The current and previous position of the cursor (used to calculate movement delta). float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional. bool m_handlingEvents = false; //!< Is the camera system currently handling events (events are consumed and not propagated). @@ -316,7 +319,7 @@ namespace AzFramework return AZStd::fmod(yaw + AZ::Constants::TwoPi, AZ::Constants::TwoPi); } - //! A camera input to handle motion deltas that can rotate or pivot the camera. + //! A camera input to handle motion deltas that can change the orientation of the camera (update pitch and yaw). class RotateCameraInput : public CameraInput { public: @@ -348,15 +351,16 @@ namespace AzFramework //! PanAxes build function that will return a pair of pan axes depending on the camera orientation. using PanAxesFn = AZStd::function; - //! PanAxes to use while in 'look' camera behavior (free look). + //! PanAxes to use while in 'look' or 'orbit' camera behavior. inline PanAxes LookPan(const Camera& camera) { const AZ::Matrix3x3 orientation = camera.Rotation(); return { orientation.GetBasisX(), orientation.GetBasisZ() }; } - //! PanAxes to use while in 'pivot' camera behavior. - inline PanAxes PivotPan(const Camera& camera) + //! Optional PanAxes to use while in 'orbit' camera behavior. + //! @note This will move the camera in the local X/Y plane instead of usual X/Z plane. + inline PanAxes OrbitPan(const Camera& camera) { const AZ::Matrix3x3 orientation = camera.Rotation(); @@ -370,14 +374,23 @@ namespace AzFramework return { basisX, basisY }; } + //! TranslationDeltaFn is used by PanCameraInput and TranslateCameraInput + //! @note Choose the appropriate function if the behavior should be operating as a free look camera (TranslatePivotLook) + //! or an orbit camera (TranslateOffsetOrbit). using TranslationDeltaFn = AZStd::function; - inline void TranslatePivot(Camera& camera, const AZ::Vector3& delta) + //! Update the pivot camera position. + //! @note delta will need to have been transformed to world space, e.g. To move the camera right, (1, 0, 0) must + //! first be transformed by the orientation of the camera before being applied to m_pivot. + inline void TranslatePivotLook(Camera& camera, const AZ::Vector3& delta) { camera.m_pivot += delta; } - inline void TranslateOffset(Camera& camera, const AZ::Vector3& delta) + //! Update the offset camera position. + //! @note delta still needs to be transformed to world space (as with TranslatePivotLook) but internally this is undone + //! to be performed in local space when being applied to m_offset. + inline void TranslateOffsetOrbit(Camera& camera, const AZ::Vector3& delta) { camera.m_offset += camera.View().TransformVector(delta); } @@ -409,7 +422,7 @@ namespace AzFramework //! Axes to use while translating the camera. using TranslationAxesFn = AZStd::function; - //! TranslationAxes to use while in 'look' camera behavior (free look). + //! TranslationAxes to use while in 'look' or 'orbit' camera behavior. inline AZ::Matrix3x3 LookTranslation(const Camera& camera) { const AZ::Matrix3x3 orientation = camera.Rotation(); @@ -421,8 +434,8 @@ namespace AzFramework return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ); } - //! TranslationAxes to use while in 'pivot' camera behavior. - inline AZ::Matrix3x3 PivotTranslation(const Camera& camera) + //! Optional TranslationAxes to use while in 'orbit' camera behavior. + inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera) { const AZ::Matrix3x3 orientation = camera.Rotation(); @@ -535,11 +548,11 @@ namespace AzFramework bool m_boost = false; //!< Is the translation speed currently being multiplied/scaled upwards. }; - //! A camera input to handle discrete scroll events that can modify the camera pivot distance. - class PivotDollyScrollCameraInput : public CameraInput + //! A camera input to handle discrete scroll events that can modify the camera offset. + class OrbitDollyScrollCameraInput : public CameraInput { public: - PivotDollyScrollCameraInput(); + OrbitDollyScrollCameraInput(); // CameraInput overrides ... bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; @@ -548,11 +561,11 @@ namespace AzFramework AZStd::function m_scrollSpeedFn; }; - //! A camera input to handle motion deltas that can modify the camera pivot distance. - class PivotDollyMotionCameraInput : public CameraInput + //! A camera input to handle motion deltas that can modify the camera offset. + class OrbitDollyMotionCameraInput : public CameraInput { public: - explicit PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId); + explicit OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId); // CameraInput overrides ... bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; @@ -569,10 +582,10 @@ namespace AzFramework }; //! A camera input to handle discrete scroll events that can scroll (translate) the camera along its forward axis. - class ScrollTranslationCameraInput : public CameraInput + class LookScrollTranslationCameraInput : public CameraInput { public: - ScrollTranslationCameraInput(); + LookScrollTranslationCameraInput(); // CameraInput overrides ... bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; @@ -583,36 +596,36 @@ namespace AzFramework //! A camera input that doubles as its own set of camera inputs. //! It is 'exclusive', so does not overlap with other sibling camera inputs - it runs its own set of camera inputs as 'children'. - class PivotCameraInput : public CameraInput + class OrbitCameraInput : public CameraInput { public: using PivotFn = AZStd::function; - explicit PivotCameraInput(const InputChannelId& pivotChannelId); + explicit OrbitCameraInput(const InputChannelId& orbitChannelId); // CameraInput overrides ... bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; bool Exclusive() const override; - void SetPivotInputChannelId(const InputChannelId& pivotChanneId); + void SetOrbitInputChannelId(const InputChannelId& orbitChanneId); - Cameras m_pivotCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive). + Cameras m_orbitCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive). //! Override the default behavior for how a pivot point is calculated. void SetPivotFn(PivotFn pivotFn); private: - InputChannelId m_pivotChannelId; //!< Input channel to begin the pivot camera input. - PivotFn m_pivotFn; //!< The pivot position to use for this pivot camera (how is the pivot point calculated/retrieved). + InputChannelId m_orbitChannelId; //!< Input channel to begin the orbit camera input. + PivotFn m_pivotFn; //!< The pivot position to use for this orbit camera (how is the pivot point calculated/retrieved). }; - inline void PivotCameraInput::SetPivotFn(PivotFn pivotFn) + inline void OrbitCameraInput::SetPivotFn(PivotFn pivotFn) { m_pivotFn = AZStd::move(pivotFn); } - inline bool PivotCameraInput::Exclusive() const + inline bool OrbitCameraInput::Exclusive() const { return true; } @@ -624,9 +637,9 @@ namespace AzFramework return AZ::Vector3::CreateZero(); } - //! Callback to use for FocusCameraInput when a pivot camera is being used. + //! Callback to use for FocusCameraInput when a orbit camera is being used. //! @note This is when offset is non zero. - inline AZ::Vector3 FocusPivot(const float length) + inline AZ::Vector3 FocusOrbit(const float length) { return AZ::Vector3::CreateAxisY(-length); } @@ -667,7 +680,9 @@ namespace AzFramework bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; + //! HandleEvents delegates directly to m_handleEventsFn. AZStd::function m_handleEventsFn; + //! StepCamera delegates directly to m_stepCameraFn. AZStd::function m_stepCameraFn; }; diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index 6ab97fd616..43f7e7140f 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include diff --git a/Code/Framework/AzFramework/Platform/iOS/AzFramework/Application/Application_iOS.mm b/Code/Framework/AzFramework/Platform/iOS/AzFramework/Application/Application_iOS.mm index 41bba124c7..36d920085b 100644 --- a/Code/Framework/AzFramework/Platform/iOS/AzFramework/Application/Application_iOS.mm +++ b/Code/Framework/AzFramework/Platform/iOS/AzFramework/Application/Application_iOS.mm @@ -112,9 +112,10 @@ namespace AzFramework void ApplicationIos::PumpSystemEventLoopUntilEmpty() { SInt32 result; + const CFTimeInterval MaxSecondsInRunLoop = 0.001; // One millisecond do { - result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_EPSILON, TRUE); + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, MaxSecondsInRunLoop, TRUE); } while (result == kCFRunLoopRunHandledSource); } diff --git a/Code/Framework/AzFramework/Tests/CameraInputTests.cpp b/Code/Framework/AzFramework/Tests/CameraInputTests.cpp index 1e0c805587..a89fb0bd84 100644 --- a/Code/Framework/AzFramework/Tests/CameraInputTests.cpp +++ b/Code/Framework/AzFramework/Tests/CameraInputTests.cpp @@ -53,31 +53,31 @@ namespace UnitTest }; m_firstPersonTranslateCamera = AZStd::make_shared( - m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot); + m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook); - m_pivotCamera = AZStd::make_shared(m_pivotChannelId); - m_pivotCamera->SetPivotFn( + m_orbitCamera = AZStd::make_shared(m_orbitChannelId); + m_orbitCamera->SetPivotFn( [this](const AZ::Vector3&, const AZ::Vector3&) { return m_pivot; }); - auto pivotRotateCamera = AZStd::make_shared(AzFramework::InputDeviceMouse::Button::Left); + auto orbitRotateCamera = AZStd::make_shared(AzFramework::InputDeviceMouse::Button::Left); // set rotate speed to be a value that will scale motion delta (pixels moved) by a thousandth. - pivotRotateCamera->m_rotateSpeedFn = []() + orbitRotateCamera->m_rotateSpeedFn = []() { return 0.001f; }; - auto pivotTranslateCamera = AZStd::make_shared( - m_translateCameraInputChannelIds, AzFramework::PivotTranslation, AzFramework::TranslateOffset); + auto orbitTranslateCamera = AZStd::make_shared( + m_translateCameraInputChannelIds, AzFramework::OrbitTranslation, AzFramework::TranslateOffsetOrbit); - m_pivotCamera->m_pivotCameras.AddCamera(pivotRotateCamera); - m_pivotCamera->m_pivotCameras.AddCamera(pivotTranslateCamera); + m_orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); + m_orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); m_cameraSystem->m_cameras.AddCamera(m_firstPersonRotateCamera); m_cameraSystem->m_cameras.AddCamera(m_firstPersonTranslateCamera); - m_cameraSystem->m_cameras.AddCamera(m_pivotCamera); + m_cameraSystem->m_cameras.AddCamera(m_orbitCamera); // these tests rely on using motion delta, not cursor positions (default is true) AzFramework::ed_cameraSystemUseCursor = false; @@ -87,7 +87,7 @@ namespace UnitTest { AzFramework::ed_cameraSystemUseCursor = true; - m_pivotCamera.reset(); + m_orbitCamera.reset(); m_firstPersonRotateCamera.reset(); m_firstPersonTranslateCamera.reset(); @@ -97,11 +97,11 @@ namespace UnitTest AllocatorsTestFixture::TearDown(); } - AzFramework::InputChannelId m_pivotChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l"); + AzFramework::InputChannelId m_orbitChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l"); AzFramework::TranslateCameraInputChannelIds m_translateCameraInputChannelIds; AZStd::shared_ptr m_firstPersonRotateCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; - AZStd::shared_ptr m_pivotCamera; + AZStd::shared_ptr m_orbitCamera; AZ::Vector3 m_pivot = AZ::Vector3::CreateZero(); //! This is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based @@ -109,17 +109,17 @@ namespace UnitTest inline static const int PixelMotionDelta = 1570; }; - TEST_F(CameraInputFixture, BeginAndEndPivotCameraInputConsumesCorrectEvents) + TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents) { - // begin pivot camera + // begin orbit camera const bool consumed1 = HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceKeyboard::Key::ModifierAltL, AzFramework::InputChannel::State::Began }); - // begin listening for pivot rotate (click detector) - event is not consumed + // begin listening for orbit rotate (click detector) - event is not consumed const bool consumed2 = HandleEventAndUpdate( AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began }); - // begin pivot rotate (mouse has moved sufficient distance to initiate) + // begin orbit rotate (mouse has moved sufficient distance to initiate) const bool consumed3 = HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ 5 }); - // end pivot (mouse up) - event is not consumed + // end orbit (mouse up) - event is not consumed const bool consumed4 = HandleEventAndUpdate( AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended }); @@ -260,10 +260,10 @@ namespace UnitTest EXPECT_TRUE(activationEnded); } - TEST_F(CameraInputFixture, PivotCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenPivoting) + TEST_F(CameraInputFixture, OrbitCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenOrbiting) { // create pathological lookAtFn that just returns the same position as the camera - m_pivotCamera->SetPivotFn( + m_orbitCamera->SetPivotFn( [](const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { return position; @@ -275,7 +275,7 @@ namespace UnitTest AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), expectedCameraPosition)); - HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began }); + HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began }); // verify the camera yaw has not changed and pivot point matches the expected camera position using ::testing::FloatNear; @@ -321,14 +321,14 @@ namespace UnitTest EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateZero())); } - TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta) + TEST_F(CameraInputFixture, OrbitRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta) { const auto cameraStartingPosition = AZ::Vector3::CreateAxisY(-20.0f); m_targetCamera.m_pivot = cameraStartingPosition; m_pivot = AZ::Vector3::CreateAxisY(-10.0f); - HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began }); + HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began }); HandleEventAndUpdate( AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began }); HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta }); @@ -344,14 +344,14 @@ namespace UnitTest EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f)); } - TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta) + TEST_F(CameraInputFixture, OrbitRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta) { const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f); m_targetCamera.m_pivot = cameraStartingPosition; m_pivot = AZ::Vector3(10.0f, -10.0f, 0.0f); - HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began }); + HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began }); HandleEventAndUpdate( AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began }); HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta }); diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp index 3abbcdc564..76d00eda50 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -19,6 +19,7 @@ #include "Matchers.h" #include "Actions.h" #include "XcbBaseTestFixture.h" +#include "XcbTestApplication.h" template xcb_generic_event_t MakeEvent(T event) @@ -32,6 +33,7 @@ namespace AzFramework class XcbInputDeviceKeyboardTests : public XcbBaseTestFixture { + public: void SetUp() override { using testing::Return; @@ -122,6 +124,15 @@ namespace AzFramework static constexpr xcb_keycode_t s_keycodeForAKey{38}; static constexpr xcb_keycode_t s_keycodeForShiftLKey{50}; + + XcbTestApplication m_application{ + /*enabledGamepadsCount=*/0, + /*keyboardEnabled=*/true, + /*motionEnabled=*/false, + /*mouseEnabled=*/false, + /*touchEnabled=*/false, + /*virtualKeyboardEnabled=*/false + }; }; class InputTextNotificationListener @@ -194,26 +205,23 @@ namespace AzFramework EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey)) .Times(2); - Application application; - application.Start({}, {}); + m_application.Start(); const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA); ASSERT_TRUE(inputChannel); EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle)); - application.PumpSystemEventLoopUntilEmpty(); - application.TickSystem(); - application.Tick(); + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began)); - application.PumpSystemEventLoopUntilEmpty(); - application.TickSystem(); - application.Tick(); + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended)); - - application.Stop(); } TEST_F(XcbInputDeviceKeyboardTests, TextEnteredFromXcbKeyPressEvents) @@ -418,16 +426,13 @@ namespace AzFramework EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1); EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1); - Application application; - application.Start({}, {}); + m_application.Start(); for (int i = 0; i < 4; ++i) { - application.PumpSystemEventLoopUntilEmpty(); - application.TickSystem(); - application.Tick(); + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); } - - application.Stop(); } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h new file mode 100644 index 0000000000..3035a9de74 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AzFramework +{ + class XcbTestApplication + : public Application + { + public: + XcbTestApplication(AZ::u64 enabledGamepadsCount, bool keyboardEnabled, bool motionEnabled, bool mouseEnabled, bool touchEnabled, bool virtualKeyboardEnabled) + { + auto* settingsRegistry = AZ::SettingsRegistry::Get(); + settingsRegistry->Set("/O3DE/InputSystem/GamepadsEnabled", enabledGamepadsCount); + settingsRegistry->Set("/O3DE/InputSystem/KeyboardEnabled", keyboardEnabled); + settingsRegistry->Set("/O3DE/InputSystem/MotionEnabled", motionEnabled); + settingsRegistry->Set("/O3DE/InputSystem/MouseEnabled", mouseEnabled); + settingsRegistry->Set("/O3DE/InputSystem/TouchEnabled", touchEnabled); + settingsRegistry->Set("/O3DE/InputSystem/VirtualKeyboardEnabled", virtualKeyboardEnabled); + } + + void Start(const Descriptor& descriptor = {}, const StartupParameters& startupParameters = {}) override + { + Application::Start(descriptor, startupParameters); + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); + } + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake index 762d5d74fe..147fd2bfe1 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -17,4 +17,5 @@ set(FILES XcbBaseTestFixture.cpp XcbBaseTestFixture.h XcbInputDeviceKeyboardTests.cpp + XcbTestApplication.h ) diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index 462de43262..0cce93d751 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -96,6 +96,8 @@ namespace AzGameFramework AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true); +#else + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false); #endif // Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/CardNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/CardNotification.cpp index d9451949ea..d2144457c8 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/CardNotification.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/CardNotification.cpp @@ -33,6 +33,9 @@ namespace AzQtComponents titleLabel->setObjectName("Title"); titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); titleLabel->setWordWrap(true); + titleLabel->setTextFormat(Qt::RichText); + titleLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + titleLabel->setOpenExternalLinks(true); QHBoxLayout* headerLayout = new QHBoxLayout(headerFrame); headerLayout->setSizeConstraint(QLayout::SetMinimumSize); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.cpp index d2d773ff93..3061ceedd5 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.cpp @@ -6,10 +6,11 @@ * */ +#include #include #include -#include +#include namespace AzQtComponents { @@ -24,7 +25,12 @@ namespace AzQtComponents // Trigger Qt's save filename dialog // If filePath isn't empty, it means we are prompting again because the filename was invalid, // so pass it instead of the directory so the filename is pre-filled in for the user - filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, selectedFilter, options); + QString localSelectedFilter; + filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, &localSelectedFilter, options); + if (selectedFilter) + { + *selectedFilter = localSelectedFilter; + } if (!filePath.isEmpty()) { @@ -32,15 +38,39 @@ namespace AzQtComponents QString fileName = fileInfo.fileName(); // Check if the filename has any invalid characters - QRegExp validFileNameRegex("^[a-zA-Z0-9_\\-./]*$"); - shouldPromptAgain = !validFileNameRegex.exactMatch(fileName); + QRegularExpression validFileNameRegex("^[a-zA-Z0-9_\\-./]*$"); + QRegularExpressionMatch validFileNameMatch = validFileNameRegex.match(fileName); // If the filename had invalid characters, then show a warning message and then we will re-prompt the save filename dialog - if (shouldPromptAgain) + if (!validFileNameMatch.hasMatch()) { QMessageBox::warning(parent, QObject::tr("Invalid filename"), QObject::tr("O3DE assets are restricted to alphanumeric characters, hyphens (-), underscores (_), and dots (.)\n\n%1").arg(fileName)); + shouldPromptAgain = true; + continue; } + else + { + shouldPromptAgain = false; + } +#if AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION + // If a filter was selected, then make sure that the resulting filename ends with that extension. On systems that use the default QFileDialog, + // the extension is not guaranteed to be set in the resulting filename + if (FileDialog::ApplyMissingExtension(localSelectedFilter, filePath)) + { + // If an extension had to be applied, then the file dialog did not handle the case of overwriting existing files. + // We need to check that condition before we proceed + QFileInfo updatedFilePath(filePath); + + if (updatedFilePath.exists()) + { + QMessageBox::StandardButton overwriteSelection = QMessageBox::question(parent, + QObject::tr("File exists"), + QObject::tr("%1 exists. Do you want to overwrite the existing file?").arg(updatedFilePath.fileName())); + shouldPromptAgain = (overwriteSelection == QMessageBox::No); + } + } +#endif // AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION } else { @@ -51,4 +81,56 @@ namespace AzQtComponents return filePath; } + + bool FileDialog::ApplyMissingExtension(const QString& selectedFilter, QString& filePath) + { + if (selectedFilter.isEmpty()) + { + return false; + } + + // According to the QT documentation for QFileDialog, the selected filter will come in the form + // ( .. ) + // + // For example: + // "Images (*.gif *.png *.jpg)" + // + // Extract the contents of the (s) inside the parenthesis and split them based on a whitespace or comma + const QRegularExpression filterContent(".*\\((?[^\\)]+)\\)"); + QRegularExpressionMatch filterContentMatch = filterContent.match(selectedFilter); + if (!filterContentMatch.hasMatch()) + { + return false; + } + QString filterExtensionsString = filterContentMatch.captured("filters"); + QStringList filterExtensionsFull = filterExtensionsString.split(" ", Qt::SkipEmptyParts); + if (filterExtensionsFull.length() <= 0) + { + return false; + } + + // If there are multiple suffixes in the selected filter, then default to the first one if a suffix needs to be appended + QString defaultSuffix = filterExtensionsFull[0].mid(1); + + // Iterate through the filter patterns to see if the current filename matches + QFileInfo fileInfo(filePath); + bool extensionNeeded = true; + for (const QString& filterExtensionFull : filterExtensionsFull) + { + QString wildcardExpression = QRegularExpression::wildcardToRegularExpression(filterExtensionFull); + QRegularExpression filterPattern(wildcardExpression, AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY); + QRegularExpressionMatch filterPatternMatch = filterPattern.match(fileInfo.fileName()); + if (filterPatternMatch.hasMatch()) + { + // The filename matches one of the filter patterns already, the extension does not need to be added to the filename + extensionNeeded = false; + } + } + if (extensionNeeded) + { + // If the current (if any) suffix does not match, automatically add the default suffix for the selected filter + filePath.append(defaultSuffix); + } + return extensionNeeded; + } } // namespace AzQtComponents diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.h index 6b63404949..9f27431400 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/FileDialog.h @@ -24,6 +24,12 @@ namespace AzQtComponents static QString GetSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(), const QString& dir = QString(), const QString& filter = QString(), QString* selectedFilter = nullptr, QFileDialog::Options options = QFileDialog::Options()); + + //! Helper method that parses a selected filter from Qt's QFileDialog::getSaveFileName and applies the + //! selected filter's extension to the filePath if it doesnt already have the extension. This is needed + //! on platforms that do not have a default file dialog (These platforms uses Qt's custom file dialog which will + //! not apply the filter's extension automatically on user entered filenames) + static bool ApplyMissingExtension(const QString& selectedFilter, QString& filePath); }; } // namespace AzQtComponents diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake index 7f204e5022..f292c29c3c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake @@ -12,4 +12,7 @@ set(FILES ../../Utilities/QtWindowUtilities_linux.cpp ../../Utilities/ScreenGrabber_linux.cpp ../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp + ../../../Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp + ../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h + ../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake index 50084d7a1e..4addf53599 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake @@ -12,4 +12,7 @@ set(FILES ../../Utilities/QtWindowUtilities_mac.mm ../../Utilities/ScreenGrabber_mac.mm ../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp + ../../../Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp + ../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h + ../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake index 37d1d0f390..bf0d0bb785 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,7 @@ set(FILES ../../natvis/qt.natvis ../../../Platform/Windows/AzQtComponents/Utilities/HandleDpiAwareness_Windows.cpp + ../../../Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp ../../Utilities/MouseHider_win.cpp ../../Utilities/QtWindowUtilities_win.cpp ../../Utilities/ScreenGrabber_win.cpp @@ -17,4 +18,6 @@ set(FILES ../../Components/TitleBarOverdrawScreenHandler_win.h ../../Components/TitleBarOverdrawScreenHandler_win.cpp ../../../Platform/Windows/AzQtComponents/Components/StyledDockWidget_Windows.cpp + ../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h + ../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Tests/FileDialogTests.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Tests/FileDialogTests.cpp new file mode 100644 index 0000000000..e3dc4b4f0d --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Tests/FileDialogTests.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtension_Success) +{ + const QString textFiler{"Text Files (*.txt)"}; + QString testPath{"testFile"}; + bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath); + EXPECT_TRUE(result); + EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData()); +} + +TEST(AzQtComponents, ApplyMissingExtension_NoUpdateExistingExtension_Success) +{ + const QString textFiler{"Text Files (*.txt)"}; + QString testPath{"testFile.txt"}; + bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath); + EXPECT_FALSE(result); + EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData()); +} + +TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtensionMultipleExtensionFilter_Success) +{ + const QString textFiler{"Image Files (*.jpg *.bmp *.png)"}; + QString testPath{"testFile"}; + bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath); + EXPECT_TRUE(result); + EXPECT_STRCASEEQ("testFile.jpg", testPath.toUtf8().constData()); +} + +TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionMultipleExtensionFilter_Success) +{ + const QString textFiler{"Image Files (*.jpg *.bmp *.png)"}; + QString testPath{"testFile.png"}; + bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath); + EXPECT_FALSE(result); + EXPECT_STRCASEEQ("testFile.png", testPath.toUtf8().constData()); +} + +TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionEmptyFilter_Success) +{ + const QString textFiler{""}; + QString testPath{"testFile"}; + bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath); + EXPECT_FALSE(result); + EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData()); +} + +TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionInvalidFilter_Success) +{ + const QString textFiler{"Bad Filter!!"}; + QString testPath{"testFile"}; + bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath); + EXPECT_FALSE(result); + EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData()); +} diff --git a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake index 8219ab04b2..4c343b852c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake @@ -271,7 +271,6 @@ set(FILES Utilities/ColorUtilities.h Utilities/Conversions.h Utilities/Conversions.cpp - Utilities/DesktopUtilities.cpp Utilities/DesktopUtilities.h Utilities/HandleDpiAwareness.cpp Utilities/HandleDpiAwareness.h diff --git a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_testing_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_testing_files.cmake index 2611063305..b4ab487c79 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_testing_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_testing_files.cmake @@ -9,6 +9,7 @@ set(FILES Tests/AzQtComponentTests.cpp Tests/ColorControllerTests.cpp + Tests/FileDialogTests.cpp Tests/FloatToStringConversionTests.cpp Tests/HexParsingTests.cpp Tests/StyleSheetCacheTests.cpp diff --git a/Code/Framework/AzQtComponents/CMakeLists.txt b/Code/Framework/AzQtComponents/CMakeLists.txt index dd217a7be2..a24b68bcd7 100644 --- a/Code/Framework/AzQtComponents/CMakeLists.txt +++ b/Code/Framework/AzQtComponents/CMakeLists.txt @@ -10,6 +10,8 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) return() endif() +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}) + ly_add_target( NAME AzQtComponents SHARED NAMESPACE AZ @@ -26,6 +28,7 @@ ly_add_target( AzQtComponents PUBLIC . + ${pal_dir} COMPILE_DEFINITIONS PRIVATE AZ_QT_COMPONENTS_EXPORT_SYMBOLS @@ -53,6 +56,7 @@ ly_add_target( . AzQtComponents AzQtComponents/Gallery + ${pal_dir} BUILD_DEPENDENCIES PRIVATE 3rdParty::Qt::Svg @@ -86,6 +90,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE Tests AzQtComponents + ${pal_dir} BUILD_DEPENDENCIES PRIVATE AZ::AzQtComponents diff --git a/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h new file mode 100644 index 0000000000..685ac4dfed --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 1 +#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption diff --git a/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h new file mode 100644 index 0000000000..101fe3a494 --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp new file mode 100644 index 0000000000..e4ddaceec7 --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +namespace AzQtComponents +{ + void ShowFileOnDesktop(const QString& path) + { + const char* defaultNautilusPath = "/usr/bin/nautilus"; + const char* defaultXdgOpenPath = "/usr/bin/xdg-open"; + + // Determine if Nautilus (for Gnome Desktops) is available because it supports opening the file manager + // and selecting a specific file + bool nautilusAvailable = QFileInfo(defaultNautilusPath).exists(); + + QFileInfo pathInfo(path); + if (pathInfo.isDir()) + { + QProcess::startDetached(defaultXdgOpenPath, { path }); + } + else + { + if (nautilusAvailable) + { + QProcess::startDetached(defaultNautilusPath, { "--select", path }); + } + else + { + QDir parentDir { pathInfo.dir() }; + QProcess::startDetached(defaultXdgOpenPath, { parentDir.path() }); + } + } + } + + QString fileBrowserActionName() + { + const char* exploreActionName = "Open in file browser"; + return QObject::tr(exploreActionName); + } +} diff --git a/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h new file mode 100644 index 0000000000..1cb91ec09e --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0 +#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption diff --git a/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h new file mode 100644 index 0000000000..9b285476c1 --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp similarity index 67% rename from Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp rename to Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp index c210f17136..5920dc76c6 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp +++ b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp @@ -15,21 +15,6 @@ namespace AzQtComponents { void ShowFileOnDesktop(const QString& path) { -#if defined(AZ_PLATFORM_WINDOWS) - - // Launch explorer at the path provided - QStringList args; - if (!QFileInfo(path).isDir()) - { - // Folders are just opened, files are selected - args << "/select,"; - } - args << QDir::toNativeSeparators(path); - - QProcess::startDetached("explorer", args); - -#else - if (QFileInfo(path).isDir()) { QProcess::startDetached("/usr/bin/osascript", { "-e", @@ -43,19 +28,11 @@ namespace AzQtComponents QProcess::startDetached("/usr/bin/osascript", { "-e", QStringLiteral("tell application \"Finder\" to activate") }); - -#endif } QString fileBrowserActionName() { -#ifdef AZ_PLATFORM_WINDOWS - const char* exploreActionName = "Open in Explorer"; -#elif defined(AZ_PLATFORM_MAC) const char* exploreActionName = "Open in Finder"; -#else - const char* exploreActionName = "Open in file browser"; -#endif return QObject::tr(exploreActionName); } } diff --git a/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h new file mode 100644 index 0000000000..2c3efc5e6c --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h new file mode 100644 index 0000000000..edba0043b4 --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0 +#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::CaseInsensitiveOption diff --git a/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp new file mode 100644 index 0000000000..796e2c0ccf --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +namespace AzQtComponents +{ + void ShowFileOnDesktop(const QString& path) + { + // Launch explorer at the path provided + QStringList args; + if (!QFileInfo(path).isDir()) + { + // Folders are just opened, files are selected + args << "/select,"; + } + args << QDir::toNativeSeparators(path); + + QProcess::startDetached("explorer", args); + } + + QString fileBrowserActionName() + { + const char* exploreActionName = "Open in Explorer"; + return QObject::tr(exploreActionName); + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ComponentModeCollectionInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ComponentModeCollectionInterface.h new file mode 100644 index 0000000000..4f9d72cb4d --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ComponentModeCollectionInterface.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AzToolsFramework +{ + //! The AZ::Interface for component mode collection queries. + class ComponentModeCollectionInterface + { + public: + AZ_RTTI(ComponentModeCollectionInterface, "{DFAA4450-BBCD-47C0-9B91-FEA2DBD9B152}"); + + virtual ~ComponentModeCollectionInterface() = default; + + //! Retrieves the list of all Component types (usually one). + //! @note If called outside of component mode, an empty vector will be returned. + virtual AZStd::vector GetComponentTypes() const = 0; + }; +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.cpp new file mode 100644 index 0000000000..a61ea088d0 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace AzToolsFramework +{ + void ViewportEditorModeNotifications::Reflect(AZ::ReflectContext* context) + { + if (auto* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("ViewportEditorModeNotificationsBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "editor") + ->Event("OnEditorModeActivated", &ViewportEditorModeNotifications::OnEditorModeActivated) + ->Event("OnEditorModeDeactivated", &ViewportEditorModeNotifications::OnEditorModeDeactivated) + ; + } + } +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h index 4fcb891e61..4fb4191d45 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h @@ -34,6 +34,8 @@ namespace AzToolsFramework class ViewportEditorModesInterface { public: + AZ_RTTI(ViewportEditorModesInterface, "{2421496C-4A46-41C9-8AEF-AE2B6E43E6CF}"); + virtual ~ViewportEditorModesInterface() = default; //! Returns true if the specified editor mode is active, otherwise false. @@ -52,6 +54,9 @@ namespace AzToolsFramework using BusIdType = ViewportEditorModeTrackerInfo::IdType; ////////////////////////////////////////////////////////////////////////// + AZ_RTTI(ViewportEditorModeNotifications, "{9469DE39-6C21-423C-94FA-EF3A9616B14F}", AZ::EBusTraits); + static void Reflect(AZ::ReflectContext* context); + //! Notifies subscribers of the a given viewport to the activation of the specified editor mode. virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp index 8856989911..a159882f72 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp @@ -410,7 +410,7 @@ namespace AzToolsFramework filter.append(ext); if (i < n - 1) { - filter.append(", "); + filter.append(" "); } } filter.append(")"); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp index df93e924b8..1768cb5920 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp @@ -203,6 +203,12 @@ namespace AzToolsFramework } } + AZStd::vector ComponentModeCollection::GetComponentTypes() const + { + // If in component mode, return the active component types, otherwise return an empty vector + return InComponentMode() ? m_activeComponentTypes : AZStd::vector{}; + } + void ComponentModeCollection::BeginComponentMode() { m_selectedComponentModeIndex = 0; @@ -211,13 +217,6 @@ namespace AzToolsFramework // notify listeners the editor has entered ComponentMode - listeners may // wish to modify state to indicate this (e.g. appearance, functionality etc.) - EditorComponentModeNotificationBus::Event( - GetEntityContextId(), &EditorComponentModeNotifications::EnteredComponentMode, - m_activeComponentTypes); - - // this call to activate the component mode editor state should eventually replace the bus call in - // ComponentModeCollection::BeginComponentMode() to EditorComponentModeNotifications::EnteredComponentMode - // such that all of the notifications for activating/deactivating the different editor modes are in a central location m_viewportEditorModeTracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Component); // enable actions for the first/primary ComponentMode @@ -288,14 +287,6 @@ namespace AzToolsFramework // notify listeners the editor has left ComponentMode - listeners may // wish to modify state to indicate this (e.g. appearance, functionality etc.) - EditorComponentModeNotificationBus::Event( - GetEntityContextId(), - &EditorComponentModeNotifications::LeftComponentMode, - m_activeComponentTypes); - - // this call to deactivate the component mode editor state should eventually replace the bus call in - // ComponentModeCollection::EndComponentMode() to EditorComponentModeNotifications::LeftComponentMode - // such that all of the notifications for activating/deactivating the different editor modes are in a central location m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Component); // clear stored modes and builders for this ComponentMode diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h index 9e299d2323..3b7690b969 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -21,6 +22,7 @@ namespace AzToolsFramework { /// Manages all individual ComponentModes for a single instance of Editor wide ComponentMode. class ComponentModeCollection + : public ComponentModeCollectionInterface { public: AZ_CLASS_ALLOCATOR_DECL @@ -89,6 +91,9 @@ namespace AzToolsFramework /// Called once each time a ComponentMode is added. void PopulateViewportUi(); + // ComponentModeCollectionInterface overrides ... + AZStd::vector GetComponentTypes() const override; + private: // Internal helper used by Select[|Prev|Next]ActiveComponentMode bool ActiveComponentModeChanged(const AZ::Uuid& previousComponentType); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeDelegate.cpp index 325fc3909b..f0baeaea7e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeDelegate.cpp @@ -24,18 +24,8 @@ namespace AzToolsFramework : public EditorComponentModeNotificationBus::Handler , public AZ::BehaviorEBusHandler { - AZ_EBUS_BEHAVIOR_BINDER(EditorComponentModeNotificationBusHandler, "{AD2F4204-0913-4FC9-9A10-492538F60C70}", AZ::SystemAllocator, - EnteredComponentMode, LeftComponentMode, ActiveComponentModeChanged); - - void EnteredComponentMode(const AZStd::vector& componentTypes) override - { - Call(FN_EnteredComponentMode, componentTypes); - } - - void LeftComponentMode(const AZStd::vector& componentTypes) override - { - Call(FN_LeftComponentMode, componentTypes); - } + AZ_EBUS_BEHAVIOR_BINDER( + EditorComponentModeNotificationBusHandler, "{AD2F4204-0913-4FC9-9A10-492538F60C70}", AZ::SystemAllocator, ActiveComponentModeChanged); void ActiveComponentModeChanged(const AZ::Uuid& componentType) override { @@ -171,8 +161,6 @@ namespace AzToolsFramework ->Attribute(AZ::Script::Attributes::Category, "Editor") ->Attribute(AZ::Script::Attributes::Module, "editor") ->Handler() - ->Event("EnteredComponentMode", &EditorComponentModeNotifications::EnteredComponentMode) - ->Event("LeftComponentMode", &EditorComponentModeNotifications::LeftComponentMode) ->Event("ActiveComponentModeChanged", &EditorComponentModeNotifications::ActiveComponentModeChanged) ; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorComponentModeBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorComponentModeBus.h index 5be535e1ed..67a8291e22 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorComponentModeBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorComponentModeBus.h @@ -238,12 +238,6 @@ namespace AzToolsFramework using BusIdType = AzFramework::EntityContextId; static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - /// Called when Editor enters ComponentMode - pass the list of all Component types (usually one). - virtual void EnteredComponentMode(const AZStd::vector& componentTypes) = 0; - - /// Called when Editor leaves ComponentMode - pass the list of all Component types (usually one). - virtual void LeftComponentMode(const AZStd::vector& componentTypes) = 0; - /// Called when Tab is pressed to cycle the 'selected' ComponentMode (which shortcuts/actions are active). /// Also called when directly selecting a Component in the EntityOutliner. virtual void ActiveComponentModeChanged(const AZ::Uuid& /*componentType*/) {} @@ -255,42 +249,6 @@ namespace AzToolsFramework /// Type to inherit to implement EditorComponentModeNotifications. using EditorComponentModeNotificationBus = AZ::EBus; - /// Helper for EditorComponentModeNotifications to be used - /// as a member instead of inheriting from EBus directly. - class EditorComponentModeNotificationBusImpl - : public EditorComponentModeNotificationBus::Handler - { - public: - /// Set the function to be called when entering ComponentMode. - void SetEnteredComponentModeFunc( - const AZStd::function&)>& enteredComponentModeFunc) - { - m_enteredComponentModeFunc = enteredComponentModeFunc; - } - - /// Set the function to be called when leaving ComponentMode. - void SetLeftComponentModeFunc( - const AZStd::function&)>& leftComponentModeFunc) - { - m_leftComponentModeFunc = leftComponentModeFunc; - } - - private: - // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override - { - m_enteredComponentModeFunc(componentModeTypes); - } - - void LeftComponentMode(const AZStd::vector& componentModeTypes) override - { - m_leftComponentModeFunc(componentModeTypes); - } - - AZStd::function&)> m_enteredComponentModeFunc; ///< Function to call when entering ComponentMode. - AZStd::function&)> m_leftComponentModeFunc; ///< Function to call when leaving ComponentMode. - }; - /// Helper to answer if the Editor is in ComponentMode or not. inline bool InComponentMode() { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h index 45da3a9d8f..2d7d9dc511 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h @@ -11,6 +11,8 @@ #include #include +#include + namespace AzToolsFramework { //! Outcome object that returns an error message in case of failure to allow caller to handle internal errors. @@ -43,7 +45,7 @@ namespace AzToolsFramework //! @param entityId The entityId whose open state will be set. //! @param open True if the container should be opened, false if it should be closed. //! @return An error message if the operation was invalid, success otherwise. - virtual ContainerEntityOperationResult SetContainerOpenState(AZ::EntityId entityId, bool open) = 0; + virtual ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) = 0; //! If the entity id provided is registered as a container, it returns whether it's open. //! @note the default value for non-containers is true, so this function can be called without @@ -56,6 +58,16 @@ namespace AzToolsFramework //! @return The highest closed entity container id if any, or entityId otherwise. virtual AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const = 0; + //! Clears all open state information for Container Entities for the EntityContextId provided. + //! Used when context is switched, for example in the case of a new root prefab being loaded + //! in place of an old one. + //! @note Clear is meant to be called when no container is registered for the context provided. + //! @return An error message if any container was registered for the context, success otherwise. + virtual ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) = 0; + + //! Returns true if one of the ancestors of entityId is a closed container entity. + virtual bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const = 0; + }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp index 0ea2eeb5f6..61b257a189 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp @@ -10,16 +10,19 @@ #include #include +#include namespace AzToolsFramework { void ContainerEntitySystemComponent::Activate() { AZ::Interface::Register(this); + EditorEntityContextNotificationBus::Handler::BusConnect(); } void ContainerEntitySystemComponent::Deactivate() { + EditorEntityContextNotificationBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); } @@ -63,7 +66,7 @@ namespace AzToolsFramework return m_containers.contains(entityId); } - ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpenState(AZ::EntityId entityId, bool open) + ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpen(AZ::EntityId entityId, bool open) { if (!IsContainer(entityId)) { @@ -87,7 +90,7 @@ namespace AzToolsFramework bool ContainerEntitySystemComponent::IsContainerOpen(AZ::EntityId entityId) const { - // If the entity is not a container, it should behave as open. + // Non-container entities behave the same as open containers. This saves the caller an additional check. if(!m_containers.contains(entityId)) { return true; @@ -99,8 +102,17 @@ namespace AzToolsFramework AZ::EntityId ContainerEntitySystemComponent::FindHighestSelectableEntity(AZ::EntityId entityId) const { + if (!entityId.IsValid()) + { + return entityId; + } + + // Return the highest closed container, or the entity if none is found. AZ::EntityId highestSelectableEntityId = entityId; + // Skip the queried entity, as we only want to check its ancestors. + AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId); + // Go up the hierarchy until you hit the root while (entityId.IsValid()) { @@ -117,4 +129,72 @@ namespace AzToolsFramework return highestSelectableEntityId; } + void ContainerEntitySystemComponent::OnEntityStreamLoadSuccess() + { + // We don't yet support multiple entity contexts, so just use the default. + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + Clear(editorEntityContextId); + } + + ContainerEntityOperationResult ContainerEntitySystemComponent::Clear(AzFramework::EntityContextId entityContextId) + { + // We don't yet support multiple entity contexts, so only clear the default. + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + if (entityContextId != editorEntityContextId) + { + return AZ::Failure(AZStd::string( + "Error in ContainerEntitySystemComponent::Clear - cannot clear non-default Entity Context!")); + } + + if (!m_containers.empty()) + { + return AZ::Failure(AZStd::string( + "Error in ContainerEntitySystemComponent::Clear - cannot clear container states if entities are still registered!")); + } + + m_openContainers.clear(); + + return AZ::Success(); + } + + bool ContainerEntitySystemComponent::IsUnderClosedContainerEntity(AZ::EntityId entityId) const + { + if (!entityId.IsValid()) + { + return false; + } + + // Skip the queried entity, as we only want to check its ancestors. + AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId); + + // Go up the hierarchy until you hit the root. + while (entityId.IsValid()) + { + if (!IsContainerOpen(entityId)) + { + // One of the ancestors is a container and it's closed. + return true; + } + + AZ::EntityId parentId; + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId); + + if (parentId == entityId) + { + // In some circumstances, querying a root level entity with GetParentId will return + // the entity itself instead of an invalid entityId. + break; + } + + entityId = parentId; + } + + // All ancestors are either regular entities or open containers. + return false; + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h index 18c0fb70c6..7a11e05096 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h @@ -12,6 +12,7 @@ #include #include +#include namespace AzToolsFramework { @@ -23,6 +24,7 @@ namespace AzToolsFramework class ContainerEntitySystemComponent final : public AZ::Component , private ContainerEntityInterface + , private EditorEntityContextNotificationBus::Handler { public: AZ_COMPONENT(ContainerEntitySystemComponent, "{74349759-B36B-44A6-B89F-F45D7111DD11}"); @@ -42,9 +44,14 @@ namespace AzToolsFramework ContainerEntityOperationResult RegisterEntityAsContainer(AZ::EntityId entityId) override; ContainerEntityOperationResult UnregisterEntityAsContainer(AZ::EntityId entityId) override; bool IsContainer(AZ::EntityId entityId) const override; - ContainerEntityOperationResult SetContainerOpenState(AZ::EntityId entityId, bool open) override; + ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) override; bool IsContainerOpen(AZ::EntityId entityId) const override; AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override; + ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override; + bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override; + + // EditorEntityContextNotificationBus overrides ... + void OnEntityStreamLoadSuccess() override; private: AZStd::unordered_set m_containers; //!< All entities in this set are containers. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp index 7789070209..8d40162f52 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp @@ -16,9 +16,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -588,15 +590,40 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AzToolsFramework); + // Detect if the Entity is Visible bool visible = false; EditorEntityInfoRequestBus::EventResult( visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible); - bool locked = false; - EditorEntityInfoRequestBus::EventResult( - locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + if (!visible) + { + return false; + } - return visible && !locked; + // Detect if the Entity is Locked + bool locked = false; + EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + + if (locked) + { + return false; + } + + // Detect if the Entity is part of the Editor Focus + if (auto focusModeInterface = AZ::Interface::Get(); + !focusModeInterface->IsInFocusSubTree(entityId)) + { + return false; + } + + // Detect if the Entity is a descendant of a closed container + if (auto containerEntityInterface = AZ::Interface::Get(); + containerEntityInterface->IsUnderClosedContainerEntity(entityId)) + { + return false; + } + + return true; } static void SetEntityLockStateRecursively( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h index ab8629ac85..81f1f7bb3b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h @@ -28,8 +28,10 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// //! Triggered when the editor focus is changed to a different entity. - //! @param entityId The entity the focus has been moved to. - virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0; + //! @param previousFocusEntityId The entity the focus has been moved from. + //! @param newFocusEntityId The entity the focus has been moved to. + virtual void OnEditorFocusChanged( + [[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) {} protected: ~FocusModeNotifications() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index af518afd66..f592c471d0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -71,8 +71,9 @@ namespace AzToolsFramework return; } + AZ::EntityId previousFocusEntityId = m_focusRoot; m_focusRoot = entityId; - FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot); + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); if (auto tracker = AZ::Interface::Get(); tracker != nullptr) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index 5fcf7e11d3..18acccf049 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -261,10 +261,10 @@ namespace AzToolsFramework // ensures cursor positions are refreshed correctly with context menu focus changes) if (eventType == QEvent::FocusIn) { - const auto widgetCursorPosition = m_sourceWidget->mapFromGlobal(QCursor::pos()); - if (m_sourceWidget->geometry().contains(widgetCursorPosition)) + const auto globalCursorPosition = QCursor::pos(); + if (m_sourceWidget->geometry().contains(globalCursorPosition)) { - HandleMouseMoveEvent(widgetCursorPosition); + HandleMouseMoveEvent(globalCursorPosition); } } } @@ -290,7 +290,7 @@ namespace AzToolsFramework else if (eventType == QEvent::Type::MouseMove) { auto mouseEvent = static_cast(event); - HandleMouseMoveEvent(mouseEvent->pos()); + HandleMouseMoveEvent(mouseEvent->globalPos()); } // Map wheel events to the mouse Z movement channel. else if (eventType == QEvent::Type::Wheel) @@ -370,11 +370,12 @@ namespace AzToolsFramework return QPoint{ denormalizedX, denormalizedY }; } - void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& cursorPosition) + void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition) { - const QPoint cursorDelta = cursorPosition - m_previousCursorPosition; + const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition; - m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(cursorPosition); + m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = + WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition)); m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta); ProcessPendingMouseEvents(cursorDelta); @@ -382,12 +383,11 @@ namespace AzToolsFramework if (m_capturingCursor) { // Reset our cursor position to the previous point - const QPoint screenCursorPosition = m_sourceWidget->mapToGlobal(m_previousCursorPosition); - AzQtComponents::SetCursorPos(screenCursorPosition); + AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition); } else { - m_previousCursorPosition = cursorPosition; + m_previousGlobalCursorPosition = globalCursorPosition; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index aaf3fb6295..5add24ad84 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -129,7 +129,7 @@ namespace AzToolsFramework // Handle mouse click events. void HandleMouseButtonEvent(QMouseEvent* mouseEvent); // Handle mouse move events. - void HandleMouseMoveEvent(const QPoint& cursorPosition); + void HandleMouseMoveEvent(const QPoint& globalCursorPosition); // Handles key press / release events (or ShortcutOverride events for keys listed in m_highPriorityKeys). void HandleKeyEvent(QKeyEvent* keyEvent); // Handles mouse wheel events. @@ -156,8 +156,8 @@ namespace AzToolsFramework AZStd::unordered_set m_highPriorityKeys; // A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device. AZStd::unordered_map m_channels; - // Where the position of the mouse cursor was at the last cursor event. - QPoint m_previousCursorPosition; + // Where the mouse cursor was at the last cursor event. + QPoint m_previousGlobalCursorPosition; // The source widget to map events from, used to calculate the relative mouse position within the widget bounds. QWidget* m_sourceWidget; // Flags whether or not Qt events should currently be processed. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp index 3a7fffb3be..4c7afa2275 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp @@ -116,7 +116,7 @@ namespace AzToolsFramework { return AZ::Intersect::IntersectRayBox( rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, m_halfExtents.GetX(), m_halfExtents.GetY(), - m_halfExtents.GetZ(), rayIntersectionDistance) > 0; + m_halfExtents.GetZ(), rayIntersectionDistance); } void ManipulatorBoundBox::SetShapeData(const BoundRequestShapeBase& shapeData) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp index f60759f77e..ea0fa55256 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp @@ -224,6 +224,7 @@ namespace AzToolsFramework return false; } + AZ::Data::SerializedAssetTracker* assetTracker = settings.m_metadata.Find(); referencedAssets = AZStd::move(assetTracker->GetTrackedAssets()); @@ -245,6 +246,30 @@ namespace AzToolsFramework entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random); } + // some assets may come in from the JSON serialzier with no AssetID, but have an asset hint + // this attempts to fix up the assets using the assetHint field + auto fixUpInvalidAssets = [](AZ::Data::Asset& asset) + { + if (!asset.GetId().IsValid() && !asset.GetHint().empty()) + { + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, + asset.GetHint().c_str(), + AZ::Data::s_invalidAssetType, + false); + + if (assetId.IsValid()) + { + asset.Create(assetId, true); + } + } + }; + + auto tracker = AZ::Data::SerializedAssetTracker{}; + tracker.SetAssetFixUp(fixUpInvalidAssets); + AZ::JsonDeserializerSettings settings; // The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is // specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta @@ -252,16 +277,17 @@ namespace AzToolsFramework settings.m_metadata.Add(static_cast(&entityIdMapper)); settings.m_metadata.Add(&entityIdMapper); settings.m_metadata.Create(newlyAddedEntities); + settings.m_metadata.Add(tracker); AZStd::string scratchBuffer; auto issueReportingCallback = [&scratchBuffer]( - AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, - AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode + AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, + AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode { return Internal::JsonIssueReporter(scratchBuffer, message, result, path); }; settings.m_reporting = AZStd::move(issueReportingCallback); - + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings); AZ::Data::AssetManager::Instance().ResumeAssetRelease(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 9ffc18af5a..a79b9eb73d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -8,12 +8,14 @@ #include +#include #include #include #include #include #include #include +#include namespace AzToolsFramework::Prefab { @@ -28,10 +30,12 @@ namespace AzToolsFramework::Prefab EditorEntityContextNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); + AZ::Interface::Register(this); } PrefabFocusHandler::~PrefabFocusHandler() { + AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); EditorEntityContextNotificationBus::Handler::BusDisconnect(); } @@ -61,6 +65,44 @@ namespace AzToolsFramework::Prefab } PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) + { + // Initialize Undo Batch object + ScopedUndoBatch undoBatch("Edit Prefab"); + + // Clear selection + { + const EntityIdList selectedEntities = EntityIdList{}; + auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection"); + selectionUndo->SetParent(undoBatch.GetUndoBatch()); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities); + } + + // Edit Prefab + { + auto editUndo = aznew PrefabFocusUndo("Edit Prefab"); + editUndo->Capture(entityId); + editUndo->SetParent(undoBatch.GetUndoBatch()); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo); + } + + return AZ::Success(); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) + { + if (index < 0 || index >= m_instanceFocusVector.size()) + { + return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); + } + + InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; + + FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId()); + + return AZ::Success(); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) { InstanceOptionalReference focusedInstance; @@ -85,18 +127,6 @@ namespace AzToolsFramework::Prefab return FocusOnPrefabInstance(focusedInstance); } - PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) - { - if (index < 0 || index >= m_instanceFocusVector.size()) - { - return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); - } - - InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; - - return FocusOnPrefabInstance(focusedInstance); - } - PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance) { if (!focusedInstance.has_value()) @@ -122,17 +152,10 @@ namespace AzToolsFramework::Prefab if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) { containerEntityId = focusedInstance->get().GetContainerEntityId(); - - // Select the container entity - AzToolsFramework::SelectEntity(containerEntityId); } else { containerEntityId = AZ::EntityId(); - - // Clear the selection - AzToolsFramework::SelectEntities({}); - } // Focus on the descendants of the container entity @@ -161,6 +184,17 @@ namespace AzToolsFramework::Prefab return m_focusedInstance; } + AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const + { + if (!m_focusedInstance.has_value()) + { + // PrefabFocusHandler has not been initialized yet. + return AZ::EntityId(); + } + + return m_focusedInstance->get().GetContainerEntityId(); + } + bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const { if (!m_focusedInstance.has_value()) @@ -196,8 +230,11 @@ namespace AzToolsFramework::Prefab Initialize(); } + // Clear the old focus vector + m_instanceFocusVector.clear(); + // Focus on the root prefab (AZ::EntityId() will default to it) - FocusOnOwningPrefab(AZ::EntityId()); + FocusOnPrefabInstanceOwningEntityId(AZ::EntityId()); } void PrefabFocusHandler::RefreshInstanceFocusList() @@ -230,7 +267,7 @@ namespace AzToolsFramework::Prefab { if (instance.has_value()) { - m_containerEntityInterface->SetContainerOpenState(instance->get().GetContainerEntityId(), true); + m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true); } } } @@ -241,7 +278,7 @@ namespace AzToolsFramework::Prefab { if (instance.has_value()) { - m_containerEntityInterface->SetContainerOpenState(instance->get().GetContainerEntityId(), false); + m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 2f631f772d..80b7a6859c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace AzToolsFramework @@ -28,6 +29,7 @@ namespace AzToolsFramework::Prefab //! Handles Prefab Focus mode, determining which prefab file entity changes will target. class PrefabFocusHandler final : private PrefabFocusInterface + , private PrefabFocusPublicInterface , private EditorEntityContextNotificationBus::Handler { public: @@ -39,10 +41,14 @@ namespace AzToolsFramework::Prefab void Initialize(); // PrefabFocusInterface overrides ... - PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; - PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; + PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override; TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override; InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override; + + // PrefabFocusPublicInterface overrides ... + PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; + PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; + AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override; const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 1c0f4f85e9..25c83b89bc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -20,7 +20,7 @@ namespace AzToolsFramework::Prefab { using PrefabFocusOperationResult = AZ::Outcome; - //! Interface to handle operations related to the Prefab Focus system. + //! Interface to handle internal operations related to the Prefab Focus system. class PrefabFocusInterface { public: @@ -28,29 +28,13 @@ namespace AzToolsFramework::Prefab //! Set the focused prefab instance to the owning instance of the entityId provided. //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. - virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; - - //! Set the focused prefab instance to the instance at position index of the current path. - //! @param index The index of the instance in the current path that we want the prefab system to focus on. - virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; + virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0; //! Returns the template id of the instance the prefab system is focusing on. virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0; //! Returns a reference to the instance the prefab system is focusing on. virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0; - - //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. - //! @param entityId The entityId of the queried entity. - //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. - virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; - - //! Returns the path from the root instance to the currently focused instance. - //! @return A path composed from the names of the container entities for the instance path. - virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0; - - //! Returns the size of the path to the currently focused instance. - virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h new file mode 100644 index 0000000000..86e476b56f --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace AzToolsFramework::Prefab +{ + using PrefabFocusOperationResult = AZ::Outcome; + + //! Public Interface for external systems to utilize the Prefab Focus system. + class PrefabFocusPublicInterface + { + public: + AZ_RTTI(PrefabFocusPublicInterface, "{53EE1D18-A41F-4DB1-9B73-9448F425722E}"); + + //! Set the focused prefab instance to the owning instance of the entityId provided. Supports undo/redo. + //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + + //! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo. + //! @param index The index of the instance in the current path that we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; + + //! Returns the entity id of the container entity for the instance the prefab system is focusing on. + virtual AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const = 0; + + //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. + //! @param entityId The entityId of the queried entity. + //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; + + //! Returns the path from the root instance to the currently focused instance. + //! @return A path composed from the names of the container entities for the instance path. + virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0; + + //! Returns the size of the path to the currently focused instance. + virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0; + }; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp new file mode 100644 index 0000000000..e5f664ea38 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include + +namespace AzToolsFramework::Prefab +{ + PrefabFocusUndo::PrefabFocusUndo(const AZStd::string& undoOperationName) + : UndoSystem::URSequencePoint(undoOperationName) + { + m_prefabFocusInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusInterface, "PrefabFocusUndo - Failed to grab prefab focus interface"); + + m_prefabFocusPublicInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusPublicInterface, "PrefabFocusUndo - Failed to grab prefab focus public interface"); + } + + bool PrefabFocusUndo::Changed() const + { + return true; + } + + void PrefabFocusUndo::Capture(AZ::EntityId entityId) + { + auto entityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + m_beforeEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(entityContextId); + m_afterEntityId = entityId; + } + + void PrefabFocusUndo::Undo() + { + m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_beforeEntityId); + } + + void PrefabFocusUndo::Redo() + { + m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_afterEntityId); + } + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h new file mode 100644 index 0000000000..3b257b6547 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AzToolsFramework::Prefab +{ + class PrefabFocusInterface; + class PrefabFocusPublicInterface; + + //! Undo node for prefab focus change operations. + class PrefabFocusUndo + : public UndoSystem::URSequencePoint + { + public: + explicit PrefabFocusUndo(const AZStd::string& undoOperationName); + + bool Changed() const override; + void Capture(AZ::EntityId entityId); + + void Undo() override; + void Redo() override; + + protected: + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; + + AZ::EntityId m_beforeEntityId; + AZ::EntityId m_afterEntityId; + }; +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 038f36eac9..b5f61b33bf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -257,9 +257,10 @@ namespace AzToolsFramework // Select Container Entity { - auto selectionUndo = aznew SelectionCommand({ containerEntityId }, "Select Prefab Container Entity"); + const EntityIdList selectedEntities = EntityIdList{ containerEntityId }; + auto selectionUndo = aznew SelectionCommand(selectedEntities, "Select Prefab Container Entity"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities); } } @@ -1097,7 +1098,7 @@ namespace AzToolsFramework // Select the duplicated entities/instances auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds); } return AZ::Success(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h index 6f074da878..acd8ba3966 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h @@ -90,7 +90,7 @@ namespace AzToolsFramework typedef SharedThumbnailKey BusIdType; //! notify product thumbnail that the data is ready - virtual void ThumbnailRendered(QPixmap& thumbnailImage) = 0; + virtual void ThumbnailRendered(const QPixmap& thumbnailImage) = 0; //! notify product thumbnail that the thumbnail failed to render virtual void ThumbnailFailedToRender() = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp index be29af5c0f..dd156e0e40 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ScriptEditorComponent.cpp @@ -1025,7 +1025,7 @@ namespace AzToolsFramework ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/LuaScript.svg") ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Script.png") - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/lua-script/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/scripting/lua-script/") ->DataElement("AssetRef", &ScriptEditorComponent::m_scriptAsset, "Script", "Which script to use") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEditorComponent::ScriptHasChanged) ->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg") diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 4364ae1efc..d3138f5139 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -313,7 +313,8 @@ namespace AzToolsFramework StyledTreeView::StartCustomDrag(indexListSorted, supportedActions); } - void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId) + void EntityOutlinerTreeView::OnEditorFocusChanged( + [[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) { viewport()->repaint(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 2ddbaaafa9..5d76ec6db2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -64,7 +64,7 @@ namespace AzToolsFramework void leaveEvent(QEvent* event) override; // FocusModeNotificationBus overrides ... - void OnEditorFocusChanged(AZ::EntityId entityId) override; + void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override; //! Renders the left side of the item: appropriate background, branch lines, icons. void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 8f90d2c0d7..4d53102edb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -292,8 +293,7 @@ namespace AzToolsFramework EntityOutlinerModelNotificationBus::Handler::BusConnect(); ToolsApplicationEvents::Bus::Handler::BusConnect(); EditorEntityContextNotificationBus::Handler::BusConnect(); - ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( - GetEntityContextId()); + ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId()); EditorEntityInfoNotificationBus::Handler::BusConnect(); Prefab::PrefabPublicNotificationBus::Handler::BusConnect(); EditorWindowUIRequestBus::Handler::BusConnect(); @@ -303,7 +303,7 @@ namespace AzToolsFramework { EditorWindowUIRequestBus::Handler::BusDisconnect(); Prefab::PrefabPublicNotificationBus::Handler::BusDisconnect(); - ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); + ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); EditorEntityInfoNotificationBus::Handler::BusDisconnect(); EditorPickModeNotificationBus::Handler::BusDisconnect(); EntityHighlightMessages::Bus::Handler::BusDisconnect(); @@ -324,7 +324,8 @@ namespace AzToolsFramework // Currently, the first behavior is implemented. void EntityOutlinerWidget::OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { - if (m_selectionChangeInProgress || !m_enableSelectionUpdates) + if (m_selectionChangeInProgress || !m_enableSelectionUpdates + || (selected.empty() && deselected.empty())) { return; } @@ -552,6 +553,13 @@ namespace AzToolsFramework return; } + // Do not display the context menu if the item under the mouse cursor is not selectable. + if (const QModelIndex& index = m_gui->m_objectTree->indexAt(pos); index.isValid() + && (index.flags() & Qt::ItemIsSelectable) == 0) + { + return; + } + QMenu* contextMenu = new QMenu(this); // Populate global context menu. @@ -1128,14 +1136,22 @@ namespace AzToolsFramework EnableUi(enable); } - void EntityOutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) + void EntityOutlinerWidget::OnEditorModeActivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - EnableUi(false); + if (mode == ViewportEditorMode::Component) + { + EnableUi(false); + } } - void EntityOutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) + void EntityOutlinerWidget::OnEditorModeDeactivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - EnableUi(true); + if (mode == ViewportEditorMode::Component) + { + EnableUi(true); + } } void EntityOutlinerWidget::OnPrefabInstancePropagationBegin() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx index 78927359f2..431861ab67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -59,7 +59,7 @@ namespace AzToolsFramework , private ToolsApplicationEvents::Bus::Handler , private EditorEntityContextNotificationBus::Handler , private EditorEntityInfoNotificationBus::Handler - , private ComponentModeFramework::EditorComponentModeNotificationBus::Handler + , private ViewportEditorModeNotificationsBus::Handler , private Prefab::PrefabPublicNotificationBus::Handler , private EditorWindowUIRequestBus::Handler { @@ -101,9 +101,11 @@ namespace AzToolsFramework void OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId /*childId*/) override; void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& /*name*/) override; - // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; // PrefabPublicNotificationBus void OnPrefabInstancePropagationBegin() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index d4b227c4ff..6ffa3aab69 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -24,11 +24,11 @@ #include #include #include -#include +#include +#include +#include #include #include -#include -#include #include #include #include @@ -39,7 +39,6 @@ #include #include - #include #include #include @@ -56,14 +55,13 @@ #include #include - namespace AzToolsFramework { namespace Prefab { ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr; EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr; - PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr; PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr; @@ -129,14 +127,15 @@ namespace AzToolsFramework return; } - s_prefabFocusInterface = AZ::Interface::Get(); - if (s_prefabFocusInterface == nullptr) + s_prefabFocusPublicInterface = AZ::Interface::Get(); + if (s_prefabFocusPublicInterface == nullptr) { - AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction."); + AZ_Assert(false, "Prefab - could not get PrefabFocusPublicInterface on PrefabIntegrationManager construction."); return; } EditorContextMenuBus::Handler::BusConnect(); + EditorEventsBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); @@ -147,6 +146,7 @@ namespace AzToolsFramework AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); + EditorEventsBus::Handler::BusDisconnect(); EditorContextMenuBus::Handler::BusDisconnect(); } @@ -245,12 +245,8 @@ namespace AzToolsFramework if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity)) { // Edit Prefab - if (prefabWipFeaturesEnabled) + if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) { - bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity); - - if (!beingEdited) - { QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); @@ -259,7 +255,6 @@ namespace AzToolsFramework }); itemWasShown = true; - } } // Save Prefab @@ -313,6 +308,11 @@ namespace AzToolsFramework } } + void PrefabIntegrationManager::OnEscape() + { + s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId()); + } + void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const { auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position); @@ -483,7 +483,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { - s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity); + s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity); } void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity) @@ -1393,7 +1393,7 @@ namespace AzToolsFramework AZStd::unique_ptr PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId) { - FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow()); + FlowLayout* unsavedPrefabsLayout = new FlowLayout(nullptr); AZStd::set dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 696c05991c..e8c10c150a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -30,7 +30,7 @@ namespace AzToolsFramework namespace Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabLoaderInterface; //! Structure for saving/retrieving user settings related to prefab workflows. @@ -51,6 +51,7 @@ namespace AzToolsFramework class PrefabIntegrationManager final : public EditorContextMenuBus::Handler + , public EditorEventsBus::Handler , public AssetBrowser::AssetBrowserSourceDropBus::Handler , public PrefabInstanceContainerNotificationBus::Handler , public PrefabIntegrationInterface @@ -64,19 +65,22 @@ namespace AzToolsFramework static void Reflect(AZ::ReflectContext* context); - // EditorContextMenuBus... + // EditorContextMenuBus overrides ... int GetMenuPosition() const override; AZStd::string GetMenuIdentifier() const override; void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; - // EntityOutlinerSourceDropHandlingBus... + // EditorEventsBus overrides ... + void OnEscape(); + + // EntityOutlinerSourceDropHandlingBus overrides ... void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override; - // PrefabInstanceContainerNotificationBus... + // PrefabInstanceContainerNotificationBus overrides ... void OnPrefabComponentActivate(AZ::EntityId entityId) override; void OnPrefabComponentDeactivate(AZ::EntityId entityId) override; - // PrefabIntegrationInterface... + // PrefabIntegrationInterface overrides ... AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override; int ExecuteClosePrefabDialog(TemplateId templateId) override; void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override; @@ -140,7 +144,7 @@ namespace AzToolsFramework static ContainerEntityInterface* s_containerEntityInterface; static EditorEntityUiInterface* s_editorEntityUiInterface; - static PrefabFocusInterface* s_prefabFocusInterface; + static PrefabFocusPublicInterface* s_prefabFocusPublicInterface; static PrefabLoaderInterface* s_prefabLoaderInterface; static PrefabPublicInterface* s_prefabPublicInterface; static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 7d1f3485aa..00522b29dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -10,7 +10,7 @@ #include -#include +#include #include #include @@ -35,10 +35,10 @@ namespace AzToolsFramework return; } - m_prefabFocusInterface = AZ::Interface::Get(); - if (m_prefabFocusInterface == nullptr) + m_prefabFocusPublicInterface = AZ::Interface::Get(); + if (m_prefabFocusPublicInterface == nullptr) { - AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction."); + AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction."); return; } } @@ -83,7 +83,7 @@ namespace AzToolsFramework QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const { - if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { return QIcon(m_prefabEditIconPath); } @@ -105,7 +105,7 @@ namespace AzToolsFramework const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value() && index.model()->hasChildren(index); QColor backgroundColor = m_prefabCapsuleColor; - if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { backgroundColor = m_prefabCapsuleEditColor; } @@ -191,7 +191,7 @@ namespace AzToolsFramework const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle; QColor borderColor = m_prefabCapsuleColor; - if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { borderColor = m_prefabCapsuleEditColor; } @@ -329,7 +329,7 @@ namespace AzToolsFramework if (prefabWipFeaturesEnabled) { // Focus on this prefab - m_prefabFocusInterface->FocusOnOwningPrefab(entityId); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index bb7168f409..7c68d9fd95 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -15,7 +15,7 @@ namespace AzToolsFramework namespace Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabPublicInterface; }; @@ -39,7 +39,7 @@ namespace AzToolsFramework void OnDoubleClick(AZ::EntityId entityId) const override; private: - Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr; + Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 6b0de5dc53..21ada94184 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -8,7 +8,7 @@ #include -#include +#include namespace AzToolsFramework::Prefab { @@ -31,8 +31,8 @@ namespace AzToolsFramework::Prefab void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton) { // Get reference to the PrefabFocusInterface handler - m_prefabFocusInterface = AZ::Interface::Get(); - if (m_prefabFocusInterface == nullptr) + m_prefabFocusPublicInterface = AZ::Interface::Get(); + if (m_prefabFocusPublicInterface == nullptr) { AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction."); return; @@ -46,7 +46,7 @@ namespace AzToolsFramework::Prefab connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, [&](const QString&, int linkIndex) { - m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); + m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); } ); @@ -54,9 +54,9 @@ namespace AzToolsFramework::Prefab connect(m_backButton, &QToolButton::clicked, this, [&]() { - if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) + if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) { - m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); + m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); } } ); @@ -65,7 +65,7 @@ namespace AzToolsFramework::Prefab void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() { // Push new Path - m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str()); + m_breadcrumbsWidget->pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str()); } } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h index ce7744fb1b..a97db60e34 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -19,7 +19,7 @@ namespace AzToolsFramework::Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabViewportFocusPathHandler : public PrefabFocusNotificationBus::Handler @@ -40,6 +40,6 @@ namespace AzToolsFramework::Prefab AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); - PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx index b2140868da..f450ec7ac9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx @@ -90,7 +90,6 @@ namespace AzToolsFramework void SetComponentOverridden(const bool overridden); - // Calls match EditorComponentModeNotificationBus - called from EntityPropertyEditor void EnteredComponentMode(const AZStd::vector& componentModeTypes); void LeftComponentMode(const AZStd::vector& componentModeTypes); void ActiveComponentModeChanged(const AZ::Uuid& componentType); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index 87527502dd..9ffe8021e3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -33,6 +33,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include @@ -486,8 +487,12 @@ namespace AzToolsFramework , m_isSystemEntityEditor(false) , m_isLevelEntityEditor(isLevelEntityEditor) { + initEntityPropertyEditorResources(); + m_componentModeCollection = AZ::Interface::Get(); + AZ_Assert(m_componentModeCollection, "Could not retrieve component mode collection."); + m_prefabPublicInterface = AZ::Interface::Get(); AZ_Assert(m_prefabPublicInterface != nullptr, "EntityPropertyEditor requires a PrefabPublicInterface instance on Initialize."); @@ -5698,39 +5703,49 @@ namespace AzToolsFramework SaveComponentEditorState(); } - void EntityPropertyEditor::EnteredComponentMode(const AZStd::vector& componentModeTypes) + void EntityPropertyEditor::OnEditorModeActivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - DisableComponentActions(this, m_entityComponentActions); - SetPropertyEditorState(m_gui, false); - m_disabled = true; - - if (!componentModeTypes.empty()) + if (mode == AzToolsFramework::ViewportEditorMode::Component) { - m_componentEditorLastSelectedIndex = GetComponentEditorIndexFromType(componentModeTypes.front()); - } + DisableComponentActions(this, m_entityComponentActions); + SetPropertyEditorState(m_gui, false); + const auto componentModeTypes = m_componentModeCollection->GetComponentTypes(); + m_disabled = true; + + if (!componentModeTypes.empty()) + { + m_componentEditorLastSelectedIndex = GetComponentEditorIndexFromType(componentModeTypes.front()); + } - for (auto componentEditor : m_componentEditors) - { - componentEditor->EnteredComponentMode(componentModeTypes); - } + for (auto componentEditor : m_componentEditors) + { + componentEditor->EnteredComponentMode(componentModeTypes); + } - // record the selected state after entering component mode - SaveComponentEditorState(); + // record the selected state after entering component mode + SaveComponentEditorState(); + } } - void EntityPropertyEditor::LeftComponentMode(const AZStd::vector& componentModeTypes) + void EntityPropertyEditor::OnEditorModeDeactivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - EnableComponentActions(this, m_entityComponentActions); - SetPropertyEditorState(m_gui, true); - m_disabled = false; - - for (auto componentEditor : m_componentEditors) + if (mode == AzToolsFramework::ViewportEditorMode::Component) { - componentEditor->LeftComponentMode(componentModeTypes); - } + EnableComponentActions(this, m_entityComponentActions); + SetPropertyEditorState(m_gui, true); + const auto componentModeTypes = m_componentModeCollection->GetComponentTypes(); + m_disabled = false; - // record the selected state after leaving component mode - SaveComponentEditorState(); + for (auto componentEditor : m_componentEditors) + { + componentEditor->LeftComponentMode(componentModeTypes); + } + + // record the selected state after leaving component mode + SaveComponentEditorState(); + } } void EntityPropertyEditor::ActiveComponentModeChanged(const AZ::Uuid& componentType) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx index 83b275b81a..5279cefa9f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ namespace AzToolsFramework { class ComponentEditor; class ComponentPaletteWidget; + class ComponentModeCollectionInterface; struct SourceControlFileInfo; namespace AssetBrowser @@ -108,6 +110,7 @@ namespace AzToolsFramework , public AzToolsFramework::EditorEntityContextNotificationBus::Handler , public AzToolsFramework::EntityPropertyEditorRequestBus::Handler , public AzToolsFramework::PropertyEditorEntityChangeNotificationBus::MultiHandler + , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler , public EditorInspectorComponentNotificationBus::MultiHandler , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler , public AZ::EntitySystemBus::Handler @@ -231,10 +234,14 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; void ActiveComponentModeChanged(const AZ::Uuid& componentType) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + // EntityPropertEditorRequestBus void GetSelectedAndPinnedEntities(EntityIdList& selectedEntityIds) override; void GetSelectedEntities(EntityIdList& selectedEntityIds) override; @@ -627,6 +634,8 @@ namespace AzToolsFramework float m_moveFadeSecondsRemaining; AZStd::vector m_indexMapOfMovedRow; + AzToolsFramework::ComponentModeCollectionInterface* m_componentModeCollection = nullptr; + // When m_initiatingPropertyChangeNotification is set to true, it means this EntityPropertyEditor is // broadcasting a change to all listeners about a property change for a given entity. This is needed // so that we don't update the values twice for this inspector diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index ba84e662c1..7f3b9e61fd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -28,6 +28,9 @@ AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option") #include #include #include +#include +#include +#include AZ_POP_DISABLE_WARNING #include @@ -1230,6 +1233,16 @@ namespace AzToolsFramework return m_showThumbnailDropDownButton; } + void PropertyAssetCtrl::SetCustomThumbnailEnabled(bool enabled) + { + m_thumbnail->SetCustomThumbnailEnabled(enabled); + } + + void PropertyAssetCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap) + { + m_thumbnail->SetCustomThumbnailPixmap(pixmap); + } + void PropertyAssetCtrl::SetThumbnailCallback(EditCallbackType* editNotifyCallback) { m_thumbnailCallback = editNotifyCallback; @@ -1356,15 +1369,27 @@ namespace AzToolsFramework GUI->SetClearNotifyCallback(nullptr); } } - else if (attrib == AZ_CRC("BrowseIcon", 0x507d7a4f)) + else if (attrib == AZ_CRC_CE("BrowseIcon")) { AZStd::string iconPath; - attrValue->Read(iconPath); - - if (!iconPath.empty()) + if (attrValue->Read(iconPath) && !iconPath.empty()) { GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str())); } + else + { + // A QPixmap object can't be assigned directly via an attribute. + // This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap. + AZStd::vector pixmapBuffer; + if (attrValue->Read>(pixmapBuffer) && !pixmapBuffer.empty()) + { + QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast(pixmapBuffer.size())); + QDataStream stream(&pixmapBytes, QIODevice::ReadOnly); + QPixmap pixmap; + stream >> pixmap; + GUI->SetBrowseButtonIcon(pixmap); + } + } } else if (attrib == AZ_CRC_CE("BrowseButtonEnabled")) { @@ -1390,6 +1415,30 @@ namespace AzToolsFramework GUI->SetShowThumbnail(showThumbnail); } } + else if (attrib == AZ_CRC_CE("ThumbnailIcon")) + { + AZStd::string iconPath; + if (attrValue->Read(iconPath) && !iconPath.empty()) + { + GUI->SetCustomThumbnailEnabled(true); + GUI->SetCustomThumbnailPixmap(QPixmap::fromImage(QImage(iconPath.c_str()))); + } + else + { + // A QPixmap object can't be assigned directly via an attribute. + // This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap. + AZStd::vector pixmapBuffer; + if (attrValue->Read>(pixmapBuffer) && !pixmapBuffer.empty()) + { + QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast(pixmapBuffer.size())); + QDataStream stream(&pixmapBytes, QIODevice::ReadOnly); + QPixmap pixmap; + stream >> pixmap; + GUI->SetCustomThumbnailEnabled(true); + GUI->SetCustomThumbnailPixmap(pixmap); + } + } + } else if (attrib == AZ_CRC_CE("ThumbnailCallback")) { PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast(attrValue->GetAttribute()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index cc4aff5649..0b98278bc5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -217,12 +217,17 @@ namespace AzToolsFramework void SetHideProductFilesInAssetPicker(bool hide); bool GetHideProductFilesInAssetPicker() const; + // Enable and configure a thumbnail widget that displays an asset preview and dropdown arrow for a dropdown menu void SetShowThumbnail(bool enable); bool GetShowThumbnail() const; void SetShowThumbnailDropDownButton(bool enable); bool GetShowThumbnailDropDownButton() const; void SetThumbnailCallback(EditCallbackType* editNotifyCallback); + // If enabled, replaces the thumbnail widget content with a custom pixmap + void SetCustomThumbnailEnabled(bool enabled); + void SetCustomThumbnailPixmap(const QPixmap& pixmap); + void SetSelectedAssetID(const AZ::Data::AssetId& newID); void SetCurrentAssetType(const AZ::Data::AssetType& newType); void SetSelectedAssetID(const AZ::Data::AssetId& newID, const AZ::Data::AssetType& newType); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h index eda8b482a1..01675e0044 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h @@ -38,7 +38,7 @@ namespace AzToolsFramework static bool UnsignedToolTip(QWidget* widget, QString& toolTipString); }; - //! Base class for integer widget handlers to provide functionality independant + //! Base class for integer widget handlers to provide functionality independent //! of widget type. //! @tparam ValueType The integer primitive type of the handler. //! @tparam PropertyControl The widget type of the handler. @@ -167,8 +167,7 @@ namespace AzToolsFramework PropertyControl* newCtrl = aznew PropertyControl(pParent); this->connect(newCtrl, &PropertyControl::valueChanged, this, [newCtrl]() { - EBUS_EVENT(PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl); - AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl); + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Events::RequestWrite, newCtrl); }); // note: Qt automatically disconnects objects from each other when either end is destroyed, no need to worry about delete. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx index 1e1e6a9592..fef4639a9f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx @@ -98,11 +98,6 @@ namespace AzToolsFramework QWidget* IntSpinBoxHandler::CreateGUI(QWidget* parent) { PropertyIntSpinCtrl* newCtrl = static_cast(BaseHandler::CreateGUI(parent)); - this->connect(newCtrl, &PropertyIntSpinCtrl::valueChanged, [newCtrl]() - { - AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl); - }); - return newCtrl; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp index c458f7c47e..d8ddee6b76 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp @@ -7,75 +7,117 @@ */ #include -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer' needs to have dll-interface to be used by clients of class 'QRawFont' - // 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning) -#include -#include -#include -#include -#include -#include + +// 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer' needs to have dll-interface to be used by clients of class +// 'QRawFont' 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning) +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") #include +#include +#include +#include +#include +#include +#include AZ_POP_DISABLE_WARNING #include "ThumbnailPropertyCtrl.h" namespace AzToolsFramework { - ThumbnailPropertyCtrl::ThumbnailPropertyCtrl(QWidget* parent) : QWidget(parent) { - QHBoxLayout* pLayout = new QHBoxLayout(); - pLayout->setContentsMargins(0, 0, 0, 0); - pLayout->setSpacing(0); - m_thumbnail = new Thumbnailer::ThumbnailWidget(this); m_thumbnail->setFixedSize(QSize(24, 24)); + m_thumbnailEnlarged = new Thumbnailer::ThumbnailWidget(this); + m_thumbnailEnlarged->setFixedSize(QSize(180, 180)); + m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + + m_customThumbnail = new QLabel(this); + m_customThumbnail->setFixedSize(QSize(24, 24)); + m_customThumbnail->setScaledContents(true); + + m_customThumbnailEnlarged = new QLabel(this); + m_customThumbnailEnlarged->setFixedSize(QSize(180, 180)); + m_customThumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + m_customThumbnailEnlarged->setScaledContents(true); + m_dropDownArrow = new AspectRatioAwarePixmapWidget(this); m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png")); m_dropDownArrow->setFixedSize(QSize(8, 24)); - ShowDropDownArrow(false); m_emptyThumbnail = new QLabel(this); m_emptyThumbnail->setPixmap(QPixmap(":/stylesheet/img/line.png")); m_emptyThumbnail->setFixedSize(QSize(24, 24)); - pLayout->addWidget(m_emptyThumbnail); + QHBoxLayout* pLayout = new QHBoxLayout(); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSpacing(0); pLayout->addWidget(m_thumbnail); + pLayout->addWidget(m_customThumbnail); + pLayout->addWidget(m_emptyThumbnail); pLayout->addSpacing(4); pLayout->addWidget(m_dropDownArrow); pLayout->addSpacing(4); - setLayout(pLayout); + + ShowDropDownArrow(false); + UpdateVisibility(); } void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName) { - m_key = key; - m_emptyThumbnail->setVisible(false); - m_thumbnail->SetThumbnailKey(key, contextName); + if (m_customThumbnailEnabled) + { + ClearThumbnail(); + } + else + { + m_key = key; + m_thumbnail->SetThumbnailKey(m_key, contextName); + m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName); + } + UpdateVisibility(); } void ThumbnailPropertyCtrl::ClearThumbnail() { - m_emptyThumbnail->setVisible(true); + m_key.clear(); m_thumbnail->ClearThumbnail(); + m_thumbnailEnlarged->ClearThumbnail(); + UpdateVisibility(); } void ThumbnailPropertyCtrl::ShowDropDownArrow(bool visible) { - if (visible) - { - setFixedSize(QSize(40, 24)); - } - else - { - setFixedSize(QSize(24, 24)); - } + setFixedSize(QSize(visible ? 40 : 24, 24)); m_dropDownArrow->setVisible(visible); } + void ThumbnailPropertyCtrl::SetCustomThumbnailEnabled(bool enabled) + { + m_customThumbnailEnabled = enabled; + UpdateVisibility(); + } + + void ThumbnailPropertyCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap) + { + m_customThumbnail->setPixmap(pixmap); + m_customThumbnailEnlarged->setPixmap(pixmap); + UpdateVisibility(); + } + + void ThumbnailPropertyCtrl::UpdateVisibility() + { + m_thumbnail->setVisible(m_key && !m_customThumbnailEnabled); + m_thumbnailEnlarged->setVisible(false); + + m_customThumbnail->setVisible(m_customThumbnailEnabled); + m_customThumbnailEnlarged->setVisible(false); + + m_emptyThumbnail->setVisible(!m_key && !m_customThumbnailEnabled); + } + bool ThumbnailPropertyCtrl::event(QEvent* e) { if (isEnabled()) @@ -83,7 +125,7 @@ namespace AzToolsFramework if (e->type() == QEvent::MouseButtonPress) { emit clicked(); - return true; //ignore + return true; // ignore } } @@ -94,37 +136,32 @@ namespace AzToolsFramework { QPainter p(this); QRect targetRect(QPoint(), QSize(40, 24)); - p.fillRect(targetRect, QColor(17, 17, 17)); // #111111 + p.fillRect(targetRect, QColor("#111111")); QWidget::paintEvent(e); } void ThumbnailPropertyCtrl::enterEvent(QEvent* e) { m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0_highlighted.png")); - if (!m_thumbnailEnlarged && m_key) - { - QPoint position = mapToGlobal(pos() - QPoint(185, 0)); - QSize size(180, 180); - m_thumbnailEnlarged.reset(new Thumbnailer::ThumbnailWidget()); - m_thumbnailEnlarged->setFixedSize(size); - m_thumbnailEnlarged->move(position); - m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); - m_thumbnailEnlarged->SetThumbnailKey(m_key); - m_thumbnailEnlarged->raise(); - m_thumbnailEnlarged->show(); - } + const QPoint offset(-m_thumbnailEnlarged->width() - 5, -m_thumbnailEnlarged->height() / 2 + m_thumbnail->height() / 2); + + m_thumbnailEnlarged->move(mapToGlobal(pos()) + offset); + m_thumbnailEnlarged->raise(); + m_thumbnailEnlarged->setVisible(m_key && !m_customThumbnailEnabled); + + m_customThumbnailEnlarged->move(mapToGlobal(pos()) + offset); + m_customThumbnailEnlarged->raise(); + m_customThumbnailEnlarged->setVisible(m_customThumbnailEnabled); QWidget::enterEvent(e); } void ThumbnailPropertyCtrl::leaveEvent(QEvent* e) { m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png")); - if (m_thumbnailEnlarged) - { - m_thumbnailEnlarged.reset(); - } + m_thumbnailEnlarged->setVisible(false); + m_customThumbnailEnlarged->setVisible(false); QWidget::leaveEvent(e); } -} +} // namespace AzToolsFramework #include "UI/PropertyEditor/moc_ThumbnailPropertyCtrl.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h index 93f703c4c5..ffd8234190 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h @@ -1,5 +1,3 @@ -#pragma once - /* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. @@ -8,6 +6,8 @@ * */ +#pragma once + #if !defined(Q_MOC_RUN) #include #include @@ -35,25 +35,38 @@ namespace AzToolsFramework //! Call this to set what thumbnail widget will display void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName = "Default"); + //! Remove current thumbnail void ClearThumbnail(); + //! Display a clickable dropdown arrow next to the thumbnail void ShowDropDownArrow(bool visible); - bool event(QEvent* e) override; + //! Override the thumbnail widget with a custom image + void SetCustomThumbnailEnabled(bool enabled); + + //! Assign a custom image to display in place of thumbnail + void SetCustomThumbnailPixmap(const QPixmap& pixmap); Q_SIGNALS: void clicked(); - protected: + private: + void UpdateVisibility(); + + bool event(QEvent* e) override; void paintEvent(QPaintEvent* e) override; void enterEvent(QEvent* e) override; void leaveEvent(QEvent* e) override; - private: Thumbnailer::SharedThumbnailKey m_key; Thumbnailer::ThumbnailWidget* m_thumbnail = nullptr; - QScopedPointer m_thumbnailEnlarged; + Thumbnailer::ThumbnailWidget* m_thumbnailEnlarged = nullptr; + + QLabel* m_customThumbnail = nullptr; + QLabel* m_customThumbnailEnlarged = nullptr; + bool m_customThumbnailEnabled = false; + QLabel* m_emptyThumbnail = nullptr; AspectRatioAwarePixmapWidget* m_dropDownArrow = nullptr; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp index 8d30359562..a0d02f47e7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp @@ -129,7 +129,7 @@ namespace UnitTest using AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus; AzToolsFramework::EditorActionRequestBus::Handler::BusConnect(); - EditorComponentModeNotificationBus::Handler::BusConnect(GetEntityContextId()); + ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId()); m_defaultWidget.setFocus(); } @@ -137,18 +137,26 @@ namespace UnitTest { using AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus; - EditorComponentModeNotificationBus::Handler::BusDisconnect(); + ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); AzToolsFramework::EditorActionRequestBus::Handler::BusDisconnect(); } - void TestEditorActions::EnteredComponentMode([[maybe_unused]] const AZStd::vector& componentTypes) + void TestEditorActions::OnEditorModeActivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - m_componentModeWidget.setFocus(); + if (mode == ViewportEditorMode::Component) + { + m_componentModeWidget.setFocus(); + } } - void TestEditorActions::LeftComponentMode([[maybe_unused]] const AZStd::vector& componentTypes) + void TestEditorActions::OnEditorModeDeactivated( + [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { - m_defaultWidget.setFocus(); + if (mode == ViewportEditorMode::Component) + { + m_defaultWidget.setFocus(); + } } void TestEditorActions::AddActionViaBus(int id, QAction* action) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h index c4aefecce5..4a7039423c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h @@ -23,9 +23,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -103,7 +103,7 @@ namespace UnitTest /// component mode editing. class TestEditorActions : private AzToolsFramework::EditorActionRequestBus::Handler - , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler + , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler { // EditorActionRequestBus ... void AddActionViaBus(int id, QAction* action) override; @@ -114,9 +114,11 @@ namespace UnitTest void AttachOverride(QWidget* /*object*/) override {} void DetachOverride() override {} - // EditorComponentModeNotificationBus ... - void EnteredComponentMode(const AZStd::vector& componentTypes) override; - void LeftComponentMode(const AZStd::vector& componentTypes) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; + void OnEditorModeDeactivated( + const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; public: void Connect(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp index 1ad0ee8ff3..1541d4678e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorDefaultSelection.cpp @@ -27,6 +27,10 @@ namespace AzToolsFramework , m_viewportEditorModeTracker(viewportEditorModeTracker) , m_componentModeCollection(viewportEditorModeTracker) { + AZ_Assert( + AZ::Interface::Get() == nullptr, "Unexpected registration of component mode collection.") + AZ::Interface::Register(&m_componentModeCollection); + ActionOverrideRequestBus::Handler::BusConnect(GetEntityContextId()); ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusConnect(); @@ -40,6 +44,11 @@ namespace AzToolsFramework ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusDisconnect(); ActionOverrideRequestBus::Handler::BusDisconnect(); m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Default); + + AZ_Assert( + AZ::Interface::Get() != nullptr, + "Unexpected unregistration of component mode collection.") + AZ::Interface::Unregister(&m_componentModeCollection); } void EditorDefaultSelection::SetOverridePhantomWidget(QWidget* phantomOverrideWidget) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 3ce350287f..bb718d0dc9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -187,15 +187,14 @@ namespace AzToolsFramework } // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. - if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor)) + if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { return AZ::EntityId(); } // Container Entity support - if the entity that is being selected is part of a closed container, // change the selection to the container instead. - ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get(); - if (containerEntityInterface) + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) { return containerEntityInterface->FindHighestSelectableEntity(entityIdUnderCursor); } @@ -217,7 +216,7 @@ namespace AzToolsFramework { const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex); - if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex)) + if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId)) { continue; } @@ -263,4 +262,24 @@ namespace AzToolsFramework } } } + + bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId) + { + return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId); + } + + bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId) + { + return m_focusModeInterface->IsInFocusSubTree(entityId); + } + + bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId) + { + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) + { + return !containerEntityInterface->IsUnderClosedContainerEntity(entityId); + } + + return true; + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h index a6a78a4e61..909a231635 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -58,7 +58,19 @@ namespace AzToolsFramework AzFramework::DebugDisplayRequests& debugDisplay, const AZStd::function& showIconCheck); + //! Returns whether the entityId can be selected in the viewport according + //! to the current Editor Focus Mode and Container Entity setup. + bool IsSelectableInViewport(AZ::EntityId entityId); + private: + //! Returns whether the entityId can be selected in the viewport according + //! to the current Editor Focus Mode setup. + bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId); + + //! Returns whether the entityId can be selected in the viewport according + //! to the current Container Entityu setup. + bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId); + const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers. const FocusModeInterface* m_focusModeInterface = nullptr; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index ccb72e1d50..d5a0595049 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -103,10 +103,6 @@ namespace AzToolsFramework static const char* const ResetEntityTransformDesc = "Reset transform based on manipulator mode"; static const char* const ResetManipulatorTitle = "Reset Manipulator"; static const char* const ResetManipulatorDesc = "Reset the manipulator to recenter it on the selected entity"; - static const char* const ResetTransformLocalTitle = "Reset Transform (Local)"; - static const char* const ResetTransformLocalDesc = "Reset transform to local space"; - static const char* const ResetTransformWorldTitle = "Reset Transform (World)"; - static const char* const ResetTransformWorldDesc = "Reset transform to world space"; static const char* const EntityBoxSelectUndoRedoDesc = "Box Select Entities"; static const char* const EntityDeselectUndoRedoDesc = "Deselect Entity"; @@ -411,7 +407,7 @@ namespace AzToolsFramework const AzFramework::CameraState cameraState = GetCameraState(viewportId); for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex) { - if (entityDataCache.IsVisibleEntityLocked(entityCacheIndex) || !entityDataCache.IsVisibleEntityVisible(entityCacheIndex)) + if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex)) { continue; } @@ -1027,7 +1023,7 @@ namespace AzToolsFramework EditorTransformComponentSelectionRequestBus::Handler::BusConnect(entityContextId); ToolsApplicationNotificationBus::Handler::BusConnect(); Camera::EditorCameraNotificationBus::Handler::BusConnect(); - ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(entityContextId); + ViewportEditorModeNotificationsBus::Handler::BusConnect(entityContextId); EditorEntityContextNotificationBus::Handler::BusConnect(); EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); @@ -1075,7 +1071,7 @@ namespace AzToolsFramework EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); EditorEntityContextNotificationBus::Handler::BusDisconnect(); - ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); + ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); Camera::EditorCameraNotificationBus::Handler::BusDisconnect(); ToolsApplicationNotificationBus::Handler::BusDisconnect(); EditorTransformComponentSelectionRequestBus::Handler::BusDisconnect(); @@ -1117,7 +1113,7 @@ namespace AzToolsFramework }); m_boxSelect.InstallLeftMouseUp( - [this, entityBoxSelectData]() + [this, entityBoxSelectData] { entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds)); @@ -2175,7 +2171,7 @@ namespace AzToolsFramework // lock selection AddAction( m_actions, { QKeySequence(Qt::Key_L) }, LockSelection, LockSelectionTitle, LockSelectionDesc, - [lockUnlock]() + [lockUnlock] { lockUnlock(true); }); @@ -2183,7 +2179,7 @@ namespace AzToolsFramework // unlock selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, UnlockSelection, LockSelectionTitle, LockSelectionDesc, - [lockUnlock]() + [lockUnlock] { lockUnlock(false); }); @@ -2213,7 +2209,7 @@ namespace AzToolsFramework // hide selection AddAction( m_actions, { QKeySequence(Qt::Key_H) }, HideSelection, HideSelectionTitle, HideSelectionDesc, - [showHide]() + [showHide] { showHide(false); }); @@ -2221,7 +2217,7 @@ namespace AzToolsFramework // show selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, ShowSelection, HideSelectionTitle, HideSelectionDesc, - [showHide]() + [showHide] { showHide(true); }); @@ -2229,7 +2225,7 @@ namespace AzToolsFramework // unlock all entities in the level/scene AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, UnlockAll, UnlockAllTitle, UnlockAllDesc, - []() + [] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2246,14 +2242,14 @@ namespace AzToolsFramework // show all entities in the level/scene AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, ShowAll, ShowAllTitle, ShowAllDesc, - []() + [] { AZ_PROFILE_FUNCTION(AzToolsFramework); ScopedUndoBatch undoBatch(ShowAllEntitiesUndoRedoDesc); EnumerateEditorEntities( - [](AZ::EntityId entityId) + [](const AZ::EntityId entityId) { ScopedUndoBatch::MarkEntityDirty(entityId); SetEntityVisibility(entityId, true); @@ -2263,7 +2259,7 @@ namespace AzToolsFramework // select all entities in the level/scene AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, SelectAll, SelectAllTitle, SelectAllDesc, - [this]() + [this] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2303,7 +2299,7 @@ namespace AzToolsFramework // invert current selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, InvertSelect, InvertSelectionTitle, InvertSelectionDesc, - [this]() + [this] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2350,17 +2346,10 @@ namespace AzToolsFramework // duplicate selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_D) }, DuplicateSelect, DuplicateTitle, DuplicateDesc, - []() + [] { AZ_PROFILE_FUNCTION(AzToolsFramework); - // Clear Widget selection - Prevents issues caused by cloning entities while a property in the Reflected Property Editor - // is being edited. - if (QApplication::focusWidget()) - { - QApplication::focusWidget()->clearFocus(); - } - ScopedUndoBatch undoBatch(DuplicateUndoRedoDesc); auto selectionCommand = AZStd::make_unique(EntityIdList(), DuplicateUndoRedoDesc); selectionCommand->SetParent(undoBatch.GetUndoBatch()); @@ -2375,7 +2364,7 @@ namespace AzToolsFramework // delete selection AddAction( m_actions, { QKeySequence(Qt::Key_Delete) }, DeleteSelect, DeleteTitle, DeleteDesc, - [this]() + [this] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2392,21 +2381,21 @@ namespace AzToolsFramework AddAction( m_actions, { QKeySequence(Qt::Key_Space) }, EditEscaspe, "", "", - [this]() + [this] { DeselectEntities(); }); AddAction( m_actions, { QKeySequence(Qt::Key_P) }, EditPivot, TogglePivotTitleEditMenu, TogglePivotDesc, - [this]() + [this] { ToggleCenterPivotSelection(); }); AddAction( m_actions, { QKeySequence(Qt::Key_R) }, EditReset, ResetEntityTransformTitle, ResetEntityTransformDesc, - [this]() + [this] { switch (m_mode) { @@ -2424,50 +2413,14 @@ namespace AzToolsFramework AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_R) }, EditResetManipulator, ResetManipulatorTitle, ResetManipulatorDesc, - AZStd::bind(AZStd::mem_fn(&EditorTransformComponentSelection::DelegateClearManipulatorOverride), this)); - - AddAction( - m_actions, { QKeySequence(Qt::ALT + Qt::Key_R) }, EditResetLocal, ResetTransformLocalTitle, ResetTransformLocalDesc, - [this]() + [this] { - switch (m_mode) - { - case Mode::Rotation: - ResetOrientationForSelectedEntitiesLocal(); - break; - case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualWorld(1.0f); - break; - case Mode::Translation: - // do nothing - break; - } - }); - - AddAction( - m_actions, { QKeySequence(Qt::SHIFT + Qt::Key_R) }, EditResetWorld, ResetTransformWorldTitle, ResetTransformWorldDesc, - [this]() - { - switch (m_mode) - { - case Mode::Rotation: - { - // begin an undo batch so operations inside CopyOrientation... and - // DelegateClear... are grouped into a single undo/redo - ScopedUndoBatch undoBatch{ ResetTransformWorldTitle }; - CopyOrientationToSelectedEntitiesIndividual(AZ::Quaternion::CreateIdentity()); - ClearManipulatorOrientationOverride(); - } - break; - case Mode::Scale: - case Mode::Translation: - break; - } + DelegateClearManipulatorOverride(); }); AddAction( m_actions, { QKeySequence(Qt::Key_U) }, ViewportUiVisible, "Toggle Viewport UI", "Hide/Show Viewport UI", - [this]() + [this] { SetAllViewportUiVisible(!m_viewportUiVisible); }); @@ -3276,7 +3229,7 @@ namespace AzToolsFramework QAction* action = menu->addAction(QObject::tr(TogglePivotTitleRightClick)); QObject::connect( action, &QAction::triggered, action, - [this]() + [this] { ToggleCenterPivotSelection(); }); @@ -3707,22 +3660,30 @@ namespace AzToolsFramework m_selectedEntityIdsAndManipulatorsDirty = true; } - void EditorTransformComponentSelection::EnteredComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) + void EditorTransformComponentSelection::OnEditorModeActivated( + [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) { - SetAllViewportUiVisible(false); + if (mode == ViewportEditorMode::Component) + { + SetAllViewportUiVisible(false); - EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); - EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); - ToolsApplicationNotificationBus::Handler::BusDisconnect(); + EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); + EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); + ToolsApplicationNotificationBus::Handler::BusDisconnect(); + } } - void EditorTransformComponentSelection::LeftComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) + void EditorTransformComponentSelection::OnEditorModeDeactivated( + [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) { - SetAllViewportUiVisible(true); + if (mode == ViewportEditorMode::Component) + { + SetAllViewportUiVisible(true); - ToolsApplicationNotificationBus::Handler::BusConnect(); - EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); - EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); + ToolsApplicationNotificationBus::Handler::BusConnect(); + EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); + EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); + } } void EditorTransformComponentSelection::CreateEntityManipulatorDeselectCommand(ScopedUndoBatch& undoBatch) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index a0e87d9d6f..1e1cc6d2e1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -153,7 +153,7 @@ namespace AzToolsFramework , private EditorTransformComponentSelectionRequestBus::Handler , private ToolsApplicationNotificationBus::Handler , private Camera::EditorCameraNotificationBus::Handler - , private ComponentModeFramework::EditorComponentModeNotificationBus::Handler + , private ViewportEditorModeNotificationsBus::Handler , private EditorEntityContextNotificationBus::Handler , private EditorEntityVisibilityNotificationBus::Router , private EditorEntityLockComponentNotificationBus::Router @@ -286,9 +286,9 @@ namespace AzToolsFramework // EditorContextLockComponentNotificationBus overrides ... void OnEntityLockChanged(bool locked) override; - // EditorComponentModeNotificationBus overrides ... - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; + // ViewportEditorModeNotificationsBus overrides ... + void OnEditorModeActivated(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override; + void OnEditorModeDeactivated(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override; // EditorEntityContextNotificationBus overrides ... void OnStartPlayInEditor() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h index d0c106a15e..35f5b0ba99 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h @@ -31,8 +31,6 @@ namespace AzToolsFramework constexpr inline AZ::Crc32 EditPivot = AZ_CRC_CE("com.o3de.action.editortransform.editpivot"); constexpr inline AZ::Crc32 EditReset = AZ_CRC_CE("com.o3de.action.editortransform.editreset"); constexpr inline AZ::Crc32 EditResetManipulator = AZ_CRC_CE("com.o3de.action.editortransform.editresetmanipulator"); - constexpr inline AZ::Crc32 EditResetLocal = AZ_CRC_CE("com.o3de.action.editortransform.editresetlocal"); - constexpr inline AZ::Crc32 EditResetWorld = AZ_CRC_CE("com.o3de.action.editortransform.editresetworld"); constexpr inline AZ::Crc32 ViewportUiVisible = AZ_CRC_CE("com.o3de.action.editortransform.viewportuivisible"); //@} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp index babc6f972c..328cfc3ae5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp @@ -9,7 +9,9 @@ #include "EditorVisibleEntityDataCache.h" #include +#include #include +#include #include #include @@ -21,13 +23,23 @@ namespace AzToolsFramework using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType; EntityData() = default; - EntityData(AZ::EntityId entityId, const AZ::Transform& worldFromLocal, bool locked, bool visible, bool selected, bool iconHidden); + EntityData( + AZ::EntityId entityId, + const AZ::Transform& worldFromLocal, + bool locked, + bool visible, + bool inFocus, + bool descendantOfClosedContainer, + bool selected, + bool iconHidden); AZ::Transform m_worldFromLocal; AZ::EntityId m_entityId; ComponentEntityAccentType m_accent = ComponentEntityAccentType::None; bool m_locked = false; bool m_visible = true; + bool m_inFocus = true; + bool m_descendantOfClosedContainer = false; bool m_selected = false; bool m_iconHidden = false; }; @@ -57,12 +69,16 @@ namespace AzToolsFramework const AZ::Transform& worldFromLocal, const bool locked, const bool visible, + const bool inFocus, + const bool descendantOfClosedContainer, const bool selected, const bool iconHidden) : m_worldFromLocal(worldFromLocal) , m_entityId(entityId) , m_locked(locked) , m_visible(visible) + , m_inFocus(inFocus) + , m_descendantOfClosedContainer(descendantOfClosedContainer) , m_selected(selected) , m_iconHidden(iconHidden) { @@ -106,6 +122,18 @@ namespace AzToolsFramework bool locked = false; EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + bool inFocus = false; + if (auto focusModeInterface = AZ::Interface::Get()) + { + inFocus = focusModeInterface->IsInFocusSubTree(entityId); + } + + bool descendantOfClosedContainer = false; + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) + { + descendantOfClosedContainer = containerEntityInterface->IsUnderClosedContainerEntity(entityId); + } + bool iconHidden = false; EditorEntityIconComponentRequestBus::EventResult( iconHidden, entityId, &EditorEntityIconComponentRequests::IsEntityIconHiddenInViewport); @@ -113,7 +141,7 @@ namespace AzToolsFramework AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); - return { entityId, worldFromLocal, locked, visible, IsSelected(entityId), iconHidden }; + return { entityId, worldFromLocal, locked, visible, inFocus, descendantOfClosedContainer, IsSelected(entityId), iconHidden }; } EditorVisibleEntityDataCache::EditorVisibleEntityDataCache() @@ -126,10 +154,17 @@ namespace AzToolsFramework EntitySelectionEvents::Bus::Router::BusRouterConnect(); EditorEntityIconComponentNotificationBus::Router::BusRouterConnect(); ToolsApplicationNotificationBus::Handler::BusConnect(); + + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + + ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId); + FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId); } EditorVisibleEntityDataCache::~EditorVisibleEntityDataCache() { + FocusModeNotificationBus::Handler::BusDisconnect(); + ContainerEntityNotificationBus::Handler::BusDisconnect(); ToolsApplicationNotificationBus::Handler::BusDisconnect(); EditorEntityIconComponentNotificationBus::Router::BusRouterDisconnect(); EntitySelectionEvents::Bus::Router::BusRouterDisconnect(); @@ -260,7 +295,10 @@ namespace AzToolsFramework bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const { - return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked; + return m_impl->m_visibleEntityDatas[index].m_visible + && !m_impl->m_visibleEntityDatas[index].m_locked + && m_impl->m_visibleEntityDatas[index].m_inFocus + && !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer; } AZStd::optional EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const @@ -371,4 +409,72 @@ namespace AzToolsFramework m_impl->m_visibleEntityDatas[entityIndex.value()].m_iconHidden = iconHidden; } } + + void EditorVisibleEntityDataCache::OnContainerEntityStatusChanged(AZ::EntityId entityId, [[maybe_unused]] bool open) + { + // Get container descendants + AzToolsFramework::EntityIdList descendantIds; + AZ::TransformBus::EventResult(descendantIds, entityId, &AZ::TransformBus::Events::GetAllDescendants); + + // Update cached values + if (auto containerEntityInterface = AZ::Interface::Get()) + { + for (AZ::EntityId descendantId : descendantIds) + { + if (AZStd::optional entityIndex = GetVisibleEntityIndexFromId(descendantId)) + { + m_impl->m_visibleEntityDatas[entityIndex.value()].m_descendantOfClosedContainer = + containerEntityInterface->IsUnderClosedContainerEntity(descendantId); + } + } + } + } + + void EditorVisibleEntityDataCache::OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) + { + if (previousFocusEntityId.IsValid() && newFocusEntityId.IsValid()) + { + // Get previous focus root descendants + AzToolsFramework::EntityIdList previousDescendantIds; + AZ::TransformBus::EventResult(previousDescendantIds, previousFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants); + + // Get new focus root descendants + AzToolsFramework::EntityIdList newDescendantIds; + AZ::TransformBus::EventResult(newDescendantIds, newFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants); + + // Merge EntityId Lists to avoid refreshing values twice + AzToolsFramework::EntityIdSet descendantsSet; + descendantsSet.insert(previousFocusEntityId); + descendantsSet.insert(newFocusEntityId); + descendantsSet.insert(previousDescendantIds.begin(), previousDescendantIds.end()); + descendantsSet.insert(newDescendantIds.begin(), newDescendantIds.end()); + + // Update cached values + if (auto focusModeInterface = AZ::Interface::Get()) + { + for (const AZ::EntityId& descendantId : descendantsSet) + { + if (AZStd::optional entityIndex = GetVisibleEntityIndexFromId(descendantId)) + { + m_impl->m_visibleEntityDatas[entityIndex.value()].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId); + } + } + } + } + else + { + // If either focus was the invalid entity, refresh all entities. + if (auto focusModeInterface = AZ::Interface::Get()) + { + for (size_t entityIndex = 0; entityIndex < m_impl->m_visibleEntityDatas.size(); ++entityIndex) + { + if (AZ::EntityId descendantId = GetVisibleEntityId(entityIndex); descendantId.IsValid()) + { + m_impl->m_visibleEntityDatas[entityIndex].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId); + } + } + } + } + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h index b34defc25b..16fa1b6d14 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +30,8 @@ namespace AzToolsFramework , private EntitySelectionEvents::Bus::Router , private EditorEntityIconComponentNotificationBus::Router , private ToolsApplicationNotificationBus::Handler + , private ContainerEntityNotificationBus::Handler + , private FocusModeNotificationBus::Handler { public: EditorVisibleEntityDataCache(); @@ -58,28 +62,34 @@ namespace AzToolsFramework void AddEntityIds(const EntityIdList& entityIds); private: - // ToolsApplicationNotificationBus + // ToolsApplicationNotificationBus overrides ... void AfterUndoRedo() override; - // EditorEntityVisibilityNotificationBus + // EditorEntityVisibilityNotificationBus overrides ... void OnEntityVisibilityChanged(bool visibility) override; - // EditorEntityLockComponentNotificationBus + // EditorEntityLockComponentNotificationBus overrides ... void OnEntityLockChanged(bool locked) override; - // TransformNotificationBus + // TransformNotificationBus overrides ... void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; - // EditorComponentSelectionNotificationsBus + // EditorComponentSelectionNotificationsBus overrides ... void OnAccentTypeChanged(EntityAccentType accent) override; - // EntitySelectionEvents::Bus + // EntitySelectionEvents::Bus overrides ... void OnSelected() override; void OnDeselected() override; - // EditorEntityIconComponentNotificationBus + // EditorEntityIconComponentNotificationBus overrides ... void OnEntityIconChanged(const AZ::Data::AssetId& entityIconAssetId) override; + // ContainerEntityNotificationBus overrides ... + void OnContainerEntityStatusChanged(AZ::EntityId entityId, bool open) override; + + // FocusModeNotificationBus overrides ... + void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override; + class EditorVisibleEntityDataCacheImpl; AZStd::unique_ptr m_impl; //!< Internal representation of entity data cache. }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 80cc7941b0..e27627eab6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -339,7 +339,7 @@ namespace AzToolsFramework::ViewportUi::Internal { // no background for the widget else each set of buttons/text-fields/etc would have a black box around them SetTransparentBackground(mainWindow); - mainWindow->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); + mainWindow->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); } void ViewportUiDisplay::InitializeUiOverlay() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index ff7fe58f1e..8dec7ab611 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -30,12 +30,14 @@ set(FILES API/AssetDatabaseBus.h API/ComponentEntityObjectBus.h API/ComponentEntitySelectionBus.h + API/ComponentModeCollectionInterface.h API/EditorCameraBus.h API/EditorCameraBus.cpp API/EditorAnimationSystemRequestBus.h API/EditorEntityAPI.h API/EditorLevelNotificationBus.h API/ViewportEditorModeTrackerNotificationBus.h + API/ViewportEditorModeTrackerNotificationBus.cpp API/EditorVegetationRequestsBus.h API/EditorPythonConsoleBus.h API/EditorPythonRunnerRequestsBus.h @@ -644,6 +646,9 @@ set(FILES Prefab/PrefabFocusHandler.cpp Prefab/PrefabFocusInterface.h Prefab/PrefabFocusNotificationBus.h + Prefab/PrefabFocusPublicInterface.h + Prefab/PrefabFocusUndo.h + Prefab/PrefabFocusUndo.cpp Prefab/PrefabIdTypes.h Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntitySelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntitySelectionTests.cpp new file mode 100644 index 0000000000..a47e41da42 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntitySelectionTests.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AzToolsFramework +{ + TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithNoContainers) + { + // When no containers are in the way, the function will just return the entityId of the entity that was clicked. + + // Click on Car Entity + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); + + // Verify the correct entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); + } + + TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithClosedContainer) + { + // If a closed container is an ancestor of the queried entity, the closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default + + // Click on Car Entity + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); + + // Verify the correct entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + } + + TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithOpenContainer) + { + // If a closed container is an ancestor of the queried entity, the closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); + + // Verify the correct entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + } + + TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleClosedContainers) + { + // If multiple closed containers are ancestors of the queried entity, the highest closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); + + // Verify the correct entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CityEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + } + + TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleContainers) + { + // If multiple containers are ancestors of the queried entity, the highest closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true); + + // Click on Car Entity + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); + + // Verify the correct entity is selected + auto selectedEntitiesAfter = GetSelectedEntities(); + EXPECT_EQ(selectedEntitiesAfter.size(), 1); + EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntityTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntityTests.cpp new file mode 100644 index 0000000000..031062f027 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntityTests.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AzToolsFramework +{ + TEST_F(EditorFocusModeFixture, ContainerEntityTests_Register) + { + // Registering an entity is successful. + auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]); + EXPECT_TRUE(outcome.IsSuccess()); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_RegisterTwice) + { + // Registering an entity twice fails. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]); + auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]); + EXPECT_FALSE(outcome.IsSuccess()); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_Unregister) + { + // Unregistering a container entity is successful. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]); + auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]); + EXPECT_TRUE(outcome.IsSuccess()); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterRegularEntity) + { + // Unregistering an entity that was not previously registered fails. + auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]); + EXPECT_FALSE(outcome.IsSuccess()); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterTwice) + { + // Unregistering a container entity twice fails. + auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]); + EXPECT_FALSE(outcome.IsSuccess()); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegularEntity) + { + // If a regular entity is passed, IsContainer returns false. + // Note that we use a different entity than the tests above to validate a completely new EntityId. + bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]); + EXPECT_FALSE(isContainer); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegisteredContainer) + { + // If a container entity is passed, IsContainer returns true. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]); + EXPECT_TRUE(isContainer); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnUnRegisteredContainer) + { + // If an entity that was previously a container but was then unregistered is passed, IsContainer returns false. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + + bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]); + EXPECT_FALSE(isContainer); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenOnRegularEntity) + { + // Setting a regular entity to open should return a failure. + auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + EXPECT_FALSE(outcome.IsSuccess()); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpen) + { + // Set a container entity to open, and verify the operation was successful. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + EXPECT_TRUE(outcome.IsSuccess()); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenTwice) + { + // Set a container entity to open twice, and verify that does not cause a failure (as intended). + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + EXPECT_TRUE(outcome.IsSuccess()); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerClosed) + { + // Set a container entity to closed, and verify the operation was successful. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + EXPECT_TRUE(outcome.IsSuccess()); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnRegularEntity) + { + // Query open state on a regular entity, and verify it returns true. + // Open containers behave exactly as regular entities, so this is the expected return value. + bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]); + EXPECT_TRUE(isOpen); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnDefaultContainerEntity) + { + // Query open state on a newly registered container entity, and verify it returns false. + // Containers are registered closed by default. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]); + EXPECT_FALSE(isOpen); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnOpenContainerEntity) + { + // Query open state on a container entity that was opened, and verify it returns true. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true); + bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]); + EXPECT_TRUE(isOpen); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnClosedContainerEntity) + { + // Query open state on a container entity that was opened and then closed, and verify it returns false. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true); + m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], false); + bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]); + EXPECT_FALSE(isOpen); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_ContainerOpenStateIsPreserved) + { + // Register an entity as container, open it, then unregister it. + // When the entity is registered again, the open state should be preserved. + // This behavior is necessary for the system to work alongside Prefab propagation refreshes. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]); + bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]); + EXPECT_TRUE(isOpen); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceeds) + { + // The Clear function works if no container is registered. + auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId); + EXPECT_TRUE(outcome.IsSuccess()); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearFailsIfContainersAreStillRegistered) + { + // The Clear function fails if a container is registered. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId); + EXPECT_FALSE(outcome.IsSuccess()); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceedsIfContainersAreUnregistered) + { + // The Clear function fails if a container is registered. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId); + EXPECT_TRUE(outcome.IsSuccess()); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearDeletesPreservedOpenStates) + { + // Register an entity as container, open it, unregister it, then call clear. + // When the entity is registered again, the open state should not be preserved. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[Passenger1EntityName], true); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + + m_containerEntityInterface->Clear(m_editorEntityContextId); + + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[Passenger1EntityName]); + EXPECT_FALSE(isOpen); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithNoContainers) + { + // When no containers are in the way, the function will just return the entityId that was passed to it. + AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]); + EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithClosedContainer) + { + // If a closed container is an ancestor of the queried entity, the closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); // Containers are closed by default + AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]); + EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithOpenContainer) + { + // If an open container is an ancestor of the queried entity, it is ignored. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[SportsCarEntityName], true); + + AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]); + EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleClosedContainers) + { + // If multiple closed containers are ancestors of the queried entity, the highest closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + + AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]); + EXPECT_EQ(selectedEntityId, m_entityMap[StreetEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + } + + TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleContainers) + { + // If multiple containers are ancestors of the queried entity, the highest closed container is selected. + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true); + + AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]); + EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]); + + // Restore default state for other tests. + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]); + m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]); + } + +} diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp index c309261a27..49bf7cee15 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp @@ -14,6 +14,20 @@ namespace AzToolsFramework { + void ClearSelectedEntities() + { + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList()); + } + + AzToolsFramework::EntityIdList EditorFocusModeFixture::GetSelectedEntities() + { + AzToolsFramework::EntityIdList selectedEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( + selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities); + return selectedEntities; + } + void EditorFocusModeFixture::SetUpEditorFixtureImpl() { // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is @@ -21,6 +35,9 @@ namespace AzToolsFramework // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); + m_containerEntityInterface = AZ::Interface::Get(); + ASSERT_TRUE(m_containerEntityInterface != nullptr); + m_focusModeInterface = AZ::Interface::Get(); ASSERT_TRUE(m_focusModeInterface != nullptr); @@ -31,6 +48,24 @@ namespace AzToolsFramework m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); GenerateTestHierarchy(); + + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + + // Clear selection + ClearSelectedEntities(); + } + + void EditorFocusModeFixture::TearDownEditorFixtureImpl() + { + // Clear Container Entity preserved open states + m_containerEntityInterface->Clear(m_editorEntityContextId); + + // Clear the focus, disabling focus mode + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + + // Clear selection + ClearSelectedEntities(); } void EditorFocusModeFixture::GenerateTestHierarchy() @@ -59,7 +94,7 @@ namespace AzToolsFramework entity->Activate(); // Move the CarEntity so it's out of the way. - AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, CarEntityPosition); + AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, WorldCarEntityPosition); // Setup the camera so the Car entity is in view. AzFramework::SetCameraTransform( diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h index f038408012..c48795a3a4 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h @@ -14,6 +14,7 @@ #include +#include #include #include @@ -24,16 +25,20 @@ namespace AzToolsFramework { protected: void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; void GenerateTestHierarchy(); AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId); AZStd::unordered_map m_entityMap; + + ContainerEntityInterface* m_containerEntityInterface = nullptr; FocusModeInterface* m_focusModeInterface = nullptr; public: - AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EntityIdList GetSelectedEntities(); + AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); AzFramework::CameraState m_cameraState; inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f); @@ -45,6 +50,7 @@ namespace AzToolsFramework inline static const char* Passenger1EntityName = "Passenger1"; inline static const char* Passenger2EntityName = "Passenger2"; - inline static AZ::Vector3 CarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f); + inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f); }; + } diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionFixture.h b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionFixture.h new file mode 100644 index 0000000000..4c9369bc46 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionFixture.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace AzToolsFramework +{ + class EditorFocusModeSelectionFixture : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin + { + public: + void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition) + { + // Calculate the world position in screen space + const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState); + + // Click the entity in the viewport + m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp(); + } + }; +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp index 968bb3f293..2f746c9d63 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionTests.cpp @@ -6,64 +6,14 @@ * */ -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - +#include namespace AzToolsFramework { - class EditorFocusModeSelectionFixture - : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin - { - public: - void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition) - { - // Calculate the world position in screen space - const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState); - - // Click the entity in the viewport - m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp(); - } - }; - - void ClearSelectedEntities() - { - AzToolsFramework::ToolsApplicationRequestBus::Broadcast( - &AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList()); - } - - AzToolsFramework::EntityIdList GetSelectedEntities() - { - AzToolsFramework::EntityIdList selectedEntities; - AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( - selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities); - return selectedEntities; - } - TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel) { - // Clear the focus, disabling focus mode - m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); - // Clear selection - ClearSelectedEntities(); - // Click on Car Entity - ClickAtWorldPositionOnViewport(CarEntityPosition); + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); // Verify entity is selected auto selectedEntitiesAfter = GetSelectedEntities(); @@ -75,73 +25,53 @@ namespace AzToolsFramework { // Set the focus on the Street Entity (parent of the test entity) m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); - // Clear selection - ClearSelectedEntities(); // Click on Car Entity - ClickAtWorldPositionOnViewport(CarEntityPosition); + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); // Verify entity is selected auto selectedEntitiesAfter = GetSelectedEntities(); EXPECT_EQ(selectedEntitiesAfter.size(), 1); EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); - - // Clear the focus, disabling focus mode - m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); } TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself) { // Set the focus on the Car Entity (test entity) m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); - // Clear selection - ClearSelectedEntities(); // Click on Car Entity - ClickAtWorldPositionOnViewport(CarEntityPosition); + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); // Verify entity is selected auto selectedEntitiesAfter = GetSelectedEntities(); EXPECT_EQ(selectedEntitiesAfter.size(), 1); EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]); - - // Clear the focus, disabling focus mode - m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); } TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling) { // Set the focus on the SportsCar Entity (sibling of the test entity) m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]); - // Clear selection - ClearSelectedEntities(); // Click on Car Entity - ClickAtWorldPositionOnViewport(CarEntityPosition); + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); // Verify entity is selected auto selectedEntitiesAfter = GetSelectedEntities(); EXPECT_EQ(selectedEntitiesAfter.size(), 0); - - // Clear the focus, disabling focus mode - m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); } TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant) { // Set the focus on the Passenger1 Entity (child of the entity) m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]); - // Clear selection - ClearSelectedEntities(); // Click on Car Entity - ClickAtWorldPositionOnViewport(CarEntityPosition); + ClickAtWorldPositionOnViewport(WorldCarEntityPosition); // Verify entity is selected auto selectedEntitiesAfter = GetSelectedEntities(); EXPECT_EQ(selectedEntitiesAfter.size(), 0); - - // Clear the focus, disabling focus mode - m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull()); } } diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp index 22bc7a494c..eec3902f99 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -33,55 +33,40 @@ namespace AzToolsFramework TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants) { // When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't. - { - m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); + m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); - } - - // Restore default expected focus. - m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); } TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings) { // If the root entity has siblings, they are also outside of the focus subtree. - { - m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); + m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false); - } - - // Restore default expected focus. - m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false); } TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf) { // If the root is a leaf, then the focus subtree will consists of just that entity. - { - m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]); + m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); - } - - // Restore default expected focus. - m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); } TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear) @@ -90,15 +75,13 @@ namespace AzToolsFramework m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); // When the focus is cleared, the whole level is in the focus subtree; so we expect all entities to return true. - { - m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true); - EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); - } + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true); + EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true); } } diff --git a/Code/Framework/AzToolsFramework/Tests/LogLines.cpp b/Code/Framework/AzToolsFramework/Tests/LogLines.cpp index 5af6513720..1c69e855ee 100644 --- a/Code/Framework/AzToolsFramework/Tests/LogLines.cpp +++ b/Code/Framework/AzToolsFramework/Tests/LogLines.cpp @@ -25,7 +25,7 @@ namespace UnitTest R"X(Executing RC.EXE: '"E:\lyengine\dev\windows\bin\profile\rc.exe" "E:/Directory/File.tga")X", R"X(Executing RC.EXE with working directory : '')X", R"X(ResourceCompiler 64 - bit DEBUG)X", - R"X(Platform support : PC, PowerVR, etc2Comp)X", + R"X(Platform support : PC, PowerVR)X", R"X(Version 1.1.8.6 Nov 5 2018 13 : 28 : 28)X" }; diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp index 72489b07a1..86c73e72e5 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace UnitTest @@ -72,6 +73,9 @@ namespace UnitTest m_prefabFocusInterface = AZ::Interface::Get(); ASSERT_TRUE(m_prefabFocusInterface != nullptr); + m_prefabFocusPublicInterface = AZ::Interface::Get(); + ASSERT_TRUE(m_prefabFocusPublicInterface != nullptr); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); @@ -91,6 +95,7 @@ namespace UnitTest AZStd::unique_ptr m_rootInstance; PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); inline static const char* CityEntityName = "City"; @@ -105,7 +110,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing the container entity of the root prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CityEntityName]->GetTemplateId()); @@ -120,7 +125,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CityEntityName]->GetTemplateId()); @@ -135,7 +140,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId()); @@ -149,7 +154,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId()); @@ -169,7 +174,7 @@ namespace UnitTest prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); EXPECT_TRUE(rootPrefabInstance.has_value()); - m_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), rootPrefabInstance->get().GetTemplateId()); @@ -183,10 +188,10 @@ namespace UnitTest { // Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested) { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); } } @@ -194,13 +199,13 @@ namespace UnitTest { // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants) { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId()); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); } } @@ -208,12 +213,12 @@ namespace UnitTest { // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings) { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId())); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); } } diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 87d522c4a6..ef6774f620 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -37,8 +37,11 @@ set(FILES EntityTestbed.h FileFunc.cpp FingerprintingTests.cpp + FocusMode/ContainerEntitySelectionTests.cpp + FocusMode/ContainerEntityTests.cpp FocusMode/EditorFocusModeFixture.cpp FocusMode/EditorFocusModeFixture.h + FocusMode/EditorFocusModeSelectionFixture.h FocusMode/EditorFocusModeSelectionTests.cpp FocusMode/EditorFocusModeTests.cpp GenericComponentWrapperTest.cpp diff --git a/Code/Legacy/CryCommon/IMovieSystem.h b/Code/Legacy/CryCommon/IMovieSystem.h index 6cc5a06c8f..4da08d3bbf 100644 --- a/Code/Legacy/CryCommon/IMovieSystem.h +++ b/Code/Legacy/CryCommon/IMovieSystem.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -181,7 +182,7 @@ public: private: AnimParamType m_type; - AZStd::string m_name; + AZStd::basic_string, AZStd::stateless_allocator> m_name; }; namespace AZStd @@ -617,7 +618,7 @@ public: , valueType(_valueType) , flags(_flags) {}; - AZStd::string name; // parameter name. + AZStd::basic_string, AZStd::stateless_allocator> name; // parameter name. CAnimParamType paramType; // parameter id. AnimValueType valueType; // value type, defines type of track to use for animating this parameter. ESupportedParamFlags flags; // combination of flags from ESupportedParamFlags. diff --git a/Code/Legacy/CryCommon/StlUtils.h b/Code/Legacy/CryCommon/StlUtils.h index 4aafd4dd0e..f5128a564f 100644 --- a/Code/Legacy/CryCommon/StlUtils.h +++ b/Code/Legacy/CryCommon/StlUtils.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -491,6 +492,13 @@ namespace stl return type; } + //! Specialization of string to const char cast. + template <> + inline const char* constchar_cast(const AZStd::basic_string, AZStd::stateless_allocator>& type) + { + return type.c_str(); + } + //! Specialization of string to const char cast. template <> inline const char* constchar_cast(const AZStd::string& type) diff --git a/Code/Legacy/CrySystem/CMakeLists.txt b/Code/Legacy/CrySystem/CMakeLists.txt index 4c1f0d9b82..ebfc866f4a 100644 --- a/Code/Legacy/CrySystem/CMakeLists.txt +++ b/Code/Legacy/CrySystem/CMakeLists.txt @@ -27,7 +27,7 @@ ly_add_target( 3rdParty::expat 3rdParty::lz4 3rdParty::md5 - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::zstd Legacy::CryCommon Legacy::CrySystem.XMLBinary diff --git a/Code/Legacy/CrySystem/DebugCallStack.cpp b/Code/Legacy/CrySystem/DebugCallStack.cpp index 8018e46d69..19fe4ce8df 100644 --- a/Code/Legacy/CrySystem/DebugCallStack.cpp +++ b/Code/Legacy/CrySystem/DebugCallStack.cpp @@ -222,6 +222,9 @@ void UpdateFPExceptionsMaskForThreads() ////////////////////////////////////////////////////////////////////////// int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer) { + AZ_TracePrintf("Exit", "Exception with exit code: 0x%x", exception_pointer->ExceptionRecord->ExceptionCode); + AZ::Debug::Trace::PrintCallstack("Exit"); + if (gEnv == NULL) { return EXCEPTION_EXECUTE_HANDLER; diff --git a/Code/Legacy/CrySystem/LevelSystem/SpawnableLevelSystem.cpp b/Code/Legacy/CrySystem/LevelSystem/SpawnableLevelSystem.cpp index a82c4a6914..b2b67c3b75 100644 --- a/Code/Legacy/CrySystem/LevelSystem/SpawnableLevelSystem.cpp +++ b/Code/Legacy/CrySystem/LevelSystem/SpawnableLevelSystem.cpp @@ -22,22 +22,33 @@ #include #include #include +#include #include #include namespace LegacyLevelSystem { + constexpr AZStd::string_view DeferredLoadLevelKey = "/O3DE/Runtime/SpawnableLevelSystem/DeferredLoadLevel"; //------------------------------------------------------------------------ static void LoadLevel(const AZ::ConsoleCommandContainer& arguments) { AZ_Error("SpawnableLevelSystem", !arguments.empty(), "LoadLevel requires a level file name to be provided."); AZ_Error("SpawnableLevelSystem", arguments.size() == 1, "LoadLevel requires a single level file name to be provided."); - if (!arguments.empty() && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) + if (!arguments.empty() && gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) { gEnv->pSystem->GetILevelSystem()->LoadLevel(arguments[0].data()); } + else if (!arguments.empty()) + { + // The SpawnableLevelSystem isn't available yet. + // Defer the level load until later by storing it in the SettingsRegistry + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Set(DeferredLoadLevelKey, arguments.front()); + } + } } //------------------------------------------------------------------------ @@ -45,7 +56,7 @@ namespace LegacyLevelSystem { AZ_Warning("SpawnableLevelSystem", !arguments.empty(), "UnloadLevel doesn't use any arguments."); - if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) + if (gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) { gEnv->pSystem->GetILevelSystem()->UnloadLevel(); } @@ -73,6 +84,24 @@ namespace LegacyLevelSystem } AzFramework::RootSpawnableNotificationBus::Handler::BusConnect(); + + // If there were LoadLevel command invocations before the creation of the level system + // then those invocations were queued. + // load the last level in the queue, since only one level can be loaded at a time + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::SettingsRegistryInterface::FixedValueString deferredLevelName; + settingsRegistry->Get(deferredLevelName, DeferredLoadLevelKey) && !deferredLevelName.empty()) + { + // since this is the constructor any derived classes vtables aren't setup yet + // call this class LoadLevel function + AZ_TracePrintf("SpawnableLevelSystem", "The Level System is now available." + " Loading level %s which could not be loaded earlier\n", deferredLevelName.c_str()); + SpawnableLevelSystem::LoadLevel(deferredLevelName.c_str()); + // Delete the key with the deferred level name + settingsRegistry->Remove(DeferredLoadLevelKey); + } + } } //------------------------------------------------------------------------ @@ -173,7 +202,7 @@ namespace LegacyLevelSystem } // Make sure a spawnable level exists that matches levelname - AZStd::string validLevelName = ""; + AZStd::string validLevelName; AZ::Data::AssetId rootSpawnableAssetId; AZ::Data::AssetCatalogRequestBus::BroadcastResult( rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, nullptr, false); diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index 6de10f3855..ee1c498a9a 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -1273,17 +1273,6 @@ AZ_POP_DISABLE_WARNING //Load config files ////////////////////////////////////////////////////////////////////////// - int curSpecVal = 0; - ICVar* pSysSpecCVar = gEnv->pConsole->GetCVar("r_GraphicsQuality"); - if (gEnv->pSystem->IsDevMode()) - { - if (pSysSpecCVar && pSysSpecCVar->GetFlags() & VF_WASINCONFIG) - { - curSpecVal = pSysSpecCVar->GetIVal(); - pSysSpecCVar->SetFlags(pSysSpecCVar->GetFlags() | VF_SYSSPEC_OVERWRITE); - } - } - // tools may not interact with @user@ if (!gEnv->IsInToolMode()) { @@ -1293,16 +1282,6 @@ AZ_POP_DISABLE_WARNING } } - // If sys spec variable was specified, is not 0, and we are in devmode restore the value from before loading game.cfg - // This enables setting of a specific sys_spec outside menu and game.cfg - if (gEnv->pSystem->IsDevMode()) - { - if (pSysSpecCVar && curSpecVal && curSpecVal != pSysSpecCVar->GetIVal()) - { - pSysSpecCVar->Set(curSpecVal); - } - } - { // We have to load this file again since first time we did it without devmode LoadConfiguration(m_systemConfigName.c_str()); diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 6f07901e27..69397745f1 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -590,20 +590,22 @@ TEST_F(PlatformConfigurationUnitTests, Test_GemHandling) AssetUtilities::ResetAssetRoot(); - ASSERT_EQ(2, config.GetScanFolderCount()); + ASSERT_EQ(4, config.GetScanFolderCount()); EXPECT_FALSE(config.GetScanFolderAt(0).IsRoot()); EXPECT_TRUE(config.GetScanFolderAt(0).RecurseSubFolders()); // the first one is a game gem, so its order should be above 1 but below 100. EXPECT_GE(config.GetScanFolderAt(0).GetOrder(), 100); EXPECT_EQ(0, config.GetScanFolderAt(0).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive)); - // for each gem, there are currently 1 scan folder, the gem assets folder, with no output prefix + // for each gem, there are currently 2 scan folders: + // The Gem's 'Assets' folder + // The Gem's 'Registry' folder expectedScanFolder = tempPath.absoluteFilePath("Gems/LmbrCentral/v2/Assets"); - EXPECT_FALSE(config.GetScanFolderAt(1).IsRoot() ); - EXPECT_TRUE(config.GetScanFolderAt(1).RecurseSubFolders()); - EXPECT_GT(config.GetScanFolderAt(1).GetOrder(), config.GetScanFolderAt(0).GetOrder()); - EXPECT_EQ(0, config.GetScanFolderAt(1).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive)); + EXPECT_FALSE(config.GetScanFolderAt(2).IsRoot() ); + EXPECT_TRUE(config.GetScanFolderAt(2).RecurseSubFolders()); + EXPECT_GT(config.GetScanFolderAt(2).GetOrder(), config.GetScanFolderAt(0).GetOrder()); + EXPECT_EQ(0, config.GetScanFolderAt(2).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive)); } TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes) diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp index 52df8e901d..4a033cf6ca 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp @@ -1582,6 +1582,24 @@ namespace AssetProcessor gemOrder, /*scanFolderId*/ 0, /*canSaveNewAssets*/ true)); // Users can create assets like slices in Gem asset folders. + + // Now add another scan folder on Gem/GemName/Registry... + gemFolder = gemDir.absoluteFilePath(AzFramework::GemInfo::GetGemRegistryFolder()); + gemFolder = AssetUtilities::NormalizeDirectoryPath(gemFolder); + + assetBrowserDisplayName = AzFramework::GemInfo::GetGemRegistryFolder(); + portableKey = QString("gemregistry-%1").arg(gemNameAsUuid); + gemOrder++; + + AZ_TracePrintf(AssetProcessor::DebugChannel, "Adding GEM registry folder for monitoring / scanning: %s.\n", gemFolder.toUtf8().data()); + AddScanFolder(ScanFolderInfo( + gemFolder, + assetBrowserDisplayName, + portableKey, + isRoot, + isRecursive, + platforms, + gemOrder)); } } } diff --git a/Code/Tools/ProjectManager/Resources/Download.svg b/Code/Tools/ProjectManager/Resources/Download.svg new file mode 100644 index 0000000000..c2b0c2ce3c --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Download.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 30bcc1ace5..aeaf9a9248 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -34,9 +34,11 @@ Warning.svg Backgrounds/DefaultBackground.jpg Backgrounds/FtueBackground.jpg - FeatureTagClose.svg + X.svg Refresh.svg Edit.svg Delete.svg + Download.svg + in_progress.gif diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index f91a597481..5f7826dbac 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -496,13 +496,34 @@ QProgressBar::chunk { background-color: #333333; } -/************** Filter tag widget **************/ +#GemDependenciesDialog QLabel { + margin-bottom:10px; +} + +#GemDependenciesDialog QCheckBox { + background-color: #333333; + border-radius: 3px; + spacing:3px; + margin-right:5px; + padding:4px; + margin-top:5px; +} + +/************** Filter Tag widget **************/ #FilterTagWidgetTextLabel { color: #94D2FF; font-size: 10px; } +#TagWidget { + background-color: #333333; + padding:3px; + font-size:12px; + border-radius: 3px; + margin-right: 3px; +} + /************** Gems SubWidget **************/ #gemSubWidgetTitleLabel { diff --git a/Code/Tools/ProjectManager/Resources/FeatureTagClose.svg b/Code/Tools/ProjectManager/Resources/X.svg similarity index 100% rename from Code/Tools/ProjectManager/Resources/FeatureTagClose.svg rename to Code/Tools/ProjectManager/Resources/X.svg diff --git a/Code/Tools/ProjectManager/Resources/in_progress.gif b/Code/Tools/ProjectManager/Resources/in_progress.gif new file mode 100644 index 0000000000..eb392a9b89 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/in_progress.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64985a78205da45f4bb92b040c348d96fe7cd7277549c1f79c430469a0d3bab7 +size 166393 diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index f098518fd3..84b931cb30 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -238,9 +238,13 @@ namespace O3DE::ProjectManager PythonBindingsInterface::Get()->AddProject(projectInfo.m_path); #ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED - if (!m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path)) + const GemCatalogScreen::EnableDisableGemsResult gemResult = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); + if (gemResult == GemCatalogScreen::EnableDisableGemsResult::Failed) { QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for template.")); + } + if (gemResult != GemCatalogScreen::EnableDisableGemsResult::Success) + { return; } #endif // TEMPLATE_GEM_CONFIGURATION_ENABLED diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index a41a81b448..945878768d 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -134,7 +135,7 @@ namespace O3DE::ProjectManager } } - bool GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath) + GemCatalogScreen::EnableDisableGemsResult GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath) { IPythonBindings* pythonBindings = PythonBindingsInterface::Get(); QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); @@ -142,15 +143,25 @@ namespace O3DE::ProjectManager if (m_gemModel->DoGemsToBeAddedHaveRequirements()) { - GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, toBeAdded, this); - confirmRequirementsDialog->exec(); - - if (confirmRequirementsDialog->GetButtonResult() != QDialogButtonBox::ApplyRole) + GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, this); + if(confirmRequirementsDialog->exec() == QDialog::Rejected) { - return false; + return EnableDisableGemsResult::Cancel; } } + if (m_gemModel->HasDependentGemsToRemove()) + { + GemDependenciesDialog* dependenciesDialog = new GemDependenciesDialog(m_gemModel, this); + if(dependenciesDialog->exec() == QDialog::Rejected) + { + return EnableDisableGemsResult::Cancel; + } + + toBeAdded = m_gemModel->GatherGemsToBeAdded(); + toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + } + for (const QModelIndex& modelIndex : toBeAdded) { const QString gemPath = GemModel::GetPath(modelIndex); @@ -160,7 +171,7 @@ namespace O3DE::ProjectManager QMessageBox::critical(nullptr, "Operation failed", QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); - return false; + return EnableDisableGemsResult::Failed; } } @@ -173,11 +184,11 @@ namespace O3DE::ProjectManager QMessageBox::critical(nullptr, "Operation failed", QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); - return false; + return EnableDisableGemsResult::Failed; } } - return true; + return EnableDisableGemsResult::Success; } ProjectManagerScreen GemCatalogScreen::GetScreenEnum() diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 5b48b2f90e..72e8d44f65 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -29,7 +29,14 @@ namespace O3DE::ProjectManager ProjectManagerScreen GetScreenEnum() override; void ReinitForProject(const QString& projectPath); - bool EnableDisableGemsForProject(const QString& projectPath); + + enum class EnableDisableGemsResult + { + Failed = 0, + Success, + Cancel + }; + EnableDisableGemsResult EnableDisableGemsForProject(const QString& projectPath); GemModel* GetGemModel() const { return m_gemModel; } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.cpp new file mode 100644 index 0000000000..98aebd8fab --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemDependenciesDialog::GemDependenciesDialog(GemModel* gemModel, QWidget *parent) + : QDialog(parent) + { + setWindowTitle(tr("Dependent Gems")); + setObjectName("GemDependenciesDialog"); + setAttribute(Qt::WA_DeleteOnClose); + setModal(true); + + QVBoxLayout* layout = new QVBoxLayout(); + // layout margin/alignment cannot be set with qss + layout->setMargin(15); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + // message + QLabel* instructionLabel = new QLabel( + tr("The following gem dependencies are no longer needed and will be deactivated.

" + "To keep these Gems enabled, select the checkbox next to it.")); + layout->addWidget(instructionLabel); + + // checkboxes + FlowLayout* flowLayout = new FlowLayout(); + QVector gemsToRemove = gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true); + for (const QModelIndex& gem : gemsToRemove) + { + if (GemModel::WasPreviouslyAddedDependency(gem)) + { + QCheckBox* checkBox = new QCheckBox(GemModel::GetName(gem)); + connect(checkBox, &QCheckBox::stateChanged, this, + [=](int state) + { + GemModel::SetIsAdded(*gemModel, gem, /*isAdded=*/state == Qt::Checked); + }); + flowLayout->addWidget(checkBox); + } + } + layout->addLayout(flowLayout); + + layout->addSpacing(10); + layout->addStretch(1); + + // buttons + QDialogButtonBox* dialogButtons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + connect(dialogButtons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(dialogButtons, &QDialogButtonBox::rejected, this, + [=]() + { + // de-select any Gems the user selected because they're canceling + for (const QModelIndex& gem : gemsToRemove) + { + if (GemModel::WasPreviouslyAddedDependency(gem) && GemModel::IsAdded(gem)) + { + GemModel::SetIsAdded(*gemModel, gem, /*isAdded=*/false); + } + } + + reject(); + }); + layout->addWidget(dialogButtons); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h new file mode 100644 index 0000000000..df8ca6f8a2 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(GemModel) + + class GemDependenciesDialog + : public QDialog + { + Q_OBJECT // AUTOMOC + public: + explicit GemDependenciesDialog(GemModel* gemModel, QWidget *parent = nullptr); + ~GemDependenciesDialog() = default; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp index 138880f44e..69a883d169 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager m_closeButton = new QPushButton(); m_closeButton->setFlat(true); - m_closeButton->setIcon(QIcon(":/FeatureTagClose.svg")); + m_closeButton->setIcon(QIcon(":/X.svg")); m_closeButton->setIconSize(QSize(12, 12)); m_closeButton->setStyleSheet("QPushButton { background-color: transparent; border: 0px }"); layout->addWidget(m_closeButton); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp index 771d644617..cbdcf64162 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp @@ -8,6 +8,8 @@ #include "GemInfo.h" +#include + namespace O3DE::ProjectManager { GemInfo::GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded) @@ -29,17 +31,17 @@ namespace O3DE::ProjectManager switch (platform) { case Android: - return "Android"; + return QObject::tr("Android"); case iOS: - return "iOS"; + return QObject::tr("iOS"); case Linux: - return "Linux"; + return QObject::tr("Linux"); case macOS: - return "macOS"; + return QObject::tr("macOS"); case Windows: - return "Windows"; + return QObject::tr("Windows"); default: - return ""; + return QObject::tr(""); } } @@ -48,13 +50,13 @@ namespace O3DE::ProjectManager switch (type) { case Asset: - return "Asset"; + return QObject::tr("Asset"); case Code: - return "Code"; + return QObject::tr("Code"); case Tool: - return "Tool"; + return QObject::tr("Tool"); default: - return ""; + return QObject::tr(""); } } @@ -62,15 +64,33 @@ namespace O3DE::ProjectManager { switch (origin) { - case Open3DEEngine: - return "Open 3D Engine"; + case Open3DEngine: + return QObject::tr("Open 3D Engine"); case Local: - return "Local"; + return QObject::tr("Local"); + case Remote: + return QObject::tr("Remote"); default: - return ""; + return QObject::tr(""); } } + QString GemInfo::GetDownloadStatusString(DownloadStatus status) + { + switch (status) + { + case NotDownloaded: + return QObject::tr("Not Downloaded"); + case Downloading: + return QObject::tr("Downloading"); + case Downloaded: + return QObject::tr("Downloaded"); + case UnknownDownloadStatus: + default: + return QObject::tr(""); + } + }; + bool GemInfo::IsPlatformSupported(Platform platform) const { return (m_platforms & platform); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 311eeb93f6..8c6d40505a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -44,13 +44,23 @@ namespace O3DE::ProjectManager enum GemOrigin { - Open3DEEngine = 1 << 0, + Open3DEngine = 1 << 0, Local = 1 << 1, - NumGemOrigins = 2 + Remote = 1 << 2, + NumGemOrigins = 3 }; Q_DECLARE_FLAGS(GemOrigins, GemOrigin) static QString GetGemOriginString(GemOrigin origin); + enum DownloadStatus + { + UnknownDownloadStatus = -1, + NotDownloaded, + Downloading, + Downloaded, + }; + static QString GetDownloadStatusString(DownloadStatus status); + GemInfo() = default; GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded); bool IsPlatformSupported(Platform platform) const; @@ -68,6 +78,7 @@ namespace O3DE::ProjectManager QString m_summary = "No summary provided."; Platforms m_platforms; Types m_types; //! Asset and/or Code and/or Tool + DownloadStatus m_downloadStatus = UnknownDownloadStatus; QStringList m_features; QString m_requirement; QString m_directoryLink; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index dc24c13009..e15c4b3b39 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -9,6 +9,8 @@ #include #include #include +#include + #include #include #include @@ -16,6 +18,10 @@ #include #include #include +#include +#include +#include +#include namespace O3DE::ProjectManager { @@ -28,6 +34,11 @@ namespace O3DE::ProjectManager AddPlatformIcon(GemInfo::Linux, ":/Linux.svg"); AddPlatformIcon(GemInfo::macOS, ":/macOS.svg"); AddPlatformIcon(GemInfo::Windows, ":/Windows.svg"); + + SetStatusIcon(m_notDownloadedPixmap, ":/Download.svg"); + SetStatusIcon(m_unknownStatusPixmap, ":/X.svg"); + + m_downloadingMovie = new QMovie(":/in_progress.gif"); } void GemItemDelegate::AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath) @@ -37,6 +48,25 @@ namespace O3DE::ProjectManager m_platformIcons.insert(platform, QIcon(iconPath).pixmap(static_cast(static_cast(s_platformIconSize) * aspectRatio), s_platformIconSize)); } + void GemItemDelegate::SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath) + { + QPixmap pixmap(iconPath); + float aspectRatio = static_cast(pixmap.width()) / pixmap.height(); + int xScaler = s_statusIconSize; + int yScaler = s_statusIconSize; + + if (aspectRatio > 1.0f) + { + yScaler = static_cast(1.0f / aspectRatio * s_statusIconSize); + } + else if (aspectRatio < 1.0f) + { + xScaler = static_cast(aspectRatio * s_statusIconSize); + } + + m_iconPixmap = QPixmap(QIcon(iconPath).pixmap(xScaler, yScaler)); + } + void GemItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const { if (!modelIndex.isValid()) @@ -52,6 +82,8 @@ namespace O3DE::ProjectManager QRect fullRect, itemRect, contentRect; CalcRects(options, fullRect, itemRect, contentRect); + QRect buttonRect = CalcButtonRect(contentRect); + QFont standardFont(options.font); standardFont.setPixelSize(static_cast(s_fontSize)); QFontMetrics standardFontMetrics(standardFont); @@ -104,30 +136,31 @@ namespace O3DE::ProjectManager painter->drawText(gemCreatorRect, Qt::TextSingleLine, gemCreator); // Gem summary - - // In case there are feature tags displayed at the bottom, decrease the size of the summary text field. const QStringList featureTags = GemModel::GetFeatures(modelIndex); - const int featureTagAreaHeight = 30; - const int summaryHeight = contentRect.height() - (!featureTags.empty() * featureTagAreaHeight); - - const int additionalSummarySpacing = s_itemMargins.right() * 3; - const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - additionalSummarySpacing, - summaryHeight); - const QRect summaryRect = QRect(/*topLeft=*/QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), summarySize); - - painter->setFont(standardFont); - painter->setPen(m_textColor); - + const bool hasTags = !featureTags.isEmpty(); const QString summary = GemModel::GetSummary(modelIndex); - painter->drawText(summaryRect, Qt::AlignLeft | Qt::TextWordWrap, summary); + const QRect summaryRect = CalcSummaryRect(contentRect, hasTags); + DrawText(summary, painter, summaryRect, standardFont); - DrawButton(painter, contentRect, modelIndex); + DrawDownloadStatusIcon(painter, contentRect, buttonRect, modelIndex); + DrawButton(painter, buttonRect, modelIndex); DrawPlatformIcons(painter, contentRect, modelIndex); DrawFeatureTags(painter, contentRect, featureTags, standardFont, summaryRect); painter->restore(); } + QRect GemItemDelegate::CalcSummaryRect(const QRect& contentRect, bool hasTags) const + { + const int featureTagAreaHeight = 30; + const int summaryHeight = contentRect.height() - (hasTags * featureTagAreaHeight); + + const int additionalSummarySpacing = s_itemMargins.right() * 3; + const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - additionalSummarySpacing, + summaryHeight); + return QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), summarySize); + } + QSize GemItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const { QStyleOptionViewItem options(option); @@ -154,7 +187,7 @@ namespace O3DE::ProjectManager return true; } } - else if (event->type() == QEvent::MouseButtonPress ) + else if (event->type() == QEvent::MouseButtonPress) { QMouseEvent* mouseEvent = static_cast(event); @@ -168,6 +201,21 @@ namespace O3DE::ProjectManager GemModel::SetIsAdded(*model, modelIndex, !isAdded); return true; } + + // we must manually handle html links because we aren't using QLabels + const QStringList featureTags = GemModel::GetFeatures(modelIndex); + const bool hasTags = !featureTags.isEmpty(); + const QRect summaryRect = CalcSummaryRect(contentRect, hasTags); + if (summaryRect.contains(mouseEvent->pos())) + { + const QString html = GemModel::GetSummary(modelIndex); + QString anchor = anchorAt(html, mouseEvent->pos(), summaryRect); + if (!anchor.isEmpty()) + { + QDesktopServices::openUrl(QUrl(anchor)); + return true; + } + } } return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); @@ -251,7 +299,7 @@ namespace O3DE::ProjectManager QRect GemItemDelegate::CalcButtonRect(const QRect& contentRect) const { - const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth - s_itemMargins.right(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2); + const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth, contentRect.center().y() - s_buttonHeight / 2); const QSize size = QSize(s_buttonWidth, s_buttonHeight); return QRect(topLeft, size); } @@ -321,10 +369,47 @@ namespace O3DE::ProjectManager } } - void GemItemDelegate::DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const + AZStd::unique_ptr GetTextDocument(const QString& text, int width) + { + // using unique_ptr as a workaround for QTextDocument having a private copy constructor + auto doc = AZStd::make_unique(); + QTextOption textOption(doc->defaultTextOption()); + textOption.setWrapMode(QTextOption::WordWrap); + doc->setDefaultTextOption(textOption); + doc->setHtml(text); + doc->setTextWidth(width); + return doc; + } + + void GemItemDelegate::DrawText(const QString& text, QPainter* painter, const QRect& rect, const QFont& standardFont) const + { + painter->save(); + + if (text.contains('<')) + { + painter->translate(rect.topLeft()); + + // use QTextDocument because drawText does not support rich text or html + QAbstractTextDocumentLayout::PaintContext paintContext; + paintContext.clip = QRect(0, 0, rect.width(), rect.height()); + paintContext.palette.setColor(QPalette::Text, painter->pen().color()); + + AZStd::unique_ptr textDocument = GetTextDocument(text, rect.width()); + textDocument->documentLayout()->draw(painter, paintContext); + } + else + { + painter->setFont(standardFont); + painter->setPen(m_textColor); + painter->drawText(rect, Qt::AlignLeft | Qt::TextWordWrap, text); + } + + painter->restore(); + } + + void GemItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const { painter->save(); - const QRect buttonRect = CalcButtonRect(contentRect); QPoint circleCenter; if (GemModel::IsAdded(modelIndex)) @@ -355,4 +440,60 @@ namespace O3DE::ProjectManager painter->restore(); } + + QString GemItemDelegate::anchorAt(const QString& html, const QPoint& position, const QRect& rect) + { + if (!html.isEmpty()) + { + AZStd::unique_ptr doc = GetTextDocument(html, rect.width()); + QAbstractTextDocumentLayout* layout = doc->documentLayout(); + if (layout) + { + return layout->anchorAt(position - rect.topLeft()); + } + } + + return QString(); + } + + void GemItemDelegate::DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const + { + const GemInfo::DownloadStatus downloadStatus = GemModel::GetDownloadStatus(modelIndex); + + // Show no icon if gem is already downloaded + if (downloadStatus == GemInfo::DownloadStatus::Downloaded) + { + return; + } + + QPixmap currentFrame; + const QPixmap* statusPixmap; + if (downloadStatus == GemInfo::DownloadStatus::Downloading) + { + if (m_downloadingMovie->state() != QMovie::Running) + { + m_downloadingMovie->start(); + emit MovieStartedPlaying(m_downloadingMovie); + } + + currentFrame = m_downloadingMovie->currentPixmap(); + currentFrame = currentFrame.scaled(s_statusIconSize, s_statusIconSize); + statusPixmap = ¤tFrame; + } + else if (downloadStatus == GemInfo::DownloadStatus::NotDownloaded) + { + statusPixmap = &m_notDownloadedPixmap; + } + else + { + statusPixmap = &m_unknownStatusPixmap; + } + + QSize statusSize = statusPixmap->size(); + + painter->drawPixmap( + buttonRect.left() - s_statusButtonSpacing - statusSize.width(), + contentRect.center().y() - statusSize.height() / 2, + *statusPixmap); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h index d842f63ae7..c013be0d9e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -30,6 +30,7 @@ namespace O3DE::ProjectManager void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + virtual QString anchorAt(const QString& html, const QPoint& position, const QRect& rect); // Colors const QColor m_textColor = QColor("#FFFFFF"); @@ -48,13 +49,13 @@ namespace O3DE::ProjectManager // Margin and borders inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/16, /*top=*/8, /*right=*/16, /*bottom=*/8); // Item border distances - inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/15, /*bottom=*/12); // Distances of the elements within an item to the item borders + inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/20, /*bottom=*/12); // Distances of the elements within an item to the item borders inline constexpr static int s_borderWidth = 4; // Button - inline constexpr static int s_buttonWidth = 55; - inline constexpr static int s_buttonHeight = 18; - inline constexpr static int s_buttonBorderRadius = 9; + inline constexpr static int s_buttonWidth = 32; + inline constexpr static int s_buttonHeight = 16; + inline constexpr static int s_buttonBorderRadius = s_buttonHeight / 2; inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2; inline constexpr static qreal s_buttonFontSize = 10.0; @@ -64,6 +65,9 @@ namespace O3DE::ProjectManager inline constexpr static int s_featureTagBorderMarginY = 3; inline constexpr static int s_featureTagSpacing = 7; + signals: + void MovieStartedPlaying(const QMovie* playingMovie) const; + protected: bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) override; @@ -71,9 +75,12 @@ namespace O3DE::ProjectManager void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; QRect CalcButtonRect(const QRect& contentRect) const; + QRect CalcSummaryRect(const QRect& contentRect, bool hasTags) const; void DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; - void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; + void DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const; void DrawFeatureTags(QPainter* painter, const QRect& contentRect, const QStringList& featureTags, const QFont& standardFont, const QRect& summaryRect) const; + void DrawText(const QString& text, QPainter* painter, const QRect& rect, const QFont& standardFont) const; + void DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const; QAbstractItemModel* m_model = nullptr; @@ -82,5 +89,14 @@ namespace O3DE::ProjectManager void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath); inline constexpr static int s_platformIconSize = 12; QHash m_platformIcons; + + // Status icons + void SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath); + inline constexpr static int s_statusIconSize = 16; + inline constexpr static int s_statusButtonSpacing = 5; + + QPixmap m_unknownStatusPixmap; + QPixmap m_notDownloadedPixmap; + QMovie* m_downloadingMovie = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index ab51c7511c..10ff31f33b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -103,11 +103,11 @@ namespace O3DE::ProjectManager QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); columnHeaderLayout->addSpacerItem(horizontalSpacer); - QLabel* gemSelectedLabel = new QLabel(tr("Selected")); + QLabel* gemSelectedLabel = new QLabel(tr("Status")); gemSelectedLabel->setObjectName("GemCatalogHeaderLabel"); columnHeaderLayout->addWidget(gemSelectedLabel); - columnHeaderLayout->addSpacing(65); + columnHeaderLayout->addSpacing(72); vLayout->addLayout(columnHeaderLayout); } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp index afdb9697c9..cfdf7fa5b3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp @@ -8,27 +8,11 @@ #include #include -#include -#include + +#include namespace O3DE::ProjectManager { - class GemListViewProxyStyle : public QProxyStyle - { - public: - using QProxyStyle::QProxyStyle; - int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override - { - if (hint == QStyle::SH_ToolTip_WakeUpDelay || hint == QStyle::SH_ToolTip_FallAsleepDelay) - { - // no delay - return 0; - } - - return QProxyStyle::styleHint(hint, option, widget, returnData); - } - }; - GemListView::GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent) : QListView(parent) { @@ -37,9 +21,17 @@ namespace O3DE::ProjectManager setModel(model); setSelectionModel(selectionModel); - setItemDelegate(new GemItemDelegate(model, this)); + GemItemDelegate* itemDelegate = new GemItemDelegate(model, this); + + connect(itemDelegate, &GemItemDelegate::MovieStartedPlaying, [=](const QMovie* playingMovie) + { + // Force redraw when movie is playing so animation is smooth + connect(playingMovie, &QMovie::frameChanged, this, [=] + { + this->viewport()->repaint(); + }); + }); - // use a custom proxy style so we get immediate tooltips for gem radio buttons - setStyle(new GemListViewProxyStyle(this->style())); + setItemDelegate(itemDelegate); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 0941541793..35491f4ddd 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -48,6 +48,7 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_features, RoleFeatures); item->setData(gemInfo.m_path, RolePath); item->setData(gemInfo.m_requirement, RoleRequirement); + item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus); appendRow(item); @@ -132,6 +133,11 @@ namespace O3DE::ProjectManager return static_cast(modelIndex.data(RoleTypes).toInt()); } + GemInfo::DownloadStatus GemModel::GetDownloadStatus(const QModelIndex& modelIndex) + { + return static_cast(modelIndex.data(RoleDownloadStatus).toInt()); + } + QString GemModel::GetSummary(const QModelIndex& modelIndex) { return modelIndex.data(RoleSummary).toString(); @@ -373,6 +379,11 @@ namespace O3DE::ProjectManager return previouslyAdded && !added; } + void GemModel::SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status) + { + model.setData(modelIndex, status, RoleDownloadStatus); + } + bool GemModel::HasRequirement(const QModelIndex& modelIndex) { return !modelIndex.data(RoleRequirement).toString().isEmpty(); @@ -391,6 +402,20 @@ namespace O3DE::ProjectManager return false; } + bool GemModel::HasDependentGemsToRemove() const + { + for (int row = 0; row < rowCount(); ++row) + { + const QModelIndex modelIndex = index(row, 0); + if (GemModel::NeedsToBeRemoved(modelIndex, /*includeDependencies=*/true) && + GemModel::WasPreviouslyAddedDependency(modelIndex)) + { + return true; + } + } + return false; + } + QVector GemModel::GatherGemDependencies(const QModelIndex& modelIndex) const { QVector result; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 0591094c11..0d1c225f74 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -40,6 +40,7 @@ namespace O3DE::ProjectManager static GemInfo::GemOrigin GetGemOrigin(const QModelIndex& modelIndex); static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex); static GemInfo::Types GetTypes(const QModelIndex& modelIndex); + static GemInfo::DownloadStatus GetDownloadStatus(const QModelIndex& modelIndex); static QString GetSummary(const QModelIndex& modelIndex); static QString GetDirectoryLink(const QModelIndex& modelIndex); static QString GetDocLink(const QModelIndex& modelIndex); @@ -64,8 +65,10 @@ namespace O3DE::ProjectManager static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false); static bool HasRequirement(const QModelIndex& modelIndex); static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex); + static void SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status); bool DoGemsToBeAddedHaveRequirements() const; + bool HasDependentGemsToRemove() const; QVector GatherGemDependencies(const QModelIndex& modelIndex) const; QVector GatherDependentGems(const QModelIndex& modelIndex, bool addedOnly = false) const; @@ -100,7 +103,8 @@ namespace O3DE::ProjectManager RoleFeatures, RoleTypes, RolePath, - RoleRequirement + RoleRequirement, + RoleDownloadStatus }; QHash m_nameToIndexMap; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp index 0ca5ca836f..f4a7148d46 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include namespace O3DE::ProjectManager { @@ -38,7 +40,6 @@ namespace O3DE::ProjectManager standardFont.setPixelSize(static_cast(s_fontSize)); QFontMetrics standardFontMetrics(standardFont); - painter->save(); painter->setClipping(true); painter->setClipRect(fullRect); painter->setFont(options.font); @@ -65,25 +66,51 @@ namespace O3DE::ProjectManager painter->drawText(gemNameRect, Qt::TextSingleLine, gemName); // Gem requirement - const QSize requirementSize = QSize(contentRect.width() - s_summaryStartX - s_itemMargins.right(), contentRect.height()); - const QRect requirementRect = QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), requirementSize); - - painter->setFont(standardFont); - painter->setPen(m_textColor); - + const QRect requirementRect = CalcRequirementRect(contentRect); const QString requirement = GemModel::GetRequirement(modelIndex); - painter->drawText(requirementRect, Qt::AlignLeft | Qt::TextWordWrap, requirement); + DrawText(requirement, painter, requirementRect, standardFont); painter->restore(); } + QRect GemRequirementDelegate::CalcRequirementRect(const QRect& contentRect) const + { + const QSize requirementSize = QSize(contentRect.width() - s_summaryStartX - s_itemMargins.right(), contentRect.height()); + return QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), requirementSize); + } + bool GemRequirementDelegate::editorEvent( [[maybe_unused]] QEvent* event, [[maybe_unused]] QAbstractItemModel* model, [[maybe_unused]] const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& modelIndex) { - // Do nothing here - return false; + if (!modelIndex.isValid()) + { + return false; + } + + if (event->type() == QEvent::MouseButtonPress) + { + QMouseEvent* mouseEvent = static_cast(event); + + QRect fullRect, itemRect, contentRect; + CalcRects(option, fullRect, itemRect, contentRect); + + const QRect requirementsRect = CalcRequirementRect(contentRect); + if (requirementsRect.contains(mouseEvent->pos())) + { + const QString html = GemModel::GetRequirement(modelIndex); + QString anchor = anchorAt(html, mouseEvent->pos(), requirementsRect); + if (!anchor.isEmpty()) + { + QDesktopServices::openUrl(QUrl(anchor)); + return true; + } + } + } + + return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h index a23b999ca0..e9001df7fa 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h @@ -29,5 +29,8 @@ namespace O3DE::ProjectManager const QColor m_backgroundColor = QColor("#444444"); // Outside of the actual gem item const QColor m_itemBackgroundColor = QColor("#393939"); // Background color of the gem item + + private: + QRect CalcRequirementRect(const QRect& contentRect) const; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.cpp index 4740d29344..23bb776c29 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.cpp @@ -19,11 +19,12 @@ namespace O3DE::ProjectManager { - GemRequirementDialog::GemRequirementDialog(GemModel* model, const QVector& gemsToAdd, QWidget* parent) + GemRequirementDialog::GemRequirementDialog(GemModel* model, QWidget* parent) : QDialog(parent) { setWindowTitle(tr("Manual setup is required")); setModal(true); + setAttribute(Qt::WA_DeleteOnClose); QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setMargin(0); @@ -51,7 +52,7 @@ namespace O3DE::ProjectManager vLayout->addSpacing(20); - GemRequirementFilterProxyModel* proxModel = new GemRequirementFilterProxyModel(model, gemsToAdd, this); + GemRequirementFilterProxyModel* proxModel = new GemRequirementFilterProxyModel(model, this); GemRequirementListView* m_gemListView = new GemRequirementListView(proxModel, proxModel->GetSelectionModel(), this); vLayout->addWidget(m_gemListView); @@ -62,27 +63,9 @@ namespace O3DE::ProjectManager QPushButton* cancelButton = dialogButtons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); cancelButton->setProperty("secondary", true); - QPushButton* continueButton = dialogButtons->addButton(tr("Continue"), QDialogButtonBox::ApplyRole); + QPushButton* continueButton = dialogButtons->addButton(tr("Continue"), QDialogButtonBox::AcceptRole); - connect(cancelButton, &QPushButton::clicked, this, &GemRequirementDialog::CancelButtonPressed); - connect(continueButton, &QPushButton::clicked, this, &GemRequirementDialog::ContinueButtonPressed); + connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject); + connect(continueButton, &QPushButton::clicked, this, &QDialog::accept); } - - QDialogButtonBox::ButtonRole GemRequirementDialog::GetButtonResult() - { - return m_buttonResult; - } - - void GemRequirementDialog::CancelButtonPressed() - { - m_buttonResult = QDialogButtonBox::RejectRole; - close(); - } - - void GemRequirementDialog::ContinueButtonPressed() - { - m_buttonResult = QDialogButtonBox::ApplyRole; - close(); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h index 22f6977332..af8b1e2cc9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h @@ -10,8 +10,6 @@ #if !defined(Q_MOC_RUN) #include - -#include #endif namespace O3DE::ProjectManager @@ -23,15 +21,7 @@ namespace O3DE::ProjectManager { Q_OBJECT // AUTOMOC public: - explicit GemRequirementDialog(GemModel* model, const QVector& gemsToAdd, QWidget *parent = nullptr); + explicit GemRequirementDialog(GemModel* model, QWidget *parent = nullptr); ~GemRequirementDialog() = default; - - QDialogButtonBox::ButtonRole GetButtonResult(); - - private: - void CancelButtonPressed(); - void ContinueButtonPressed(); - - QDialogButtonBox::ButtonRole m_buttonResult = QDialogButtonBox::RejectRole; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.cpp index 50639b7936..e89de82c6c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.cpp @@ -13,10 +13,8 @@ namespace O3DE::ProjectManager { - GemRequirementFilterProxyModel::GemRequirementFilterProxyModel(GemModel* sourceModel, const QVector& addedGems, QObject* parent) + GemRequirementFilterProxyModel::GemRequirementFilterProxyModel(GemModel* sourceModel, QObject* parent) : QSortFilterProxyModel(parent) - , m_sourceModel(sourceModel) - , m_addedGems(addedGems) { setSourceModel(sourceModel); m_selectionProxyModel = new AzQtComponents::SelectionProxyModel(sourceModel->GetSelectionModel(), this, parent); @@ -26,22 +24,7 @@ namespace O3DE::ProjectManager { // Do not use sourceParent->child because an invalid parent does not produce valid children (which our index function does) QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); - if (!sourceIndex.isValid()) - { - return false; - } - - if (!m_addedGems.contains(sourceIndex)) - { - return false; - } - - if (!m_sourceModel->HasRequirement(sourceIndex)) - { - return false; - } - - return true; + return GemModel::IsAdded(sourceIndex) && GemModel::HasRequirement(sourceIndex); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h index a40eed9fb4..7df75f7d94 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h @@ -25,16 +25,13 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - GemRequirementFilterProxyModel(GemModel* sourceModel, const QVector& addedGems, QObject* parent = nullptr); + GemRequirementFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr); AzQtComponents::SelectionProxyModel* GetSelectionModel() const { return m_selectionProxyModel; } bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; private: - GemModel* m_sourceModel = nullptr; AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr; - - QVector m_addedGems; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.cpp index daed5764ff..fd7d22cbfc 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.cpp @@ -8,7 +8,6 @@ #include #include -#include namespace O3DE::ProjectManager { diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h index 14c76bd0c2..61220cefe7 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h @@ -30,7 +30,7 @@ namespace O3DE::ProjectManager bool operator<(const GemRepoInfo& gemRepoInfo) const; QString m_path = ""; - QString m_name = "Unknown Gem Repo Name"; + QString m_name = "Unknown Repo Name"; QString m_creator = "Unknown Creator"; bool m_isEnabled = false; //! Is the repo currently enabled for this engine? QString m_summary = "No summary provided."; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp index 88ccee2636..58b10a1d1a 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -95,7 +96,7 @@ namespace O3DE::ProjectManager painter->drawText(repoCreatorRect, Qt::TextSingleLine, repoCreator); // Repo update - QString repoUpdatedDate = GemRepoModel::GetLastUpdated(modelIndex).toString("dd/MM/yyyy hh:mmap"); + QString repoUpdatedDate = GemRepoModel::GetLastUpdated(modelIndex).toString(RepoTimeFormat); repoUpdatedDate = standardFontMetrics.elidedText(repoUpdatedDate, Qt::TextElideMode::ElideRight, s_updatedMaxWidth); QRect repoUpdatedDateRect = GetTextRect(standardFont, repoUpdatedDate, s_fontSize); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index 9c432884e6..a233010225 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -104,9 +105,29 @@ namespace O3DE::ProjectManager { // Add all available repos to the model const QVector allGemRepoInfos = allGemRepoInfosResult.GetValue(); + QDateTime oldestRepoUpdate; + if (!allGemRepoInfos.isEmpty()) + { + oldestRepoUpdate = allGemRepoInfos[0].m_lastUpdated; + } for (const GemRepoInfo& gemRepoInfo : allGemRepoInfos) { m_gemRepoModel->AddGemRepo(gemRepoInfo); + + // Find least recently updated repo + if (gemRepoInfo.m_lastUpdated < oldestRepoUpdate) + { + oldestRepoUpdate = gemRepoInfo.m_lastUpdated; + } + } + + if (!allGemRepoInfos.isEmpty()) + { + m_lastAllUpdateLabel->setText(tr("Last Updated: %1").arg(oldestRepoUpdate.toString(RepoTimeFormat))); + } + else + { + m_lastAllUpdateLabel->setText(tr("Last Updated: Never")); } } else diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h index 81320e3732..d784fcf5fd 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h @@ -27,4 +27,6 @@ namespace O3DE::ProjectManager static const QString ProjectCMakeCommand = "cmake"; static const QString ProjectCMakeBuildTargetEditor = "Editor"; + static const QString RepoTimeFormat = "dd/MM/yyyy hh:mmap"; + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 768e44ce15..d91f08c73e 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -668,7 +668,21 @@ namespace O3DE::ProjectManager if (gemInfo.m_creator.contains("Open 3D Engine")) { - gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEEngine; + gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEngine; + } + else if (gemInfo.m_creator.contains("Amazon Web Services")) + { + gemInfo.m_gemOrigin = GemInfo::GemOrigin::Local; + } + else if (data.contains("origin")) + { + gemInfo.m_gemOrigin = GemInfo::GemOrigin::Remote; + } + + // As long Base Open3DEngine gems are installed before first startup non-remote gems will be downloaded + if (gemInfo.m_gemOrigin != GemInfo::GemOrigin::Remote) + { + gemInfo.m_downloadStatus = GemInfo::DownloadStatus::Downloaded; } if (data.contains("user_tags")) @@ -932,13 +946,55 @@ namespace O3DE::ProjectManager return AZ::Failure("Adding Gem Repo not implemented yet in o3de scripts."); } - GemRepoInfo PythonBindings::GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath) + GemRepoInfo PythonBindings::GetGemRepoInfo(pybind11::handle repoUri) { - /* Placeholder Logic */ - (void)path; - (void)pyEnginePath; + GemRepoInfo gemRepoInfo; + gemRepoInfo.m_repoLink = Py_To_String(repoUri); - return GemRepoInfo(); + auto data = m_manifest.attr("get_repo_json_data")(repoUri); + if (pybind11::isinstance(data)) + { + try + { + // required + gemRepoInfo.m_repoLink = Py_To_String(data["repo_uri"]); + gemRepoInfo.m_name = Py_To_String(data["repo_name"]); + gemRepoInfo.m_creator = Py_To_String(data["origin"]); + + // optional + gemRepoInfo.m_summary = Py_To_String_Optional(data, "summary", "No summary provided."); + gemRepoInfo.m_additionalInfo = Py_To_String_Optional(data, "additional_info", ""); + + auto repoPath = m_manifest.attr("get_repo_path")(repoUri); + gemRepoInfo.m_path = gemRepoInfo.m_directoryLink = Py_To_String(repoPath); + + QString lastUpdated = Py_To_String_Optional(data, "last_updated", ""); + gemRepoInfo.m_lastUpdated = QDateTime::fromString(lastUpdated, RepoTimeFormat); + + if (data.contains("enabled")) + { + gemRepoInfo.m_isEnabled = data["enabled"].cast(); + } + else + { + gemRepoInfo.m_isEnabled = false; + } + + if (data.contains("gem_paths")) + { + for (auto gemPath : data["gem_paths"]) + { + gemRepoInfo.m_includedGemPaths.push_back(Py_To_String(gemPath)); + } + } + } + catch ([[maybe_unused]] const std::exception& e) + { + AZ_Warning("PythonBindings", false, "Failed to get GemRepoInfo for repo %s", Py_To_String(repoUri)); + } + } + + return gemRepoInfo; } //#define MOCK_GEM_REPO_INFO true @@ -951,14 +1007,10 @@ namespace O3DE::ProjectManager auto result = ExecuteWithLockErrorHandling( [&] { - /* Placeholder Logic, o3de scripts need method added - * - for (auto path : m_manifest.attr("get_gem_repos")()) + for (auto repoUri : m_manifest.attr("get_repos")()) { - gemRepos.push_back(GemRepoInfoFromPath(path, pybind11::none())); + gemRepos.push_back(GetGemRepoInfo(repoUri)); } - * - */ }); if (!result.IsSuccess()) { diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 5542fe146e..090094e9b6 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -67,7 +67,7 @@ namespace O3DE::ProjectManager AZ::Outcome ExecuteWithLockErrorHandling(AZStd::function executionCallback); bool ExecuteWithLock(AZStd::function executionCallback); GemInfo GemInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath); - GemRepoInfo GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath); + GemRepoInfo GetGemRepoInfo(pybind11::handle repoUri); ProjectInfo ProjectInfoFromPath(pybind11::handle path); ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath); bool RegisterThisEngine(); diff --git a/Code/Tools/ProjectManager/Source/TagWidget.cpp b/Code/Tools/ProjectManager/Source/TagWidget.cpp index 8f433f420f..ace9d72d8f 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/TagWidget.cpp @@ -8,87 +8,44 @@ #include #include +#include namespace O3DE::ProjectManager { TagWidget::TagWidget(const QString& text, QWidget* parent) : QLabel(text, parent) { - setFixedHeight(24); - setMargin(5); - setStyleSheet("font-size: 12px; background-color: #333333; border-radius: 3px;"); + setObjectName("TagWidget"); } TagContainerWidget::TagContainerWidget(QWidget* parent) : QWidget(parent) { - m_layout = new QVBoxLayout(); - m_layout->setAlignment(Qt::AlignTop); - m_layout->setMargin(0); - setLayout(m_layout); + setObjectName("TagWidgetContainer"); + setLayout(new FlowLayout(this)); + + // layout margins cannot be set via qss + constexpr int verticalMargin = 10; + constexpr int horizontalMargin = 0; + layout()->setContentsMargins(horizontalMargin, verticalMargin, horizontalMargin, verticalMargin); + + setAttribute(Qt::WA_StyledBackground, true); } void TagContainerWidget::Update(const QStringList& tags) { - QWidget* parentWidget = qobject_cast(parent()); - int width = 200; - if (parentWidget) + FlowLayout* flowLayout = static_cast(layout()); + + // remove old tags + QLayoutItem* layoutItem = nullptr; + while ((layoutItem = layout()->takeAt(0)) != nullptr) { - width = parentWidget->width(); + layoutItem->widget()->deleteLater(); } - if (m_widget) + foreach (const QString& tag, tags) { - // Hide the old widget and request deletion. - m_widget->hide(); - m_widget->deleteLater(); - } - - QVBoxLayout* vLayout = new QVBoxLayout(); - m_widget = new QWidget(this); - m_widget->setLayout(vLayout); - m_layout->addWidget(m_widget); - - vLayout->setAlignment(Qt::AlignTop); - vLayout->setMargin(0); - - QHBoxLayout* hLayout = nullptr; - int usedSpaceInRow = 0; - const int numTags = tags.count(); - - for (int i = 0; i < numTags; ++i) - { - // Create the new tag widget. - TagWidget* tagWidget = new TagWidget(tags[i]); - const int tagWidgetWidth = tagWidget->minimumSizeHint().width(); - - // Calculate the width we're currently using in the current row. Does the new tag still fit in the current row? - const bool isRowFull = width - usedSpaceInRow - tagWidgetWidth < 0; - if (isRowFull || i == 0) - { - // Add a spacer widget after the last tag widget in a row to push the tag widgets to the left. - if (i > 0) - { - QWidget* spacerWidget = new QWidget(); - spacerWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - hLayout->addWidget(spacerWidget); - } - - // Add a new row for the current tag widget. - hLayout = new QHBoxLayout(); - hLayout->setAlignment(Qt::AlignLeft); - hLayout->setMargin(0); - vLayout->addLayout(hLayout); - - // Reset the used space in the row. - usedSpaceInRow = 0; - } - - // Calculate the width of the tag widgets including the spacing between them of the current row. - usedSpaceInRow += tagWidgetWidth + hLayout->spacing(); - - // Add the tag widget to the current row. - hLayout->addWidget(tagWidget); + flowLayout->addWidget(new TagWidget(tag)); } } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TagWidget.h b/Code/Tools/ProjectManager/Source/TagWidget.h index 16eca28fc1..0dad7468eb 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.h +++ b/Code/Tools/ProjectManager/Source/TagWidget.h @@ -14,8 +14,6 @@ #include #endif -QT_FORWARD_DECLARE_CLASS(QVBoxLayout) - namespace O3DE::ProjectManager { // Single tag @@ -40,9 +38,5 @@ namespace O3DE::ProjectManager ~TagContainerWidget() = default; void Update(const QStringList& tags); - - private: - QVBoxLayout* m_layout = nullptr; - QWidget* m_widget = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 08ad7f24d9..e952ada57a 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -137,9 +137,13 @@ namespace O3DE::ProjectManager else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen) { // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. - if (!m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path)) + const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); + if (result == GemCatalogScreen::EnableDisableGemsResult::Failed) { QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for project.")); + } + if (result != GemCatalogScreen::EnableDisableGemsResult::Success) + { return; } diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 544fa2537b..fd8389ca4f 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -92,6 +92,8 @@ set(FILES Source/GemCatalog/GemListHeaderWidget.cpp Source/GemCatalog/GemModel.h Source/GemCatalog/GemModel.cpp + Source/GemCatalog/GemDependenciesDialog.h + Source/GemCatalog/GemDependenciesDialog.cpp Source/GemCatalog/GemRequirementDialog.h Source/GemCatalog/GemRequirementDialog.cpp Source/GemCatalog/GemRequirementDelegate.h diff --git a/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.cpp b/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.cpp index c88ea59bdb..33d8373487 100644 --- a/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.cpp +++ b/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.cpp @@ -395,7 +395,7 @@ namespace LUAEditor void LUAEditorMainWindow::OnLuaDocumentation() { - QDesktopServices::openUrl(QUrl("http://docs.aws.amazon.com/lumberyard/latest/developerguide/lua-scripting-intro.html")); + QDesktopServices::openUrl(QUrl("https://o3de.org/docs/user-guide/scripting/lua/")); } void LUAEditorMainWindow::OnMenuCloseCurrentWindow() diff --git a/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp b/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp index dbde19aad6..92c01dab19 100644 --- a/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp +++ b/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp @@ -33,7 +33,7 @@ namespace AWSClientAuth AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { - serialize->Class()->Version(1); + serialize->Class()->Version(2); if (AZ::EditContext* ec = serialize->GetEditContext()) { @@ -105,12 +105,22 @@ namespace AWSClientAuth behaviorContext->EBus("AWSCognitoUserManagementRequestBus") ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) ->Event("Initialize", &AWSCognitoUserManagementRequestBus::Events::Initialize) - ->Event("EmailSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::EmailSignUpAsync) - ->Event("PhoneSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::PhoneSignUpAsync) - ->Event("ConfirmSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmSignUpAsync) - ->Event("ForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ForgotPasswordAsync) - ->Event("ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync) - ->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync); + ->Event( + "EmailSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::EmailSignUpAsync, + { { { "Username", "The client's username" }, { "Password", "The client's password" }, { "Email", "The email address used to sign up" } } }) + ->Event( + "PhoneSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::PhoneSignUpAsync, + { { { "Username", "The client's username" }, { "Password", "The client's password" }, { "Phone number", "The phone number used to sign up" } } }) + ->Event( + "ConfirmSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmSignUpAsync, + { { { "Username", "The client's username" }, { "Confirmation code", "The client's confirmation code" } } }) + ->Event( + "ForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ForgotPasswordAsync, + { { { "Username", "The client's username" } } }) + ->Event( + "ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync, + { { { "Username", "The client's username" }, { "Confirmation code", "The client's confirmation code" }, { "New password", "The new password for the client" } } }) + ->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync, { { { "Access token", "The MFA access token" } } }); behaviorContext->EBus("AuthenticationProviderNotificationBus") diff --git a/Gems/AWSCore/Code/Include/Private/AWSCoreModule.h b/Gems/AWSCore/Code/Include/Private/AWSCoreModule.h index ae5b424e69..f03579a0dd 100644 --- a/Gems/AWSCore/Code/Include/Private/AWSCoreModule.h +++ b/Gems/AWSCore/Code/Include/Private/AWSCoreModule.h @@ -24,7 +24,7 @@ namespace AWSCore /** * Add required SystemComponents to the SystemEntity. */ - virtual AZ::ComponentTypeList GetRequiredSystemComponents() const override; + AZ::ComponentTypeList GetRequiredSystemComponents() const override; }; } diff --git a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h index 9834f9ca38..dd9f22f04d 100644 --- a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h +++ b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h @@ -42,7 +42,7 @@ namespace AWSCore AWSCoreConfiguration(); - ~AWSCoreConfiguration() = default; + ~AWSCoreConfiguration() override = default; void ActivateConfig(); void DeactivateConfig(); diff --git a/Gems/AWSCore/Code/Include/Private/Credential/AWSCVarCredentialHandler.h b/Gems/AWSCore/Code/Include/Private/Credential/AWSCVarCredentialHandler.h index 542177dcc4..388f8eefd5 100644 --- a/Gems/AWSCore/Code/Include/Private/Credential/AWSCVarCredentialHandler.h +++ b/Gems/AWSCore/Code/Include/Private/Credential/AWSCVarCredentialHandler.h @@ -21,7 +21,7 @@ namespace AWSCore { public: AWSCVarCredentialHandler() = default; - ~AWSCVarCredentialHandler() = default; + ~AWSCVarCredentialHandler() override = default; //! Activate handler and its credentials provider, make sure activation //! invoked after AWSNativeSDK init to avoid memory leak diff --git a/Gems/AWSCore/Code/Include/Private/Credential/AWSDefaultCredentialHandler.h b/Gems/AWSCore/Code/Include/Private/Credential/AWSDefaultCredentialHandler.h index 7f021f5748..068addfc24 100644 --- a/Gems/AWSCore/Code/Include/Private/Credential/AWSDefaultCredentialHandler.h +++ b/Gems/AWSCore/Code/Include/Private/Credential/AWSDefaultCredentialHandler.h @@ -23,7 +23,7 @@ namespace AWSCore { public: AWSDefaultCredentialHandler(); - ~AWSDefaultCredentialHandler() = default; + ~AWSDefaultCredentialHandler() override = default; //! Activate handler and its credentials provider, make sure activation //! invoked after AWSNativeSDK init to avoid memory leak diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConsentDialog.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConsentDialog.h index c2194ccfb4..8a3445c742 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConsentDialog.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConsentDialog.h @@ -15,13 +15,12 @@ namespace AWSCore { //! Defines AWSCoreAttributionConsent QT dialog as QT message box. - class AWSCoreAttributionConsentDialog : - public QMessageBox + class AWSCoreAttributionConsentDialog + : public QMessageBox { public: AZ_CLASS_ALLOCATOR(AWSCoreAttributionConsentDialog, AZ::SystemAllocator, 0); AWSCoreAttributionConsentDialog(); - virtual ~AWSCoreAttributionConsentDialog() = default; - + ~AWSCoreAttributionConsentDialog() override = default; }; } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h index e8a034e8e9..49c533cef5 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/Attribution/AWSCoreAttributionConstant.h @@ -18,4 +18,4 @@ namespace AWSCore static constexpr char AwsAttributionAttributeKeyActiveAWSGems[] = "aws_gems"; static constexpr char AwsAttributionAttributeKeyTimestamp[] = "timestamp"; -} // namespace AWSCOre +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h index 39e96517a7..62f91d36ef 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h @@ -34,7 +34,7 @@ namespace AWSCore "Failed to launch Resource Mapping Tool, please check logs for details."; AWSCoreEditorMenu(const QString& text); - ~AWSCoreEditorMenu(); + ~AWSCoreEditorMenu() override; private: QAction* AddExternalLinkAction(const AZStd::string& name, const AZStd::string& url, const AZStd::string& icon = ""); diff --git a/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h b/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h index fbc37622bf..27ff275630 100644 --- a/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h +++ b/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h @@ -71,7 +71,7 @@ namespace AWSCore }; AWSResourceMappingManager(); - ~AWSResourceMappingManager() = default; + ~AWSResourceMappingManager() override = default; void ActivateManager(); void DeactivateManager(); diff --git a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiClientJobConfig.h b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiClientJobConfig.h index e991b2af7e..d14509550e 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiClientJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiClientJobConfig.h @@ -64,9 +64,6 @@ namespace AWSCore /// Initialize an AwsApiClientJobConfig object. /// - /// \param DefaultConfigType - the type of the config object from which - /// default values will be taken. - /// /// \param defaultConfig - the config object that provides values when /// no override has been set in this object. The default is nullptr, which /// will cause a default value to be used. @@ -83,10 +80,10 @@ namespace AWSCore } } - virtual ~AwsApiClientJobConfig() = default; + ~AwsApiClientJobConfig() override = default; /// Gets a client initialized used currently applied settings. If - /// any settings change after first use, code must call + /// any settings change after first use, code must call /// ApplySettings before those changes will take effect. std::shared_ptr GetClient() override { @@ -112,7 +109,7 @@ namespace AWSCore } else { - // If no explict credenitals are provided then AWS C++ SDK will perform standard search + // If no explicit credentials are provided then AWS C++ SDK will perform standard search return std::make_shared(Aws::Auth::AWSCredentials(), GetClientConfiguration()); } } diff --git a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJob.h b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJob.h index 00949011cb..93fc75feed 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJob.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJob.h @@ -14,14 +14,12 @@ namespace AWSCore { - - /// Base class for all AWS jobs. Primarily exists so that + /// Base class for all AWS jobs. Primarily exists so that /// AwsApiJob::s_config can be used for settings that apply to /// all AWS jobs. class AwsApiJob : public AZ::Job { - public: // To use a different allocator, extend this class and use this macro. AZ_CLASS_ALLOCATOR(AwsApiJob, AZ::SystemAllocator, 0); @@ -33,11 +31,10 @@ namespace AWSCore protected: AwsApiJob(bool isAutoDelete, IConfig* config = GetDefaultConfig()); - virtual ~AwsApiJob(); + ~AwsApiJob() override = default; /// Used for error messages. static const char* COMPONENT_DISPLAY_NAME; - }; } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJobConfig.h b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJobConfig.h index 0f25c648d0..516b42c0dd 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJobConfig.h @@ -96,9 +96,6 @@ namespace AWSCore /// Initialize an AwsApiClientJobConfig object. /// - /// \param DefaultConfigType - the type of the config object from which - /// default values will be taken. - /// /// \param defaultConfig - the config object that provides values when /// no override has been set in this object. The default is nullptr, which /// will cause a default value to be used. @@ -146,7 +143,7 @@ namespace AWSCore #endif Override caFile; - /// Applys settings changes made after first use. + /// Applies settings changes made after first use. virtual void ApplySettings(); ////////////////////////////////////////////////////////////////////////// @@ -217,7 +214,7 @@ namespace AWSCore : protected AWSCoreNotificationsBus::Handler { public: - ~AwsApiJobConfigHolder() + ~AwsApiJobConfigHolder() override { AWSCoreNotificationsBus::Handler::BusDisconnect(); } diff --git a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiRequestJob.h b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiRequestJob.h index c189e94f22..80ac6657ed 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiRequestJob.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiRequestJob.h @@ -145,10 +145,10 @@ namespace AWSCore AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER typename AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunctionType AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunction = _AsyncFunction; - /// Macro that simplifies the declaration of an AwsRequstJob that has a result. + /// Macro that simplifies the declaration of an AwsRequestJob that has a result. #define AWS_API_REQUEST_JOB(SERVICE, REQUEST) AWSCore::AwsApiRequestJob -/// Macro that simplifies the declaration of an AwsRequstJob that has no result. +/// Macro that simplifies the declaration of an AwsRequestJob that has no result. #define AWS_API_REQUEST_JOB_NO_RESULT(SERVICE, REQUEST) AWSCore::AwsApiRequestJob /// An Az::Job that that executes a specific AWS request. @@ -257,7 +257,7 @@ namespace AWSCore /// of request data until your running on the job's worker thread, /// instead of setting the request data before calling Start. /// - /// \param true if the request should be made. + /// \return true if the request should be made. virtual bool PrepareRequest() { return true; diff --git a/Gems/AWSCore/Code/Include/Public/Framework/HttpClientComponent.h b/Gems/AWSCore/Code/Include/Public/Framework/HttpClientComponent.h index f936e7f107..48b59cc56a 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/HttpClientComponent.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/HttpClientComponent.h @@ -39,7 +39,7 @@ namespace AWSCore : public AZ::ComponentBus { public: - virtual ~HttpClientComponentNotifications() {} + ~HttpClientComponentNotifications() override = default; virtual void OnHttpRequestSuccess(int responseCode, AZStd::string responseBody) {} virtual void OnHttpRequestFailure(int responseCode) {} }; @@ -55,7 +55,7 @@ namespace AWSCore { public: AZ_COMPONENT(HttpClientComponent, "{23ECDBDF-129A-4670-B9B4-1E0B541ACD61}"); - virtual ~HttpClientComponent() = default; + ~HttpClientComponent() override = default; void Init() override; void Activate() override; diff --git a/Gems/AWSCore/Code/Include/Public/Framework/HttpRequestJob.h b/Gems/AWSCore/Code/Include/Public/Framework/HttpRequestJob.h index 60bb0ffd34..b277340be9 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/HttpRequestJob.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/HttpRequestJob.h @@ -178,7 +178,7 @@ namespace AWSCore }; /// Override to process the response to the HTTP request before callbacks are fired. - /// WARNING: This gets called on the job's thread, so observe thread safety precations. + /// WARNING: This gets called on the job's thread, so observe thread safety precautions. virtual void ProcessResponse(const std::shared_ptr& response) { AZ_UNUSED(response); diff --git a/Gems/AWSCore/Code/Include/Public/Framework/JsonObjectHandler.h b/Gems/AWSCore/Code/Include/Public/Framework/JsonObjectHandler.h index 57eabe3593..e86db54bbf 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/JsonObjectHandler.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/JsonObjectHandler.h @@ -29,24 +29,24 @@ namespace AWSCore Ch Peek() const { int c = m_is.peek(); - return c == std::char_traits::eof() ? '\0' : (Ch)c; + return c == std::char_traits::eof() ? '\0' : static_cast(c); } Ch Take() { int c = m_is.get(); - return c == std::char_traits::eof() ? '\0' : (Ch)c; + return c == std::char_traits::eof() ? '\0' : static_cast(c); } size_t Tell() const { - return (size_t)m_is.tellg(); + return static_cast(m_is.tellg()); } Ch* PutBegin() { AZ_Assert(false, "Not Implemented"); - return 0; + return nullptr; } void Put(Ch) diff --git a/Gems/AWSCore/Code/Include/Public/Framework/JsonWriter.h b/Gems/AWSCore/Code/Include/Public/Framework/JsonWriter.h index 8b3f4d55b2..261907b7ec 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/JsonWriter.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/JsonWriter.h @@ -161,7 +161,7 @@ namespace AWSCore } /// Write JSON format content directly to the writer's output stream. - /// This can be used to efficently output static content. + /// This can be used to efficiently output static content. bool WriteJson(const Ch* json) { if (json) @@ -182,7 +182,7 @@ namespace AWSCore } /// Write an object. The object can implement a WriteJson function - /// or you can provide an GobalWriteJson template function + /// or you can provide an GlobalWriteJson template function /// specialization. template bool Object(const ObjectType& obj) diff --git a/Gems/AWSCore/Code/Include/Public/Framework/RequestBuilder.h b/Gems/AWSCore/Code/Include/Public/Framework/RequestBuilder.h index 1e4b7c34c2..c12e8b1d5c 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/RequestBuilder.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/RequestBuilder.h @@ -36,7 +36,7 @@ namespace AWSCore class RequestBuilder { public: - RequestBuilder() = default; + RequestBuilder(); /// Converts the provided object to JSON and sends it as the /// body of the request. The object can implement the following diff --git a/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h b/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h index 9082498e96..89141bb492 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/ServiceClientJobConfig.h @@ -20,7 +20,7 @@ namespace AWSCore { public: - virtual const AZStd::string GetServiceUrl() = 0; + virtual AZStd::string GetServiceUrl() = 0; }; /// Encapsulates what code needs to know about a service in order to @@ -81,9 +81,6 @@ namespace AWSCore /// Initialize an ServiceClientJobConfig object. /// - /// \param DefaultConfigType - the type of the config object from which - /// default values will be taken. - /// /// \param defaultConfig - the config object that provides values when /// no override has been set in this object. The default is nullptr, which /// will cause a default value to be used. @@ -102,7 +99,7 @@ namespace AWSCore /// This implementation assumes the caller will cache this value as /// needed. See it's use in ServiceRequestJobConfig. - const AZStd::string GetServiceUrl() override + AZStd::string GetServiceUrl() override { if (endpointOverride.has_value()) { diff --git a/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJob.h b/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJob.h index cd0686c2d0..c24387a7ba 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJob.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJob.h @@ -119,7 +119,7 @@ namespace AWSCore Error error; /// Determines if the AWS credentials, as supplied by the credentialsProvider from - /// the ServiceReqestJobConfig object (which defaults to the user's credentials), + /// the ServiceRequestJobConfig object (which defaults to the user's credentials), /// are used to sign the request. The default is true. Override this and return false /// if calling a public API and want to avoid the overhead of signing requests. bool UseAWSCredentials() { @@ -565,13 +565,11 @@ namespace AWSCore } AZStd::string requestContent; - AZStd::string responseContent; - - std::istreambuf_iterator eos; std::shared_ptr requestStream = response->GetOriginatingRequest().GetContentBody(); if (requestStream) { + std::istreambuf_iterator eos; requestStream->clear(); requestStream->seekg(0); requestContent = AZStd::string{ std::istreambuf_iterator(*requestStream.get()),eos }; @@ -584,7 +582,7 @@ namespace AWSCore Aws::IOStream& responseStream = response->GetResponseBody(); responseStream.clear(); responseStream.seekg(0); - responseContent = AZStd::string{ std::istreambuf_iterator(responseStream),responseEos }; + AZStd::string responseContent = AZStd::string{ std::istreambuf_iterator(responseStream), responseEos }; responseContent = EscapePercentCharsInString(responseContent); responseStream.seekg(0); diff --git a/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h b/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h index 10b14ed5af..6c4e884f49 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h @@ -44,9 +44,6 @@ namespace AWSCore /// Initialize an ServiceRequestJobConfig object. /// - /// \param DefaultConfigType - the type of the config object from which - /// default values will be taken. - /// /// \param defaultConfig - the config object that provides values when /// no override has been set in this object. The default is nullptr, which /// will cause a default value to be used. diff --git a/Gems/AWSCore/Code/Source/AWSCoreEditorSystemComponent.cpp b/Gems/AWSCore/Code/Source/AWSCoreEditorSystemComponent.cpp index f4420cb40a..a522f71084 100644 --- a/Gems/AWSCore/Code/Source/AWSCoreEditorSystemComponent.cpp +++ b/Gems/AWSCore/Code/Source/AWSCoreEditorSystemComponent.cpp @@ -79,7 +79,7 @@ namespace AWSCore QMenuBar* menuBar = mainWindow->menuBar(); QList actionList = menuBar->actions(); QAction* insertPivot = nullptr; - for (QList::iterator itr = actionList.begin(); itr != actionList.end(); itr++) + for (QList::iterator itr = actionList.begin(); itr != actionList.end(); ++itr) { if (QString::compare((*itr)->text(), EDITOR_HELP_MENU_TEXT) == 0) { @@ -88,7 +88,7 @@ namespace AWSCore } } - auto menu = m_awsCoreEditorManager->GetAWSCoreEditorMenu(); + const auto menu = m_awsCoreEditorManager->GetAWSCoreEditorMenu(); if (insertPivot) { menuBar->insertMenu(insertPivot, menu); diff --git a/Gems/AWSCore/Code/Source/Credential/AWSCVarCredentialHandler.cpp b/Gems/AWSCore/Code/Source/Credential/AWSCVarCredentialHandler.cpp index 83487f665d..8102efa2f8 100644 --- a/Gems/AWSCore/Code/Source/Credential/AWSCVarCredentialHandler.cpp +++ b/Gems/AWSCore/Code/Source/Credential/AWSCVarCredentialHandler.cpp @@ -5,15 +5,14 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - #include #include namespace AWSCore { - AZ_CVAR(AZ::CVarFixedString, cl_awsAccessKey, "", nullptr, AZ::ConsoleFunctorFlags::Null, "Override AWS access key"); - AZ_CVAR(AZ::CVarFixedString, cl_awsSecretKey, "", nullptr, AZ::ConsoleFunctorFlags::Null, "Override AWS secret key"); + AZ_CVAR(AZ::CVarFixedString, cl_awsAccessKey, "", nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Override AWS access key"); + AZ_CVAR(AZ::CVarFixedString, cl_awsSecretKey, "", nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Override AWS secret key"); static constexpr char AWSCVARCREDENTIALHANDLER_ALLOC_TAG[] = "AWSCVarCredentialHandler"; @@ -36,12 +35,12 @@ namespace AWSCore std::shared_ptr AWSCVarCredentialHandler::GetCredentialsProvider() { - auto accessKey = static_cast(cl_awsAccessKey); - auto secretKey = static_cast(cl_awsSecretKey); + const auto accessKey = static_cast(cl_awsAccessKey); + const auto secretKey = static_cast(cl_awsSecretKey); if (!accessKey.empty() && !secretKey.empty()) { - AZStd::lock_guard credentialsLock{m_credentialMutex}; + AZStd::lock_guard credentialsLock{ m_credentialMutex }; m_cvarCredentialsProvider = Aws::MakeShared( AWSCVARCREDENTIALHANDLER_ALLOC_TAG, accessKey.c_str(), secretKey.c_str()); return m_cvarCredentialsProvider; @@ -52,7 +51,7 @@ namespace AWSCore void AWSCVarCredentialHandler::ResetCredentialsProvider() { // Must reset credential provider after AWSNativeSDKs init or before AWSNativeSDKs shutdown - AZStd::lock_guard credentialsLock{m_credentialMutex}; + AZStd::lock_guard credentialsLock{ m_credentialMutex }; m_cvarCredentialsProvider.reset(); } } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionConsentDialog.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionConsentDialog.cpp index 85912616c2..7ec9f9ea4f 100644 --- a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionConsentDialog.cpp +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionConsentDialog.cpp @@ -35,8 +35,7 @@ namespace AWSCore this->setDefaultButton(QMessageBox::Save); this->button(QMessageBox::Cancel)->hide(); this->setIcon(QMessageBox::Information); - QGridLayout* layout = (QGridLayout*)this->layout(); - if (layout) + if (QGridLayout* layout = static_cast(this->layout())) { layout->setVerticalSpacing(20); layout->setHorizontalSpacing(10); diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp index 712c083e7a..3b9352d250 100644 --- a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp @@ -68,19 +68,19 @@ namespace AWSCore AZ_Assert(fileIO, "File IO is not initialized."); // Resolve path to editor_aws_preferences.setreg - AZStd::string editorAWSPreferencesFilePath = + const AZStd::string editorAWSPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); - AZStd::array resolvedPathAWSPreference{}; - if (!fileIO->ResolvePath(editorAWSPreferencesFilePath.c_str(), resolvedPathAWSPreference.data(), resolvedPathAWSPreference.size())) + AZ::IO::FixedMaxPath resolvedPathAWSPreference; + if (!fileIO->ResolvePath(resolvedPathAWSPreference, AZ::IO::PathView(editorAWSPreferencesFilePath))) { - AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.data()); + AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.c_str()); return; } - if (fileIO->Exists(resolvedPathAWSPreference.data())) + if (fileIO->Exists(resolvedPathAWSPreference.c_str())) { m_settingsRegistry->MergeSettingsFile( - resolvedPathAWSPreference.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); + resolvedPathAWSPreference.String(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""); } } @@ -136,8 +136,8 @@ namespace AWSCore return true; } - AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds); - AZStd::chrono::seconds secondsSinceLastSend = + const AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds); + const AZStd::chrono::seconds secondsSinceLastSend = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now().time_since_epoch()) - lastSendTimeStamp; if (static_cast(secondsSinceLastSend.count()) >= delayInSeconds) { @@ -154,7 +154,7 @@ namespace AWSCore if (credentialResult.result) { std::shared_ptr provider = credentialResult.result; - auto creds = provider->GetAWSCredentials(); + const auto creds = provider->GetAWSCredentials(); if (!creds.IsEmpty()) { return true; @@ -200,9 +200,13 @@ namespace AWSCore AZ_Assert(fileIO, "File IO is not initialized."); // Resolve path to editor_aws_preferences.setreg - AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); - AZStd::array resolvedPath {}; - fileIO->ResolvePath(editorPreferencesFilePath.c_str(), resolvedPath.data(), resolvedPath.size()); + const AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName); + AZ::IO::FixedMaxPath resolvedPathAWSPreference; + if (!fileIO->ResolvePath(resolvedPathAWSPreference, AZ::IO::PathView(editorPreferencesFilePath))) + { + AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", editorPreferencesFilePath.c_str()); + return; + } AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings; dumperSettings.m_prettifyOutput = true; @@ -215,14 +219,14 @@ namespace AWSCore { AZ_Warning( "AWSAttributionManager", false, R"(Unable to save changes to the Editor AWS Preferences registry file at "%s"\n)", - resolvedPath.data()); + resolvedPathAWSPreference.c_str()); return; } bool saved {}; constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; - if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode)) + if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPathAWSPreference.c_str(), configurationMode)) { saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size(); } diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp index d55510930e..6391dd94ee 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp @@ -54,7 +54,7 @@ namespace AWSCore { if (m_resourceMappingToolWatcher->IsProcessRunning()) { - m_resourceMappingToolWatcher->TerminateProcess(AZ::u32(-1)); + m_resourceMappingToolWatcher->TerminateProcess(static_cast(-1)); } m_resourceMappingToolWatcher.reset(); } @@ -214,7 +214,7 @@ namespace AWSCore QMenu* AWSCoreEditorMenu::SetAWSFeatureSubMenu(const AZStd::string& menuText) { auto actionList = this->actions(); - for (QList::iterator itr = actionList.begin(); itr != actionList.end(); itr++) + for (QList::iterator itr = actionList.begin(); itr != actionList.end(); ++itr) { if (QString::compare((*itr)->text(), menuText.c_str()) == 0) { diff --git a/Gems/AWSCore/Code/Source/Framework/AWSApiJob.cpp b/Gems/AWSCore/Code/Source/Framework/AWSApiJob.cpp index f203506edd..34581e00ff 100644 --- a/Gems/AWSCore/Code/Source/Framework/AWSApiJob.cpp +++ b/Gems/AWSCore/Code/Source/Framework/AWSApiJob.cpp @@ -22,10 +22,6 @@ namespace AWSCore { } - AwsApiJob::~AwsApiJob() - { - } - AwsApiJob::Config* AwsApiJob::GetDefaultConfig() { static AwsApiJobConfigHolder s_configHolder{}; diff --git a/Gems/AWSCore/Code/Source/Framework/MultipartFormData.cpp b/Gems/AWSCore/Code/Source/Framework/MultipartFormData.cpp index ff4668a62b..47059ac156 100644 --- a/Gems/AWSCore/Code/Source/Framework/MultipartFormData.cpp +++ b/Gems/AWSCore/Code/Source/Framework/MultipartFormData.cpp @@ -49,7 +49,7 @@ namespace AWSCore { m_fileFields.emplace_back(FileField{ std::move(fieldName), std::move(fileName) , AZStd::vector{} }); m_fileFields.back().m_fileData.reserve(length); - m_fileFields.back().m_fileData.assign((const char*)bytes, (const char*)bytes + length); + m_fileFields.back().m_fileData.assign(static_cast(bytes), static_cast(bytes) + length); } void MultipartFormData::SetCustomBoundary(AZStd::string boundary) diff --git a/Gems/AWSCore/Code/Source/Framework/RequestBuilder.cpp b/Gems/AWSCore/Code/Source/Framework/RequestBuilder.cpp index 16a5163fc7..d32ec9d039 100644 --- a/Gems/AWSCore/Code/Source/Framework/RequestBuilder.cpp +++ b/Gems/AWSCore/Code/Source/Framework/RequestBuilder.cpp @@ -10,6 +10,10 @@ namespace AWSCore { + RequestBuilder::RequestBuilder() + : m_httpMethod(Aws::Http::HttpMethod::HTTP_GET) + { + } bool RequestBuilder::SetPathParameterUnescaped(const char* key, const char* value) { diff --git a/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp b/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp index c3d87bff86..faec07b19e 100644 --- a/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp +++ b/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp @@ -26,7 +26,6 @@ namespace AWSCore : m_status(Status::NotLoaded) , m_defaultAccountId("") , m_defaultRegion("") - , m_resourceMappings() { } @@ -164,7 +163,7 @@ namespace AWSCore m_defaultRegion = jsonDocument.FindMember(ResourceMappingRegionKeyName)->value.GetString(); auto resourceMappings = jsonDocument.FindMember(ResourceMappingResourcesKeyName)->value.GetObject(); - for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); mappingIter++) + for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); ++mappingIter) { auto mappingValue = mappingIter->value.GetObject(); if (mappingValue.MemberCount() != 0) diff --git a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp index 4467cc5db7..4183997d4a 100644 --- a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp +++ b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp @@ -71,7 +71,7 @@ namespace AWSCore [](DynamoDBGetItemRequestJob* job) // OnSuccess handler { auto item = job->result.GetItem(); - if (item.size() > 0) + if (!item.empty()) { DynamoDBAttributeValueMap result; for (const auto& itermPair : item) diff --git a/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp b/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp index f9fff631f4..648648e945 100644 --- a/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp +++ b/Gems/AWSCore/Code/Tests/AWSCoreSystemComponentTest.cpp @@ -40,7 +40,7 @@ public: AWSCoreNotificationsBus::Handler::BusConnect(); } - ~AWSCoreNotificationsBusMock() + ~AWSCoreNotificationsBusMock() override { AWSCoreNotificationsBus::Handler::BusDisconnect(); } diff --git a/Gems/AWSCore/Code/Tests/Credential/AWSCVarCredentialHandlerTest.cpp b/Gems/AWSCore/Code/Tests/Credential/AWSCVarCredentialHandlerTest.cpp index 73120d4adc..244c945af9 100644 --- a/Gems/AWSCore/Code/Tests/Credential/AWSCVarCredentialHandlerTest.cpp +++ b/Gems/AWSCore/Code/Tests/Credential/AWSCVarCredentialHandlerTest.cpp @@ -18,7 +18,7 @@ class AWSCVarCredentialHandlerTest { public: AWSCVarCredentialHandlerTest() = default; - virtual ~AWSCVarCredentialHandlerTest() = default; + ~AWSCVarCredentialHandlerTest() override = default; void SetUp() override { diff --git a/Gems/AWSCore/Code/Tests/Credential/AWSCredentialBusTest.cpp b/Gems/AWSCore/Code/Tests/Credential/AWSCredentialBusTest.cpp index 238b5c819c..82c8c84b81 100644 --- a/Gems/AWSCore/Code/Tests/Credential/AWSCredentialBusTest.cpp +++ b/Gems/AWSCore/Code/Tests/Credential/AWSCredentialBusTest.cpp @@ -36,14 +36,14 @@ public: m_credentialsProvider.reset(); } - int GetCredentialHandlerOrder() const + int GetCredentialHandlerOrder() const override { return 1; } - std::shared_ptr GetCredentialsProvider() + std::shared_ptr GetCredentialsProvider() override { - m_handlerCounter++; + ++m_handlerCounter; return m_credentialsProvider; } @@ -72,14 +72,14 @@ public: m_credentialsProvider.reset(); } - int GetCredentialHandlerOrder() const + int GetCredentialHandlerOrder() const override { return 2; } - std::shared_ptr GetCredentialsProvider() + std::shared_ptr GetCredentialsProvider() override { - m_handlerCounter++; + ++m_handlerCounter; return m_credentialsProvider; } @@ -115,10 +115,10 @@ public: TEST_F(AWSCredentialBusTest, GetCredentialsProvider_CallFromMultithread_GetExpectedCredentialsProviderAndNumberOfCalls) { - int testThreadNumber = 10; + constexpr int testThreadNumber = 10; AZStd::atomic actualEbusCalls = 0; AZStd::vector testThreadPool; - for (int index = 0; index < testThreadNumber; index++) + for (int index = 0; index < testThreadNumber; ++index) { testThreadPool.emplace_back(AZStd::thread([&]() { AWSCredentialResult result; diff --git a/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp b/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp index b3e2ec5738..af03afb337 100644 --- a/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp +++ b/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp @@ -49,7 +49,7 @@ class AWSDefaultCredentialHandlerTest { public: AWSDefaultCredentialHandlerTest() = default; - virtual ~AWSDefaultCredentialHandlerTest() = default; + ~AWSDefaultCredentialHandlerTest() override = default; void SetUp() override { diff --git a/Gems/AWSCore/Code/Tests/Framework/AWSApiClientJobConfigTest.cpp b/Gems/AWSCore/Code/Tests/Framework/AWSApiClientJobConfigTest.cpp index b49cdc6a71..db433062ad 100644 --- a/Gems/AWSCore/Code/Tests/Framework/AWSApiClientJobConfigTest.cpp +++ b/Gems/AWSCore/Code/Tests/Framework/AWSApiClientJobConfigTest.cpp @@ -23,6 +23,11 @@ class AWSApiClientJobConfigTest , public AWSCredentialRequestBus::Handler { public: + AWSApiClientJobConfigTest() + : m_credentialHandlerCounter(0) + { + } + void SetUp() override { AWSNativeSDKInit::InitializationManager::InitAwsApi(); diff --git a/Gems/AWSCore/Code/Tests/Framework/ServiceClientJobConfigTest.cpp b/Gems/AWSCore/Code/Tests/Framework/ServiceClientJobConfigTest.cpp index 7a45ffb739..9bc43482cd 100644 --- a/Gems/AWSCore/Code/Tests/Framework/ServiceClientJobConfigTest.cpp +++ b/Gems/AWSCore/Code/Tests/Framework/ServiceClientJobConfigTest.cpp @@ -84,7 +84,7 @@ class ServiceClientJobConfigTest void ReloadConfigFile(bool reloadConfigFileName = false) override { AZ_UNUSED(reloadConfigFileName); - }; + } }; TEST_F(ServiceClientJobConfigTest, GetServiceUrl_CreateServiceWithServiceNameOnly_GetExpectedFeatureServiceUrl) diff --git a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp index d438a99f83..fb03dea4c0 100644 --- a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp +++ b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp @@ -217,7 +217,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi CreateTestConfigFile(TEST_VALID_RESOURCE_MAPPING_CONFIG_FILE); m_resourceMappingManager->ActivateManager(); - int testThreadNumber = 10; + constexpr int testThreadNumber = 10; AZStd::atomic actualEbusCalls = 0; AZStd::vector testThreadPool; for (int index = 0; index < testThreadNumber; index++) @@ -226,7 +226,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi AZStd::string actualAccountId; AWSResourceMappingRequestBus::BroadcastResult(actualAccountId, &AWSResourceMappingRequests::GetDefaultAccountId); EXPECT_FALSE(actualAccountId.empty()); - actualEbusCalls++; + ++actualEbusCalls; })); } diff --git a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingUtilsTest.cpp b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingUtilsTest.cpp index 9c23615917..f1a90aa2ae 100644 --- a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingUtilsTest.cpp +++ b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingUtilsTest.cpp @@ -44,19 +44,19 @@ TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiId_Ret { auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl("", TEST_VALID_RESTAPI_REGION, TEST_VALID_RESTAPI_STAGE); - EXPECT_TRUE(actualUrl == ""); + EXPECT_TRUE(actualUrl.empty()); } TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiRegion_ReturnEmptyResult) { auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl(TEST_VALID_RESTAPI_ID, "", TEST_VALID_RESTAPI_STAGE); - EXPECT_TRUE(actualUrl == ""); + EXPECT_TRUE(actualUrl.empty()); } TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiStage_ReturnEmptyResult) { auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl(TEST_VALID_RESTAPI_ID, TEST_VALID_RESTAPI_REGION, ""); - EXPECT_TRUE(actualUrl == ""); + EXPECT_TRUE(actualUrl.empty()); } diff --git a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorDynamoDBTest.cpp b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorDynamoDBTest.cpp index 52b91c306d..e42e270bc2 100644 --- a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorDynamoDBTest.cpp +++ b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorDynamoDBTest.cpp @@ -23,7 +23,7 @@ public: AWSScriptBehaviorDynamoDBNotificationBus::Handler::BusConnect(); } - ~AWSScriptBehaviorDynamoDBNotificationBusHandlerMock() + ~AWSScriptBehaviorDynamoDBNotificationBusHandlerMock() override { AWSScriptBehaviorDynamoDBNotificationBus::Handler::BusDisconnect(); } diff --git a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorLambdaTest.cpp b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorLambdaTest.cpp index 299f4a95d6..42ccd6eddc 100644 --- a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorLambdaTest.cpp +++ b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorLambdaTest.cpp @@ -22,7 +22,7 @@ public: AWSScriptBehaviorLambdaNotificationBus::Handler::BusConnect(); } - ~AWSScriptBehaviorLambdaNotificationBusHandlerMock() + ~AWSScriptBehaviorLambdaNotificationBusHandlerMock() override { AWSScriptBehaviorLambdaNotificationBus::Handler::BusDisconnect(); } diff --git a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorS3Test.cpp b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorS3Test.cpp index 118174576c..34a0166e32 100644 --- a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorS3Test.cpp +++ b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorS3Test.cpp @@ -24,7 +24,7 @@ public: AWSScriptBehaviorS3NotificationBus::Handler::BusConnect(); } - ~AWSScriptBehaviorS3NotificationBusHandlerMock() + ~AWSScriptBehaviorS3NotificationBusHandlerMock() override { AWSScriptBehaviorS3NotificationBus::Handler::BusDisconnect(); } diff --git a/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h b/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h index a5c2bf6475..1d14484387 100644 --- a/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h +++ b/Gems/AWSCore/Code/Tests/TestFramework/AWSCoreFixture.h @@ -107,8 +107,8 @@ class AWSCoreFixture : public UnitTest::ScopedAllocatorSetupFixture { public: - AWSCoreFixture() {} - virtual ~AWSCoreFixture() = default; + AWSCoreFixture() = default; + ~AWSCoreFixture() override = default; void SetUp() override { diff --git a/Gems/AWSCore/cdk/README.md b/Gems/AWSCore/cdk/README.md index e4c0335c9c..d50ca40279 100644 --- a/Gems/AWSCore/cdk/README.md +++ b/Gems/AWSCore/cdk/README.md @@ -64,6 +64,16 @@ To add additional dependencies, for example other CDK libraries, just add them to your `setup.py` file and rerun the `pip install -r requirements.txt` command. +## Optional Features +Server access logging is enabled by default. To disable the feature, use the following commands to synthesize and deploy this CDK application. + +``` +$ cdk synth -c disable_access_log=true --all +$ cdk deploy -c disable_access_log=true --all +``` + +See https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html for more information about server access logging. + ## Useful commands * `cdk ls` list all stacks in the app diff --git a/Gems/AWSCore/cdk/app.py b/Gems/AWSCore/cdk/app.py index 16773b5f69..9a401033ef 100755 --- a/Gems/AWSCore/cdk/app.py +++ b/Gems/AWSCore/cdk/app.py @@ -57,8 +57,9 @@ example_stack = ExampleResources( tags={Constants.O3DE_PROJECT_TAG_NAME: PROJECT_NAME, Constants.O3DE_FEATURE_TAG_NAME: FEATURE_NAME}, env=env ) -# -# Add the common stack as a dependency of the feature stack + +# Add the core stack as a dependency of the feature stack since the feature stack +# requires the core stack outputs for deployment. example_stack.add_dependency(core_construct.common_stack) app.synth() diff --git a/Gems/AWSCore/cdk/core/core_stack.py b/Gems/AWSCore/cdk/core/core_stack.py index fc1b4cf8d8..c124cb72ab 100755 --- a/Gems/AWSCore/cdk/core/core_stack.py +++ b/Gems/AWSCore/cdk/core/core_stack.py @@ -60,17 +60,6 @@ class CoreStack(core.Stack): type='TAG_FILTERS_1_0') ) - # Create an S3 bucket for Amazon S3 server access logging - # See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html - self._server_access_logs_bucket = s3.Bucket( - self, - f'{self._project_name}-{self._feature_name}-Access-Log-Bucket', - block_public_access=s3.BlockPublicAccess.BLOCK_ALL, - encryption=s3.BucketEncryption.S3_MANAGED, - access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE - ) - self._server_access_logs_bucket.grant_read(self._admin_group) - # Define exports # Export resource group self._resource_group_output = core.CfnOutput( @@ -94,10 +83,22 @@ class CoreStack(core.Stack): export_name=f"{self._project_name}:AdminGroup", value=self._admin_group.group_arn) - # Export access log bucket name - self._server_access_logs_bucket_output = core.CfnOutput( - self, - id=f'ServerAccessLogsBucketOutput', - description='Name of the S3 bucket for storing server access logs generated by the sample CDK application(s)', - export_name=f"{self._project_name}:ServerAccessLogsBucket", - value=self._server_access_logs_bucket.bucket_name) + # Create an S3 bucket for Amazon S3 server access logging + # See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html + if self.node.try_get_context('disable_access_log') != 'true': + self._server_access_logs_bucket = s3.Bucket( + self, + f'{self._project_name}-{self._feature_name}-Access-Log-Bucket', + block_public_access=s3.BlockPublicAccess.BLOCK_ALL, + encryption=s3.BucketEncryption.S3_MANAGED, + access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE + ) + self._server_access_logs_bucket.grant_read(self._admin_group) + + # Export access log bucket name + self._server_access_logs_bucket_output = core.CfnOutput( + self, + id=f'ServerAccessLogsBucketOutput', + description='Name of the S3 bucket for storing server access logs generated by the sample CDK application(s)', + export_name=f"{self._project_name}:ServerAccessLogsBucket", + value=self._server_access_logs_bucket.bucket_name) diff --git a/Gems/AWSCore/cdk/example/example_resources_stack.py b/Gems/AWSCore/cdk/example/example_resources_stack.py index 23bc78d8fc..ac229cb313 100755 --- a/Gems/AWSCore/cdk/example/example_resources_stack.py +++ b/Gems/AWSCore/cdk/example/example_resources_stack.py @@ -118,19 +118,23 @@ class ExampleResources(core.Stack): # https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html # 3. Enable Amazon S3 server access logging # https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html - server_access_logs_bucket = s3.Bucket.from_bucket_name( - self, - f'{self._project_name}-{self._feature_name}-ImportedAccessLogsBucket', - core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket") - ) + server_access_logs_bucket = None + if self.node.try_get_context('disable_access_log') != 'true': + server_access_logs_bucket = s3.Bucket.from_bucket_name( + self, + f'{self._project_name}-{self._feature_name}-ImportedAccessLogsBucket', + core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket") + ) example_bucket = s3.Bucket( self, f'{self._project_name}-{self._feature_name}-Example-S3bucket', block_public_access=s3.BlockPublicAccess.BLOCK_ALL, encryption=s3.BucketEncryption.S3_MANAGED, - server_access_logs_bucket=server_access_logs_bucket, - server_access_logs_prefix=f'{self._project_name}-{self._feature_name}-{self.region}-AccessLogs' + server_access_logs_bucket= + server_access_logs_bucket if server_access_logs_bucket else None, + server_access_logs_prefix= + f'{self._project_name}-{self._feature_name}-{self.region}-AccessLogs' if server_access_logs_bucket else None ) s3_deployment.BucketDeployment( diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt b/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt index 3eb5eafab0..1fab09e9f4 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt @@ -15,10 +15,11 @@ ly_add_target( awsgamelift_client_files.cmake INCLUDE_DIRECTORIES PUBLIC + ../AWSGameLiftCommon/Include Include PRIVATE - Source ../AWSGameLiftCommon/Source + Source COMPILE_DEFINITIONS PRIVATE ${awsgameliftclient_compile_definition} @@ -78,10 +79,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) awsgamelift_client_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE + ../AWSGameLiftCommon/Include + ../AWSGameLiftCommon/Source Include Tests Source - ../AWSGameLiftCommon/Source BUILD_DEPENDENCIES PRIVATE AZ::AzCore diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h index cd30bc45ab..ec3719c6d3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h @@ -8,35 +8,14 @@ #pragma once +#include +#include #include +#include + namespace AWSGameLift { - //! AWSGameLiftPlayerInformation - //! Information on each player to be matched - //! This information must include a player ID, and may contain player attributes and latency data to be used in the matchmaking process - //! After a successful match, Player objects contain the name of the team the player is assigned to - struct AWSGameLiftPlayerInformation - { - AZ_RTTI(AWSGameLiftPlayerInformation, "{B62C118E-C55D-4903-8ECB-E58E8CA613C4}"); - static void Reflect(AZ::ReflectContext* context); - - AWSGameLiftPlayerInformation() = default; - virtual ~AWSGameLiftPlayerInformation() = default; - - // A map of region names to latencies in millseconds, that indicates - // the amount of latency that a player experiences when connected to AWS Regions - AZStd::unordered_map m_latencyInMs; - // A collection of key:value pairs containing player information for use in matchmaking - // Player attribute keys must match the playerAttributes used in a matchmaking rule set - // Example: {"skill": "{\"N\": \"23\"}", "gameMode": "{\"S\": \"deathmatch\"}"} - AZStd::unordered_map m_playerAttributes; - // A unique identifier for a player - AZStd::string m_playerId; - // Name of the team that the player is assigned to in a match - AZStd::string m_team; - }; - //! AWSGameLiftStartMatchmakingRequest //! GameLift start matchmaking request which corresponds to Amazon GameLift //! Uses FlexMatch to create a game match for a group of players based on custom matchmaking rules @@ -54,6 +33,6 @@ namespace AWSGameLift // Name of the matchmaking configuration to use for this request AZStd::string m_configurationName; // Information on each player to be matched - AZStd::vector m_players; + AZStd::vector m_players; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp index 9ae168aea9..af0cf1c35c 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,15 @@ namespace AWSGameLift AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket tracker is running."); return; } + + // Make sure thread and wait event are both in clean state before starting new one + m_waitEvent.release(); + if (m_trackerThread.joinable()) + { + m_trackerThread.join(); + } + m_waitEvent.acquire(); + m_status = TicketTrackerStatus::Running; m_trackerThread = AZStd::thread(AZStd::bind( &AWSGameLiftClientLocalTicketTracker::ProcessPolling, this, ticketId, playerId)); @@ -56,6 +66,7 @@ namespace AWSGameLift { AZStd::lock_guard lock(m_trackerMutex); m_status = TicketTrackerStatus::Idle; + m_waitEvent.release(); if (m_trackerThread.joinable()) { m_trackerThread.join(); @@ -81,24 +92,25 @@ namespace AWSGameLift auto ticket = describeMatchmakingOutcome.GetResult().GetTicketList().front(); if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED) { - m_status = TicketTrackerStatus::Idle; AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket %s is complete.", ticket.GetTicketId().c_str()); RequestPlayerJoinMatch(ticket, playerId); + m_status = TicketTrackerStatus::Idle; return; } else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::TIMED_OUT || ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::FAILED || ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::CANCELLED) { - m_status = TicketTrackerStatus::Idle; AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, "Matchmaking ticket %s is not complete, %s", ticket.GetTicketId().c_str(), ticket.GetStatusReason().c_str()); + m_status = TicketTrackerStatus::Idle; return; } else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE) { // broadcast acceptance requires to player + AzFramework::MatchAcceptanceNotificationBus::Broadcast(&AzFramework::MatchAcceptanceNotifications::OnMatchAcceptance); } else { @@ -122,7 +134,7 @@ namespace AWSGameLift { AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftClientMissingErrorMessage); } - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_pollingPeriodInMS)); + m_waitEvent.try_acquire_for(AZStd::chrono::milliseconds(m_pollingPeriodInMS)); } } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h index 7b317a0485..23083e9fd9 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -58,5 +59,6 @@ namespace AWSGameLift AZStd::mutex m_trackerMutex; AZStd::thread m_trackerThread; + AZStd::binary_semaphore m_waitEvent; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp index ee731993aa..224f16481e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp @@ -18,12 +18,15 @@ #include #include +#include #include #include #include #include #include #include +#include +#include #include @@ -43,10 +46,22 @@ namespace AWSGameLift AZ::Interface::Register(this); AWSGameLiftSessionRequestBus::Handler::BusConnect(); + + AZ::Interface::Register(this); + AWSGameLiftMatchmakingAsyncRequestBus::Handler::BusConnect(); + + AZ::Interface::Register(this); + AWSGameLiftMatchmakingRequestBus::Handler::BusConnect(); } void AWSGameLiftClientManager::DeactivateManager() { + AWSGameLiftMatchmakingRequestBus::Handler::BusDisconnect(); + AZ::Interface::Unregister(this); + + AWSGameLiftMatchmakingAsyncRequestBus::Handler::BusDisconnect(); + AZ::Interface::Unregister(this); + AWSGameLiftSessionRequestBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); @@ -111,12 +126,53 @@ namespace AWSGameLift void AWSGameLiftClientManager::AcceptMatch(const AzFramework::AcceptMatchRequest& acceptMatchRequest) { - AZ_UNUSED(acceptMatchRequest); + if (AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest)) + { + const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest = + static_cast(acceptMatchRequest); + AcceptMatchHelper(gameliftStartMatchmakingRequest); + } } void AWSGameLiftClientManager::AcceptMatchAsync(const AzFramework::AcceptMatchRequest& acceptMatchRequest) { - AZ_UNUSED(acceptMatchRequest); + if (!AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest)) + { + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnAcceptMatchAsyncComplete); + return; + } + + const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest = static_cast(acceptMatchRequest); + + AZ::JobContext* jobContext = nullptr; + AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext); + AZ::Job* acceptMatchJob = AZ::CreateJobFunction( + [this, gameliftStartMatchmakingRequest]() + { + AcceptMatchHelper(gameliftStartMatchmakingRequest); + + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnAcceptMatchAsyncComplete); + }, + true, jobContext); + + acceptMatchJob->Start(); + } + + void AWSGameLiftClientManager::AcceptMatchHelper(const AWSGameLiftAcceptMatchRequest& acceptMatchRequest) + { + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); + + AZStd::string response; + if (!gameliftClient) + { + AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage); + } + else + { + AcceptMatchActivity::AcceptMatch(*gameliftClient, acceptMatchRequest); + } } AZStd::string AWSGameLiftClientManager::CreateSession(const AzFramework::CreateSessionRequest& createSessionRequest) @@ -357,23 +413,108 @@ namespace AWSGameLift AZStd::string AWSGameLiftClientManager::StartMatchmaking(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest) { - AZ_UNUSED(startMatchmakingRequest); + AZStd::string response; + if (StartMatchmakingActivity::ValidateStartMatchmakingRequest(startMatchmakingRequest)) + { + const AWSGameLiftStartMatchmakingRequest& gameliftStartMatchmakingRequest = + static_cast(startMatchmakingRequest); + response = StartMatchmakingHelper(gameliftStartMatchmakingRequest); + } - return AZStd::string{}; + return response; } void AWSGameLiftClientManager::StartMatchmakingAsync(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest) { - AZ_UNUSED(startMatchmakingRequest); + if (!StartMatchmakingActivity::ValidateStartMatchmakingRequest(startMatchmakingRequest)) + { + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnStartMatchmakingAsyncComplete, AZStd::string{}); + return; + } + + const AWSGameLiftStartMatchmakingRequest& gameliftStartMatchmakingRequest = + static_cast(startMatchmakingRequest); + + AZ::JobContext* jobContext = nullptr; + AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext); + AZ::Job* startMatchmakingJob = AZ::CreateJobFunction( + [this, gameliftStartMatchmakingRequest]() + { + AZStd::string response = StartMatchmakingHelper(gameliftStartMatchmakingRequest); + + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnStartMatchmakingAsyncComplete, response); + }, + true, jobContext); + + startMatchmakingJob->Start(); + } + + AZStd::string AWSGameLiftClientManager::StartMatchmakingHelper(const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest) + { + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); + + AZStd::string response; + if (!gameliftClient) + { + AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage); + } + else + { + response = StartMatchmakingActivity::StartMatchmaking(*gameliftClient, startMatchmakingRequest); + } + return response; } void AWSGameLiftClientManager::StopMatchmaking(const AzFramework::StopMatchmakingRequest& stopMatchmakingRequest) { - AZ_UNUSED(stopMatchmakingRequest); + if (StopMatchmakingActivity::ValidateStopMatchmakingRequest(stopMatchmakingRequest)) + { + const AWSGameLiftStopMatchmakingRequest& gameliftStopMatchmakingRequest = + static_cast(stopMatchmakingRequest); + StopMatchmakingHelper(gameliftStopMatchmakingRequest); + } } void AWSGameLiftClientManager::StopMatchmakingAsync(const AzFramework::StopMatchmakingRequest& stopMatchmakingRequest) { - AZ_UNUSED(stopMatchmakingRequest); + if (!StopMatchmakingActivity::ValidateStopMatchmakingRequest(stopMatchmakingRequest)) + { + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnStopMatchmakingAsyncComplete); + return; + } + + const AWSGameLiftStopMatchmakingRequest& gameliftStopMatchmakingRequest = + static_cast(stopMatchmakingRequest); + + AZ::JobContext* jobContext = nullptr; + AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext); + AZ::Job* stopMatchmakingJob = AZ::CreateJobFunction( + [this, gameliftStopMatchmakingRequest]() + { + StopMatchmakingHelper(gameliftStopMatchmakingRequest); + + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnStopMatchmakingAsyncComplete); + }, + true, jobContext); + + stopMatchmakingJob->Start(); + } + + void AWSGameLiftClientManager::StopMatchmakingHelper(const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest) + { + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); + + if (!gameliftClient) + { + AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage); + } + else + { + StopMatchmakingActivity::StopMatchmaking(*gameliftClient, stopMatchmakingRequest); + } } } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h index fbdc1d6104..1f32b69f75 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h @@ -15,10 +15,13 @@ namespace AWSGameLift { + struct AWSGameLiftAcceptMatchRequest; struct AWSGameLiftCreateSessionRequest; struct AWSGameLiftCreateSessionOnQueueRequest; struct AWSGameLiftJoinSessionRequest; struct AWSGameLiftSearchSessionsRequest; + struct AWSGameLiftStartMatchmakingRequest; + struct AWSGameLiftStopMatchmakingRequest; // MatchAcceptanceNotificationBus EBus handler for scripting class AWSGameLiftMatchAcceptanceNotificationBusHandler @@ -156,9 +159,12 @@ namespace AWSGameLift void LeaveSession() override; private: + void AcceptMatchHelper(const AWSGameLiftAcceptMatchRequest& createSessionRequest); AZStd::string CreateSessionHelper(const AWSGameLiftCreateSessionRequest& createSessionRequest); AZStd::string CreateSessionOnQueueHelper(const AWSGameLiftCreateSessionOnQueueRequest& createSessionOnQueueRequest); bool JoinSessionHelper(const AWSGameLiftJoinSessionRequest& joinSessionRequest); AzFramework::SearchSessionsResponse SearchSessionsHelper(const AWSGameLiftSearchSessionsRequest& searchSessionsRequest) const; + AZStd::string StartMatchmakingHelper(const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest); + void StopMatchmakingHelper(const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest); }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp index ea5fff99a3..ed7830ce57 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp @@ -210,6 +210,7 @@ namespace AWSGameLift ->Property("SessionId", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionId)) ->Property("SessionName", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionName)) ->Property("SessionProperties", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionProperties)) + ->Property("MatchmakingData", BehaviorValueProperty(&AzFramework::SessionConfig::m_matchmakingData)) ->Property("Status", BehaviorValueProperty(&AzFramework::SessionConfig::m_status)) ->Property("StatusReason", BehaviorValueProperty(&AzFramework::SessionConfig::m_statusReason)) ->Property("TerminationTime", BehaviorValueProperty(&AzFramework::SessionConfig::m_terminationTime)) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp new file mode 100644 index 0000000000..25ba328fd0 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution.AcceptMatch + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +#include +#include + +namespace AWSGameLift +{ + namespace AcceptMatchActivity + { + Aws::GameLift::Model::AcceptMatchRequest BuildAWSGameLiftAcceptMatchRequest( + const AWSGameLiftAcceptMatchRequest& acceptMatchRequest) + { + Aws::GameLift::Model::AcceptMatchRequest request; + request.SetAcceptanceType(acceptMatchRequest.m_acceptMatch ? + Aws::GameLift::Model::AcceptanceType::ACCEPT : Aws::GameLift::Model::AcceptanceType::REJECT); + + Aws::Vector playerIds; + for (const AZStd::string& playerId : acceptMatchRequest.m_playerIds) + { + playerIds.emplace_back(playerId.c_str()); + } + request.SetPlayerIds(playerIds); + + if (!acceptMatchRequest.m_ticketId.empty()) + { + request.SetTicketId(acceptMatchRequest.m_ticketId.c_str()); + } + + AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "Built AcceptMatchRequest with TicketId=%s", request.GetTicketId().c_str()); + + return request; + } + + void AcceptMatch(const Aws::GameLift::GameLiftClient& gameliftClient, + const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest) + { + AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "Requesting AcceptMatch against Amazon GameLift service ..."); + + Aws::GameLift::Model::AcceptMatchRequest request = BuildAWSGameLiftAcceptMatchRequest(AcceptMatchRequest); + auto AcceptMatchOutcome = gameliftClient.AcceptMatch(request); + + if (AcceptMatchOutcome.IsSuccess()) + { + AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "AcceptMatch request against Amazon GameLift service is complete"); + } + else + { + AZ_Error(AWSGameLiftAcceptMatchActivityName, false, AWSGameLiftErrorMessageTemplate, + AcceptMatchOutcome.GetError().GetExceptionName().c_str(), AcceptMatchOutcome.GetError().GetMessage().c_str()); + } + } + + bool ValidateAcceptMatchRequest(const AzFramework::AcceptMatchRequest& AcceptMatchRequest) + { + auto gameliftAcceptMatchRequest = azrtti_cast(&AcceptMatchRequest); + bool isValid = gameliftAcceptMatchRequest && + (gameliftAcceptMatchRequest->m_playerIds.size() > 0) && + (!gameliftAcceptMatchRequest->m_ticketId.empty()); + + AZ_Error(AWSGameLiftAcceptMatchActivityName, isValid, AWSGameLiftAcceptMatchRequestInvalidErrorMessage); + + return isValid; + } + } // namespace AcceptMatchActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h new file mode 100644 index 0000000000..d5f28f92e2 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +namespace AWSGameLift +{ + namespace AcceptMatchActivity + { + static constexpr const char AWSGameLiftAcceptMatchActivityName[] = "AWSGameLiftAcceptMatchActivity"; + static constexpr const char AWSGameLiftAcceptMatchRequestInvalidErrorMessage[] = "Invalid GameLift AcceptMatch request."; + + // Build AWS GameLift AcceptMatchRequest by using AWSGameLiftAcceptMatchRequest + Aws::GameLift::Model::AcceptMatchRequest BuildAWSGameLiftAcceptMatchRequest(const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest); + + // Create AcceptMatchRequest and make a AcceptMatch call through GameLift client + void AcceptMatch(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest); + + // Validate AcceptMatchRequest and check required request parameters + bool ValidateAcceptMatchRequest(const AzFramework::AcceptMatchRequest& AcceptMatchRequest); + } // namespace AcceptMatchActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp index 5bbd1d4654..f9968a7dd9 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp @@ -6,6 +6,7 @@ * */ +#include #include namespace AWSGameLift @@ -31,5 +32,53 @@ namespace AWSGameLift outGamePropertiesOutput.substr(0, outGamePropertiesOutput.size() - 1); // Trim last comma to fit array format } } + + void ConvertPlayerAttributes( + const AZStd::unordered_map& playerAttributes, + Aws::Map& outPlayerAttributes) + { + outPlayerAttributes.clear(); + for (auto& playerAttribute : playerAttributes) + { + Aws::Utils::Json::JsonValue keyJsonValue(playerAttribute.second.c_str()); + Aws::GameLift::Model::AttributeValue attribute(keyJsonValue); + outPlayerAttributes[playerAttribute.first.c_str()] = attribute; + } + } + + void ConvertRegionToLatencyMap( + const AZStd::unordered_map& regionToLatencyMap, + Aws::Map& outRegionToLatencyMap) + { + outRegionToLatencyMap.clear(); + for (auto& regionToLatencyPair : regionToLatencyMap) + { + outRegionToLatencyMap[regionToLatencyPair.first.c_str()] = regionToLatencyPair.second; + } + } + + bool ValidatePlayerAttributes( + const AZStd::unordered_map& playerAttributes) + { + for (auto& playerAttribute : playerAttributes) + { + Aws::Utils::Json::JsonValue keyJsonValue(playerAttribute.second.c_str()); + Aws::GameLift::Model::AttributeValue attribute(keyJsonValue); + + // Each AttributeValue object can use only one of the available properties: + // 1) number values (N) + // 2) single string values (S) + // 3) string to double map (SDM) + // 4) array of strings (SL) + if (!attribute.SHasBeenSet() && + !attribute.NHasBeenSet() && + !attribute.SDMHasBeenSet() && + !attribute.SLHasBeenSet()) + { + return false; + } + } + return true; + } } // namespace AWSGameLiftActivityUtils } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h index 05ab2867d0..729f1deec6 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace AWSGameLift { @@ -21,5 +22,17 @@ namespace AWSGameLift const AZStd::unordered_map& sessionProperties, Aws::Vector& outGameProperties, AZStd::string& outGamePropertiesOutput); + + void ConvertPlayerAttributes( + const AZStd::unordered_map& playerAttributes, + Aws::Map& outPlayerAttributes); + + void ConvertRegionToLatencyMap( + const AZStd::unordered_map& regionToLatencyMap, + Aws::Map& outRegionToLatencyMap); + + bool ValidatePlayerAttributes( + const AZStd::unordered_map& playerAttributes); + } // namespace AWSGameLiftActivityUtils } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp index 3d29fa2b71..5f0b6fd012 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp @@ -105,6 +105,7 @@ namespace AWSGameLift session.m_status = AWSGameLiftSessionStatusNames[(int)gameSession.GetStatus()]; session.m_statusReason = AWSGameLiftSessionStatusReasons[(int)gameSession.GetStatusReason()]; session.m_terminationTime = gameSession.GetTerminationTime().Millis(); + session.m_matchmakingData = gameSession.GetMatchmakerData().c_str(); // TODO: Update the AWS Native SDK to get the new game session attributes. //session.m_dnsName = gameSession.GetDnsName(); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp new file mode 100644 index 0000000000..00a7773491 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include + +#include +#include + +namespace AWSGameLift +{ + namespace StartMatchmakingActivity + { + Aws::GameLift::Model::StartMatchmakingRequest BuildAWSGameLiftStartMatchmakingRequest( + const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest) + { + Aws::GameLift::Model::StartMatchmakingRequest request; + if (!startMatchmakingRequest.m_configurationName.empty()) + { + request.SetConfigurationName(startMatchmakingRequest.m_configurationName.c_str()); + } + + Aws::Vector players; + for (const AWSGameLiftPlayer& playerInfo : startMatchmakingRequest.m_players) + { + Aws::GameLift::Model::Player player; + if (!playerInfo.m_playerId.empty()) + { + player.SetPlayerId(playerInfo.m_playerId.c_str()); + } + + // Optional attributes + if (!playerInfo.m_team.empty()) + { + player.SetTeam(playerInfo.m_team.c_str()); + } + if (playerInfo.m_latencyInMs.size() > 0) + { + Aws::Map regionToLatencyMap; + AWSGameLiftActivityUtils::ConvertRegionToLatencyMap(playerInfo.m_latencyInMs, regionToLatencyMap); + player.SetLatencyInMs(AZStd::move(regionToLatencyMap)); + } + if (playerInfo.m_playerAttributes.size() > 0) + { + Aws::Map playerAttributes; + AWSGameLiftActivityUtils::ConvertPlayerAttributes(playerInfo.m_playerAttributes, playerAttributes); + player.SetPlayerAttributes(AZStd::move(playerAttributes)); + } + players.emplace_back(player); + } + if (startMatchmakingRequest.m_players.size() > 0) + { + request.SetPlayers(players); + } + + // Optional attributes + if (!startMatchmakingRequest.m_ticketId.empty()) + { + request.SetTicketId(startMatchmakingRequest.m_ticketId.c_str()); + } + + AZ_TracePrintf(AWSGameLiftStartMatchmakingActivityName, + "Built StartMatchmakingRequest with TicketId=%s, ConfigurationName=%s and PlayersCount=%d", + request.GetTicketId().c_str(), + request.GetConfigurationName().c_str(), + request.GetPlayers().size()); + + return request; + } + + AZStd::string StartMatchmaking( + const Aws::GameLift::GameLiftClient& gameliftClient, + const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest) + { + AZ_TracePrintf(AWSGameLiftStartMatchmakingActivityName, "Requesting StartMatchmaking against Amazon GameLift service ..."); + + AZStd::string result = ""; + Aws::GameLift::Model::StartMatchmakingRequest request = BuildAWSGameLiftStartMatchmakingRequest(startMatchmakingRequest); + auto startMatchmakingOutcome = gameliftClient.StartMatchmaking(request); + if (startMatchmakingOutcome.IsSuccess()) + { + result = AZStd::string(startMatchmakingOutcome.GetResult().GetMatchmakingTicket().GetTicketId().c_str()); + + AZ_TracePrintf(AWSGameLiftStartMatchmakingActivityName, "StartMatchmaking request against Amazon GameLift service is complete"); + } + else + { + AZ_Error(AWSGameLiftStartMatchmakingActivityName, false, AWSGameLiftErrorMessageTemplate, + startMatchmakingOutcome.GetError().GetExceptionName().c_str(), startMatchmakingOutcome.GetError().GetMessage().c_str()); + } + + return result; + } + + bool ValidateStartMatchmakingRequest(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest) + { + auto gameliftStartMatchmakingRequest = azrtti_cast(&startMatchmakingRequest); + bool isValid = gameliftStartMatchmakingRequest && + (!gameliftStartMatchmakingRequest->m_configurationName.empty()) && + gameliftStartMatchmakingRequest->m_players.size() > 0; + + if (isValid) + { + for (const AWSGameLiftPlayer& playerInfo : gameliftStartMatchmakingRequest->m_players) + { + isValid &= !playerInfo.m_playerId.empty(); + isValid &= AWSGameLiftActivityUtils::ValidatePlayerAttributes(playerInfo.m_playerAttributes); + } + } + + AZ_Error(AWSGameLiftStartMatchmakingActivityName, isValid, AWSGameLiftStartMatchmakingRequestInvalidErrorMessage); + + return isValid; + } + } // namespace StartMatchmakingActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h new file mode 100644 index 0000000000..db814c14b2 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +namespace AWSGameLift +{ + namespace StartMatchmakingActivity + { + static constexpr const char AWSGameLiftStartMatchmakingActivityName[] = "AWSGameLiftStartMatchmakingActivity"; + static constexpr const char AWSGameLiftStartMatchmakingRequestInvalidErrorMessage[] = "Invalid GameLift StartMatchmaking request."; + + // Build AWS GameLift StartMatchmakingRequest by using AWSGameLiftStartMatchmakingRequest + Aws::GameLift::Model::StartMatchmakingRequest BuildAWSGameLiftStartMatchmakingRequest(const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest); + + // Create StartMatchmakingRequest and make a StartMatchmaking call through GameLift client + AZStd::string StartMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest); + + // Validate StartMatchmakingRequest and check required request parameters + bool ValidateStartMatchmakingRequest(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest); + } // namespace StartMatchmakingActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp new file mode 100644 index 0000000000..b427d0323a --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution.StopMatchmaking + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +#include +#include + +namespace AWSGameLift +{ + namespace StopMatchmakingActivity + { + Aws::GameLift::Model::StopMatchmakingRequest BuildAWSGameLiftStopMatchmakingRequest( + const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest) + { + Aws::GameLift::Model::StopMatchmakingRequest request; + if (!stopMatchmakingRequest.m_ticketId.empty()) + { + request.SetTicketId(stopMatchmakingRequest.m_ticketId.c_str()); + } + + AZ_TracePrintf(AWSGameLiftStopMatchmakingActivityName, "Built StopMatchmakingRequest with TicketId=%s", request.GetTicketId().c_str()); + + return request; + } + + void StopMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, + const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest) + { + AZ_TracePrintf(AWSGameLiftStopMatchmakingActivityName, "Requesting StopMatchmaking against Amazon GameLift service ..."); + + Aws::GameLift::Model::StopMatchmakingRequest request = BuildAWSGameLiftStopMatchmakingRequest(stopMatchmakingRequest); + auto stopMatchmakingOutcome = gameliftClient.StopMatchmaking(request); + + if (stopMatchmakingOutcome.IsSuccess()) + { + AZ_TracePrintf(AWSGameLiftStopMatchmakingActivityName, "StopMatchmaking request against Amazon GameLift service is complete"); + } + else + { + AZ_Error(AWSGameLiftStopMatchmakingActivityName, false, AWSGameLiftErrorMessageTemplate, + stopMatchmakingOutcome.GetError().GetExceptionName().c_str(), stopMatchmakingOutcome.GetError().GetMessage().c_str()); + } + } + + bool ValidateStopMatchmakingRequest(const AzFramework::StopMatchmakingRequest& StopMatchmakingRequest) + { + auto gameliftStopMatchmakingRequest = azrtti_cast(&StopMatchmakingRequest); + bool isValid = gameliftStopMatchmakingRequest && (!gameliftStopMatchmakingRequest->m_ticketId.empty()); + + AZ_Error(AWSGameLiftStopMatchmakingActivityName, isValid, AWSGameLiftStopMatchmakingRequestInvalidErrorMessage); + + return isValid; + } + } // namespace StopMatchmakingActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h new file mode 100644 index 0000000000..0820f2c05e --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +namespace AWSGameLift +{ + namespace StopMatchmakingActivity + { + static constexpr const char AWSGameLiftStopMatchmakingActivityName[] = "AWSGameLiftStopMatchmakingActivity"; + static constexpr const char AWSGameLiftStopMatchmakingRequestInvalidErrorMessage[] = "Invalid GameLift StopMatchmaking request."; + + // Build AWS GameLift StopMatchmakingRequest by using AWSGameLiftStopMatchmakingRequest + Aws::GameLift::Model::StopMatchmakingRequest BuildAWSGameLiftStopMatchmakingRequest(const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest); + + // Create StopMatchmakingRequest and make a StopMatchmaking call through GameLift client + void StopMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest); + + // Validate StopMatchmakingRequest and check required request parameters + bool ValidateStopMatchmakingRequest(const AzFramework::StopMatchmakingRequest& stopMatchmakingRequest); + } // namespace StopMatchmakingActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp index 03d802fd36..31e9c5eff7 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp @@ -14,53 +14,10 @@ namespace AWSGameLift { - void AWSGameLiftPlayerInformation::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(0) - ->Field("latencyInMs", &AWSGameLiftPlayerInformation::m_latencyInMs) - ->Field("playerAttributes", &AWSGameLiftPlayerInformation::m_playerAttributes) - ->Field("playerId", &AWSGameLiftPlayerInformation::m_playerId) - ->Field("team", &AWSGameLiftPlayerInformation::m_team); - - if (AZ::EditContext* editContext = serializeContext->GetEditContext()) - { - editContext->Class("AWSGameLiftPlayerInformation", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_latencyInMs, "LatencyInMs", - "A set of values, expressed in milliseconds, that indicates the amount of latency that" - "a player experiences when connected to AWS Regions") - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_playerAttributes, "PlayerAttributes", - "A collection of key:value pairs containing player information for use in matchmaking") - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_playerId, "PlayerId", - "A unique identifier for a player") - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_team, "Team", - "Name of the team that the player is assigned to in a match"); - } - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class("AWSGameLiftPlayerInformation") - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) - ->Property("LatencyInMs", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_latencyInMs)) - ->Property("PlayerAttributes", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_playerAttributes)) - ->Property("PlayerId", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_playerId)) - ->Property("Team", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_team)); - } - } - void AWSGameLiftStartMatchmakingRequest::Reflect(AZ::ReflectContext* context) { AzFramework::StartMatchmakingRequest::Reflect(context); - AWSGameLiftPlayerInformation::Reflect(context); + AWSGameLiftPlayer::Reflect(context); if (auto serializeContext = azrtti_cast(context)) { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp index 6e688b28e3..4dc4dd85f6 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp @@ -16,7 +16,7 @@ using namespace AWSGameLift; -static constexpr const uint64_t TEST_RACKER_POLLING_PERIOD_MS = 100; +static constexpr const uint64_t TEST_RACKER_POLLING_PERIOD_MS = 1000; static constexpr const uint64_t TEST_WAIT_BUFFER_TIME_MS = 10; static constexpr const uint64_t TEST_WAIT_MAXIMUM_TIME_MS = 10000; @@ -351,3 +351,41 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallAndTicketComple WaitForProcessFinish(); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_RequiresAcceptanceAndTicketCompleteAtLast_ProcessContinuesAndStop) +{ + Aws::GameLift::Model::MatchmakingTicket ticket1; + ticket1.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE); + + Aws::GameLift::Model::DescribeMatchmakingResult result1; + result1.AddTicketList(ticket1); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome1(result1); + + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + connectionInfo.AddMatchedPlayerSessions( + Aws::GameLift::Model::MatchedPlayerSession().WithPlayerId("player1").WithPlayerSessionId("playersession1")); + + Aws::GameLift::Model::MatchmakingTicket ticket2; + ticket2.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket2.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result2; + result2.AddTicketList(ticket2); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome2(result2); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .WillOnce(::testing::Return(outcome1)) + .WillOnce(::testing::Return(outcome2)); + + MatchAcceptanceNotificationsHandlerMock handlerMock1; + EXPECT_CALL(handlerMock1, OnMatchAcceptance()).Times(1); + + SessionHandlingClientRequestsMock handlerMock2; + EXPECT_CALL(handlerMock2, RequestPlayerJoinSession(::testing::_)).Times(1).WillOnce(::testing::Return(true)); + + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp index e7612618ee..1c3e8726fd 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp @@ -19,11 +19,14 @@ #include #include +#include #include #include #include #include #include +#include +#include using namespace AWSGameLift; @@ -205,6 +208,7 @@ protected: sessionConfig.m_terminationTime = 0; sessionConfig.m_creatorId = "dummyCreatorId"; sessionConfig.m_sessionProperties["dummyKey"] = "dummyValue"; + sessionConfig.m_matchmakingData = "dummyMatchmakingData"; sessionConfig.m_sessionId = "dummyGameSessionId"; sessionConfig.m_sessionName = "dummyGameSessionName"; sessionConfig.m_ipAddress = "dummyIpAddress"; @@ -223,11 +227,43 @@ protected: return response; } + AWSGameLiftStartMatchmakingRequest GetValidStartMatchmakingRequest() + { + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + request.m_ticketId = DummyMatchmakingTicketId; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"N\": \"1\"}"; + player.m_playerId = DummyPlayerId; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + + return request; + } + + Aws::GameLift::Model::StartMatchmakingOutcome GetValidStartMatchmakingResponse() + { + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetTicketId(DummyMatchmakingTicketId); + Aws::GameLift::Model::StartMatchmakingResult result; + result.SetMatchmakingTicket(ticket); + Aws::GameLift::Model::StartMatchmakingOutcome outcome(result); + + return outcome; + } + + static const char* const DummyMatchmakingTicketId; + static const char* const DummyPlayerId; + public: AZStd::unique_ptr m_gameliftClientManager; AZStd::shared_ptr m_gameliftClientMockPtr; }; +const char* const AWSGameLiftClientManagerTest::DummyMatchmakingTicketId = "dummyTicketId"; +const char* const AWSGameLiftClientManagerTest::DummyPlayerId = "dummyPlayerId"; + TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithoutRegion_GetFalseAsResult) { AZ_TEST_START_TRACE_SUPPRESSION; @@ -762,3 +798,315 @@ TEST_F(AWSGameLiftClientManagerTest, LeaveSessionAsync_CallWithInterfaceRegister m_gameliftClientManager->LeaveSessionAsync(); } + +TEST_F(AWSGameLiftClientManagerTest, StartMatchmaking_CallWithoutClientSetup_GetFalseResponse) +{ + AWSGameLiftStartMatchmakingRequest request = GetValidStartMatchmakingRequest(); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->ConfigureGameLiftClient(""); + AZStd::string response = m_gameliftClientManager->StartMatchmaking(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(2); // capture 2 error message + EXPECT_TRUE(response.empty()); + +} +TEST_F(AWSGameLiftClientManagerTest, StartMatchmaking_CallWithInvalidRequest_GetErrorWithEmptyResponse) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"A\": \"1\"}"; + request.m_players.emplace_back(player); + + AZ_TEST_START_TRACE_SUPPRESSION; + AZStd::string response = m_gameliftClientManager->StartMatchmaking(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message + EXPECT_TRUE(response.empty()); +} + +TEST_F(AWSGameLiftClientManagerTest, StartMatchmaking_CallWithValidRequest_GetSuccessOutcome) +{ + AWSGameLiftStartMatchmakingRequest request = GetValidStartMatchmakingRequest(); + Aws::GameLift::Model::StartMatchmakingOutcome outcome = GetValidStartMatchmakingResponse(); + + EXPECT_CALL(*m_gameliftClientMockPtr, StartMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZStd::string response = m_gameliftClientManager->StartMatchmaking(request); + EXPECT_EQ(response, DummyMatchmakingTicketId); +} + +TEST_F(AWSGameLiftClientManagerTest, StartMatchmaking_CallWithValidRequest_GetErrorOutcome) +{ + AWSGameLiftStartMatchmakingRequest request = GetValidStartMatchmakingRequest(); + + Aws::Client::AWSError error; + Aws::GameLift::Model::StartMatchmakingOutcome outcome(error); + + EXPECT_CALL(*m_gameliftClientMockPtr, StartMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StartMatchmaking(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, StartMatchmakingAsync_CallWithInvalidRequest_GetNotificationWithErrorOutcome) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"A\": \"1\"}"; + request.m_players.emplace_back(player); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnStartMatchmakingAsyncComplete(AZStd::string{})).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StartMatchmakingAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, StartMatchmakingAsync_CallWithValidRequest_GetNotificationWithSuccessOutcome) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftStartMatchmakingRequest request = GetValidStartMatchmakingRequest(); + Aws::GameLift::Model::StartMatchmakingOutcome outcome = GetValidStartMatchmakingResponse(); + + EXPECT_CALL(*m_gameliftClientMockPtr, StartMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnStartMatchmakingAsyncComplete(AZStd::string(DummyMatchmakingTicketId))).Times(1); + + m_gameliftClientManager->StartMatchmakingAsync(request); +} + +TEST_F(AWSGameLiftClientManagerTest, StartMatchmakingAsync_CallWithValidRequest_GetNotificationWithErrorOutcome) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftStartMatchmakingRequest request = GetValidStartMatchmakingRequest(); + + Aws::Client::AWSError error; + Aws::GameLift::Model::StartMatchmakingOutcome outcome(error); + EXPECT_CALL(*m_gameliftClientMockPtr, StartMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnStartMatchmakingAsyncComplete(AZStd::string{})).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StartMatchmakingAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, StopMatchmaking_CallWithoutClientSetup_GetError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->ConfigureGameLiftClient(""); + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = DummyMatchmakingTicketId; + + m_gameliftClientManager->StopMatchmaking(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(2); // capture 2 error message +} +TEST_F(AWSGameLiftClientManagerTest, StopMatchmaking_CallWithInvalidRequest_GetError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StopMatchmaking(AzFramework::StopMatchmakingRequest()); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, StopMatchmaking_CallWithValidRequest_Success) +{ + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::GameLift::Model::StopMatchmakingResult result; + Aws::GameLift::Model::StopMatchmakingResult outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, StopMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + m_gameliftClientManager->StopMatchmaking(request); +} + +TEST_F(AWSGameLiftClientManagerTest, StopMatchmaking_CallWithValidRequest_GetError) +{ + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::Client::AWSError error; + Aws::GameLift::Model::StopMatchmakingOutcome outcome(error); + + EXPECT_CALL(*m_gameliftClientMockPtr, StopMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StopMatchmaking(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, StopMatchmakingAsync_CallWithInvalidRequest_GetNotificationWithError) +{ + AWSGameLiftStopMatchmakingRequest request; + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnStopMatchmakingAsyncComplete()).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StopMatchmakingAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, StopMatchmakingAsync_CallWithValidRequest_GetNotification) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::GameLift::Model::StopMatchmakingResult result; + Aws::GameLift::Model::StopMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, StopMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnStopMatchmakingAsyncComplete()).Times(1); + + m_gameliftClientManager->StopMatchmakingAsync(request); +} + +TEST_F(AWSGameLiftClientManagerTest, StopMatchmakingAsync_CallWithValidRequest_GetNotificationWithError) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::Client::AWSError error; + Aws::GameLift::Model::StopMatchmakingOutcome outcome(error); + EXPECT_CALL(*m_gameliftClientMockPtr, StopMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnStopMatchmakingAsyncComplete()).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->StopMatchmakingAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithoutClientSetup_GetError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->ConfigureGameLiftClient(""); + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + m_gameliftClientManager->AcceptMatch(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(2); // capture 2 error message +} +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithInvalidRequest_GetError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatch(AzFramework::AcceptMatchRequest()); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithValidRequest_Success) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::GameLift::Model::AcceptMatchResult result; + Aws::GameLift::Model::AcceptMatchResult outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + + m_gameliftClientManager->AcceptMatch(request); +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithValidRequest_GetError) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::Client::AWSError error; + Aws::GameLift::Model::AcceptMatchOutcome outcome(error); + + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatch(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithInvalidRequest_GetNotificationWithError) +{ + AWSGameLiftAcceptMatchRequest request; + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatchAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithValidRequest_GetNotification) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::GameLift::Model::AcceptMatchResult result; + Aws::GameLift::Model::AcceptMatchOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1); + + m_gameliftClientManager->AcceptMatchAsync(request); +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithValidRequest_GetNotificationWithError) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::Client::AWSError error; + Aws::GameLift::Model::AcceptMatchOutcome outcome(error); + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatchAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h index 5c09f43e08..6ba05747aa 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h @@ -9,14 +9,18 @@ #pragma once #include +#include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -27,6 +31,12 @@ #include #include #include +#include +#include +#include +#include + +#include using namespace Aws::GameLift; @@ -39,11 +49,50 @@ public: { } + MOCK_CONST_METHOD1(AcceptMatch, Model::AcceptMatchOutcome(const Model::AcceptMatchRequest&)); MOCK_CONST_METHOD1(CreateGameSession, Model::CreateGameSessionOutcome(const Model::CreateGameSessionRequest&)); MOCK_CONST_METHOD1(CreatePlayerSession, Model::CreatePlayerSessionOutcome(const Model::CreatePlayerSessionRequest&)); MOCK_CONST_METHOD1(DescribeMatchmaking, Model::DescribeMatchmakingOutcome(const Model::DescribeMatchmakingRequest&)); MOCK_CONST_METHOD1(SearchGameSessions, Model::SearchGameSessionsOutcome(const Model::SearchGameSessionsRequest&)); MOCK_CONST_METHOD1(StartGameSessionPlacement, Model::StartGameSessionPlacementOutcome(const Model::StartGameSessionPlacementRequest&)); + MOCK_CONST_METHOD1(StartMatchmaking, Model::StartMatchmakingOutcome(const Model::StartMatchmakingRequest&)); + MOCK_CONST_METHOD1(StopMatchmaking, Model::StopMatchmakingOutcome(const Model::StopMatchmakingRequest&)); +}; + +class MatchmakingAsyncRequestNotificationsHandlerMock + : public AzFramework::MatchmakingAsyncRequestNotificationBus::Handler +{ +public: + MatchmakingAsyncRequestNotificationsHandlerMock() + { + AzFramework::MatchmakingAsyncRequestNotificationBus::Handler::BusConnect(); + } + + ~MatchmakingAsyncRequestNotificationsHandlerMock() + { + AzFramework::MatchmakingAsyncRequestNotificationBus::Handler::BusDisconnect(); + } + + MOCK_METHOD0(OnAcceptMatchAsyncComplete, void()); + MOCK_METHOD1(OnStartMatchmakingAsyncComplete, void(const AZStd::string&)); + MOCK_METHOD0(OnStopMatchmakingAsyncComplete, void()); +}; + +class MatchAcceptanceNotificationsHandlerMock + : public AzFramework::MatchAcceptanceNotificationBus::Handler +{ +public: + MatchAcceptanceNotificationsHandlerMock() + { + AzFramework::MatchAcceptanceNotificationBus::Handler::BusConnect(); + } + + ~MatchAcceptanceNotificationsHandlerMock() + { + AzFramework::MatchAcceptanceNotificationBus::Handler::BusDisconnect(); + } + + MOCK_METHOD0(OnMatchAcceptance, void()); }; class SessionAsyncRequestNotificationsHandlerMock diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp new file mode 100644 index 0000000000..af78ca8c0c --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +#include + +using namespace AWSGameLift; + +using AWSGameLiftAcceptMatchActivityTest = AWSGameLiftClientFixture; + +TEST_F(AWSGameLiftAcceptMatchActivityTest, BuildAWSGameLiftAcceptMatchRequest_Call_GetExpectedResult) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_ticketId = "dummyTicketId"; + request.m_playerIds = { "dummyPlayerId" }; + + auto awsRequest = AcceptMatchActivity::BuildAWSGameLiftAcceptMatchRequest(request); + + EXPECT_EQ(awsRequest.GetAcceptanceType(), Aws::GameLift::Model::AcceptanceType::ACCEPT); + EXPECT_TRUE(strcmp(awsRequest.GetTicketId().c_str(), request.m_ticketId.c_str()) == 0); + EXPECT_EQ(awsRequest.GetPlayerIds().size(), request.m_playerIds.size()); + EXPECT_TRUE(strcmp(awsRequest.GetPlayerIds().begin()->c_str(), request.m_playerIds.begin()->c_str()) == 0); +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithBaseType_GetFalseResult) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(AzFramework::AcceptMatchRequest()); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithoutTicketId_GetFalseResult) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { "dummyPlayerId" }; + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithoutPlayerIds_GetFalseResult) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { "dummyPlayerId" }; + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithValidAttributes_GetTrueResult) +{ + + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_ticketId = "dummyTicketId"; + request.m_playerIds = { "dummyPlayerId" }; + + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request); + EXPECT_TRUE(result); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp new file mode 100644 index 0000000000..6da932828c --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include + +using namespace AWSGameLift; + +using AWSGameLiftStartMatchmakingActivityTest = AWSGameLiftClientFixture; + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, BuildAWSGameLiftStartMatchmakingRequest_Call_GetExpectedResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + request.m_ticketId = "dummyTicketId"; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; + player.m_playerId = "dummyPlayerId"; + player.m_team = "dummyTeam"; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + auto awsRequest = StartMatchmakingActivity::BuildAWSGameLiftStartMatchmakingRequest(request); + + EXPECT_TRUE(strcmp(awsRequest.GetConfigurationName().c_str(), request.m_configurationName.c_str()) == 0); + EXPECT_TRUE(strcmp(awsRequest.GetTicketId().c_str(), request.m_ticketId.c_str()) == 0); + + EXPECT_TRUE(awsRequest.GetPlayers().size() == request.m_players.size()); + EXPECT_TRUE(strcmp(awsRequest.GetPlayers()[0].GetPlayerId().c_str(), request.m_players[0].m_playerId.c_str()) == 0); + EXPECT_TRUE(strcmp(awsRequest.GetPlayers()[0].GetTeam().c_str(), request.m_players[0].m_team.c_str()) == 0); + + EXPECT_TRUE(awsRequest.GetPlayers()[0].GetLatencyInMs().size() == request.m_players[0].m_latencyInMs.size()); + EXPECT_TRUE(strcmp(awsRequest.GetPlayers()[0].GetLatencyInMs().begin()->first.c_str(), request.m_players[0].m_latencyInMs.begin()->first.c_str()) == 0); + EXPECT_TRUE(awsRequest.GetPlayers()[0].GetLatencyInMs().begin()->second == request.m_players[0].m_latencyInMs.begin()->second); + + EXPECT_TRUE(awsRequest.GetPlayers()[0].GetPlayerAttributes().size() == request.m_players[0].m_playerAttributes.size()); + EXPECT_TRUE(strcmp(awsRequest.GetPlayers()[0].GetPlayerAttributes().begin()->first.c_str(), request.m_players[0].m_playerAttributes.begin()->first.c_str()) == 0); + EXPECT_TRUE(strcmp(awsRequest.GetPlayers()[0].GetPlayerAttributes().begin()->second.GetS().c_str(), "test") == 0); +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithBaseType_GetFalseResult) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(AzFramework::StartMatchmakingRequest()); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithoutConfigurationName_GetFalseResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_ticketId = "dummyTicketId"; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; + player.m_playerId = "dummyPlayerId"; + player.m_team = "dummyTeam"; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithoutPlayers_GetFalseResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + request.m_ticketId = "dummyTicketId"; + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithoutPlayerId_GetFalseResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + request.m_ticketId = "dummyTicketId"; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; + player.m_team = "dummyTeam"; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithInvalidPlayerAttribute_GetFalseResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + request.m_ticketId = "dummyTicketId"; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"A\": \"test\"}"; + player.m_playerId = "dummyPlayerId"; + player.m_team = "dummyTeam"; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithoutTicketId_GetTrueResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_configurationName = "dummyConfiguration"; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; + player.m_playerId = "dummyPlayerId"; + player.m_team = "dummyTeam"; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(request); + EXPECT_TRUE(result); +} + +TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_CallWithValidParameters_GetTrueResult) +{ + AWSGameLiftStartMatchmakingRequest request; + request.m_ticketId = "dummyTicketId"; + request.m_configurationName = "dummyConfiguration"; + + AWSGameLiftPlayer player; + player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; + player.m_playerId = "dummyPlayerId"; + player.m_team = "dummyTeam"; + player.m_latencyInMs["us-east-1"] = 10; + request.m_players.emplace_back(player); + + auto result = StartMatchmakingActivity::ValidateStartMatchmakingRequest(request); + EXPECT_TRUE(result); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp new file mode 100644 index 0000000000..ba0e40c7e6 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include + +using namespace AWSGameLift; + +using AWSGameLiftStopMatchmakingActivityTest = AWSGameLiftClientFixture; + +TEST_F(AWSGameLiftStopMatchmakingActivityTest, BuildAWSGameLiftStopMatchmakingRequest_Call_GetExpectedResult) +{ + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = "dummyTicketId"; + auto awsRequest = StopMatchmakingActivity::BuildAWSGameLiftStopMatchmakingRequest(request); + EXPECT_TRUE(strcmp(awsRequest.GetTicketId().c_str(), request.m_ticketId.c_str()) == 0); +} + +TEST_F(AWSGameLiftStopMatchmakingActivityTest, ValidateStopMatchmakingRequest_CallWithoutTicketId_GetFalseResult) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = StopMatchmakingActivity::ValidateStopMatchmakingRequest(AzFramework::StopMatchmakingRequest()); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftStopMatchmakingActivityTest, ValidateStopMatchmakingRequest_CallWithTicketId_GetTrueResult) +{ + AWSGameLiftStopMatchmakingRequest request; + request.m_ticketId = "dummyTicketId"; + auto result = StopMatchmakingActivity::ValidateStopMatchmakingRequest(request); + EXPECT_TRUE(result); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake index d3b9d8d1b9..cd54414cc1 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -7,6 +7,8 @@ # set(FILES + ../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h + ../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h Include/Request/AWSGameLiftAcceptMatchRequest.h Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h @@ -18,6 +20,8 @@ set(FILES Include/Request/IAWSGameLiftRequests.h Source/Activity/AWSGameLiftActivityUtils.cpp Source/Activity/AWSGameLiftActivityUtils.h + Source/Activity/AWSGameLiftAcceptMatchActivity.cpp + Source/Activity/AWSGameLiftAcceptMatchActivity.h Source/Activity/AWSGameLiftCreateSessionActivity.cpp Source/Activity/AWSGameLiftCreateSessionActivity.h Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp @@ -30,6 +34,10 @@ set(FILES Source/Activity/AWSGameLiftSearchSessionsActivity.h Source/AWSGameLiftClientLocalTicketTracker.cpp Source/AWSGameLiftClientLocalTicketTracker.h + Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp + Source/Activity/AWSGameLiftStartMatchmakingActivity.h + Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp + Source/Activity/AWSGameLiftStopMatchmakingActivity.h Source/AWSGameLiftClientManager.cpp Source/AWSGameLiftClientManager.h Source/AWSGameLiftClientSystemComponent.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake index 130ed28e4e..3e73dd8a0a 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake @@ -7,10 +7,13 @@ # set(FILES + Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp Tests/Activity/AWSGameLiftCreateSessionActivityTest.cpp Tests/Activity/AWSGameLiftCreateSessionOnQueueActivityTest.cpp Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp Tests/Activity/AWSGameLiftSearchSessionsActivityTest.cpp + Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp + Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp Tests/AWSGameLiftClientFixture.h Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp Tests/AWSGameLiftClientManagerTest.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h new file mode 100644 index 0000000000..1fbdadc8bf --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AWSGameLift +{ + //! AWSGameLiftPlayer + //! Information on each player to be matched + //! This information must include a player ID, and may contain player attributes and latency data to be used in the matchmaking process + //! After a successful match, Player objects contain the name of the team the player is assigned to + struct AWSGameLiftPlayer + { + AZ_RTTI(AWSGameLiftPlayer, "{B62C118E-C55D-4903-8ECB-E58E8CA613C4}"); + static void Reflect(AZ::ReflectContext* context); + + AWSGameLiftPlayer() = default; + virtual ~AWSGameLiftPlayer() = default; + + // A map of region names to latencies in millseconds, that indicates + // the amount of latency that a player experiences when connected to AWS Regions + AZStd::unordered_map m_latencyInMs; + + // A collection of key:value pairs containing player information for use in matchmaking + // Player attribute keys must match the playerAttributes used in a matchmaking rule set + // Example: {"skill": "{\"N\": 23}", "gameMode": "{\"S\": \"deathmatch\"}"} + AZStd::unordered_map m_playerAttributes; + + // A unique identifier for a player + AZStd::string m_playerId; + + // Name of the team that the player is assigned to in a match + AZStd::string m_team; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp new file mode 100644 index 0000000000..80a3915a6b --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include + +namespace AWSGameLift +{ + void AWSGameLiftPlayer::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("latencyInMs", &AWSGameLiftPlayer::m_latencyInMs) + ->Field("playerAttributes", &AWSGameLiftPlayer::m_playerAttributes) + ->Field("playerId", &AWSGameLiftPlayer::m_playerId) + ->Field("team", &AWSGameLiftPlayer::m_team); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("AWSGameLiftPlayer", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_latencyInMs, "LatencyInMs", + "A set of values, expressed in milliseconds, that indicates the amount of latency that" + "a player experiences when connected to AWS Regions") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_playerAttributes, "PlayerAttributes", + "A collection of key:value pairs containing player information for use in matchmaking") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_playerId, "PlayerId", + "A unique identifier for a player") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_team, "Team", + "Name of the team that the player is assigned to in a match"); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AWSGameLiftPlayer") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("LatencyInMs", BehaviorValueProperty(&AWSGameLiftPlayer::m_latencyInMs)) + ->Property("PlayerAttributes", BehaviorValueProperty(&AWSGameLiftPlayer::m_playerAttributes)) + ->Property("PlayerId", BehaviorValueProperty(&AWSGameLiftPlayer::m_playerId)) + ->Property("Team", BehaviorValueProperty(&AWSGameLiftPlayer::m_team)); + } + } +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt b/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt index 798c6a6a25..d05ec46b52 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt @@ -17,6 +17,7 @@ ly_add_target( awsgamelift_server_files.cmake INCLUDE_DIRECTORIES PUBLIC + ../AWSGameLiftCommon/Include Include PRIVATE ../AWSGameLiftCommon/Source @@ -54,6 +55,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) awsgamelift_server_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE + ../AWSGameLiftCommon/Include + ../AWSGameLiftCommon/Source Tests Source BUILD_DEPENDENCIES diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h index e5b14319a8..777086e633 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h @@ -9,9 +9,11 @@ #pragma once #include -#include +#include +#include #include -#include + +#include namespace AWSGameLift { @@ -26,8 +28,21 @@ namespace AWSGameLift virtual ~IAWSGameLiftServerRequests() = default; //! Notify GameLift that the server process is ready to host a game session. - //! @return Whether the ProcessReady notification is sent to GameLift. + //! @return True if the ProcessReady notification is sent to GameLift successfully, false otherwise virtual bool NotifyGameLiftProcessReady() = 0; + + //! Sends a request to find new players for open slots in a game session created with FlexMatch. + //! @param ticketId Unique identifier for match backfill request ticket + //! @param players A set of data representing all players who are currently in the game session, + //! if not provided, system will use lazy loaded game session data which is not guaranteed to + //! be accurate (no latency data either) + //! @return True if StartMatchBackfill succeeds, false otherwise + virtual bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector& players) = 0; + + //! Cancels an active match backfill request that was created with StartMatchBackfill + //! @param ticketId Unique identifier of the backfill request ticket to be canceled + //! @return True if StopMatchBackfill succeeds, false otherwise + virtual bool StopMatchBackfill(const AZStd::string& ticketId) = 0; }; // IAWSGameLiftServerRequests EBus wrapper for scripting diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp index e739933ab2..70d0063b61 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include @@ -112,6 +115,7 @@ namespace AWSGameLift { propertiesOutput = propertiesOutput.substr(0, propertiesOutput.size() - 1); // Trim last comma to fit array format } + sessionConfig.m_matchmakingData = gameSession.GetMatchmakerData().c_str(); sessionConfig.m_sessionId = gameSession.GetGameSessionId().c_str(); sessionConfig.m_ipAddress = gameSession.GetIpAddress().c_str(); sessionConfig.m_maxPlayer = gameSession.GetMaximumPlayerSessionCount(); @@ -133,6 +137,276 @@ namespace AWSGameLift return sessionConfig; } + bool AWSGameLiftServerManager::BuildServerMatchBackfillPlayer( + const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer) + { + outBackfillPlayer.SetPlayerId(player.m_playerId.c_str()); + outBackfillPlayer.SetTeam(player.m_team.c_str()); + for (auto latencyPair : player.m_latencyInMs) + { + outBackfillPlayer.AddLatencyInMs(latencyPair.first.c_str(), latencyPair.second); + } + + for (auto attributePair : player.m_playerAttributes) + { + Aws::GameLift::Server::Model::AttributeValue playerAttribute; + rapidjson::Document attributeDocument; + rapidjson::ParseResult parseResult = attributeDocument.Parse(attributePair.second.c_str()); + // player attribute json content should always be a single member object + if (parseResult && attributeDocument.IsObject() && attributeDocument.MemberCount() == 1) + { + if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSServerTypeName)) && + attributeDocument.MemberBegin()->value.IsString()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue( + attributeDocument.MemberBegin()->value.GetString()); + } + else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeNTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeNServerTypeName)) && + attributeDocument.MemberBegin()->value.IsNumber()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue( + attributeDocument.MemberBegin()->value.GetDouble()); + } + else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSDMTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName)) && + attributeDocument.MemberBegin()->value.IsObject()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap(); + for (auto iter = attributeDocument.MemberBegin()->value.MemberBegin(); + iter != attributeDocument.MemberBegin()->value.MemberEnd(); iter++) + { + if (iter->name.IsString() && iter->value.IsNumber()) + { + playerAttribute.AddStringAndDouble(iter->name.GetString(), iter->value.GetDouble()); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), "String double map key must be string type and value must be number type"); + return false; + } + } + } + else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSLTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName)) && + attributeDocument.MemberBegin()->value.IsArray()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList(); + for (auto iter = attributeDocument.MemberBegin()->value.Begin(); + iter != attributeDocument.MemberBegin()->value.End(); iter++) + { + if (iter->IsString()) + { + playerAttribute.AddString(iter->GetString()); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), "String list element must be string type"); + return false; + } + } + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), "S, N, SDM or SLM is expected as attribute type."); + return false; + } + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), rapidjson::GetParseError_En(parseResult.Code())); + return false; + } + outBackfillPlayer.AddPlayerAttribute(attributePair.first.c_str(), playerAttribute); + } + return true; + } + + AZStd::vector AWSGameLiftServerManager::GetActiveServerMatchBackfillPlayers() + { + AZStd::vector activePlayers; + // Keep processing only when game session has matchmaking data + if (IsMatchmakingDataValid()) + { + auto activePlayerSessions = GetActivePlayerSessions(); + for (auto playerSession : activePlayerSessions) + { + AWSGameLiftPlayer player; + if (BuildActiveServerMatchBackfillPlayer(playerSession.GetPlayerId().c_str(), player)) + { + activePlayers.push_back(player); + } + } + } + return activePlayers; + } + + bool AWSGameLiftServerManager::IsMatchmakingDataValid() + { + return m_matchmakingData.IsObject() && + m_matchmakingData.HasMember(AWSGameLiftMatchmakingConfigurationKeyName) && + m_matchmakingData.HasMember(AWSGameLiftMatchmakingTeamsKeyName); + } + + AZStd::vector AWSGameLiftServerManager::GetActivePlayerSessions() + { + Aws::GameLift::Server::Model::DescribePlayerSessionsRequest describeRequest; + describeRequest.SetGameSessionId(m_gameSession.GetGameSessionId()); + describeRequest.SetPlayerSessionStatusFilter( + Aws::GameLift::Server::Model::PlayerSessionStatusMapper::GetNameForPlayerSessionStatus( + Aws::GameLift::Server::Model::PlayerSessionStatus::ACTIVE)); + int maxPlayerSession = m_gameSession.GetMaximumPlayerSessionCount(); + + AZStd::vector activePlayerSessions; + if (maxPlayerSession <= AWSGameLiftDescribePlayerSessionsPageSize) + { + describeRequest.SetLimit(maxPlayerSession); + auto outcome = m_gameLiftServerSDKWrapper->DescribePlayerSessions(describeRequest); + if (outcome.IsSuccess()) + { + for (auto playerSession : outcome.GetResult().GetPlayerSessions()) + { + activePlayerSessions.push_back(playerSession); + } + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftDescribePlayerSessionsErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + } + } + else + { + describeRequest.SetLimit(AWSGameLiftDescribePlayerSessionsPageSize); + while (true) + { + auto outcome = m_gameLiftServerSDKWrapper->DescribePlayerSessions(describeRequest); + if (outcome.IsSuccess()) + { + for (auto playerSession : outcome.GetResult().GetPlayerSessions()) + { + activePlayerSessions.push_back(playerSession); + } + if (outcome.GetResult().GetNextToken().empty()) + { + break; + } + else + { + describeRequest.SetNextToken(outcome.GetResult().GetNextToken()); + } + } + else + { + activePlayerSessions.clear(); + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftDescribePlayerSessionsErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + break; + } + } + } + return activePlayerSessions; + } + + bool AWSGameLiftServerManager::BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer) + { + // As data is from GameLift service, assume it is always in correct format + rapidjson::Value& teams = m_matchmakingData[AWSGameLiftMatchmakingTeamsKeyName]; + + // Iterate through teams to find target player + for (rapidjson::SizeType teamIndex = 0; teamIndex < teams.Size(); ++teamIndex) + { + rapidjson::Value& players = teams[teamIndex][AWSGameLiftMatchmakingPlayersKeyName]; + + // Iterate through players under the team to find target player + for (rapidjson::SizeType playerIndex = 0; playerIndex < players.Size(); ++playerIndex) + { + if (std::strcmp(players[playerIndex][AWSGameLiftMatchmakingPlayerIdKeyName].GetString(), playerId.c_str()) == 0) + { + outPlayer.m_playerId = playerId; + outPlayer.m_team = teams[teamIndex][AWSGameLiftMatchmakingTeamNameKeyName].GetString(); + // Get player attributes if target player has + if (players[playerIndex].HasMember(AWSGameLiftMatchmakingPlayerAttributesKeyName)) + { + BuildServerMatchBackfillPlayerAttributes( + players[playerIndex][AWSGameLiftMatchmakingPlayerAttributesKeyName], outPlayer); + } + } + else + { + return false; + } + } + } + return true; + } + + void AWSGameLiftServerManager::BuildServerMatchBackfillPlayerAttributes( + const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer) + { + for (auto iter = playerAttributes.MemberBegin(); iter != playerAttributes.MemberEnd(); iter++) + { + AZStd::string attributeName = iter->name.GetString(); + + rapidjson::StringBuffer jsonStringBuffer; + rapidjson::Writer writer(jsonStringBuffer); + iter->value[AWSGameLiftMatchmakingPlayerAttributeValueKeyName].Accept(writer); + AZStd::string attributeType = iter->value[AWSGameLiftMatchmakingPlayerAttributeTypeKeyName].GetString(); + AZStd::string attributeValue = AZStd::string::format("{\"%s\": %s}", + attributeType.c_str(), jsonStringBuffer.GetString()); + + outPlayer.m_playerAttributes.emplace(attributeName, attributeValue); + } + } + + bool AWSGameLiftServerManager::BuildStartMatchBackfillRequest( + const AZStd::string& ticketId, + const AZStd::vector& players, + Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest) + { + outRequest.SetGameSessionArn(m_gameSession.GetGameSessionId()); + outRequest.SetMatchmakingConfigurationArn(m_matchmakingData[AWSGameLiftMatchmakingConfigurationKeyName].GetString()); + if (!ticketId.empty()) + { + outRequest.SetTicketId(ticketId.c_str()); + } + + AZStd::vector requestPlayers(players); + if (players.size() == 0) + { + requestPlayers = GetActiveServerMatchBackfillPlayers(); + } + for (auto player : requestPlayers) + { + Aws::GameLift::Server::Model::Player backfillPlayer; + if (BuildServerMatchBackfillPlayer(player, backfillPlayer)) + { + outRequest.AddPlayer(backfillPlayer); + } + else + { + return false; + } + } + return true; + } + + void AWSGameLiftServerManager::BuildStopMatchBackfillRequest( + const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest) + { + outRequest.SetGameSessionArn(m_gameSession.GetGameSessionId()); + outRequest.SetMatchmakingConfigurationArn(m_matchmakingData[AWSGameLiftMatchmakingConfigurationKeyName].GetString()); + if (!ticketId.empty()) + { + outRequest.SetTicketId(ticketId.c_str()); + } + } + AZ::IO::Path AWSGameLiftServerManager::GetExternalSessionCertificate() { // TODO: Add support to get TLS cert file path @@ -238,7 +512,7 @@ namespace AWSGameLift Aws::GameLift::Server::ProcessParameters processReadyParameter = Aws::GameLift::Server::ProcessParameters( AZStd::bind(&AWSGameLiftServerManager::OnStartGameSession, this, AZStd::placeholders::_1), - AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this), + AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this, AZStd::placeholders::_1), AZStd::bind(&AWSGameLiftServerManager::OnProcessTerminate, this), AZStd::bind(&AWSGameLiftServerManager::OnHealthCheck, this), desc.m_port, Aws::GameLift::Server::LogParameters(logPaths)); @@ -260,6 +534,7 @@ namespace AWSGameLift void AWSGameLiftServerManager::OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession) { + UpdateGameSessionData(gameSession); AzFramework::SessionConfig sessionConfig = BuildSessionConfig(gameSession); bool createSessionResult = true; @@ -311,10 +586,19 @@ namespace AWSGameLift return m_serverSDKInitialized && healthCheckResult; } - void AWSGameLiftServerManager::OnUpdateGameSession() + void AWSGameLiftServerManager::OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession) { - // TODO: Perform game-specific tasks to prep for newly matched players - return; + Aws::GameLift::Server::Model::UpdateReason updateReason = updateGameSession.GetUpdateReason(); + if (updateReason == Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED) + { + UpdateGameSessionData(updateGameSession.GetGameSession()); + } + AzFramework::SessionConfig sessionConfig = BuildSessionConfig(updateGameSession.GetGameSession()); + + AzFramework::SessionNotificationBus::Broadcast( + &AzFramework::SessionNotifications::OnUpdateSessionBegin, + sessionConfig, + Aws::GameLift::Server::Model::UpdateReasonMapper::GetNameForUpdateReason(updateReason).c_str()); } bool AWSGameLiftServerManager::RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId) @@ -340,6 +624,92 @@ namespace AWSGameLift m_gameLiftServerSDKWrapper = AZStd::move(gameLiftServerSDKWrapper); } + bool AWSGameLiftServerManager::StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector& players) + { + if (!m_serverSDKInitialized) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage); + return false; + } + + if (!IsMatchmakingDataValid()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingDataMissingErrorMessage); + return false; + } + + Aws::GameLift::Server::Model::StartMatchBackfillRequest request; + if (!BuildStartMatchBackfillRequest(ticketId, players, request)) + { + return false; + } + + AZ_TracePrintf(AWSGameLiftServerManagerName, "Starting match backfill %s ...", ticketId.c_str()); + auto outcome = m_gameLiftServerSDKWrapper->StartMatchBackfill(request); + if (!outcome.IsSuccess()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftStartMatchBackfillErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + return false; + } + else + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "StartMatchBackfill request against Amazon GameLift service is complete."); + return true; + } + } + + bool AWSGameLiftServerManager::StopMatchBackfill(const AZStd::string& ticketId) + { + if (!m_serverSDKInitialized) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage); + return false; + } + + if (!IsMatchmakingDataValid()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingDataMissingErrorMessage); + return false; + } + + Aws::GameLift::Server::Model::StopMatchBackfillRequest request; + BuildStopMatchBackfillRequest(ticketId, request); + + AZ_TracePrintf(AWSGameLiftServerManagerName, "Stopping match backfill %s ...", ticketId.c_str()); + auto outcome = m_gameLiftServerSDKWrapper->StopMatchBackfill(request); + if (!outcome.IsSuccess()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftStopMatchBackfillErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + return false; + } + else + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "StopMatchBackfill request against Amazon GameLift service is complete."); + return true; + } + } + + void AWSGameLiftServerManager::UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession) + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "Lazy loading game session and matchmaking data from Amazon GameLift service ..."); + m_gameSession = Aws::GameLift::Server::Model::GameSession(gameSession); + if (m_gameSession.GetMatchmakerData().empty()) + { + m_matchmakingData.Parse("{}"); + } + else + { + rapidjson::ParseResult parseResult = m_matchmakingData.Parse(m_gameSession.GetMatchmakerData().c_str()); + if (!parseResult) + { + AZ_Error(AWSGameLiftServerManagerName, false, + AWSGameLiftMatchmakingDataInvalidErrorMessage, rapidjson::GetParseError_En(parseResult.Code())); + } + } + } + bool AWSGameLiftServerManager::ValidatePlayerJoinSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) { uint32_t playerConnectionId = playerConnectionConfig.m_playerConnectionId; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h index c0f1c00bea..fa2f783eca 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h @@ -11,11 +11,15 @@ #include #include +#include +#include #include #include #include #include #include + +#include #include namespace AWSGameLift @@ -66,6 +70,36 @@ namespace AWSGameLift "Invalid player connection config, player connection id: %d, player session id: %s"; static constexpr const char AWSGameLiftServerRemovePlayerSessionErrorMessage[] = "Failed to notify GameLift that the player with the player session id %s has disconnected from the server process. ErrorMessage: %s"; + static constexpr const char AWSGameLiftMatchmakingDataInvalidErrorMessage[] = + "Failed to parse GameLift matchmaking data. ErrorMessage: %s"; + static constexpr const char AWSGameLiftMatchmakingDataMissingErrorMessage[] = + "GameLift matchmaking data is missing or invalid to parse."; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage[] = + "Failed to build player %s attributes. ErrorMessage: %s"; + static constexpr const char AWSGameLiftDescribePlayerSessionsErrorMessage[] = + "Failed to describe player sessions. ErrorMessage: %s"; + static constexpr const char AWSGameLiftStartMatchBackfillErrorMessage[] = + "Failed to start match backfill. ErrorMessage: %s"; + static constexpr const char AWSGameLiftStopMatchBackfillErrorMessage[] = + "Failed to stop match backfill. ErrorMessage: %s"; + + static constexpr const char AWSGameLiftMatchmakingConfigurationKeyName[] = "matchmakingConfigurationArn"; + static constexpr const char AWSGameLiftMatchmakingTeamsKeyName[] = "teams"; + static constexpr const char AWSGameLiftMatchmakingTeamNameKeyName[] = "name"; + static constexpr const char AWSGameLiftMatchmakingPlayersKeyName[] = "players"; + static constexpr const char AWSGameLiftMatchmakingPlayerIdKeyName[] = "playerId"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributesKeyName[] = "attributes"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeTypeKeyName[] = "attributeType"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeValueKeyName[] = "valueAttribute"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSTypeName[] = "S"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSServerTypeName[] = "STRING"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNTypeName[] = "N"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNServerTypeName[] = "NUMBER"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLTypeName[] = "SL"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName[] = "STRING_LIST"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMTypeName[] = "SDM"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName[] = "STRING_DOUBLE_MAP"; + static constexpr const uint16_t AWSGameLiftDescribePlayerSessionsPageSize = 30; AWSGameLiftServerManager(); virtual ~AWSGameLiftServerManager(); @@ -78,6 +112,8 @@ namespace AWSGameLift // AWSGameLiftServerRequestBus interface implementation bool NotifyGameLiftProcessReady() override; + bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector& players) override; + bool StopMatchBackfill(const AZStd::string& ticketId) override; // ISessionHandlingProviderRequests interface implementation void HandleDestroySession() override; @@ -92,18 +128,48 @@ namespace AWSGameLift //! Add connected player session id. bool AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig); + //! Get active server player data from lazy loaded game session for server match backfill + AZStd::vector GetActiveServerMatchBackfillPlayers(); + + //! Update local game session data to latest one + void UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession); + private: //! Build the serverProcessDesc with appropriate server port number and log paths. GameLiftServerProcessDesc BuildGameLiftServerProcessDesc(); + //! Build active server player data from lazy loaded game session based on player id + bool BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer); + + //! Build server player attribute data from lazy load matchmaking data + void BuildServerMatchBackfillPlayerAttributes(const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer); + + //! Build server player data for server match backfill + bool BuildServerMatchBackfillPlayer(const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer); + + //! Build start match backfill request for StartMatchBackfill operation + bool BuildStartMatchBackfillRequest( + const AZStd::string& ticketId, + const AZStd::vector& players, + Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest); + + //! Build stop match backfill request for StopMatchBackfill operation + void BuildStopMatchBackfillRequest(const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest); + //! Build session config by using AWS GameLift Server GameSession Model. AzFramework::SessionConfig BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession); + //! Check whether matchmaking data is in proper format + bool IsMatchmakingDataValid(); + + //! Fetch active player sessions in game session. + AZStd::vector GetActivePlayerSessions(); + //! Callback function that the GameLift service invokes to activate a new game session. void OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession); //! Callback function that the GameLift service invokes to pass an updated game session object to the server process. - void OnUpdateGameSession(); + void OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession); //! Callback function that the server process or GameLift service invokes to force the server process to shut down. void OnProcessTerminate(); @@ -125,5 +191,12 @@ namespace AWSGameLift using PlayerConnectionId = uint32_t; using PlayerSessionId = AZStd::string; AZStd::unordered_map m_connectedPlayers; + + // Lazy loaded game session and matchmaking data + Aws::GameLift::Server::Model::GameSession m_gameSession; + // Matchmaking data contains a unique match ID, it identifies the matchmaker that created the match + // and describes the teams, team assignments, and players. + // Reference https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-server.html#match-server-data + rapidjson::Document m_matchmakingData; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp index 7e216f33be..d51c8eb8cb 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp @@ -22,6 +22,12 @@ namespace AWSGameLift return Aws::GameLift::Server::ActivateGameSession(); } + Aws::GameLift::DescribePlayerSessionsOutcome GameLiftServerSDKWrapper::DescribePlayerSessions( + const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest) + { + return Aws::GameLift::Server::DescribePlayerSessions(describePlayerSessionsRequest); + } + Aws::GameLift::Server::InitSDKOutcome GameLiftServerSDKWrapper::InitSDK() { return Aws::GameLift::Server::InitSDK(); @@ -69,4 +75,17 @@ namespace AWSGameLift { return Aws::GameLift::Server::RemovePlayerSession(playerSessionId.c_str()); } + + Aws::GameLift::StartMatchBackfillOutcome GameLiftServerSDKWrapper::StartMatchBackfill( + const Aws::GameLift::Server::Model::StartMatchBackfillRequest& startMatchBackfillRequest) + { + return Aws::GameLift::Server::StartMatchBackfill(startMatchBackfillRequest); + } + + Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::StopMatchBackfill( + const Aws::GameLift::Server::Model::StopMatchBackfillRequest& stopMatchBackfillRequest) + { + return Aws::GameLift::Server::StopMatchBackfill(stopMatchBackfillRequest); + } + } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h index a656087206..e56366d75a 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h @@ -33,6 +33,14 @@ namespace AWSGameLift //! @return Returns a generic outcome consisting of success or failure with an error message. virtual Aws::GameLift::GenericOutcome ActivateGameSession(); + //! Retrieves player session data, including settings, session metadata, and player data. + //! Use this action to get information for a single player session, + //! for all player sessions in a game session, or for all player sessions associated with a single player ID. + //! @param describePlayerSessionsRequest The request object describing which player sessions to retrieve. + //! @return If successful, returns a DescribePlayerSessionsOutcome object containing a set of player session objects that fit the request parameters. + virtual Aws::GameLift::DescribePlayerSessionsOutcome DescribePlayerSessions( + const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest); + //! Initializes the GameLift SDK. //! Should be called when the server starts, before any GameLift-dependent initialization happens. //! @return If successful, returns an InitSdkOutcome object indicating that the server process is ready to call ProcessReady(). @@ -56,5 +64,16 @@ namespace AWSGameLift //! @param playerSessionId Unique ID issued by the Amazon GameLift service in response to a call to the AWS SDK Amazon GameLift API action CreatePlayerSession. //! @return Returns a generic outcome consisting of success or failure with an error message. virtual Aws::GameLift::GenericOutcome RemovePlayerSession(const AZStd::string& playerSessionId); + + //! Sends a request to find new players for open slots in a game session created with FlexMatch. + //! When the match has been successfully, backfilled updated matchmaker data will be sent to the OnUpdateGameSession callback. + //! @param startMatchBackfillRequest This data type is used to send a matchmaking backfill request. + //! @return Returns a StartMatchBackfillOutcome object with the match backfill ticket or failure with an error message. + virtual Aws::GameLift::StartMatchBackfillOutcome StartMatchBackfill(const Aws::GameLift::Server::Model::StartMatchBackfillRequest& startMatchBackfillRequest); + + //! Cancels an active match backfill request that was created with StartMatchBackfill + //! @param stopMatchBackfillRequest This data type is used to cancel a matchmaking backfill request. + //! @return Returns a generic outcome consisting of success or failure with an error message. + virtual Aws::GameLift::GenericOutcome StopMatchBackfill(const Aws::GameLift::Server::Model::StopMatchBackfillRequest& stopMatchBackfillRequest); }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp index 2c8929b297..d172734ec0 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp @@ -16,6 +16,136 @@ namespace UnitTest { + static constexpr const char TEST_SERVER_MATCHMAKING_DATA[] = +R"({ + "matchId":"testmatchid", + "matchmakingConfigurationArn":"testmatchconfig", + "teams":[ + {"name":"testteam", + "players":[ + {"playerId":"testplayer", + "attributes":{ + "skills":{ + "attributeType":"STRING_DOUBLE_MAP", + "valueAttribute":{"test1":10.0,"test2":20.0,"test3":30.0,"test4":40.0} + }, + "mode":{ + "attributeType":"STRING", + "valueAttribute":"testmode" + }, + "level":{ + "attributeType":"NUMBER", + "valueAttribute":10.0 + }, + "items":{ + "attributeType":"STRING_LIST", + "valueAttribute":["test1","test2","test3"] + } + }} + ]} + ] +})"; + + Aws::GameLift::Server::Model::StartMatchBackfillRequest GetTestStartMatchBackfillRequest() + { + Aws::GameLift::Server::Model::StartMatchBackfillRequest request; + request.SetMatchmakingConfigurationArn("testmatchconfig"); + Aws::GameLift::Server::Model::Player player; + player.SetPlayerId("testplayer"); + player.SetTeam("testteam"); + player.AddPlayerAttribute("mode", Aws::GameLift::Server::Model::AttributeValue("testmode")); + player.AddPlayerAttribute("level", Aws::GameLift::Server::Model::AttributeValue(10.0)); + auto sdmValue = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap(); + sdmValue.AddStringAndDouble("test1", 10.0); + player.AddPlayerAttribute("skills", sdmValue); + auto slValue = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList(); + slValue.AddString("test1"); + player.AddPlayerAttribute("items", slValue); + player.AddLatencyInMs("testregion", 10); + request.AddPlayer(player); + request.SetTicketId("testticket"); + return request; + } + + AWSGameLiftPlayer GetTestGameLiftPlayer() + { + AWSGameLiftPlayer player; + player.m_team = "testteam"; + player.m_playerId = "testplayer"; + player.m_playerAttributes.emplace("mode", "{\"S\": \"testmode\"}"); + player.m_playerAttributes.emplace("level", "{\"N\": 10.0}"); + player.m_playerAttributes.emplace("skills", "{\"SDM\": {\"test1\":10.0}}"); + player.m_playerAttributes.emplace("items", "{\"SL\": [\"test1\"]}"); + player.m_latencyInMs.emplace("testregion", 10); + return player; + } + + MATCHER_P(StartMatchBackfillRequestMatcher, expectedRequest, "") + { + // Custome matcher for checking the SearchSessionsResponse type argument. + AZ_UNUSED(result_listener); + if (strcmp(arg.GetGameSessionArn().c_str(), expectedRequest.GetGameSessionArn().c_str()) != 0) + { + return false; + } + if (strcmp(arg.GetMatchmakingConfigurationArn().c_str(), expectedRequest.GetMatchmakingConfigurationArn().c_str()) != 0) + { + return false; + } + if (strcmp(arg.GetTicketId().c_str(), expectedRequest.GetTicketId().c_str()) != 0) + { + return false; + } + if (arg.GetPlayers().size() != expectedRequest.GetPlayers().size()) + { + return false; + } + for (int playerIndex = 0; playerIndex < expectedRequest.GetPlayers().size(); playerIndex++) + { + auto actualPlayerAttributes = arg.GetPlayers()[playerIndex].GetPlayerAttributes(); + auto expectedPlayerAttributes = expectedRequest.GetPlayers()[playerIndex].GetPlayerAttributes(); + if (actualPlayerAttributes.size() != expectedPlayerAttributes.size()) + { + return false; + } + for (auto attributePair : expectedPlayerAttributes) + { + if (actualPlayerAttributes.find(attributePair.first) == actualPlayerAttributes.end()) + { + return false; + } + if (!(attributePair.second.GetType() == actualPlayerAttributes[attributePair.first].GetType() && + (attributePair.second.GetS() == actualPlayerAttributes[attributePair.first].GetS() || + attributePair.second.GetN() == actualPlayerAttributes[attributePair.first].GetN() || + attributePair.second.GetSL() == actualPlayerAttributes[attributePair.first].GetSL() || + attributePair.second.GetSDM() == actualPlayerAttributes[attributePair.first].GetSDM()))) + { + return false; + } + } + + auto actualLatencies = arg.GetPlayers()[playerIndex].GetLatencyInMs(); + auto expectedLatencies = expectedRequest.GetPlayers()[playerIndex].GetLatencyInMs(); + if (actualLatencies.size() != expectedLatencies.size()) + { + return false; + } + for (auto latencyPair : expectedLatencies) + { + if (actualLatencies.find(latencyPair.first) == actualLatencies.end()) + { + return false; + } + if (latencyPair.second != actualLatencies[latencyPair.first]) + { + return false; + } + } + } + + return true; + } + class SessionNotificationsHandlerMock : public AzFramework::SessionNotificationBus::Handler { @@ -33,6 +163,7 @@ namespace UnitTest MOCK_METHOD0(OnSessionHealthCheck, bool()); MOCK_METHOD1(OnCreateSessionBegin, bool(const AzFramework::SessionConfig&)); MOCK_METHOD0(OnDestroySessionBegin, bool()); + MOCK_METHOD2(OnUpdateSessionBegin, void(const AzFramework::SessionConfig&, const AZStd::string&)); }; class GameLiftServerManagerTest @@ -228,6 +359,64 @@ namespace UnitTest AZ_TEST_STOP_TRACE_SUPPRESSION(1); } + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithUnknownReason_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + Aws::GameLift::Server::Model::GameSession(), + Aws::GameLift::Server::Model::UpdateReason::UNKNOWN, + "testticket")); + } + + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithEmptyMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + Aws::GameLift::Server::Model::GameSession(), + Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, + "testticket")); + } + + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithValidMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + Aws::GameLift::Server::Model::GameSession gameSession; + gameSession.SetMatchmakerData(TEST_SERVER_MATCHMAKING_DATA); + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket")); + } + + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithInvalidMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + Aws::GameLift::Server::Model::GameSession gameSession; + gameSession.SetMatchmakerData("{invalid}"); + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket")); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + TEST_F(GameLiftServerManagerTest, ValidatePlayerJoinSession_CallWithInvalidConnectionConfig_GetFalseResultAndExpectedErrorLog) { AZ_TEST_START_TRACE_SUPPRESSION; @@ -425,4 +614,331 @@ namespace UnitTest } AZ_TEST_STOP_TRACE_SUPPRESSION(testThreadNumber - 1); // The player is only disconnected once. } + + TEST_F(GameLiftServerManagerTest, UpdateGameSessionData_CallWithInvalidMatchmakingData_GetExpectedError) + { + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->SetupTestMatchmakingData("{invalid}"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithInvalidMatchmakingData_GetEmptyResult) + { + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->SetupTestMatchmakingData("{invalid}"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithEmptyMatchmakingData_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(""); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallButDescribePlayerError_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::GameLiftError error; + Aws::GameLift::DescribePlayerSessionsOutcome errorOutcome(error); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(errorOutcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallButNoActivePlayer_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithValidMatchmakingData_GetExpectedResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::PlayerSession playerSession; + playerSession.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + result.AddPlayerSessions(playerSession); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.size() == 1); + EXPECT_TRUE(actualResult[0].m_team == "testteam"); + EXPECT_TRUE(actualResult[0].m_playerId == "testplayer"); + EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayerButError_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50); + + Aws::GameLift::GameLiftError error; + Aws::GameLift::DescribePlayerSessionsOutcome errorOutcome(error); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(errorOutcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayer_GetExpectedResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50); + + Aws::GameLift::Server::Model::PlayerSession playerSession1; + playerSession1.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result1; + result1.AddPlayerSessions(playerSession1); + result1.SetNextToken("testtoken"); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome1(result1); + + Aws::GameLift::Server::Model::PlayerSession playerSession2; + playerSession2.SetPlayerId("playernotinmatch"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result2; + result2.AddPlayerSessions(playerSession2); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome2(result2); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .WillOnce(Return(successOutcome1)) + .WillOnce(Return(successOutcome2)); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.size() == 1); + EXPECT_TRUE(actualResult[0].m_team == "testteam"); + EXPECT_TRUE(actualResult[0].m_playerId == "testplayer"); + EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_SDKNotInitialized_GetExpectedError) + { + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithEmptyMatchmakingData_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(""); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithInvalidPlayerAttribute_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{invalid}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongPlayerAttributeType_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SDM\": [\"test1\"]}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithUnexpectedPlayerAttributeType_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"UNEXPECTED\": [\"test1\"]}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongSLPlayerAttributeValue_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SL\": [10.0]}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongSDMPlayerAttributeValue_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SDM\": {10.0: \"test1\"}}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithValidPlayersData_GetExpectedResult) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::StartMatchBackfillResult backfillResult; + Aws::GameLift::StartMatchBackfillOutcome backfillSuccessOutcome(backfillResult); + Aws::GameLift::Server::Model::StartMatchBackfillRequest request = GetTestStartMatchBackfillRequest(); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(StartMatchBackfillRequestMatcher(request))) + .Times(1) + .WillOnce(Return(backfillSuccessOutcome)); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {testPlayer}); + EXPECT_TRUE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithoutGivingPlayersData_GetExpectedResult) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::PlayerSession playerSession; + playerSession.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + result.AddPlayerSessions(playerSession); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + Aws::GameLift::Server::Model::StartMatchBackfillResult backfillResult; + Aws::GameLift::StartMatchBackfillOutcome backfillSuccessOutcome(backfillResult); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(backfillSuccessOutcome)); + + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + EXPECT_TRUE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallButStartBackfillFail_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::PlayerSession playerSession; + playerSession.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + result.AddPlayerSessions(playerSession); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + Aws::GameLift::GameLiftError error; + Aws::GameLift::StartMatchBackfillOutcome errorOutcome(error); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(errorOutcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_SDKNotInitialized_GetExpectedError) + { + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallWithEmptyMatchmakingData_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(""); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallAndSuccessOutcome_GetExpectedResult) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StopMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(Aws::GameLift::GenericOutcome(nullptr))); + + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + EXPECT_TRUE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallButErrorOutcome_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StopMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(Aws::GameLift::GenericOutcome())); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } } // namespace UnitTest diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h index 24336680b0..7ab9c51cd1 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -40,17 +41,25 @@ namespace UnitTest MOCK_METHOD1(AcceptPlayerSession, GenericOutcome(const std::string&)); MOCK_METHOD0(ActivateGameSession, GenericOutcome()); + MOCK_METHOD1(DescribePlayerSessions, DescribePlayerSessionsOutcome( + const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest&)); MOCK_METHOD0(InitSDK, Server::InitSDKOutcome()); MOCK_METHOD1(ProcessReady, GenericOutcome(const Server::ProcessParameters& processParameters)); MOCK_METHOD0(ProcessEnding, GenericOutcome()); MOCK_METHOD1(RemovePlayerSession, GenericOutcome(const AZStd::string& playerSessionId)); MOCK_METHOD0(GetTerminationTime, AZStd::string()); + MOCK_METHOD1(StartMatchBackfill, StartMatchBackfillOutcome( + const Aws::GameLift::Server::Model::StartMatchBackfillRequest&)); + MOCK_METHOD1(StopMatchBackfill, GenericOutcome( + const Aws::GameLift::Server::Model::StopMatchBackfillRequest&)); + GenericOutcome ProcessReadyMock(const Server::ProcessParameters& processParameters) { m_healthCheckFunc = processParameters.getOnHealthCheck(); m_onStartGameSessionFunc = processParameters.getOnStartGameSession(); m_onProcessTerminateFunc = processParameters.getOnProcessTerminate(); + m_onUpdateGameSessionFunc = processParameters.getOnUpdateGameSession(); GenericOutcome successOutcome(nullptr); return successOutcome; @@ -59,6 +68,7 @@ namespace UnitTest AZStd::function m_healthCheckFunc; AZStd::function m_onProcessTerminateFunc; AZStd::function m_onStartGameSessionFunc; + AZStd::function m_onUpdateGameSessionFunc; }; class AWSGameLiftServerManagerMock @@ -78,12 +88,25 @@ namespace UnitTest m_gameLiftServerSDKWrapperMockPtr = nullptr; } + void SetupTestMatchmakingData(const AZStd::string& matchmakingData, int maxPlayer = 10) + { + m_testGameSession.SetMatchmakerData(matchmakingData.c_str()); + m_testGameSession.SetMaximumPlayerSessionCount(maxPlayer); + UpdateGameSessionData(m_testGameSession); + } + bool AddConnectedTestPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) { return AddConnectedPlayer(playerConnectionConfig); } + AZStd::vector GetTestServerMatchBackfillPlayers() + { + return GetActiveServerMatchBackfillPlayers(); + } + NiceMock* m_gameLiftServerSDKWrapperMockPtr; + Aws::GameLift::Server::Model::GameSession m_testGameSession; }; class AWSGameLiftServerSystemComponentMock diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake index 95b5e1d2d2..9039c9943e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake @@ -7,6 +7,8 @@ # set(FILES + ../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h + ../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h Include/Request/IAWSGameLiftServerRequests.h Source/AWSGameLiftServerManager.cpp diff --git a/Gems/AWSMetrics/Code/Source/AWSMetricsSystemComponent.cpp b/Gems/AWSMetrics/Code/Source/AWSMetricsSystemComponent.cpp index 6850fe694b..7f320f398a 100644 --- a/Gems/AWSMetrics/Code/Source/AWSMetricsSystemComponent.cpp +++ b/Gems/AWSMetrics/Code/Source/AWSMetricsSystemComponent.cpp @@ -74,7 +74,12 @@ namespace AWSMetrics { behaviorContext->EBus("AWSMetricsRequestBus", "Generate and submit metrics to the metrics analytics pipeline") ->Attribute(AZ::Script::Attributes::Category, "AWSMetrics") - ->Event("SubmitMetrics", &AWSMetricsRequestBus::Events::SubmitMetrics) + ->Event( + "SubmitMetrics", &AWSMetricsRequestBus::Events::SubmitMetrics, + { { { "Metrics Attributes list", "The list of metrics attributes to submit." }, + { "Event priority", "Priority of the event. Defaults to 0, which is highest priority." }, + { "Event source override", "Event source used to override the default, 'AWSMetricGem'." }, + { "Buffer metrics", "Whether to buffer metrics and send them in a batch." } } }) ->Event("FlushMetrics", &AWSMetricsRequestBus::Events::FlushMetrics) ; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt b/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt index 1463124e27..982ec43715 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt @@ -63,13 +63,10 @@ ly_add_target( 3rdParty::Qt::Widgets 3rdParty::Qt::Gui 3rdParty::astc-encoder - 3rdParty::etc2comp - 3rdParty::PVRTexTool 3rdParty::squish-ccr - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::ISPCTexComp 3rdParty::ilmbase - Legacy::CryCommon AZ::AzFramework AZ::AzToolsFramework AZ::AzQtComponents diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/PixelFormats.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/PixelFormats.h index fbde69abb2..4da996dbde 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/PixelFormats.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/PixelFormats.h @@ -39,16 +39,7 @@ namespace ImageProcessingAtom ePixelFormat_ASTC_10x8, ePixelFormat_ASTC_10x10, ePixelFormat_ASTC_12x10, - ePixelFormat_ASTC_12x12, - //Formats supported by PowerVR GPU. Mainly for ios devices. - ePixelFormat_PVRTC2, //2bpp - ePixelFormat_PVRTC4, //4bpp - //formats for opengl and opengles 3.0 (android devices) - ePixelFormat_EAC_R11, //one channel unsigned data - ePixelFormat_EAC_RG11, //two channel unsigned data - ePixelFormat_ETC2, //Compresses RGB888 data, it taks 4x4 groups of pixel data and compresses each into a 64-bit - ePixelFormat_ETC2a1, //Compresses RGB888A1 data, it taks 4x4 groups of pixel data and compresses each into a 64-bit - ePixelFormat_ETC2a, //Compresses RGBA8888 data with full alpha support + ePixelFormat_ASTC_12x12, // Standardized Compressed DXGI Formats (DX10+) // Data in these compressed formats is hardware decodable on all DX10 chips, and manageable with the DX10-API. @@ -88,7 +79,6 @@ namespace ImageProcessingAtom }; bool IsASTCFormat(EPixelFormat fmt); - bool IsETCFormat(EPixelFormat fmt); } // namespace ImageProcessingAtom namespace AZ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp index 924c01b916..a4ba846f1b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp @@ -137,25 +137,6 @@ namespace ImageProcessingAtom return nullptr; } - const PresetSettings* BuilderSettingManager::GetPreset(const AZ::Uuid presetId, const PlatformName& platform, AZStd::string_view* settingsFilePathOut) - { - AZStd::lock_guard lock(m_presetMapLock); - - for (const auto& namePreset : m_presets) - { - if (namePreset.second.m_multiPreset.GetPresetId() == presetId) - { - if (settingsFilePathOut) - { - *settingsFilePathOut = namePreset.second.m_presetFilePath; - } - return namePreset.second.m_multiPreset.GetPreset(platform); - } - } - - return nullptr; - } - const BuilderSettings* BuilderSettingManager::GetBuilderSetting(const PlatformName& platform) { if (m_builderSettings.find(platform) != m_builderSettings.end()) @@ -180,26 +161,12 @@ namespace ImageProcessingAtom return platforms; } - const AZStd::map >& BuilderSettingManager::GetPresetFilterMap() + const AZStd::map >& BuilderSettingManager::GetPresetFilterMap() { AZStd::lock_guard lock(m_presetMapLock); return m_presetFilterMap; } - const AZ::Uuid BuilderSettingManager::GetPresetIdFromName(const PresetName& presetName) - { - AZStd::lock_guard lock(m_presetMapLock); - - auto itr = m_presets.find(presetName); - - if (itr != m_presets.end()) - { - return itr->second.m_multiPreset.GetPresetId(); - } - - return AZ::Uuid::CreateNull(); - } - const PresetName BuilderSettingManager::GetPresetNameFromId(const AZ::Uuid& presetId) { AZStd::lock_guard lock(m_presetMapLock); @@ -212,7 +179,7 @@ namespace ImageProcessingAtom } } - return "Unknown"; + return {}; } void BuilderSettingManager::ClearSettings() @@ -296,7 +263,7 @@ namespace ImageProcessingAtom AZ_Warning("Image Processing", presetName == preset.GetPresetName(), "Preset file name '%s' is not" " same as preset name '%s'. Using preset file name as preset name", - filePath.toUtf8().data(), preset.GetPresetName().c_str()); + filePath.toUtf8().data(), preset.GetPresetName().GetCStr()); preset.SetPresetName(presetName); @@ -442,8 +409,20 @@ namespace ImageProcessingAtom return AZStd::string(); } - AZ::Uuid BuilderSettingManager::GetSuggestedPreset(AZStd::string_view imageFilePath, IImageObjectPtr imageFromFile) + bool BuilderSettingManager::IsValidPreset(PresetName presetName) const { + if (presetName.IsEmpty()) + { + return false; + } + + return m_presets.find(presetName) != m_presets.end(); + } + + PresetName BuilderSettingManager::GetSuggestedPreset(AZStd::string_view imageFilePath, IImageObjectPtr imageFromFile) + { + PresetName emptyPreset; + //load the image to get its size for later use IImageObjectPtr image = imageFromFile; //if the input image is empty we will try to load it from the path @@ -454,34 +433,37 @@ namespace ImageProcessingAtom if (image == nullptr) { - return AZ::Uuid::CreateNull(); + return emptyPreset; } //get file mask of this image file AZStd::string fileMask = GetFileMask(imageFilePath); - AZ::Uuid outPreset = AZ::Uuid::CreateNull(); + PresetName outPreset = emptyPreset; //check default presets for some file masks if (m_defaultPresetByFileMask.find(fileMask) != m_defaultPresetByFileMask.end()) { outPreset = m_defaultPresetByFileMask[fileMask]; + if (!IsValidPreset(outPreset)) + { + outPreset = emptyPreset; + } } //use the preset filter map to find - if (outPreset.IsNull() && !fileMask.empty()) + if (outPreset.IsEmpty() && !fileMask.empty()) { auto& presetFilterMap = GetPresetFilterMap(); if (presetFilterMap.find(fileMask) != presetFilterMap.end()) { - AZStd::string presetName = *(presetFilterMap.find(fileMask)->second.begin()); - outPreset = GetPresetIdFromName(presetName); + outPreset = *(presetFilterMap.find(fileMask)->second.begin()); } } const PresetSettings* presetInfo = nullptr; - if (!outPreset.IsNull()) + if (!outPreset.IsEmpty()) { presetInfo = GetPreset(outPreset); @@ -491,12 +473,12 @@ namespace ImageProcessingAtom // If it's not a latitude-longitude map or it doesn't match any cubemap layouts then reset its preset if (!IsValidLatLongMap(image) && CubemapLayout::GetCubemapLayoutInfo(image) == nullptr) { - outPreset = AZ::Uuid::CreateNull(); + outPreset = emptyPreset; } } } - if (outPreset.IsNull()) + if (outPreset == emptyPreset) { if (image->GetAlphaContent() == EAlphaContent::eAlphaContent_Absent) { @@ -521,7 +503,7 @@ namespace ImageProcessingAtom } else { - AZ_Warning("Image Processing", false, "Image dimensions are not compatible with preset '%s'. The default preset will be used.", presetInfo->m_name.c_str()); + AZ_Warning("Image Processing", false, "Image dimensions are not compatible with preset '%s'. The default preset will be used.", presetInfo->m_name.GetCStr()); } } @@ -540,7 +522,7 @@ namespace ImageProcessingAtom for (const auto& element : m_presets) { const PresetEntry& presetEntry = element.second; - AZStd::string fileName = AZStd::string::format("%s.preset", presetEntry.m_multiPreset.GetDefaultPreset().m_name.c_str()); + AZStd::string fileName = AZStd::string::format("%s.preset", presetEntry.m_multiPreset.GetDefaultPreset().m_name.GetCStr()); AZStd::string filePath; if (!AzFramework::StringFunc::Path::Join(outputFolder.data(), fileName.c_str(), filePath)) { @@ -552,7 +534,7 @@ namespace ImageProcessingAtom if (!result.IsSuccess()) { AZ_Warning("Image Processing", false, "Failed to save preset '%s' to file '%s'. Error: %s", - presetEntry.m_multiPreset.GetDefaultPreset().m_name.c_str(), filePath.c_str(), result.GetError().c_str()); + presetEntry.m_multiPreset.GetDefaultPreset().m_name.GetCStr(), filePath.c_str(), result.GetError().c_str()); } } } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.h index b0a7e103be..3bbb71ea43 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.h @@ -49,7 +49,6 @@ namespace ImageProcessingAtom static void DestroyInstance(); static void Reflect(AZ::ReflectContext* context); - const PresetSettings* GetPreset(const AZ::Uuid presetId, const PlatformName& platform = "", AZStd::string_view* settingsFilePathOut = nullptr); const PresetSettings* GetPreset(const PresetName& presetName, const PlatformName& platform = "", AZStd::string_view* settingsFilePathOut = nullptr); const BuilderSettings* GetBuilderSetting(const PlatformName& platform); @@ -60,10 +59,7 @@ namespace ImageProcessingAtom //! Return A map of preset settings based on their filemasks. //! @key filemask string, empty string means no filemask //! @value set of preset setting names supporting the specified filemask - const AZStd::map>& GetPresetFilterMap(); - - //!Find preset id list based on the preset name. - const AZ::Uuid GetPresetIdFromName(const PresetName& presetName); + const AZStd::map>& GetPresetFilterMap(); //! Find preset name based on the preset id. const PresetName GetPresetNameFromId(const AZ::Uuid& presetId); @@ -84,8 +80,10 @@ namespace ImageProcessingAtom //! Find a suitable preset a given image file. //! @param imageFilePath: Filepath string of the image file. The function may load the image from the path for better detection //! @param image: an optional image object which can be used for preset selection if there is no match based file mask. - //! @return suggested preset uuid. - AZ::Uuid GetSuggestedPreset(AZStd::string_view imageFilePath, IImageObjectPtr image = nullptr); + //! @return suggested preset name. + PresetName GetSuggestedPreset(AZStd::string_view imageFilePath, IImageObjectPtr image = nullptr); + + bool IsValidPreset(PresetName presetName) const; bool DoesSupportPlatform(AZStd::string_view platformId); @@ -131,27 +129,27 @@ namespace ImageProcessingAtom // Builder settings for each platform AZStd::map m_builderSettings; - AZStd::map m_presets; + AZStd::unordered_map m_presets; // Cached list of presets mapped by their file masks. // @Key file mask, use empty string to indicate all presets without filtering // @Value set of preset names that matches the file mask - AZStd::map > m_presetFilterMap; + AZStd::map > m_presetFilterMap; // A mutex to protect when modifying any map in this manager AZStd::recursive_mutex m_presetMapLock; // Default presets for certain file masks - AZStd::map m_defaultPresetByFileMask; + AZStd::map m_defaultPresetByFileMask; // Default preset for none power of two image - AZ::Uuid m_defaultPresetNonePOT; + PresetName m_defaultPresetNonePOT; // Default preset for power of two - AZ::Uuid m_defaultPreset; + PresetName m_defaultPreset; // Default preset for power of two with alpha - AZ::Uuid m_defaultPresetAlpha; + PresetName m_defaultPresetAlpha; // Image builder's version AZStd::string m_analysisFingerprint; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/CubemapSettings.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/CubemapSettings.h index 281075b80c..9135a0f4d1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/CubemapSettings.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/CubemapSettings.h @@ -43,14 +43,14 @@ namespace ImageProcessingAtom // generate an IBL specular cubemap bool m_generateIBLSpecular = false; - // the UUID of the preset to be used for generating the IBL specular cubemap - AZ::Uuid m_iblSpecularPreset = AZ::Uuid::CreateNull(); + // the name of the preset to be used for generating the IBL specular cubemap + PresetName m_iblSpecularPreset; // generate an IBL diffuse cubemap bool m_generateIBLDiffuse = false; - // the UUID of the preset to be used for generating the IBL diffuse cubemap - AZ::Uuid m_iblDiffusePreset = AZ::Uuid::CreateNull(); + // the name of the preset to be used for generating the IBL diffuse cubemap + PresetName m_iblDiffusePreset; // "cm_requiresconvolve", convolve the cubemap mips bool m_requiresConvolve = true; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/ImageProcessingDefines.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/ImageProcessingDefines.h index d735608db5..e421715995 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/ImageProcessingDefines.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/ImageProcessingDefines.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -38,7 +39,8 @@ namespace ImageProcessingAtom #define STRING_OUTCOME_ERROR(error) AZ::Failure(AZStd::string(error)) // Common typedefs (with dependent forward-declarations) - typedef AZStd::string PlatformName, PresetName, FileMask; + typedef AZStd::string PlatformName, FileMask; + typedef AZ::Name PresetName; typedef AZStd::vector PlatformNameVector; typedef AZStd::list PlatformNameList; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp index 7ffabc45a9..3812b86026 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp @@ -109,13 +109,6 @@ namespace ImageProcessingAtom ->Value("ASTC_10x10", EPixelFormat::ePixelFormat_ASTC_10x10) ->Value("ASTC_12x10", EPixelFormat::ePixelFormat_ASTC_12x10) ->Value("ASTC_12x12", EPixelFormat::ePixelFormat_ASTC_12x12) - ->Value("PVRTC2", EPixelFormat::ePixelFormat_PVRTC2) - ->Value("PVRTC4", EPixelFormat::ePixelFormat_PVRTC4) - ->Value("EAC_R11", EPixelFormat::ePixelFormat_EAC_R11) - ->Value("EAC_RG11", EPixelFormat::ePixelFormat_EAC_RG11) - ->Value("ETC2", EPixelFormat::ePixelFormat_ETC2) - ->Value("ETC2a1", EPixelFormat::ePixelFormat_ETC2a1) - ->Value("ETC2a", EPixelFormat::ePixelFormat_ETC2a) ->Value("BC1", EPixelFormat::ePixelFormat_BC1) ->Value("BC1a", EPixelFormat::ePixelFormat_BC1a) ->Value("BC3", EPixelFormat::ePixelFormat_BC3) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h index b7ef72753e..941437bbf4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h @@ -31,7 +31,8 @@ namespace ImageProcessingAtom bool operator== (const PresetSettings& other) const; static void Reflect(AZ::ReflectContext* context); - //unique id for the preset + // unique id for the preset + // this uuid will be deprecated. The preset name will be used as an unique id for the preset AZ::Uuid m_uuid = 0; PresetName m_name; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.cpp index 863b3358c5..29d9b21775 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.cpp @@ -23,7 +23,7 @@ namespace ImageProcessingAtom const char* TextureSettings::ExtensionName = ".assetinfo"; TextureSettings::TextureSettings() - : m_preset(0) + : m_presetId(0) , m_sizeReduceLevel(0) , m_suppressEngineReduce(false) , m_enableMipmap(true) @@ -45,8 +45,9 @@ namespace ImageProcessingAtom if (serialize) { serialize->Class() - ->Version(1) - ->Field("PresetID", &TextureSettings::m_preset) + ->Version(2) + ->Field("PresetID", &TextureSettings::m_presetId) + ->Field("Preset", &TextureSettings::m_preset) ->Field("SizeReduceLevel", &TextureSettings::m_sizeReduceLevel) ->Field("EngineReduce", &TextureSettings::m_suppressEngineReduce) ->Field("EnableMipmap", &TextureSettings::m_enableMipmap) @@ -169,9 +170,9 @@ namespace ImageProcessingAtom return 0.5f - fVal / 100.0f; } - void TextureSettings::ApplyPreset(AZ::Uuid presetId) + void TextureSettings::ApplyPreset(PresetName presetName) { - const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(presetId); + const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(presetName); if (presetSetting != nullptr) { m_sizeReduceLevel = presetSetting->m_sizeReduceLevel; @@ -181,11 +182,11 @@ namespace ImageProcessingAtom m_mipGenType = presetSetting->m_mipmapSetting->m_type; } - m_preset = presetId; + m_preset = presetName; } else { - AZ_Error("Image Processing", false, "Cannot set an invalid preset %s!", presetId.ToString().c_str()); + AZ_Error("Image Processing", false, "Cannot set an invalid preset %s!", presetName.GetCStr()); } } @@ -199,6 +200,14 @@ namespace ImageProcessingAtom } textureSettingPtrOut = *loadedTextureSettingPtr; + + // In old format, the preset name doesn't exist. Using preset id to get preset name + // We can remove this when we fully deprecate the preset uuid + if (textureSettingPtrOut.m_preset.IsEmpty()) + { + textureSettingPtrOut.m_preset = BuilderSettingManager::Instance()->GetPresetNameFromId(textureSettingPtrOut.m_presetId); + } + return AZ::Success(AZStd::string()); } @@ -216,7 +225,7 @@ namespace ImageProcessingAtom { MultiplatformTextureSettings settings; PlatformNameList platformsList = BuilderSettingManager::Instance()->GetPlatformList(); - AZ::Uuid suggestedPreset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilepath); + PresetName suggestedPreset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilepath); for (PlatformName& platform : platformsList) { TextureSettings textureSettings; @@ -234,7 +243,7 @@ namespace ImageProcessingAtom if (overrideIter == baseTextureSettings.m_platfromOverrides.end()) { return STRING_OUTCOME_ERROR(AZStd::string::format("TextureSettings preset [%s] does not have override for platform [%s]", - baseTextureSettings.m_preset.ToString().c_str(), platformName.c_str())); + baseTextureSettings.m_preset.GetCStr(), platformName.c_str())); } AZ::DataPatch& platformOverride = const_cast(overrideIter->second); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.h index 5bf4fa0d86..24a2f07bfb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/TextureSettings.h @@ -43,7 +43,7 @@ namespace ImageProcessingAtom /** * Apply value of some preset settings to this texture settings */ - void ApplyPreset(AZ::Uuid presetId); + void ApplyPreset(PresetName presetName); /** * Performs a comprehensive comparison between two TextureSettings instances. @@ -116,7 +116,10 @@ namespace ImageProcessingAtom static const size_t s_MaxMipMaps = 6; // uuid of selected preset for this texture - AZ::Uuid m_preset; + // We are deprecating preset UUID and switching to preset name as an unique id + AZ::Uuid m_presetId; + + PresetName m_preset; // texture size reduce level. the value of this variable will override the same variable in PresetSettings unsigned int m_sizeReduceLevel; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp index 1dd18faaf3..ac5340eaab 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include namespace ImageProcessingAtom @@ -42,26 +40,6 @@ namespace ImageProcessingAtom } } - // Both ETC2Compressor and PVRTCCompressor can process ETC formats - // According to Mobile team, Etc2Com is faster than PVRTexLib, so we check with ETC2Compressor before PVRTCCompressor - // Note: with the test I have done, I found out it cost similar time for both Etc2Com and PVRTexLib to compress - // a 2048x2048 test texture to EAC_R11 and EAC_RG11. It was around 7 minutes for EAC_R11 and 14 minutes for EAC_RG11 - if (ETC2Compressor::IsCompressedPixelFormatSupported(fmt)) - { - if (isCompressing || (!isCompressing && ETC2Compressor::DoesSupportDecompress(fmt))) - { - return ICompressorPtr(new ETC2Compressor()); - } - } - - if (PVRTCCompressor::IsCompressedPixelFormatSupported(fmt)) - { - if (isCompressing || (!isCompressing && PVRTCCompressor::DoesSupportDecompress(fmt))) - { - return ICompressorPtr(new PVRTCCompressor()); - } - } - return nullptr; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ETC2.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ETC2.cpp deleted file mode 100644 index 73108d5502..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ETC2.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace ImageProcessingAtom -{ - //limited to 1 thread because AP requires so. We may change to n when AP allocate n thread to a job in the furture - static const int MAX_COMP_JOBS = 1; - static const int MIN_COMP_JOBS = 1; - static const float ETC_LOW_EFFORT_LEVEL = 25.0f; - static const float ETC_MED_EFFORT_LEVEL = 40.0f; - static const float ETC_HIGH_EFFORT_LEVEL = 80.0f; - - //Grab the Etc2Comp specific pixel format enum - static Etc::Image::Format FindEtc2PixelFormat(EPixelFormat fmt) - { - switch (fmt) - { - case ePixelFormat_EAC_RG11: - return Etc::Image::Format::RG11; - case ePixelFormat_EAC_R11: - return Etc::Image::Format::R11; - case ePixelFormat_ETC2: - return Etc::Image::Format::RGB8; - case ePixelFormat_ETC2a1: - return Etc::Image::Format::RGB8A1; - case ePixelFormat_ETC2a: - return Etc::Image::Format::RGBA8; - default: - return Etc::Image::Format::FORMATS; - } - } - - //Get the errmetric required for the compression - static Etc::ErrorMetric FindErrMetric(Etc::Image::Format fmt) - { - switch (fmt) - { - case Etc::Image::Format::RG11: - return Etc::ErrorMetric::NORMALXYZ; - case Etc::Image::Format::R11: - return Etc::ErrorMetric::NUMERIC; - case Etc::Image::Format::RGB8: - return Etc::ErrorMetric::RGBX; - case Etc::Image::Format::RGBA8: - case Etc::Image::Format::RGB8A1: - return Etc::ErrorMetric::RGBA; - default: - return Etc::ErrorMetric::ERROR_METRICS; - } - } - - //Convert to sRGB format - static Etc::Image::Format FindGammaEtc2PixelFormat(Etc::Image::Format fmt) - { - switch (fmt) - { - case Etc::Image::Format::RGB8: - return Etc::Image::Format::SRGB8; - case Etc::Image::Format::RGBA8: - return Etc::Image::Format::SRGBA8; - case Etc::Image::Format::RGB8A1: - return Etc::Image::Format::SRGB8A1; - default: - return Etc::Image::Format::FORMATS; - } - } - - bool ETC2Compressor::IsCompressedPixelFormatSupported(EPixelFormat fmt) - { - return (FindEtc2PixelFormat(fmt) != Etc::Image::Format::FORMATS); - } - - bool ETC2Compressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt) - { - //for uncompress format - if (fmt == ePixelFormat_R8G8B8A8) - { - return true; - } - return false; - } - - EPixelFormat ETC2Compressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, [[maybe_unused]] EPixelFormat uncompressedfmt) const - { - return ePixelFormat_R8G8B8A8; - } - - bool ETC2Compressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst) - { - return false; - } - - ColorSpace ETC2Compressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const - { - return ColorSpace::autoSelect; - } - - const char* ETC2Compressor::GetName() const - { - return "ETC2Compressor"; - } - - IImageObjectPtr ETC2Compressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, - const CompressOption* compressOption) const - { - //validate input - EPixelFormat fmtSrc = srcImage->GetPixelFormat(); - - //src format need to be uncompressed and dst format need to compressed. - if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst)) - { - return nullptr; - } - - IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst)); - - //determinate compression quality - ICompressor::EQuality quality = ICompressor::eQuality_Normal; - //get setting from compression option - if (compressOption) - { - quality = compressOption->compressQuality; - } - - float qualityEffort = 0.0f; - switch (quality) - { - case eQuality_Preview: - case eQuality_Fast: - { - qualityEffort = ETC_LOW_EFFORT_LEVEL; - break; - } - case eQuality_Normal: - { - qualityEffort = ETC_MED_EFFORT_LEVEL; - break; - } - default: - { - qualityEffort = ETC_HIGH_EFFORT_LEVEL; - } - } - - Etc::Image::Format dstEtc2Format = FindEtc2PixelFormat(fmtDst); - if (srcImage->GetImageFlags() & EIF_SRGBRead) - { - dstEtc2Format = FindGammaEtc2PixelFormat(dstEtc2Format); - } - - //use to read pixel data from src image - IPixelOperationPtr pixelOp = CreatePixelOperation(fmtSrc); - //get count of bytes per pixel for images - AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc)->bitsPerBlock / 8; - - const AZ::u32 mipCount = dstImage->GetMipCount(); - for (AZ::u32 mip = 0; mip < mipCount; ++mip) - { - const AZ::u32 width = srcImage->GetWidth(mip); - const AZ::u32 height = srcImage->GetHeight(mip); - - // Prepare source data - AZ::u8* srcMem; - AZ::u32 srcPitch; - srcImage->GetImagePointer(mip, srcMem, srcPitch); - const AZ::u32 pixelCount = srcImage->GetPixelCount(mip); - - Etc::ColorFloatRGBA* rgbaPixels = new Etc::ColorFloatRGBA[pixelCount]; - Etc::ColorFloatRGBA* rgbaPixelPtr = rgbaPixels; - float r, g, b, a; - for (AZ::u32 pixelIdx = 0; pixelIdx < pixelCount; pixelIdx++, srcMem += pixelBytes, rgbaPixelPtr++) - { - pixelOp->GetRGBA(srcMem, r, g, b, a); - rgbaPixelPtr->fA = a; - rgbaPixelPtr->fR = r; - rgbaPixelPtr->fG = g; - rgbaPixelPtr->fB = b; - } - - //Call into etc2Comp lib to compress. https://medium.com/@duhroach/building-a-blazing-fast-etc2-compressor-307f3e9aad99 - Etc::ErrorMetric errMetric = FindErrMetric(dstEtc2Format); - unsigned char* paucEncodingBits; - unsigned int uiEncodingBitsBytes; - unsigned int uiExtendedWidth; - unsigned int uiExtendedHeight; - int iEncodingTime_ms; - - Etc::Encode(reinterpret_cast(rgbaPixels), - width, height, - dstEtc2Format, - errMetric, - qualityEffort, - MIN_COMP_JOBS, - MAX_COMP_JOBS, - &paucEncodingBits, &uiEncodingBitsBytes, - &uiExtendedWidth, &uiExtendedHeight, - &iEncodingTime_ms); - - AZ::u8* dstMem; - AZ::u32 dstPitch; - dstImage->GetImagePointer(mip, dstMem, dstPitch); - - memcpy(dstMem, paucEncodingBits, uiEncodingBitsBytes); - delete[] rgbaPixels; - } - - return dstImage; - } - - IImageObjectPtr ETC2Compressor::DecompressImage(IImageObjectPtr srcImage, [[maybe_unused]] EPixelFormat fmtDst) const - { - //etc2Comp doesn't support decompression - //Since PVRTexLib support ETC formats too. It may take over the decompression. - return nullptr; - } -} // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ETC2.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ETC2.h deleted file mode 100644 index 9ab5a164a5..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ETC2.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -namespace ImageProcessingAtom -{ - class ETC2Compressor - : public ICompressor - { - public: - static bool IsCompressedPixelFormatSupported(EPixelFormat fmt); - static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt); - static bool DoesSupportDecompress(EPixelFormat fmtDst); - - IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override; - IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override; - - EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override; - ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final; - const char* GetName() const final; - - }; -} // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp deleted file mode 100644 index f3a630e8c1..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - - -namespace ImageProcessingAtom -{ - // Note: PVRTexLib supports ETC formats, PVRTC formats and BC formats - // We haven't tested the performace to compress BC formats compare to CTSquisher - // For PVRTC formats, we only added PVRTC 1 support for now - // The compression for ePVRTPF_EAC_R11 and ePVRTPF_EAC_RG11 are very slow. It takes 7 and 14 minutes for a 2048x2048 texture. - EPVRTPixelFormat FindPvrPixelFormat(EPixelFormat fmt) - { - switch (fmt) - { - case ePixelFormat_PVRTC2: - return ePVRTPF_PVRTCI_2bpp_RGBA; - case ePixelFormat_PVRTC4: - return ePVRTPF_PVRTCI_4bpp_RGBA; - case ePixelFormat_EAC_R11: - return ePVRTPF_EAC_R11; - case ePixelFormat_EAC_RG11: - return ePVRTPF_EAC_RG11; - case ePixelFormat_ETC2: - return ePVRTPF_ETC2_RGB; - case ePixelFormat_ETC2a1: - return ePVRTPF_ETC2_RGB_A1; - case ePixelFormat_ETC2a: - return ePVRTPF_ETC2_RGBA; - default: - return ePVRTPF_NumCompressedPFs; - } - } - - bool PVRTCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt) - { - return (FindPvrPixelFormat(fmt) != ePVRTPF_NumCompressedPFs); - } - - bool PVRTCCompressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt) - { - //for uncompress format - if (fmt == ePixelFormat_R8G8B8A8) - { - return true; - } - return false; - } - - EPixelFormat PVRTCCompressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, [[maybe_unused]] EPixelFormat uncompressedfmt) const - { - return ePixelFormat_R8G8B8A8; - } - - ColorSpace PVRTCCompressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const - { - return ColorSpace::autoSelect; - } - - const char* PVRTCCompressor::GetName() const - { - return "PVRTCCompressor"; - } - - bool PVRTCCompressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst) - { - return true; - } - - IImageObjectPtr PVRTCCompressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, - const CompressOption* compressOption) const - { - //validate input - EPixelFormat fmtSrc = srcImage->GetPixelFormat(); - - //src format need to be uncompressed and dst format need to compressed. - if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst)) - { - return nullptr; - } - - IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst)); - - //determinate compression quality - pvrtexture::ECompressorQuality internalQuality = pvrtexture::eETCFast; - ICompressor::EQuality quality = ICompressor::eQuality_Normal; - AZ::Vector3 uniformWeights = AZ::Vector3(0.3333f, 0.3334f, 0.3333f); - AZ::Vector3 weights = uniformWeights; - bool isUniform = true; - //get setting from compression option - if (compressOption) - { - quality = compressOption->compressQuality; - weights = compressOption->rgbWeight; - isUniform = (weights == uniformWeights); - } - - if (IsETCFormat(fmtDst)) - { - if ((quality <= eQuality_Normal) && isUniform) - { - internalQuality = pvrtexture::eETCFast; - } - else if (quality <= eQuality_Normal) - { - internalQuality = pvrtexture::eETCNormal; - } - else if (isUniform) - { - internalQuality = pvrtexture::eETCSlow; - } - else - { - internalQuality = pvrtexture::eETCSlow; - } - } - else - { - if (quality == eQuality_Preview) - { - internalQuality = pvrtexture::ePVRTCFastest; - } - else if (quality == eQuality_Fast) - { - internalQuality = pvrtexture::ePVRTCFast; - } - else if (quality == eQuality_Normal) - { - internalQuality = pvrtexture::ePVRTCNormal; - } - else - { - internalQuality = pvrtexture::ePVRTCHigh; - } - } - - // setup color space - EPVRTColourSpace cspace = ePVRTCSpacelRGB; - if (srcImage->GetImageFlags() & EIF_SRGBRead) - { - cspace = ePVRTCSpacesRGB; - } - - //setup src texture for compression - const pvrtexture::PixelType srcPixelType('r', 'g', 'b', 'a', 8, 8, 8, 8); - const AZ::u32 dstMips = dstImage->GetMipCount(); - for (AZ::u32 mip = 0; mip < dstMips; ++mip) - { - const AZ::u32 width = srcImage->GetWidth(mip); - const AZ::u32 height = srcImage->GetHeight(mip); - - // Prepare source data - AZ::u8* srcMem; - uint32 srcPitch; - srcImage->GetImagePointer(mip, srcMem, srcPitch); - - const pvrtexture::CPVRTextureHeader srcHeader( - srcPixelType.PixelTypeID, // AZ::u64 u64PixelFormat, - width, // uint32 u32Height=1, - height, // uint32 u32Width=1, - 1, // uint32 u32Depth=1, - 1, // uint32 u32NumMipMaps=1, - 1, // uint32 u32NumArrayMembers=1, - 1, // uint32 u32NumFaces=1, - cspace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB, - ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm, - false); // bool bPreMultiplied=false); - - pvrtexture::CPVRTexture compressTexture(srcHeader, srcMem); - - //compressing - bool isSuccess = false; -#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - try -#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - { - isSuccess = pvrtexture::Transcode( - compressTexture, - pvrtexture::PixelType(FindPvrPixelFormat(fmtDst)), - ePVRTVarTypeUnsignedByteNorm, - cspace, - internalQuality); - } -#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - catch (...) - { - AZ_Error("Image Processing", false, "Unknown exception in PVRTexLib"); - return nullptr; - } -#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - - if (!isSuccess) - { - AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib."); - return nullptr; - } - - // Getting compressed data - const void* const compressedData = compressTexture.getDataPtr(); - if (!compressedData) - { - AZ_Error("Image Processing", false, "Failed to obtain compressed image data by using PVRTexLib"); - return nullptr; - } - - const AZ::u32 compressedDataSize = compressTexture.getDataSize(); - if (dstImage->GetMipBufSize(mip) != compressedDataSize) - { - AZ_Error("Image Processing", false, "Compressed image data size mismatch while using PVRTexLib"); - return nullptr; - } - - //save compressed data to dst image - AZ::u8* dstMem; - AZ::u32 dstPitch; - dstImage->GetImagePointer(mip, dstMem, dstPitch); - memcpy(dstMem, compressedData, compressedDataSize); - } - - return dstImage; - } - - IImageObjectPtr PVRTCCompressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const - { - //validate input - EPixelFormat fmtSrc = srcImage->GetPixelFormat(); //compressed - - if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst)) - { - return nullptr; - } - - EPVRTColourSpace colorSpace = ePVRTCSpacelRGB; - if (srcImage->GetImageFlags() & EIF_SRGBRead) - { - colorSpace = ePVRTCSpacesRGB; - } - - IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst)); - - const AZ::u32 mipCount = dstImage->GetMipCount(); - for (AZ::u32 mip = 0; mip < mipCount; ++mip) - { - const AZ::u32 width = srcImage->GetWidth(mip); - const AZ::u32 height = srcImage->GetHeight(mip); - - // Preparing source compressed data - const pvrtexture::CPVRTextureHeader compressedHeader( - FindPvrPixelFormat(fmtSrc), // AZ::u64 u64PixelFormat, - width, // uint32 u32Height=1, - height, // uint32 u32Width=1, - 1, // uint32 u32Depth=1, - 1, // uint32 u32NumMipMaps=1, - 1, // uint32 u32NumArrayMembers=1, - 1, // uint32 u32NumFaces=1, - colorSpace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB, - ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm, - false); // bool bPreMultiplied=false); - - const AZ::u32 compressedDataSize = compressedHeader.getDataSize(); - if (srcImage->GetMipBufSize(mip) != compressedDataSize) - { - AZ_Error("Image Processing", false, "Decompressed image data size mismatch while using PVRTexLib"); - return nullptr; - } - - AZ::u8* srcMem; - AZ::u32 srcPitch; - srcImage->GetImagePointer(mip, srcMem, srcPitch); - pvrtexture::CPVRTexture cTexture(compressedHeader, srcMem); - - // Decompress - bool bOk = false; -#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - try - { -#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - bOk = pvrtexture::Transcode( - cTexture, - pvrtexture::PVRStandard8PixelType, - ePVRTVarTypeUnsignedByteNorm, - colorSpace, - pvrtexture::ePVRTCHigh); - -#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - } - catch (...) - { - AZ_Error("Image Processing", false, "Unknown exception in PVRTexLib when decompressing"); - return nullptr; - } -#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH - - if (!bOk) - { - AZ_Error("Image Processing", false, "Failed to decompress an image by using PVRTexLib"); - return nullptr; - } - - // Getting decompressed data - const void* const pDecompressedData = cTexture.getDataPtr(); - if (!pDecompressedData) - { - AZ_Error("Image Processing", false, "Failed to obtain decompressed image data by using PVRTexLib"); - return nullptr; - } - - const AZ::u32 decompressedDataSize = cTexture.getDataSize(); - if (dstImage->GetMipBufSize(mip) != decompressedDataSize) - { - AZ_Error("Image Processing", false, "Decompressed image data size mismatch while using PVRTexLib"); - return nullptr; - } - - //save decompressed image to dst image - AZ::u8* dstMem; - AZ::u32 dstPitch; - dstImage->GetImagePointer(mip, dstMem, dstPitch); - memcpy(dstMem, pDecompressedData, decompressedDataSize); - } - - return dstImage; - } -} //namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.h deleted file mode 100644 index cedd6d6643..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -namespace ImageProcessingAtom -{ - class PVRTCCompressor - : public ICompressor - { - public: - static bool IsCompressedPixelFormatSupported(EPixelFormat fmt); - static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt); - static bool DoesSupportDecompress(EPixelFormat fmtDst); - - IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override; - IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override; - - EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override; - ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final; - const char* GetName() const final; - }; -} // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp index c6f1398703..067faf3dab 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp @@ -165,17 +165,17 @@ namespace ImageProcessingAtomEditor // Get the preset id from one platform. The preset id for each platform should always be same AZ_Assert(m_settingsMap.size() > 0, "There is no platform information"); - AZ::Uuid presetId = m_settingsMap.begin()->second.m_preset; - const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetId); + PresetName presetName = m_settingsMap.begin()->second.m_preset; + const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName); if (!preset) { - AZ_Warning("Texture Editor", false, "Cannot find preset %s! Will assign a suggested one for the texture.", presetId.ToString().c_str()); - presetId = BuilderSettingManager::Instance()->GetSuggestedPreset(m_fullPath, m_img); + AZ_Warning("Texture Editor", false, "Cannot find preset %s! Will assign a suggested one for the texture.", presetName.GetCStr()); + presetName = BuilderSettingManager::Instance()->GetSuggestedPreset(m_fullPath, m_img); for (auto& settingIter : m_settingsMap) { - settingIter.second.ApplyPreset(presetId); + settingIter.second.ApplyPreset(presetName); } } } @@ -198,25 +198,18 @@ namespace ImageProcessingAtomEditor } else { - AZ_Error("Texture Editor", false, "Texture Preset %s is not found!", textureSetting.m_preset.ToString().c_str()); + AZ_Error("Texture Editor", false, "Texture Preset %s is not found!", textureSetting.m_preset.GetCStr()); } } } - void EditorTextureSetting::SetToPreset(const AZStd::string& presetName) + void EditorTextureSetting::SetToPreset(const PresetName& presetName) { m_overrideFromPreset = false; - AZ::Uuid presetId = BuilderSettingManager::Instance()->GetPresetIdFromName(presetName); - if (presetId.IsNull()) - { - AZ_Error("Texture Editor", false, "Texture Preset %s has no associated UUID.", presetName.c_str()); - return; - } - for (auto& settingIter : m_settingsMap) { - settingIter.second.ApplyPreset(presetId); + settingIter.second.ApplyPreset(presetName); } } @@ -305,7 +298,7 @@ namespace ImageProcessingAtomEditor { it.second.m_enableMipmap = false; enabled = false; - AZ_Error("Texture Editor", false, "Preset %s does not support mipmap!", preset->m_name.c_str()); + AZ_Error("Texture Editor", false, "Preset %s does not support mipmap!", preset->m_name.GetCStr()); } } else diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.h index 24efb7a70a..78bc403906 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.h @@ -62,7 +62,7 @@ namespace ImageProcessingAtomEditor void SetIsOverrided(); - void SetToPreset(const AZStd::string& presetName); + void SetToPreset(const ImageProcessingAtom::PresetName& presetName); //Get the texture setting on certain platform ImageProcessingAtom::TextureSettings& GetMultiplatformTextureSetting(const AZStd::string& platform = ""); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp index daaac5176b..a5b942fa58 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp @@ -66,7 +66,7 @@ namespace ImageProcessingAtomEditor } presetInfoText += QString("UUID: %1\n").arg(presetSettings->m_uuid.ToString().c_str()); - presetInfoText += QString("Name: %1\n").arg(presetSettings->m_name.c_str()); + presetInfoText += QString("Name: %1\n").arg(presetSettings->m_name.GetCStr()); presetInfoText += QString("Generate IBL Only: %1\n").arg(presetSettings->m_generateIBLOnly ? "True" : "False"); presetInfoText += QString("RGB Weight: %1\n").arg(RGBWeightToString(presetSettings->m_rgbWeight)); presetInfoText += QString("Source ColorSpace: %1\n").arg(ColorSpaceToString(presetSettings->m_srcColorSpace)); @@ -82,7 +82,6 @@ namespace ImageProcessingAtomEditor presetInfoText += "\n"; presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False"); presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False"); - presetInfoText += QString("Is Power Of 2: %1\n").arg(presetSettings->m_isPowerOf2 ? "True" : "False"); presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False"); presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip); presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals); @@ -99,9 +98,9 @@ namespace ImageProcessingAtomEditor presetInfoText += QString("Mip Slope: %1\n").arg(presetSettings->m_cubemapSetting->m_mipSlope); presetInfoText += QString("Edge Fixup: %1\n").arg(presetSettings->m_cubemapSetting->m_edgeFixup); presetInfoText += QString("Generate IBL Specular: %1\n").arg(presetSettings->m_cubemapSetting->m_generateIBLSpecular ? "True" : "False"); - presetInfoText += QString("IBL Specular Preset: %1\n").arg(presetSettings->m_cubemapSetting->m_iblSpecularPreset.ToString().c_str()); + presetInfoText += QString("IBL Specular Preset: %1\n").arg(presetSettings->m_cubemapSetting->m_iblSpecularPreset.GetCStr()); presetInfoText += QString("Generate IBL Diffuse: %1\n").arg(presetSettings->m_cubemapSetting->m_generateIBLDiffuse ? "True" : "False"); - presetInfoText += QString("IBL Diffuse Preset: %1\n").arg(presetSettings->m_cubemapSetting->m_iblDiffusePreset.ToString().c_str()); + presetInfoText += QString("IBL Diffuse Preset: %1\n").arg(presetSettings->m_cubemapSetting->m_iblDiffusePreset.GetCStr()); presetInfoText += QString("Requires Convolve: %1\n").arg(presetSettings->m_cubemapSetting->m_requiresConvolve ? "True" : "False"); presetInfoText += QString("SubId: %1\n").arg(presetSettings->m_cubemapSetting->m_subId); } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.cpp index 6df7b8f3df..43f99c4b60 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.cpp @@ -104,7 +104,7 @@ namespace ImageProcessingAtomEditor } } - QString ResolutionSettingItemWidget::GetFinalFormat([[maybe_unused]] const AZ::Uuid& presetId) + QString ResolutionSettingItemWidget::GetFinalFormat([[maybe_unused]] const ImageProcessingAtom::PresetName& preset) { if (m_preset && m_preset->m_pixelFormat >= 0 && m_preset->m_pixelFormat < ePixelFormat_Count) { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.h index dde3a0e32c..ce66ee1cd9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/ResolutionSettingItemWidget.h @@ -60,7 +60,7 @@ namespace ImageProcessingAtomEditor void SetupFormatComboBox(); void SetupResolutionInfo(); void RefreshUI(); - QString GetFinalFormat(const AZ::Uuid& presetId); + QString GetFinalFormat(const ImageProcessingAtom::PresetName& preset); QScopedPointer m_ui; ResoultionWidgetType m_type; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.cpp index 5b34133f96..e50d95b907 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.cpp @@ -29,7 +29,7 @@ namespace ImageProcessingAtomEditor m_presetList.clear(); auto& presetFilterMap = BuilderSettingManager::Instance()->GetPresetFilterMap(); - AZStd::set noFilterPresetList; + AZStd::unordered_set noFilterPresetList; // Check if there is any filtered preset list first for(auto& presetFilter : presetFilterMap) @@ -40,7 +40,7 @@ namespace ImageProcessingAtomEditor } else if (IsMatchingWithFileMask(m_textureSetting->m_textureName, presetFilter.first)) { - for(const AZStd::string& presetName : presetFilter.second) + for(const auto& presetName : presetFilter.second) { m_presetList.insert(presetName); } @@ -52,18 +52,18 @@ namespace ImageProcessingAtomEditor m_presetList = noFilterPresetList; } - foreach (const AZStd::string& presetName, m_presetList) + foreach (const auto& presetName, m_presetList) { - m_ui->presetComboBox->addItem(QString(presetName.c_str())); + m_ui->presetComboBox->addItem(QString(presetName.GetCStr())); } // Set current preset - const AZ::Uuid& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset; + const auto& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset; const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(currPreset); if (presetSetting) { - m_ui->presetComboBox->setCurrentText(presetSetting->m_name.c_str()); + m_ui->presetComboBox->setCurrentText(presetSetting->m_name.GetCStr()); QObject::connect(m_ui->presetComboBox, static_cast(&QComboBox::currentIndexChanged), this, &TexturePresetSelectionWidget::OnChangePreset); // Suppress engine reduction checkbox @@ -109,20 +109,20 @@ namespace ImageProcessingAtomEditor void TexturePresetSelectionWidget::OnRestButton() { - m_textureSetting->SetToPreset(AZStd::string(m_ui->presetComboBox->currentText().toUtf8().data())); + m_textureSetting->SetToPreset(PresetName(m_ui->presetComboBox->currentText().toUtf8().data())); EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, true, BuilderSettingManager::s_defaultPlatform); } void TexturePresetSelectionWidget::OnChangePreset(int index) { QString text = m_ui->presetComboBox->itemText(index); - m_textureSetting->SetToPreset(AZStd::string(text.toUtf8().data())); + m_textureSetting->SetToPreset(PresetName(text.toUtf8().data())); EditorInternalNotificationBus::Broadcast(&EditorInternalNotificationBus::Events::OnEditorSettingsChanged, true, BuilderSettingManager::s_defaultPlatform); } void ImageProcessingAtomEditor::TexturePresetSelectionWidget::OnPresetInfoButton() { - const AZ::Uuid& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset; + const auto& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset; const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(currPreset); m_presetPopup.reset(new PresetInfoPopup(presetSetting, this)); m_presetPopup->installEventFilter(this); @@ -136,7 +136,7 @@ namespace ImageProcessingAtomEditor bool oldState = m_ui->serCheckBox->blockSignals(true); m_ui->serCheckBox->setChecked(m_textureSetting->GetMultiplatformTextureSetting().m_suppressEngineReduce); // If the preset's SER is true, texture setting should not override - const AZ::Uuid& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset; + const auto& currPreset = m_textureSetting->GetMultiplatformTextureSetting().m_preset; const PresetSettings* presetSetting = BuilderSettingManager::Instance()->GetPreset(currPreset); if (presetSetting) { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.h index bbb6e6e7df..ad819840f9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePresetSelectionWidget.h @@ -49,7 +49,7 @@ namespace ImageProcessingAtomEditor private: QScopedPointer m_ui; - AZStd::set m_presetList; + AZStd::unordered_set m_presetList; EditorTextureSetting* m_textureSetting; QScopedPointer m_presetPopup; bool IsMatchingWithFileMask(const AZStd::string& filename, const AZStd::string& fileMask); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp index c688b0b20c..a489c8da5e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp @@ -74,7 +74,7 @@ namespace ImageProcessingAtom builderDescriptor.m_busId = azrtti_typeid(); builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - builderDescriptor.m_version = 24; // [SPEC-7821] + builderDescriptor.m_version = 25; // [ATOM-16575] builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint(); m_imageBuilder.BusConnect(builderDescriptor.m_busId); AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor); @@ -164,7 +164,7 @@ namespace ImageProcessingAtom AZStd::vector outProducts; AZStd::string_view presetFilePath; - const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName, platformName, &presetFilePath); + const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(PresetName(presetName), platformName, &presetFilePath); if (preset == nullptr) { AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str()); @@ -173,7 +173,7 @@ namespace ImageProcessingAtom AZStd::unique_ptr desc = AZStd::make_unique(); TextureSettings& textureSettings = desc->m_textureSetting; - textureSettings.m_preset = preset->m_uuid; + textureSettings.m_preset = preset->m_name; desc->m_inputImage = imageObject; desc->m_presetSetting = *preset; desc->m_isPreview = false; @@ -204,7 +204,7 @@ namespace ImageProcessingAtom bool BuilderPluginComponent::IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName) { AZStd::string_view filePath; - const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName, platformName, &filePath); + const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(PresetName(presetName), platformName, &filePath); if (preset == nullptr) { AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str()); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/DdsLoader.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/DdsLoader.cpp index 20367227bd..d7f36398e3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/DdsLoader.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/DdsLoader.cpp @@ -24,201 +24,6 @@ namespace ImageProcessingAtom { namespace DdsLoader { - IImageObject* CreateImageFromHeaderLegacy(DDS_HEADER_LEGACY& header, DDS_HEADER_DXT10& exthead) - { - EPixelFormat eFormat = ePixelFormat_Unknown; - AZ::u32 dwWidth, dwMips, dwHeight; - AZ::u32 imageFlags = header.dwReserved1; - AZ::Color colMinARGB, colMaxARGB; - - dwWidth = header.dwWidth; - dwHeight = header.dwHeight; - dwMips = 1; - if (header.dwHeaderFlags & DDS_HEADER_FLAGS_MIPMAP) - { - dwMips = header.dwMipMapCount; - } - if ((header.dwSurfaceFlags & DDS_SURFACE_FLAGS_CUBEMAP) && (header.dwCubemapFlags & DDS_CUBEMAP_ALLFACES)) - { - AZ_Assert(header.dwReserved1 & EIF_Cubemap, "Image flag should have cubemap flag"); - dwHeight *= 6; - } - - colMinARGB = AZ::Color(header.cMinColor[0], header.cMinColor[1], header.cMinColor[2], header.cMinColor[3]); - colMaxARGB = AZ::Color(header.cMaxColor[0], header.cMaxColor[1], header.cMaxColor[2], header.cMaxColor[3]); - - //get pixel format - { - // DX10 formats - if (header.ddspf.dwFourCC == FOURCC_DX10) - { - AZ::u32 dxgiFormat = exthead.dxgiFormat; - - //remove the SRGB from dxgi format and add sRGB to image flag - if (dxgiFormat == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) - { - dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; - } - else if (dxgiFormat == DXGI_FORMAT_BC1_UNORM_SRGB) - { - dxgiFormat = DXGI_FORMAT_BC1_UNORM; - } - else if (dxgiFormat == DXGI_FORMAT_BC2_UNORM_SRGB) - { - dxgiFormat = DXGI_FORMAT_BC2_UNORM; - } - else if (dxgiFormat == DXGI_FORMAT_BC3_UNORM_SRGB) - { - dxgiFormat = DXGI_FORMAT_BC3_UNORM; - } - else if (dxgiFormat == DXGI_FORMAT_BC7_UNORM_SRGB) - { - dxgiFormat = DXGI_FORMAT_BC7_UNORM; - } - - //add rgb flag if the dxgiformat was changed (which means it was sRGB format) above - if (dxgiFormat != exthead.dxgiFormat) - { - AZ_Assert(imageFlags & EIF_SRGBRead, "Image flags should have SRGBRead flag"); - imageFlags |= EIF_SRGBRead; - } - - //check all the pixel formats and find matching one - if (dxgiFormat != DXGI_FORMAT_UNKNOWN) - { - int i = 0; - for (; i < ePixelFormat_Count; i++) - { - const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo((EPixelFormat)i); - if (static_cast(info->d3d10Format) == dxgiFormat) - { - eFormat = (EPixelFormat)i; - break; - } - } - if (i == ePixelFormat_Count) - { - AZ_Error("Image Processing", false, "Unhandled d3d10 format: %d", dxgiFormat); - return nullptr; - } - } - } - else - { - //for non-dx10 formats, use fourCC to find out its pixel formats - //go through all pixel formats and find a match with the fourcc - for (AZ::u32 formatIdx = 0; formatIdx < ePixelFormat_Count; formatIdx++) - { - const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo((EPixelFormat)formatIdx); - if (header.ddspf.dwFourCC == info->fourCC) - { - eFormat = (EPixelFormat)formatIdx; - break; - } - } - - //legacy formats. This section is only used for load dds files converted by RC.exe - //our save to dds file function won't use any of these fourcc - if (eFormat == ePixelFormat_Unknown) - { - if (header.ddspf.dwFourCC == FOURCC_DXT1) - { - eFormat = ePixelFormat_BC1; - } - else if (header.ddspf.dwFourCC == FOURCC_DXT5) - { - eFormat = ePixelFormat_BC3; - } - else if (header.ddspf.dwFourCC == FOURCC_3DCP) - { - eFormat = ePixelFormat_BC4; - } - else if (header.ddspf.dwFourCC == FOURCC_3DC) - { - eFormat = ePixelFormat_BC5; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_R32F) - { - eFormat = ePixelFormat_R32F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_G32R32F) - { - eFormat = ePixelFormat_R32G32F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_A32B32G32R32F) - { - eFormat = ePixelFormat_R32G32B32A32F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_R16F) - { - eFormat = ePixelFormat_R16F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_G16R16F) - { - eFormat = ePixelFormat_R16G16F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16F) - { - eFormat = ePixelFormat_R16G16B16A16F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16) - { - eFormat = ePixelFormat_R16G16B16A16; - } - else if ((header.ddspf.dwFlags == DDS_RGBA || header.ddspf.dwFlags == DDS_RGB) - && header.ddspf.dwRGBBitCount == 32) - { - if (header.ddspf.dwRBitMask == 0x00ff0000) - { - eFormat = ePixelFormat_B8G8R8A8; - } - else - { - eFormat = ePixelFormat_R8G8B8A8; - } - } - else if (header.ddspf.dwFlags == DDS_LUMINANCEA && header.ddspf.dwRGBBitCount == 8) - { - eFormat = ePixelFormat_R8G8; - } - else if (header.ddspf.dwFlags == DDS_LUMINANCE && header.ddspf.dwRGBBitCount == 8) - { - eFormat = ePixelFormat_A8; - } - else if ((header.ddspf.dwFlags == DDS_A || header.ddspf.dwFlags == DDS_A_ONLY || header.ddspf.dwFlags == (DDS_A | DDS_A_ONLY)) && header.ddspf.dwRGBBitCount == 8) - { - eFormat = ePixelFormat_A8; - } - } - } - } - - if (eFormat == ePixelFormat_Unknown) - { - AZ_Error("Image Processing", false, "Unhandled dds pixel format fourCC: %d, flags: %d", - header.ddspf.dwFourCC, header.ddspf.dwFlags); - return nullptr; - } - - IImageObject* newImage = IImageObject::CreateImage(dwWidth, dwHeight, dwMips, eFormat); - - if (dwMips != newImage->GetMipCount()) - { - AZ_Error("Image Processing", false, "Mipcount from image data doesn't match image size and pixelformat"); - delete newImage; - return nullptr; - } - - //set properties - newImage->SetImageFlags(imageFlags); - newImage->SetAverageBrightness(header.fAvgBrightness); - newImage->SetColorRange(colMinARGB, colMaxARGB); - newImage->SetNumPersistentMips(header.bNumPersistentMips); - - return newImage; - } - - bool IsExtensionSupported(const char* extension) { QString ext = QString(extension).toLower(); @@ -226,215 +31,6 @@ namespace ImageProcessingAtom return ext == "dds"; } - IImageObject* LoadImageFromFileLegacy(const AZStd::string& filename) - { - AZ::IO::SystemFile file; - file.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY); - - AZ::IO::SystemFileStream fileLoadStream(&file, true); - if (!fileLoadStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to open file %s", __FUNCTION__, filename.c_str()); - return nullptr; - } - - AZStd::string ext = ""; - AzFramework::StringFunc::Path::GetExtension(filename.c_str(), ext, false); - bool isAlphaImage = (ext == "a"); - - IImageObject* imageObj = LoadImageFromFileStreamLegacy(fileLoadStream); - - //load mips from seperated files if it's splitted - if (imageObj && imageObj->HasImageFlags(EIF_Splitted)) - { - AZStd::string baseName; - if (isAlphaImage) - { - baseName = filename.substr(0, filename.size() - 2); - } - else - { - baseName = filename; - } - - AZ::u32 externalMipCount = 0; - if (imageObj->GetNumPersistentMips() < imageObj->GetMipCount()) - { - externalMipCount = imageObj->GetMipCount() - imageObj->GetNumPersistentMips(); - } - //load other mips from files with number extensions - for (AZ::u32 mipIdx = 1; mipIdx <= externalMipCount; mipIdx++) - { - AZ::u32 mip = externalMipCount - mipIdx; - AZStd::string mipFileName = AZStd::string::format("%s.%d%s", baseName.c_str(), mipIdx, isAlphaImage ? "a" : ""); - - AZ::IO::SystemFile mipFile; - mipFile.Open(mipFileName.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY); - - AZ::IO::SystemFileStream mipFileLoadStream(&mipFile, true); - - if (!mipFileLoadStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to open mip file %s", __FUNCTION__, mipFileName.c_str()); - break; - } - - AZ::u32 pitch; - AZ::u8* mem; - imageObj->GetImagePointer(mip, mem, pitch); - AZ::u32 bufSize = imageObj->GetMipBufSize(mip); - mipFileLoadStream.Read(bufSize, mem); - } - } - - return imageObj; - } - - IImageObject* LoadImageFromFileStreamLegacy(AZ::IO::SystemFileStream& fileLoadStream) - { - if (fileLoadStream.GetLength() - fileLoadStream.GetCurPos() < sizeof(DDS_FILE_DESC_LEGACY)) - { - AZ_Error("Image Processing", false, "%s: Trying to load a none-DDS file", __FUNCTION__); - return nullptr; - } - - DDS_FILE_DESC_LEGACY desc; - DDS_HEADER_DXT10 exthead; - - AZ::IO::SizeType startPos = fileLoadStream.GetCurPos(); - fileLoadStream.Read(sizeof(desc.dwMagic), &desc.dwMagic); - - if (desc.dwMagic != FOURCC_DDS) - { - desc.dwMagic = FOURCC_DDS; - //the old cry .a file doesn't have "DDS " in the beginning of the file. - //so reset to previous position - fileLoadStream.Seek(startPos, AZ::IO::GenericStream::ST_SEEK_BEGIN); - } - - fileLoadStream.Read(sizeof(desc.header), &desc.header); - - if (!desc.IsValid()) - { - AZ_Error("Image Processing", false, "%s: Trying to load a none-DDS file", __FUNCTION__); - return nullptr; - } - - if (desc.header.IsDX10Ext()) - { - fileLoadStream.Read(sizeof(exthead), &exthead); - } - - IImageObject* outImage = CreateImageFromHeaderLegacy(desc.header, exthead); - - if (outImage == nullptr) - { - return nullptr; - } - - //load mip data - AZ::u32 mipStart = 0; - //There are at least three lowest mips are in the file if it was splitted. This is to load splitted dds file exported by legacy rc.exe - int numPersistentMips = outImage->GetNumPersistentMips(); - if (numPersistentMips == 0 && outImage->HasImageFlags(EIF_Splitted)) - { - outImage->SetNumPersistentMips(3); - } - - if (outImage->HasImageFlags(EIF_Splitted) - && outImage->GetMipCount() > outImage->GetNumPersistentMips()) - { - mipStart = outImage->GetMipCount() - outImage->GetNumPersistentMips(); - } - - AZ::u32 faces = 1; - if (outImage->HasImageFlags(EIF_Cubemap)) - { - faces = 6; - } - - for (AZ::u32 face = 0; face < faces; face++) - { - for (AZ::u32 mip = mipStart; mip < outImage->GetMipCount(); ++mip) - { - AZ::u32 pitch; - AZ::u8* mem; - outImage->GetImagePointer(mip, mem, pitch); - AZ::u32 faceBufSize = outImage->GetMipBufSize(mip) / faces; - fileLoadStream.Read(faceBufSize, mem + faceBufSize * face); - } - } - - return outImage; - } - - IImageObject* LoadAttachedImageFromDdsFileLegacy(const AZStd::string& filename, IImageObjectPtr originImage) - { - if (originImage == nullptr) - { - return nullptr; - } - - AZ_Assert(originImage->HasImageFlags(EIF_AttachedAlpha), - "this function should only be called for origin image loaded from same file with attached alpha flag"); - - AZ::IO::SystemFile file; - file.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY); - - AZ::IO::SystemFileStream fileLoadStream(&file, true); - if (!fileLoadStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to open file %s", __FUNCTION__, filename.c_str()); - return nullptr; - } - - DDS_FILE_DESC_LEGACY desc; - DDS_HEADER_DXT10 exthead; - - fileLoadStream.Read(sizeof(desc), &desc); - if (desc.dwMagic != FOURCC_DDS) - { - AZ_Error("Image Processing", false, "%s:Trying to load a none-DDS file", __FUNCTION__); - return nullptr; - } - - if (desc.header.IsDX10Ext()) - { - fileLoadStream.Read(sizeof(exthead), &exthead); - } - - //skip size for originImage's mip data - for (AZ::u32 mip = 0; mip < originImage->GetMipCount(); ++mip) - { - AZ::u32 bufSize = originImage->GetMipBufSize(mip); - fileLoadStream.Seek(bufSize, AZ::IO::GenericStream::ST_SEEK_CUR); - } - - IImageObject* alphaImage = nullptr; - - AZ::u32 marker = 0; - fileLoadStream.Read(4, &marker); - if (marker == FOURCC_CExt) // marker for the start of O3DE Extended data - { - fileLoadStream.Read(4, &marker); - if (FOURCC_AttC == marker) // Attached Channel chunk - { - AZ::u32 size = 0; - fileLoadStream.Read(4, &size); - - alphaImage = LoadImageFromFileStreamLegacy(fileLoadStream); - fileLoadStream.Read(4, &marker); - } - - if (FOURCC_CEnd == marker) // marker for the end of O3DE Extended data - { - fileLoadStream.Read(4, &marker); - } - } - - return alphaImage; - } - // Create an image object from standard dds header IImageObject* CreateImageFromHeader(DDS_HEADER& header, DDS_HEADER_DXT10& exthead) { @@ -526,45 +122,14 @@ namespace ImageProcessingAtom { format = ePixelFormat_BC1; } - else if (header.ddspf.dwFourCC == FOURCC_DXT5) + else if (header.ddspf.dwFourCC == FOURCC_DXT5 || header.ddspf.dwFourCC == FOURCC_DXT4) { format = ePixelFormat_BC3; } - else if (header.ddspf.dwFourCC == FOURCC_3DCP) + else { - format = ePixelFormat_BC4; - } - else if (header.ddspf.dwFourCC == FOURCC_3DC) - { - format = ePixelFormat_BC5; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_R32F) - { - format = ePixelFormat_R32F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_G32R32F) - { - format = ePixelFormat_R32G32F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_A32B32G32R32F) - { - format = ePixelFormat_R32G32B32A32F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_R16F) - { - format = ePixelFormat_R16F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_G16R16F) - { - format = ePixelFormat_R16G16F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16F) - { - format = ePixelFormat_R16G16B16A16F; - } - else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16) - { - format = ePixelFormat_R16G16B16A16; + AZ_Error("Image Processing", false, "unsupported fourCC format: 0x%x", header.ddspf.dwFourCC); + return nullptr; } } else diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/ImageLoaders.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/ImageLoaders.h index 587c9b551b..b860e82631 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/ImageLoaders.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageLoader/ImageLoaders.h @@ -44,11 +44,6 @@ namespace ImageProcessingAtom { bool IsExtensionSupported(const char* extension); IImageObject* LoadImageFromFile(const AZStd::string& filename); - - // These functions are for loading legacy O3DE dds files - IImageObject* LoadImageFromFileLegacy(const AZStd::string& filename); - IImageObject* LoadImageFromFileStreamLegacy(AZ::IO::SystemFileStream& fileLoadStream); - IImageObject* LoadAttachedImageFromDdsFileLegacy(const AZStd::string& filename, IImageObjectPtr originImage); };// namespace DdsLoader // Load .exr files to an image object diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/platform_mac.cmake b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/platform_mac.cmake index 7008b352a8..7a325ca97e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/platform_mac.cmake +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/platform_mac.cmake @@ -5,8 +5,3 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # - -set(LY_COMPILE_OPTIONS - PRIVATE - -fexceptions #ImageLoader/ExrLoader.cpp and PVRTC.cpp uses exceptions -) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/DDSHeader.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/DDSHeader.h index 785424a9b1..0f953897c9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/DDSHeader.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/DDSHeader.h @@ -237,14 +237,8 @@ namespace ImageProcessingAtom const static AZ::u32 FOURCC_CEnd = IMAGE_BUIDER_MAKEFOURCC('C', 'E', 'n', 'd'); // O3DE extension end const static AZ::u32 FOURCC_AttC = IMAGE_BUIDER_MAKEFOURCC('A', 't', 't', 'C'); // Chunk Attached Channel - //Fourcc for pixel formats which aren't supported by dx10, such as astc formats, etc formats, pvrtc formats + //Fourcc for pixel formats which aren't supported by dx10, such as astc formats //They are used for dwFourCC of dds header's DDS_PIXELFORMAT to identify non-dx10 pixel formats - const static AZ::u32 FOURCC_EAC_R11 = IMAGE_BUIDER_MAKEFOURCC('E', 'A', 'R', ' '); - const static AZ::u32 FOURCC_EAC_RG11 = IMAGE_BUIDER_MAKEFOURCC('E', 'A', 'R', 'G'); - const static AZ::u32 FOURCC_ETC2 = IMAGE_BUIDER_MAKEFOURCC('E', 'T', '2', ' '); - const static AZ::u32 FOURCC_ETC2A = IMAGE_BUIDER_MAKEFOURCC('E', 'T', '2', 'A'); - const static AZ::u32 FOURCC_PVRTC2 = IMAGE_BUIDER_MAKEFOURCC('P', 'V', 'R', '2'); - const static AZ::u32 FOURCC_PVRTC4 = IMAGE_BUIDER_MAKEFOURCC('P', 'V', 'R', '4'); const static AZ::u32 FOURCC_ASTC_4x4 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '4', '4'); const static AZ::u32 FOURCC_ASTC_5x4 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '5', '4'); const static AZ::u32 FOURCC_ASTC_5x5 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '5', '5'); @@ -260,10 +254,10 @@ namespace ImageProcessingAtom const static AZ::u32 FOURCC_ASTC_12x10 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'C', 'A'); const static AZ::u32 FOURCC_ASTC_12x12 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'C', 'C'); - //legacy formats names. they are only used for load rc.exe's dds formats + //legacy formats names. they are only used for load old dds formats. const static AZ::u32 FOURCC_DXT1 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '1'); + const static AZ::u32 FOURCC_DXT2 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '2'); const static AZ::u32 FOURCC_DXT3 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '3'); + const static AZ::u32 FOURCC_DXT4 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '4'); const static AZ::u32 FOURCC_DXT5 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '5'); - const static AZ::u32 FOURCC_3DCP = IMAGE_BUIDER_MAKEFOURCC('A', 'T', 'I', '1'); - const static AZ::u32 FOURCC_3DC = IMAGE_BUIDER_MAKEFOURCC('A', 'T', 'I', '2'); } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp index 5995146c59..977359e4f6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp @@ -186,12 +186,12 @@ namespace ImageProcessingAtom { // check and generate IBL specular and diffuse, if necessary AZStd::unique_ptr& cubemapSettings = m_input->m_presetSetting.m_cubemapSetting; - if (cubemapSettings->m_generateIBLSpecular && !cubemapSettings->m_iblSpecularPreset.IsNull()) + if (cubemapSettings->m_generateIBLSpecular && !cubemapSettings->m_iblSpecularPreset.IsEmpty()) { CreateIBLCubemap(cubemapSettings->m_iblSpecularPreset, SpecularCubemapSuffix, m_iblSpecularCubemapImage); } - if (cubemapSettings->m_generateIBLDiffuse && !cubemapSettings->m_iblDiffusePreset.IsNull()) + if (cubemapSettings->m_generateIBLDiffuse && !cubemapSettings->m_iblDiffusePreset.IsEmpty()) { CreateIBLCubemap(cubemapSettings->m_iblDiffusePreset, DiffuseCubemapSuffix, m_iblDiffuseCubemapImage); } @@ -367,7 +367,7 @@ namespace ImageProcessingAtom else { AZ_TracePrintf("Image Processing", "Image converted with preset [%s] [%s] and saved to [%s] (%d bytes) taking %f seconds\n", - m_input->m_presetSetting.m_name.c_str(), + m_input->m_presetSetting.m_name.GetCStr(), m_input->m_filePath.c_str(), m_input->m_outputFolder.c_str(), sizeTotal, m_processTime); } @@ -834,7 +834,7 @@ namespace ImageProcessingAtom // if get textureSetting failed, use the default texture setting, and find suitable preset for this file // in very rare user case, an old texture setting file may not have a preset. We fix it over here too. - if (textureSettings.m_preset.IsNull()) + if (textureSettings.m_preset.IsEmpty()) { textureSettings.m_preset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilePath, srcImage); } @@ -845,9 +845,7 @@ namespace ImageProcessingAtom if (preset == nullptr) { - AZStd::string uuidStr; - textureSettings.m_preset.ToString(uuidStr); - AZ_Assert(false, "%s cannot find image preset with ID %s.", imageFilePath.c_str(), uuidStr.c_str()); + AZ_Assert(false, "%s cannot find image preset %s.", imageFilePath.c_str(), textureSettings.m_preset.GetCStr()); return nullptr; } @@ -875,11 +873,11 @@ namespace ImageProcessingAtom return process; } - void ImageConvertProcess::CreateIBLCubemap(AZ::Uuid presetUUID, const char* fileNameSuffix, IImageObjectPtr& cubemapImage) + void ImageConvertProcess::CreateIBLCubemap(PresetName preset, const char* fileNameSuffix, IImageObjectPtr& cubemapImage) { const AZStd::string& platformId = m_input->m_platform; AZStd::string_view filePath; - const PresetSettings* presetSettings = BuilderSettingManager::Instance()->GetPreset(presetUUID, platformId, &filePath); + const PresetSettings* presetSettings = BuilderSettingManager::Instance()->GetPreset(preset, platformId, &filePath); if (presetSettings == nullptr) { AZ_Error("Image Processing", false, "Couldn't find preset for IBL cubemap generation"); @@ -900,7 +898,7 @@ namespace ImageProcessingAtom // the diffuse irradiance cubemap is generated with a separate ImageConvertProcess TextureSettings textureSettings = m_input->m_textureSetting; - textureSettings.m_preset = presetUUID; + textureSettings.m_preset = preset; AZStd::unique_ptr desc = AZStd::make_unique(); desc->m_presetSetting = *presetSettings; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h index 6ab866ee09..eaf05c3280 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h @@ -163,7 +163,7 @@ namespace ImageProcessingAtom bool FillCubemapMipmaps(); //IBL cubemap generation, this creates a separate ImageConvertProcess - void CreateIBLCubemap(AZ::Uuid presetUUID, const char* fileNameSuffix, IImageObjectPtr& cubemapImage); + void CreateIBLCubemap(PresetName preset, const char* fileNameSuffix, IImageObjectPtr& cubemapImage); //convert color space to linear with pixel format rgba32f bool ConvertToLinear(); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h index 96393f4a1f..99e752c90a 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h @@ -22,7 +22,7 @@ namespace ImageProcessingAtom const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use const static AZ::u32 EIF_AttachedAlpha = 0x400; // info for the engine: it's a texture with attached alpha channel const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear) - const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized with r_TexResolution + const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized const static AZ::u32 EIF_RenormalizedTexture = 0x10000; // info for the engine: for dds textures that have renormalized color range const static AZ::u32 EIF_CafeNative = 0x20000; // info for the engine: native Cafe texture format const static AZ::u32 EIF_RestrictedPlatformONative = 0x40000; // native tiled texture for restrict platform O diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImagePreview.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImagePreview.cpp index b73157744c..f51741eba9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImagePreview.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImagePreview.cpp @@ -43,7 +43,7 @@ namespace ImageProcessingAtom m_inputImage = IImageObjectPtr(LoadImageFromFile(m_imageFileName)); } // Get preset if the setting in texture is changed - if (m_presetSetting == nullptr || m_presetSetting->m_uuid != m_textureSetting->m_preset) + if (m_presetSetting == nullptr || m_presetSetting->m_name != m_textureSetting->m_preset) { m_presetSetting = BuilderSettingManager::Instance()->GetPreset(m_textureSetting->m_preset); } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.cpp index 94fce9bc1f..8a741d6637 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.cpp @@ -52,19 +52,6 @@ namespace ImageProcessingAtom return false; } - bool IsETCFormat(EPixelFormat fmt) - { - if (fmt == ePixelFormat_ETC2 - || fmt == ePixelFormat_ETC2a - || fmt == ePixelFormat_ETC2a1 - || fmt == ePixelFormat_EAC_R11 - || fmt == ePixelFormat_EAC_RG11) - { - return true; - } - return false; - } - PixelFormatInfo::PixelFormatInfo( uint32_t a_bitsPerPixel, uint32_t a_Channels, @@ -96,7 +83,6 @@ namespace ImageProcessingAtom , fourCC(a_fourCC) , eSampleType(a_eSampleType) , szName(a_szName) - , szLegacyName(a_szName) , szDescription(a_szDescription) , bCompressed(a_bCompressed) , bSelectable(a_bSelectable) @@ -122,15 +108,6 @@ namespace ImageProcessingAtom CPixelFormats::CPixelFormats() { InitPixelFormats(); - - m_removedLegacyFormats["DXT1"] = ePixelFormat_BC1; - m_removedLegacyFormats["DXT1a"] = ePixelFormat_BC1a; - m_removedLegacyFormats["DXT3"] = ePixelFormat_BC3; - m_removedLegacyFormats["DXT3t"] = ePixelFormat_BC3t; - m_removedLegacyFormats["DXT5"] = ePixelFormat_BC3; - m_removedLegacyFormats["DXT5t"] = ePixelFormat_BC3t; - m_removedLegacyFormats["3DCp"] = ePixelFormat_BC4; - m_removedLegacyFormats["3DC"] = ePixelFormat_BC5; } void CPixelFormats::InitPixelFormat(EPixelFormat format, const PixelFormatInfo& formatInfo) @@ -176,13 +153,6 @@ namespace ImageProcessingAtom InitPixelFormat(ePixelFormat_ASTC_10x10, PixelFormatInfo(0, 4, true, "?", 16, 16, 10, 10, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_10x10, ESampleType::eSampleType_Compressed, "ASTC_10x10", "ASTC 10x10 compressed texture format", true, false)); InitPixelFormat(ePixelFormat_ASTC_12x10, PixelFormatInfo(0, 4, true, "?", 16, 16, 12, 10, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_12x10, ESampleType::eSampleType_Compressed, "ASTC_12x10", "ASTC 12x10 compressed texture format", true, false)); InitPixelFormat(ePixelFormat_ASTC_12x12, PixelFormatInfo(0, 4, true, "?", 16, 16, 12, 12, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_12x12, ESampleType::eSampleType_Compressed, "ASTC_12x12", "ASTC 12x12 compressed texture format", true, false)); - InitPixelFormat(ePixelFormat_PVRTC2, PixelFormatInfo(2, 4, true, "2", 16, 16, 8, 4, 64, true, DXGI_FORMAT_UNKNOWN, FOURCC_PVRTC2, ESampleType::eSampleType_Compressed, "PVRTC2", "POWERVR 2 bpp compressed texture format", true, false)); - InitPixelFormat(ePixelFormat_PVRTC4, PixelFormatInfo(4, 4, true, "2", 8, 8, 4, 4, 64, true, DXGI_FORMAT_UNKNOWN, FOURCC_PVRTC4, ESampleType::eSampleType_Compressed, "PVRTC4", "POWERVR 4 bpp compressed texture format", true, false)); - InitPixelFormat(ePixelFormat_EAC_R11, PixelFormatInfo(4, 1, true, "4", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_EAC_R11, ESampleType::eSampleType_Compressed, "EAC_R11", "EAC 4 bpp single channel texture format", true, false)); - InitPixelFormat(ePixelFormat_EAC_RG11, PixelFormatInfo(8, 2, false, "0", 4, 4, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_EAC_RG11, ESampleType::eSampleType_Compressed, "EAC_RG11", "EAC 8 bpp dual channel texture format", true, false)); - InitPixelFormat(ePixelFormat_ETC2, PixelFormatInfo(4, 3, false, "0", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2, ESampleType::eSampleType_Compressed, "ETC2", "ETC2 RGB 4 bpp compressed texture format", true, false)); - InitPixelFormat(ePixelFormat_ETC2a, PixelFormatInfo(8, 4, true, "4", 4, 4, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2A, ESampleType::eSampleType_Compressed, "ETC2a", "ETC2 RGBA 8 bpp compressed texture format", true, false)); - InitPixelFormat(ePixelFormat_ETC2a1, PixelFormatInfo(4, 4, true, "1", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2A, ESampleType::eSampleType_Compressed, "ETC2a1", "ETC2 RGBA1 8 bpp compressed texture format", true, false)); // Standardized Compressed DXGI Formats (DX10+) // Data in these compressed formats is hardware decodable on all DX10 chips, and manageable with the DX10-API. @@ -216,17 +186,6 @@ namespace ImageProcessingAtom InitPixelFormat(ePixelFormat_R32, PixelFormatInfo(32, 1, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_FORCE_UINT, FOURCC_DX10, ESampleType::eSampleType_Uint32, "R32", "32-bit red only", false, false)); - //Set legacy name it can be used for convertion - m_pixelFormatInfo[ePixelFormat_R8G8B8A8].szLegacyName = "A8R8G8B8"; - m_pixelFormatInfo[ePixelFormat_R8G8B8X8].szLegacyName = "X8R8G8B8"; - m_pixelFormatInfo[ePixelFormat_R8G8].szLegacyName = "G8R8"; - m_pixelFormatInfo[ePixelFormat_R16G16B16A16].szLegacyName = "A16B16G16R16"; - m_pixelFormatInfo[ePixelFormat_R16G16].szLegacyName = "G16R16"; - m_pixelFormatInfo[ePixelFormat_R32G32B32A32F].szLegacyName = "A32B32G32R32F"; - m_pixelFormatInfo[ePixelFormat_R32G32F].szLegacyName = "G32R32F"; - m_pixelFormatInfo[ePixelFormat_R16G16B16A16F].szLegacyName = "A16B16G16R16F"; - m_pixelFormatInfo[ePixelFormat_R16G16F].szLegacyName = "G16R16F"; - //validate all pixel formats are proper initialized for (int i = 0; i < ePixelFormat_Count; ++i) { @@ -249,23 +208,6 @@ namespace ImageProcessingAtom return ePixelFormat_Unknown; } - EPixelFormat CPixelFormats::FindPixelFormatByLegacyName(const char* name) - { - if (m_removedLegacyFormats.find(name) != m_removedLegacyFormats.end()) - { - return m_removedLegacyFormats[name]; - } - - for (int i = 0; i < ePixelFormat_Count; ++i) - { - if (azstricmp(m_pixelFormatInfo[i].szLegacyName, name) == 0) - { - return (EPixelFormat)i; - } - } - return ePixelFormat_Unknown; - } - const PixelFormatInfo* CPixelFormats::GetPixelFormatInfo(EPixelFormat format) { AZ_Assert((format >= 0) && (format < ePixelFormat_Count), "Unsupport pixel format: %d", format); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.h index ec1ec1bfaf..92c4bdd99e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/PixelFormatInfo.h @@ -119,7 +119,6 @@ namespace ImageProcessingAtom bool bSquarePow2; // whether the pixel format requires image size be square and power of 2. DXGI_FORMAT d3d10Format; // the mapping d3d10 pixel format ESampleType eSampleType; // the data type used to present pixel - const char* szLegacyName; // name used for cryEngine const char* szName; // name for showing in editors const char* szDescription; // description for showing in editors bool bCompressed; // if it's a compressed format @@ -173,10 +172,6 @@ namespace ImageProcessingAtom bool IsFormatSigned(EPixelFormat fmt); bool IsFormatFloatingPoint(EPixelFormat fmt, bool bFullPrecision); - //find the pixel format for name used by Cry's RC.ini - //returns ePixelFormat_Unknown if the name was not found in registed format list - EPixelFormat FindPixelFormatByLegacyName(const char* name); - //find pixel format by its name EPixelFormat FindPixelFormatByName(const char* name); @@ -208,9 +203,6 @@ namespace ImageProcessingAtom //pixel format name to pixel format enum AZStd::map m_pixelFormatNameMap; - - // some formats from cryEngine were removed. using this name-pixelFormat mapping to look for new format - AZStd::map m_removedLegacyFormats; }; template diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.cpp index d282e9b02b..29ba8c3351 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.cpp @@ -102,32 +102,6 @@ namespace ImageProcessingAtom case AZ::RHI::Format::ASTC_12x12_UNORM: return ePixelFormat_ASTC_12x12; - case AZ::RHI::Format::PVRTC2_UNORM_SRGB: - isSRGB = true; - case AZ::RHI::Format::PVRTC2_UNORM: - return ePixelFormat_PVRTC2; - case AZ::RHI::Format::PVRTC4_UNORM_SRGB: - isSRGB = true; - case AZ::RHI::Format::PVRTC4_UNORM: - return ePixelFormat_PVRTC4; - - case AZ::RHI::Format::EAC_R11_UNORM: - return ePixelFormat_EAC_R11; - case AZ::RHI::Format::EAC_RG11_UNORM: - return ePixelFormat_EAC_RG11; - case AZ::RHI::Format::ETC2_UNORM_SRGB: - isSRGB = true; - case AZ::RHI::Format::ETC2_UNORM: - return ePixelFormat_ETC2; - case AZ::RHI::Format::ETC2A_UNORM_SRGB: - isSRGB = true; - case AZ::RHI::Format::ETC2A_UNORM: - return ePixelFormat_ETC2a; - case AZ::RHI::Format::ETC2A1_UNORM_SRGB: - isSRGB = true; - case AZ::RHI::Format::ETC2A1_UNORM: - return ePixelFormat_ETC2a1; - case AZ::RHI::Format::BC1_UNORM_SRGB: isSRGB = true; case AZ::RHI::Format::BC1_UNORM: @@ -225,22 +199,6 @@ namespace ImageProcessingAtom case ePixelFormat_ASTC_12x12: return isSrgb ? RHI::Format::ASTC_12x12_UNORM_SRGB : RHI::Format::ASTC_12x12_UNORM; - case ePixelFormat_PVRTC2: - return isSrgb ? RHI::Format::PVRTC2_UNORM_SRGB : RHI::Format::PVRTC2_UNORM; - case ePixelFormat_PVRTC4: - return isSrgb ? RHI::Format::PVRTC4_UNORM_SRGB : RHI::Format::PVRTC4_UNORM; - - case ePixelFormat_EAC_R11: - return RHI::Format::EAC_R11_UNORM; - case ePixelFormat_EAC_RG11: - return RHI::Format::EAC_RG11_UNORM; - case ePixelFormat_ETC2: - return isSrgb ? RHI::Format::ETC2_UNORM_SRGB : RHI::Format::ETC2_UNORM; - case ePixelFormat_ETC2a: - return isSrgb ? RHI::Format::ETC2A_UNORM_SRGB : RHI::Format::ETC2A_UNORM; - case ePixelFormat_ETC2a1: - return isSrgb ? RHI::Format::ETC2A1_UNORM_SRGB : RHI::Format::ETC2A1_UNORM; - case ePixelFormat_BC1: case ePixelFormat_BC1a: return isSrgb ? RHI::Format::BC1_UNORM_SRGB : RHI::Format::BC1_UNORM; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp index 084e38a2db..cdd63dca18 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp @@ -67,7 +67,7 @@ namespace ImageProcessingAtom m_renderWait.acquire(); } - void ImageThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) + void ImageThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; m_renderWait.release(); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h index 45fd178285..eadbfca945 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h @@ -34,7 +34,7 @@ namespace ImageProcessingAtom ~ImageThumbnail() override; //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; + void ThumbnailRendered(const QPixmap& thumbnailImage) override; void ThumbnailFailedToRender() override; protected: diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp index c72e1dc309..3e0bbd89eb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -145,6 +146,8 @@ namespace UnitTest AZ::Data::AssetManager::Descriptor desc; AZ::Data::AssetManager::Create(desc); + AZ::NameDictionary::Create(); + m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler()); m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler()); m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler()); @@ -153,6 +156,7 @@ namespace UnitTest //prepare reflection m_context = AZStd::make_unique(); + AZ::Name::Reflect(m_context.get()); BuilderPluginComponent::Reflect(m_context.get()); AZ::DataPatch::Reflect(m_context.get()); AZ::RHI::ReflectSystemComponent::Reflect(m_context.get()); @@ -164,6 +168,7 @@ namespace UnitTest m_jsonRegistrationContext = AZStd::make_unique(); m_jsonSystemComponent = AZStd::make_unique(); m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get()); + AZ::Name::Reflect(m_jsonRegistrationContext.get()); BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get()); // Setup job context for job system @@ -227,14 +232,18 @@ namespace UnitTest m_jsonRegistrationContext->EnableRemoveReflection(); m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get()); BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get()); + AZ::Name::Reflect(m_jsonRegistrationContext.get()); m_jsonRegistrationContext->DisableRemoveReflection(); m_jsonRegistrationContext.reset(); m_jsonSystemComponent.reset(); m_context.reset(); BuilderSettingManager::DestroyInstance(); + CPixelFormats::DestroyInstance(); + AZ::NameDictionary::Destroy(); + AZ::Data::AssetManager::Destroy(); AZ::AllocatorInstance::Destroy(); @@ -426,60 +435,6 @@ namespace UnitTest return isDifferent; } - - bool CompareDDSImage(const QString& imagePath1, const QString& imagePath2, QString& output) - { - IImageObjectPtr image1, alphaImage1, image2, alphaImage2; - - - image1 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(imagePath1.toUtf8().constData())); - if (image1 && image1->HasImageFlags(EIF_AttachedAlpha)) - { - if (image1->HasImageFlags(EIF_Splitted)) - { - alphaImage1 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(QString(imagePath1 + ".a").toUtf8().constData())); - } - else - { - alphaImage1 = IImageObjectPtr(DdsLoader::LoadAttachedImageFromDdsFileLegacy(imagePath1.toUtf8().constData(), image1)); - } - } - - image2 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(imagePath2.toUtf8().constData())); - if (image2 && image2->HasImageFlags(EIF_AttachedAlpha)) - { - if (image2->HasImageFlags(EIF_Splitted)) - { - alphaImage2 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(QString(imagePath2 + ".a").toUtf8().constData())); - } - else - { - alphaImage2 = IImageObjectPtr(DdsLoader::LoadAttachedImageFromDdsFileLegacy(imagePath2.toUtf8().constData(), image2)); - } - } - - if (!image1 && !image2) - { - output += "Cannot load both image file! "; - return false; - } - bool isDifferent = false; - - isDifferent = GetComparisonResult(image1, image2, output); - - - QFileInfo fi(imagePath1); - AZStd::string imageName = fi.baseName().toUtf8().constData(); - SaveImageToFile(image1, imageName + "_new"); - SaveImageToFile(image2, imageName + "_old"); - - if (alphaImage1 || alphaImage2) - { - isDifferent |= GetComparisonResult(alphaImage1, alphaImage2, output); - } - - return isDifferent; - } }; // test CPixelFormats related functions @@ -487,34 +442,6 @@ namespace UnitTest { CPixelFormats& pixelFormats = CPixelFormats::GetInstance(); - //verify names which was used for legacy rc.ini - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC7t") == ePixelFormat_BC7t); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("ETC2A") == ePixelFormat_ETC2a); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("PVRTC4") == ePixelFormat_PVRTC4); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC1") == ePixelFormat_BC1); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("ETC2") == ePixelFormat_ETC2); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC1a") == ePixelFormat_BC1a); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC3") == ePixelFormat_BC3); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC7") == ePixelFormat_BC7); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC5s") == ePixelFormat_BC5s); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("EAC_RG11") == ePixelFormat_EAC_RG11); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC4") == ePixelFormat_BC4); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("EAC_R11") == ePixelFormat_EAC_R11); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("A8R8G8B8") == ePixelFormat_R8G8B8A8); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC6UH") == ePixelFormat_BC6UH); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("R9G9B9E5") == ePixelFormat_R9G9B9E5); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("X8R8G8B8") == ePixelFormat_R8G8B8X8); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("A16B16G16R16F") == ePixelFormat_R16G16B16A16F); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G8R8") == ePixelFormat_R8G8); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G16R16") == ePixelFormat_R16G16); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G16R16F") == ePixelFormat_R16G16F); - - //some legacy format need to be mapping to new format. - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("DXT1") == ePixelFormat_BC1); - ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("DXT5") == ePixelFormat_BC3); - - //calculate mipmap count. no cubemap support at this moment - //for all the non-compressed textures, if there minimum required texture size is 1x1 for (uint32 i = 0; i < ePixelFormat_Count; i++) { @@ -543,13 +470,6 @@ namespace UnitTest } //check function IsImageSizeValid && EvaluateImageDataSize function - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 2, 1, false) == false); - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 4, 4, false) == false); - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 16, 16, false) == true); - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 16, 32, false) == false); - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 34, 34, false) == false); - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 256, 256, false) == true); - ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 2, 1, false) == false); ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 16, 16, false) == true); ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 16, 32, false) == true); @@ -834,9 +754,7 @@ namespace UnitTest if (formatInfo->bCompressed) { // skip ASTC formats which are tested in TestConvertASTCCompressor - if (!IsASTCFormat(pixelFormat) - && pixelFormat != ePixelFormat_PVRTC2 && pixelFormat != ePixelFormat_PVRTC4 - && !IsETCFormat(pixelFormat)) // skip ETC since it's very slow + if (!IsASTCFormat(pixelFormat)) { compressedFormats.push_back(pixelFormat); } @@ -1112,62 +1030,13 @@ namespace UnitTest } } - TEST_F(ImageProcessingTest, DISABLED_CompareOutputImage) - { - AZStd::string curretTextureFolder = "../TestAssets/TextureAssets/assets_new/textures"; - AZStd::string oldTextureFolder = "../TestAssets/TextureAssets/assets_old/textures"; - bool outputOnlyDifferent = false; - QDirIterator it(curretTextureFolder.c_str(), QStringList() << "*.dds", QDir::Files, QDirIterator::Subdirectories); - QFile f("../texture_comparison_output.csv"); - f.open(QIODevice::ReadWrite | QIODevice::Truncate); - // Write a header for csv file - f.write("Texture Name, Path, Mip new/old, MipDiff, Format new/old, Flag new/old, MemSize new/old, MemDiff, Error, AlphaMip new/old, AlphaMipDiff, AlphaFormat new/old, AlphaFlag new/old, AlphaMemSize new/old, AlphaMemDiff, AlphaError\r\n"); - int i = 0; - while (it.hasNext()) - { - i++; - it.next(); - - QString fileName = it.fileName(); - QString newFilePath = it.filePath(); - QString sharedPath = QString(newFilePath).remove(curretTextureFolder.c_str()); - QString oldFilePath = QString(oldTextureFolder.c_str()) + sharedPath; - QString output; - if (QFile::exists(oldFilePath)) - { - bool isDifferent = CompareDDSImage(newFilePath, oldFilePath, output); - if (outputOnlyDifferent && !isDifferent) - { - continue; - } - else - { - f.write(fileName.toUtf8().constData()); - f.write(","); - f.write(sharedPath.toUtf8().constData()); - f.write(output.toUtf8().constData()); - } - } - else - { - f.write(fileName.toUtf8().constData()); - f.write(","); - f.write(sharedPath.toUtf8().constData()); - output += ",No old file for comparison!"; - f.write(output.toUtf8().constData()); - } - f.write("\r\n"); - } - f.close(); - } - TEST_F(ImageProcessingTest, TextureSettingReflect_SerializingModernDataInAndOut_WritesAndParsesFileAccurately) { AZStd::string filepath = "test.xml"; // Fill-in structure with test data TextureSettings fakeTextureSettings; - fakeTextureSettings.m_preset = AZ::Uuid::CreateRandom(); + fakeTextureSettings.m_preset = "testPreset"; fakeTextureSettings.m_sizeReduceLevel = 0; fakeTextureSettings.m_suppressEngineReduce = true; fakeTextureSettings.m_enableMipmap = false; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings deleted file mode 100644 index 7bce3041b9..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /bumptype=none /M=62,18,32,83,50,50 /preset=Diffuse_highQ /mipgentype=kaiser /reduce="android:0,ios:3,mac:0,pc:4,provo:1" /ser=0 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake index ba9e9f29b7..aad400518d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake @@ -120,10 +120,6 @@ set(FILES Source/Compressors/Compressor.cpp Source/Compressors/CTSquisher.h Source/Compressors/CTSquisher.cpp - Source/Compressors/PVRTC.cpp - Source/Compressors/PVRTC.h - Source/Compressors/ETC2.cpp - Source/Compressors/ETC2.h Source/Compressors/CryTextureSquisher/ColorBlockRGBA4x4f.cpp Source/Compressors/CryTextureSquisher/ColorBlockRGBA4x4s.cpp Source/Compressors/CryTextureSquisher/ColorBlockRGBA4x4c.cpp diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset deleted file mode 100644 index 346f0f8cac..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset +++ /dev/null @@ -1,104 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", - "Name": "AlbedoWithOpacity", - "RGB_Weight": "CIEXYZ", - "FileMasks": [ - "_diff", - "_color", - "_albedo", - "_alb", - "_basecolor", - "_bc", - "_diffuse" - ], - "PixelFormat": "BC7t", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", - "Name": "AlbedoWithOpacity", - "RGB_Weight": "CIEXYZ", - "FileMasks": [ - "_diff", - "_color", - "_albedo", - "_alb", - "_basecolor", - "_bc", - "_diffuse" - ], - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", - "Name": "AlbedoWithOpacity", - "RGB_Weight": "CIEXYZ", - "FileMasks": [ - "_diff", - "_color", - "_albedo", - "_alb", - "_basecolor", - "_bc", - "_diffuse" - ], - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", - "Name": "AlbedoWithOpacity", - "RGB_Weight": "CIEXYZ", - "FileMasks": [ - "_diff", - "_color", - "_albedo", - "_alb", - "_basecolor", - "_bc", - "_diffuse" - ], - "PixelFormat": "BC3", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", - "Name": "AlbedoWithOpacity", - "RGB_Weight": "CIEXYZ", - "FileMasks": [ - "_diff", - "_color", - "_albedo", - "_alb", - "_basecolor", - "_bc", - "_diffuse" - ], - "PixelFormat": "BC7t", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset deleted file mode 100644 index 8d1105cdfc..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset +++ /dev/null @@ -1,44 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", - "Name": "CloudShadows", - "DestColor": "Linear", - "PixelFormat": "BC4", - "IsPowerOf2": true - }, - "PlatformsPresets": { - "android": { - "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", - "Name": "CloudShadows", - "DestColor": "Linear", - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true - }, - "ios": { - "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", - "Name": "CloudShadows", - "DestColor": "Linear", - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true - }, - "mac": { - "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", - "Name": "CloudShadows", - "DestColor": "Linear", - "PixelFormat": "BC4", - "IsPowerOf2": true - }, - "provo": { - "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", - "Name": "CloudShadows", - "DestColor": "Linear", - "PixelFormat": "BC4", - "IsPowerOf2": true - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset deleted file mode 100644 index 5f0480cee7..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset +++ /dev/null @@ -1,64 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", - "Name": "ColorChart", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_cch" - ], - "PixelFormat": "R8G8B8X8", - "IsColorChart": true - }, - "PlatformsPresets": { - "android": { - "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", - "Name": "ColorChart", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_cch" - ], - "PixelFormat": "R8G8B8X8", - "IsColorChart": true - }, - "ios": { - "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", - "Name": "ColorChart", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_cch" - ], - "PixelFormat": "R8G8B8X8", - "IsColorChart": true - }, - "mac": { - "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", - "Name": "ColorChart", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_cch" - ], - "PixelFormat": "R8G8B8X8", - "IsColorChart": true - }, - "provo": { - "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", - "Name": "ColorChart", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_cch" - ], - "PixelFormat": "R8G8B8X8", - "IsColorChart": true - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset deleted file mode 100644 index 5bfea9a376..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset +++ /dev/null @@ -1,79 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", - "Name": "Detail_MergedAlbedoNormalsSmoothness", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "PixelFormat": "BC7", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", - "Name": "Detail_MergedAlbedoNormalsSmoothness", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", - "Name": "Detail_MergedAlbedoNormalsSmoothness", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", - "Name": "Detail_MergedAlbedoNormalsSmoothness", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "PixelFormat": "BC3", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", - "Name": "Detail_MergedAlbedoNormalsSmoothness", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "PixelFormat": "BC7", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset deleted file mode 100644 index fec11218e8..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset +++ /dev/null @@ -1,74 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", - "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", - "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", - "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", - "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", - "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_detail" - ], - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLGlobal.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLGlobal.preset index eb829c3120..717157f2aa 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLGlobal.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLGlobal.preset @@ -15,9 +15,9 @@ ], "CubemapSettings": { "GenerateIBLSpecular": true, - "IBLSpecularPreset": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", + "IBLSpecularPreset": "IBLSpecular", "GenerateIBLDiffuse": true, - "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" + "IBLDiffusePreset": "IBLDiffuse" } } } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset index 402fc470eb..eee9af4cea 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset @@ -20,9 +20,9 @@ "CubemapSettings": { "RequiresConvolve": false, "GenerateIBLSpecular": true, - "IBLSpecularPreset": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", + "IBLSpecularPreset": "IBLSpecular", "GenerateIBLDiffuse": true, - "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" + "IBLDiffusePreset": "IBLDiffuse" } }, "PlatformsPresets": { @@ -42,9 +42,9 @@ "CubemapSettings": { "RequiresConvolve": false, "GenerateIBLSpecular": true, - "IBLSpecularPreset": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", + "IBLSpecularPreset": "IBLSpecular", "GenerateIBLDiffuse": true, - "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" + "IBLDiffusePreset": "IBLDiffuse" } }, "ios": { @@ -63,9 +63,9 @@ "CubemapSettings": { "RequiresConvolve": false, "GenerateIBLSpecular": true, - "IBLSpecularPreset": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", + "IBLSpecularPreset": "IBLSpecular", "GenerateIBLDiffuse": true, - "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" + "IBLDiffusePreset": "IBLDiffuse" } }, "mac": { @@ -84,9 +84,9 @@ "CubemapSettings": { "RequiresConvolve": false, "GenerateIBLSpecular": true, - "IBLSpecularPreset": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", + "IBLSpecularPreset": "IBLSpecular", "GenerateIBLDiffuse": true, - "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" + "IBLDiffusePreset": "IBLDiffuse" } }, "provo": { @@ -105,9 +105,9 @@ "CubemapSettings": { "RequiresConvolve": false, "GenerateIBLSpecular": true, - "IBLSpecularPreset": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", + "IBLSpecularPreset": "IBLSpecular", "GenerateIBLDiffuse": true, - "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" + "IBLDiffusePreset": "IBLDiffuse" } } } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings b/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings index c513e05a97..466ac4b71d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings @@ -43,29 +43,25 @@ } }, "DefaultPresetsByFileMask": { - "_basecolor": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", - "_cch": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", - "_ccm": "{2174E04B-73BB-4DF1-8961-4900DC3C9D72}", - "_cm": "{A13B2FCE-2F6A-4634-B112-5A0A912B50CE}", - "_ddn": "{508B21D5-5250-4003-97EC-1CF28D571ACF}", - "_ddna": "{6EE749F4-846E-4F7A-878C-F211F85EA59F}", - "_diff": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", - "_diffuse": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", - "_glossness": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", - "_ibldiffusecm": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}", - "_iblskyboxcm": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}", - "_iblglobalcm": "{A13B2FCE-2F6A-4634-B112-5A0A912B50CE}", - "_iblspecularcm": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", - "_metallic": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", - "_normal": "{508B21D5-5250-4003-97EC-1CF28D571ACF}", - "_refl": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", - "_roughness": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", - "_skyboxcm": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}", - "_spec": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", - "_specular": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}" + "_basecolor": "Albedo", + "_diff": "Albedo", + "_diffuse": "Albedo", + "_ddn": "Normals", + "_normal": "Normals", + "_ddna": "NormalsWithSmoothness", + "_glossness": "Reflectance", + "_spec": "Reflectance", + "_specular": "Reflectance", + "_metallic": "Reflectance", + "_refl": "Reflectance", + "_roughness": "Reflectance", + "_ibldiffusecm": "IBLDiffuse", + "_iblskyboxcm": "IBLSkybox", + "_iblspecularcm": "IBLSpecular", + "_skyboxcm": "Skybox" }, - "DefaultPreset": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", - "DefaultPresetAlpha": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}", - "DefaultPresetNonePOT": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}" + "DefaultPreset": "Albedo", + "DefaultPresetAlpha": "AlbedoWithGenericAlpha", + "DefaultPresetNonePOT": "ReferenceImage" } } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset deleted file mode 100644 index d277785151..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset +++ /dev/null @@ -1,34 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", - "Name": "LensOptics", - "PixelFormat": "BC1" - }, - "PlatformsPresets": { - "android": { - "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", - "Name": "LensOptics", - "PixelFormat": "ASTC_4x4" - }, - "ios": { - "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", - "Name": "LensOptics", - "PixelFormat": "ASTC_4x4" - }, - "mac": { - "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", - "Name": "LensOptics", - "PixelFormat": "BC1" - }, - "provo": { - "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", - "Name": "LensOptics", - "PixelFormat": "BC1" - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset deleted file mode 100644 index 4de95427d6..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset +++ /dev/null @@ -1,59 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", - "Name": "LightProjector", - "DestColor": "Linear", - "PixelFormat": "BC4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", - "Name": "LightProjector", - "DestColor": "Linear", - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", - "Name": "LightProjector", - "DestColor": "Linear", - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", - "Name": "LightProjector", - "DestColor": "Linear", - "PixelFormat": "BC4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", - "Name": "LightProjector", - "DestColor": "Linear", - "PixelFormat": "BC4", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset deleted file mode 100644 index ad0e2ddf06..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset +++ /dev/null @@ -1,34 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", - "Name": "LoadingScreen", - "PixelFormat": "R8G8B8X8" - }, - "PlatformsPresets": { - "android": { - "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", - "Name": "LoadingScreen", - "PixelFormat": "R8G8B8X8" - }, - "ios": { - "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", - "Name": "LoadingScreen", - "PixelFormat": "R8G8B8X8" - }, - "mac": { - "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", - "Name": "LoadingScreen", - "PixelFormat": "R8G8B8X8" - }, - "provo": { - "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", - "Name": "LoadingScreen", - "PixelFormat": "R8G8B8X8" - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset deleted file mode 100644 index 79dda1977e..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset +++ /dev/null @@ -1,64 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", - "Name": "Minimap", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true, - "SizeReduceLevel": 1, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", - "Name": "Minimap", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "SizeReduceLevel": 1, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", - "Name": "Minimap", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "SizeReduceLevel": 1, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", - "Name": "Minimap", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true, - "SizeReduceLevel": 1, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", - "Name": "Minimap", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true, - "SizeReduceLevel": 1, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset deleted file mode 100644 index b02227b454..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset +++ /dev/null @@ -1,59 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", - "Name": "MuzzleFlash", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", - "Name": "MuzzleFlash", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", - "Name": "MuzzleFlash", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", - "Name": "MuzzleFlash", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", - "Name": "MuzzleFlash", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset index 04307eada4..eee0b88686 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset @@ -11,6 +11,7 @@ "FileMasks": [ "_ddn", "_normal", + "_normalmap", "_normals", "_norm", "_nor", @@ -35,6 +36,7 @@ "FileMasks": [ "_ddn", "_normal", + "_normalmap", "_normals", "_norm", "_nor", @@ -59,6 +61,7 @@ "FileMasks": [ "_ddn", "_normal", + "_normalmap", "_normals", "_norm", "_nor", @@ -83,6 +86,7 @@ "FileMasks": [ "_ddn", "_normal", + "_normalmap", "_normals", "_norm", "_nor", @@ -106,6 +110,7 @@ "FileMasks": [ "_ddn", "_normal", + "_normalmap", "_normals", "_norm", "_nor", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset deleted file mode 100644 index a960ae41b2..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset +++ /dev/null @@ -1,86 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", - "Name": "NormalsFromDisplacement", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_bump" - ], - "PixelFormat": "BC5s", - "IsPowerOf2": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", - "Name": "NormalsFromDisplacement", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_bump" - ], - "PixelFormat": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", - "Name": "NormalsFromDisplacement", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_bump" - ], - "PixelFormat": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", - "Name": "NormalsFromDisplacement", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_bump" - ], - "PixelFormat": "BC5s", - "IsPowerOf2": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", - "Name": "NormalsFromDisplacement", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_bump" - ], - "PixelFormat": "BC5s", - "IsPowerOf2": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset deleted file mode 100644 index f7cba42886..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset +++ /dev/null @@ -1,101 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", - "Name": "NormalsWithSmoothness_Legacy", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_ddna" - ], - "PixelFormat": "BC5s", - "PixelFormatAlpha": "BC4", - "IsPowerOf2": true, - "GlossFromNormal": 1, - "UseLegacyGloss": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", - "Name": "NormalsWithSmoothness_Legacy", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_ddna" - ], - "PixelFormat": "ASTC_4x4", - "PixelFormatAlpha": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "GlossFromNormal": 1, - "UseLegacyGloss": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", - "Name": "NormalsWithSmoothness_Legacy", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_ddna" - ], - "PixelFormat": "ASTC_4x4", - "PixelFormatAlpha": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "GlossFromNormal": 1, - "UseLegacyGloss": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", - "Name": "NormalsWithSmoothness_Legacy", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_ddna" - ], - "PixelFormat": "BC5s", - "PixelFormatAlpha": "BC4", - "IsPowerOf2": true, - "GlossFromNormal": 1, - "UseLegacyGloss": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", - "Name": "NormalsWithSmoothness_Legacy", - "SourceColor": "Linear", - "DestColor": "Linear", - "FileMasks": [ - "_ddna" - ], - "PixelFormat": "BC5s", - "PixelFormatAlpha": "BC4", - "IsPowerOf2": true, - "GlossFromNormal": 1, - "UseLegacyGloss": true, - "MipRenormalize": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset deleted file mode 100644 index 7a0c137f07..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset +++ /dev/null @@ -1,71 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", - "Name": "ReflectanceWithSmoothness_Legacy", - "FileMasks": [ - "_spec" - ], - "PixelFormat": "BC7", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", - "Name": "ReflectanceWithSmoothness_Legacy", - "FileMasks": [ - "_spec" - ], - "PixelFormat": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", - "Name": "ReflectanceWithSmoothness_Legacy", - "FileMasks": [ - "_spec" - ], - "PixelFormat": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", - "Name": "ReflectanceWithSmoothness_Legacy", - "FileMasks": [ - "_spec" - ], - "PixelFormat": "BC3", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", - "Name": "ReflectanceWithSmoothness_Legacy", - "FileMasks": [ - "_spec" - ], - "PixelFormat": "BC7", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset deleted file mode 100644 index f59fd594f4..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset +++ /dev/null @@ -1,81 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", - "Name": "Reflectance_Linear", - "DestColor": "Linear", - "FileMasks": [ - "_spec", - "_refl" - ], - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", - "Name": "Reflectance_Linear", - "DestColor": "Linear", - "FileMasks": [ - "_spec", - "_refl" - ], - "PixelFormat": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", - "Name": "Reflectance_Linear", - "DestColor": "Linear", - "FileMasks": [ - "_spec", - "_refl" - ], - "PixelFormat": "ASTC_4x4", - "MaxTextureSize": 2048, - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", - "Name": "Reflectance_Linear", - "DestColor": "Linear", - "FileMasks": [ - "_spec", - "_refl" - ], - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", - "Name": "Reflectance_Linear", - "DestColor": "Linear", - "FileMasks": [ - "_spec", - "_refl" - ], - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset deleted file mode 100644 index f76741148c..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset +++ /dev/null @@ -1,49 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", - "Name": "SF_Font", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "PlatformsPresets": { - "android": { - "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", - "Name": "SF_Font", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "ios": { - "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", - "Name": "SF_Font", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "mac": { - "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", - "Name": "SF_Font", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "provo": { - "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", - "Name": "SF_Font", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset deleted file mode 100644 index aff25dc83d..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset +++ /dev/null @@ -1,49 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", - "Name": "SF_Gradient", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "PlatformsPresets": { - "android": { - "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", - "Name": "SF_Gradient", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "ios": { - "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", - "Name": "SF_Gradient", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "mac": { - "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", - "Name": "SF_Gradient", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - }, - "provo": { - "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", - "Name": "SF_Gradient", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "IsPowerOf2": true - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset deleted file mode 100644 index 191425bb92..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset +++ /dev/null @@ -1,54 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", - "Name": "SF_Image", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true - }, - "PlatformsPresets": { - "android": { - "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", - "Name": "SF_Image", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true - }, - "ios": { - "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", - "Name": "SF_Image", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_4x4", - "IsPowerOf2": true - }, - "mac": { - "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", - "Name": "SF_Image", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true - }, - "provo": { - "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", - "Name": "SF_Image", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "BC1", - "IsPowerOf2": true - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset deleted file mode 100644 index 9b1a9c7c45..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset +++ /dev/null @@ -1,49 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", - "Name": "SF_Image_nonpower2", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "BC1" - }, - "PlatformsPresets": { - "android": { - "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", - "Name": "SF_Image_nonpower2", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_4x4" - }, - "ios": { - "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", - "Name": "SF_Image_nonpower2", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "ASTC_4x4" - }, - "mac": { - "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", - "Name": "SF_Image_nonpower2", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "BC1" - }, - "provo": { - "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", - "Name": "SF_Image_nonpower2", - "SourceColor": "Linear", - "DestColor": "Linear", - "SuppressEngineReduce": true, - "PixelFormat": "BC1" - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset deleted file mode 100644 index 8b12a08465..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset +++ /dev/null @@ -1,69 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", - "Name": "Terrain_Albedo", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "BC1", - "IsPowerOf2": true, - "HighPassMip": 5, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", - "Name": "Terrain_Albedo", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "HighPassMip": 5, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", - "Name": "Terrain_Albedo", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "HighPassMip": 5, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", - "Name": "Terrain_Albedo", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "BC1", - "IsPowerOf2": true, - "HighPassMip": 5, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", - "Name": "Terrain_Albedo", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "BC1", - "IsPowerOf2": true, - "HighPassMip": 5, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset deleted file mode 100644 index d24531858f..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset +++ /dev/null @@ -1,64 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", - "Name": "Terrain_Albedo_HighPassed", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", - "Name": "Terrain_Albedo_HighPassed", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", - "Name": "Terrain_Albedo_HighPassed", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "ASTC_6x6", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", - "Name": "Terrain_Albedo_HighPassed", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", - "Name": "Terrain_Albedo_HighPassed", - "SourceColor": "Linear", - "DestColor": "Linear", - "PixelFormat": "BC1", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset deleted file mode 100644 index 6e28cafe11..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset +++ /dev/null @@ -1,54 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "MultiplatformPresetSettings", - "ClassData": { - "DefaultPreset": { - "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", - "Name": "Uncompressed", - "PixelFormat": "R8G8B8X8", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "PlatformsPresets": { - "android": { - "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", - "Name": "Uncompressed", - "PixelFormat": "R8G8B8X8", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "ios": { - "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", - "Name": "Uncompressed", - "PixelFormat": "R8G8B8X8", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "mac": { - "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", - "Name": "Uncompressed", - "PixelFormat": "R8G8B8X8", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - }, - "provo": { - "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", - "Name": "Uncompressed", - "PixelFormat": "R8G8B8X8", - "IsPowerOf2": true, - "MipMapSetting": { - "MipGenType": "Box" - } - } - } - } -} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index 26a7e21a6a..11859de7d6 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -185,7 +185,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float2 normalUv = IN.m_uv[MaterialSrg::m_normalMapUvIndex]; float3x3 uvMatrix = MaterialSrg::m_normalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); // By design, only UV0 is allowed to apply transforms. float detailLayerNormalFactor = MaterialSrg::m_detail_normal_factor * detailLayerBlendFactor; - + surface.vertexNormal = normalize(IN.m_normal); surface.normal = GetDetailedNormalInputWS( isFrontFace, IN.m_normal, tangents[MaterialSrg::m_normalMapUvIndex], bitangents[MaterialSrg::m_normalMapUvIndex], MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_normalFactor, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, uvMatrix, o_normal_useTexture, diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 97cbfb2622..523353f8fa 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -9,7 +9,7 @@ #include "Skin_Common.azsli" // SRGs -#include +#include #include // Pass Output @@ -237,6 +237,7 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture4, normalMapSample, MaterialSrg::m_wrinkle_normal_texture4, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_wrinkleBlendFactors.a); } + surface.vertexNormal = normalize(IN.m_normal); if(o_detail_normal_useTexture) { float3 normalTS = GetTangentSpaceNormal(normalMapSample, uvMatrix, MaterialSrg::m_normalFactor); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index 0969cc36e8..e4da9c6022 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -973,15 +973,15 @@ "tag": "ForwardPass" }, { - "file": "Shaders/Shadow/Shadowmap.shader", + "file": "Shaders/Shadow/ShadowmapSkin.shader", "tag": "Shadowmap" }, { - "file": "Shaders/Depth/DepthPass.shader", + "file": "Shaders/Depth/DepthPassSkin.shader", "tag": "DepthPass" }, { - "file": "Shaders/MotionVector/MeshMotionVector.shader", + "file": "Shaders/MotionVector/MeshMotionVectorSkin.shader", "tag": "MeshMotionVector" } ], diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 5d2aec063b..6a97c0e785 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -445,6 +445,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); } // [GFX TODO][ATOM-14591]: This will only work if the normal maps all use the same UV stream. We would need to add support for having them in different UV streams. + surface.vertexNormal = normalize(IN.m_normal); surface.normal = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex])); // ------- Combine Albedo, roughness, specular, roughness --------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index 1fa5fc683c..8df5e1ba56 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -146,7 +146,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float2 normalUv = IN.m_uv[MaterialSrg::m_normalMapUvIndex]; float3x3 uvMatrix = MaterialSrg::m_normalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); // By design, only UV0 is allowed to apply transforms. - + surface.vertexNormal = normalize(IN.m_normal); surface.normal = GetNormalInputWS(MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, isFrontFace, IN.m_normal, tangents[MaterialSrg::m_normalMapUvIndex], bitangents[MaterialSrg::m_normalMapUvIndex], uvMatrix, o_normal_useTexture, MaterialSrg::m_normalFactor); diff --git a/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass index f45f5c8b07..752d565234 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass @@ -472,36 +472,15 @@ "Pass": "Parent", "Attachment": "DepthLinear" } - } - ] - }, - { - "Name": "ModulateWithSsao", - "TemplateName": "ModulateTextureTemplate", - "Enabled": true, - "Connections": [ - { - "LocalSlot": "Input", - "AttachmentRef": { - "Pass": "Ssao", - "Attachment": "Output" - } }, { - "LocalSlot": "InputOutput", + "LocalSlot": "Modulate", "AttachmentRef": { "Pass": "SubsurfaceScatteringPass", "Attachment": "Output" } } - ], - "PassData": { - "$type": "ComputePassData", - "ShaderAsset": { - "FilePath": "Shaders/PostProcessing/ModulateTexture.shader" - }, - "Make Fullscreen Pass": true - } + ] }, { "Name": "DiffuseSpecularMergePass", @@ -510,8 +489,8 @@ { "LocalSlot": "InputDiffuse", "AttachmentRef": { - "Pass": "ModulateWithSsao", - "Attachment": "InputOutput" + "Pass": "Ssao", + "Attachment": "Output" } }, { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SsaoCompute.pass b/Gems/Atom/Feature/Common/Assets/Passes/SsaoCompute.pass index 034b4359b4..3c4a8c76f1 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/SsaoCompute.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/SsaoCompute.pass @@ -51,7 +51,13 @@ }, "Make Fullscreen Pass": true, "PipelineViewTag": "MainCamera" - } + }, + "FallbackConnections": [ + { + "Input": "LinearDepth", + "Output": "Output" + } + ] } } } diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SsaoParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/SsaoParent.pass index b94ca4ab38..111518706f 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/SsaoParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/SsaoParent.pass @@ -12,6 +12,11 @@ "SlotType": "Input", "ScopeAttachmentUsage": "Shader" }, + { + "Name": "Modulate", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, { "Name": "Output", "SlotType": "Output", @@ -22,8 +27,8 @@ { "LocalSlot": "Output", "AttachmentRef": { - "Pass": "Upsample", - "Attachment": "Output" + "Pass": "ModulateWithSsao", + "Attachment": "InputOutput" } } ], @@ -103,6 +108,34 @@ } } ] + }, + { + "Name": "ModulateWithSsao", + "TemplateName": "ModulateTextureTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "Upsample", + "Attachment": "Output" + } + }, + { + "LocalSlot": "InputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Modulate" + } + } + ], + "PassData": { + "$type": "ComputePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/ModulateTexture.shader" + }, + "Make Fullscreen Pass": true + } } ] } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index 7a2d2ee428..10526597cb 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -27,16 +27,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); } - //[GFX TODO][ATOM-15280] Move wrinkle mask data from the default object srg into something specific to the Skin shader - uint m_wrinkle_mask_count; - float4 m_wrinkle_mask_weights[4]; - Texture2D m_wrinkle_masks[16]; - - float GetWrinkleMaskWeight(uint index) - { - return m_wrinkle_mask_weights[index / 4][index % 4]; - } - //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli index 34c17e388c..1eee53a24f 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli @@ -24,7 +24,7 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData) litRatio = DirectionalLightShadow::GetVisibility( shadowIndex, lightingData.shadowCoords, - surface.normal, + surface.vertexNormal, debugInfo); if (o_transmission_mode == TransmissionMode::ThickObject) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli index ac9227f64a..63f671f021 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli @@ -86,7 +86,7 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat light.m_position, surface.position, -dirToConeTip, - surface.normal); + surface.vertexNormal); // Use backShadowRatio to carry thickness from shadow map for thick mode backShadowRatio = 1.0 - litRatio; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli index b88b6574a4..e6eea9728f 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli @@ -83,7 +83,7 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD light.m_position, surface.position, lightDir, - surface.normal); + surface.vertexNormal); // Use backShadowRatio to carry thickness from shadow map for thick mode backShadowRatio = 1.0 - litRatio; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/EnhancedSurface.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/EnhancedSurface.azsli index 1b0ac3d76d..eb8c33cdce 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/EnhancedSurface.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/EnhancedSurface.azsli @@ -23,6 +23,7 @@ class Surface float3 position; //!< Position in world-space float3 normal; //!< Normal in world-space + float3 vertexNormal; //!< Vertex normal in world-space float3 albedo; //!< Albedo color of the non-metallic material, will be multiplied against the diffuse lighting value float3 specularF0; //!< Fresnel f0 spectral value of the surface float roughnessLinear; //!< Perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/SkinSurface.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/SkinSurface.azsli index 40f30d4f03..f82d80adee 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/SkinSurface.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/SkinSurface.azsli @@ -22,6 +22,7 @@ class Surface float3 position; //!< Position in world-space float3 normal; //!< Normal in world-space + float3 vertexNormal; //!< Vertex normal in world-space float3 albedo; //!< Albedo color of the non-metallic material, will be multiplied against the diffuse lighting value float3 specularF0; //!< Fresnel f0 spectral value of the surface float roughnessLinear; //!< Perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli index 30e90323a5..084943bf38 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli @@ -22,6 +22,7 @@ class Surface float3 position; //!< Position in world-space float3 normal; //!< Normal in world-space + float3 vertexNormal; //!< Vertex normal in world-space float3 albedo; //!< Albedo color of the non-metallic material, will be multiplied against the diffuse lighting value float3 specularF0; //!< Fresnel f0 spectral value of the surface float roughnessLinear; //!< Perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli new file mode 100644 index 0000000000..d0766c295d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli @@ -0,0 +1,81 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +ShaderResourceGroup ObjectSrg : SRG_PerObject +{ + uint m_objectId; + + //! Returns the matrix for transforming points from Object Space to World Space. + float4x4 GetWorldMatrix() + { + return SceneSrg::GetObjectToWorldMatrix(m_objectId); + } + + //! Returns the inverse-transpose of the world matrix. + //! Commonly used to transform normals while supporting non-uniform scale. + float3x3 GetWorldMatrixInverseTranspose() + { + return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); + } + + uint m_wrinkle_mask_count; + float4 m_wrinkle_mask_weights[4]; + Texture2D m_wrinkle_masks[16]; + + float GetWrinkleMaskWeight(uint index) + { + return m_wrinkle_mask_weights[index / 4][index % 4]; + } + + //! Reflection Probe (smallest probe volume that overlaps the object position) + struct ReflectionProbeData + { + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; + float m_padding; + bool m_useReflectionProbe; + bool m_useParallaxCorrection; + }; + + ReflectionProbeData m_reflectionProbeData; + TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl index 8eb657f8f5..876f4b1a61 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl @@ -6,30 +6,7 @@ * */ -#include #include +#include -struct VSInput -{ - float3 m_position : POSITION; -}; - -struct VSDepthOutput -{ - float4 m_position : SV_Position; -}; - -VSDepthOutput DepthPassVS(VSInput IN) -{ - VSDepthOutput OUT; - - float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); - float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0)); - OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition); - - return OUT; -} - - - - +// Use the depth pass shader with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli new file mode 100644 index 0000000000..f658dd13da --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli @@ -0,0 +1,36 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +struct VSInput +{ + float3 m_position : POSITION; +}; + +struct VSDepthOutput +{ + float4 m_position : SV_Position; +}; + +VSDepthOutput DepthPassVS(VSInput IN) +{ + VSDepthOutput OUT; + + float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); + float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0)); + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition); + + return OUT; +} + + + + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl new file mode 100644 index 0000000000..0947be6cf8 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl @@ -0,0 +1,12 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +// Use the depth pass shader with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader new file mode 100644 index 0000000000..de6c989223 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader @@ -0,0 +1,24 @@ +{ + "Source" : "DepthPassSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "CompilerHints" : { + "DisableOptimizations" : false + }, + + "ProgramSettings" : + { + "EntryPoints": + [ + { + "name": "DepthPassVS", + "type" : "Vertex" + } + ] + }, + + "DrawList" : "depth" +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl index 11cf20dcaf..a14d07194f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl @@ -6,77 +6,7 @@ * */ -#include -#include - #include -#include +#include -struct VSInput -{ - float3 m_position : POSITION; - - // This gets set automatically by the system at runtime only if it's available. - // There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available. - // (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). - // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. - // Vertex position of last frame to capture small scale motion due to vertex animation - float3 m_optional_prevPosition : POSITIONT; -}; - -struct VSOutput -{ - float4 m_position : SV_Position; - float3 m_worldPos : TEXCOORD0; - float3 m_worldPosPrev: TEXCOORD1; -}; - -struct PSOutput -{ - float2 m_motion : SV_Target0; -}; - -// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition. -// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition. -// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). -// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. -option bool o_prevPosition_isBound; - -VSOutput MainVS(VSInput IN) -{ - VSOutput OUT; - - OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; - OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0)); - - if (o_prevPosition_isBound) - { - OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz; - } - else - { - OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; - } - - return OUT; -} - -PSOutput MainPS(VSOutput IN) -{ - PSOutput OUT; - - // Current clip position - float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0)); - - // Reprojected last frame's clip position, for skinned mesh it also implies last key frame - float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0)); - - float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; - - OUT.m_motion = motion; - - // Flip y to line up with uv coordinates - OUT.m_motion.y = -OUT.m_motion.y; - - return OUT; -} +// Use the mesh motion vector with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli new file mode 100644 index 0000000000..c934b056db --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli @@ -0,0 +1,83 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include + +struct VSInput +{ + float3 m_position : POSITION; + + // This gets set automatically by the system at runtime only if it's available. + // There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available. + // (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). + // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. + // Vertex position of last frame to capture small scale motion due to vertex animation + float3 m_optional_prevPosition : POSITIONT; +}; + +struct VSOutput +{ + float4 m_position : SV_Position; + float3 m_worldPos : TEXCOORD0; + float3 m_worldPosPrev: TEXCOORD1; +}; + +struct PSOutput +{ + float2 m_motion : SV_Target0; +}; + +// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition. +// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition. +// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). +// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. +option bool o_prevPosition_isBound; + +VSOutput MainVS(VSInput IN) +{ + VSOutput OUT; + + OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0)); + + if (o_prevPosition_isBound) + { + OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz; + } + else + { + OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; + } + + return OUT; +} + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + // Current clip position + float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0)); + + // Reprojected last frame's clip position, for skinned mesh it also implies last key frame + float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0)); + + float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; + + OUT.m_motion = motion; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + + return OUT; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl new file mode 100644 index 0000000000..fc089ac7a3 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl @@ -0,0 +1,12 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +// Use the mesh motion vector with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader new file mode 100644 index 0000000000..9a50e4e2cf --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader @@ -0,0 +1,24 @@ +{ + "Source" : "MeshMotionVectorSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "DrawList" : "motion", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl index 713ff7c393..7a958a73ca 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl @@ -6,27 +6,7 @@ * */ -#include -#include #include +#include -struct VertexInput -{ - float3 m_position : POSITION; -}; - -struct VertexOutput -{ - float4 m_position : SV_Position; -}; - -VertexOutput MainVS(VertexInput input) -{ - const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix(); - VertexOutput output; - - const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz; - output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); - - return output; -} +// Use the shadowmap shader with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli new file mode 100644 index 0000000000..213fb18dc4 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +struct VertexInput +{ + float3 m_position : POSITION; +}; + +struct VertexOutput +{ + float4 m_position : SV_Position; +}; + +VertexOutput MainVS(VertexInput input) +{ + const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix(); + VertexOutput output; + + const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz; + output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); + + return output; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl new file mode 100644 index 0000000000..6f0d8e1a31 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl @@ -0,0 +1,12 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +// Use the shadowmap shader with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader new file mode 100644 index 0000000000..14c1352c08 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader @@ -0,0 +1,26 @@ +{ + "Source" : "ShadowmapSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } + }, + + "DrawList" : "shadow", + + "RasterState" : + { + "depthBias" : "10", + "depthBiasSlopeScale" : "4" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h index 3c3ddc7ddc..fea6d090f7 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h @@ -51,7 +51,7 @@ namespace AZ } //! Returns the buffer asset that is used for all skinned mesh outputs - virtual Data::Asset GetBufferAsset() const = 0; + virtual Data::Asset GetBufferAsset() = 0; //! Returns the buffer that is used for all skinned mesh outputs virtual Data::Instance GetBuffer() = 0; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h index 37835bd6a8..82cc1e7d50 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h @@ -30,6 +30,7 @@ namespace AZ::Render void Clear(); IndexType GetFreeSlotIndex(); void RemoveIndex(IndexType index); + void RemoveData(DataType* data); DataType& GetData(IndexType index); const DataType& GetData(IndexType index) const; @@ -42,6 +43,7 @@ namespace AZ::Render const AZStd::vector& GetIndexVector() const; IndexType GetRawIndex(IndexType index) const; + IndexType GetIndexForData(const DataType* data) const; private: constexpr static size_t InitialReservedSize = 128; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl index 076caad7f4..581186dbcc 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl @@ -83,6 +83,16 @@ namespace AZ::Render m_indices.at(index) = m_firstFreeSlot; m_firstFreeSlot = index; } + + template + inline void IndexedDataVector::RemoveData(DataType* data) + { + IndexType indexForData = GetIndexForData(data); + if (indexForData != NoFreeSlot) + { + RemoveIndex(indexForData); + } + } template inline DataType& IndexedDataVector::GetData(IndexType index) @@ -131,4 +141,14 @@ namespace AZ::Render { return m_indices.at(index); } + + template + IndexType IndexedDataVector::GetIndexForData(const DataType* data) const + { + if (data >= &m_data.front() && data <= &m_data.back()) + { + return m_dataToIndices.at(data - &m_data.front()); + } + return NoFreeSlot; + } } // namespace AZ::Render diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index 472dbff5c8..e9bc1a1277 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -357,7 +357,7 @@ namespace AZ // Texture2DArray m_decalTextureArrayNormalMaps0; // Texture2DArray m_decalTextureArrayNormalMaps1; // Texture2DArray m_decalTextureArrayNormalMaps2; - static const AZStd::array ShaderNames = { "m_decalTextureArrayDiffuse", + static constexpr AZStd::array ShaderNames = { "m_decalTextureArrayDiffuse", "m_decalTextureArrayNormalMaps" }; for (int mapType = 0; mapType < DecalMapType_Num; ++mapType) @@ -365,7 +365,7 @@ namespace AZ for (int texArrayIdx = 0; texArrayIdx < NumTextureArrays; ++texArrayIdx) { const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get(); - const AZStd::string baseName = ShaderNames[mapType] + AZStd::to_string(texArrayIdx); + const AZStd::string baseName = AZStd::string(ShaderNames[mapType]) + AZStd::to_string(texArrayIdx); m_decalTextureArrayIndices[texArrayIdx][mapType] = viewSrgLayout->FindShaderInputImageIndex(Name(baseName.c_str())); AZ_Warning( diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp index 5e6d4b2ad9..15f7f52338 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp @@ -34,7 +34,32 @@ namespace AZ bool SsaoParentPass::IsEnabled() const { - return ParentPass::IsEnabled(); + if (!ParentPass::IsEnabled()) + { + return false; + } + const RPI::Scene* scene = GetScene(); + if (!scene) + { + return false; + } + PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor(); + const RPI::ViewPtr view = GetRenderPipeline()->GetDefaultView(); + if (!fp) + { + return true; + } + PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view); + if (!postProcessSettings) + { + return true; + } + const SsaoSettings* ssaoSettings = postProcessSettings->GetSsaoSettings(); + if (!ssaoSettings) + { + return true; + } + return ssaoSettings->GetEnabled(); } void SsaoParentPass::InitializeInternal() diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index a476b5b839..4accbf0bba 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -457,17 +456,8 @@ namespace AZ JsonSerializerSettings serializationSettings; serializationSettings.m_keepDefaults = true; - double frameTime = 0.0; - const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics(); - if (stats) - { - frameTime = stats->GetFrameToFrameTimeMilliseconds(); - } - else - { - AZStd::string warning = AZStd::string::format("Failed to get Cpu frame time"); - AZ_Warning("ProfilingCaptureSystemComponent", false, warning.c_str()); - } + double frameTime = AZ::RHI::RHISystemInterface::Get()->GetCpuFrameTime(); + AZ_Warning("ProfilingCaptureSystemComponent", frameTime > 0, "Failed to get Cpu frame time"); CpuFrameTimeSerializer serializer(frameTime); const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&serializer, diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 042e934eb0..6e78d3170c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -507,8 +507,13 @@ namespace AZ } } - RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayIndex = srgLayout->FindShaderInputBufferUnboundedArrayIndex(AZ::Name("m_meshBuffers")); - m_rayTracingSceneSrg->SetBufferViewUnboundedArray(bufferUnboundedArrayIndex, meshBuffers); + //Check if buffer view data changed from previous frame. + if (m_meshBuffers.size() != meshBuffers.size() || m_meshBuffers != meshBuffers) + { + m_meshBuffers = meshBuffers; + RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayIndex = srgLayout->FindShaderInputBufferUnboundedArrayIndex(AZ::Name("m_meshBuffers")); + m_rayTracingSceneSrg->SetBufferViewUnboundedArray(bufferUnboundedArrayIndex, m_meshBuffers); + } } m_rayTracingSceneSrg->Compile(); @@ -554,8 +559,13 @@ namespace AZ } } - RHI::ShaderInputImageUnboundedArrayIndex textureUnboundedArrayIndex = srgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name("m_materialTextures")); - m_rayTracingMaterialSrg->SetImageViewUnboundedArray(textureUnboundedArrayIndex, materialTextures); + // Check if image view data changed from previous frame. + if (m_materialTextures.size() != materialTextures.size() || m_materialTextures != materialTextures) + { + m_materialTextures = materialTextures; + RHI::ShaderInputImageUnboundedArrayIndex textureUnboundedArrayIndex = srgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name("m_materialTextures")); + m_rayTracingMaterialSrg->SetImageViewUnboundedArray(textureUnboundedArrayIndex, materialTextures); + } } m_rayTracingMaterialSrg->Compile(); diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h index 52bb67f547..c0ebd6a4e7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h @@ -281,6 +281,10 @@ namespace AZ using BlasInstanceMap = AZStd::unordered_map; BlasInstanceMap m_blasInstanceMap; + + // Cache view pointers so we dont need to update them if none changed from frame to frame. + AZStd::vector m_meshBuffers; + AZStd::vector m_materialTextures; }; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index c0202c7c74..6452b312c8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -346,7 +346,7 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) { - static const Name LightTypeName = Name("projected"); + const Name LightTypeName = Name("projected"); const auto* passSystem = RPI::PassSystemInterface::Get(); const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp index c4eac30431..8bf518e277 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp @@ -8,6 +8,8 @@ #include +#include + #include #include @@ -72,11 +74,32 @@ namespace AZ creator.End(m_bufferAsset); } + + // default value of 256mb supports roughly 42 character instances at 100,000 vertices per character x 64 bytes per vertex (12 byte position + 12 byte previous frame position + 12 byte normal + 16 byte tangent + 12 byte bitangent) + // This includes only the output of the skinning compute shader, not the input buffers or bone transforms + AZ_CVAR( + int, + r_skinnedMeshInstanceMemoryPoolSize, + 256, + nullptr, + AZ::ConsoleFunctorFlags::NeedsReload, + "The amount of memory in Mb available for all actor skinning data. Note that this must only be set once at application startup" + ); + void SkinnedMeshOutputStreamManager::Init() { - // 256mb supports roughly 42 character instances at 100,000 vertices per character x 64 bytes per vertex (12 byte position + 12 byte previous frame position + 12 byte normal + 16 byte tangent + 12 byte bitangent) - // This includes only the output of the skinning compute shader, not the input buffers or bone transforms - m_sizeInBytes = 256u * (1024u * 1024u); + } + + void SkinnedMeshOutputStreamManager::EnsureInit() + { + if (!m_needsInit) + { + return; + } + m_needsInit = false; + + const AZ::u64 sizeInMb = r_skinnedMeshInstanceMemoryPoolSize; + m_sizeInBytes = sizeInMb * (1024u * 1024u); CalculateAlignment(); @@ -90,6 +113,8 @@ namespace AZ RHI::VirtualAddress result; { AZStd::lock_guard lock(m_allocatorMutex); + + EnsureInit(); result = m_freeListAllocator.Allocate(byteCount, m_alignment); } @@ -127,13 +152,15 @@ namespace AZ } } - Data::Asset SkinnedMeshOutputStreamManager::GetBufferAsset() const + Data::Asset SkinnedMeshOutputStreamManager::GetBufferAsset() { + EnsureInit(); return m_bufferAsset; } Data::Instance SkinnedMeshOutputStreamManager::GetBuffer() { + EnsureInit(); if (!m_buffer) { m_buffer = RPI::Buffer::FindOrCreate(m_bufferAsset); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.h b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.h index 3cec34a2e2..d0d00ead18 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.h +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.h @@ -39,13 +39,14 @@ namespace AZ AZStd::intrusive_ptr Allocate(size_t byteCount) override; void DeAllocate(RHI::VirtualAddress allocation) override; void DeAllocateNoSignal(RHI::VirtualAddress allocation) override; - Data::Asset GetBufferAsset() const override; + Data::Asset GetBufferAsset() override; Data::Instance GetBuffer() override; private: // SystemTickBus void OnSystemTick() override; + void EnsureInit(); void GarbageCollect(); void CalculateAlignment(); void CreateBufferAsset(); @@ -58,6 +59,7 @@ namespace AZ size_t m_sizeInBytes = 0; bool m_memoryWasFreed = false; bool m_broadcastMemoryAvailableEvent = false; + bool m_needsInit = true; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Base.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Base.h index 9e1ec4585e..977c2c9ce8 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Base.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/Base.h @@ -16,6 +16,7 @@ #include AZ_DECLARE_BUDGET(RHI); +inline static constexpr AZ::Crc32 rhiMetricsId = AZ_CRC_CE("RHI"); namespace UnitTest { diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/CpuTimingStatistics.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/CpuTimingStatistics.h deleted file mode 100644 index 2ab23def08..0000000000 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/CpuTimingStatistics.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -#pragma once - -#include - -#include -#include -#include -#include - -namespace AZ -{ - namespace RHI - { - //! Container and helper type for storing per frame CPU timing data. - //! Users can queue up generic timings in Scopes or add to specific timing data. - struct CpuTimingStatistics - { - struct QueueStatistics - { - //! The display name of the queue the statistics are for. - Name m_queueName; - - //! Time spent executing queued work. - AZStd::sys_time_t m_executeDuration{}; - }; - - //! Statistics for each command queue. - AZStd::vector m_queueStatistics; - - //! The amount of time spent between two calls to EndFrame. - AZStd::sys_time_t m_frameToFrameTime{}; - - //! The amount of time spent presenting (vsync can affect this). - AZStd::sys_time_t m_presentDuration{}; - - void Reset() - { - m_queueStatistics.clear(); - } - - double GetFrameToFrameTimeMilliseconds() const - { - return (m_frameToFrameTime * 1000) / aznumeric_cast(AZStd::GetTimeTicksPerSecond()); - } - }; - - //! Utility type that updates the given variable with the lifetime of the object in cycles. - //! Useful for quick scope based timing. - struct VariableTimer - { - VariableTimer() = delete; - VariableTimer(AZStd::sys_time_t& variable) - : m_variable(variable) - { - m_timer.Stamp(); - } - ~VariableTimer() - { - m_variable = m_timer.GetDeltaTimeInTicks(); - } - - AZStd::sys_time_t& m_variable; - AZ::Debug::Timer m_timer; - }; - } -} - -//! Utility for timing a section of code and writing the timing (in cycles) to the given variable. -#define AZ_PROFILE_RHI_VARIABLE(variable) \ - AZ::RHI::VariableTimer AZ_JOIN(variableTimer, __LINE__)(variable); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h index fb4082bb25..358fdcb4c4 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h @@ -27,9 +27,6 @@ namespace AZ { namespace RHI { - struct CpuTimingStatistics; - - //! The Device is a context for managing GPU state and memory on a physical device. The user creates //! a device instance from a PhysicalDevice. Each device has its own capabilities and limits, and can //! be configured to buffer a specific number of frames. @@ -91,10 +88,10 @@ namespace AZ //! scope. Otherwise, an error code is returned. ResultCode CompileMemoryStatistics(MemoryStatistics& memoryStatistics, MemoryStatisticsReportFlags reportFlags); - //! Fills the provided data structure with cpu timing statistics specific to this device. This - //! method can only be called on an initialized device, and outside of the BeginFrame / EndFrame - //! scope. Otherwise, an error code is returned. - ResultCode UpdateCpuTimingStatistics(CpuTimingStatistics& cpuTimingStatistics) const; + //! Pushes internally recorded timing statistics upwards into the global stats profiler, under the RHI section. + //! This method can only be called on an initialized device, and outside of the BeginFrame / EndFrame scope. + //! Otherwise, an error code is returned. + ResultCode UpdateCpuTimingStatistics() const; //! Returns the physical device associated with this device. const PhysicalDevice& GetPhysicalDevice() const; @@ -186,7 +183,7 @@ namespace AZ virtual void CompileMemoryStatisticsInternal(MemoryStatisticsBuilder& builder) = 0; //! Called when the device is reporting cpu timing statistics. - virtual void UpdateCpuTimingStatisticsInternal(CpuTimingStatistics& cpuTimingStatistics) const = 0; + virtual void UpdateCpuTimingStatisticsInternal() const = 0; //! Fills the capabilities for each format. virtual void FillFormatsCapabilitiesInternal(FormatCapabilitiesList& formatsCapabilities) = 0; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h index 48e7e0f339..eb2b0b5b0c 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h @@ -7,7 +7,6 @@ */ #pragma once -#include #include #include #include @@ -168,8 +167,8 @@ namespace AZ /// Returns the timing statistics for the previous frame. const TransientAttachmentStatistics* GetTransientAttachmentStatistics() const; - /// Returns cpu timing statistics for the previous frame. - const CpuTimingStatistics* GetCpuTimingStatistics() const; + /// Returns current CPU frame to frame time in milliseconds. + double GetCpuFrameTime() const; /// Returns memory statistics for the previous frame. const MemoryStatistics* GetMemoryStatistics() const; @@ -216,7 +215,6 @@ namespace AZ Ptr m_transientAttachmentPool; - CpuTimingStatistics m_cpuTimingStatistics; AZStd::sys_time_t m_lastFrameEndTime{}; MemoryStatistics m_memoryStatistics; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystem.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystem.h index 599108654a..52a44c0903 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystem.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystem.h @@ -48,7 +48,7 @@ namespace AZ RHI::PipelineStateCache* GetPipelineStateCache() override; const RHI::FrameSchedulerCompileRequest& GetFrameSchedulerCompileRequest() const override; void ModifyFrameSchedulerStatisticsFlags(RHI::FrameSchedulerStatisticsFlags statisticsFlags, bool enableFlags) override; - const RHI::CpuTimingStatistics* GetCpuTimingStatistics() const override; + double GetCpuFrameTime() const override; const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const override; const RHI::MemoryStatistics* GetMemoryStatistics() const override; const RHI::TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const override; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystemInterface.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystemInterface.h index 6a9650e0a1..19ba1cb762 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystemInterface.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/RHISystemInterface.h @@ -27,7 +27,6 @@ namespace AZ class PipelineStateCache; class PlatformLimitsDescriptor; class RayTracingShaderTable; - struct CpuTimingStatistics; struct FrameSchedulerCompileRequest; struct TransientAttachmentStatistics; struct TransientAttachmentPoolDescriptor; @@ -55,7 +54,7 @@ namespace AZ virtual void ModifyFrameSchedulerStatisticsFlags(RHI::FrameSchedulerStatisticsFlags statisticsFlags, bool enableFlags) = 0; - virtual const RHI::CpuTimingStatistics* GetCpuTimingStatistics() const = 0; + virtual double GetCpuFrameTime() const = 0; virtual const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const = 0; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h index cbb0db53c8..ef4e734c01 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h @@ -183,6 +183,42 @@ namespace AZ //! Returns the shader resource layout for this group. const ShaderResourceGroupLayout* GetLayout() const; + enum class ResourceType : uint32_t + { + ConstantData, + BufferView, + ImageView, + BufferViewUnboundedArray, + ImageViewUnboundedArray, + Sampler, + Count + }; + + enum class ResourceTypeMask : uint32_t + { + None = 0, + ConstantDataMask = AZ_BIT(static_cast(ResourceType::ConstantData)), + BufferViewMask = AZ_BIT(static_cast(ResourceType::BufferView)), + ImageViewMask = AZ_BIT(static_cast(ResourceType::ImageView)), + BufferViewUnboundedArrayMask = AZ_BIT(static_cast(ResourceType::BufferViewUnboundedArray)), + ImageViewUnboundedArrayMask = AZ_BIT(static_cast(ResourceType::ImageViewUnboundedArray)), + SamplerMask = AZ_BIT(static_cast(ResourceType::Sampler)) + }; + + //! Returns true if a resource type specified by resourceTypeMask is enabled for compilation + bool IsResourceTypeEnabledForCompilation(uint32_t resourceTypeMask) const; + + //! Disables all resource types for compilation after m_updateMaskResetLatency number of compiles + //! This allows higher level code to ensure that if SRG is multi-buffered it can compile multiple + //! times in order to ensure all SRG buffers are updated. + void DisableCompilationForAllResourceTypes(); + + //! Returns true if any of the resource type has been enabled for compilation. + bool IsAnyResourceTypeUpdated() const; + + //! Enable compilation for a resourceType specified by resourceType/resourceTypeMask + void EnableResourceTypeCompilation(ResourceTypeMask resourceTypeMask, ResourceType resourceType); + private: static const ConstPtr s_nullImageView; static const ConstPtr s_nullBufferView; @@ -207,23 +243,43 @@ namespace AZ //! The backing data store of constants for the shader resource group. ConstantsData m_constantsData; + + //! Mask used to check whether to compile a specific resource type + uint32_t m_updateMask = 0; + + //! Track iteration for each resource type in order to keep compiling it for m_updateMaskResetLatency number of times + uint32_t m_resourceTypeIteration[static_cast(ResourceType::Count)] = { 0 }; + uint32_t m_updateMaskResetLatency = RHI::Limits::Device::FrameCountMax; }; template bool ShaderResourceGroupData::SetConstant(ShaderInputConstantIndex inputIndex, const T& value) { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); return m_constantsData.SetConstant(inputIndex, value); } template bool ShaderResourceGroupData::SetConstant(ShaderInputConstantIndex inputIndex, const T& value, uint32_t arrayIndex) { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); return m_constantsData.SetConstant(inputIndex, value, arrayIndex); } + + template + bool ShaderResourceGroupData::SetConstantMatrixRows(ShaderInputConstantIndex inputIndex, const T& value, uint32_t rowCount) + { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); + return m_constantsData.SetConstantMatrixRows(inputIndex, value, rowCount); + } template bool ShaderResourceGroupData::SetConstantArray(ShaderInputConstantIndex inputIndex, AZStd::array_view values) { + if (!values.empty()) + { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); + } return m_constantsData.SetConstantArray(inputIndex, values); } @@ -245,12 +301,6 @@ namespace AZ return m_constantsData.GetConstant(inputIndex, arrayIndex); } - template - bool ShaderResourceGroupData::SetConstantMatrixRows(ShaderInputConstantIndex inputIndex, const T& value, uint32_t rowCount) - { - return m_constantsData.SetConstantMatrixRows(inputIndex, value, rowCount); - } - template bool ShaderResourceGroupData::ValidateImageViewAccess(TShaderInput inputIndex, const ImageView* imageView, [[maybe_unused]] uint32_t arrayIndex) const { diff --git a/Gems/Atom/RHI/Code/Source/RHI.Reflect/PhysicalDeviceDescriptor.cpp b/Gems/Atom/RHI/Code/Source/RHI.Reflect/PhysicalDeviceDescriptor.cpp index 17819ab1f5..9da4f583ac 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Reflect/PhysicalDeviceDescriptor.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Reflect/PhysicalDeviceDescriptor.cpp @@ -86,6 +86,13 @@ namespace AZ PhysicalDeviceDriverValidator::ValidationResult PhysicalDeviceDriverValidator::ValidateDriverVersion(const PhysicalDeviceDescriptor& descriptor) const { + // [GFX TODO] Add driver info for other platforms besides Windows. Currently, avoid spamming warnings. + // ATOM-14967 [RHI][Metal] - Address driver version validator for Mac + if (m_driverInfo.size() == 0) + { + return ValidationResult::MissingInfo; + } + auto iter = m_driverInfo.find(descriptor.m_vendorId); if (iter == m_driverInfo.end()) diff --git a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp index f65c36f2ed..a365b23e94 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp @@ -32,6 +32,23 @@ namespace AZ return ResultCode::InvalidOperation; } #endif + + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) + { + auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId); + + static constexpr AZStd::string_view presentStatName("Present"); + static constexpr AZ::Crc32 presentStatId(presentStatName); + rhiMetrics.GetStatsManager().AddStatistic(presentStatId, presentStatName, /*units=*/"clocks", /*failIfExist=*/false); + + if (!GetName().IsEmpty()) + { + const AZStd::string commandQueueName(GetName().GetCStr()); + const AZ::Crc32 commandQueueId(GetName().GetHash()); + rhiMetrics.GetStatsManager().AddStatistic(commandQueueId, commandQueueName, /*units=*/"clocks", /*failIfExist=*/false); + } + } + const ResultCode resultCode = InitInternal(device, descriptor); if (resultCode == ResultCode::Success) diff --git a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp index 1bc17adb22..826e0b6aa3 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace AZ @@ -73,6 +74,11 @@ namespace AZ m_initialized = true; SystemTickBus::Handler::BusConnect(); m_continuousCaptureData.set_capacity(10); + + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) + { + statsProfiler->ActivateProfiler(AZ_CRC_CE("RHI"), true); + } } void CpuProfilerImpl::Shutdown() diff --git a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp index 2f4ca297a1..c8497cf7b1 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp @@ -160,11 +160,11 @@ namespace AZ return ResultCode::InvalidOperation; } - ResultCode Device::UpdateCpuTimingStatistics(CpuTimingStatistics& cpuTimingStatistics) const + ResultCode Device::UpdateCpuTimingStatistics() const { if (ValidateIsNotInFrame()) { - UpdateCpuTimingStatisticsInternal(cpuTimingStatistics); + UpdateCpuTimingStatisticsInternal(); return ResultCode::Success; } return ResultCode::InvalidOperation; diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 0d3ac8216b..a15db9e24b 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -35,6 +35,9 @@ namespace AZ { namespace RHI { + static constexpr const char* frameTimeMetricName = "Frame to Frame Time"; + static constexpr AZ::Crc32 frameTimeMetricId = AZ_CRC_CE(frameTimeMetricName); + ResultCode FrameScheduler::Init(Device& device, const FrameSchedulerDescriptor& descriptor) { ResultCode resultCode = ResultCode::Success; @@ -81,6 +84,12 @@ namespace AZ m_taskGraphActive = AZ::Interface::Get(); + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) + { + auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId); + rhiMetrics.GetStatsManager().AddStatistic(frameTimeMetricId, frameTimeMetricName, /*units=*/"clocks", /*failIfExist=*/false); + } + m_lastFrameEndTime = AZStd::GetTimeNowTicks(); return ResultCode::Success; @@ -278,7 +287,7 @@ namespace AZ AZ::TaskDescriptor srgCompileEndDesc{"SrgCompileEnd", "Graphics"}; auto srgCompileEndTask = taskGraph.AddTask( - srgCompileEndDesc, + srgCompileEndDesc, [srgPool]() { srgPool->CompileGroupsEnd(); @@ -449,7 +458,7 @@ namespace AZ m_device->CompileMemoryStatistics(m_memoryStatistics, MemoryStatisticsReportFlags::Detail); } - m_device->UpdateCpuTimingStatistics(m_cpuTimingStatistics); + m_device->UpdateCpuTimingStatistics(); m_scopeProducers.clear(); m_scopeProducerLookup.clear(); @@ -460,7 +469,10 @@ namespace AZ } const AZStd::sys_time_t timeNowTicks = AZStd::GetTimeNowTicks(); - m_cpuTimingStatistics.m_frameToFrameTime = timeNowTicks - m_lastFrameEndTime; + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) + { + statsProfiler->PushSample(rhiMetricsId, frameTimeMetricId, static_cast(timeNowTicks - m_lastFrameEndTime)); + } m_lastFrameEndTime = timeNowTicks; return ResultCode::Success; @@ -588,12 +600,18 @@ namespace AZ : nullptr; } - const CpuTimingStatistics* FrameScheduler::GetCpuTimingStatistics() const + double FrameScheduler::GetCpuFrameTime() const { - return - CheckBitsAny(m_compileRequest.m_statisticsFlags, FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics) - ? &m_cpuTimingStatistics - : nullptr; + if (CheckBitsAny(m_compileRequest.m_statisticsFlags, FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics)) + { + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) + { + auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId); + const auto* frameTimeStat = rhiMetrics.GetStatistic(frameTimeMetricId); + return (frameTimeStat->GetMostRecentSample() * 1000) / aznumeric_cast(AZStd::GetTimeTicksPerSecond()); + } + } + return 0; } ScopeId FrameScheduler::GetRootScopeId() const diff --git a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp index 744b688c60..b40ad3e11a 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp @@ -254,9 +254,9 @@ namespace AZ : RHI::ResetBits(m_compileRequest.m_statisticsFlags, statisticsFlags); } - const RHI::CpuTimingStatistics* RHISystem::GetCpuTimingStatistics() const + double RHISystem::GetCpuFrameTime() const { - return m_frameScheduler.GetCpuTimingStatistics(); + return m_frameScheduler.GetCpuFrameTime(); } const RHI::TransientAttachmentStatistics* RHISystem::GetTransientAttachmentStatistics() const diff --git a/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupData.cpp b/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupData.cpp index 18b292ac64..eed14b41b7 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupData.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupData.cpp @@ -7,6 +7,7 @@ */ #include #include +#include namespace AZ { @@ -126,6 +127,12 @@ namespace AZ } isValidAll &= isValid; } + + if(!imageViews.empty()) + { + EnableResourceTypeCompilation(ResourceTypeMask::ImageViewMask, ResourceType::ImageView); + } + return isValidAll; } return false; @@ -146,6 +153,11 @@ namespace AZ } isValidAll &= isValid; } + + if (!imageViews.empty()) + { + EnableResourceTypeCompilation(ResourceTypeMask::ImageViewUnboundedArrayMask, ResourceType::ImageViewUnboundedArray); + } return isValidAll; } return false; @@ -172,6 +184,11 @@ namespace AZ } isValidAll &= isValid; } + + if (!bufferViews.empty()) + { + EnableResourceTypeCompilation(ResourceTypeMask::BufferViewMask, ResourceType::BufferView); + } return isValidAll; } return false; @@ -192,6 +209,11 @@ namespace AZ } isValidAll &= isValid; } + + if (!bufferViews.empty()) + { + EnableResourceTypeCompilation(ResourceTypeMask::BufferViewUnboundedArrayMask, ResourceType::BufferViewUnboundedArray); + } return isValidAll; } return false; @@ -211,6 +233,11 @@ namespace AZ { m_samplers[interval.m_min + arrayIndex + i] = samplers[i]; } + + if (!samplers.empty()) + { + EnableResourceTypeCompilation(ResourceTypeMask::SamplerMask, ResourceType::Sampler); + } return true; } return false; @@ -223,16 +250,19 @@ namespace AZ bool ShaderResourceGroupData::SetConstantRaw(ShaderInputConstantIndex inputIndex, const void* bytes, uint32_t byteOffset, uint32_t byteCount) { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); return m_constantsData.SetConstantRaw(inputIndex, bytes, byteOffset, byteCount); } bool ShaderResourceGroupData::SetConstantData(const void* bytes, uint32_t byteCount) { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); return m_constantsData.SetConstantData(bytes, byteCount); } bool ShaderResourceGroupData::SetConstantData(const void* bytes, uint32_t byteOffset, uint32_t byteCount) { + EnableResourceTypeCompilation(ResourceTypeMask::ConstantDataMask, ResourceType::ConstantData); return m_constantsData.SetConstantData(bytes, byteOffset, byteCount); } @@ -348,5 +378,33 @@ namespace AZ return m_constantsData; } + bool ShaderResourceGroupData::IsResourceTypeEnabledForCompilation(uint32_t resourceTypeMask) const + { + return RHI::CheckBitsAny(m_updateMask, resourceTypeMask); + } + + bool ShaderResourceGroupData::IsAnyResourceTypeUpdated() const + { + return m_updateMask != 0; + } + + void ShaderResourceGroupData::EnableResourceTypeCompilation(ResourceTypeMask resourceTypeMask, ResourceType resourceType) + { + AZ_Assert(static_cast(resourceTypeMask) == AZ_BIT(static_cast(resourceType)), "resourceType and resourceTypeMask should point to the same ResourceType"); + m_updateMask = RHI::SetBits(m_updateMask, static_cast(resourceTypeMask)); + m_resourceTypeIteration[static_cast(resourceType)] = 0; + } + + void ShaderResourceGroupData::DisableCompilationForAllResourceTypes() + { + for (uint32_t i = 0; i < static_cast(ResourceType::Count); i++) + { + if (m_resourceTypeIteration[i] == m_updateMaskResetLatency) + { + m_updateMask = RHI::ResetBits(m_updateMask, AZ_BIT(i)); + } + m_resourceTypeIteration[i]++; + } + } } // namespace RHI } // namespace AZ diff --git a/Gems/Atom/RHI/Code/Tests/Device.h b/Gems/Atom/RHI/Code/Tests/Device.h index d3177fc823..2b6a81face 100644 --- a/Gems/Atom/RHI/Code/Tests/Device.h +++ b/Gems/Atom/RHI/Code/Tests/Device.h @@ -47,7 +47,7 @@ namespace UnitTest void CompileMemoryStatisticsInternal(AZ::RHI::MemoryStatisticsBuilder&) override {} - void UpdateCpuTimingStatisticsInternal([[maybe_unused]] AZ::RHI::CpuTimingStatistics& cpuTimingStatistics) const override {} + void UpdateCpuTimingStatisticsInternal() const override {} AZStd::chrono::microseconds GpuTimestampToMicroseconds([[maybe_unused]] uint64_t gpuTimestamp, [[maybe_unused]] AZ::RHI::HardwareQueueClass queueClass) const override { diff --git a/Gems/Atom/RHI/Code/atom_rhi_reflect_files.cmake b/Gems/Atom/RHI/Code/atom_rhi_reflect_files.cmake index e211462eeb..9a2b6e8765 100644 --- a/Gems/Atom/RHI/Code/atom_rhi_reflect_files.cmake +++ b/Gems/Atom/RHI/Code/atom_rhi_reflect_files.cmake @@ -112,7 +112,6 @@ set(FILES Source/RHI.Reflect/ShaderResourceGroupLayout.cpp Source/RHI.Reflect/ShaderResourceGroupLayoutDescriptor.cpp Source/RHI.Reflect/ShaderResourceGroupPoolDescriptor.cpp - Include/Atom/RHI.Reflect/CpuTimingStatistics.h Include/Atom/RHI.Reflect/MemoryStatistics.h Include/Atom/RHI.Reflect/TransientAttachmentStatistics.h Include/Atom/RHI.Reflect/SwapChainDescriptor.h diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp index 0cde6aeb09..7ab2b07b03 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp @@ -10,8 +10,8 @@ #include #include #include -#include -#include + +#include namespace AZ { @@ -139,7 +139,7 @@ namespace AZ QueueCommand([=](void* commandQueue) { AZ_PROFILE_SCOPE(RHI, "ExecuteWork"); - AZ_PROFILE_RHI_VARIABLE(m_lastExecuteDuration); + AZ::Debug::ScopedTimer executionTimer(m_lastExecuteDuration); static const uint32_t CommandListCountMax = 128; ID3D12CommandQueue* dx12CommandQueue = static_cast(commandQueue); @@ -185,7 +185,7 @@ namespace AZ dx12CommandQueue->Signal(fence->Get(), fence->GetPendingValue()); } - AZ_PROFILE_RHI_VARIABLE(m_lastPresentDuration); + AZ::Debug::ScopedTimer presentTimer(m_lastPresentDuration); for (RHI::SwapChain* swapChain : request.m_swapChainsToPresent) { swapChain->Present(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp index 012784d3fc..5692809443 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -183,17 +182,22 @@ namespace AZ return *m_commandQueues[static_cast(hardwareQueueClass)]; } - void CommandQueueContext::UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const + void CommandQueueContext::UpdateCpuTimingStatistics() const { - cpuTimingStatistics.Reset(); - - AZStd::sys_time_t presentDuration = 0; - for (const RHI::Ptr& commandQueue : m_commandQueues) + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) { - cpuTimingStatistics.m_queueStatistics.push_back({ commandQueue->GetName(), commandQueue->GetLastExecuteDuration() }); - presentDuration += commandQueue->GetLastPresentDuration(); + auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId); + + AZStd::sys_time_t presentDuration = 0; + for (const RHI::Ptr& commandQueue : m_commandQueues) + { + const AZ::Crc32 commandQueueId(commandQueue->GetName().GetHash()); + rhiMetrics.PushSample(commandQueueId, static_cast(commandQueue->GetLastExecuteDuration())); + presentDuration += commandQueue->GetLastPresentDuration(); + } + + rhiMetrics.PushSample(AZ_CRC_CE("Present"), static_cast(presentDuration)); } - cpuTimingStatistics.m_presentDuration = presentDuration; } const FenceSet& CommandQueueContext::GetCompiledFences() diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.h index cf820026aa..dff1741109 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.h @@ -14,11 +14,6 @@ namespace AZ { - namespace RHI - { - struct CpuTimingStatistics; - } - namespace DX12 { class CommandQueueContext @@ -49,7 +44,7 @@ namespace AZ RHI::HardwareQueueClass hardwareQueueClass, const ExecuteWorkRequest& request); - void UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const; + void UpdateCpuTimingStatistics() const; // Fences across all queues that are compiled by the frame graph compilation phase const FenceSet& GetCompiledFences(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp index 6f614499d2..3d44e56953 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp @@ -184,9 +184,9 @@ namespace AZ m_stagingMemoryAllocator.ReportMemoryUsage(builder); } - void Device::UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const + void Device::UpdateCpuTimingStatisticsInternal() const { - m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics); + m_commandQueueContext.UpdateCpuTimingStatistics(); } void Device::EndFrameInternal() diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h index 40e26db891..29e006fca0 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h @@ -147,7 +147,7 @@ namespace AZ void ShutdownInternal() override; void CompileMemoryStatisticsInternal(RHI::MemoryStatisticsBuilder& builder) override; - void UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const override; + void UpdateCpuTimingStatisticsInternal() const override; void BeginFrameInternal() override; void EndFrameInternal() override; void WaitForIdleInternal() override; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp index bd648cf535..087a36dc2f 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -206,12 +206,21 @@ namespace AZ auto& device = static_cast(GetDevice()); group.m_compiledDataIndex = (group.m_compiledDataIndex + 1) % RHI::Limits::Device::FrameCountMax; - if (m_constantBufferSize) + if (!groupData.IsAnyResourceTypeUpdated()) + { + return RHI::ResultCode::Success; + } + + if (m_constantBufferSize && + groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ConstantDataMask))) { memcpy(group.GetCompiledData().m_cpuConstantAddress, groupData.GetConstantData().data(), groupData.GetConstantData().size()); } - if (m_viewsDescriptorTableSize) + if (m_viewsDescriptorTableSize && + groupData.IsResourceTypeEnabledForCompilation( + static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewMask) | + static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewMask))) { //Lazy initialization for cbv/srv/uav Descriptor Tables if (!group.m_viewsDescriptorTable.IsValid()) @@ -236,12 +245,17 @@ namespace AZ UpdateViewsDescriptorTable(descriptorTable, groupData); } - if (m_unboundedArrayCount) + if (m_unboundedArrayCount && + groupData.IsResourceTypeEnabledForCompilation( + static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewUnboundedArrayMask) | + static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewUnboundedArrayMask))) { UpdateUnboundedArrayDescriptorTables(group, groupData); } - if (m_samplersDescriptorTableSize) + if (m_samplersDescriptorTableSize && + groupData.IsResourceTypeEnabledForCompilation( + static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::SamplerMask))) { const DescriptorTable descriptorTable( group.m_samplersDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_samplersDescriptorTableSize, diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueue.cpp index d151a11ec1..a1f4616147 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueue.cpp @@ -5,14 +5,15 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include + #include #include #include #include #include +#include + namespace AZ { namespace Metal @@ -115,7 +116,7 @@ namespace AZ @autoreleasepool { AZ_PROFILE_SCOPE(RHI, "ExecuteWork"); - AZ_PROFILE_RHI_VARIABLE(m_lastExecuteDuration); + AZ::Debug::ScopedTimer executionTimer(m_lastExecuteDuration); if (request.m_signalFenceValue > 0) { @@ -128,7 +129,7 @@ namespace AZ } { - AZ_PROFILE_RHI_VARIABLE(m_lastPresentDuration); + AZ::Debug::ScopedTimer presentTimer(m_lastPresentDuration); for (RHI::SwapChain* swapChain : request.m_swapChainsToPresent) { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp index e4c651c2b1..37f8a8bab8 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -135,18 +134,23 @@ namespace AZ m_commandQueues[hardwareQueueIdx]->QueueGpuSignal(fence); } } - - void CommandQueueContext::UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const - { - cpuTimingStatistics.Reset(); - AZStd::sys_time_t presentDuration = 0; - for (const RHI::Ptr& commandQueue : m_commandQueues) + void CommandQueueContext::UpdateCpuTimingStatistics() const + { + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) { - cpuTimingStatistics.m_queueStatistics.push_back({ commandQueue->GetName(), commandQueue->GetLastExecuteDuration() }); - presentDuration += commandQueue->GetLastPresentDuration(); + auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId); + + AZStd::sys_time_t presentDuration = 0; + for (const RHI::Ptr& commandQueue : m_commandQueues) + { + const AZ::Crc32 commandQueueId(commandQueue->GetName().GetHash()); + rhiMetrics.PushSample(commandQueueId, static_cast(commandQueue->GetLastExecuteDuration())); + presentDuration += commandQueue->GetLastPresentDuration(); + } + + rhiMetrics.PushSample(AZ_CRC_CE("Present"), static_cast(presentDuration)); } - cpuTimingStatistics.m_presentDuration = presentDuration; } } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.h index 747667e1a3..41c0f63189 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.h @@ -40,7 +40,7 @@ namespace AZ /// Fences across all queues that are compiled by the frame graph compilation phase const FenceSet& GetCompiledFences(); - void UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const; + void UpdateCpuTimingStatistics() const; private: AZStd::array, RHI::HardwareQueueClassCount> m_commandQueues; FenceSet m_compiledFences; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp index 782ea7174b..76af1ecab1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp @@ -245,9 +245,9 @@ namespace AZ { } - void Device::UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const + void Device::UpdateCpuTimingStatisticsInternal() const { - m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics); + m_commandQueueContext.UpdateCpuTimingStatistics(); } void Device::FillFormatsCapabilitiesInternal(FormatCapabilitiesList& formatsCapabilities) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h index 90dd4ff4a0..8a8ef662f9 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h @@ -161,7 +161,7 @@ namespace AZ RHI::ResultCode InitInternal(RHI::PhysicalDevice& physicalDevice) override; void ShutdownInternal() override; void CompileMemoryStatisticsInternal(RHI::MemoryStatisticsBuilder& builder) override; - void UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const override; + void UpdateCpuTimingStatisticsInternal() const override; void BeginFrameInternal() override; void EndFrameInternal() override; void WaitForIdleInternal() override; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ShaderResourceGroupPool.cpp index 6be5ec7259..0a9c001868 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -63,39 +63,58 @@ namespace AZ { ShaderResourceGroup& group = static_cast(groupBase); group.UpdateCompiledDataIndex(); - + + if (!groupData.IsAnyResourceTypeUpdated()) + { + return RHI::ResultCode::Success; + } + ArgumentBuffer& argBuffer = *group.m_compiledArgBuffers[group.m_compiledDataIndex]; argBuffer.ClearResourceTracking(); - argBuffer.UpdateConstantBufferViews(groupData.GetConstantData()); - + + auto constantData = groupData.GetConstantData(); + if (!constantData.empty() && groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ConstantDataMask))) + { + argBuffer.UpdateConstantBufferViews(groupData.GetConstantData()); + } + const RHI::ShaderResourceGroupLayout* layout = groupData.GetLayout(); uint32_t shaderInputIndex = 0; - for (const RHI::ShaderInputImageDescriptor& shaderInputImage : layout->GetShaderInputListForImages()) + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewMask))) { - const RHI::ShaderInputImageIndex imageInputIndex(shaderInputIndex); - AZStd::array_view> imageViews = groupData.GetImageViewArray(imageInputIndex); - argBuffer.UpdateImageViews(shaderInputImage, imageInputIndex, imageViews); - ++shaderInputIndex; + for (const RHI::ShaderInputImageDescriptor& shaderInputImage : layout->GetShaderInputListForImages()) + { + const RHI::ShaderInputImageIndex imageInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = groupData.GetImageViewArray(imageInputIndex); + argBuffer.UpdateImageViews(shaderInputImage, imageInputIndex, imageViews); + ++shaderInputIndex; + } } - - shaderInputIndex = 0; - for (const RHI::ShaderInputSamplerDescriptor& shaderInputSampler : layout->GetShaderInputListForSamplers()) + + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::SamplerMask))) { - const RHI::ShaderInputSamplerIndex samplerInputIndex(shaderInputIndex); - AZStd::array_view samplerStates= groupData.GetSamplerArray(samplerInputIndex); - argBuffer.UpdateSamplers(shaderInputSampler, samplerInputIndex, samplerStates); - ++shaderInputIndex; + shaderInputIndex = 0; + for (const RHI::ShaderInputSamplerDescriptor& shaderInputSampler : layout->GetShaderInputListForSamplers()) + { + const RHI::ShaderInputSamplerIndex samplerInputIndex(shaderInputIndex); + AZStd::array_view samplerStates = groupData.GetSamplerArray(samplerInputIndex); + argBuffer.UpdateSamplers(shaderInputSampler, samplerInputIndex, samplerStates); + ++shaderInputIndex; + } } - - shaderInputIndex = 0; - for (const RHI::ShaderInputBufferDescriptor& shaderInputBuffer : layout->GetShaderInputListForBuffers()) + + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewMask))) { - const RHI::ShaderInputBufferIndex bufferInputIndex(shaderInputIndex); - AZStd::array_view> bufferViews = groupData.GetBufferViewArray(bufferInputIndex); - argBuffer.UpdateBufferViews(shaderInputBuffer, bufferInputIndex, bufferViews); - ++shaderInputIndex; + shaderInputIndex = 0; + for (const RHI::ShaderInputBufferDescriptor& shaderInputBuffer : layout->GetShaderInputListForBuffers()) + { + const RHI::ShaderInputBufferIndex bufferInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = groupData.GetBufferViewArray(bufferInputIndex); + argBuffer.UpdateBufferViews(shaderInputBuffer, bufferInputIndex, bufferViews); + ++shaderInputIndex; + } } - + return RHI::ResultCode::Success; } diff --git a/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h index 27873887d4..73e1cd9119 100644 --- a/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h @@ -32,7 +32,7 @@ namespace AZ RHI::ResultCode InitInternal([[maybe_unused]] RHI::PhysicalDevice& physicalDevice) override { return RHI::ResultCode::Success; } void ShutdownInternal() override {} void CompileMemoryStatisticsInternal([[maybe_unused]] RHI::MemoryStatisticsBuilder& builder) override {} - void UpdateCpuTimingStatisticsInternal([[maybe_unused]] RHI::CpuTimingStatistics& cpuTimingStatistics) const override {} + void UpdateCpuTimingStatisticsInternal() const override {} void BeginFrameInternal() override {} void EndFrameInternal() override {} void WaitForIdleInternal() override {} diff --git a/Gems/Atom/RHI/Registry/Platform/Android/PhysicalDeviceDriverInfo.setreg b/Gems/Atom/RHI/Registry/Platform/Android/PhysicalDeviceDriverInfo.setreg new file mode 100644 index 0000000000..a0df86c923 --- /dev/null +++ b/Gems/Atom/RHI/Registry/Platform/Android/PhysicalDeviceDriverInfo.setreg @@ -0,0 +1,23 @@ +// +// Copyright (c) Contributors to the Open 3D Engine Project. +// For complete copyright and license terms please see the LICENSE at the root of this distribution. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// +// + +{ + "O3DE": + { + "Atom": + { + "RHI": + { + "PhysicalDeviceDriverInfo": + { + } + } + } + } +} diff --git a/Gems/Atom/RHI/Registry/Platform/Linux/PhysicalDeviceDriverInfo.setreg b/Gems/Atom/RHI/Registry/Platform/Linux/PhysicalDeviceDriverInfo.setreg new file mode 100644 index 0000000000..a0df86c923 --- /dev/null +++ b/Gems/Atom/RHI/Registry/Platform/Linux/PhysicalDeviceDriverInfo.setreg @@ -0,0 +1,23 @@ +// +// Copyright (c) Contributors to the Open 3D Engine Project. +// For complete copyright and license terms please see the LICENSE at the root of this distribution. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// +// + +{ + "O3DE": + { + "Atom": + { + "RHI": + { + "PhysicalDeviceDriverInfo": + { + } + } + } + } +} diff --git a/Gems/Atom/RHI/Registry/Platform/Mac/PhysicalDeviceDriverInfo.setreg b/Gems/Atom/RHI/Registry/Platform/Mac/PhysicalDeviceDriverInfo.setreg new file mode 100644 index 0000000000..a0df86c923 --- /dev/null +++ b/Gems/Atom/RHI/Registry/Platform/Mac/PhysicalDeviceDriverInfo.setreg @@ -0,0 +1,23 @@ +// +// Copyright (c) Contributors to the Open 3D Engine Project. +// For complete copyright and license terms please see the LICENSE at the root of this distribution. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// +// + +{ + "O3DE": + { + "Atom": + { + "RHI": + { + "PhysicalDeviceDriverInfo": + { + } + } + } + } +} diff --git a/Gems/Atom/RHI/Registry/PhysicalDeviceDriverInfo.setreg b/Gems/Atom/RHI/Registry/Platform/Windows/PhysicalDeviceDriverInfo.setreg similarity index 100% rename from Gems/Atom/RHI/Registry/PhysicalDeviceDriverInfo.setreg rename to Gems/Atom/RHI/Registry/Platform/Windows/PhysicalDeviceDriverInfo.setreg diff --git a/Gems/Atom/RHI/Registry/Platform/iOS/PhysicalDeviceDriverInfo.setreg b/Gems/Atom/RHI/Registry/Platform/iOS/PhysicalDeviceDriverInfo.setreg new file mode 100644 index 0000000000..a0df86c923 --- /dev/null +++ b/Gems/Atom/RHI/Registry/Platform/iOS/PhysicalDeviceDriverInfo.setreg @@ -0,0 +1,23 @@ +// +// Copyright (c) Contributors to the Open 3D Engine Project. +// For complete copyright and license terms please see the LICENSE at the root of this distribution. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// +// + +{ + "O3DE": + { + "Atom": + { + "RHI": + { + "PhysicalDeviceDriverInfo": + { + } + } + } + } +} diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp index c712099172..6a43b66474 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp @@ -5,13 +5,14 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include #include #include -#include + +#include namespace AZ { @@ -46,7 +47,7 @@ namespace AZ QueueCommand([=](void* queue) { AZ_PROFILE_SCOPE(RHI, "ExecuteWork"); - AZ_PROFILE_RHI_VARIABLE(m_lastExecuteDuration); + AZ::Debug::ScopedTimer executionTimer(m_lastExecuteDuration); Queue* vulkanQueue = static_cast(queue); @@ -80,7 +81,7 @@ namespace AZ } { - AZ_PROFILE_RHI_VARIABLE(m_lastPresentDuration); + AZ::Debug::ScopedTimer presentTimer(m_lastPresentDuration); // present the image of the current frame. for (RHI::SwapChain* swapChain : request.m_swapChainsToPresent) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp index ec7311cd6e..9cf6dce77b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace AZ { @@ -363,17 +362,22 @@ namespace AZ return queueSelection.m_familyIndex != InvalidFamilyIndex; } - void CommandQueueContext::UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const + void CommandQueueContext::UpdateCpuTimingStatistics() const { - cpuTimingStatistics.Reset(); - - AZStd::sys_time_t presentDuration = 0; - for (const RHI::Ptr& commandQueue : m_commandQueues) + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) { - cpuTimingStatistics.m_queueStatistics.push_back({ commandQueue->GetName(), commandQueue->GetLastExecuteDuration() }); - presentDuration += commandQueue->GetLastPresentDuration(); + auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId); + + AZStd::sys_time_t presentDuration = 0; + for (const RHI::Ptr& commandQueue : m_commandQueues) + { + const AZ::Crc32 commandQueueId(commandQueue->GetName().GetHash()); + rhiMetrics.PushSample(commandQueueId, static_cast(commandQueue->GetLastExecuteDuration())); + presentDuration += commandQueue->GetLastPresentDuration(); + } + + rhiMetrics.PushSample(AZ_CRC_CE("Present"), static_cast(presentDuration)); } - cpuTimingStatistics.m_presentDuration = presentDuration; } } } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.h index 8528185686..61e9b46d9f 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.h @@ -16,11 +16,6 @@ namespace AZ { - namespace RHI - { - struct CpuTimingStatistics; - } - namespace Vulkan { class Device; @@ -62,7 +57,7 @@ namespace AZ AZStd::vector GetQueueFamilyIndices(const RHI::HardwareQueueClassMask hardwareQueueClassMask) const; VkPipelineStageFlags GetSupportedPipelineStages(uint32_t queueFamilyIndex) const; - void UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const; + void UpdateCpuTimingStatistics() const; private: Descriptor m_descriptor; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp index 35bc966d1d..132f9929c1 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp @@ -547,9 +547,9 @@ namespace AZ physicalDevice.CompileMemoryStatistics(builder); } - void Device::UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const + void Device::UpdateCpuTimingStatisticsInternal() const { - m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics); + m_commandQueueContext.UpdateCpuTimingStatistics(); } AZStd::vector Device::GetValidSwapChainImageFormats(const RHI::WindowHandle& windowHandle) const diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h index 13ccf9367b..9c21103929 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h @@ -126,7 +126,7 @@ namespace AZ void EndFrameInternal() override; void WaitForIdleInternal() override; void CompileMemoryStatisticsInternal(RHI::MemoryStatisticsBuilder& builder) override; - void UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const override; + void UpdateCpuTimingStatisticsInternal() const override; AZStd::vector GetValidSwapChainImageFormats(const RHI::WindowHandle& windowHandle) const override; AZStd::chrono::microseconds GpuTimestampToMicroseconds(uint64_t gpuTimestamp, RHI::HardwareQueueClass queueClass) const override; void FillFormatsCapabilitiesInternal(FormatCapabilitiesList& formatsCapabilities) override; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp index c2fe16b551..2dce5d85f3 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp @@ -16,12 +16,27 @@ namespace AZ { static const uint32_t s_minVulkanSupportedVersion = VK_API_VERSION_1_0; + static EnvironmentVariable s_vulkanInstance; + static constexpr const char* s_vulkanInstanceKey = "VulkanInstance"; + Instance& Instance::GetInstance() { - static Instance s_instance; - return s_instance; + if (!s_vulkanInstance) + { + s_vulkanInstance = Environment::FindVariable(s_vulkanInstanceKey); + if (!s_vulkanInstance) + { + s_vulkanInstance = Environment::CreateVariable(s_vulkanInstanceKey); + } + } + + return s_vulkanInstance.Get(); } + void Instance::Reset() + { + s_vulkanInstance.Reset(); + } Instance::~Instance() { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h index 40a6951c2d..efa8d36d2b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h @@ -34,6 +34,7 @@ namespace AZ }; static Instance& GetInstance(); + static void Reset(); ~Instance(); bool Init(const Descriptor& descriptor); void Shutdown(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp index c5ca07eb27..59dbaf4377 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -105,66 +105,87 @@ namespace AZ { auto& group = static_cast(groupBase); group.UpdateCompiledDataIndex(m_currentIteration); + + if (!groupData.IsAnyResourceTypeUpdated()) + { + return RHI::ResultCode::Success; + } + DescriptorSet& descriptorSet = *group.m_compiledData[group.GetCompileDataIndex()]; const RHI::ShaderResourceGroupLayout* layout = groupData.GetLayout(); - for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForBuffers().size()); ++groupIndex) + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewMask))) { - const RHI::ShaderInputBufferIndex index(groupIndex); - auto bufViews = groupData.GetBufferViewArray(index); - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferView); - descriptorSet.UpdateBufferViews(layoutIndex, bufViews); - } - - auto const& shaderImageList = layout->GetShaderInputListForImages(); - for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForImages().size()); ++groupIndex) - { - const RHI::ShaderInputImageIndex index(groupIndex); - auto imgViews = groupData.GetImageViewArray(index); - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageView); - descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageList[groupIndex].m_type); - } - - for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForBufferUnboundedArrays().size()); ++groupIndex) - { - const RHI::ShaderInputBufferUnboundedArrayIndex index(groupIndex); - auto bufViews = groupData.GetBufferViewUnboundedArray(index); - if (bufViews.empty()) + for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForBuffers().size()); ++groupIndex) { - // skip empty unbounded arrays - continue; + const RHI::ShaderInputBufferIndex index(groupIndex); + auto bufViews = groupData.GetBufferViewArray(index); + uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferView); + descriptorSet.UpdateBufferViews(layoutIndex, bufViews); } - - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferViewUnboundedArray); - descriptorSet.UpdateBufferViews(layoutIndex, bufViews); } - auto const& shaderImageUnboundeArrayList = layout->GetShaderInputListForImageUnboundedArrays(); - for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForImageUnboundedArrays().size()); ++groupIndex) + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewMask))) { - const RHI::ShaderInputImageUnboundedArrayIndex index(groupIndex); - auto imgViews = groupData.GetImageViewUnboundedArray(index); - if (imgViews.empty()) + auto const& shaderImageList = layout->GetShaderInputListForImages(); + for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForImages().size()); ++groupIndex) { - // skip empty unbounded arrays - continue; + const RHI::ShaderInputImageIndex index(groupIndex); + auto imgViews = groupData.GetImageViewArray(index); + uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageView); + descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageList[groupIndex].m_type); } - - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageViewUnboundedArray); - descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageUnboundeArrayList[groupIndex].m_type); } - for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForSamplers().size()); ++groupIndex) + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewUnboundedArrayMask))) { - const RHI::ShaderInputSamplerIndex index(groupIndex); - auto samplerArray = groupData.GetSamplerArray(index); - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::Sampler); - descriptorSet.UpdateSamplers(layoutIndex, samplerArray); + for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForBufferUnboundedArrays().size()); ++groupIndex) + { + const RHI::ShaderInputBufferUnboundedArrayIndex index(groupIndex); + auto bufViews = groupData.GetBufferViewUnboundedArray(index); + if (bufViews.empty()) + { + // skip empty unbounded arrays + continue; + } + + uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferViewUnboundedArray); + descriptorSet.UpdateBufferViews(layoutIndex, bufViews); + } + } + + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewUnboundedArrayMask))) + { + auto const& shaderImageUnboundeArrayList = layout->GetShaderInputListForImageUnboundedArrays(); + for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForImageUnboundedArrays().size()); ++groupIndex) + { + const RHI::ShaderInputImageUnboundedArrayIndex index(groupIndex); + auto imgViews = groupData.GetImageViewUnboundedArray(index); + if (imgViews.empty()) + { + // skip empty unbounded arrays + continue; + } + + uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageViewUnboundedArray); + descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageUnboundeArrayList[groupIndex].m_type); + } + } + + if (groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::SamplerMask))) + { + for (uint32_t groupIndex = 0; groupIndex < static_cast(layout->GetShaderInputListForSamplers().size()); ++groupIndex) + { + const RHI::ShaderInputSamplerIndex index(groupIndex); + auto samplerArray = groupData.GetSamplerArray(index); + uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::Sampler); + descriptorSet.UpdateSamplers(layoutIndex, samplerArray); + } } auto constantData = groupData.GetConstantData(); - if (!constantData.empty()) + if (!constantData.empty() && groupData.IsResourceTypeEnabledForCompilation(static_cast(RHI::ShaderResourceGroupData::ResourceTypeMask::ConstantDataMask))) { descriptorSet.UpdateConstantData(constantData); } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp index 36e0c2b2f5..0d401003d9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp @@ -101,6 +101,7 @@ namespace AZ RHI::FactoryManagerBus::Broadcast(&RHI::FactoryManagerRequest::UnregisterFactory, this); Instance::GetInstance().Shutdown(); + Instance::Reset(); } Name SystemComponent::GetName() diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h index 90389687de..fcf812cc38 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h @@ -228,9 +228,6 @@ namespace AZ PipelineViewMap m_pipelineViewsByTag; - /// The system time when the last time this pipeline render was started - float m_lastRenderStartTime = 0; - // RenderPipeline's name id, it will be used to identify the render pipeline when it's added to a Scene RenderPipelineId m_nameId; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index 5df1c655d6..eb74a81e3e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -263,7 +263,7 @@ namespace AZ AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime); ScriptTimePoint currentTime; AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick); - m_tickTime.m_currentGameTime = static_cast(currentTime.GetMilliseconds()); + m_tickTime.m_currentGameTime = static_cast(currentTime.GetSeconds()); } void RPISystem::RenderTick() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index 1b99abae4e..6378a249a4 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -375,12 +375,10 @@ namespace AZ m_scene->RemoveRenderPipeline(m_nameId); } - void RenderPipeline::OnStartFrame(const TickTimeInfo& tick) + void RenderPipeline::OnStartFrame([[maybe_unused]] const TickTimeInfo& tick) { AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); - m_lastRenderStartTime = tick.m_currentGameTime; - OnPassModified(); for (auto& viewItr : m_pipelineViewsByTag) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroup.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroup.cpp index f95c6abfcd..df16b5cd08 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroup.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroup.cpp @@ -114,6 +114,10 @@ namespace AZ void ShaderResourceGroup::Compile() { m_shaderResourceGroup->Compile(m_data); + + //Disable compilation for all resource types as a performance optimization + //No need to re-update SRG data on GPU timeline if nothing was updated. + m_data.DisableCompilationForAllResourceTypes(); } bool ShaderResourceGroup::IsQueuedForCompile() const diff --git a/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h b/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h index 2ed2875cad..b8d1cfa050 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h +++ b/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h @@ -61,7 +61,7 @@ namespace UnitTest void EndFrameInternal() override {} void WaitForIdleInternal() override {} void CompileMemoryStatisticsInternal(AZ::RHI::MemoryStatisticsBuilder&) override {} - void UpdateCpuTimingStatisticsInternal([[maybe_unused]] AZ::RHI::CpuTimingStatistics& cpuTimingStatistics) const override {} + void UpdateCpuTimingStatisticsInternal() const override {} AZStd::chrono::microseconds GpuTimestampToMicroseconds([[maybe_unused]] uint64_t gpuTimestamp, [[maybe_unused]] AZ::RHI::HardwareQueueClass queueClass) const override { return AZStd::chrono::microseconds(); diff --git a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl index 7be62c9343..2dbba46d8b 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl +++ b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl @@ -170,6 +170,7 @@ ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN) // Position, Normal, Roughness surface.position = IN.m_worldPosition.xyz; surface.normal = normalize(normal); + surface.vertexNormal = surfaceNormal; surface.roughnessLinear = 1.0f; surface.CalculateRoughnessA(); diff --git a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.azsl b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.azsl index 6f73b63ad7..2f6f038e4b 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.azsl +++ b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.azsl @@ -63,6 +63,7 @@ ForwardPassOutput MinimalPBR_MainPassPS(VSOutput IN) // Position, Normal, Roughness surface.position = IN.m_worldPosition.xyz; surface.normal = normalize(IN.m_normal); + surface.vertexNormal = normalize(IN.m_normal); surface.roughnessLinear = MinimalPBRSrg::m_roughness; surface.CalculateRoughnessA(); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index 623d759c9f..b407aa2b4c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -90,7 +90,7 @@ namespace AtomToolsFramework //! Input processing is enabled by default. void SetInputProcessingEnabled(bool enabled); - // AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler ... + // AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler overrides ... AzFramework::CameraState GetCameraState() override; AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) override; AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; @@ -98,12 +98,12 @@ namespace AtomToolsFramework const AzFramework::ScreenPoint& screenPosition) override; float DeviceScalingFactor() override; - // AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler ... + // AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler overrides ... void BeginCursorCapture() override; void EndCursorCapture() override; bool IsMouseOver() const override; - // AzFramework::WindowRequestBus::Handler ... + // AzFramework::WindowRequestBus::Handler overrides ... void SetWindowTitle(const AZStd::string& title) override; AzFramework::WindowSize GetClientAreaSize() const override; void ResizeClientArea(AzFramework::WindowSize clientAreaSize) override; @@ -116,18 +116,17 @@ namespace AtomToolsFramework uint32_t GetDisplayRefreshRate() const override; protected: - // AzFramework::InputChannelEventListener ... + // AzFramework::InputChannelEventListener overrides ... bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override; - // AZ::TickBus::Handler ... + // AZ::TickBus::Handler overrides ... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; - // QWidget ... + // QWidget overrides ... void resizeEvent(QResizeEvent *event) override; bool event(QEvent* event) override; void enterEvent(QEvent* event) override; void leaveEvent(QEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; private: void SendWindowResizeEvent(); @@ -143,8 +142,6 @@ namespace AtomToolsFramework AZ::RPI::AuxGeomDrawPtr m_auxGeom; // Tracks whether the cursor is currently over our viewport, used for mouse input event book-keeping. bool m_mouseOver = false; - // The last recorded mouse position, in local viewport screen coordinates. - QPointF m_mousePosition; // Captures the time between our render events to give controllers a time delta. QElapsedTimer m_renderTimer; // The time of the last recorded tick event from the system tick bus. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png new file mode 100644 index 0000000000..d040fa2e14 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81b5fa1f978888c3be8a40fce20455668df2723a77587aeb7039f8bf74bdd0e3 +size 119 diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg new file mode 100644 index 0000000000..c33e340a54 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc index 81a962801d..76733c52ed 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc @@ -2,5 +2,7 @@ Icons/group_closed.png Icons/group_open.png + Icons/blank.png + Icons/changed_property.svg diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 7192afebf7..83926ecc79 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -214,20 +214,17 @@ namespace AtomToolsFramework m_mouseOver = false; } - void RenderViewportWidget::mouseMoveEvent(QMouseEvent* event) - { - m_mousePosition = event->localPos(); - } - void RenderViewportWidget::SendWindowResizeEvent() { // Scale the size by the DPI of the platform to // get the proper size in pixels. + const auto pixelRatio = devicePixelRatioF(); const QSize uiWindowSize = size(); - const QSize windowSize = uiWindowSize * devicePixelRatioF(); + const QSize windowSize = uiWindowSize * pixelRatio; const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); - AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); + AzFramework::WindowNotificationBus::Event( + windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); } AZ::Name RenderViewportWidget::GetCurrentContextName() const diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp index 5598d894ff..c3bb13d1d2 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -96,10 +95,10 @@ namespace MaterialEditor ResetStats(); } - const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics(); - if (stats) + double frameTime = AZ::RHI::RHISystemInterface::Get()->GetCpuFrameTime(); + if (frameTime > 0) { - m_cpuFrameTimeMs.PushSample(stats->GetFrameToFrameTimeMilliseconds()); + m_cpuFrameTimeMs.PushSample(frameTime); } AZ::RHI::Ptr rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass(); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg index 83df996198..a79bebdd46 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg @@ -1,15 +1,6 @@ - - - - icon / Environmental / Sky Highlight - Created with Sketch. - - - - - - - - - - \ No newline at end of file + + + + + + diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 025de21d31..e99f456653 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -101,9 +101,9 @@ namespace MaterialEditor { if (IsInstanceNodePropertyModifed(node)) { - return ":/PropertyEditor/Resources/changed_data_item.png"; + return ":/Icons/changed_property.svg"; } - return ":/PropertyEditor/Resources/blank.png"; + return ":/Icons/blank.png"; } void MaterialInspector::AddOverviewGroup() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp index 1e189168da..10442e0c27 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp @@ -88,18 +88,18 @@ namespace MaterialEditor toneMappingButton->setVisible(true); addWidget(toneMappingButton); - // Add model combo box - auto modelPresetComboBox = new ModelPresetComboBox(this); - modelPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); - modelPresetComboBox->view()->setMinimumWidth(200); - addWidget(modelPresetComboBox); - // Add lighting preset combo box auto lightingPresetComboBox = new LightingPresetComboBox(this); lightingPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); lightingPresetComboBox->view()->setMinimumWidth(200); addWidget(lightingPresetComboBox); + // Add model combo box + auto modelPresetComboBox = new ModelPresetComboBox(this); + modelPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); + modelPresetComboBox->view()->setMinimumWidth(200); + addWidget(modelPresetComboBox); + MaterialViewportNotificationBus::Handler::BusConnect(); } diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h index a649fcf0f7..4a326660dd 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h @@ -12,17 +12,11 @@ #include #include -#include #include namespace AZ { - namespace RHI - { - struct CpuTimingStatistics; - } - namespace Render { //! Stores all the data associated with a row in the table. @@ -88,11 +82,17 @@ namespace AZ using GroupRegionName = AZ::RHI::CachedTimeRegion::GroupRegionName; public: + struct CpuTimingEntry + { + const AZStd::string& m_name; + double m_executeDuration; + }; + ImGuiCpuProfiler() = default; ~ImGuiCpuProfiler() = default; //! Draws the overall CPU profiling window, defaults to the statistical view - void Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& cpuTimingStatistics); + void Draw(bool& keepDrawing); private: static constexpr float RowHeight = 35.0; @@ -121,11 +121,14 @@ namespace AZ // Sort the table by a given column, rearranges the pointers in m_tableData. void SortTable(ImGuiTableSortSpecs* sortSpecs); + // gather the latest timing statistics + void CacheCpuTimingStatistics(); + // Get the profiling data from the last frame, only called when the profiler is not paused. void CollectFrameData(); // Cull old data from internal storage, only called when profiler is not paused. - void CullFrameData(const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics); + void CullFrameData(); // Draws a single block onto the timeline into the specified row void DrawBlock(const TimeRegion& block, u64 targetRow); @@ -204,7 +207,8 @@ namespace AZ bool m_enableVisualizer = false; // Last captured CPU timing statistics - AZ::RHI::CpuTimingStatistics m_cpuTimingStatisticsWhenPause; + AZStd::vector m_cpuTimingStatisticsWhenPause; + AZStd::sys_time_t m_frameToFrameTime{}; AZStd::string m_lastCapturedFilePath; diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl index a573e3019a..daf6eca5c0 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -16,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +31,7 @@ namespace AZ { namespace CpuProfilerImGuiHelper { - inline float TicksToMs(AZStd::sys_time_t ticks) + inline float TicksToMs(double ticks) { // Note: converting to microseconds integer before converting to milliseconds float const AZStd::sys_time_t ticksPerSecond = AZStd::GetTimeTicksPerSecond(); @@ -39,6 +39,11 @@ namespace AZ return static_cast((ticks * 1000) / (ticksPerSecond / 1000)) / 1000.0f; } + inline float TicksToMs(AZStd::sys_time_t ticks) + { + return TicksToMs(static_cast(ticks)); + } + using DeserializedCpuData = AZStd::vector; inline Outcome LoadSavedCpuProfilingStatistics(const AZStd::string& capturePath) { @@ -108,7 +113,9 @@ namespace AZ } } // namespace CpuProfilerImGuiHelper - inline void ImGuiCpuProfiler::Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics) + + + inline void ImGuiCpuProfiler::Draw(bool& keepDrawing) { // Cache the value to detect if it was changed by ImGui(user pressed 'x') const bool cachedShowCpuProfiler = keepDrawing; @@ -121,10 +128,10 @@ namespace AZ if (!m_paused) { // Update region map and cache the input cpu timing statistics when the profiling is not paused - m_cpuTimingStatisticsWhenPause = currentCpuTimingStatistics; + CacheCpuTimingStatistics(); CollectFrameData(); - CullFrameData(currentCpuTimingStatistics); + CullFrameData(); // Only listen to system ticks when the profiler is active if (!SystemTickBus::Handler::BusIsConnected()) @@ -354,19 +361,12 @@ namespace AZ { DrawCommonHeader(); - const AZ::RHI::CpuTimingStatistics& cpuTimingStatistics = m_cpuTimingStatisticsWhenPause; - - const auto ShowTimeInMs = [](AZStd::sys_time_t duration) - { - ImGui::Text("%.2f ms", CpuProfilerImGuiHelper::TicksToMs(duration)); - }; - - const auto ShowRow = [&ShowTimeInMs](const char* regionLabel, AZStd::sys_time_t duration) + const auto ShowRow = [](const char* regionLabel, double duration) { ImGui::Text("%s", regionLabel); ImGui::NextColumn(); - ShowTimeInMs(duration); + ImGui::Text("%.2f ms", CpuProfilerImGuiHelper::TicksToMs(duration)); ImGui::NextColumn(); }; @@ -377,11 +377,9 @@ namespace AZ ImGui::SetColumnWidth(0, 660.0f); ImGui::SetColumnWidth(1, 100.0f); - ShowRow("Frame to Frame Time", cpuTimingStatistics.m_frameToFrameTime); - ShowRow("Present Time", cpuTimingStatistics.m_presentDuration); - for (const auto& queueStatistics : cpuTimingStatistics.m_queueStatistics) + for (const auto& queueStatistics : m_cpuTimingStatisticsWhenPause) { - ShowRow(queueStatistics.m_queueName.GetCStr(), queueStatistics.m_executeDuration); + ShowRow(queueStatistics.m_name.c_str(), queueStatistics.m_executeDuration); } ImGui::Separator(); @@ -653,6 +651,32 @@ namespace AZ ImGui::EndChild(); } + inline void ImGuiCpuProfiler::CacheCpuTimingStatistics() + { + using namespace AZ::Statistics; + + m_cpuTimingStatisticsWhenPause.clear(); + if (auto statsProfiler = AZ::Interface::Get(); statsProfiler) + { + auto& rhiMetrics = statsProfiler->GetProfiler(AZ_CRC_CE("RHI")); + + const NamedRunningStatistic* frameTimeMetric = rhiMetrics.GetStatistic(AZ_CRC_CE("Frame to Frame Time")); + if (frameTimeMetric) + { + m_frameToFrameTime = static_cast(frameTimeMetric->GetMostRecentSample()); + } + + AZStd::vector statistics; + rhiMetrics.GetStatsManager().GetAllStatistics(statistics); + + for (NamedRunningStatistic* stat : statistics) + { + m_cpuTimingStatisticsWhenPause.push_back({ stat->GetName(), stat->GetMostRecentSample() }); + stat->Reset(); + } + } + } + inline void ImGuiCpuProfiler::CollectFrameData() { // We maintain separate datastores for the visualizer and the statistical view because they require different @@ -721,10 +745,9 @@ namespace AZ } } - inline void ImGuiCpuProfiler::CullFrameData(const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics) + inline void ImGuiCpuProfiler::CullFrameData() { - const AZStd::sys_time_t frameToFrameTime = currentCpuTimingStatistics.m_frameToFrameTime; - const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - frameToFrameTime * m_framesToCollect; + const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - m_frameToFrameTime * m_framesToCollect; // Remove old frame boundary data auto firstBoundaryToKeepItr = AZStd::upper_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), deleteBeforeTick); diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/Code/Source/AtomImGuiToolsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomImGuiTools/Code/Source/AtomImGuiToolsSystemComponent.cpp index ec18997a01..b369ab2ee2 100644 --- a/Gems/AtomLyIntegration/AtomImGuiTools/Code/Source/AtomImGuiToolsSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomImGuiTools/Code/Source/AtomImGuiToolsSystemComponent.cpp @@ -86,11 +86,7 @@ namespace AtomImGuiTools } if (m_showCpuProfiler) { - const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics(); - if (stats) - { - m_imguiCpuProfiler.Draw(m_showCpuProfiler, *stats); - } + m_imguiCpuProfiler.Draw(m_showCpuProfiler); } if (m_showTransientAttachmentProfiler) { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp index b10a3e6695..fb21e74d8e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp @@ -71,7 +71,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://o3de.org/docs/user-guide/components/reference/attachment/") + "https://o3de.org/docs/user-guide/components/reference/animation/attachment/") ->DataElement(0, &EditorAttachmentComponent::m_targetId, "Target entity", "Attach to this entity.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetIdChanged) ->DataElement( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 659c394cba..954ec4cad8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -49,7 +49,7 @@ namespace AZ ->Attribute(Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AreaLight.svg") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/area-light/") + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/light/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp index ef9c73c0b4..2854244f6b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp @@ -44,7 +44,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO][ATOM-1998] create icons. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-1998] create page + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/directional-light/") // [GFX TODO][ATOM-1998] create page ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index 4363ac1a62..c14510f195 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp @@ -54,6 +54,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/diffuse-probe-grid/") ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp index 76f198852e..253f3ddd90 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp @@ -34,7 +34,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/grid/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp index 52a4cd4343..b4131894f4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp @@ -175,6 +175,10 @@ namespace AZ void GridComponentController::OnBeginPrepareRender() { auto* auxGeomFP = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + if (!auxGeomFP) + { + return; + } if (auto auxGeom = auxGeomFP->GetDrawQueue()) { BuildGrid(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/EditorImageBasedLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/EditorImageBasedLightComponent.cpp index 0547da6240..31eb0d51b1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/EditorImageBasedLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/EditorImageBasedLightComponent.cpp @@ -34,7 +34,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/global-skylight-ibl/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp index b7bf191bc9..8e5e7e2345 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp @@ -86,7 +86,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/material/") ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorMaterialComponent::m_message, "Message", "") ->Attribute(AZ_CRC("PlaceholderText", 0xa23ec278), "Component cannot be edited with multiple entities selected") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 665adcd568..815f14aba7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -521,9 +521,9 @@ namespace AZ { if (IsInstanceNodePropertyModifed(node)) { - return ":/PropertyEditor/Resources/changed_data_item.png"; + return ":/Icons/changed_property.svg"; } - return ":/PropertyEditor/Resources/blank.png"; + return ":/Icons/blank.png"; } bool MaterialPropertyInspector::SaveMaterial() const diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp index 0723859ff5..2931b647e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp @@ -54,7 +54,7 @@ namespace AZ AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } - void MaterialThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) + void MaterialThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; m_renderWait.release(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h index dba922a1b2..8f2053ba67 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h @@ -36,7 +36,7 @@ namespace AZ ~MaterialThumbnail() override; //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; + void ThumbnailRendered(const QPixmap& thumbnailImage) override; void ThumbnailFailedToRender() override; protected: diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp index 3324bdfa8d..c89546264a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp @@ -48,7 +48,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Mesh.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/mesh/") ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) ->DataElement(AZ::Edit::UIHandlers::Button, &EditorMeshComponent::m_addMaterialComponentFlag, "Add Material Component", "Add Material Component") ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp index 658d420a16..845197d939 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp @@ -55,7 +55,7 @@ namespace AZ AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } - void MeshThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) + void MeshThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; m_renderWait.release(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h index 2975b6950c..872e1029d2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h @@ -35,7 +35,7 @@ namespace AZ ~MeshThumbnail() override; //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; + void ThumbnailRendered(const QPixmap& thumbnailImage) override; void ThumbnailFailedToRender() override; protected: diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp index 3908aafe89..85eb4a209a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp @@ -37,6 +37,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/occlusion-culling-plane/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Bloom/EditorBloomComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Bloom/EditorBloomComponent.cpp index 22c8e2dceb..9018b0860b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Bloom/EditorBloomComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Bloom/EditorBloomComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need create page for PostProcessing. + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/bloom/") // [TODO ATOM-2672][PostFX] need create page for PostProcessing. ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.cpp index b3f77b1b8f..4b9ed8f6af 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.y ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing. + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/depth-of-field/") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing. ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index e10a8b14c9..5ae3d88e50 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -34,7 +34,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO][ATOM-2672][PostFX] need to create icons for PostProcessing. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) })) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need to create page for PostProcessing. + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/display-mapper/") // [GFX TODO][ATOM-2672][PostFX] need to create page for PostProcessing. ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/EditorPostFxLayerComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/EditorPostFxLayerComponent.cpp index 344c5f8edd..b61cce839e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/EditorPostFxLayerComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/EditorPostFxLayerComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/postfx-layer/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/EditorExposureControlComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/EditorExposureControlComponent.cpp index 5bf3e0ec9c..d8bf13dd81 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/EditorExposureControlComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/EditorExposureControlComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need create page for PostProcessing. + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/exposure-control/") // [TODO ATOM-2672][PostFX] need create page for PostProcessing. ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/EditorGradientWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/EditorGradientWeightModifierComponent.cpp index 86b5ad554a..2f143491f5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/EditorGradientWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/GradientWeightModifier/EditorGradientWeightModifierComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "") + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/postfx-gradient-weight-modifier/") ; editContext->Class("GradientWeightModifierComponentController", "") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp index 310762863a..b23ae9805b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need to create page for PostProcessing. + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/look-modification/") // [TODO ATOM-2672][PostFX] need to create page for PostProcessing. ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp index 219ab6acb9..d4d86d6496 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "") + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/radius-weight-modifier/") ; editContext->Class("RadiusWeightModifierComponentController", "") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/EditorShapeWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/EditorShapeWeightModifierComponent.cpp index 46de0f0c68..842e1a6995 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/EditorShapeWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ShapeWeightModifier/EditorShapeWeightModifierComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "") + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/postfx-shape-weight-modifier/") ; editContext->Class("ShapeWeightModifierComponentController", "") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Ssao/EditorSsaoComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Ssao/EditorSsaoComponent.cpp index b018ac7a54..43f311a77e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Ssao/EditorSsaoComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/Ssao/EditorSsaoComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing. + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/ssao/") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing. ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index f325a20ac1..99e99abf4a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -53,6 +53,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/reflection-probe/") ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) ->ClassElement(AZ::Edit::ClassElements::Group, "Cubemap Bake") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ScreenSpace/EditorDeferredFogComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ScreenSpace/EditorDeferredFogComponent.cpp index 208747abaa..139c24a959 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ScreenSpace/EditorDeferredFogComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ScreenSpace/EditorDeferredFogComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO][ATOM-13427] Create Wiki for Deferred Fog + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/deferred-fog/") // [TODO][ATOM-13427] Create Wiki for Deferred Fog ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Scripting/EditorEntityReferenceComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Scripting/EditorEntityReferenceComponent.cpp index 65491a9833..e297b7cc12 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Scripting/EditorEntityReferenceComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Scripting/EditorEntityReferenceComponent.cpp @@ -31,7 +31,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "") + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/entity-reference/") ; editContext->Class("EntityReferenceComponentController", "") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorHDRiSkyboxComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorHDRiSkyboxComponent.cpp index a0ffb4e509..1cf14cca71 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorHDRiSkyboxComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorHDRiSkyboxComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/hdri-skybox/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorPhysicalSkyComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorPhysicalSkyComponent.cpp index 28cb4121cf..81b182ccaa 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorPhysicalSkyComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/EditorPhysicalSkyComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/physical-sky/") ; editContext->Class( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp index b71ba1e148..171c5e417f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp @@ -65,7 +65,7 @@ namespace AZ m_featureProcessorInterface = RPI::Scene::GetFeatureProcessorForEntity(entityId); // only activate if there is no other skybox activate - if (!m_featureProcessorInterface->IsEnabled()) + if (m_featureProcessorInterface && !m_featureProcessorInterface->IsEnabled()) { m_featureProcessorInterface->SetSkyboxMode(SkyBoxMode::Cubemap); m_featureProcessorInterface->Enable(true); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt index 5a08bab4fa..cc71e4f237 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt @@ -54,11 +54,21 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) INCLUDE_DIRECTORIES PRIVATE Source + Tools BUILD_DEPENDENCIES PRIVATE AZ::AzCore Gem::EMotionFX_Atom.Static + Gem::EMotionFX.Editor.Static + Gem::AtomToolsFramework.Static + Gem::AtomToolsFramework.Editor + Gem::Atom_Component_DebugCamera.Static + Gem::Atom_Feature_Common.Static + Gem::AtomLyIntegration_CommonFeatures.Static RUNTIME_DEPENDENCIES Gem::EMotionFX.Editor + COMPILE_DEFINITIONS + PUBLIC + EMOTIONFXATOM_EDITOR ) endif() diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorModule.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorModule.cpp index 49c2e52c1a..93481c1da6 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorModule.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorModule.cpp @@ -11,6 +11,9 @@ #include #include #include +#ifdef EMOTIONFXATOM_EDITOR +#include +#endif namespace AZ { @@ -28,7 +31,10 @@ namespace AZ : Module() { m_descriptors.insert(m_descriptors.end(), { - ActorSystemComponent::CreateDescriptor() + ActorSystemComponent::CreateDescriptor(), +#ifdef EMOTIONFXATOM_EDITOR + EMotionFXAtom::EditorSystemComponent::CreateDescriptor(), +#endif }); } @@ -39,6 +45,9 @@ namespace AZ { return ComponentTypeList{ azrtti_typeid(), +#ifdef EMOTIONFXATOM_EDITOR + azrtti_typeid(), +#endif }; } }; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/Editor/EditorSystemComponent.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/Editor/EditorSystemComponent.cpp new file mode 100644 index 0000000000..edd2f074fe --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/Editor/EditorSystemComponent.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include + +namespace AZ::EMotionFXAtom +{ + void EditorSystemComponent::Reflect(ReflectContext* context) + { + if (SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class()->Version(0); + } + } + + void EditorSystemComponent::Activate() + { + EMotionFX::Integration::SystemNotificationBus::Handler::BusConnect(); + } + + void EditorSystemComponent::Deactivate() + { + EMotionFX::Integration::SystemNotificationBus::Handler::BusDisconnect(); + } + + void EditorSystemComponent::OnRegisterPlugin() + { + EMStudio::PluginManager* pluginManager = EMStudio::EMStudioManager::GetInstance()->GetPluginManager(); + pluginManager->RegisterPlugin(aznew EMStudio::AtomRenderPlugin()); + } +} // namespace AZ::EMotionFXAtom diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/Editor/EditorSystemComponent.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/Editor/EditorSystemComponent.h new file mode 100644 index 0000000000..80ff7b0b8d --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/Editor/EditorSystemComponent.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include + +namespace AZ::EMotionFXAtom +{ + class EditorSystemComponent + : public Component + , private EMotionFX::Integration::SystemNotificationBus::Handler + { + public: + AZ_COMPONENT(EditorSystemComponent, "{1FAEC046-255D-4664-8F12-D16503C34431}"); + + static void Reflect(ReflectContext* context); + + protected: + // AZ::Component overrides + void Activate() override; + void Deactivate() override; + + // SystemNotificationBus::OnRegisterPlugin + void OnRegisterPlugin() override; + }; +} // namespace AZ::EMotionFXAtom diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp new file mode 100644 index 0000000000..52f857551c --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace EMStudio +{ + static constexpr float DepthNear = 0.01f; + + AnimViewportRenderer::AnimViewportRenderer(AZ::RPI::ViewportContextPtr viewportContext) + : m_windowContext(viewportContext->GetWindowContext()) + { + // Create a new entity context + m_entityContext = AZStd::make_unique(); + m_entityContext->InitContext(); + + // Create the scene + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Unable to retrieve scene system."); + AZ::Outcome, AZStd::string> createSceneOutcome = sceneSystem->CreateScene("AnimViewport"); + AZ_Assert(createSceneOutcome, "%s", createSceneOutcome.GetError().data()); + m_frameworkScene = createSceneOutcome.TakeValue(); + m_frameworkScene->SetSubsystem(m_entityContext.get()); + + // Create and register a scene with all available feature processors + AZ::RPI::SceneDescriptor sceneDesc; + m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); + m_scene->EnableAllFeatureProcessors(); + + // Link our RPI::Scene to the AzFramework::Scene + m_frameworkScene->SetSubsystem(m_scene); + + // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene + AZStd::string defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset"; + AZ::Data::Asset pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath( + defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error); + m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *m_windowContext.get()); + pipelineAsset.Release(); + m_scene->AddRenderPipeline(m_renderPipeline); + m_renderPipeline->SetDefaultView(viewportContext->GetDefaultView()); + + // Currently the scene has to be activated after render pipeline was added so some feature processors (i.e. imgui) can be + // initialized properly with pipeline's pass information. + m_scene->Activate(); + AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene); + AzFramework::EntityContextId entityContextId = m_entityContext->GetContextId(); + + // Get the FeatureProcessors + m_meshFeatureProcessor = m_scene->GetFeatureProcessor(); + + // Configure tone mapper + AzFramework::EntityContextRequestBus::EventResult( + m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity"); + AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity."); + + m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId); + m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId); + m_postProcessEntity->CreateComponent(azrtti_typeid()); + m_postProcessEntity->Init(); + m_postProcessEntity->Activate(); + + // Init directional light processor + m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor(); + + // Init display mapper processor + m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor(); + + // Init Skybox + m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor(); + m_skyboxFeatureProcessor->Enable(true); + m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap); + + // Create IBL + AzFramework::EntityContextRequestBus::EventResult( + m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity"); + AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity."); + + m_iblEntity->CreateComponent(AZ::Render::ImageBasedLightComponentTypeId); + m_iblEntity->CreateComponent(azrtti_typeid()); + m_iblEntity->Init(); + m_iblEntity->Activate(); + + // Load light preset + AZ::Data::Asset lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath( + "lightingpresets/default.lightingpreset.azasset", AZ::RPI::AssetUtils::TraceLevel::Warning); + const AZ::Render::LightingPreset* preset = lightingPresetAsset->GetDataAs(); + SetLightingPreset(preset); + + // Create grid + AzFramework::EntityContextRequestBus::EventResult( + m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid"); + AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity."); + + AZ::Render::GridComponentConfig gridConfig; + gridConfig.m_gridSize = 4.0f; + gridConfig.m_axisColor = AZ::Color(0.5f, 0.5f, 0.5f, 1.0f); + gridConfig.m_primaryColor = AZ::Color(0.3f, 0.3f, 0.3f, 1.0f); + gridConfig.m_secondaryColor = AZ::Color(0.5f, 0.1f, 0.1f, 1.0f); + auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId); + gridComponent->SetConfiguration(gridConfig); + + m_gridEntity->CreateComponent(azrtti_typeid()); + m_gridEntity->Init(); + m_gridEntity->Activate(); + + Reinit(); + } + + AnimViewportRenderer::~AnimViewportRenderer() + { + // Destroy all the entity we created. + m_entityContext->DestroyEntity(m_iblEntity); + m_entityContext->DestroyEntity(m_postProcessEntity); + m_entityContext->DestroyEntity(m_gridEntity); + for (AZ::Entity* entity : m_actorEntities) + { + m_entityContext->DestroyEntity(entity); + } + m_actorEntities.clear(); + m_entityContext->DestroyContext(); + + for (AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle& handle : m_lightHandles) + { + m_directionalLightFeatureProcessor->ReleaseLight(handle); + } + m_lightHandles.clear(); + + m_frameworkScene->UnsetSubsystem(m_scene); + + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "AtomViewportRenderer was unable to get the scene system during destruction."); + bool removeSuccess = sceneSystem->RemoveScene("AnimViewport"); + if (!removeSuccess) + { + AZ_Assert(false, "AtomViewportRenderer should be removed."); + } + + AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene); + m_scene = nullptr; + } + + void AnimViewportRenderer::Reinit() + { + ReinitActorEntities(); + ResetEnvironment(); + } + + void AnimViewportRenderer::ResetEnvironment() + { + // Reset environment + AZ::Transform iblTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::Event(m_iblEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, iblTransform); + + const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); + AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); + auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); + } + + void AnimViewportRenderer::ReinitActorEntities() + { + // 1. Destroy all the entities that do not point to any actorAsset anymore. + AZStd::set assetLookup; + AzFramework::EntityContext* entityContext = m_entityContext.get(); + const size_t numActors = EMotionFX::GetActorManager().GetNumActors(); + for (size_t i = 0; i < numActors; ++i) + { + assetLookup.emplace(EMotionFX::GetActorManager().GetActorAsset(i).GetId()); + } + m_actorEntities.erase( + AZStd::remove_if( + m_actorEntities.begin(), m_actorEntities.end(), + [&assetLookup, entityContext](AZ::Entity* entity) + { + EMotionFX::Integration::ActorComponent* actorComponent = + entity->FindComponent(); + if (assetLookup.find(actorComponent->GetActorAsset().GetId()) == assetLookup.end()) + { + entityContext->DestroyEntity(entity); + return true; + } + return false; + }), + m_actorEntities.end()); + + // 2. Create an entity for every actorAsset stored in actor manager. + for (size_t i = 0; i < numActors; ++i) + { + AZ::Data::Asset actorAsset = EMotionFX::GetActorManager().GetActorAsset(i); + if (!actorAsset->IsReady()) + { + continue; + } + + AZ::Entity* entity = FindActorEntity(actorAsset); + if (!entity) + { + m_actorEntities.emplace_back(CreateActorEntity(actorAsset)); + } + } + } + + AZ::Entity* AnimViewportRenderer::FindActorEntity(AZ::Data::Asset actorAsset) const + { + const auto foundEntity = AZStd::find_if( + begin(m_actorEntities), end(m_actorEntities), + [match = actorAsset](const AZ::Entity* entity) + { + EMotionFX::Integration::ActorComponent* actorComponent = entity->FindComponent(); + return actorComponent->GetActorAsset() == match; + }); + return foundEntity != end(m_actorEntities) ? (*foundEntity) : nullptr; + } + + AZ::Entity* AnimViewportRenderer::CreateActorEntity(AZ::Data::Asset actorAsset) + { + AZ::Entity* actorEntity = m_entityContext->CreateEntity(actorAsset->GetActor()->GetName()); + actorEntity->CreateComponent(azrtti_typeid()); + actorEntity->CreateComponent(AZ::Render::MaterialComponentTypeId); + actorEntity->CreateComponent(azrtti_typeid()); + actorEntity->Init(); + actorEntity->Activate(); + + EMotionFX::Integration::ActorComponent* actorComponent = actorEntity->FindComponent(); + actorComponent->SetActorAsset(actorAsset); + + // Since this entity belongs to the animation editor, we need to set the isOwnByRuntime flag to false. + actorComponent->GetActorInstance()->SetIsOwnedByRuntime(false); + // Selet the actor instance in the command manager after it has been created. + AZStd::string outResult; + EMStudioManager::GetInstance()->GetCommandManager()->ExecuteCommandInsideCommand( + AZStd::string::format("Select -actorInstanceID %i", actorComponent->GetActorInstance()->GetID()).c_str(), outResult); + + return actorEntity; + } + + void AnimViewportRenderer::SetLightingPreset(const AZ::Render::LightingPreset* preset) + { + if (!preset) + { + AZ_Warning("AnimViewportRenderer", false, "Attempting to set invalid lighting preset."); + return; + } + + AZ::Render::ImageBasedLightFeatureProcessorInterface* iblFeatureProcessor = + m_scene->GetFeatureProcessor(); + AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = + m_scene->GetFeatureProcessor(); + AZ::Render::ExposureControlSettingsInterface* exposureControlSettingInterface = + postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_postProcessEntity->GetId()) + ->GetOrCreateExposureControlSettingsInterface(); + + Camera::Configuration cameraConfig; + cameraConfig.m_fovRadians = AZ::Constants::HalfPi; + cameraConfig.m_nearClipDistance = DepthNear; + + preset->ApplyLightingPreset( + iblFeatureProcessor, m_skyboxFeatureProcessor, exposureControlSettingInterface, m_directionalLightFeatureProcessor, + cameraConfig, m_lightHandles, nullptr, AZ::RPI::MaterialPropertyIndex::Null, false); + } +} // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h new file mode 100644 index 0000000000..dd420c175c --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class Entity; + class Component; + + namespace Render + { + class DisplayMapperFeatureProcessorInterface; + class DirectionalLightFeatureProcessorInterface; + class MeshFeatureProcessorInterface; + } + + namespace RPI + { + class WindowContext; + } +} + +namespace EMStudio +{ + class AnimViewportRenderer + { + public: + AZ_CLASS_ALLOCATOR(AnimViewportRenderer, AZ::SystemAllocator, 0); + + AnimViewportRenderer(AZ::RPI::ViewportContextPtr viewportContext); + ~AnimViewportRenderer(); + + void Reinit(); + + private: + + // This function resets the light, camera and other environment settings. + void ResetEnvironment(); + + // This function creates in-editor entities for all actor assets stored in the actor manager, + // and deletes all the actor entities that no longer has an actor asset in the actor manager. + // Those entities are used in atom render viewport to visualize actors in animation editor. + void ReinitActorEntities(); + + AZ::Entity* CreateActorEntity(AZ::Data::Asset actorAsset); + + AZ::Entity* FindActorEntity(AZ::Data::Asset actorAsset) const; + void SetLightingPreset(const AZ::Render::LightingPreset* preset); + + AZStd::shared_ptr m_windowContext; + AZStd::unique_ptr m_entityContext; + AZStd::shared_ptr m_frameworkScene; + AZ::RPI::ScenePtr m_scene; + AZ::RPI::RenderPipelinePtr m_renderPipeline; + AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr; + AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = nullptr; + AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr; + AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; + + AZ::Entity* m_postProcessEntity = nullptr; + AZ::Entity* m_iblEntity = nullptr; + AZ::Entity* m_cameraEntity = nullptr; + AZ::Component* m_cameraComponent = nullptr; + AZ::Entity* m_modelEntity = nullptr; + AZ::Data::AssetId m_modelAssetId; + AZ::Entity* m_gridEntity = nullptr; + AZStd::vector m_actorEntities; + + AZStd::vector m_lightHandles; + }; +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp new file mode 100644 index 0000000000..347e077af3 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include + +namespace EMStudio::ViewportUtil +{ + constexpr AZStd::string_view CameraRotateSmoothnessSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothness"; + constexpr AZStd::string_view CameraTranslateSmoothnessSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothness"; + constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing"; + constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing"; + + constexpr AZStd::string_view CameraOrbitLookIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitLookId"; + constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId"; + constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId"; + constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId"; + constexpr AZStd::string_view CameraTranslateRightIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateRightId"; + constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId"; + constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId"; + constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId"; + + AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds() + { + AzFramework::TranslateCameraInputChannelIds translateCameraInputChannelIds; + translateCameraInputChannelIds.m_leftChannelId = + AzFramework::InputChannelId(GetRegistry(CameraTranslateLeftIdSetting, AZStd::string("keyboard_key_alphanumeric_A")).c_str()); + translateCameraInputChannelIds.m_rightChannelId = + AzFramework::InputChannelId(GetRegistry(CameraTranslateRightIdSetting, AZStd::string("keyboard_key_alphanumeric_D")).c_str()); + translateCameraInputChannelIds.m_forwardChannelId = + AzFramework::InputChannelId(GetRegistry(CameraTranslateForwardIdSetting, AZStd::string("keyboard_key_alphanumeric_W")).c_str()); + translateCameraInputChannelIds.m_backwardChannelId = AzFramework::InputChannelId( + GetRegistry(CameraTranslateBackwardIdSetting, AZStd::string("keyboard_key_alphanumeric_S")).c_str()); + translateCameraInputChannelIds.m_upChannelId = + AzFramework::InputChannelId(GetRegistry(CameraTranslateUpIdSetting, AZStd::string("keyboard_key_alphanumeric_E")).c_str()); + translateCameraInputChannelIds.m_downChannelId = + AzFramework::InputChannelId(GetRegistry(CameraTranslateDownIdSetting, AZStd::string("keyboard_key_alphanumeric_Q")).c_str()); + translateCameraInputChannelIds.m_boostChannelId = + AzFramework::InputChannelId(GetRegistry(CameraTranslateBoostIdSetting, AZStd::string("keyboard_key_modifier_shift_l")).c_str()); + + return translateCameraInputChannelIds; + } + + float CameraRotateSmoothness() + { + return aznumeric_cast(GetRegistry(CameraRotateSmoothnessSetting, 5.0)); + } + + float CameraTranslateSmoothness() + { + return aznumeric_cast(GetRegistry(CameraTranslateSmoothnessSetting, 5.0)); + } + + bool CameraRotateSmoothingEnabled() + { + return GetRegistry(CameraRotateSmoothingSetting, true); + } + + bool CameraTranslateSmoothingEnabled() + { + return GetRegistry(CameraTranslateSmoothingSetting, true); + } + + AzFramework::InputChannelId BuildRotateCameraInputId() + { + return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str()); + } +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h new file mode 100644 index 0000000000..5fa14a8aae --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once +#include +#include + +namespace EMStudio::ViewportUtil +{ + template + AZStd::remove_cvref_t GetRegistry(const AZStd::string_view setting, T&& defaultValue) + { + AZStd::remove_cvref_t value = AZStd::forward(defaultValue); + if (const auto* registry = AZ::SettingsRegistry::Get()) + { + T potentialValue; + if (registry->Get(potentialValue, setting)) + { + value = AZStd::move(potentialValue); + } + } + + return value; + } + + float CameraRotateSmoothness(); + float CameraTranslateSmoothness(); + bool CameraRotateSmoothingEnabled(); + bool CameraTranslateSmoothingEnabled(); + + AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds(); + AzFramework::InputChannelId BuildRotateCameraInputId(); +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp new file mode 100644 index 0000000000..6a6c9df901 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace EMStudio +{ + AnimViewportWidget::AnimViewportWidget(QWidget* parent) + : AtomToolsFramework::RenderViewportWidget(parent) + { + setObjectName(QString::fromUtf8("AtomViewportWidget")); + QSizePolicy qSize(QSizePolicy::Preferred, QSizePolicy::Preferred); + qSize.setHorizontalStretch(0); + qSize.setVerticalStretch(0); + qSize.setHeightForWidth(sizePolicy().hasHeightForWidth()); + setSizePolicy(qSize); + setAutoFillBackground(false); + setStyleSheet(QString::fromUtf8("")); + + m_renderer = AZStd::make_unique(GetViewportContext()); + + SetupCameras(); + SetupCameraController(); + } + + void AnimViewportWidget::SetupCameras() + { + m_rotateCamera = AZStd::make_shared(EMStudio::ViewportUtil::BuildRotateCameraInputId()); + + const auto translateCameraInputChannelIds = EMStudio::ViewportUtil::BuildTranslateCameraInputChannelIds(); + m_translateCamera = AZStd::make_shared( + translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook); + m_translateCamera.get()->m_translateSpeedFn = [] + { + return 3.0f; + }; + + m_orbitDollyScrollCamera = AZStd::make_shared(); + } + + void AnimViewportWidget::SetupCameraController() + { + auto controller = AZStd::make_shared(); + controller->SetCameraViewportContextBuilderCallback( + [viewportId = + GetViewportContext()->GetId()](AZStd::unique_ptr& cameraViewportContext) + { + cameraViewportContext = AZStd::make_unique(viewportId); + }); + + controller->SetCameraPriorityBuilderCallback( + [](AtomToolsFramework::CameraControllerPriorityFn& cameraControllerPriorityFn) + { + cameraControllerPriorityFn = AtomToolsFramework::DefaultCameraControllerPriority; + }); + + controller->SetCameraPropsBuilderCallback( + [](AzFramework::CameraProps& cameraProps) + { + cameraProps.m_rotateSmoothnessFn = [] + { + return EMStudio::ViewportUtil::CameraRotateSmoothness(); + }; + + cameraProps.m_translateSmoothnessFn = [] + { + return EMStudio::ViewportUtil::CameraTranslateSmoothness(); + }; + + cameraProps.m_rotateSmoothingEnabledFn = [] + { + return EMStudio::ViewportUtil::CameraRotateSmoothingEnabled(); + }; + + cameraProps.m_translateSmoothingEnabledFn = [] + { + return EMStudio::ViewportUtil::CameraTranslateSmoothingEnabled(); + }; + }); + + controller->SetCameraListBuilderCallback( + [this](AzFramework::Cameras& cameras) + { + cameras.AddCamera(m_rotateCamera); + cameras.AddCamera(m_translateCamera); + cameras.AddCamera(m_orbitDollyScrollCamera); + }); + GetControllerList()->Add(controller); + } +} // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h new file mode 100644 index 0000000000..7c2e085f84 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include + +namespace EMStudio +{ + class AnimViewportRenderer; + + class AnimViewportWidget + : public AtomToolsFramework::RenderViewportWidget + { + public: + AnimViewportWidget(QWidget* parent = nullptr); + AnimViewportRenderer* GetAnimViewportRenderer() { return m_renderer.get(); } + + private: + void SetupCameras(); + void SetupCameraController(); + + AZStd::unique_ptr m_renderer; + AZStd::shared_ptr m_rotateCamera; + AZStd::shared_ptr m_translateCamera; + AZStd::shared_ptr m_orbitDollyScrollCamera; + }; +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp new file mode 100644 index 0000000000..91a34e16cd --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +namespace EMStudio +{ + AZ_CLASS_ALLOCATOR_IMPL(AtomRenderPlugin, EMotionFX::EditorAllocator, 0); + + AtomRenderPlugin::AtomRenderPlugin() + : DockWidgetPlugin() + { + } + + AtomRenderPlugin::~AtomRenderPlugin() + { + + } + + const char* AtomRenderPlugin::GetName() const + { + return "Atom Render Window (Preview)"; + } + + uint32 AtomRenderPlugin::GetClassID() const + { + return static_cast(AtomRenderPlugin::CLASS_ID); + } + + const char* AtomRenderPlugin::GetCreatorName() const + { + return "O3DE"; + } + + float AtomRenderPlugin::GetVersion() const + { + return 1.0f; + } + + bool AtomRenderPlugin::GetIsClosable() const + { + return true; + } + + bool AtomRenderPlugin::GetIsFloatable() const + { + return true; + } + + bool AtomRenderPlugin::GetIsVertical() const + { + return false; + } + + EMStudioPlugin* AtomRenderPlugin::Clone() + { + return new AtomRenderPlugin(); + } + + EMStudioPlugin::EPluginType AtomRenderPlugin::GetPluginType() const + { + return EMStudioPlugin::PLUGINTYPE_RENDERING; + } + + void AtomRenderPlugin::ReinitRenderer() + { + m_animViewportWidget->GetAnimViewportRenderer()->Reinit(); + } + + bool AtomRenderPlugin::Init() + { + m_innerWidget = new QWidget(); + m_dock->setWidget(m_innerWidget); + + QVBoxLayout* verticalLayout = new QVBoxLayout(m_innerWidget); + verticalLayout->setSizeConstraint(QLayout::SetNoConstraint); + verticalLayout->setSpacing(1); + verticalLayout->setMargin(0); + m_animViewportWidget = new AnimViewportWidget(m_innerWidget); + verticalLayout->addWidget(m_animViewportWidget); + + // Register command callbacks. + m_importActorCallback = new ImportActorCallback(false); + m_removeActorCallback = new RemoveActorCallback(false); + EMStudioManager::GetInstance()->GetCommandManager()->RegisterCommandCallback("ImportActor", m_importActorCallback); + EMStudioManager::GetInstance()->GetCommandManager()->RegisterCommandCallback("RemoveActor", m_removeActorCallback); + + return true; + } + + // Command callbacks + bool ReinitAtomRenderPlugin() + { + EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(static_cast(AtomRenderPlugin::CLASS_ID)); + if (!plugin) + { + AZ_Error("AtomRenderPlugin", false, "Cannot execute command callback. Atom render plugin does not exist."); + return false; + } + + AtomRenderPlugin* atomRenderPlugin = static_cast(plugin); + atomRenderPlugin->ReinitRenderer(); + + return true; + } + + bool AtomRenderPlugin::ImportActorCallback::Execute( + [[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine) + { + return ReinitAtomRenderPlugin(); + } + bool AtomRenderPlugin::ImportActorCallback::Undo( + [[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine) + { + return ReinitAtomRenderPlugin(); + } + + bool AtomRenderPlugin::RemoveActorCallback::Execute( + [[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine) + { + return ReinitAtomRenderPlugin(); + } + bool AtomRenderPlugin::RemoveActorCallback::Undo( + [[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine) + { + return ReinitAtomRenderPlugin(); + } +}// namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h new file mode 100644 index 0000000000..1516b45e77 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include + +#include +#include +#endif + +namespace AZ +{ + class Entity; +} + +namespace EMStudio +{ + class AtomRenderPlugin + : public DockWidgetPlugin + { + public: + AZ_CLASS_ALLOCATOR_DECL + + enum + { + CLASS_ID = 0x32b0c04d + }; + AtomRenderPlugin(); + ~AtomRenderPlugin(); + + // Plugin information + const char* GetName() const override; + uint32 GetClassID() const override; + const char* GetCreatorName() const override; + float GetVersion() const override; + bool GetIsClosable() const override; + bool GetIsFloatable() const override; + bool GetIsVertical() const override; + bool Init() override; + EMStudioPlugin* Clone(); + EMStudioPlugin::EPluginType GetPluginType() const override; + + void ReinitRenderer(); + + private: + MCORE_DEFINECOMMANDCALLBACK(ImportActorCallback); + MCORE_DEFINECOMMANDCALLBACK(RemoveActorCallback); + ImportActorCallback* m_importActorCallback = nullptr; + RemoveActorCallback* m_removeActorCallback = nullptr; + QWidget* m_innerWidget = nullptr; + AnimViewportWidget* m_animViewportWidget = nullptr; + }; +}// namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake index 6401beb232..a88581099f 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake @@ -8,4 +8,14 @@ set(FILES Source/ActorModule.cpp + Source/Editor/EditorSystemComponent.h + Source/Editor/EditorSystemComponent.cpp + Tools/EMStudio/AtomRenderPlugin.h + Tools/EMStudio/AtomRenderPlugin.cpp + Tools/EMStudio/AnimViewportWidget.h + Tools/EMStudio/AnimViewportWidget.cpp + Tools/EMStudio/AnimViewportRenderer.h + Tools/EMStudio/AnimViewportRenderer.cpp + Tools/EMStudio/AnimViewportSettings.h + Tools/EMStudio/AnimViewportSettings.cpp ) diff --git a/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli b/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli index 535d1380e7..4b0fa654e3 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairLightTypes.azsli @@ -135,6 +135,7 @@ void SetNormalAndUpdateLightingParams( float3 projectedNormal = cross(biNormal, tangent); surface.normal = normalize(projectedNormal); // the normalization might be redundunt + surface.vertexNormal = surface.normal; // [To Do] - support proper vertex normals in the hair shader. // Next is important in order to set NdotV and other PBR settings - needs to be set once per light UpdateLightingParameters(lightingData, surface.position, surface.normal, surface.roughnessLinear); @@ -362,7 +363,7 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData) litRatio = DirectionalLightShadow::GetVisibility( shadowIndex, lightingData.shadowCoords, - surface.normal, + surface.vertexNormal, debugInfo); } diff --git a/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli b/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli index bf452e6ba8..dcdcf43243 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairLighting.azsli @@ -197,6 +197,7 @@ float3 CalculateLighting( surface.position = vPositionWS; surface.tangent = vTangent; // Redundant - will be calculated per light surface.normal = float3(0, 0, 0); // Will fail lights that did not initialize properly. + surface.vertexNormal = float3(0,0,0); // [To Do] - vertex normals are not handled yet in the hair shader. surface.roughnessLinear = material.m_roughness; surface.cuticleTilt = material.m_cuticleTilt; surface.thickness = thickness; diff --git a/Gems/AtomTressFX/Assets/Shaders/HairSurface.azsli b/Gems/AtomTressFX/Assets/Shaders/HairSurface.azsli index 16496f0dcd..6ffd342734 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairSurface.azsli +++ b/Gems/AtomTressFX/Assets/Shaders/HairSurface.azsli @@ -18,6 +18,7 @@ class Surface // ------- BasePbrSurfaceData ------- float3 position; //!< Position in world-space float3 normal; //!< Normal in world-space + float3 vertexNormal; //!< Vertex normal in world-space float3 albedo; //!< Albedo color of the non-metallic material, will be multiplied against the diffuse lighting value float3 specularF0; //!< Fresnel f0 spectral value of the surface float roughnessLinear; //!< Perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use diff --git a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp new file mode 100644 index 0000000000..7af5903cf3 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +//#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + namespace Hair + { + + // --- Creation & Initialization --- + RPI::Ptr HairGeometryRasterPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew HairGeometryRasterPass(descriptor); + return pass; + } + + HairGeometryRasterPass::HairGeometryRasterPass(const RPI::PassDescriptor& descriptor) + : RasterPass(descriptor), + m_passDescriptor(descriptor) + { + // For inherited classes, override this method and set the proper path. + // Example: "Shaders/hairrenderingfillppll.azshader" + SetShaderPath("dummyShaderPath"); + } + + bool HairGeometryRasterPass::AcquireFeatureProcessor() + { + if (m_featureProcessor) + { + return true; + } + + RPI::Scene* scene = GetScene(); + if (scene) + { + m_featureProcessor = scene->GetFeatureProcessor(); + } + else + { + return false; + } + + if (!m_featureProcessor) + { + AZ_Warning("Hair Gem", false, + "HairGeometryRasterPass [%s] - Failed to retrieve Hair feature processor from the scene", + GetName().GetCStr()); + return false; + } + return true; + } + + void HairGeometryRasterPass::InitializeInternal() + { + if (GetScene()) + { + RasterPass::InitializeInternal(); + } + } + + bool HairGeometryRasterPass::IsEnabled() const + { + return (RPI::RasterPass::IsEnabled() && m_initialized) ? true : false; + } + + bool HairGeometryRasterPass::LoadShaderAndPipelineState() + { + RPI::ShaderReloadNotificationBus::Handler::BusDisconnect(); + + const RPI::RasterPassData* passData = RPI::PassUtils::GetPassData(m_passDescriptor); + + // If we successfully retrieved our custom data, use it to set the DrawListTag + if (!passData) + { + AZ_Error("Hair Gem", false, "Missing pass raster data"); + return false; + } + + // Load Shader + const char* shaderFilePath = m_shaderPath.c_str(); + Data::Asset shaderAsset = + RPI::AssetUtils::LoadAssetByProductPath(shaderFilePath, RPI::AssetUtils::TraceLevel::Error); + + if (!shaderAsset.GetId().IsValid()) + { + AZ_Error("Hair Gem", false, "Invalid shader asset for shader '%s'!", shaderFilePath); + return false; + } + + m_shader = RPI::Shader::FindOrCreate(shaderAsset); + if (m_shader == nullptr) + { + AZ_Error("Hair Gem", false, "Pass failed to load shader '%s'!", shaderFilePath); + return false; + } + + // Per Pass Srg + { + // Using 'PerPass' naming since currently RasterPass assumes that the pass Srg is always named 'PassSrg' + // [To Do] - RasterPass should use srg slot index and not name - currently this will + // result in a crash in one of the Atom existing MSAA passes that requires further dive. + // m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "HairPerPassSrg", "Hair Gem"); + m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "PassSrg", "Hair Gem"); + if (!m_shaderResourceGroup) + { + AZ_Error("Hair Gem", false, "Failed to create the per pass srg"); + return false; + } + } + + const RPI::ShaderVariant& shaderVariant = m_shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; + shaderVariant.ConfigurePipelineState(pipelineStateDescriptor); + + RPI::Scene* scene = GetScene(); + if (!scene) + { + AZ_Error("Hair Gem", false, "Scene could not be acquired" ); + return false; + } + RHI::DrawListTag drawListTag = m_shader->GetDrawListTag(); + scene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor); + + pipelineStateDescriptor.m_renderAttachmentConfiguration = GetRenderAttachmentConfiguration(); + pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList); + pipelineStateDescriptor.m_inputStreamLayout.Finalize(); + + m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor); + if (!m_pipelineState) + { + AZ_Error("Hair Gem", false, "Pipeline state could not be acquired"); + return false; + } + + RPI::ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId()); + + m_initialized = true; + return true; + } + + void HairGeometryRasterPass::SchedulePacketBuild(HairRenderObject* hairObject) + { + m_newRenderObjects.insert(hairObject); + } + + bool HairGeometryRasterPass::BuildDrawPacket(HairRenderObject* hairObject) + { + if (!m_initialized) + { + return false; + } + + RHI::DrawPacketBuilder::DrawRequest drawRequest; + drawRequest.m_listTag = m_drawListTag; + drawRequest.m_pipelineState = m_pipelineState; +// drawRequest.m_streamBufferViews = // no explicit vertex buffer. shader is using the srg buffers + drawRequest.m_stencilRef = 0; + drawRequest.m_sortKey = 0; + + // Seems that the PerView and PerScene are gathered through RenderPass::CollectSrgs() + // The PerPass is gathered through the RasterPass::m_shaderResourceGroup + AZStd::lock_guard lock(m_mutex); + + return hairObject->BuildPPLLDrawPacket(drawRequest); + } + + bool HairGeometryRasterPass::AddDrawPackets(AZStd::list>& hairRenderObjects) + { + bool overallSuccess = true; + + if (!m_currentView && + (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag))) + { + m_currentView = nullptr; // set it to nullptr to prevent further attempts this frame + AZ_Warning("Hair Gem", false, "AddDrawPackets: failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); + return false; + } + + for (auto& renderObject : hairRenderObjects) + { + const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket(); + if (!drawPacket) + { // might not be an error - the object might have just been added and the DrawPacket is + // scheduled to be built when the render frame begins + AZ_Warning("Hair Gem", !m_newRenderObjects.empty(), "HairGeometryRasterPass - DrawPacket wasn't built"); + overallSuccess = false; + continue; + } + + m_currentView->AddDrawPacket(drawPacket); + } + return overallSuccess; + } + + void HairGeometryRasterPass::FrameBeginInternal(FramePrepareParams params) + { + { + AZStd::lock_guard lock(m_mutex); + if (!m_initialized && AcquireFeatureProcessor()) + { + LoadShaderAndPipelineState(); + m_featureProcessor->ForceRebuildRenderData(); + } + } + + if (!m_initialized) + { + return; + } + + // Bind the Per Object resources and trigger the RHI validation that will use attachment + // for its validation. The attachments are invalidated outside the render begin/end frame. + for (HairRenderObject* newObject : m_newRenderObjects) + { + newObject->BindPerObjectSrgForRaster(); + BuildDrawPacket(newObject); + } + + // Clear the new added objects - BuildDrawPacket should only be carried out once per + // object/shader lifetime + m_newRenderObjects.clear(); + + // Refresh current view every frame + if (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag)) + { + m_currentView = nullptr; // set it to null if view exists but no tag match + AZ_Warning("Hair Gem", false, "FrameBeginInternal: failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); + return; + } + + RPI::RasterPass::FrameBeginInternal(params); + } + + void HairGeometryRasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + AZ_PROFILE_FUNCTION(AzRender); + + if (!m_featureProcessor) + { + return; + } + + // Compilation of remaining srgs will be done by the parent class + RPI::RasterPass::CompileResources(context); + } + + void HairGeometryRasterPass::BuildShaderAndRenderData() + { + AZStd::lock_guard lock(m_mutex); + m_initialized = false; // make sure we initialize it even if not in this frame + if (AcquireFeatureProcessor()) + { + LoadShaderAndPipelineState(); + m_featureProcessor->ForceRebuildRenderData(); + } + } + + void HairGeometryRasterPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader & shader) + { + BuildShaderAndRenderData(); + } + + void HairGeometryRasterPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset& shaderAsset) + { + BuildShaderAndRenderData(); + } + + void HairGeometryRasterPass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant) + { + BuildShaderAndRenderData(); + } + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h new file mode 100644 index 0000000000..a226d2c294 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace AZ +{ + namespace RHI + { + struct DrawItem; + } + + namespace Render + { + namespace Hair + { + class HairRenderObject; + class HairFeatureProcessor; + + //! A HairGeometryRasterPass is used for the render of the hair geometries. This is the base + //! class that can be inherited - for example by the HiarPPLLRasterPass and have only the specific + //! class data handling added on top. + class HairGeometryRasterPass + : public RPI::RasterPass + , private RPI::ShaderReloadNotificationBus::Handler + { + AZ_RPI_PASS(HairGeometryRasterPass); + + public: + AZ_RTTI(HairGeometryRasterPass, "{0F07360A-A286-4060-8C62-137AFFA50561}", RasterPass); + AZ_CLASS_ALLOCATOR(HairGeometryRasterPass, SystemAllocator, 0); + + //! Creates a HairGeometryRasterPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + bool AddDrawPackets(AZStd::list>& hairObjects); + + //! The following will be called when an object was added or shader has been compiled + void SchedulePacketBuild(HairRenderObject* hairObject); + + Data::Instance GetShader() { return m_shader; } + + void SetFeatureProcessor(HairFeatureProcessor* featureProcessor) + { + m_featureProcessor = featureProcessor; + } + + virtual bool IsEnabled() const override; + + protected: + explicit HairGeometryRasterPass(const RPI::PassDescriptor& descriptor); + + // ShaderReloadNotificationBus::Handler overrides... + void OnShaderReinitialized(const RPI::Shader& shader) override; + void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; + void OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant) override; + + void SetShaderPath(const char* shaderPath) { m_shaderPath = shaderPath; } + bool LoadShaderAndPipelineState(); + bool AcquireFeatureProcessor(); + void BuildShaderAndRenderData(); + bool BuildDrawPacket(HairRenderObject* hairObject); + + // Pass behavior overrides + void InitializeInternal() override; +// void BuildInternal() override; + void FrameBeginInternal(FramePrepareParams params) override; + + // Scope producer functions... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + + protected: + HairFeatureProcessor* m_featureProcessor = nullptr; + + // The shader that will be used by the pass + Data::Instance m_shader = nullptr; + + // Override the following in the inherited class + AZStd::string m_shaderPath = "dummyShaderPath"; + + // To help create the pipeline state + RPI::PassDescriptor m_passDescriptor; + + const RHI::PipelineState* m_pipelineState = nullptr; + RPI::ViewPtr m_currentView = nullptr; + + AZStd::mutex m_mutex; + + //! List of new render objects introduced this frame so that their in order to identify + //! that their PerObject (dynamic) Srg needs binding to the resources. + //! Done once per every new object introduced / requires update. + AZStd::unordered_set m_newRenderObjects; + + bool m_initialized = false; + }; + + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp index 746a3c2640..a677a929a2 100644 --- a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp +++ b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp @@ -34,61 +34,25 @@ namespace AZ namespace Hair { - // --- Creation & Initialization --- + HairPPLLRasterPass::HairPPLLRasterPass(const RPI::PassDescriptor& descriptor) + : HairGeometryRasterPass(descriptor) + { + SetShaderPath("Shaders/hairrenderingfillppll.azshader"); + } + RPI::Ptr HairPPLLRasterPass::Create(const RPI::PassDescriptor& descriptor) { RPI::Ptr pass = aznew HairPPLLRasterPass(descriptor); return pass; } - HairPPLLRasterPass::HairPPLLRasterPass(const RPI::PassDescriptor& descriptor) - : RasterPass(descriptor), - m_passDescriptor(descriptor) - { - } - - HairPPLLRasterPass::~HairPPLLRasterPass() - { - } - - bool HairPPLLRasterPass::AcquireFeatureProcessor() - { - if (m_featureProcessor) - { - return true; - } - - RPI::Scene* scene = GetScene(); - if (scene) - { - m_featureProcessor = scene->GetFeatureProcessor(); - } - else - { - return false; - } - - if (!m_featureProcessor) - { - AZ_Warning("Hair Gem", false, - "HairPPLLRasterPass [%s] - Failed to retrieve Hair feature processor from the scene", - GetName().GetCStr()); - return false; - } - return true; - } - - void HairPPLLRasterPass::InitializeInternal() - { - if (GetScene()) - { - RasterPass::InitializeInternal(); - } - } - + //! This method is used for attaching the PPLL data buffer which is a transient buffer. + //! It is done this ways because Atom doesn't support transient structured buffers declaration + //! via Pass yet. + //! Once supported, this will be done via data driven code and the method can be removed. void HairPPLLRasterPass::BuildInternal() { - RasterPass::BuildInternal(); + RasterPass::BuildInternal(); // change this to call parent if the method exists if (!AcquireFeatureProcessor()) { @@ -104,217 +68,6 @@ namespace AZ AttachBufferToSlot(Name{ "PerPixelLinkedList" }, m_featureProcessor->GetPerPixelListBuffer()); } - bool HairPPLLRasterPass::IsEnabled() const - { - return (RPI::RasterPass::IsEnabled() && m_initialized) ? true : false; - } - - bool HairPPLLRasterPass::LoadShaderAndPipelineState() - { - RPI::ShaderReloadNotificationBus::Handler::BusDisconnect(); - - const RPI::RasterPassData* passData = RPI::PassUtils::GetPassData(m_passDescriptor); - - // If we successfully retrieved our custom data, use it to set the DrawListTag - if (!passData) - { - AZ_Error("Hair Gem", false, "Missing pass raster data"); - return false; - } - - // Load Shader - const char* shaderFilePath = "Shaders/hairrenderingfillppll.azshader"; - Data::Asset shaderAsset = - RPI::AssetUtils::LoadAssetByProductPath(shaderFilePath, RPI::AssetUtils::TraceLevel::Error); - - if (!shaderAsset.GetId().IsValid()) - { - AZ_Error("Hair Gem", false, "Invalid shader asset for shader '%s'!", shaderFilePath); - return false; - } - - m_shader = RPI::Shader::FindOrCreate(shaderAsset); - if (m_shader == nullptr) - { - AZ_Error("Hair Gem", false, "Pass failed to load shader '%s'!", shaderFilePath); - return false; - } - - // Per Pass Srg - { - // Using 'PerPass' naming since currently RasterPass assumes that the pass Srg is always named 'PassSrg' - // [To Do] - RasterPass should use srg slot index and not name - currently this will - // result in a crash in one of the Atom existing MSAA passes that requires further dive. - // m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "HairPerPassSrg", "Hair Gem"); - m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "PassSrg", "Hair Gem"); - if (!m_shaderResourceGroup) - { - AZ_Error("Hair Gem", false, "Failed to create the per pass srg"); - return false; - } - } - - const RPI::ShaderVariant& shaderVariant = m_shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); - RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; - shaderVariant.ConfigurePipelineState(pipelineStateDescriptor); - - RPI::Scene* scene = GetScene(); - if (!scene) - { - AZ_Error("Hair Gem", false, "Scene could not be acquired" ); - return false; - } - RHI::DrawListTag drawListTag = m_shader->GetDrawListTag(); - scene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor); - - pipelineStateDescriptor.m_renderAttachmentConfiguration = GetRenderAttachmentConfiguration(); - pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList); - pipelineStateDescriptor.m_inputStreamLayout.Finalize(); - - m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor); - if (!m_pipelineState) - { - AZ_Error("Hair Gem", false, "Pipeline state could not be acquired"); - return false; - } - - RPI::ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId()); - - m_initialized = true; - return true; - } - - void HairPPLLRasterPass::SchedulePacketBuild(HairRenderObject* hairObject) - { - m_newRenderObjects.insert(hairObject); - } - - bool HairPPLLRasterPass::BuildDrawPacket(HairRenderObject* hairObject) - { - if (!m_initialized) - { - return false; - } - - RHI::DrawPacketBuilder::DrawRequest drawRequest; - drawRequest.m_listTag = m_drawListTag; - drawRequest.m_pipelineState = m_pipelineState; -// drawRequest.m_streamBufferViews = // no explicit vertex buffer. shader is using the srg buffers - drawRequest.m_stencilRef = 0; - drawRequest.m_sortKey = 0; - - // Seems that the PerView and PerScene are gathered through RenderPass::CollectSrgs() - // The PerPass is gathered through the RasterPass::m_shaderResourceGroup - AZStd::lock_guard lock(m_mutex); - - return hairObject->BuildPPLLDrawPacket(drawRequest); - } - - bool HairPPLLRasterPass::AddDrawPackets(AZStd::list>& hairRenderObjects) - { - bool overallSuccess = true; - - if (!m_currentView && - (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag))) - { - m_currentView = nullptr; // set it to nullptr to prevent further attempts this frame - AZ_Warning("Hair Gem", false, "HairPPLLRasterPass failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); - return false; - } - - for (auto& renderObject : hairRenderObjects) - { - const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket(); - if (!drawPacket) - { // might not be an error - the object might have just been added and the DrawPacket is - // scheduled to be built when the render frame begins - AZ_Warning("Hair Gem", !m_newRenderObjects.empty(), "HairPPLLRasterPass - DrawPacket wasn't built"); - overallSuccess = false; - continue; - } - - m_currentView->AddDrawPacket(drawPacket); - } - return overallSuccess; - } - - void HairPPLLRasterPass::FrameBeginInternal(FramePrepareParams params) - { - { - AZStd::lock_guard lock(m_mutex); - if (!m_initialized && AcquireFeatureProcessor()) - { - LoadShaderAndPipelineState(); - m_featureProcessor->ForceRebuildRenderData(); - } - } - - if (!m_initialized) - { - return; - } - - // Bind the Per Object resources and trigger the RHI validation that will use attachment - // for its validation. The attachments are invalidated outside the render begin/end frame. - for (HairRenderObject* newObject : m_newRenderObjects) - { - newObject->BindPerObjectSrgForRaster(); - BuildDrawPacket(newObject); - } - - // Clear the new added objects - BuildDrawPacket should only be carried out once per - // object/shader lifetime - m_newRenderObjects.clear(); - - // Refresh current view every frame - if (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag)) - { - m_currentView = nullptr; // set it to null if view exists but no tag match - AZ_Warning("Hair Gem", false, "HairPPLLRasterPass failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); - return; - } - - RPI::RasterPass::FrameBeginInternal(params); - } - - void HairPPLLRasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) - { - AZ_PROFILE_FUNCTION(AzRender); - - if (!m_featureProcessor) - { - return; - } - - // Compilation of remaining srgs will be done by the parent class - RPI::RasterPass::CompileResources(context); - } - - void HairPPLLRasterPass::BuildShaderAndRenderData() - { - AZStd::lock_guard lock(m_mutex); - m_initialized = false; // make sure we initialize it even if not in this frame - if (AcquireFeatureProcessor()) - { - LoadShaderAndPipelineState(); - m_featureProcessor->ForceRebuildRenderData(); - } - } - - void HairPPLLRasterPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader & shader) - { - BuildShaderAndRenderData(); - } - - void HairPPLLRasterPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset& shaderAsset) - { - BuildShaderAndRenderData(); - } - - void HairPPLLRasterPass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant) - { - BuildShaderAndRenderData(); - } } // namespace Hair } // namespace Render } // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h index ef7167897f..bf90fe0521 100644 --- a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h +++ b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h @@ -7,14 +7,7 @@ */ #pragma once -#include - -#include - -#include -#include -#include -#include +#include namespace AZ { @@ -27,11 +20,8 @@ namespace AZ { namespace Hair { - class HairRenderObject; - class HairFeatureProcessor; - //! A HairPPLLRasterPass is used for the hair fragments fill render after the data - //! went through the skinning and simulation passes. + //! went through the skinning and simulation passes. //! The output of this pass is the general list of fragment data that can now be //! traversed for depth resolve and lighting. //! The Fill pass uses the following Srgs: @@ -41,74 +31,22 @@ namespace AZ //! - HairDynamicDataSrg (PerObjectSrg) - shared buffers views for this hair object only. //! - PerViewSrg and PerSceneSrg - as per the data from Atom. class HairPPLLRasterPass - : public RPI::RasterPass - , private RPI::ShaderReloadNotificationBus::Handler + : public HairGeometryRasterPass { AZ_RPI_PASS(HairPPLLRasterPass); public: - AZ_RTTI(HairPPLLRasterPass, "{6614D7DD-24EE-4A2B-B314-7C035E2FB3C4}", RasterPass); + AZ_RTTI(HairPPLLRasterPass, "{6614D7DD-24EE-4A2B-B314-7C035E2FB3C4}", HairGeometryRasterPass); AZ_CLASS_ALLOCATOR(HairPPLLRasterPass, SystemAllocator, 0); - virtual ~HairPPLLRasterPass(); //! Creates a HairPPLLRasterPass static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); - bool AddDrawPackets(AZStd::list>& hairObjects); - - //! The following will be called when an object was added or shader has been compiled - void SchedulePacketBuild(HairRenderObject* hairObject); - - Data::Instance GetShader() { return m_shader; } - - void SetFeatureProcessor(HairFeatureProcessor* featureProcessor) - { - m_featureProcessor = featureProcessor; - } - - virtual bool IsEnabled() const override; protected: - // ShaderReloadNotificationBus::Handler overrides... - void OnShaderReinitialized(const RPI::Shader& shader) override; - void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; - void OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant) override; - - private: explicit HairPPLLRasterPass(const RPI::PassDescriptor& descriptor); - bool LoadShaderAndPipelineState(); - bool AcquireFeatureProcessor(); - void BuildShaderAndRenderData(); - bool BuildDrawPacket(HairRenderObject* hairObject); - // Pass behavior overrides - void InitializeInternal() override; void BuildInternal() override; - void FrameBeginInternal(FramePrepareParams params) override; - - // Scope producer functions... - void CompileResources(const RHI::FrameGraphCompileContext& context) override; - - private: - HairFeatureProcessor* m_featureProcessor = nullptr; - - // The shader that will be used by the pass - Data::Instance m_shader = nullptr; - - // To help create the pipeline state - RPI::PassDescriptor m_passDescriptor; - - const RHI::PipelineState* m_pipelineState = nullptr; - RPI::ViewPtr m_currentView = nullptr; - - AZStd::mutex m_mutex; - - //! List of new render objects introduced this frame so that their - //! Per Object (dynamic) Srg should be bound to the resources. - //! Done once per every new object introduced / requires update. - AZStd::unordered_set m_newRenderObjects; - - bool m_initialized = false; }; } // namespace Hair diff --git a/Gems/AtomTressFX/Hair_files.cmake b/Gems/AtomTressFX/Hair_files.cmake index c726d931f7..000abeb559 100644 --- a/Gems/AtomTressFX/Hair_files.cmake +++ b/Gems/AtomTressFX/Hair_files.cmake @@ -61,10 +61,16 @@ set(FILES #) # #set(atom_hair_passes + # The simulation pass class shared by all simulation / skinning compute passes Code/Passes/HairSkinningComputePass.h Code/Passes/HairSkinningComputePass.cpp + # Base class of all geometry raster passes + Code/Passes/HairGeometryRasterPass.h + Code/Passes/HairGeometryRasterPass.cpp + # PPLL rendering technique - geometry raster pass Code/Passes/HairPPLLRasterPass.h Code/Passes/HairPPLLRasterPass.cpp + # PP full screen resolve pass Code/Passes/HairPPLLResolvePass.h Code/Passes/HairPPLLResolvePass.cpp #) diff --git a/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp b/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp index b1e7e3ac18..1af927a7e7 100644 --- a/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp +++ b/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp @@ -90,6 +90,9 @@ namespace AudioSystemGem AudioSystemGemSystemComponent::~AudioSystemGemSystemComponent() { + // The audio system uses the Audio::AudioSystemAllocator + // so it needs to be deleted before the allocator is shutdown + m_audioSystem.reset(); Audio::Platform::ShutdownAudioAllocators(); } diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.cpp b/Gems/Camera/Code/Source/EditorCameraComponent.cpp index 3ac60cbff8..cf32fdedff 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.cpp +++ b/Gems/Camera/Code/Source/EditorCameraComponent.cpp @@ -102,7 +102,7 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/Camera.svg") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/camera/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/camera/camera/") ->UIElement(AZ::Edit::UIHandlers::Button,"", "Sets the view to this camera") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCameraComponent::OnPossessCameraButtonClicked) ->Attribute(AZ::Edit::Attributes::ButtonText, &EditorCameraComponent::GetCameraViewButtonText) diff --git a/Gems/CameraFramework/Code/Source/CameraRigComponent.cpp b/Gems/CameraFramework/Code/Source/CameraRigComponent.cpp index e8df095ed0..12c1e5861b 100644 --- a/Gems/CameraFramework/Code/Source/CameraRigComponent.cpp +++ b/Gems/CameraFramework/Code/Source/CameraRigComponent.cpp @@ -126,7 +126,7 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/CameraRig.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/CameraRig.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/camera-rig/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/camera/camera-rig/") ->DataElement(0, &CameraRigComponent::m_targetAcquirers, "Target acquirers", "A list of behaviors that define how a camera will select a target. They are executed in order until one succeeds") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/EMotionFX/Code/CMakeLists.txt b/Gems/EMotionFX/Code/CMakeLists.txt index dace02b2ae..67d0ba6d85 100644 --- a/Gems/EMotionFX/Code/CMakeLists.txt +++ b/Gems/EMotionFX/Code/CMakeLists.txt @@ -109,6 +109,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzToolsFramework Legacy::Editor.Headers 3rdParty::OpenGLInterface + Gem::AtomToolsFramework.Static + Gem::AtomToolsFramework.Editor COMPILE_DEFINITIONS PUBLIC EMFX_EMSTUDIOLYEMBEDDED diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp index 85bb726822..ef573ab40c 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp @@ -22,6 +22,7 @@ #include "CommandManager.h" #include #include +#include namespace CommandSystem @@ -729,7 +730,8 @@ namespace CommandSystem m_oldWorkspaceDirtyFlag = GetCommandManager()->GetWorkspaceDirtyFlag(); // get rid of the actor - EMotionFX::GetActorManager().UnregisterActor(EMotionFX::GetActorManager().FindSharedActorByID(actor->GetID())); + const AZ::Data::AssetId actorAssetId = EMotionFX::GetActorManager().FindAssetIdByActorId(actor->GetID()); + EMotionFX::GetActorManager().UnregisterActor(actorAssetId); // mark the workspace as dirty GetCommandManager()->SetWorkspaceDirtyFlag(true); @@ -818,12 +820,16 @@ namespace CommandSystem { continue; } - // ignore visualization actor instances if (actorInstance->GetIsUsedForVisualization()) { continue; } + // Ignore actor instances owned by entity + if (actorInstance->GetEntity()) + { + continue; + } // generate command to remove the actor instance const AZStd::string command = AZStd::string::format("RemoveActorInstance -actorInstanceID %i", actorInstance->GetID()); @@ -849,12 +855,6 @@ namespace CommandSystem // get the current actor EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i); - // ignore runtime-owned actors - if (actor->GetIsOwnedByRuntime()) - { - continue; - } - // ignore visualization actors if (actor->GetIsUsedForVisualization()) { diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp index 168de93e38..ea3f816e86 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp @@ -17,6 +17,7 @@ #include #include "CommandManager.h" #include +#include namespace CommandSystem @@ -65,35 +66,29 @@ namespace CommandSystem filename = EMotionFX::EMotionFXManager::ResolvePath(filename.c_str()); } - // check if we have already loaded the actor - EMotionFX::Actor* actorFromManager = EMotionFX::GetActorManager().FindActorByFileName(filename.c_str()); - if (actorFromManager) + AZ::Data::AssetId actorAssetId; + EBUS_EVENT_RESULT( + actorAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, filename.c_str(), AZ::Data::s_invalidAssetType, false); + if (!actorAssetId.IsValid()) { - AZStd::to_string(outResult, actorFromManager->GetID()); - return true; - } - - // init the settings - EMotionFX::Importer::ActorSettings settings; - - // extract default values from the command syntax automatically, if they aren't specified explicitly - settings.m_loadLimits = parameters.GetValueAsBool("loadLimits", this); - settings.m_loadMorphTargets = parameters.GetValueAsBool("loadMorphTargets", this); - settings.m_loadSkeletalLoDs = parameters.GetValueAsBool("loadSkeletalLODs", this); - settings.m_dualQuatSkinning = parameters.GetValueAsBool("dualQuatSkinning", this); - - // try to load the actor - AZStd::shared_ptr actor {EMotionFX::GetImporter().LoadActor(filename.c_str(), &settings)}; - if (!actor) - { - outResult = AZStd::string::format("Failed to load actor from '%s'. File may not exist at this path or may have incorrect permissions", filename.c_str()); + outResult = AZStd::string::format("Cannot import actor. Cannot find asset at path %s.", filename.c_str()); return false; } - // Because the actor is directly loaded from disk (without going through an actor asset), we need to ask for a blocking - // load for the asset that actor is depend on. - actor->Finalize(EMotionFX::Actor::LoadRequirement::RequireBlockingLoad); + // check if we have already loaded the actor + const size_t actorIndex = EMotionFX::GetActorManager().FindActorIndex(actorAssetId); + if (actorIndex != InvalidIndex) + { + return true; + } + // Do a blocking load of the asset. + AZ::Data::Asset actorAsset = + AZ::Data::AssetManager::Instance().GetAsset( + actorAssetId, AZ::Data::AssetLoadBehavior::Default); + actorAsset.BlockUntilLoadComplete(); + + EMotionFX::Actor* actor = actorAsset->GetActor(); // set the actor id in case we have specified it as parameter if (actorID != MCORE_INVALIDINDEX32) { @@ -113,7 +108,6 @@ namespace CommandSystem GetCommandManager()->ExecuteCommandInsideCommand(AZStd::string::format("Select -actorID %i", actor->GetID()).c_str(), outResult); } - // mark the workspace as dirty m_oldWorkspaceDirtyFlag = GetCommandManager()->GetWorkspaceDirtyFlag(); GetCommandManager()->SetWorkspaceDirtyFlag(true); @@ -121,7 +115,8 @@ namespace CommandSystem // return the id of the newly created actor AZStd::to_string(outResult, actor->GetID()); - EMotionFX::GetActorManager().RegisterActor(AZStd::move(actor)); + // Register actor asset. + EMotionFX::GetActorManager().RegisterActor(AZStd::move(actorAsset)); return true; } @@ -145,14 +140,14 @@ namespace CommandSystem } // find the actor based on the given id - AZStd::shared_ptr actor = EMotionFX::GetActorManager().FindSharedActorByID(actorID); - if (actor == nullptr) + AZ::Data::AssetId actorAssetId = EMotionFX::GetActorManager().FindAssetIdByActorId(actorID); + if (!actorAssetId.IsValid()) { outResult = AZStd::string::format("Cannot remove actor. Actor ID %i is not valid.", actorID); return false; } - EMotionFX::GetActorManager().UnregisterActor(actor); + EMotionFX::GetActorManager().UnregisterActor(actorAssetId); // update our render actors AZStd::string updateRenderActorsResult; diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionCommands.cpp index bcc9769c44..7e1a9fe0f5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/SelectionCommands.cpp @@ -183,11 +183,6 @@ namespace CommandSystem for (size_t i = 0; i < numActors; ++i) { EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i); - - if (actor->GetIsOwnedByRuntime()) - { - continue; - } if (unselect == false) { @@ -211,11 +206,6 @@ namespace CommandSystem return false; } - if (actor->GetIsOwnedByRuntime()) - { - return false; - } - if (unselect == false) { selection.AddActor(actor); @@ -244,11 +234,6 @@ namespace CommandSystem { EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i); - if (actor->GetIsOwnedByRuntime()) - { - continue; - } - if (AzFramework::StringFunc::Equal(valueString.c_str(), actor->GetName(), false /* no case */)) { if (unselect == false) diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp index 4cba50138a..3d5e4fa413 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h index e94137b317..d2db7d91c5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h @@ -8,7 +8,6 @@ #pragma once #include -#include #include #include #include @@ -44,7 +43,7 @@ namespace EMotionFX static AZStd::optional GetFirstProductByType( const ActorGroupExportContext& context, AZ::Data::AssetType type); - AutoRegisteredActor m_actor; + AZStd::shared_ptr m_actor; AZStd::vector m_actorMaterialReferences; }; } // namespace Pipeline diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp index 3e592113b1..861271fdb3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp @@ -91,9 +91,6 @@ namespace EMotionFX m_simulatedObjectSetup = AZStd::make_shared(this); m_optimizeSkeleton = false; -#if defined(EMFX_DEVELOPMENT_BUILD) - m_isOwnedByRuntime = false; -#endif // EMFX_DEVELOPMENT_BUILD // make sure we have at least allocated the first LOD of materials and facial setups m_materials.reserve(4); // reserve space for 4 lods @@ -2074,25 +2071,6 @@ namespace EMotionFX return m_usedForVisualization; } - void Actor::SetIsOwnedByRuntime(bool isOwnedByRuntime) - { -#if defined(EMFX_DEVELOPMENT_BUILD) - m_isOwnedByRuntime = isOwnedByRuntime; -#else - AZ_UNUSED(isOwnedByRuntime); -#endif - } - - - bool Actor::GetIsOwnedByRuntime() const - { -#if defined(EMFX_DEVELOPMENT_BUILD) - return m_isOwnedByRuntime; -#else - return true; -#endif - } - const AZ::Aabb& Actor::GetStaticAabb() const { return m_staticAabb; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h index aa5c5df45c..dc83972d40 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h @@ -720,12 +720,6 @@ namespace EMotionFX void SetIsUsedForVisualization(bool flag); bool GetIsUsedForVisualization() const; - /** - * Marks the actor as used by the engine runtime, as opposed to the tool suite. - */ - void SetIsOwnedByRuntime(bool isOwnedByRuntime); - bool GetIsOwnedByRuntime() const; - /** * Recursively find the parent bone that is enabled in a given LOD, starting from a given node. * For example if you have a finger bone, while the finger bones are disabled in the skeletal LOD, this function will return the index to the hand bone. @@ -940,8 +934,5 @@ namespace EMotionFX bool m_usedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */ bool m_optimizeSkeleton; /**< Indicates if we should perform/ */ bool m_isReady = false; /**< If actor as well as its dependent files are fully loaded and initialized.*/ -#if defined(EMFX_DEVELOPMENT_BUILD) - bool m_isOwnedByRuntime; /**< Set if the actor is used/owned by the engine runtime. */ -#endif // EMFX_DEVELOPMENT_BUILD }; } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp index 681efb6039..184aac6e2c 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp @@ -31,7 +31,6 @@ namespace EMotionFX SetScheduler(MultiThreadScheduler::Create()); // reserve memory - m_actors.reserve(512); m_actorInstances.reserve(1024); m_rootActorInstances.reserve(1024); } @@ -111,20 +110,23 @@ namespace EMotionFX // register the actor - void ActorManager::RegisterActor(AZStd::shared_ptr actor) + void ActorManager::RegisterActor(ActorAssetData actorAsset) { LockActors(); // check if we already registered - if (FindActorIndex(actor.get()) != InvalidIndex) + if (FindActorIndex(actorAsset.GetId()) != InvalidIndex) { - MCore::LogWarning("EMotionFX::ActorManager::RegisterActor() - The actor at location 0x%x has already been registered as actor, most likely already by the LoadActor of the importer.", actor.get()); + MCore::LogWarning( + "EMotionFX::ActorManager::RegisterActor() - The actor %s has already been registered as actor, most likely " + "already by the LoadActor of the importer.", + actorAsset->GetActor()->GetName()); UnlockActors(); return; } // register it - m_actors.emplace_back(AZStd::move(actor)); + m_actorAssets.emplace_back(AZStd::move(actorAsset)); UnlockActors(); } @@ -146,60 +148,82 @@ namespace EMotionFX Actor* ActorManager::FindActorByName(const char* actorName) const { // get the number of actors and iterate through them - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [actorName](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [actorName](const ActorAssetData& a) { - return a->GetNameString() == actorName; + return a->GetActor()->GetNameString() == actorName; }); - return (found != m_actors.end()) ? found->get() : nullptr; + return (found != m_actorAssets.end()) ? (*found)->GetActor() : nullptr; } // find the actor for a given filename Actor* ActorManager::FindActorByFileName(const char* fileName) const { - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [fileName](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [fileName](const ActorAssetData& a) { - return AzFramework::StringFunc::Equal(a->GetFileNameString().c_str(), fileName, false /* no case */); + return AzFramework::StringFunc::Equal( + a->GetActor()->GetFileNameString().c_str(), fileName, false /* no case */); }); - return (found != m_actors.end()) ? found->get() : nullptr; + return (found != m_actorAssets.end()) ? (*found)->GetActor() : nullptr; } // find the leader actor record for a given actor - size_t ActorManager::FindActorIndex(Actor* actor) const + size_t ActorManager::FindActorIndex(AZ::Data::AssetId assetId) const { - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [actor](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [assetId](const ActorAssetData& a) { - return a.get() == actor; + return a.GetId() == assetId; }); - return (found != m_actors.end()) ? AZStd::distance(m_actors.begin(), found) : InvalidIndex; + return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex; } + size_t ActorManager::FindActorIndex(const Actor* actor) const + { + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [actor](const ActorAssetData& a) + { + return a->GetActor() == actor; + }); + + return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex; + } // find the actor for a given actor name size_t ActorManager::FindActorIndexByName(const char* actorName) const { - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [actorName](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [actorName](const ActorAssetData& a) { - return a->GetNameString() == actorName; + return a->GetActor()->GetNameString() == actorName; }); - return (found != m_actors.end()) ? AZStd::distance(m_actors.begin(), found) : InvalidIndex; + return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex; } // find the actor for a given actor filename size_t ActorManager::FindActorIndexByFileName(const char* filename) const { - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [filename](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [filename](const ActorAssetData& a) { - return a->GetFileNameString() == filename; + return a->GetActor()->GetFileNameString() == filename; }); - return (found != m_actors.end()) ? AZStd::distance(m_actors.begin(), found) : InvalidIndex; + return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex; } @@ -237,22 +261,26 @@ namespace EMotionFX // find the actor by the identification number Actor* ActorManager::FindActorByID(uint32 id) const { - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [id](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [id](const ActorAssetData& a) { - return a->GetID() == id; + return a->GetActor()->GetID() == id; }); - return (found != m_actors.end()) ? found->get() : nullptr; + return (found != m_actorAssets.end()) ? (*found)->GetActor() : nullptr; } - AZStd::shared_ptr ActorManager::FindSharedActorByID(uint32 id) const + AZ::Data::AssetId ActorManager::FindAssetIdByActorId(uint32 id) const { - const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [id](const AZStd::shared_ptr& a) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [id](const ActorAssetData& a) { - return a->GetID() == id; + return a->GetActor()->GetID() == id; }); - return (found != m_actors.end()) ? *found : nullptr; + return (found != m_actorAssets.end()) ? found->GetId() : AZ::Data::AssetId(); } @@ -264,14 +292,20 @@ namespace EMotionFX // unregister an actor - void ActorManager::UnregisterActor(const AZStd::shared_ptr& actor) + void ActorManager::UnregisterActor(AZ::Data::AssetId actorAssetID) { LockActors(); - auto result = AZStd::find(m_actors.begin(), m_actors.end(), actor); - if (result != m_actors.end()) + const auto found = AZStd::find_if( + m_actorAssets.begin(), m_actorAssets.end(), + [actorAssetID](const ActorAssetData& a) + { + return a.GetId() == actorAssetID; + }); + if (found != m_actorAssets.end()) { - m_actors.erase(result); + m_actorAssets.erase(found); } + UnlockActors(); } @@ -297,7 +331,7 @@ namespace EMotionFX LockActors(); // clear all actors - m_actors.clear(); + m_actorAssets.clear(); // TODO: what if there are still references to the actors inside the list of registered actor instances? UnlockActors(); @@ -390,7 +424,13 @@ namespace EMotionFX Actor* ActorManager::GetActor(size_t nr) const { - return m_actors[nr].get(); + return m_actorAssets[nr]->GetActor(); + } + + + ActorAssetData ActorManager::GetActorAsset(size_t nr) const + { + return m_actorAssets[nr]; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h index ff78250141..003153b36c 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h @@ -16,7 +16,8 @@ #include #include #include - +#include +#include namespace EMotionFX { @@ -47,7 +48,7 @@ namespace EMotionFX * Register an actor. * @param actor The actor to register. */ - void RegisterActor(AZStd::shared_ptr actor); + void RegisterActor(ActorAssetData actorAsset); /** * Unregister all actors. @@ -60,14 +61,14 @@ namespace EMotionFX * Unregister a specific actor. * @param actor The actor you passed to the RegisterActor function sometime before. */ - void UnregisterActor(const AZStd::shared_ptr& actor); + void UnregisterActor(AZ::Data::AssetId actorAssetID); /** * Get the number of registered actors. * This does not include the clones that have been optionally created. * @result The number of registered actors. */ - MCORE_INLINE size_t GetNumActors() const { return m_actors.size(); } + MCORE_INLINE size_t GetNumActors() const { return m_actorAssets.size(); } /** * Get a given actor. @@ -78,6 +79,7 @@ namespace EMotionFX * @result A reference to the actor object that contains the array of Actor objects. */ Actor* GetActor(size_t nr) const; + ActorAssetData GetActorAsset(size_t nr) const; /** * Find the given actor by name. @@ -99,7 +101,8 @@ namespace EMotionFX * @param actor The actor object you once passed to RegisterActor. * @result Returns the actor number, which is in range of [0..GetNumActors()-1], or returns MCORE_INVALIDINDEX32 when not found. */ - size_t FindActorIndex(Actor* actor) const; + size_t FindActorIndex(AZ::Data::AssetId assetId) const; + size_t FindActorIndex(const Actor* actor) const; /** * Find the actor number for a given actor name. @@ -160,7 +163,7 @@ namespace EMotionFX */ Actor* FindActorByID(uint32 id) const; - AZStd::shared_ptr FindSharedActorByID(uint32 id) const; + AZ::Data::AssetId FindAssetIdByActorId(uint32 id) const; /** * Check if the given actor instance is registered. @@ -255,9 +258,9 @@ namespace EMotionFX void UnlockActors(); private: - AZStd::vector m_actorInstances; /**< The registered actor instances. */ - AZStd::vector> m_actors; /**< The registered actors. */ - AZStd::vector m_rootActorInstances; /**< Root actor instances (roots of all attachment chains). */ + AZStd::vector m_actorInstances; /**< The registered actor instances. */ + AZStd::vector m_actorAssets; + AZStd::vector m_rootActorInstances; /**< Root actor instances (roots of all attachment chains). */ ActorUpdateScheduler* m_scheduler; /**< The update scheduler to use. */ MCore::MutexRecursive m_actorLock; /**< The multithread lock for touching the actors array. */ MCore::MutexRecursive m_actorInstanceLock; /**< The multithread lock for touching the actor instances array. */ diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AutoRegisteredActor.h b/Gems/EMotionFX/Code/EMotionFX/Source/AutoRegisteredActor.h deleted file mode 100644 index 8304976b15..0000000000 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AutoRegisteredActor.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include - -namespace EMotionFX -{ - class Actor; - - /** - * @brief An Actor pointer that unregisters itself when it goes out of - * scope - * - * This class allows for simple functionality of automatically registering - * and unregistering an actor from the manager. Its primary use case is the - * ActorAsset, that shares ownership with the manager. But it can also be - * used anywhere that needs to make an Actor that needs to be in the - * Manager for a given period of time. A good example of this is anything - * that needs Actor commands to work on an actor that is made in a given - * scope. One main place where this happens is in the Actor asset processor - * code. - */ - class AutoRegisteredActor - { - public: - AutoRegisteredActor() = default; - - template - AutoRegisteredActor(AZStd::shared_ptr actor) - : m_actor(AZStd::move(actor)) - { - Register(m_actor); - } - template - AutoRegisteredActor(AZStd::unique_ptr actor) - : m_actor(AZStd::move(actor)) - { - Register(m_actor); - } - - // This class is not copyable, because a given actor cannot be - // registered with the manager multiple times - AutoRegisteredActor(const AutoRegisteredActor&) = delete; - AutoRegisteredActor& operator=(const AutoRegisteredActor&) = delete; - - AutoRegisteredActor(AutoRegisteredActor&& other) noexcept - { - *this = AZStd::move(other); - } - - AutoRegisteredActor& operator=(AutoRegisteredActor&& other) noexcept - { - if (this != &other) - { - Unregister(m_actor); - m_actor = AZStd::move(other.m_actor); - } - return *this; - } - - ~AutoRegisteredActor() - { - Unregister(m_actor); - } - - Actor* operator->() const - { - return m_actor.operator->(); - } - - operator bool() const - { - return static_cast(m_actor); - } - - Actor* get() const - { - return m_actor.get(); - } - - private: - void Register(const AZStd::shared_ptr& actor) - { - if (actor) - { - GetActorManager().RegisterActor(actor); - } - } - - void Unregister(const AZStd::shared_ptr& actor) - { - if (actor) - { - GetActorManager().UnregisterActor(actor); - } - } - - AZStd::shared_ptr m_actor; - }; -} // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.cpp index 2321d7c71b..b385de62be 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.cpp @@ -46,15 +46,10 @@ AZ_POP_DISABLE_WARNING namespace EMStudio { - //-------------------------------------------------------------------------- - // globals - //-------------------------------------------------------------------------- - EMStudioManager* gEMStudioMgr = nullptr; - - //-------------------------------------------------------------------------- // class EMStudioManager //-------------------------------------------------------------------------- + AZ_CLASS_ALLOCATOR_IMPL(EMStudioManager, AZ::SystemAllocator, 0) // constructor EMStudioManager::EMStudioManager(QApplication* app, [[maybe_unused]] int& argc, [[maybe_unused]] char* argv[]) @@ -105,8 +100,9 @@ namespace EMStudio // log some information LogInfo(); - } + AZ::Interface::Register(this); + } // destructor EMStudioManager::~EMStudioManager() @@ -130,6 +126,8 @@ namespace EMStudio delete m_commandManager; AZ::AllocatorInstance::Destroy(); + + AZ::Interface::Unregister(this); } MainWindow* EMStudioManager::GetMainWindow() @@ -422,6 +420,12 @@ namespace EMStudio } + EMStudioManager* EMStudioManager::GetInstance() + { + return AZ::Interface().Get(); + } + + // function to add a gizmo to the manager MCommon::TransformationManipulator* EMStudioManager::AddTransformationManipulator(MCommon::TransformationManipulator* manipulator) { @@ -494,30 +498,48 @@ namespace EMStudio painter.drawPath(path); } - //-------------------------------------------------------------------------- - // class Initializer - //-------------------------------------------------------------------------- - // initialize EMotion Studio - bool Initializer::Init(QApplication* app, int& argc, char* argv[]) + // shortcuts + QApplication* GetApp() { - // do nothing if we already have initialized - if (gEMStudioMgr) - { - return true; - } - - // create the new EMStudio object - gEMStudioMgr = new EMStudioManager(app, argc, argv); - - // return success - return true; + return EMStudioManager::GetInstance()->GetApp(); + } + EMStudioManager* GetManager() + { + return EMStudioManager::GetInstance(); } - - // the shutdown function - void Initializer::Shutdown() + bool HasMainWindow() { - delete gEMStudioMgr; - gEMStudioMgr = nullptr; + return EMStudioManager::GetInstance()->HasMainWindow(); + } + + MainWindow* GetMainWindow() + { + return EMStudioManager::GetInstance()->GetMainWindow(); + } + + PluginManager* GetPluginManager() + { + return EMStudioManager::GetInstance()->GetPluginManager(); + } + + LayoutManager* GetLayoutManager() + { + return EMStudioManager::GetInstance()->GetLayoutManager(); + } + + NotificationWindowManager* GetNotificationWindowManager() + { + return EMStudioManager::GetInstance()->GetNotificationWindowManager(); + } + + MotionEventPresetManager* GetEventPresetManager() + { + return EMStudioManager::GetInstance()->GetEventPresetManger(); + } + + CommandSystem::CommandManager* GetCommandManager() + { + return EMStudioManager::GetInstance()->GetCommandManager(); } } // namespace EMStudio diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h index 909f0231f3..4c3139b77a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h @@ -53,9 +53,10 @@ namespace EMStudio class EMSTUDIO_API EMStudioManager : private EMotionFX::SkeletonOutlinerNotificationBus::Handler { - MCORE_MEMORYOBJECTCATEGORY(EMStudioManager, MCore::MCORE_DEFAULT_ALIGNMENT, MEMCATEGORY_EMSTUDIOSDK) - public: + AZ_RTTI(EMStudio::EMStudioManager, "{D45E95CF-0C7B-44F1-A9D4-99A1E12A5AB5}") + AZ_CLASS_ALLOCATOR_DECL + EMStudioManager(QApplication* app, int& argc, char* argv[]); ~EMStudioManager(); @@ -72,6 +73,9 @@ namespace EMStudio AZStd::string GetRecoverFolder() const; AZStd::string GetAutosavesFolder() const; + // Singleton pattern + static EMStudioManager* GetInstance(); + // text rendering helper function static void RenderText(QPainter& painter, const QString& text, const QColor& textColor, const QFont& font, const QFontMetrics& fontMetrics, Qt::Alignment textAlignment, const QRect& rect); @@ -150,34 +154,17 @@ namespace EMStudio void OnRemoveCommand(size_t historyIndex) override { MCORE_UNUSED(historyIndex); } void OnSetCurrentCommand(size_t index) override { MCORE_UNUSED(index); } }; - EventProcessingCallback* m_eventProcessingCallback; + EventProcessingCallback* m_eventProcessingCallback = nullptr; }; - - /** - * - * - * - */ - class EMSTUDIO_API Initializer - { - public: - static bool MCORE_CDECL Init(QApplication* app, int& argc, char* argv[]); - static void MCORE_CDECL Shutdown(); - }; - - - // the global manager - extern EMSTUDIO_API EMStudioManager* gEMStudioMgr; - - // shortcuts - MCORE_INLINE QApplication* GetApp() { return gEMStudioMgr->GetApp(); } - MCORE_INLINE EMStudioManager* GetManager() { return gEMStudioMgr; } - MCORE_INLINE bool HasMainWindow() { return gEMStudioMgr->HasMainWindow(); } - MCORE_INLINE MainWindow* GetMainWindow() { return gEMStudioMgr->GetMainWindow(); } - MCORE_INLINE PluginManager* GetPluginManager() { return gEMStudioMgr->GetPluginManager(); } - MCORE_INLINE LayoutManager* GetLayoutManager() { return gEMStudioMgr->GetLayoutManager(); } - MCORE_INLINE NotificationWindowManager* GetNotificationWindowManager() { return gEMStudioMgr->GetNotificationWindowManager(); } - MCORE_INLINE MotionEventPresetManager* GetEventPresetManager() { return gEMStudioMgr->GetEventPresetManger(); } - MCORE_INLINE CommandSystem::CommandManager* GetCommandManager() { return gEMStudioMgr->GetCommandManager(); } + // Shortcuts + QApplication* GetApp(); + EMStudioManager* GetManager(); + bool HasMainWindow(); + MainWindow* GetMainWindow(); + PluginManager* GetPluginManager(); + LayoutManager* GetLayoutManager(); + NotificationWindowManager* GetNotificationWindowManager(); + MotionEventPresetManager* GetEventPresetManager(); + CommandSystem::CommandManager* GetCommandManager(); } // namespace EMStudio diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/FileManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/FileManager.cpp index 2d65a228b7..17438dd59a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/FileManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/FileManager.cpp @@ -112,10 +112,6 @@ namespace EMStudio for (size_t i = 0; i < actorCount; ++i) { EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i); - if (actor->GetIsOwnedByRuntime()) - { - continue; - } if (AzFramework::StringFunc::Equal(filename, actor->GetFileName())) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp index 002634ea90..b75fb71060 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp @@ -1792,11 +1792,6 @@ namespace EMStudio { EMotionFX::Actor* actor = selectionList.GetActorInstance(i)->GetActor(); - if (actor->GetIsOwnedByRuntime()) - { - continue; - } - if (AZStd::find(savingActors.begin(), savingActors.end(), actor) == savingActors.end()) { savingActors.push_back(actor); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h index 585101f7d2..1aa8676352 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h @@ -10,9 +10,9 @@ #if !defined(Q_MOC_RUN) #include -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp index db0c355002..c74cb3cb0f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp @@ -389,7 +389,7 @@ namespace EMStudio // get the current actor and the number of clones EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i); - if (actor->GetIsOwnedByRuntime() || !actor->IsReady()) + if (!actor->IsReady()) { continue; } @@ -421,7 +421,7 @@ namespace EMStudio // At this point the render actor could point to an already deleted actor. // In case the actor got deleted we might get an unexpected flag as result. - if (!found || (found && actor->GetIsOwnedByRuntime()) || (!actor->IsReady())) + if (!found || (!actor->IsReady())) { DestroyEMStudioActor(actor); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp index 93a6dd3019..fe8fb61f02 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp @@ -155,12 +155,12 @@ namespace EMStudio cameraMenu->addAction("Reset Camera", [this]() { this->OnResetCamera(); }); QAction* showSelectedAction = cameraMenu->addAction("Show Selected", this, &RenderViewWidget::OnShowSelected); - showSelectedAction->setShortcut(Qt::Key_S); + showSelectedAction->setShortcut(QKeySequence(Qt::Key_S + Qt::SHIFT)); GetMainWindow()->GetShortcutManager()->RegisterKeyboardShortcut(showSelectedAction, RenderPlugin::s_renderWindowShortcutGroupName, true); addAction(showSelectedAction); QAction* showEntireSceneAction = cameraMenu->addAction("Show Entire Scene", this, &RenderViewWidget::OnShowEntireScene); - showEntireSceneAction->setShortcut(Qt::Key_A); + showEntireSceneAction->setShortcut(QKeySequence(Qt::Key_A + Qt::SHIFT)); GetMainWindow()->GetShortcutManager()->RegisterKeyboardShortcut(showEntireSceneAction, RenderPlugin::s_renderWindowShortcutGroupName, true); addAction(showEntireSceneAction); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/ResetSettingsDialog.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/ResetSettingsDialog.cpp index 2f45a89ea2..5c15f4b6be 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/ResetSettingsDialog.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/ResetSettingsDialog.cpp @@ -65,8 +65,7 @@ namespace EMStudio m_actorCheckbox = new QCheckBox("Actors"); m_actorCheckbox->setObjectName("EMFX.ResetSettingsDialog.Actors"); - const bool hasActors = HasEntityInEditor( - EMotionFX::GetActorManager(), &EMotionFX::ActorManager::GetNumActors, &EMotionFX::ActorManager::GetActor); + const bool hasActors = EMotionFX::GetActorManager().GetNumActors() > 0; m_actorCheckbox->setChecked(hasActors); m_actorCheckbox->setDisabled(!hasActors); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp index ea7c92b742..cada8c7f59 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp @@ -105,7 +105,9 @@ namespace EMStudio } } - commandString = AZStd::string::format("%s -filename \"%s\"", command, resultFileName.c_str()); + AZStd::string resultFilenameString = resultFileName.c_str(); + AzFramework::StringFunc::AssetDatabasePath::Normalize(resultFilenameString); + commandString = AZStd::string::format("%s -filename \"%s\"", command, resultFilenameString.c_str()); if (additionalParameters) { @@ -428,7 +430,13 @@ namespace EMStudio continue; } - AzFramework::StringFunc::Replace(commands[i], "@products@/", assetCacheFolder.c_str(), true /* case sensitive */); + AzFramework::StringFunc::Replace(commands[i], "@products@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "@assets@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "@root@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "@projectplatformcache@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "//", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); + AzFramework::StringFunc::Replace(commands[i], "\\\\", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); + AzFramework::StringFunc::Replace(commands[i], "/\\", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); // add the command to the command group commandGroup->AddCommandString(commands[i]); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.cpp index 6b7beb20de..4629e8dc54 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.cpp @@ -327,7 +327,16 @@ namespace EMStudio void NodeWindowPlugin::OnActorReady([[maybe_unused]] EMotionFX::Actor* actor) { - ReInit(); + m_reinitRequested = true; + } + + void NodeWindowPlugin::ProcessFrame([[maybe_unused]] float timePassedInSeconds) + { + if (m_reinitRequested) + { + ReInit(); + m_reinitRequested = false; + } } //----------------------------------------------------------------------------------------- diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h index 157486df10..59c9f78719 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h @@ -60,6 +60,8 @@ namespace EMStudio EMStudioPlugin* Clone() override; void ReInit(); + void ProcessFrame(float timePassedInSeconds) override; + public slots: void OnNodeChanged(); void VisibilityChanged(bool isVisible); @@ -87,5 +89,8 @@ namespace EMStudio AZStd::unique_ptr m_actorInfo; AZStd::unique_ptr m_nodeInfo; + + // Use this flag to defer the reinit function to main thread. + bool m_reinitRequested = false; }; } // namespace EMStudio diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/ActorsWindow.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/ActorsWindow.cpp index 5012ee2578..c1e35967ec 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/ActorsWindow.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/ActorsWindow.cpp @@ -123,12 +123,6 @@ namespace EMStudio continue; } - // ignore engine actors - if (actor->GetIsOwnedByRuntime()) - { - continue; - } - // create a tree item for the new attachment QTreeWidgetItem* newItem = new QTreeWidgetItem(m_treeWidget); diff --git a/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake b/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake index b12cbe102f..9947850471 100644 --- a/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake +++ b/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake @@ -25,7 +25,6 @@ set(FILES Source/AttachmentNode.h Source/AttachmentSkin.cpp Source/AttachmentSkin.h - Source/AutoRegisteredActor.h Source/BaseObject.cpp Source/BaseObject.h Source/CompressedKeyFrames.h diff --git a/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h b/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h index 6000873db1..4161b3c77d 100644 --- a/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h +++ b/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h @@ -16,7 +16,7 @@ #include #include #include - +#include namespace EMotionFX { @@ -96,6 +96,9 @@ namespace EMotionFX /// Returns skinning method used by the actor. virtual SkinningMethod GetSkinningMethod() const = 0; + // Use this to alter the actor asset. + virtual void SetActorAsset(AZ::Data::Asset actorAsset) = 0; + static const size_t s_invalidJointIndex = std::numeric_limits::max(); }; diff --git a/Gems/EMotionFX/Code/Include/Integration/AnimationBus.h b/Gems/EMotionFX/Code/Include/Integration/AnimationBus.h index 1b9eb55216..474ac1b6d5 100644 --- a/Gems/EMotionFX/Code/Include/Integration/AnimationBus.h +++ b/Gems/EMotionFX/Code/Include/Integration/AnimationBus.h @@ -46,6 +46,9 @@ namespace EMotionFX public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + + // Use this bus to register custom EMotionFX plugin. + virtual void OnRegisterPlugin() = 0; }; using SystemNotificationBus = AZ::EBus; diff --git a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp index b534603be6..8cee46133e 100644 --- a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp @@ -55,6 +55,7 @@ namespace EMotionFX m_spinboxX->setDecimals(4); m_spinboxX->setRange(-FLT_MAX, FLT_MAX); m_spinboxX->setProperty("motionId", motionId.c_str()); + m_spinboxX->setKeyboardTracking(false); layoutX->addWidget(m_spinboxX); layout->addLayout(layoutX, row, column); @@ -76,6 +77,7 @@ namespace EMotionFX m_spinboxY->setDecimals(4); m_spinboxY->setRange(-FLT_MAX, FLT_MAX); m_spinboxY->setProperty("motionId", motionId.c_str()); + m_spinboxX->setKeyboardTracking(false); layoutY->addWidget(m_spinboxY); layout->addLayout(layoutY, row, column); diff --git a/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp b/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp index 5082d4b739..64117fa814 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include @@ -64,8 +63,7 @@ namespace EMotionFX &actorSettings, ""); - // Set the is owned by runtime flag before finalizing the actor, as that uses the flag already. - assetData->m_emfxActor->SetIsOwnedByRuntime(true); + assetData->m_emfxActor->SetFileName(asset.GetHint().c_str()); assetData->m_emfxActor->Finalize(); // Clear out the EMFX raw asset data. diff --git a/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.h b/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.h index 8732c6a448..4b13603ed5 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.h +++ b/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.h @@ -15,7 +15,6 @@ #include #include -#include namespace EMotionFX @@ -58,7 +57,7 @@ namespace EMotionFX void InitRenderActor(); private: - AutoRegisteredActor m_emfxActor; ///< Pointer to shared EMotionFX actor + AZStd::shared_ptr m_emfxActor; AZStd::unique_ptr m_renderActor; }; @@ -81,6 +80,8 @@ namespace EMotionFX const char* GetBrowserIcon() const override; }; } // namespace Integration + + using ActorAssetData = AZ::Data::Asset; } // namespace EMotionFX namespace AZ diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index b376fa891d..15d9736f34 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -121,6 +121,7 @@ namespace EMotionFX void SetRenderCharacter(bool enable) override; bool GetRenderActorVisible() const override; SkinningMethod GetSkinningMethod() const override; + void SetActorAsset(AZ::Data::Asset actorAsset) override; ////////////////////////////////////////////////////////////////////////// // ActorComponentNotificationBus::Handler @@ -178,8 +179,6 @@ namespace EMotionFX void OnAssetReloaded(AZ::Data::Asset asset) override; bool IsPhysicsSceneSimulationFinishEventConnected() const; - - void SetActorAsset(AZ::Data::Asset actorAsset); AZ::Data::Asset GetActorAsset() const { return m_configuration.m_actorAsset; } private: diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index cf0ceab25d..5da9716d15 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -107,7 +107,7 @@ namespace EMotionFX ->Attribute(AZ::Edit::Attributes::ViewportIcon, ":/EMotionFX/Viewport/ActorComponent.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/actor/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/animation/actor/") ->DataElement(0, &EditorActorComponent::m_actorAsset, "Actor asset", "Assigned actor asset") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnAssetSelected) diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h index e8f76bdd49..1d682b47d4 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h @@ -60,6 +60,7 @@ namespace EMotionFX bool GetRenderActorVisible() const override; size_t GetNumJoints() const override; SkinningMethod GetSkinningMethod() const override; + void SetActorAsset(AZ::Data::Asset actorAsset) override; // EditorActorComponentRequestBus overrides ... const AZ::Data::AssetId& GetActorAssetId() override; @@ -79,8 +80,6 @@ namespace EMotionFX void OnAssetReady(AZ::Data::Asset asset) override; void OnAssetReloaded(AZ::Data::Asset asset) override; - void SetActorAsset(AZ::Data::Asset actorAsset); - // BoundsRequestBus overrides ... AZ::Aabb GetWorldBounds() override; AZ::Aabb GetLocalBounds() override; diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp index 9833fcc87a..3fc4f4a93d 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorAnimGraphComponent.cpp @@ -65,7 +65,7 @@ namespace EMotionFX ->Attribute(AZ::Edit::Attributes::ViewportIcon, ":/EMotionFX/Viewport/AnimGraphComponent.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/animgraph/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/animation/animgraph/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorAnimGraphComponent::m_motionSetAsset, "Motion set asset", "EMotion FX motion set asset to be loaded for this actor.") ->Attribute("EditButton", "") diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorSimpleMotionComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorSimpleMotionComponent.cpp index 8446050254..783399c956 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorSimpleMotionComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorSimpleMotionComponent.cpp @@ -46,12 +46,12 @@ namespace EMotionFX ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, azrtti_typeid()) ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Mannequin.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/animation/simple-motion/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &EditorSimpleMotionComponent::m_previewInEditor, "Preview In Editor", "Plays motion in Editor") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSimpleMotionComponent::OnEditorPropertyChanged) ->DataElement(0, &EditorSimpleMotionComponent::m_configuration, "Configuration", "Settings for this Simple Motion") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSimpleMotionComponent::OnEditorPropertyChanged) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/simple-motion/") ; } } diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 08e99f97d0..38af4232ba 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ #include #include +#include #include #include #include @@ -69,7 +71,6 @@ # include # include # include -# include # include # include // EMStudio plugins @@ -528,7 +529,7 @@ namespace EMotionFX if (EMStudio::GetManager()) { - EMStudio::Initializer::Shutdown(); + m_emstudioManager.reset(); MysticQt::Initializer::Shutdown(); } @@ -798,6 +799,8 @@ namespace EMotionFX pluginManager->RegisterPlugin(new EMotionFX::RagdollNodeInspectorPlugin()); pluginManager->RegisterPlugin(new EMotionFX::ClothJointInspectorPlugin()); pluginManager->RegisterPlugin(new EMotionFX::SimulatedObjectWidget()); + + SystemNotificationBus::Broadcast(&SystemNotificationBus::Events::OnRegisterPlugin); } ////////////////////////////////////////////////////////////////////////// @@ -813,7 +816,7 @@ namespace EMotionFX char** argv = nullptr; MysticQt::Initializer::Init("", editorAssetsPath.c_str()); - EMStudio::Initializer::Init(qApp, argc, argv); + m_emstudioManager = AZStd::make_unique(qApp, argc, argv); InitializeEMStudioPlugins(); diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h index 5e30820ca3..3ef626c947 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h @@ -24,6 +24,7 @@ # include # include # include +# include #endif // EMOTIONFXANIMATION_EDITOR namespace AZ @@ -126,6 +127,10 @@ namespace EMotionFX AZStd::vector > m_assetHandlers; AZStd::unique_ptr m_eventHandler; AZStd::unique_ptr m_renderBackendManager; + +#if defined(EMOTIONFXANIMATION_EDITOR) + AZStd::unique_ptr m_emstudioManager; +#endif // EMOTIONFXANIMATION_EDITOR }; } } diff --git a/Gems/EMotionFX/Code/Tests/ActorFixture.cpp b/Gems/EMotionFX/Code/Tests/ActorFixture.cpp index e77df103e0..33c26ca11e 100644 --- a/Gems/EMotionFX/Code/Tests/ActorFixture.cpp +++ b/Gems/EMotionFX/Code/Tests/ActorFixture.cpp @@ -10,12 +10,15 @@ #include #include #include +#include #include #include #include #include #include +#include + namespace EMotionFX { @@ -23,8 +26,9 @@ namespace EMotionFX { SystemComponentFixture::SetUp(); - m_actor = ActorFactory::CreateAndInit(); - m_actorInstance = ActorInstance::Create(m_actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId); + m_actorInstance = ActorInstance::Create(GetActor()); } void ActorFixture::TearDown() @@ -35,6 +39,7 @@ namespace EMotionFX m_actorInstance = nullptr; } + GetEMotionFX().GetActorManager()->UnregisterAllActors(); SystemComponentFixture::TearDown(); } @@ -71,7 +76,7 @@ namespace EMotionFX AZ::ObjectStream::FilterDescriptor loadFilter(nullptr, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); SimulatedObjectSetup* setup = AZ::Utils::LoadObjectFromBuffer(data.data(), data.size(), serializeContext, loadFilter); - setup->InitAfterLoad(m_actor.get()); + setup->InitAfterLoad(GetActor()); return setup; } @@ -80,4 +85,10 @@ namespace EMotionFX { return { "Bip01__pelvis", "l_upLeg", "l_loLeg", "l_ankle" }; } + + + Actor* ActorFixture::GetActor() const + { + return m_actorAsset->GetActor(); + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/ActorFixture.h b/Gems/EMotionFX/Code/Tests/ActorFixture.h index 9fee056292..569d55cf47 100644 --- a/Gems/EMotionFX/Code/Tests/ActorFixture.h +++ b/Gems/EMotionFX/Code/Tests/ActorFixture.h @@ -9,8 +9,7 @@ #pragma once #include "SystemComponentFixture.h" -#include - +#include namespace EMotionFX { @@ -31,7 +30,9 @@ namespace EMotionFX AZStd::vector GetTestJointNames() const; protected: - AutoRegisteredActor m_actor{}; + Actor* GetActor() const; + + AZ::Data::Asset m_actorAsset; ActorInstance* m_actorInstance = nullptr; }; } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/AdditiveMotionSamplingTests.cpp b/Gems/EMotionFX/Code/Tests/AdditiveMotionSamplingTests.cpp index 4171598801..114d5c08fb 100644 --- a/Gems/EMotionFX/Code/Tests/AdditiveMotionSamplingTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AdditiveMotionSamplingTests.cpp @@ -26,7 +26,7 @@ namespace EMotionFX public: void CreateSubMotionLikeBindPose(const std::string& name) { - const Skeleton* skeleton = m_actor->GetSkeleton(); + const Skeleton* skeleton = GetActor()->GetSkeleton(); size_t jointIndex = InvalidIndex; const Node* node = skeleton->FindNodeAndIndexByName(name.c_str(), jointIndex); ASSERT_NE(node, nullptr); @@ -40,7 +40,7 @@ namespace EMotionFX void CreateSubMotion(const std::string& name, const Transform& transform) { // Find and store the joint index. - const Skeleton* skeleton = m_actor->GetSkeleton(); + const Skeleton* skeleton = GetActor()->GetSkeleton(); size_t jointIndex = InvalidIndex; const Node* node = skeleton->FindNodeAndIndexByName(name.c_str(), jointIndex); ASSERT_NE(node, nullptr); @@ -55,7 +55,7 @@ namespace EMotionFX ActorFixture::SetUp(); // Get the joint that isn't in the motion data. - Node* footNode = m_actor->GetSkeleton()->FindNodeAndIndexByName("l_ball", m_footIndex); + Node* footNode = GetActor()->GetSkeleton()->FindNodeAndIndexByName("l_ball", m_footIndex); ASSERT_NE(footNode, nullptr); ASSERT_NE(m_footIndex, InvalidIndex32); @@ -98,7 +98,7 @@ namespace EMotionFX TEST_F(MotionSamplingFixture, SampleAdditiveJoint) { - const Skeleton* skeleton = m_actor->GetSkeleton(); + const Skeleton* skeleton = GetActor()->GetSkeleton(); // Sample the joints that exist in our actor skeleton as well as inside the motion data. const Pose* bindPose = m_actorInstance->GetTransformData()->GetBindPose(); @@ -106,7 +106,7 @@ namespace EMotionFX { // Sample the motion. Transform transform = Transform::CreateZero(); // Set all to Zero, not identity as this methods might return identity and we want to verify that. - m_motion->CalcNodeTransform(m_motionInstance, &transform, m_actor.get(), skeleton->GetNode(jointIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false); + m_motion->CalcNodeTransform(m_motionInstance, &transform, GetActor(), skeleton->GetNode(jointIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false); const Transform& bindTransform = bindPose->GetLocalSpaceTransform(jointIndex); EXPECT_THAT(transform, IsClose(bindTransform)); @@ -114,7 +114,7 @@ namespace EMotionFX // Sample the motion for the foot node. Transform footTransform = Transform::CreateZero(); // Set all to Zero, not identity as this methods might return identity and we want to verify that. - m_motion->CalcNodeTransform(m_motionInstance, &footTransform, m_actor.get(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false); + m_motion->CalcNodeTransform(m_motionInstance, &footTransform, GetActor(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false); // Make sure we get an identity transform back as we try to sample a node that doesn't have a submotion in an additive motion. EXPECT_THAT(footTransform, IsClose(Transform::CreateIdentity())); @@ -125,7 +125,7 @@ namespace EMotionFX // Make sure we do not get an identity transform back now that it is a non-additive motion. footTransform.Zero(); // Set all to Zero, not identity as this methods might return identity and we want to verify that. const Transform& expectedFootTransform = m_actorInstance->GetTransformData()->GetCurrentPose()->GetLocalSpaceTransform(m_footIndex); - m_motion->CalcNodeTransform(m_motionInstance, &footTransform, m_actor.get(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false); + m_motion->CalcNodeTransform(m_motionInstance, &footTransform, GetActor(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false); EXPECT_THAT(footTransform, IsClose(expectedFootTransform)); } @@ -134,7 +134,7 @@ namespace EMotionFX // Sample a pose from the motion. Pose pose; pose.LinkToActorInstance(m_actorInstance); - pose.InitFromBindPose(m_actor.get()); + pose.InitFromBindPose(GetActor()); pose.Zero(); m_motion->Update(&pose, &pose, m_motionInstance); diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphDeferredInitTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphDeferredInitTests.cpp index 0449005659..4448deff27 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphDeferredInitTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphDeferredInitTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Tests/BlendTreeRagdollNodeTests.cpp b/Gems/EMotionFX/Code/Tests/BlendTreeRagdollNodeTests.cpp index a01d333e95..4608917523 100644 --- a/Gems/EMotionFX/Code/Tests/BlendTreeRagdollNodeTests.cpp +++ b/Gems/EMotionFX/Code/Tests/BlendTreeRagdollNodeTests.cpp @@ -105,7 +105,7 @@ namespace EMotionFX TEST_P(RagdollRootNodeFixture, RagdollRootNodeIsSimulatedTests) { - Physics::RagdollConfiguration& ragdollConfig = m_actor->GetPhysicsSetup()->GetRagdollConfig(); + Physics::RagdollConfiguration& ragdollConfig = GetActor()->GetPhysicsSetup()->GetRagdollConfig(); AZStd::vector& ragdollNodes = ragdollConfig.m_nodes; const RagdollRootNodeParam& param = GetParam(); const AZStd::string ragdollRootNodeName = param.m_ragdollRootNode.c_str(); diff --git a/Gems/EMotionFX/Code/Tests/ColliderCommandTests.cpp b/Gems/EMotionFX/Code/Tests/ColliderCommandTests.cpp index 6314253eee..a6cadc5c2f 100644 --- a/Gems/EMotionFX/Code/Tests/ColliderCommandTests.cpp +++ b/Gems/EMotionFX/Code/Tests/ColliderCommandTests.cpp @@ -24,13 +24,13 @@ namespace EMotionFX CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; - const AZ::u32 actorId = m_actor->GetID(); + const AZ::u32 actorId = GetActor()->GetID(); const AZStd::vector jointNames = GetTestJointNames(); const size_t jointCount = jointNames.size(); // 1. Add colliders - const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(GetActor()); for (const AZStd::string& jointName : jointNames) { CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid(), &commandGroup); @@ -39,23 +39,27 @@ namespace EMotionFX } EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serializedAfterAdd = SerializePhysicsSetup(m_actor.get()); - EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(jointCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); + const AZStd::string serializedAfterAdd = SerializePhysicsSetup(GetActor()); + EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ( + jointCount, + PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Box)); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(jointCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); - EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ( + jointCount, + PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Box)); + EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(GetActor())); // 2. Remove colliders commandGroup.RemoveAllCommands(); - const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(GetActor()); size_t colliderIndexToRemove = 1; for (const AZStd::string& jointName : jointNames) @@ -64,18 +68,24 @@ namespace EMotionFX } EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serializedAfterRemove = SerializePhysicsSetup(m_actor.get()); - EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Capsule)); + const AZStd::string serializedAfterRemove = SerializePhysicsSetup(GetActor()); + EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ( + 0, + PhysicsSetupUtils::CountColliders( + GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Capsule)); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Capsule)); - EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ( + 0, + PhysicsSetupUtils::CountColliders( + GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Capsule)); + EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(GetActor())); } TEST_F(ColliderCommandTests, AddRemove1000Colliders) @@ -84,11 +94,11 @@ namespace EMotionFX CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; - const AZ::u32 actorId = m_actor->GetID(); + const AZ::u32 actorId = GetActor()->GetID(); const AZStd::string jointName = "Bip01__pelvis"; // 1. Add colliders - const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(GetActor()); const size_t colliderCount = 1000; for (AZ::u32 i = 0; i < colliderCount; ++i) { @@ -96,51 +106,51 @@ namespace EMotionFX } EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serializedAfterAdd = SerializePhysicsSetup(m_actor.get()); - EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); + const AZStd::string serializedAfterAdd = SerializePhysicsSetup(GetActor()); + EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); - EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); + EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(GetActor())); // 2. Clear colliders commandGroup.RemoveAllCommands(); - const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(GetActor()); CommandColliderHelpers::ClearColliders(actorId, jointName, PhysicsSetup::HitDetection, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serializedAfterRemove = SerializePhysicsSetup(m_actor.get()); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); + const AZStd::string serializedAfterRemove = SerializePhysicsSetup(GetActor()); + EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection)); - EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); - EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(m_actor.get())); + EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection)); + EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box)); + EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(GetActor())); } TEST_F(ColliderCommandTests, AutoSizingColliders) { CommandSystem::CommandManager commandManager; - const AZ::u32 actorId = m_actor->GetID(); + const AZ::u32 actorId = GetActor()->GetID(); const AZStd::vector jointNames = GetTestJointNames(); ASSERT_TRUE(jointNames.size() > 0) << "The joint names test data needs at least one joint for this test."; const AZStd::string& jointName = jointNames[0]; CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid()); - const AZStd::shared_ptr& physicsSetup = m_actor->GetPhysicsSetup(); + const AZStd::shared_ptr& physicsSetup = GetActor()->GetPhysicsSetup(); Physics::CharacterColliderConfiguration* colliderConfig = physicsSetup->GetColliderConfigByType(PhysicsSetup::HitDetection); EXPECT_NE(colliderConfig, nullptr) << "Collider config should be valid after we added a collider to it."; @@ -184,11 +194,11 @@ namespace EMotionFX const PhysicsSetup::ColliderConfigType m_configType = PhysicsSetup::ColliderConfigType::HitDetection; // Add collider to the given joint first. - const AZStd::shared_ptr& physicsSetup = m_actor->GetPhysicsSetup(); - EXPECT_TRUE(CommandColliderHelpers::AddCollider(m_actor->GetID(), m_jointName, m_configType, param.m_shapeType)); + const AZStd::shared_ptr& physicsSetup = GetActor()->GetPhysicsSetup(); + EXPECT_TRUE(CommandColliderHelpers::AddCollider(GetActor()->GetID(), m_jointName, m_configType, param.m_shapeType)); Physics::CharacterColliderConfiguration* characterColliderConfig = physicsSetup->GetColliderConfigByType(m_configType); ASSERT_TRUE(characterColliderConfig != nullptr); - Physics::CharacterColliderNodeConfiguration* nodeConfig = CommandColliderHelpers::GetCreateNodeConfig(m_actor.get(), m_jointName, *characterColliderConfig, result); + Physics::CharacterColliderNodeConfiguration* nodeConfig = CommandColliderHelpers::GetCreateNodeConfig(GetActor(), m_jointName, *characterColliderConfig, result); ASSERT_TRUE(nodeConfig != nullptr); EXPECT_EQ(nodeConfig->m_shapes.size(), 1); @@ -200,7 +210,8 @@ namespace EMotionFX // Create the adjust collider command and using the data from the test parameter. MCore::Command* orgCommand = CommandSystem::GetCommandManager()->FindCommand(CommandAdjustCollider::s_commandName); - CommandAdjustCollider* command = aznew CommandAdjustCollider(m_actor->GetID(), m_jointName, m_configType, /*colliderIndex=*/0, orgCommand); + CommandAdjustCollider* command = + aznew CommandAdjustCollider(GetActor()->GetID(), m_jointName, m_configType, /*colliderIndex=*/0, orgCommand); command->SetOldIsTrigger(colliderConfig->m_isTrigger); command->SetIsTrigger(param.m_isTrigger); command->SetOldPosition(colliderConfig->m_position); @@ -223,9 +234,9 @@ namespace EMotionFX } // Check execute. - const AZStd::string serializedBeforeExecute = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeExecute = SerializePhysicsSetup(GetActor()); EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(command, result)); - const AZStd::string serializedAfterExecute = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedAfterExecute = SerializePhysicsSetup(GetActor()); EXPECT_EQ(colliderConfig->m_isTrigger, param.m_isTrigger); EXPECT_EQ(colliderConfig->m_position, param.m_position); @@ -243,12 +254,12 @@ namespace EMotionFX // Check undo. EXPECT_TRUE(CommandSystem::GetCommandManager()->Undo(result)); - const AZStd::string serializedAfterUndo = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedAfterUndo = SerializePhysicsSetup(GetActor()); EXPECT_EQ(serializedAfterUndo, serializedBeforeExecute); // Check redo. EXPECT_TRUE(CommandSystem::GetCommandManager()->Redo(result)); - const AZStd::string serializedAfterRedo = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedAfterRedo = SerializePhysicsSetup(GetActor()); EXPECT_EQ(serializedAfterRedo, serializedAfterExecute); } diff --git a/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp b/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp index 31e1f75d29..0c3228567e 100644 --- a/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp +++ b/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp @@ -13,9 +13,11 @@ #include #include +#include +#include #include #include -#include +#include namespace EMotionFX { @@ -33,14 +35,12 @@ namespace EMotionFX ASSERT_EQ(GetActorManager().GetNumActors(), 0); // Load an Actor - const char* actorCmd{ "ImportActor -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor" }; - { - AZStd::string result; - EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(actorCmd, result)) << result.c_str(); - } + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, "Jack"); // Ensure the Actor is correct - ASSERT_TRUE(GetActorManager().FindActorByName("rinActor")); + ASSERT_TRUE(GetActorManager().FindActorByName("Jack")); EXPECT_EQ(GetActorManager().GetNumActors(), 1); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/MotionExtractionBusTests.cpp b/Gems/EMotionFX/Code/Tests/MotionExtractionBusTests.cpp index 170f4d2d75..347d8f93a8 100644 --- a/Gems/EMotionFX/Code/Tests/MotionExtractionBusTests.cpp +++ b/Gems/EMotionFX/Code/Tests/MotionExtractionBusTests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Tests/MultiThreadSchedulerTests.cpp b/Gems/EMotionFX/Code/Tests/MultiThreadSchedulerTests.cpp index 180da46746..02622ecd67 100644 --- a/Gems/EMotionFX/Code/Tests/MultiThreadSchedulerTests.cpp +++ b/Gems/EMotionFX/Code/Tests/MultiThreadSchedulerTests.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphActivateTests.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphActivateTests.cpp index 78cac409a6..dea332b40e 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphActivateTests.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphActivateTests.cpp @@ -19,11 +19,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include namespace EMotionFX @@ -78,7 +80,8 @@ namespace EMotionFX EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(group, commandResult)) << commandResult.c_str(); // Create temp Actor - m_actor = ActorFactory::CreateAndInit(1, "tempActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 1, "tempActor"); // Cache some local poitners. m_animGraphPlugin = static_cast(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID)); @@ -90,6 +93,7 @@ namespace EMotionFX void TearDown() override { + GetEMotionFX().GetActorManager()->UnregisterAllActors(); QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); delete m_animGraph; UIFixture::TearDown(); @@ -104,7 +108,7 @@ namespace EMotionFX AZStd::string m_entryNodeName = "testEntry"; AnimGraph* m_animGraph = nullptr; EMStudio::AnimGraphPlugin* m_animGraphPlugin = nullptr; - AutoRegisteredActor m_actor; + AZ::Data::Asset m_actorAsset; }; TEST_F(PopulatedAnimGraphFixture, CanActivateValidGraph) diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphModelTests.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphModelTests.cpp index 4ffc9e0097..b9b96bb59c 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphModelTests.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/AnimGraphModelTests.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +42,7 @@ #include #include #include +#include #include namespace EMotionFX @@ -423,7 +423,10 @@ namespace EMotionFX using testing::Eq; using testing::Not; - AutoRegisteredActor actor = EMotionFX::ActorFactory::CreateAndInit(1); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 1); + auto motionSet = AZStd::make_unique(); { @@ -432,7 +435,7 @@ namespace EMotionFX } auto* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(0); - auto* actorInstance = EMotionFX::ActorInstance::Create(actor.get()); + auto* actorInstance = EMotionFX::ActorInstance::Create(actorAsset->GetActor()); auto* animGraphInstance = EMotionFX::AnimGraphInstance::Create(animGraph, actorInstance, motionSet.get()); actorInstance->SetAnimGraphInstance(animGraphInstance); diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Menus/FileMenu/CanReset.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Menus/FileMenu/CanReset.cpp index d53ddcc924..c374938590 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Menus/FileMenu/CanReset.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Menus/FileMenu/CanReset.cpp @@ -24,12 +24,11 @@ #include #include -#include #include #include +#include #include - namespace EMotionFX { @@ -58,8 +57,10 @@ namespace EMotionFX ASSERT_EQ(GetMotionManager().GetNumMotions(), 0) << "Expected exactly zero motions"; // Create Actor, AnimGraph, Motionset and Motion - AutoRegisteredActor actor = ActorFactory::CreateAndInit(2, "SampleActor"); - ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 2, "SampleActor"); + ActorInstance::Create(actorAsset->GetActor()); { AZStd::string result; ASSERT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(createAnimGraphCmd, result)) << result.c_str(); diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp index 4f43e7dae5..17f07b2180 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include namespace EMotionFX @@ -79,7 +80,11 @@ namespace EMotionFX TEST_F(CopyPasteRagdollCollidersFixture, CanCopyCollider) #endif // AZ_TRAIT_DISABLE_FAILED_EMOTION_FX_EDITOR_TESTS { - AutoRegisteredActor actor{ActorFactory::CreateAndInit(4)}; + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 4); + const Actor* actor = actorAsset->GetActor(); + const Physics::RagdollConfiguration& ragdollConfig = actor->GetPhysicsSetup()->GetRagdollConfig(); const Physics::CharacterColliderConfiguration& simulatedObjectConfig = actor->GetPhysicsSetup()->GetSimulatedObjectColliderConfig(); @@ -104,8 +109,7 @@ namespace EMotionFX { AZStd::string result; - EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand( - "Select -actorId " + AZStd::to_string(actor->GetID()), + EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand("Select -actorId " + AZStd::to_string(actor->GetID()), result)) << result.c_str(); } diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp index 8e4fba0dd6..3f475c7916 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include namespace EMotionFX @@ -76,7 +76,9 @@ namespace EMotionFX return AZStd::make_unique(); }); - AutoRegisteredActor actor {ActorFactory::CreateAndInit(4)}; + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 4); + const Actor* actor = actorAsset->GetActor(); { AZStd::string result; diff --git a/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp b/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp index e9a4cc0c26..1004b0a1f6 100644 --- a/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp +++ b/Gems/EMotionFX/Code/Tests/RagdollCommandTests.cpp @@ -99,28 +99,28 @@ namespace EMotionFX "l_hand", }; - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_shldr"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftShoulder ) ); - const AZStd::string serializedBeforeHandAdded = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeHandAdded = SerializePhysicsSetup(GetActor()); // Adding l_hand should add l_upArm and l_loArm as well commandGroup.RemoveAllCommands(); - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); - const AZStd::string serializedAfterHandAdded = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedAfterHandAdded = SerializePhysicsSetup(GetActor()); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftHand @@ -129,23 +129,23 @@ namespace EMotionFX EXPECT_TRUE(commandManager.Undo(result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftShoulder ) ); - EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedBeforeHandAdded)); + EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedBeforeHandAdded)); EXPECT_TRUE(commandManager.Redo(result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftHand ) ); - EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedAfterHandAdded)); + EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedAfterHandAdded)); } TEST_F(RagdollCommandTests, AddJointHigherInHierarchy) @@ -166,10 +166,10 @@ namespace EMotionFX "l_hand", }; - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftHand @@ -178,10 +178,10 @@ namespace EMotionFX // l_shldr should already be in the ragdoll, so adding it should do nothing commandGroup.RemoveAllCommands(); - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_shldr"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftHand @@ -190,11 +190,11 @@ namespace EMotionFX // Undo here undoes the addition of l_hand EXPECT_TRUE(commandManager.Undo(result)) << result.c_str(); - EXPECT_TRUE(GetRagdollJointNames(m_actor.get()).empty()); + EXPECT_TRUE(GetRagdollJointNames(GetActor()).empty()); EXPECT_TRUE(commandManager.Redo(result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise( StrEq(), jointsToLeftHand @@ -221,16 +221,16 @@ namespace EMotionFX }; // Add a joint to the ragdoll that does not make a chain all the way to the root - EXPECT_TRUE(commandManager.ExecuteCommand(aznew CommandAddRagdollJoint(m_actor->GetID(), "l_shldr"), result)) << result.c_str(); + EXPECT_TRUE(commandManager.ExecuteCommand(aznew CommandAddRagdollJoint(GetActor()->GetID(), "l_shldr"), result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise(StrEq(), AZStd::vector{"l_shldr"}) ); - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise(StrEq(), jointsToLeftHand) ); } @@ -250,17 +250,17 @@ namespace EMotionFX }; // Add joints from the root to the left hand - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); // Removing the left shoulder should remove the elbow, wrist, and hand // as well commandGroup.RemoveAllCommands(); - CommandRagdollHelpers::RemoveJointsFromRagdoll(m_actor->GetID(), {"l_shldr"}, &commandGroup); + CommandRagdollHelpers::RemoveJointsFromRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); EXPECT_THAT( - GetRagdollJointNames(m_actor.get()), + GetRagdollJointNames(GetActor()), testing::UnorderedPointwise(StrEq(), jointsToSpine3) ); } @@ -271,24 +271,24 @@ namespace EMotionFX CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; - CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup); + CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str(); - const AZStd::string serializedBeforeSphereAdded = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedBeforeSphereAdded = SerializePhysicsSetup(GetActor()); - CommandColliderHelpers::AddCollider(m_actor->GetID(), "l_hand", + CommandColliderHelpers::AddCollider(GetActor()->GetID(), "l_hand", PhysicsSetup::Ragdoll, azrtti_typeid()); - const AZStd::string serializedAfterSphereAdded = SerializePhysicsSetup(m_actor.get()); + const AZStd::string serializedAfterSphereAdded = SerializePhysicsSetup(GetActor()); EXPECT_THAT(serializedAfterSphereAdded, ::testing::Not(StrEq(serializedBeforeSphereAdded))); EXPECT_TRUE(commandManager.Undo(result)) << result.c_str(); - EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedBeforeSphereAdded)); + EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedBeforeSphereAdded)); EXPECT_TRUE(commandManager.Redo(result)) << result.c_str(); - EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedAfterSphereAdded)); + EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedAfterSphereAdded)); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/SimulatedObjectCommandTests.cpp b/Gems/EMotionFX/Code/Tests/SimulatedObjectCommandTests.cpp index 4bac70c13d..dab761f649 100644 --- a/Gems/EMotionFX/Code/Tests/SimulatedObjectCommandTests.cpp +++ b/Gems/EMotionFX/Code/Tests/SimulatedObjectCommandTests.cpp @@ -55,25 +55,25 @@ namespace EMotionFX CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; - const uint32 actorId = m_actor->GetID(); + const uint32 actorId = GetActor()->GetID(); const AZStd::vector jointNames = GetTestJointNames(); // 1. Add simulated object. - const AZStd::string serializedBeforeAdd = SerializeSimulatedObjectSetup(m_actor.get()); + const AZStd::string serializedBeforeAdd = SerializeSimulatedObjectSetup(GetActor()); CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup); CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup); CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serializedAfterAdd = SerializeSimulatedObjectSetup(m_actor.get()); - EXPECT_EQ(3, CountSimulatedObjects(m_actor.get())); + const AZStd::string serializedAfterAdd = SerializeSimulatedObjectSetup(GetActor()); + EXPECT_EQ(3, CountSimulatedObjects(GetActor())); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(0, CountSimulatedObjects(m_actor.get())); - EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedObjects(GetActor())); + EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(3, CountSimulatedObjects(m_actor.get())); - EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(3, CountSimulatedObjects(GetActor())); + EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(GetActor())); // 2. Remove simulated object. commandGroup.RemoveAllCommands(); @@ -81,22 +81,22 @@ namespace EMotionFX CommandSimulatedObjectHelpers::RemoveSimulatedObject(actorId, 0, &commandGroup); CommandSimulatedObjectHelpers::RemoveSimulatedObject(actorId, 0, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - EXPECT_EQ(0, CountSimulatedObjects(m_actor.get())); - EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedObjects(GetActor())); + EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(GetActor())); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(3, CountSimulatedObjects(m_actor.get())); - EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(3, CountSimulatedObjects(GetActor())); + EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(0, CountSimulatedObjects(m_actor.get())); - EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedObjects(GetActor())); + EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(GetActor())); // 3. Add simulated joints. // 3.1 Add a simulated object first to put in the simulated joints. commandGroup.RemoveAllCommands(); CommandSimulatedObjectHelpers::AddSimulatedObject(actorId); - const AZStd::string serialized3_1 = SerializeSimulatedObjectSetup(m_actor.get()); + const AZStd::string serialized3_1 = SerializeSimulatedObjectSetup(GetActor()); // 3.2 Add simulated joints. // Joint hierarchy as follow: @@ -105,7 +105,7 @@ namespace EMotionFX // --l_loLeg // --l_ankle // --l_ball - const Skeleton* skeleton = m_actor->GetSkeleton(); + const Skeleton* skeleton = GetActor()->GetSkeleton(); const size_t l_upLegIdx = skeleton->FindNodeByName("l_upLeg")->GetNodeIndex(); const size_t l_upLegRollIdx = skeleton->FindNodeByName("l_upLegRoll")->GetNodeIndex(); const size_t l_loLegIdx = skeleton->FindNodeByName("l_loLeg")->GetNodeIndex(); @@ -113,33 +113,33 @@ namespace EMotionFX const size_t l_ballIdx = skeleton->FindNodeByName("l_ball")->GetNodeIndex(); CommandSimulatedObjectHelpers::AddSimulatedJoints(actorId, {l_upLegIdx, l_upLegRollIdx, l_loLegIdx, l_ankleIdx, l_ballIdx}, 0, false, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serialized3_2 = SerializeSimulatedObjectSetup(m_actor.get()); - EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(2, CountChildJoints(m_actor.get(), 0, l_upLegIdx)); - EXPECT_EQ(0, CountChildJoints(m_actor.get(), 0, l_upLegRollIdx)); - EXPECT_EQ(1, CountChildJoints(m_actor.get(), 0, l_loLegIdx)); + const AZStd::string serialized3_2 = SerializeSimulatedObjectSetup(GetActor()); + EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(2, CountChildJoints(GetActor(), 0, l_upLegIdx)); + EXPECT_EQ(0, CountChildJoints(GetActor(), 0, l_upLegRollIdx)); + EXPECT_EQ(1, CountChildJoints(GetActor(), 0, l_loLegIdx)); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(GetActor())); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(GetActor())); // 4 Remove simulated joints. // 4.1 Test sparse chain. - EXPECT_EQ(1, CountRootJoints(m_actor.get(), 0)); + EXPECT_EQ(1, CountRootJoints(GetActor(), 0)); commandGroup.RemoveAllCommands(); CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, {l_loLegIdx}, 0, false, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - EXPECT_EQ(2, CountRootJoints(m_actor.get(), 0)); + EXPECT_EQ(2, CountRootJoints(GetActor(), 0)); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(1, CountRootJoints(m_actor.get(), 0)); + EXPECT_EQ(1, CountRootJoints(GetActor(), 0)); EXPECT_TRUE(commandManager.Redo(result)); - EXPECT_EQ(2, CountRootJoints(m_actor.get(), 0)); + EXPECT_EQ(2, CountRootJoints(GetActor(), 0)); EXPECT_TRUE(commandManager.Undo(result)); @@ -147,23 +147,23 @@ namespace EMotionFX commandGroup.RemoveAllCommands(); CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, { l_upLegIdx, l_upLegRollIdx, l_loLegIdx, l_ankleIdx, l_ballIdx }, 0, false, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(GetActor())); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(GetActor())); // 4.3 Test removing the root joint and children. commandGroup.RemoveAllCommands(); CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, { l_upLegIdx }, 0, true, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(GetActor())); EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(GetActor())); } TEST_F(SimulatedObjectCommandTests, SimulatedObjectCommands_UndoRemoveJointTest) @@ -172,35 +172,35 @@ namespace EMotionFX CommandSystem::CommandManager commandManager; MCore::CommandGroup commandGroup; - const uint32 actorId = m_actor->GetID(); + const uint32 actorId = GetActor()->GetID(); const AZStd::vector jointNames = GetTestJointNames(); // 1. Add simulated object CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - const AZStd::string serializedBase = SerializeSimulatedObjectSetup(m_actor.get()); + const AZStd::string serializedBase = SerializeSimulatedObjectSetup(GetActor()); const size_t simulatedObjectIndex = 0; // 2. Add r_upLeg simulated joints - const Skeleton* skeleton = m_actor->GetSkeleton(); + const Skeleton* skeleton = GetActor()->GetSkeleton(); const size_t r_upLegIdx = skeleton->FindNodeByName("r_upLeg")->GetNodeIndex(); const size_t r_loLegIdx = skeleton->FindNodeByName("r_loLeg")->GetNodeIndex(); CommandSimulatedObjectHelpers::AddSimulatedJoints(actorId, { r_upLegIdx, r_loLegIdx }, 0, false); - EXPECT_EQ(2, CountSimulatedJoints(m_actor.get(), 0)); - const AZStd::string serializedUpLeg = SerializeSimulatedObjectSetup(m_actor.get()); + EXPECT_EQ(2, CountSimulatedJoints(GetActor(), 0)); + const AZStd::string serializedUpLeg = SerializeSimulatedObjectSetup(GetActor()); // 3. Remove the r_loLeg simulated joint AZStd::vector jointsToBeRemoved; commandGroup.RemoveAllCommands(); CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, { r_upLegIdx }, simulatedObjectIndex, true, &commandGroup); EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)); - EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serializedBase, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serializedBase, SerializeSimulatedObjectSetup(GetActor())); // 4. Undo // This recreates r_loLeg and r_loLeg but won't add all other children recursively as only these two joints got aded in step 3. EXPECT_TRUE(commandManager.Undo(result)); - EXPECT_EQ(2, CountSimulatedJoints(m_actor.get(), 0)); - EXPECT_EQ(serializedUpLeg, SerializeSimulatedObjectSetup(m_actor.get())); + EXPECT_EQ(2, CountSimulatedJoints(GetActor(), 0)); + EXPECT_EQ(serializedUpLeg, SerializeSimulatedObjectSetup(GetActor())); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/SimulatedObjectModelTests.cpp b/Gems/EMotionFX/Code/Tests/SimulatedObjectModelTests.cpp index 41269cec77..85da96412b 100644 --- a/Gems/EMotionFX/Code/Tests/SimulatedObjectModelTests.cpp +++ b/Gems/EMotionFX/Code/Tests/SimulatedObjectModelTests.cpp @@ -22,17 +22,21 @@ #include #include #include +#include namespace EMotionFX { using SimulatedObjectModelTestsFixture = UIFixture; TEST_F(SimulatedObjectModelTestsFixture, CanUndoAddSimulatedObjectAndSimulatedJointWithChildren) { - AutoRegisteredActor actor = ActorFactory::CreateAndInit(3, "simulatedObjectModelTestActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 3, "simulatedObjectModelTestActor"); + const Actor* actor = actorAsset->GetActor(); EMotionFX::SimulatedObjectWidget* simulatedObjectWidget = static_cast(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SimulatedObjectWidget::CLASS_ID)); ASSERT_TRUE(simulatedObjectWidget) << "Simulated Object plugin not loaded"; - simulatedObjectWidget->ActorSelectionChanged(actor.get()); + simulatedObjectWidget->ActorSelectionChanged(actorAsset->GetActor()); SimulatedObjectModel* model = simulatedObjectWidget->GetSimulatedObjectModel(); diff --git a/Gems/EMotionFX/Code/Tests/SimulatedObjectSerializeTests.cpp b/Gems/EMotionFX/Code/Tests/SimulatedObjectSerializeTests.cpp index c261f63bc1..e5c2c5ce4f 100644 --- a/Gems/EMotionFX/Code/Tests/SimulatedObjectSerializeTests.cpp +++ b/Gems/EMotionFX/Code/Tests/SimulatedObjectSerializeTests.cpp @@ -20,7 +20,7 @@ namespace EMotionFX TEST_F(SimulatedObjectSerializeTests, SerializeTest) { - SimulatedObjectSetup* setup = m_actor->GetSimulatedObjectSetup().get(); + SimulatedObjectSetup* setup = GetActor()->GetSimulatedObjectSetup().get(); // Build some setup. SimulatedObject* object = setup->AddSimulatedObject(); @@ -29,7 +29,7 @@ namespace EMotionFX object->SetGravityFactor(3.0f); object->SetStiffnessFactor(4.0f); const AZStd::vector jointNames = { "l_upArm", "l_loArm", "l_hand" }; - Skeleton* skeleton = m_actor->GetSkeleton(); + Skeleton* skeleton = GetActor()->GetSkeleton(); for (const AZStd::string& name : jointNames) { size_t skeletonJointIndex; @@ -50,7 +50,7 @@ namespace EMotionFX object->GetSimulatedJoint(0)->SetPinned(true); // Serialize it and deserialize it. - const AZStd::string serialized = SerializeSimulatedObjectSetup(m_actor.get()); + const AZStd::string serialized = SerializeSimulatedObjectSetup(GetActor()); AZStd::unique_ptr loadedSetup(DeserializeSimulatedObjectSetup(serialized)); // Verify some of the contents of the deserialized version. diff --git a/Gems/EMotionFX/Code/Tests/SkeletalLODTests.cpp b/Gems/EMotionFX/Code/Tests/SkeletalLODTests.cpp index 1252eb36ae..54a81cb559 100644 --- a/Gems/EMotionFX/Code/Tests/SkeletalLODTests.cpp +++ b/Gems/EMotionFX/Code/Tests/SkeletalLODTests.cpp @@ -22,13 +22,13 @@ namespace EMotionFX void SetUp() { ActorFixture::SetUp(); - m_actor->AddLODLevel(); + GetActor()->AddLODLevel(); DisableJointsForLOD(m_disabledJointNames, 1); } void DisableJointsForLOD(const std::vector& jointNames, size_t lodLevel) { - const Skeleton* skeleton = m_actor->GetSkeleton(); + const Skeleton* skeleton = GetActor()->GetSkeleton(); for (const std::string& jointName : jointNames) { Node* joint = skeleton->FindNodeByName(jointName.c_str()); diff --git a/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.cpp b/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.cpp index 6375d48c6b..bb3c4f76e2 100644 --- a/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.cpp +++ b/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.cpp @@ -10,9 +10,10 @@ #include #include #include -#include #include #include +#include +#include namespace EMotionFX { diff --git a/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.h b/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.h index 3a041fcf34..5b75ddf590 100644 --- a/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.h +++ b/Gems/EMotionFX/Code/Tests/TestAssetCode/TestActorAssets.h @@ -10,6 +10,9 @@ #include #include +#include +#include +#include namespace EMotionFX { @@ -18,5 +21,14 @@ namespace EMotionFX public: static AZStd::string FileToBase64(const char* filePath); static AZ::Data::Asset GetAssetFromActor(const AZ::Data::AssetId& assetId, AZStd::unique_ptr&& actor); + + template + static AZ::Data::Asset CreateActorAssetAndRegister(const AZ::Data::AssetId& assetId, Args&&... args) + { + AZStd::unique_ptr actor = ActorFactory::CreateAndInit(AZStd::forward(args)...); + AZ::Data::Asset actorAsset = TestActorAssets::GetAssetFromActor(assetId, AZStd::move(actor)); + GetEMotionFX().GetActorManager()->RegisterActor(actorAsset); + return AZStd::move(actorAsset); + } }; } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/UI/CanAddJointAndChildren.cpp b/Gems/EMotionFX/Code/Tests/UI/CanAddJointAndChildren.cpp index 59506a9d43..32240ba409 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanAddJointAndChildren.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanAddJointAndChildren.cpp @@ -16,13 +16,13 @@ #include #include -#include #include #include #include #include #include +#include namespace EMotionFX { @@ -37,8 +37,11 @@ namespace EMotionFX RecordProperty("test_case_id", "C13048819"); // Create an actor - AutoRegisteredActor actor = ActorFactory::CreateAndInit(5, "SimpleActor"); - ActorInstance* actorInstance = ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 5, "SimpleActor"); + ActorInstance* actorInstance = ActorInstance::Create(actorAsset->GetActor()); + const Actor* actor = actorAsset->GetActor(); // Open simulated objects layout EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects"); diff --git a/Gems/EMotionFX/Code/Tests/UI/CanAddSimulatedObject.cpp b/Gems/EMotionFX/Code/Tests/UI/CanAddSimulatedObject.cpp index e1c4525a9e..0820895db8 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanAddSimulatedObject.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanAddSimulatedObject.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include #include #include @@ -81,9 +81,9 @@ namespace EMotionFX inputDialog->accept(); // There is one and only one simulated objects - ASSERT_EQ(m_actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 1); + ASSERT_EQ(m_actorAsset->GetActor()->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 1); // Check it has the correct name - EXPECT_STREQ(m_actor->GetSimulatedObjectSetup()->GetSimulatedObject(0)->GetName().c_str(), objectName); + EXPECT_STREQ(m_actorAsset->GetActor()->GetSimulatedObjectSetup()->GetSimulatedObject(0)->GetName().c_str(), objectName); } @@ -112,14 +112,16 @@ namespace EMotionFX }); ASSERT_NE(addCapsuleColliderAction, addSelectedColliderMenuActions.end()); - size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); + size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders( + m_actorAsset->GetActor(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); (*addCapsuleColliderAction)->trigger(); // Delete the context menu, otherwise there it will still be around during this frame as the Qt event loop has not been run. delete contextMenu; - const size_t numCapsuleCollidersAfterAdd = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); + const size_t numCapsuleCollidersAfterAdd = PhysicsSetupUtils::CountColliders( + m_actorAsset->GetActor(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); ASSERT_EQ(numCapsuleCollidersAfterAdd, numCapsuleColliders + 1); @@ -127,7 +129,7 @@ namespace EMotionFX } protected: - AutoRegisteredActor m_actor; + AZ::Data::Asset m_actorAsset; EMotionFX::SimulatedObjectWidget* m_simulatedObjectWidget = nullptr; EMotionFX::SkeletonOutlinerPlugin* m_skeletonOutliner = nullptr; @@ -144,7 +146,8 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048820"); - m_actor = ActorFactory::CreateAndInit(1, "CanAddSimulatedObjectActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 1, "CanAddSimulatedObjectActor"); CreateSimulateObject("New simulated object"); } @@ -153,9 +156,11 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048818"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(2, "CanAddSimulatedObjectWithJointsActor"); - - ActorInstance* actorInstance = ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 2, "CanAddSimulatedObjectWithJointsActor"); + Actor* actor = actorAsset->GetActor(); + ActorInstance* actorInstance = ActorInstance::Create(actor); EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects"); @@ -216,7 +221,9 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048820a"); - m_actor = ActorFactory::CreateAndInit(5, "CanAddSimulatedObjectActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 5, "CanAddSimulatedObjectActor"); + Actor* actor = m_actorAsset->GetActor(); CreateSimulateObject("sim1"); @@ -261,8 +268,8 @@ namespace EMotionFX inputDialog->SetText("sim2"); inputDialog->accept(); - ASSERT_EQ(m_actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 2); - const auto simulatedObject = m_actor->GetSimulatedObjectSetup()->GetSimulatedObject(1); + ASSERT_EQ(actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 2); + const auto simulatedObject = actor->GetSimulatedObjectSetup()->GetSimulatedObject(1); EXPECT_STREQ(simulatedObject->GetName().c_str(), "sim2"); } @@ -270,7 +277,9 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048816"); - m_actor = ActorFactory::CreateAndInit(5, "CanAddSimulatedObjectActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 5, "CanAddSimulatedObjectActor"); + Actor* actor = m_actorAsset->GetActor(); CreateSimulateObject("sim1"); @@ -301,25 +310,25 @@ namespace EMotionFX QAction* addCapsuleColliderAction; ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addCapsuleColliderAction, addSelectedColliderMenu, "Capsule")); - size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); + size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); EXPECT_EQ(numCapsuleColliders, 0); addCapsuleColliderAction->trigger(); // Check that a collider has been added. - size_t numCollidersAfterAddCapsule = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); + size_t numCollidersAfterAddCapsule = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); ASSERT_EQ(numCollidersAfterAddCapsule, numCapsuleColliders + 1) << "Capsule collider not added."; QAction* addSphereColliderAction; ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addSphereColliderAction, addSelectedColliderMenu, "Sphere")); - const size_t numSphereColliders = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); + const size_t numSphereColliders = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); EXPECT_EQ(numSphereColliders, 0); addSphereColliderAction->trigger(); // Check that a second collider has been added. - const size_t numCollidersAfterAddSphere = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); + const size_t numCollidersAfterAddSphere = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); ASSERT_EQ(numCollidersAfterAddSphere, numSphereColliders + 1) << "Sphere collider not added."; } @@ -327,7 +336,9 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048821"); - m_actor = ActorFactory::CreateAndInit(1, "CanRemoveSimulatedObjectActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 1, "CanRemoveSimulatedObjectActor"); + Actor* actor = m_actorAsset->GetActor(); CreateSimulateObject("TestObject1"); @@ -350,14 +361,16 @@ namespace EMotionFX ASSERT_TRUE(UIFixture::GetActionFromContextMenu(removeObjectAction, contextMenu, "Remove object")); removeObjectAction->trigger(); - ASSERT_EQ(m_actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 0); + ASSERT_EQ(actor->GetSimulatedObjectSetup()->GetNumSimulatedObjects(), 0); } TEST_F(CanAddSimulatedObjectFixture, CanAddColliderToSimulatedObjectFromInspector) { RecordProperty("test_case_id", "C20385259"); - - m_actor = ActorFactory::CreateAndInit(5, "CanAddSimulatedObjectActor"); + + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 5, "CanAddSimulatedObjectActor"); + Actor* actor = m_actorAsset->GetActor(); CreateSimulateObject("sim1"); @@ -380,10 +393,12 @@ namespace EMotionFX // Send the left button click directly to the button QTest::mouseClick(addColliderButton, Qt::LeftButton); - const size_t numCapsuleColliders = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); + const size_t numCapsuleColliders = + PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); EXPECT_EQ(numCapsuleColliders, 0); - const size_t numSphereColliders = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); + const size_t numSphereColliders = + PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); EXPECT_EQ(numSphereColliders, 0); QMenu* contextMenu = addColliderButton->findChild("EMFX.AddColliderButton.ContextMenu"); @@ -392,13 +407,15 @@ namespace EMotionFX QAction* addCapsuleAction; ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addCapsuleAction, contextMenu, "Add capsule")); addCapsuleAction->trigger(); - const size_t numCollidersAfterAddCapsule = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); + const size_t numCollidersAfterAddCapsule = + PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Capsule); ASSERT_EQ(numCollidersAfterAddCapsule, numCapsuleColliders + 1) << "Capsule collider not added."; QAction* addSphereAction; ASSERT_TRUE(UIFixture::GetActionFromContextMenu(addSphereAction, contextMenu, "Add sphere")); addSphereAction->trigger(); - const size_t numCollidersAfterAddSphere = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); + const size_t numCollidersAfterAddSphere = + PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider, false, Physics::ShapeType::Sphere); ASSERT_EQ(numCollidersAfterAddSphere, numSphereColliders + 1) << "Sphere collider not added."; } @@ -406,7 +423,9 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048818"); - m_actor = ActorFactory::CreateAndInit(7, "CanAddSimulatedObjectActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 7, "CanAddSimulatedObjectActor"); + Actor* actor = m_actorAsset->GetActor(); CreateSimulateObject("ANY"); @@ -442,7 +461,7 @@ namespace EMotionFX messageBoxPopupHandler.WaitForPopupPressDialogButton(QDialogButtonBox::No); newSimulatedObjectAction->trigger(); - const EMotionFX::SimulatedObject* simulatedObject = m_actor->GetSimulatedObjectSetup()->GetSimulatedObject(0); + const EMotionFX::SimulatedObject* simulatedObject = actor->GetSimulatedObjectSetup()->GetSimulatedObject(0); EXPECT_EQ(simulatedObject->GetNumSimulatedRootJoints(), 1); EXPECT_EQ(simulatedObject->GetNumSimulatedJoints(), 3); } @@ -450,7 +469,9 @@ namespace EMotionFX { RecordProperty("test_case_id", "C13048817"); - m_actor = ActorFactory::CreateAndInit(5, "CanAddSimulatedObjectActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + m_actorAsset = TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 5, "CanAddSimulatedObjectActor"); + Actor* actor = m_actorAsset->GetActor(); EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects"); @@ -470,7 +491,7 @@ namespace EMotionFX AddCapsuleColliderToJointIndex(3); AddCapsuleColliderToJointIndex(4); - const size_t numCollidersAfterAdd = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider); + const size_t numCollidersAfterAdd = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider); EXPECT_EQ(numCollidersAfterAdd, 2); m_indexList.clear(); @@ -498,7 +519,7 @@ namespace EMotionFX removeAction->trigger(); // Check that one of the colliders is now gone. - const size_t numCollidersAfterFirstRemove = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider); + const size_t numCollidersAfterFirstRemove = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider); ASSERT_EQ(numCollidersAfterFirstRemove, numCollidersAfterAdd - 1) << "RemoveCollider action in Simulated Object Inspector failed."; // Now do the same thing using the Simulated Object Inspector context menu. @@ -536,7 +557,7 @@ namespace EMotionFX delAction->trigger(); // Check that we have the number of colliders we started we expect. - const size_t numCollidersAfterSecondRemove = PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::SimulatedObjectCollider); + const size_t numCollidersAfterSecondRemove = PhysicsSetupUtils::CountColliders(actor, PhysicsSetup::SimulatedObjectCollider); ASSERT_EQ(numCollidersAfterSecondRemove, numCollidersAfterAdd - 2); } diff --git a/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp b/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp index e27d558664..a10ba352d2 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanAddToSimulatedObject.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -52,9 +53,11 @@ namespace EMotionFX { RecordProperty("test_case_id", "C14603914"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(7, "CanAddToSimulatedObjectActor"); - - ActorInstance* actorInstance = ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 7, "CanAddToSimulatedObjectActor"); + Actor* actor = actorAsset->GetActor(); + ActorInstance* actorInstance = ActorInstance::Create(actor); // Change the Editor mode to Simulated Objects EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects"); @@ -165,9 +168,11 @@ namespace EMotionFX }); RecordProperty("test_case_id", "C13291807"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(7, "CanAddToSimulatedObjectActor"); - - ActorInstance* actorInstance = ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 7, "CanAddToSimulatedObjectActor"); + Actor* actor = actorAsset->GetActor(); + ActorInstance* actorInstance = ActorInstance::Create(actor); // Change the Editor mode to Physics EMStudio::GetMainWindow()->ApplicationModeChanged("Physics"); diff --git a/Gems/EMotionFX/Code/Tests/UI/CanChangeParametersInSimulatedObject.cpp b/Gems/EMotionFX/Code/Tests/UI/CanChangeParametersInSimulatedObject.cpp index 510bfb2d83..108a637412 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanChangeParametersInSimulatedObject.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanChangeParametersInSimulatedObject.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -37,9 +38,10 @@ namespace EMotionFX { RecordProperty("test_case_id", "C14519563"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(7, "CanAddToSimulatedObjectActor"); - - ActorInstance* actorInstance = ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 7, "CanAddToSimulatedObjectActor"); + ActorInstance* actorInstance = ActorInstance::Create(actorAsset->GetActor()); // Change the Editor mode to Simulated Objects EMStudio::GetMainWindow()->ApplicationModeChanged("SimulatedObjects"); diff --git a/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp b/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp index 73a0f1c865..2af3329203 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp @@ -24,10 +24,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -141,8 +143,10 @@ namespace EMotionFX { if (EMotionFX::GetActorManager().GetNumActorInstances() == 0) { - AutoRegisteredActor actor = ActorFactory::CreateAndInit(2, "CanAddSimulatedObjectWithJointsActor"); - ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, 2, "CanAddSimulatedObjectWithJointsActor"); + ActorInstance::Create(actorAsset->GetActor()); EXPECT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to create actor set for reset test."; } @@ -152,15 +156,16 @@ namespace EMotionFX { if (EMotionFX::GetActorManager().GetNumActorInstances() == 0) { - AutoRegisteredActor actor = ActorFactory::CreateAndInit(2, "CanAddSimulatedObjectWithJointsActor"); - ActorInstance::Create(actor.get()); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = TestActorAssets::CreateActorAssetAndRegister( + actorAssetId, 2, "CanAddSimulatedObjectWithJointsActor"); + ActorInstance::Create(actorAsset->GetActor()); EXPECT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to create actor set for reset test."; - - actor->SetFileName(filename); + actorAsset->GetActor()->SetFileName(filename); AZStd::string stringFilename = filename; - ExporterLib::SaveActor(stringFilename, actor.get(), MCore::Endian::ENDIAN_LITTLE); + ExporterLib::SaveActor(stringFilename, actorAsset->GetActor(), MCore::Endian::ENDIAN_LITTLE); } } @@ -239,7 +244,6 @@ namespace EMotionFX // Load the actor we just saved, with replaceScene set to true to represent a load. LoadActor(actorFilename.toUtf8().data(), true); - ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to load Actor."; // Do it again to verify that number of actors stays the same when replaceScene is true. @@ -696,7 +700,10 @@ namespace EMotionFX TestResetMenuItem(fileMenu); - TestActorMenus(fileMenu); + // Temporarily disable loading actor test. + // This is because the importer command now load actor asset instead of reading from disk. We do not want to add dependency to the asset processor + // in this test. + // TestActorMenus(fileMenu); TestSaveAllMenuItem(fileMenu); } diff --git a/Gems/EMotionFX/Code/Tests/UI/CanUseLayoutMenu.cpp b/Gems/EMotionFX/Code/Tests/UI/CanUseLayoutMenu.cpp index 6fbaf3a028..e615d3c9e0 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanUseLayoutMenu.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanUseLayoutMenu.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/EMotionFX/Code/Tests/UI/ClothColliderTests.cpp b/Gems/EMotionFX/Code/Tests/UI/ClothColliderTests.cpp index 6428a11e6d..d87c35a4fa 100644 --- a/Gems/EMotionFX/Code/Tests/UI/ClothColliderTests.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/ClothColliderTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include namespace EMotionFX @@ -102,7 +103,9 @@ namespace EMotionFX const int lastIndex = 6; RecordProperty("test_case_id", "C18970351"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(numJoints, "RagdollEditTestsActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, numJoints, "RagdollEditTestsActor"); CreateSkeletonAndModelIndices(); EXPECT_EQ(m_indexList.size(), numJoints); diff --git a/Gems/EMotionFX/Code/Tests/UI/LODSkinnedMeshTests.cpp b/Gems/EMotionFX/Code/Tests/UI/LODSkinnedMeshTests.cpp index 6e5e14e9fc..def8bdab58 100644 --- a/Gems/EMotionFX/Code/Tests/UI/LODSkinnedMeshTests.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/LODSkinnedMeshTests.cpp @@ -80,11 +80,14 @@ namespace EMotionFX DataMembers m_data; }; - AZStd::unique_ptr CreateLODActor(int numLODs) + AZ::Data::Asset CreateLODActor(int numLODs) { - AZStd::unique_ptr actor = ActorFactory::CreateAndInit("LODSkinnedMeshTestsActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, "LODSkinnedMeshTestsActor"); // Modify the actor to have numLODs LOD levels. + Actor* actor = actorAsset->GetActor(); Mesh* lodMesh = actor->GetMesh(0, 0); StandardMaterial* dummyMat = StandardMaterial::Create("Dummy Material"); actor->AddMaterial(0, dummyMat); // owns the material @@ -97,7 +100,7 @@ namespace EMotionFX actor->AddMaterial(i, dummyMat->Clone()); } - return actor; + return AZStd::move(actorAsset); } class LODPropertyRowWidget @@ -112,9 +115,8 @@ namespace EMotionFX const int numLODs = GetParam(); RecordProperty("test_case_id", "C29202698"); - AutoRegisteredActor actor = CreateLODActor(numLODs); - - ActorInstance* actorInstance = ActorInstance::Create(actor.get()); + AZ::Data::Asset actorAsset = CreateLODActor(numLODs); + ActorInstance* actorInstance = ActorInstance::Create(actorAsset->GetActor()); // Change the Editor mode to Character EMStudio::GetMainWindow()->ApplicationModeChanged("Character"); @@ -166,8 +168,7 @@ namespace EMotionFX gameEntity->SetId(entityId); AZ::Data::AssetId actorAssetId("{85D3EF54-7400-43F8-8A40-F6BCBF534E54}"); - AZStd::unique_ptr actor = CreateLODActor(numLODs); - AZ::Data::Asset actorAsset = TestActorAssets::GetAssetFromActor(actorAssetId, AZStd::move(actor)); + AZ::Data::Asset actorAsset = CreateLODActor(numLODs); gameEntity->CreateComponent(); Integration::ActorComponent::Configuration actorConf; diff --git a/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp b/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp index 1c39cde5b6..d097e67d15 100644 --- a/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/RagdollEditTests.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -110,7 +111,9 @@ namespace EMotionFX const int numJoints = 6; RecordProperty("test_case_id", "C3122249"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(numJoints, "RagdollEditTestsActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, numJoints, "RagdollEditTestsActor"); CreateSkeletonAndModelIndices(); @@ -138,7 +141,9 @@ namespace EMotionFX const int numJoints = 8; RecordProperty("test_case_id", "C3122248"); - AutoRegisteredActor actor = ActorFactory::CreateAndInit(numJoints, "RagdollEditTestsActor"); + AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}"); + AZ::Data::Asset actorAsset = + TestActorAssets::CreateActorAssetAndRegister(actorAssetId, numJoints, "RagdollEditTestsActor"); CreateSkeletonAndModelIndices(); EXPECT_EQ(m_indexList.size(), numJoints); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp new file mode 100644 index 0000000000..6313d6ec17 --- /dev/null +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp @@ -0,0 +1,470 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include + +namespace GraphCanvas +{ + namespace Styling + { + + StyleHelper::StyleHelper(const AZ::EntityId& styledEntity) + { + SetStyle(styledEntity); + } + + StyleHelper::StyleHelper(const AZ::EntityId& realStyledEntity, const AZStd::string& virtualChildElement) + { + SetStyle(realStyledEntity, virtualChildElement); + } + + StyleHelper::~StyleHelper() + { + ReleaseStyle(); + } + + void StyleHelper::OnStylesUnloaded() + { + ReleaseStyle(); + } + + void StyleHelper::SetEditorId(const EditorId& editorId) + { + if (m_editorId != editorId) + { + ReleaseStyle(); + m_editorId = editorId; + + RegisterStyleSheetBus(m_editorId); + } + } + + void StyleHelper::SetScene(const AZ::EntityId& sceneId) + { + m_scene = sceneId; + + EditorId editorId; + SceneRequestBus::EventResult(editorId, m_scene, &SceneRequests::GetEditorId); + SetEditorId(editorId); + } + + void StyleHelper::SetStyle(const AZ::EntityId& styledEntity) + { + ReleaseStyle(); + + m_styledEntity = styledEntity; + + AZ::EntityId sceneId; + SceneMemberRequestBus::EventResult(sceneId, m_styledEntity, &SceneMemberRequests::GetScene); + if (!sceneId.IsValid()) + { + return; + } + + SetScene(sceneId); + + for (const auto& selector : m_styleSelectors) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); + } + + UpdateStyle(); + } + + void StyleHelper::SetStyle(const AZStd::string& style) + { + ReleaseStyle(); + + m_deleteStyledEntity = true; + + PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateStyleEntity, style); + + for (const auto& selector : m_styleSelectors) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); + } + + // TODO: remove/replace OnSceneSet and fix any systems/components listening for that event. + SceneMemberNotificationBus::Event(m_styledEntity, &SceneMemberNotifications::OnSceneSet, m_scene); + + UpdateStyle(); + + static bool enableDiagnostic = false; + if (enableDiagnostic) + { + AZStd::string description; + StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); + qDebug() << description.c_str(); + } + } + + void StyleHelper::SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement) + { + ReleaseStyle(); + + m_deleteStyledEntity = true; + + AZ::EntityId sceneId; + SceneMemberRequestBus::EventResult(sceneId, parentStyledEntity, &SceneMemberRequests::GetScene); + + SetScene(sceneId); + + PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateVirtualChild, parentStyledEntity, virtualChildElement); + + for (const auto& selector : m_styleSelectors) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); + } + + UpdateStyle(); + + static bool enableDiagnostic = false; + if (enableDiagnostic) + { + AZStd::string description; + StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); + qDebug() << description.c_str(); + } + + } + + void StyleHelper::RemoveAttributeOverride(Styling::Attribute attribute) + { + m_attributeOverride.erase(attribute); + } + + bool StyleHelper::HasAttribute(Styling::Attribute attribute) const + { + bool hasAttribute = (m_attributeOverride.find(attribute) != m_attributeOverride.end()); + + if (!hasAttribute) + { + StyleRequestBus::EventResult(hasAttribute, m_style, &StyleRequests::HasAttribute, static_cast(attribute)); + } + + return hasAttribute; + } + + QColor StyleHelper::GetColor(Styling::Attribute color, QColor defaultValue /*= QColor()*/) const + { + return GetAttribute(color, defaultValue); + } + + QFont StyleHelper::GetFont() const + { + QFont font; + QFontInfo info(font); + info.pixelSize(); + + font.setFamily(GetAttribute(Attribute::FontFamily, font.family())); + font.setPixelSize(GetAttribute(Attribute::FontSize, info.pixelSize())); + font.setWeight(GetAttribute(Attribute::FontWeight, font.weight())); + font.setStyle(GetAttribute(Attribute::FontStyle, font.style())); + font.setCapitalization(GetAttribute(Attribute::FontVariant, font.capitalization())); + + return font; + } + + QString StyleHelper::GetFontStyleSheet() const + { + QFont font = GetFont(); + QColor color = GetColor(Styling::Attribute::Color); + + QStringList fields; + + fields.push_back(QString("color: rgba(%1,%2,%3,%4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha())); + + fields.push_back(QString("font-family: %1").arg(font.family())); + fields.push_back(QString("font-size: %1px").arg(font.pixelSize())); + + if (font.bold()) + { + fields.push_back("font-weight: bold"); + } + + switch (font.style()) + { + case QFont::StyleNormal: + break; + case QFont::StyleItalic: + fields.push_back("font-style: italic"); + break; + case QFont::StyleOblique: + fields.push_back("font-style: italic"); + break; + } + + const bool underline = font.underline(); + const bool strikeOut = font.strikeOut(); + + if (underline && strikeOut) + { + fields.push_back("text-decoration: underline line-through"); + } + else if (underline) + { + fields.push_back("text-decoration: underline"); + } + else if (strikeOut) + { + fields.push_back("text-decoration: line-through"); + } + + return fields.join("; "); + } + + QPen StyleHelper::GetPen(Styling::Attribute width, Styling::Attribute style, Styling::Attribute color, Styling::Attribute cap, bool cosmetic /*= false*/) const + { + QPen pen; + pen.setColor(GetAttribute(color, QColor(Qt::black))); + pen.setWidth(GetAttribute(width, 1)); + pen.setStyle(GetAttribute(style, Qt::SolidLine)); + pen.setCapStyle(GetAttribute(cap, Qt::SquareCap)); + pen.setCosmetic(cosmetic); + + return pen; + } + + QPen StyleHelper::GetBorder() const + { + return GetPen(Styling::Attribute::BorderWidth, Styling::Attribute::BorderStyle, Styling::Attribute::BorderColor, Styling::Attribute::CapStyle); + } + + QBrush StyleHelper::GetBrush(Styling::Attribute color, QBrush defaultValue /*= QBrush()*/) const + { + return GetAttribute(color, defaultValue); + } + + QSizeF StyleHelper::GetSize(QSizeF defaultSize) const + { + return{ + GetAttribute(Styling::Attribute::Width, defaultSize.width()), + GetAttribute(Styling::Attribute::Height, defaultSize.height()) + }; + } + + QSizeF StyleHelper::GetMinimumSize(QSizeF defaultSize /*= QSizeF(0,0)*/) const + { + return QSizeF(GetAttribute(Styling::Attribute::MinWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MinHeight, defaultSize.height())); + } + + QSizeF StyleHelper::GetMaximumSize(QSizeF defaultSize /*= QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)*/) const + { + return QSizeF(GetAttribute(Styling::Attribute::MaxWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MaxHeight, defaultSize.height())); + } + + QMarginsF StyleHelper::GetMargins(QMarginsF defaultMargins /*= QMarginsF()*/) const + { + bool hasMargin = false; + StyleRequestBus::EventResult(hasMargin, m_style, &StyleRequests::HasAttribute, static_cast(Styling::Attribute::Margin)); + + if (hasMargin) + { + qreal defaultMargin = GetAttribute(Styling::Attribute::Margin, 0); + defaultMargins = QMarginsF(defaultMargin, defaultMargin, defaultMargin, defaultMargin); + } + + return QMarginsF( + GetAttribute(Styling::Attribute::Margin/*TODO Left*/, defaultMargins.left()), + GetAttribute(Styling::Attribute::Margin/*TODO Top*/, defaultMargins.top()), + GetAttribute(Styling::Attribute::Margin/*TODO Right*/, defaultMargins.right()), + GetAttribute(Styling::Attribute::Margin/*TODO Bottom*/, defaultMargins.bottom()) + ); + } + + bool StyleHelper::HasTextAlignment() const + { + return HasAttribute(Styling::Attribute::TextAlignment) || HasAttribute(Styling::Attribute::TextVerticalAlignment); + } + + Qt::Alignment StyleHelper::GetTextAlignment(Qt::Alignment defaultAlignment) const + { + bool horizontalAlignment = HasAttribute(Styling::Attribute::TextAlignment); + bool verticalAlignment = HasAttribute(Styling::Attribute::TextVerticalAlignment); + + if (horizontalAlignment || verticalAlignment) + { + Qt::Alignment alignment = GetAttribute(Styling::Attribute::TextAlignment, Qt::AlignmentFlag::AlignLeft); + alignment = alignment | GetAttribute(Styling::Attribute::TextVerticalAlignment, Qt::AlignmentFlag::AlignTop); + + return alignment; + } + + return defaultAlignment; + } + + void StyleHelper::AddSelector(const AZStd::string_view& selector) + { + auto insertResult = m_styleSelectors.insert(AZStd::string(selector)); + + if (insertResult.second && m_styledEntity.IsValid()) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.data()); + + UpdateStyle(); + } + } + + void StyleHelper::RemoveSelector(const AZStd::string_view& selector) + { + AZStd::size_t elements = m_styleSelectors.erase(selector); + + if (elements > 0) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::RemoveSelectorState, selector.data()); + + UpdateStyle(); + } + } + + GraphCanvas::CandyStripeConfiguration StyleHelper::GetCandyStripeConfiguration() const + { + CandyStripeConfiguration config; + + config.m_initialOffset = GetAttribute(Styling::Attribute::StripeOffset, 0); + config.m_maximumSize = GetAttribute(Styling::Attribute::MaximumStripeSize, 10); + + if (config.m_maximumSize <= 0) + { + config.m_maximumSize = 1; + } + + config.m_minStripes = GetAttribute(Styling::Attribute::MinimumStripes, 2); + + if (config.m_minStripes <= 0) + { + config.m_minStripes = 1; + } + + config.m_stripeAngle = GetAttribute(Styling::Attribute::StripeAngle, 60); + + if (config.m_stripeAngle > 90) + { + config.m_stripeAngle = 89; + } + else if (config.m_stripeAngle < -90) + { + config.m_stripeAngle = -89; + } + + if (!HasAttribute(Styling::Attribute::StripeColor)) + { + QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0, 0, 0)); + + config.m_stripeColor = backgroundColor.darker(); + + int totalDifference = 0; + + totalDifference += backgroundColor.red() - config.m_stripeColor.red(); + totalDifference += backgroundColor.green() - config.m_stripeColor.green(); + totalDifference += backgroundColor.blue() - config.m_stripeColor.blue(); + + if (totalDifference < 150) + { + config.m_stripeColor = backgroundColor.lighter(); + } + } + else + { + config.m_stripeColor = GetAttribute(Styling::Attribute::StripeColor, QColor(0, 0, 0)); + } + + return config; + } + + GraphCanvas::PatternedFillGenerator StyleHelper::GetPatternedFillGenerator() const + { + PatternedFillGenerator generator; + generator.m_editorId = m_editorId; + + generator.m_id = GetAttribute(Styling::Attribute::PatternTemplate, QString()).toUtf8().data(); + + if (HasAttribute(Styling::Attribute::PatternPalettes)) + { + AZStd::string paletteStrings = GetAttribute(Styling::Attribute::PatternPalettes, QString()).toUtf8().data(); + + AzFramework::StringFunc::Tokenize(paletteStrings.c_str(), generator.m_palettes, ','); + } + else + { + QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0, 0, 0)); + + QColor patternColor = backgroundColor.darker(); + + int totalDifference = 0; + + totalDifference += backgroundColor.red() - patternColor.red(); + totalDifference += backgroundColor.green() - patternColor.green(); + totalDifference += backgroundColor.blue() - patternColor.blue(); + + if (totalDifference < 150) + { + patternColor = backgroundColor.lighter(); + } + + generator.m_colors.push_back(patternColor); + } + + generator.m_configuration = GetPatternFillConfiguration(); + + return generator; + } + + GraphCanvas::PatternFillConfiguration StyleHelper::GetPatternFillConfiguration() const + { + PatternFillConfiguration configuration; + + configuration.m_minimumTileRepetitions = GetAttribute(Styling::Attribute::MinimumRepetitions, 1); + configuration.m_evenRowOffsetPercent = GetAttribute(Styling::Attribute::EvenOffsetPercent, 0.0f); + configuration.m_oddRowOffsetPercent = GetAttribute(Styling::Attribute::OddOffsetPercent, 0.0f); + + return configuration; + } + + void StyleHelper::PopulatePaletteConfiguration(PaletteIconConfiguration& configuration) const + { + AZStd::string stylePalette; + StyledEntityRequestBus::EventResult(stylePalette, m_styledEntity, &StyledEntityRequests::GetFullStyleElement); + + if (!stylePalette.empty()) + { + configuration.SetColorPalette(stylePalette); + } + } + + void StyleHelper::UpdateStyle() + { + ReleaseStyle(false); + StyleManagerRequestBus::EventResult(m_style, m_editorId, &StyleManagerRequests::ResolveStyles, m_styledEntity); + } + + void StyleHelper::ReleaseStyle(bool destroyChildElement /*= true*/) + { + if (m_style.IsValid()) + { + if (m_deleteStyledEntity && destroyChildElement) + { + m_deleteStyledEntity = false; + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_styledEntity); + } + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_style); + + m_style.SetInvalid(); + } + } + + void StyleHelper::RegisterStyleSheetBus(const EditorId& editorId) + { + StyleManagerNotificationBus::Handler::BusDisconnect(); + StyleManagerNotificationBus::Handler::BusConnect(editorId); + } + +} +} diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h index cc7429f8fc..b500d4b197 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h @@ -8,7 +8,8 @@ #pragma once -AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option") +#if !defined(Q_MOC_RUN) + #include #include #include @@ -16,7 +17,6 @@ AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option") #include #include #include -AZ_POP_DISABLE_WARNING #include #include @@ -32,6 +32,8 @@ AZ_POP_DISABLE_WARNING #include #include +#endif + namespace GraphCanvas { namespace Styling @@ -45,124 +47,49 @@ namespace GraphCanvas AZ_CLASS_ALLOCATOR(StyleHelper, AZ::SystemAllocator, 0); StyleHelper() = default; - - StyleHelper(const AZ::EntityId& styledEntity) - { - SetStyle(styledEntity); - } - - StyleHelper(const AZ::EntityId& realStyledEntity, const AZStd::string& virtualChildElement) - { - SetStyle(realStyledEntity, virtualChildElement); - } - - virtual ~StyleHelper() - { - ReleaseStyle(); - } + StyleHelper(const AZ::EntityId& styledEntity); + StyleHelper(const AZ::EntityId& realStyledEntity, const AZStd::string& virtualChildElement); + virtual ~StyleHelper(); // StyleManagerNotificationBus - void OnStylesUnloaded() override - { - ReleaseStyle(); - } + void OnStylesUnloaded() override; //// - void SetEditorId(const EditorId& editorId) - { - if (m_editorId != editorId) - { - ReleaseStyle(); - m_editorId = editorId; + void SetEditorId(const EditorId& editorId); + void SetScene(const AZ::EntityId& sceneId); - RegisterStyleSheetBus(m_editorId); - } - } + void SetStyle(const AZStd::string& style); + void SetStyle(const AZ::EntityId& styledEntity); + void SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement); - // TODO: Get rid of this and m_scene once the OnSceneSet notification is removed (see below). - void SetScene(const AZ::EntityId& sceneId) - { - m_scene = sceneId; + bool HasAttribute(Styling::Attribute attribute) const; + void RemoveAttributeOverride(Styling::Attribute attribute); - EditorId editorId; - SceneRequestBus::EventResult(editorId, m_scene, &SceneRequests::GetEditorId); - SetEditorId(editorId); - } + QColor GetColor(Styling::Attribute color, QColor defaultValue = QColor()) const; + QFont GetFont() const; - void SetStyle(const AZ::EntityId& styledEntity) - { - ReleaseStyle(); + //! Helper method which constructs a stylesheet based on the calculated font style. + //! We need this too pass along to certain Qt widgets because we use our own custom style parsing system. + QString GetFontStyleSheet() const; - m_styledEntity = styledEntity; + QPen GetPen(Styling::Attribute width, Styling::Attribute style, Styling::Attribute color, Styling::Attribute cap, bool cosmetic = false) const; + QPen GetBorder() const; + QBrush GetBrush(Styling::Attribute color, QBrush defaultValue = QBrush()) const; + QSizeF GetSize(QSizeF defaultSize) const; + QSizeF GetMinimumSize(QSizeF defaultSize = QSizeF(0,0)) const; + QSizeF GetMaximumSize(QSizeF defaultSize = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) const; + QMarginsF GetMargins(QMarginsF defaultMargins = QMarginsF()) const; - AZ::EntityId sceneId; - SceneMemberRequestBus::EventResult(sceneId, m_styledEntity, &SceneMemberRequests::GetScene); - if (!sceneId.IsValid()) - { - return; - } + bool HasTextAlignment() const; + Qt::Alignment GetTextAlignment(Qt::Alignment defaultAlignment) const; - SetScene(sceneId); + void AddSelector(const AZStd::string_view& selector); + void RemoveSelector(const AZStd::string_view& selector); - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } - - UpdateStyle(); - -#if 0 - AZStd::string description; - StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); - qDebug() << description.c_str(); -#endif - } - - void SetStyle(const AZStd::string& style) - { - ReleaseStyle(); - - m_deleteStyledEntity = true; - - PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateStyleEntity, style); - - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } - - // TODO: remove/replace OnSceneSet and fix any systems/components listening for that event. - SceneMemberNotificationBus::Event(m_styledEntity, &SceneMemberNotifications::OnSceneSet, m_scene); - - UpdateStyle(); - } - - void SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement) - { - ReleaseStyle(); - - m_deleteStyledEntity = true; - - AZ::EntityId sceneId; - SceneMemberRequestBus::EventResult(sceneId, parentStyledEntity, &SceneMemberRequests::GetScene); - - SetScene(sceneId); - - PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateVirtualChild, parentStyledEntity, virtualChildElement); - - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } - - UpdateStyle(); - -#if 0 - AZStd::string description; - StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); - qDebug() << description.c_str(); -#endif - } + CandyStripeConfiguration GetCandyStripeConfiguration() const; + PatternedFillGenerator GetPatternedFillGenerator() const; + PatternFillConfiguration GetPatternFillConfiguration() const; + void PopulatePaletteConfiguration(PaletteIconConfiguration& configuration) const; template void AddAttributeOverride(Styling::Attribute attribute, const Value& defaultValue = Value()) @@ -170,23 +97,6 @@ namespace GraphCanvas m_attributeOverride[attribute] = QVariant(defaultValue); } - void RemoveAttributeOverride(Styling::Attribute attribute) - { - m_attributeOverride.erase(attribute); - } - - bool HasAttribute(Styling::Attribute attribute) const - { - bool hasAttribute = (m_attributeOverride.find(attribute) != m_attributeOverride.end()); - - if (!hasAttribute) - { - StyleRequestBus::EventResult(hasAttribute, m_style, &StyleRequests::HasAttribute, static_cast(attribute)); - } - - return hasAttribute; - } - template Value GetAttribute(Styling::Attribute attribute, const Value& defaultValue = Value()) const { @@ -202,7 +112,7 @@ namespace GraphCanvas { bool hasAttribute = false; auto rawAttribute = static_cast(attribute); - + StyleRequestBus::EventResult(hasAttribute, m_style, &StyleRequests::HasAttribute, rawAttribute); if (hasAttribute) @@ -217,323 +127,11 @@ namespace GraphCanvas return retVal; } - QColor GetColor(Styling::Attribute color, QColor defaultValue = QColor()) const - { - return GetAttribute(color, defaultValue); - } - - QFont GetFont() const - { - QFont font; - QFontInfo info(font); - info.pixelSize(); - - font.setFamily(GetAttribute(Attribute::FontFamily, font.family())); - font.setPixelSize(GetAttribute(Attribute::FontSize, info.pixelSize())); - font.setWeight(GetAttribute(Attribute::FontWeight, font.weight())); - font.setStyle(GetAttribute(Attribute::FontStyle, font.style())); - font.setCapitalization(GetAttribute(Attribute::FontVariant, font.capitalization())); - - return font; - } - - //! Helper method which constructs a stylesheet based on the calculated font style. - //! We need this too pass along to certain Qt widgets because we use our own custom style parsing system. - QString GetFontStyleSheet() const - { - QFont font = GetFont(); - QColor color = GetColor(Styling::Attribute::Color); - - QStringList fields; - - fields.push_back(QString("color: rgba(%1,%2,%3,%4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha())); - - fields.push_back(QString("font-family: %1").arg(font.family())); - fields.push_back(QString("font-size: %1px").arg(font.pixelSize())); - - if (font.bold()) - { - fields.push_back("font-weight: bold"); - } - - switch (font.style()) - { - case QFont::StyleNormal: - break; - case QFont::StyleItalic: - fields.push_back("font-style: italic"); - break; - case QFont::StyleOblique: - fields.push_back("font-style: italic"); - break; - } - - const bool underline = font.underline(); - const bool strikeOut = font.strikeOut(); - - if (underline && strikeOut) - { - fields.push_back("text-decoration: underline line-through"); - } - else if (underline) - { - fields.push_back("text-decoration: underline"); - } - else if (strikeOut) - { - fields.push_back("text-decoration: line-through"); - } - - return fields.join("; "); - } - - QPen GetPen(Styling::Attribute width, Styling::Attribute style, Styling::Attribute color, Styling::Attribute cap, bool cosmetic = false) const - { - QPen pen; - pen.setColor(GetAttribute(color, QColor(Qt::black))); - pen.setWidth(GetAttribute(width, 1)); - pen.setStyle(GetAttribute(style, Qt::SolidLine)); - pen.setCapStyle(GetAttribute(cap, Qt::SquareCap)); - pen.setCosmetic(cosmetic); - - return pen; - } - - QPen GetBorder() const - { - return GetPen(Styling::Attribute::BorderWidth, Styling::Attribute::BorderStyle, Styling::Attribute::BorderColor, Styling::Attribute::CapStyle); - } - - QBrush GetBrush(Styling::Attribute color, QBrush defaultValue = QBrush()) const - { - return GetAttribute(color, defaultValue); - } - - QSizeF GetSize(QSizeF defaultSize) const - { - return{ - GetAttribute(Styling::Attribute::Width, defaultSize.width()), - GetAttribute(Styling::Attribute::Height, defaultSize.height()) - }; - } - - QSizeF GetMinimumSize(QSizeF defaultSize = QSizeF(0,0)) const - { - return QSizeF(GetAttribute(Styling::Attribute::MinWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MinHeight, defaultSize.height())); - } - - QSizeF GetMaximumSize(QSizeF defaultSize = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) const - { - return QSizeF(GetAttribute(Styling::Attribute::MaxWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MaxHeight, defaultSize.height())); - } - - QMarginsF GetMargins(QMarginsF defaultMargins = QMarginsF()) const - { - bool hasMargin = false; - StyleRequestBus::EventResult(hasMargin, m_style, &StyleRequests::HasAttribute, static_cast(Styling::Attribute::Margin)); - - if (hasMargin) - { - qreal defaultMargin = GetAttribute(Styling::Attribute::Margin, 0); - defaultMargins = QMarginsF(defaultMargin, defaultMargin, defaultMargin, defaultMargin); - } - - return QMarginsF( - GetAttribute(Styling::Attribute::Margin/*TODO Left*/, defaultMargins.left()), - GetAttribute(Styling::Attribute::Margin/*TODO Top*/, defaultMargins.top()), - GetAttribute(Styling::Attribute::Margin/*TODO Right*/, defaultMargins.right()), - GetAttribute(Styling::Attribute::Margin/*TODO Bottom*/, defaultMargins.bottom()) - ); - } - - bool HasTextAlignment() const - { - return HasAttribute(Styling::Attribute::TextAlignment) || HasAttribute(Styling::Attribute::TextVerticalAlignment); - } - - Qt::Alignment GetTextAlignment(Qt::Alignment defaultAlignment) const - { - bool horizontalAlignment = HasAttribute(Styling::Attribute::TextAlignment); - bool verticalAlignment = HasAttribute(Styling::Attribute::TextVerticalAlignment); - - if (horizontalAlignment || verticalAlignment) - { - Qt::Alignment alignment = GetAttribute(Styling::Attribute::TextAlignment, Qt::AlignmentFlag::AlignLeft); - alignment = alignment | GetAttribute(Styling::Attribute::TextVerticalAlignment, Qt::AlignmentFlag::AlignTop); - - return alignment; - } - - return defaultAlignment; - } - - void AddSelector(const AZStd::string_view& selector) - { - auto insertResult = m_styleSelectors.insert(AZStd::string(selector)); - - if (insertResult.second && m_styledEntity.IsValid()) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.data()); - - UpdateStyle(); - } - } - - void RemoveSelector(const AZStd::string_view& selector) - { - AZStd::size_t elements = m_styleSelectors.erase(selector); - - if (elements > 0) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::RemoveSelectorState, selector.data()); - - UpdateStyle(); - } - } - - CandyStripeConfiguration GetCandyStripeConfiguration() const - { - CandyStripeConfiguration config; - - config.m_initialOffset = GetAttribute(Styling::Attribute::StripeOffset, 0); - config.m_maximumSize = GetAttribute(Styling::Attribute::MaximumStripeSize, 10); - - if (config.m_maximumSize <= 0) - { - config.m_maximumSize = 1; - } - - config.m_minStripes = GetAttribute(Styling::Attribute::MinimumStripes, 2); - - if (config.m_minStripes <= 0) - { - config.m_minStripes = 1; - } - - config.m_stripeAngle = GetAttribute(Styling::Attribute::StripeAngle, 60); - - if (config.m_stripeAngle > 90) - { - config.m_stripeAngle = 89; - } - else if (config.m_stripeAngle < -90) - { - config.m_stripeAngle = -89; - } - - if (!HasAttribute(Styling::Attribute::StripeColor)) - { - QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0,0,0)); - - config.m_stripeColor = backgroundColor.darker(); - - int totalDifference = 0; - - totalDifference += backgroundColor.red() - config.m_stripeColor.red(); - totalDifference += backgroundColor.green() - config.m_stripeColor.green(); - totalDifference += backgroundColor.blue() - config.m_stripeColor.blue(); - - if (totalDifference < 150) - { - config.m_stripeColor = backgroundColor.lighter(); - } - } - else - { - config.m_stripeColor = GetAttribute(Styling::Attribute::StripeColor, QColor(0, 0, 0)); - } - - return config; - } - - PatternedFillGenerator GetPatternedFillGenerator() const - { - PatternedFillGenerator generator; - generator.m_editorId = m_editorId; - - generator.m_id = GetAttribute(Styling::Attribute::PatternTemplate, QString()).toUtf8().data(); - - if (HasAttribute(Styling::Attribute::PatternPalettes)) - { - AZStd::string paletteStrings = GetAttribute(Styling::Attribute::PatternPalettes, QString()).toUtf8().data(); - - AzFramework::StringFunc::Tokenize(paletteStrings.c_str(), generator.m_palettes, ','); - } - else - { - QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0, 0, 0)); - - QColor patternColor = backgroundColor.darker(); - - int totalDifference = 0; - - totalDifference += backgroundColor.red() - patternColor.red(); - totalDifference += backgroundColor.green() - patternColor.green(); - totalDifference += backgroundColor.blue() - patternColor.blue(); - - if (totalDifference < 150) - { - patternColor = backgroundColor.lighter(); - } - - generator.m_colors.push_back(patternColor); - } - - generator.m_configuration = GetPatternFillConfiguration(); - - return generator; - } - - PatternFillConfiguration GetPatternFillConfiguration() const - { - PatternFillConfiguration configuration; - - configuration.m_minimumTileRepetitions = GetAttribute(Styling::Attribute::MinimumRepetitions, 1); - configuration.m_evenRowOffsetPercent = GetAttribute(Styling::Attribute::EvenOffsetPercent, 0.0f); - configuration.m_oddRowOffsetPercent = GetAttribute(Styling::Attribute::OddOffsetPercent, 0.0f); - - return configuration; - } - - void PopulatePaletteConfiguration(PaletteIconConfiguration& configuration) const - { - AZStd::string stylePalette; - StyledEntityRequestBus::EventResult(stylePalette, m_styledEntity, &StyledEntityRequests::GetFullStyleElement); - - if (!stylePalette.empty()) - { - configuration.SetColorPalette(stylePalette); - } - } - private: - void UpdateStyle() - { - ReleaseStyle(false); - StyleManagerRequestBus::EventResult(m_style, m_editorId, &StyleManagerRequests::ResolveStyles, m_styledEntity); - } - - void ReleaseStyle(bool destroyChildElement = true) - { - if (m_style.IsValid()) - { - if (m_deleteStyledEntity && destroyChildElement) - { - m_deleteStyledEntity = false; - AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_styledEntity); - } - AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_style); - - m_style.SetInvalid(); - } - } - - void RegisterStyleSheetBus(const EditorId& editorId) - { - StyleManagerNotificationBus::Handler::BusDisconnect(); - StyleManagerNotificationBus::Handler::BusConnect(editorId); - } + void UpdateStyle(); + void ReleaseStyle(bool destroyChildElement = true); + void RegisterStyleSheetBus(const EditorId& editorId); EditorId m_editorId; AZ::EntityId m_scene; diff --git a/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake b/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake index 58a28484f7..bedc6329d5 100644 --- a/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake +++ b/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake @@ -91,6 +91,7 @@ set(FILES StaticLib/GraphCanvas/Styling/Style.cpp StaticLib/GraphCanvas/Styling/Style.h StaticLib/GraphCanvas/Styling/StyleHelper.h + StaticLib/GraphCanvas/Styling/StyleHelper.cpp StaticLib/GraphCanvas/Styling/StyleManager.cpp StaticLib/GraphCanvas/Styling/StyleManager.h StaticLib/GraphCanvas/Types/ComponentSaveDataInterface.h diff --git a/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h b/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h index 101b123bb5..6129c3ec08 100644 --- a/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h +++ b/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h @@ -18,8 +18,7 @@ namespace ImGui namespace LYImGuiUtils { /** - * A small class to help manage values for an ImGui Histogram ( ImGui doesn't want to manage the values itself ). - * Nothing crazy, just helps reduce boiler plate if you are ImGui::PlotHistogram()'ing + * A small class to help manage values for an ImGui Histogram (ImGui is not managing values itself). */ class HistogramContainer { @@ -40,9 +39,24 @@ namespace ImGui // Static Type to String function static const char* ViewTypeToString(ViewType viewType); + //! Horizontal move direction of the histogram when pushing new values. + enum MoveDirection : AZ::u8 + { + PushLeftMoveRight = 0, //! Push new values to the front of the buffer, which corresponds to the left side, and make the histogram move to the right. + PushRightMoveLeft = 1, //! Push new values to the back of the buffer, which corresponds to the right side, and make the histogram move to the left. + }; + + //! Mode determining the min and max values for the visible range of the vertical axis for the histogram. + enum ScaleMode : AZ::u8 + { + NoAutoScale = 0, //! Use the min and max values given by Init() as visible range. + AutoExpand = 1, //! Expand scale in case a sample is out of the current bounds. Does only expand the scale but not decrease it back again. + AutoScale = 2, //! Use a running average to expand and shrink the visible range. + }; + // Do all of the set up via Init - void Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale - , bool autoExpandScale, bool startCollapsed = false, bool drawMostRecentValue = true); + void Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale, + ScaleMode scaleMode = AutoScale, bool startCollapsed = false, bool drawMostRecentValue = true); // How many values are in the container currently int GetSize() { return static_cast(m_values.size()); } @@ -50,9 +64,6 @@ namespace ImGui // What is the max size of the container int GetMaxSize() { return m_maxSize; } - // Set the Max Size and clear the container - void SetMaxSize(int size) { m_values.clear(); m_maxSize = size; } - // Push a value to this histogram container void PushValue(float val); @@ -65,7 +76,18 @@ namespace ImGui // Draw this histogram with ImGui void Draw(float histogramWidth, float histogramHeight); + //! Adjust the scale mode to determine the min and max values for the visible range of the vertical axis for the histogram. + void SetScaleMode(ScaleMode scaleMode) { m_scaleMode = scaleMode; } + + //! Adjust the horizontal move direction of the histogram when pushing new values. + void SetMoveDirection(MoveDirection moveDirection) { m_moveDirection = moveDirection; } + + //! Calculate the min and maximum values for the present samples. + void CalcMinMaxValues(float& outMin, float& outMax); + private: + // Set the Max Size and clear the container + void SetMaxSize(int size); AZStd::string m_histogramName; AZStd::deque m_values; @@ -73,8 +95,10 @@ namespace ImGui ViewType m_viewType = ViewType::Histogram; float m_minScale; float m_maxScale; + MoveDirection m_moveDirection = PushLeftMoveRight; //! Specify if values will be added on the left and the histogram moves right or the other way around. bool m_dispalyOverlays; - bool m_autoExpandScale; + ScaleMode m_scaleMode; //! Determines if the vertical range of the histogram will be manually specified, auto-expanded or automatically scaled based on the samples. + float m_autoScaleSpeed = 0.05f; //! Indicates how fast the min max values and the visible vertical range are adapting to new samples. bool m_collapsed; bool m_drawMostRecentValueText; }; diff --git a/Gems/ImGui/Code/Source/ImGuiManager.cpp b/Gems/ImGui/Code/Source/ImGuiManager.cpp index 6fc1ea8b71..d631e2f83e 100644 --- a/Gems/ImGui/Code/Source/ImGuiManager.cpp +++ b/Gems/ImGui/Code/Source/ImGuiManager.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -31,11 +32,11 @@ using namespace AzFramework; using namespace ImGui; // Wheel Delta const value. -const constexpr uint32_t IMGUI_WHEEL_DELTA = 120; // From WinUser.h, for Linux +static const constexpr uint32_t IMGUI_WHEEL_DELTA = 120; // From WinUser.h, for Linux // Typedef and local static map to hold LyInput->ImGui Nav mappings ( filled up in Initialize() ) -typedef AZStd::pair LyButtonImGuiNavIndexPair; -typedef AZStd::unordered_map LyButtonImGuiNavIndexMap; +using LyButtonImGuiNavIndexPair = AZStd::pair; +using LyButtonImGuiNavIndexMap = AZStd::fixed_unordered_map; static LyButtonImGuiNavIndexMap s_lyInputToImGuiNavIndexMap; /** diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp index 1df74903e6..e079a01de1 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp @@ -34,13 +34,13 @@ namespace ImGui ImGuiCameraMonitorRequestBus::Handler::BusConnect(); // Init Histogram Containers - m_dofMinZHisto.Init( "DOF Min Z", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); - m_dofMinZBlendMultHisto.Init( "DOF Min Z Blend Mult", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f, true); - m_dofMinZScaleHisto.Init( "DOF Min Z Scale", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f, true); + m_dofMinZHisto.Init( "DOF Min Z", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); + m_dofMinZBlendMultHisto.Init( "DOF Min Z Blend Mult", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f); + m_dofMinZScaleHisto.Init( "DOF Min Z Scale", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f); - m_globalActiveCamInfo.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f, true); - m_globalActiveCamInfo.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); - m_globalActiveCamInfo.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); + m_globalActiveCamInfo.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f); + m_globalActiveCamInfo.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); + m_globalActiveCamInfo.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); } void ImGuiLYCameraMonitor::Shutdown() @@ -214,7 +214,7 @@ namespace ImGui } // save this cam off as the current one m_currentCamera = newCamId; - + // create a new empty CameraInfo in the queue m_cameraHistory.push_front(CameraInfo()); @@ -224,9 +224,9 @@ namespace ImGui AZ::ComponentApplicationBus::BroadcastResult(newCam.m_camName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_currentCamera); newCam.m_activeTime = 0.0f; newCam.m_activeFrames = 0; - newCam.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f, true); - newCam.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); - newCam.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); + newCam.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f); + newCam.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); + newCam.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); // reset a few variables on the global camera info m_globalActiveCamInfo.m_camId = newCam.m_camId; diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp index 28a5a30e9c..81ec4b17c8 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp @@ -199,20 +199,6 @@ namespace ImGui } } - // Lod Min - static ICVar* eLodMinCVAR = gEnv->pConsole->GetCVar("e_LodMin"); - if (eLodMinCVAR) - { - int minLodValue = eLodMinCVAR->GetIVal(); - int dragIntVal = minLodValue; - ImGui::Text("e_LodMin: %d ( Force a lowest LOD level )", minLodValue); - ImGui::SliderInt("##LodMin", &dragIntVal, 0, 5); - if (dragIntVal != minLodValue) - { - eLodMinCVAR->Set(dragIntVal); - } - } - // Texel Density static ICVar* eTexelDensityCVAR = gEnv->pConsole->GetCVar("e_texeldensity"); if (eTexelDensityCVAR) diff --git a/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp b/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp index e3fd3f2054..1915cc3721 100644 --- a/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp +++ b/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp @@ -16,45 +16,92 @@ namespace ImGui { namespace LYImGuiUtils { - void HistogramContainer::Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale - , bool autoExpandScale, bool startCollapsed/* = false*/, bool drawMostRecentValue/* = true*/) + void HistogramContainer::Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale, + ScaleMode scaleMode, bool startCollapsed/* = false*/, bool drawMostRecentValue/* = true*/) { m_histogramName = histogramName; m_minScale = minScale; m_maxScale = maxScale; m_viewType = viewType; m_dispalyOverlays = displayOverlays; - m_autoExpandScale = autoExpandScale; + m_scaleMode = scaleMode; m_collapsed = startCollapsed; m_drawMostRecentValueText = drawMostRecentValue; SetMaxSize(maxValueCountSize); } - void HistogramContainer::PushValue(float val) + void HistogramContainer::SetMaxSize(int size) + { + m_values.resize(size); + m_maxSize = size; + + // Pre-fill the histogram with zeros so that the bars do not fill the space and + // scale horizontally when there are not enough samples yet. + for (float& value : m_values) + { + value = 0.0f; + } + } + + void HistogramContainer::PushValue(float value) { if (m_maxSize == 0) { return; } + if (m_values.size() == m_maxSize) { - m_values.pop_back(); + if (m_moveDirection == PushLeftMoveRight) + { + m_values.pop_back(); + } + else + { + m_values.pop_front(); + } } else if (m_values.size() > m_maxSize) { m_values.erase(m_values.begin() + (m_maxSize - 1), m_values.end()); } - m_values.push_front(val); - if (m_autoExpandScale) + if (m_moveDirection == PushLeftMoveRight) { - if (val < m_minScale) + m_values.push_front(value); + } + else + { + m_values.push_back(value); + } + + switch (m_scaleMode) + { + case AutoExpand: { - m_minScale = val; + if (value < m_minScale) + { + m_minScale = value; + } + else if (value > m_maxScale) + { + m_maxScale = value; + } + break; } - else if (val > m_maxScale) + case AutoScale: { - m_maxScale = val; + float min = 0.0f; + float max = 0.0f; + CalcMinMaxValues(min, max); + + m_minScale = AZ::Lerp(m_minScale, min, m_autoScaleSpeed); + m_maxScale = AZ::Lerp(m_maxScale, max, m_autoScaleSpeed); + break; + } + default: + { + break; } } } @@ -86,7 +133,6 @@ namespace ImGui ImGui::DragInt("History Size", &m_maxSize, 1, 1, 1000, "%f"); ImGui::DragFloat("Max Scale", &m_maxScale, 0.0001f, -100.0f, 100.0f); ImGui::DragFloat("Min Scale", &m_minScale, 0.0001f, -100.0f, 100.0f); - ImGui::Checkbox("Auto Expand Scale", &m_autoExpandScale); ImGui::EndPopup(); } @@ -157,6 +203,26 @@ namespace ImGui return "Lines"; } } + + void HistogramContainer::CalcMinMaxValues(float& outMin, float& outMax) + { + // Use the manually set min and max scale values in case there are no samples. + if (m_values.empty()) + { + outMin = m_minScale; + outMax = m_maxScale; + return; + } + + outMin = +AZ::Constants::FloatMax; + outMax = -AZ::Constants::FloatMax; + + for (const float x : m_values) + { + outMin = AZ::GetMin(outMin, x); + outMax = AZ::GetMax(outMax, x); + } + } } } #endif // #ifdef IMGUI_ENABLED diff --git a/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationAreaComponent.cpp b/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationAreaComponent.cpp index 4a290443d0..f723918cf3 100644 --- a/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationAreaComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationAreaComponent.cpp @@ -51,7 +51,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NavigationArea.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/NavigationArea.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/nav-area/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ai/nav-area/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::CheckBox, &EditorNavigationAreaComponent::m_exclusion, "Exclusion", "Does this area add or subtract from the Navigation Mesh") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorNavigationAreaComponent::OnNavigationAreaChanged) diff --git a/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationSeedComponent.cpp b/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationSeedComponent.cpp index 975eb17da1..47999dbd4b 100644 --- a/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationSeedComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Ai/EditorNavigationSeedComponent.cpp @@ -36,7 +36,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NavigationSeed.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/NavigationSeed.svg") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/nav-seed/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ai/nav-seed/") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorNavigationSeedComponent::m_agentType, "Agent Type", "Describes the type of the Entity for navigation purposes.") ->Attribute(AZ::Edit::Attributes::StringList, &PopulateAgentTypeList) ->Attribute("ChangeNotify", &EditorNavigationSeedComponent::OnAgentTypeChanged); diff --git a/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.cpp b/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.cpp index 9897895dcd..500d5c17da 100644 --- a/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.cpp @@ -125,7 +125,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Navigation.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/navigation/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ai/navigation/") ->DataElement(AZ::Edit::UIHandlers::Default, &NavigationComponent::m_agentSpeed, "Agent Speed", "The speed of the agent while navigating ") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &NavigationComponent::m_agentType, "Agent Type", diff --git a/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp b/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp index 97f8dc60a2..6c929bd3c4 100644 --- a/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp @@ -33,7 +33,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Comment.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c), AZ_CRC("Layer", 0xe4db211a) })) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/comment/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/editor/comment/") ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorCommentComponent::m_comment,"", "Comment") ->Attribute(AZ_CRC("PlaceholderText", 0xa23ec278), "Add comment text here"); } diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorTagComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/EditorTagComponent.cpp index fa98601a11..90fc0db437 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorTagComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorTagComponent.cpp @@ -38,7 +38,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Tag.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Tag.svg") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/tag/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/tag/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorTagComponent::m_tags, "Tags", "The tags that will be on this entity by default") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorTagComponent::OnTagChanged); } diff --git a/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp index e278f18e8b..29c97cdde1 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp @@ -204,7 +204,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/SimpleState.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/SimpleState.svg") - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/simple-state/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/simple-state/") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SimpleStateComponent::m_initialStateName, "Initial state", "The initial active state") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c)) ->Attribute(AZ::Edit::Attributes::StringList, &SimpleStateComponent::GetStateNames) diff --git a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp index a776a01ccc..4f6089ba16 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp @@ -166,7 +166,7 @@ namespace LmbrCentral return intersection; } - const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance) > 0; + const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance); return intersection; } diff --git a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp index 8eaf03457f..1c4a94ced3 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp @@ -153,7 +153,7 @@ namespace LmbrCentral m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_diskShapeConfig); return AZ::Intersect::IntersectRayDisk( - src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance) > 0; + src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance); } void DiskShape::DiskIntersectionDataCache::UpdateIntersectionParamsImpl( diff --git a/Gems/LyShine/Code/Source/Animation/AnimNode.h b/Gems/LyShine/Code/Source/Animation/AnimNode.h index 0076276204..4d139e99ca 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimNode.h +++ b/Gems/LyShine/Code/Source/Animation/AnimNode.h @@ -13,6 +13,7 @@ #include #include "UiAnimationSystem.h" +#include /*! @@ -39,7 +40,7 @@ public: , valueType(_valueType) , flags(_flags) {}; - AZStd::string name; // parameter name. + AZStd::basic_string, AZStd::stateless_allocator> name; // parameter name. CUiAnimParamType paramType; // parameter id. EUiAnimValue valueType; // value type, defines type of track to use for animating this parameter. ESupportedParamFlags flags; // combination of flags from ESupportedParamFlags. diff --git a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp index 012eaacd1b..ab45042b4c 100644 --- a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp +++ b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp @@ -14,9 +14,11 @@ #include "UiAnimSerialize.h" #include +#include +#include +#include #include -#include #include #include #include @@ -25,22 +27,31 @@ #include ////////////////////////////////////////////////////////////////////////// -// Serialization for anim nodes & param types -#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.find(eUiAnimNodeType_ ## name) == g_animNodeEnumToStringMap.end()); \ - g_animNodeEnumToStringMap[eUiAnimNodeType_ ## name] = AZ_STRINGIZE(name); \ - g_animNodeStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; +namespace +{ + using UiAnimParamSystemString = AZStd::basic_string, AZStd::stateless_allocator>; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.find(eUiAnimParamType_ ## name) == g_animParamEnumToStringMap.end()); \ + template > + using UiAnimSystemOrderedMap = AZStd::map; + template , typename EqualKey = AZStd::equal_to> + using UiAnimSystemUnorderedMap = AZStd::unordered_map; +} +// Serialization for anim nodes & param types +#define REGISTER_NODE_TYPE(name) assert(!g_animNodeEnumToStringMap.contains(eUiAnimNodeType_ ## name)); \ + g_animNodeEnumToStringMap[eUiAnimNodeType_ ## name] = AZ_STRINGIZE(name); \ + g_animNodeStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; + +#define REGISTER_PARAM_TYPE(name) assert(!g_animParamEnumToStringMap.contains(eUiAnimParamType_ ## name)); \ g_animParamEnumToStringMap[eUiAnimParamType_ ## name] = AZ_STRINGIZE(name); \ - g_animParamStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = eUiAnimParamType_ ## name; + g_animParamStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimParamType_ ## name; namespace { - AZStd::unordered_map g_animNodeEnumToStringMap; - StaticInstance >> g_animNodeStringToEnumMap; + UiAnimSystemUnorderedMap g_animNodeEnumToStringMap; + UiAnimSystemOrderedMap> g_animNodeStringToEnumMap; - AZStd::unordered_map g_animParamEnumToStringMap; - StaticInstance >> g_animParamStringToEnumMap; + UiAnimSystemUnorderedMap g_animParamEnumToStringMap; + UiAnimSystemOrderedMap> g_animParamStringToEnumMap; // If you get an assert in this function, it means two node types have the same enum value. void RegisterNodeTypes() diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp index 7e32301b71..b535033351 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp @@ -8,6 +8,7 @@ #include +#include #include "AnimPostFXNode.h" #include "AnimSplineTrack.h" #include "CompoundSplineTrack.h" @@ -38,7 +39,7 @@ public: virtual void GetDefault(bool& val) const = 0; virtual void GetDefault(Vec4& val) const = 0; - AZStd::string m_name; + AZStd::basic_string, AZStd::stateless_allocator> m_name; protected: virtual ~CControlParamBase(){} diff --git a/Gems/Maestro/Code/Source/Cinematics/Movie.cpp b/Gems/Maestro/Code/Source/Cinematics/Movie.cpp index 3654af52dd..2a9a231177 100644 --- a/Gems/Maestro/Code/Source/Cinematics/Movie.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/Movie.cpp @@ -8,6 +8,9 @@ #include +#include +#include +#include #include #include #include "Movie.h" @@ -73,22 +76,32 @@ static SMovieSequenceAutoComplete s_movieSequenceAutoComplete; #endif ////////////////////////////////////////////////////////////////////////// -// Serialization for anim nodes & param types -#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.find(AnimNodeType::name) == g_animNodeEnumToStringMap.end()); \ - g_animNodeEnumToStringMap[AnimNodeType::name] = AZ_STRINGIZE(name); \ - g_animNodeStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = AnimNodeType::name; +namespace +{ + using AnimParamSystemString = AZStd::basic_string, AZStd::stateless_allocator>; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.find(AnimParamType::name) == g_animParamEnumToStringMap.end()); \ - g_animParamEnumToStringMap[AnimParamType::name] = AZ_STRINGIZE(name); \ - g_animParamStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = AnimParamType::name; + template > + using AnimSystemOrderedMap = AZStd::map; + template , typename EqualKey = AZStd::equal_to> + using AnimSystemUnorderedMap = AZStd::unordered_map; +} + +// Serialization for anim nodes & param types +#define REGISTER_NODE_TYPE(name) assert(!g_animNodeEnumToStringMap.contains(AnimNodeType::name)); \ + g_animNodeEnumToStringMap[AnimNodeType::name] = AZ_STRINGIZE(name); \ + g_animNodeStringToEnumMap[AnimParamSystemString(AZ_STRINGIZE(name))] = AnimNodeType::name; + +#define REGISTER_PARAM_TYPE(name) assert(!g_animParamEnumToStringMap.contains(AnimParamType::name)); \ + g_animParamEnumToStringMap[AnimParamType::name] = AZ_STRINGIZE(name); \ + g_animParamStringToEnumMap[AnimParamSystemString(AZ_STRINGIZE(name))] = AnimParamType::name; namespace { - AZStd::unordered_map g_animNodeEnumToStringMap; - StaticInstance >> g_animNodeStringToEnumMap; + AnimSystemUnorderedMap g_animNodeEnumToStringMap; + AnimSystemOrderedMap> g_animNodeStringToEnumMap; - AZStd::unordered_map g_animParamEnumToStringMap; - StaticInstance >> g_animParamStringToEnumMap; + AnimSystemUnorderedMap g_animParamEnumToStringMap; + AnimSystemOrderedMap> g_animParamStringToEnumMap; // If you get an assert in this function, it means two node types have the same enum value. void RegisterNodeTypes() diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 32af39a43b..19289e1e42 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -48,6 +48,7 @@ namespace Multiplayer using NotifyClientMigrationEvent = AZ::Event; using NotifyEntityMigrationEvent = AZ::Event; using ConnectionAcquiredEvent = AZ::Event; + using ServerAcceptanceReceivedEvent = AZ::Event<>; using SessionInitEvent = AZ::Event; using SessionShutdownEvent = AZ::Event; @@ -122,6 +123,10 @@ namespace Multiplayer //! @param handler The ConnectionAcquiredEvent Handler to add virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0; + //! Adds a ServerAcceptanceReceived Handler which is invoked when the client receives the accept packet from the server. + //! @param handler The ServerAcceptanceReceived Handler to add + virtual void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) = 0; + //! Adds a SessionInitEvent Handler which is invoked when a new network session starts. //! @param handler The SessionInitEvent Handler to add virtual void AddSessionInitHandler(SessionInitEvent::Handler& handler) = 0; diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp new file mode 100644 index 0000000000..7c38a340eb --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "MultiplayerDebugHierarchyReporter.h" + +#include +#include +#include +#include +#include +#include + +#if defined(IMGUI_ENABLED) +#include +#endif + +namespace Multiplayer +{ + MultiplayerDebugHierarchyReporter::MultiplayerDebugHierarchyReporter() + : m_updateDebugOverlay([this]() { UpdateDebugOverlay(); }, AZ::Name("UpdateHierarchyDebug")) + { + CollectHierarchyRoots(); + + AZ::EntitySystemBus::Handler::BusConnect(); + m_updateDebugOverlay.Enqueue(AZ::TimeMs{ 0 }, true); + } + + MultiplayerDebugHierarchyReporter::~MultiplayerDebugHierarchyReporter() + { + AZ::EntitySystemBus::Handler::BusDisconnect(); + } + + // -------------------------------------------------------------------------------------------- + void MultiplayerDebugHierarchyReporter::OnImGuiUpdate() + { +#if defined(IMGUI_ENABLED) + ImGui::Text("Hierarchies"); + ImGui::Separator(); + + for (const auto& root : m_hierarchyRoots) + { + if (const auto* rootComponent = root.second.m_rootComponent) + { + if (rootComponent->IsHierarchicalRoot()) + { + const AZStd::vector& hierarchicalChildren = rootComponent->GetHierarchicalEntities(); + + if (ImGui::TreeNode(rootComponent->GetEntity()->GetName().c_str(), + "[%s] %4zu members", + rootComponent->GetEntity()->GetName().c_str(), + hierarchicalChildren.size())) + { + ImGui::Separator(); + ImGui::Columns(4, "hierarchy_columns"); + ImGui::Text("EntityId"); + ImGui::NextColumn(); + ImGui::Text("NetEntityId"); + ImGui::NextColumn(); + ImGui::Text("Entity Name"); + ImGui::NextColumn(); + ImGui::Text("Role"); + ImGui::NextColumn(); + + ImGui::Separator(); + ImGui::Columns(4, "hierarchy child info"); + + bool firstEntity = true; + for (const AZ::Entity* entity : hierarchicalChildren) + { + ImGui::Text("%s", entity->GetId().ToString().c_str()); + ImGui::NextColumn(); + ImGui::Text("%u", GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId())); + ImGui::NextColumn(); + ImGui::Text("%s", entity->GetName().c_str()); + ImGui::NextColumn(); + + if (firstEntity) + { + ImGui::Text("Root node"); + } + else if (entity->FindComponent()) + { + ImGui::Text("Inner root node"); + } + else if (entity->FindComponent()) + { + ImGui::Text("Child node"); + } + ImGui::NextColumn(); + + firstEntity = false; + } + + ImGui::Columns(1); + ImGui::TreePop(); + } + } + } + } + + ImGui::Separator(); + if (ImGui::InputFloat("Awareness Radius", &m_awarenessRadius)) + { + CollectHierarchyRoots(); + } + if (ImGui::Button("Refresh")) + { + CollectHierarchyRoots(); + } +#endif + } + + + void MultiplayerDebugHierarchyReporter::UpdateDebugOverlay() + { + if (!m_hierarchyRoots.empty()) + { + if (m_debugDisplay == nullptr) + { + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId); + m_debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + } + + const AZ::u32 stateBefore = m_debugDisplay->GetState(); + m_debugDisplay->SetColor(AZ::Colors::White); + + for (const auto& root : m_hierarchyRoots) + { + if (const auto* rootComponent = root.second.m_rootComponent) + { + if (rootComponent->IsHierarchicalRoot()) + { + const AZStd::vector& hierarchicalChildren = rootComponent->GetHierarchicalEntities(); + + azsprintf(m_statusBuffer, "Hierarchy [%s] %u members", rootComponent->GetEntity()->GetName().c_str(), + aznumeric_cast(hierarchicalChildren.size())); + + AZ::Vector3 entityPosition = rootComponent->GetEntity()->GetTransform()->GetWorldTranslation(); + constexpr bool centerText = true; + m_debugDisplay->DrawTextLabel(entityPosition, 1.0f, m_statusBuffer, centerText, 0, 0); + } + } + } + + m_debugDisplay->SetState(stateBefore); + } + } + + void MultiplayerDebugHierarchyReporter::OnEntityActivated(const AZ::EntityId& entityId) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entityId)) + { + if (auto* rootComponent = childEntity->FindComponent()) + { + HierarchyRootInfo info; + info.m_rootComponent = rootComponent; + rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent); + rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent); + + m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info)); + } + } + } + + void MultiplayerDebugHierarchyReporter::OnEntityDeactivated(const AZ::EntityId& entityId) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entityId)) + { + if (auto* rootComponent = childEntity->FindComponent()) + { + m_hierarchyRoots.erase(rootComponent); + } + } + } + + void MultiplayerDebugHierarchyReporter::CollectHierarchyRoots() + { + m_hierarchyRoots.clear(); + AZ::Sphere awarenessSphere(AZ::Vector3::CreateZero(), m_awarenessRadius); + + const auto viewportContextManager = AZ::Interface::Get(); + if (const auto viewportContext = viewportContextManager->GetDefaultViewportContext()) + { + awarenessSphere.SetCenter(viewportContext->GetCameraTransform().GetTranslation()); + } + + AZStd::vector gatheredEntries; + AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(awarenessSphere, + [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) + { + gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size()); + for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries) + { + if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity) + { + gatheredEntries.push_back(visEntry); + } + } + } + ); + + for (const AzFramework::VisibilityEntry* entry : gatheredEntries) + { + const AZ::Entity* entity = static_cast(entry->m_userData); + if (auto* rootComponent = entity->FindComponent()) + { + if (awarenessSphere.GetCenter().GetDistanceEstimate(entity->GetTransform()->GetWorldTranslation()) < m_awarenessRadius) + { + HierarchyRootInfo info; + info.m_rootComponent = rootComponent; + rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent); + rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent); + + m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info)); + } + } + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h new file mode 100644 index 0000000000..2ce7e055a0 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugHierarchyReporter.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + /** + * /brief Provides ImGui and debug draw hierarchy information at runtime. + */ + class MultiplayerDebugHierarchyReporter + : public AZ::EntitySystemBus::Handler + { + public: + MultiplayerDebugHierarchyReporter(); + ~MultiplayerDebugHierarchyReporter() override; + + //! Main update loop. + void OnImGuiUpdate(); + + //! Draws hierarchy information over hierarchy root entities. + void UpdateDebugOverlay(); + + //! EntitySystemBus overrides. + //! @{ + void OnEntityActivated(const AZ::EntityId& entityId) override; + void OnEntityDeactivated(const AZ::EntityId& entityId) override; + //! @} + + private: + AZ::ScheduledEvent m_updateDebugOverlay; + + AzFramework::DebugDisplayRequests* m_debugDisplay = nullptr; + + struct HierarchyRootInfo + { + NetworkHierarchyRootComponent* m_rootComponent = nullptr; + NetworkHierarchyChangedEvent::Handler m_changedEvent; + NetworkHierarchyLeaveEvent::Handler m_leaveEvent; + }; + + AZStd::unordered_map m_hierarchyRoots; + void CollectHierarchyRoots(); + + char m_statusBuffer[100] = {}; + + float m_awarenessRadius = 1000.f; + }; +} diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index 422b2f0cae..b23bc677f0 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -71,6 +71,7 @@ namespace Multiplayer ImGui::Checkbox("Networking Stats", &m_displayNetworkingStats); ImGui::Checkbox("Multiplayer Stats", &m_displayMultiplayerStats); ImGui::Checkbox("Multiplayer Entity Stats", &m_displayPerEntityStats); + ImGui::Checkbox("Multiplayer Hierarchy Debugger", &m_displayHierarchyDebugger); ImGui::EndMenu(); } } @@ -464,6 +465,29 @@ namespace Multiplayer } } } + + if (m_displayHierarchyDebugger) + { + if (ImGui::Begin("Multiplayer Hierarchy Debugger", &m_displayHierarchyDebugger)) + { + if (m_hierarchyDebugger == nullptr) + { + m_hierarchyDebugger = AZStd::make_unique(); + } + + if (m_hierarchyDebugger) + { + m_hierarchyDebugger->OnImGuiUpdate(); + } + } + } + else + { + if (m_hierarchyDebugger) + { + m_hierarchyDebugger.reset(); + } + } } #endif } diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h index 7b3c852f58..9becf1543c 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h @@ -8,6 +8,8 @@ #pragma once +#include "MultiplayerDebugHierarchyReporter.h" + #include #include #include @@ -62,5 +64,8 @@ namespace Multiplayer bool m_displayPerEntityStats = false; AZStd::unique_ptr m_reporter; + + bool m_displayHierarchyDebugger = false; + AZStd::unique_ptr m_hierarchyDebugger; }; } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index 582cda3eea..a30ab207b4 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -146,7 +146,6 @@ namespace Multiplayer { // Connect the Editor to the editor server for Multiplayer simulation AZ::Interface::Get()->Connect(remoteAddress.c_str(), remotePort); - AZ::Interface::Get()->SendReadyForEntityUpdates(true); } } } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 377fb1eb56..55a05e33a7 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -62,6 +62,7 @@ namespace Multiplayer } MultiplayerEditorSystemComponent::MultiplayerEditorSystemComponent() + : m_serverAcceptanceReceivedHandler([this](){OnServerAcceptanceReceived();}) { ; } @@ -70,6 +71,7 @@ namespace Multiplayer { AzFramework::GameEntityContextEventBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + AZ::Interface::Get()->AddServerAcceptanceReceivedHandler(m_serverAcceptanceReceivedHandler); } void MultiplayerEditorSystemComponent::Deactivate() @@ -143,7 +145,11 @@ namespace Multiplayer // Start the configured server if it's available AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" --editorsv_isDedicated true", serverPath.c_str()); + processLaunchInfo.m_commandlineParameters = AZStd::string::format( + R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s")", + serverPath.c_str(), + AZ::Utils::GetProjectPath().c_str(), + static_cast(sv_defaultPlayerSpawnAsset).c_str()); processLaunchInfo.m_showWindow = true; processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; @@ -239,4 +245,12 @@ namespace Multiplayer void MultiplayerEditorSystemComponent::OnGameEntitiesReset() { } + + void MultiplayerEditorSystemComponent::OnServerAcceptanceReceived() + { + // We're now accepting the connection to the EditorServer. + // In normal game clients SendReadyForEntityUpdates will be enabled once the appropriate level's root spawnable is loaded, + // but since we're in Editor, we're already in the level. + AZ::Interface::Get()->SendReadyForEntityUpdates(true); + } } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h index 6a9e6a79b6..4e4c6b677f 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h @@ -8,6 +8,8 @@ #pragma once +#include + #include #include @@ -45,6 +47,9 @@ namespace Multiplayer MultiplayerEditorSystemComponent(); ~MultiplayerEditorSystemComponent() override = default; + //! Called once the editor receives the server's accept packet + void OnServerAcceptanceReceived(); + //! AZ::Component overrides. //! @{ void Activate() override; @@ -71,5 +76,7 @@ namespace Multiplayer IEditor* m_editor = nullptr; AzFramework::ProcessWatcher* m_serverProcess = nullptr; AzNetworking::ConnectionId m_editorConnId; + + ServerAcceptanceReceivedEvent::Handler m_serverAcceptanceReceivedHandler; }; } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 37e87ace84..4fad5697c7 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -308,6 +308,12 @@ namespace Multiplayer return true; } + void MultiplayerSystemComponent::OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) + { + AZ_UNUSED(sessionConfig); + AZ_UNUSED(updateReason); + } + void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { const AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); @@ -497,6 +503,8 @@ namespace Multiplayer AZ::Interface::Get()->PerformCommand(commandString.c_str()); AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap(); AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); + + m_serverAcceptanceReceivedEvent.Signal(); return true; } @@ -821,6 +829,11 @@ namespace Multiplayer handler.Connect(m_connectionAcquiredEvent); } + void MultiplayerSystemComponent::AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) + { + handler.Connect(m_serverAcceptanceReceivedEvent); + } + void MultiplayerSystemComponent::AddSessionInitHandler(SessionInitEvent::Handler& handler) { handler.Connect(m_initEvent); @@ -1034,7 +1047,7 @@ namespace Multiplayer INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate); NetworkEntityHandle controlledEntity; - if (entityList.size() > 0) + if (!entityList.empty()) { controlledEntity = entityList[0]; } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 316ddd41d5..ef1fb0da5b 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -37,6 +37,8 @@ namespace AzNetworking namespace Multiplayer { + AZ_CVAR_EXTERNED(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset); + //! Multiplayer system component wraps the bridging logic between the game and transport layer. class MultiplayerSystemComponent final : public AZ::Component @@ -68,6 +70,7 @@ namespace Multiplayer bool OnSessionHealthCheck() override; bool OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) override; bool OnDestroySessionBegin() override; + void OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) override; //! @} //! AZ::TickBus::Handler overrides. @@ -116,6 +119,7 @@ namespace Multiplayer void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; + void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) override; void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override; void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; @@ -157,6 +161,7 @@ namespace Multiplayer SessionInitEvent m_initEvent; SessionShutdownEvent m_shutdownEvent; ConnectionAcquiredEvent m_connectionAcquiredEvent; + ServerAcceptanceReceivedEvent m_serverAcceptanceReceivedEvent; ClientDisconnectedEvent m_clientDisconnectedEvent; ClientMigrationStartEvent m_clientMigrationStartEvent; ClientMigrationEndEvent m_clientMigrationEndEvent; diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h index 7f98d22702..5a528ed497 100644 --- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h @@ -328,6 +328,7 @@ namespace Multiplayer void Terminate([[maybe_unused]] AzNetworking::DisconnectReason reason) override {} void AddClientDisconnectedHandler([[maybe_unused]] ClientDisconnectedEvent::Handler& handler) override {} void AddConnectionAcquiredHandler([[maybe_unused]] ConnectionAcquiredEvent::Handler& handler) override {} + void AddServerAcceptanceReceivedHandler([[maybe_unused]] ServerAcceptanceReceivedEvent::Handler& handler) override {} void AddSessionInitHandler([[maybe_unused]] SessionInitEvent::Handler& handler) override {} void AddSessionShutdownHandler([[maybe_unused]] SessionShutdownEvent::Handler& handler) override {} void SendReadyForEntityUpdates([[maybe_unused]] bool readyForEntityUpdates) override {} diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h index 44024f67ab..527aeb51bc 100644 --- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -29,9 +29,10 @@ namespace UnitTest MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&)); MOCK_METHOD1(AddNotifyClientMigrationHandler, void(Multiplayer::NotifyClientMigrationEvent::Handler&)); MOCK_METHOD1(AddNotifyEntityMigrationEventHandler, void(Multiplayer::NotifyEntityMigrationEvent::Handler&)); - MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event::Handler&)); - MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event::Handler&)); - MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddConnectionAcquiredHandler, void(Multiplayer::ConnectionAcquiredEvent::Handler&)); + MOCK_METHOD1(AddServerAcceptanceReceivedHandler, void(Multiplayer::ServerAcceptanceReceivedEvent::Handler&)); + MOCK_METHOD1(AddSessionInitHandler, void(Multiplayer::SessionInitEvent::Handler&)); + MOCK_METHOD1(AddSessionShutdownHandler, void(Multiplayer::SessionShutdownEvent::Handler&)); MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId)); MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&)); MOCK_METHOD1(SendReadyForEntityUpdates, void(bool)); diff --git a/Gems/Multiplayer/Code/multiplayer_debug_files.cmake b/Gems/Multiplayer/Code/multiplayer_debug_files.cmake index 37a1c91640..1d85dd8149 100644 --- a/Gems/Multiplayer/Code/multiplayer_debug_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_debug_files.cmake @@ -9,6 +9,8 @@ set(FILES Source/Debug/MultiplayerDebugByteReporter.cpp Source/Debug/MultiplayerDebugByteReporter.h + Source/Debug/MultiplayerDebugHierarchyReporter.cpp + Source/Debug/MultiplayerDebugHierarchyReporter.h Source/Debug/MultiplayerDebugPerEntityReporter.cpp Source/Debug/MultiplayerDebugPerEntityReporter.h Source/Debug/MultiplayerDebugModule.cpp diff --git a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings deleted file mode 100644 index adbf7d20f9..0000000000 --- a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/16_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/16_ddna.tif deleted file mode 100644 index 68edfab5e2..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/16_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:78394a1c8fc1e8bb260b27c5ae3fa040cbabcbc550fb33470316f9f38b2327f7 -size 2104742 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal.mtl deleted file mode 100644 index c27dfd5bc1..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif deleted file mode 100644 index 7993c143a9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae4a72a26a2f232c6ffb42a391d81b4ca7396c73894ccb923ba2f7bc1c940f04 -size 12602810 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings deleted file mode 100644 index e97c4e452c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:0,ios:0,mac:0,pc:2,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_spec.tif deleted file mode 100644 index 531bbe52b2..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:73e215c80dcbde6b092b87aaed3532aa3e4447872a4c347248ada2e0c92b62c9 -size 12602768 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_spec.tif.exportsettings deleted file mode 100644 index 0e4a4a080e..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.mtl deleted file mode 100644 index eab5de5a5b..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.tif deleted file mode 100644 index 9db133e2cd..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0801f5089dc5d984fc2c88bd1cb3e0160fc0eddacdb95492d8ee433d0dfe675b -size 12602752 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.tif.exportsettings deleted file mode 100644 index fb61d0f55c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel2.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel2.mtl deleted file mode 100644 index dfadacee21..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel2.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif deleted file mode 100644 index 5afabeeea2..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb714bf17f4da5b74eb0fe8eb329533f1fe76c68a915f5240a30bf496d133b2a -size 33574354 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings deleted file mode 100644 index a098bdcad9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint.mtl deleted file mode 100644 index c0f0399527..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint.mtl +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_diff.tif deleted file mode 100644 index 207980ec50..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc61165bad7ff9a3f6bee23c47350bd4e69f7bce80a50acc4d353583d5a49ec2 -size 12602762 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_diff.tif.exportsettings deleted file mode 100644 index d3713274e6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_spec.tif deleted file mode 100644 index 97738f104e..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8349c8ebe5b6b58f426bccd307778aeff674970b8b7761c78bf07460a51208d1 -size 12602764 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_spec.tif.exportsettings deleted file mode 100644 index 0e4a4a080e..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/car_paint_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal.mtl deleted file mode 100644 index a5848a101c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_ddna.tif deleted file mode 100644 index f0b243c4ba..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8691f06e179e74b2035df0147b3792b28aded379fb8ca5c6a58694793bcfc7b -size 33574298 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_ddna.tif.exportsettings deleted file mode 100644 index 4377d1bc2a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_diff.tif deleted file mode 100644 index 51afab3cae..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:51afb7f2b44b7249b7b560abd165cd07f55a8f622e7ce684aa70de0fe04fe457 -size 12602798 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_diff.tif.exportsettings deleted file mode 100644 index 78867ef15c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/coal_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /mipmaps=0 /preset=Albedo /reduce=-1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_high_cch.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_high_cch.tif deleted file mode 100644 index 370a37320b..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_high_cch.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61b26a818df92997fe0336d7e4fcb84af10328be0f49bd23b28b20dd3d70dc94 -size 19460 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_low_cch.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_low_cch.tif deleted file mode 100644 index 57bb2a9eb6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_low_cch.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c266cd36f1732dd6f0e86cee98d354f1301fa06c62dc8dc1d81f2dce64b589c -size 19460 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_veryhigh_cch.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_veryhigh_cch.tif deleted file mode 100644 index ac1f01f7a7..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_contrast_veryhigh_cch.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:41360332648da3dc87d441d07efdbc9074e01de03e4752775f655299870adb84 -size 19462 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_saturation_0_cch.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_saturation_0_cch.tif deleted file mode 100644 index 5a5312dac8..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/colorcharts/debug_saturation_0_cch.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b2d948c3edd823e3ce898cb0237d37088b7c09f7a6bc4f2d5a883a3785c7b92 -size 19416 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco.mtl deleted file mode 100644 index 0bc0563dc9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_ddna.tif deleted file mode 100644 index 115848e63a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5e34cadedb6f04d7e2923ff39eb6aa26578f7fa66d55759a79fc8b2fc297a67 -size 33571726 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_ddna.tif.exportsettings deleted file mode 100644 index 4377d1bc2a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_diff.tif deleted file mode 100644 index f73e9d8e29..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f6fe115f07fabdf24effa6ccf510d7df0878338cf62ec725f99548f744d8d520 -size 25183118 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_diff.tif.exportsettings deleted file mode 100644 index fb61d0f55c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/concrete_stucco_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/conductor_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/conductor_diff.tif deleted file mode 100644 index 55869c6a65..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/conductor_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1e83c754930c838b473d0388cb7eea6fc61f00d37756f288588706ef31fbc3e7 -size 793988 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/conductor_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/conductor_diff.tif.exportsettings deleted file mode 100644 index d3713274e6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/conductor_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/copper_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/copper_spec.tif deleted file mode 100644 index eff0d838ff..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/copper_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eb7bb72c37f57ae9f5c8be2239d24da91d635281ee6c5394f4cd4376260dd659 -size 1580448 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/copper_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/copper_spec.tif.exportsettings deleted file mode 100644 index a6837b396f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/copper_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /mipgentype=average /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather.mtl deleted file mode 100644 index e1e04a218f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif deleted file mode 100644 index 05a2366629..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:28e1269ebe4839f329879adf0faa908eef16a975a37e614f65c9337c8095c694 -size 12602796 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings deleted file mode 100644 index 19e899701a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/fabric.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/fabric.mtl deleted file mode 100644 index e25536921d..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/fabric.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.mtl deleted file mode 100644 index a5becf3800..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.tif deleted file mode 100644 index 556be35401..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b3d1f17036a544942d9b5ad753d9de4e77f2d72f264e75703c777a9ec36d0ff -size 16797130 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.tif.exportsettings deleted file mode 100644 index 6a4cbb4a3f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /mipmaps=0 /preset=NormalsWithSmoothness /reduce=-1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_ddna.tif deleted file mode 100644 index b3310a7902..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ece9aed5ef96c2b677922f148ef03d6186b7eca9400e90956772bce5661ecc34 -size 33574310 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif deleted file mode 100644 index c8194ca629..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d6d9d3c1577efc9df9f9200d24d338cdca684239f0e793c217e1a7358ced6ff -size 25185726 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings deleted file mode 100644 index 08861692ea..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce="android:0,ios:0,mac:0,pc:1,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay.mtl deleted file mode 100644 index 9b722cb5e6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_ddna.tif deleted file mode 100644 index 629475cdef..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:02fdb16917657d8af287dc03bbaef856d99570a26bd8d92d1766c221abb4ffd3 -size 33571149 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_diff.tif deleted file mode 100644 index 60698476a1..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5001487368d53bba9def9ea772b129ee1e16e738b8940a0621d1822e87105ffc -size 25182520 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_diff.tif.exportsettings deleted file mode 100644 index d3713274e6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/glazed_clay_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0.mtl deleted file mode 100644 index 217d3a979a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0_ddna.tif deleted file mode 100644 index 69bda20789..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:54c5b9d01385f9301ff5958c75ddf9f7128539dbdb8c12497ef12150fd7e3991 -size 2104738 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss0_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10.mtl deleted file mode 100644 index 2d1be1df68..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100.mtl deleted file mode 100644 index a8e096eead..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100_ddna.tif deleted file mode 100644 index 9bb9eb9a2d..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aab4381b123765d164d222ab8260977302a49d176f1c6b601a3236013eb93c71 -size 2104740 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss100_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10_ddna.tif deleted file mode 100644 index c7b3884843..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c84d169f7f305dde87039b113b144335239ded615ddc060fd7648e0c69c4ebd -size 2104738 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss10_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20.mtl deleted file mode 100644 index b2b3c7643d..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20_ddna.tif deleted file mode 100644 index 6b990d7545..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6bcd9dfe932714e63612023ea8dce2a9e2c377553e666b96771bb4f831387c7d -size 2104738 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss20_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30.mtl deleted file mode 100644 index 661a8e3de2..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30_ddna.tif deleted file mode 100644 index 64094acf29..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:500b16679345f981c68521e6021aea5ef0e07603beb52fdac6bbb46320af98cb -size 2104738 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss30_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40.mtl deleted file mode 100644 index 70ee969fc0..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40_ddna.tif deleted file mode 100644 index c166c9f8cc..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:36b57374a09c929f9bdec637483f3545b320a2fe18e3e33d483cd3d027b7a4cd -size 2104738 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss40_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50.mtl deleted file mode 100644 index 4f00a19269..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50_ddna.tif deleted file mode 100644 index e92493893f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09b976c732d3b456ebd75afc814cc74c8aaa2530118e2a54127c8ce8d24c63cd -size 2104746 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss50_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60.mtl deleted file mode 100644 index c96632fe7b..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60_ddna.tif deleted file mode 100644 index 1086d2a54d..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d733ad0c67d70aa2fbc31ac6edd1a50d99ed9f4659452707db1b107433668ef6 -size 2104746 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss60_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70.mtl deleted file mode 100644 index e7afaff4cd..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70_ddna.tif deleted file mode 100644 index 56f751a811..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8fe277c640d2f17b80322de1218cb9b8d3f979c76f735a9741fd253a639868f -size 2104746 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss70_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80.mtl deleted file mode 100644 index 11bee418ea..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80_ddna.tif deleted file mode 100644 index 096aa4f81f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2019143e287e449cfa544ca8d06be38332e6bad2d92f302482670d25c27ce93 -size 2104748 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss80_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90.mtl deleted file mode 100644 index 295882a14d..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90_ddna.tif deleted file mode 100644 index dd311c14f3..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:913e9594afce1753143c4a23ba63376844285e4cfa0353bec8c8fbb32fb55244 -size 2104748 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gloss90_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gold_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gold_spec.tif deleted file mode 100644 index 7d053201a4..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gold_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:60df2f9dd0c960135c590b6bbb085d8054d4f98a9349a3aaae2b69ef2c084afd -size 1580458 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gold_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gold_spec.tif.exportsettings deleted file mode 100644 index a6837b396f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/gold_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /mipgentype=average /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/ground_mats/mixed_stones1.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/ground_mats/mixed_stones1.mtl deleted file mode 100644 index 68bc825e99..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/ground_mats/mixed_stones1.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/iron_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/iron_spec.tif deleted file mode 100644 index 69162c2479..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/iron_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0809fec4adb0ba0bd9143e599ae841b04bcfa924cdd55666e46c82771825e106 -size 794002 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/iron_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/iron_spec.tif.exportsettings deleted file mode 100644 index 0e4a4a080e..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/iron_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif deleted file mode 100644 index ff7973839e..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:44e38b77ed21dd6c4dbe506d7449440ae5edd734d8eaf9e3fc68b49f16890598 -size 16797150 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings deleted file mode 100644 index a098bdcad9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather.mtl deleted file mode 100644 index fb58f3e7d3..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif deleted file mode 100644 index 7efa3c5dc0..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7ec252b4e03cb18ae458c650c392cdcd2c3d077db54279665f7b8bf7b335ff80 -size 12602808 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings deleted file mode 100644 index 19e899701a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones.mtl deleted file mode 100644 index f7572190ce..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif deleted file mode 100644 index f65bd1efb8..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9b18ba8592261663e36608c74c763c7b80adc673f5857a605c7a1a261bb8dc3c -size 8400326 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings deleted file mode 100644 index a098bdcad9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif deleted file mode 100644 index 57b058e334..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:54e57959423057d58ec5be9530669e613566eb1152919887b10f06ab81cf901d -size 6303154 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings deleted file mode 100644 index 19e899701a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/nickel_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/nickel_spec.tif deleted file mode 100644 index 538b6adf3a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/nickel_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:af459c0475bcf52150d818190480665ca0989f94a17cf979478e0bfd078cc0bf -size 793948 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/nickel_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/nickel_spec.tif.exportsettings deleted file mode 100644 index 0e4a4a080e..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/nickel_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_ddna.tif deleted file mode 100644 index 28c888ac93..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c2b478231433a0d797bfe62bb087c8331a7edb32a150e2da6bd51fe55d4da60 -size 16797096 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_ddna.tif.exportsettings deleted file mode 100644 index 4377d1bc2a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_diff.tif deleted file mode 100644 index 791d5a9068..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d460e8b5c8c026cad1b20a71c3857c562051ccd4fc66d480357375276e41ab70 -size 12602770 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_diff.tif.exportsettings deleted file mode 100644 index d3713274e6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/plain_fabric_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/platinum_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/platinum_spec.tif deleted file mode 100644 index 293868dbcd..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/platinum_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a8a5517902429c5b518041cf13f09c0e68378ca0afcd53af487373bc23584953 -size 794002 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/platinum_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/platinum_spec.tif.exportsettings deleted file mode 100644 index 2bec812694..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/platinum_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance_Linear /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_copper.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_copper.mtl deleted file mode 100644 index e440e60c07..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_copper.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_gold.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_gold.mtl deleted file mode 100644 index ff61b723c1..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_gold.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_iron.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_iron.mtl deleted file mode 100644 index a2557c8cf5..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_iron.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_nickel.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_nickel.mtl deleted file mode 100644 index 383f132414..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_nickel.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_silver.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_silver.mtl deleted file mode 100644 index e6f78b0c92..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/polished_silver.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain.mtl deleted file mode 100644 index b82aa09808..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain_diff.tif deleted file mode 100644 index cc0ff9e1a1..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:23383293c185f7801f466779a0f223dd3eb21a8cc2c69a742d58f699b32ac375 -size 3157400 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain_diff.tif.exportsettings deleted file mode 100644 index 78867ef15c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/porcelain_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /mipmaps=0 /preset=Albedo /reduce=-1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif deleted file mode 100644 index 3532880e2d..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c38541d104c3461b22f9bf8865308f5ea07895f3cebc1fd67c29647c76ed904 -size 794036 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings deleted file mode 100644 index 48c18e1fe4..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:0,ios:0,mac:0,pc:3,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif deleted file mode 100644 index ac1ced5071..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:00928a4cbfc43bc3d13f407d8ea6d59499e658c0fdd92981b6124e999ca7d668 -size 33574298 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings deleted file mode 100644 index a098bdcad9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust.mtl deleted file mode 100644 index 5861a12251..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_blend.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_blend.tif deleted file mode 100644 index 7bd44ad5be..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_blend.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d4fc3a6dc8729848be3fa54e544e146a324198c213d91031082f911f78c2a9df -size 3154182 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_blend.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_blend.tif.exportsettings deleted file mode 100644 index 8092b156b4..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_blend.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /mipgentype=box /preset=Albedo /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif deleted file mode 100644 index 3d13dc2fd6..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a673385f65153fdb1659a008229c333ccf972bf15268e051594abb97e129a20 -size 8397168 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings deleted file mode 100644 index a098bdcad9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif deleted file mode 100644 index 9841bdb352..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6170ca5d841592ca689316e268793094b8dde3705281ad7c9d942ed9dfd7343b -size 6299995 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings deleted file mode 100644 index 19e899701a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rusted_metal.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rusted_metal.mtl deleted file mode 100644 index 7ccf171972..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rusted_metal.mtl +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/shiny_plastic.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/shiny_plastic.mtl deleted file mode 100644 index e62f3ca57b..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/shiny_plastic.mtl +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/silver_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/silver_spec.tif deleted file mode 100644 index d50c32839a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/silver_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9288cf57fb15fe106f335aa95910df01efa50eef2b69caf641c69640fc1f91e3 -size 793990 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/silver_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/silver_spec.tif.exportsettings deleted file mode 100644 index 2bec812694..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/silver_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance_Linear /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/afternoon.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/afternoon.tif deleted file mode 100644 index 9b8f9a20fb..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/afternoon.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad6459c0fc86b2ef4f585505329a649eb30bc5940582b68d4dec0bf45aae3395 -size 6295912 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/evening.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/evening.tif deleted file mode 100644 index 9a637b17aa..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/evening.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df24d58699c82f8581a8e787c12d3dadd6df97b165cb1d6f14fe68ed43ea31c0 -size 3150154 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/neutral.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/neutral.tif deleted file mode 100644 index 48ca954942..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/neutral.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ddbd14843b551d8d4894fa85f4512c58505573ac0c8f14b1afe357abce21396 -size 3150190 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/night.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/night.tif deleted file mode 100644 index 8780f7fba0..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/night.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2579027e45f1039a3b7cf253545aa166271556c2faf914cba9f5754577c49eaa -size 8393068 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_afternoon.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_afternoon.mtl deleted file mode 100644 index 76f9b83c9a..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_afternoon.mtl +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_evening.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_evening.mtl deleted file mode 100644 index 4c72ff536f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_evening.mtl +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_neutral.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_neutral.mtl deleted file mode 100644 index 748c25929b..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_neutral.mtl +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_night.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_night.mtl deleted file mode 100644 index d436db6ef2..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/skydomes/sky_night.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks.mtl deleted file mode 100644 index eb1dea0991..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif deleted file mode 100644 index 0eaf9326d0..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bba137198cf4b87c822e12f0328dbc4ed9bc612521c7985913d6363f4e3291ab -size 33574348 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings deleted file mode 100644 index a098bdcad9..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_diff.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_diff.tif deleted file mode 100644 index c919feeb0f..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_diff.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:76a8b871242fe618a5b5a11ddafa147890806759927606fe4dcc697c8e4d11d3 -size 25185676 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_diff.tif.exportsettings deleted file mode 100644 index fb61d0f55c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_diff.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_spec.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_spec.tif deleted file mode 100644 index 3dd863eb05..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_spec.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bbd50f58c8e889272b5734f3eb63527a2a2d560ccacf69bb3326b6dc866aa01a -size 25185678 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_spec.tif.exportsettings deleted file mode 100644 index c31be74648..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_spec.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce=1 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_copper.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_copper.mtl deleted file mode 100644 index 1c522de4c3..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_copper.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_gold.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_gold.mtl deleted file mode 100644 index f81420b9aa..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_gold.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal.mtl b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal.mtl deleted file mode 100644 index d5300822cd..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal.mtl +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal_ddna.tif b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal_ddna.tif deleted file mode 100644 index 4d831687a4..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal_ddna.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c84a16617421d986dbbd5c12393e6aef46de9d2898becfef6eb02d065b9d0bd0 -size 8400288 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal_ddna.tif.exportsettings deleted file mode 100644 index 0159b6ca02..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/worn_metal_ddna.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test.mtl b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test.mtl deleted file mode 100644 index 25ea4b3171..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test.mtl +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_AO.tif b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_AO.tif deleted file mode 100644 index ccebca85f8..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_AO.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45358825d1fa0fed1a9fb7028d8361e58e5d96f1415d56aebe7c1ca0e410000a -size 12605680 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_AO.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_AO.tif.exportsettings deleted file mode 100644 index 25a6d5d697..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_AO.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Reflectance /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_H.tif b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_H.tif deleted file mode 100644 index 5f89b4c33c..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_H.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:71d137d8fd428ae6191d7c97c9b48e92484975a8bc2b0e8704adab928eeb6650 -size 16798472 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_H.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_H.tif.exportsettings deleted file mode 100644 index 08f50cf38b..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_H.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /mipgentype=sigma-six /preset=Displacement /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_albedo.tif b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_albedo.tif deleted file mode 100644 index 1e4fd75fd5..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_albedo.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:caf47b0db80678caa3673566150002420677e24e7e3bc77c9f3d95cac15433e3 -size 12609308 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_albedo.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_albedo.tif.exportsettings deleted file mode 100644 index 44cd6187b1..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_albedo.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Albedo /reduce=0 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_normals_ddn.tif b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_normals_ddn.tif deleted file mode 100644 index c71f243adb..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_normals_ddn.tif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:93a0842c8d0bb3ce1d5897e415820df48cf093ff9ea4369e2e9bfac5ea018259 -size 12602730 diff --git a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_normals_ddn.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_normals_ddn.tif.exportsettings deleted file mode 100644 index d1103c9959..0000000000 --- a/Gems/PBSreferenceMaterials/Assets/materials/test_reference/test_normals_ddn.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /preset=Normals /reduce=0 diff --git a/Gems/PBSreferenceMaterials/CMakeLists.txt b/Gems/PBSreferenceMaterials/CMakeLists.txt deleted file mode 100644 index 76a911746c..0000000000 --- a/Gems/PBSreferenceMaterials/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -# This will export its "SourcePaths" to the generated "cmake_dependencies..assetbuilder.setreg" -if(PAL_TRAIT_BUILD_HOST_TOOLS) - ly_create_alias(NAME PBSreferenceMaterials.Builders NAMESPACE Gem) -endif() diff --git a/Gems/PBSreferenceMaterials/Resources/test_source.psd b/Gems/PBSreferenceMaterials/Resources/test_source.psd deleted file mode 100644 index 2f0ca41476..0000000000 --- a/Gems/PBSreferenceMaterials/Resources/test_source.psd +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:322e2360dd2a621fc733e171d676497d5351ade274df16d223ccf3b9df8c3382 -size 19277076 diff --git a/Gems/PBSreferenceMaterials/gem.json b/Gems/PBSreferenceMaterials/gem.json deleted file mode 100644 index feddc1e465..0000000000 --- a/Gems/PBSreferenceMaterials/gem.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "gem_name": "PBSreferenceMaterials", - "display_name": "PBS Reference Materials", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", - "type": "Asset", - "summary": "The PBS Reference Materials Gem provides physically based reference materials for Open 3D Engine.", - "canonical_tags": [ - "Gem" - ], - "user_tags": [ - "Rendering", - "Sample", - "Assets" - ], - "icon_path": "preview.png", - "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/", - "dependencies": [] -} diff --git a/Gems/PBSreferenceMaterials/preview.png b/Gems/PBSreferenceMaterials/preview.png deleted file mode 100644 index 51c277678f..0000000000 --- a/Gems/PBSreferenceMaterials/preview.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:097b03d36a65678b4e12e2c0830707480034fe31fe4efba9c848bb58440eb9fb -size 24928 diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 7c07ca207c..f8d0adf156 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -791,6 +792,7 @@ namespace PhysX if (shapes.empty()) { m_componentWarnings.clear(); + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); return; @@ -824,9 +826,16 @@ namespace PhysX } m_componentWarnings.push_back(AZStd::string::format( - "The Physics Asset \"%s\" is a Triangle Mesh, it is not compatible with a Dynamic Rigidbody, either:\n" - "Change the PhysicsAsset to Convex Mesh or set the Rigidbody to kinematic.", + "The physics asset \"%s\" was exported using triangle mesh geometry, which is not compatible with non-kinematic " + "dynamic rigid bodies. To make the collider compatible, you can export the asset using primitive or convex mesh " + "geometry, use mesh decomposition when exporting the asset, or set the rigid body to kinematic. Learn more about " + "colliders.", assetPath.c_str())); + + // make sure the entity inspector scrolls so the warning is visible by marking this component as having + // new content + AzToolsFramework::EntityPropertyEditorRequestBus::Broadcast( + &AzToolsFramework::EntityPropertyEditorRequests::SetNewComponentId, GetId()); } else { @@ -839,8 +848,8 @@ namespace PhysX } AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); - + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, + m_componentWarnings.empty() ? AzToolsFramework::Refresh_EntireTree : AzToolsFramework::Refresh_EntireTree_NewContent); } void EditorColliderComponent::OnAssetReloaded(AZ::Data::Asset asset) diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index 447c2755c6..24b2586c9e 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -103,7 +103,6 @@ namespace PhysX } } // namespace Internal - void EditorRigidBodyConfiguration::Reflect(AZ::ReflectContext* context) { auto serializeContext = azrtti_cast(context); @@ -310,6 +309,15 @@ namespace PhysX } } + void EditorRigidBodyComponent::OnConfigurationChanged() + { + CreateEditorWorldRigidBody(); + + // required in case the kinematic setting has changed + PhysX::EditorColliderValidationRequestBus::Event( + GetEntityId(), &PhysX::EditorColliderValidationRequestBus::Events::ValidateRigidBodyMeshGeometryType); + } + void EditorRigidBodyComponent::Reflect(AZ::ReflectContext* context) { EditorRigidBodyConfiguration::Reflect(context); @@ -336,7 +344,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/rigid-body-physics/") ->DataElement(0, &EditorRigidBodyComponent::m_config, "Configuration", "Configuration for rigid body physics.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::CreateEditorWorldRigidBody) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::OnConfigurationChanged) ; } } diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h index 5d63ec9705..ed147ad8cb 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h @@ -120,6 +120,8 @@ namespace PhysX void InitPhysicsTickHandler(); void PrePhysicsTick(); + void OnConfigurationChanged(); + Debug::DebugDisplayDataChangedEvent::Handler m_debugDisplayDataChangeHandler; EditorRigidBodyConfiguration m_config; diff --git a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings deleted file mode 100644 index 9da169c456..0000000000 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings deleted file mode 100644 index 43410a50df..0000000000 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp index 13828e5f8c..44a95c1b6b 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp @@ -124,24 +124,10 @@ namespace AZ::SceneAPI::Behaviors return {}; } - // create instance to update the asset hints - auto instance = prefabSystemComponentInterface->InstantiatePrefab(templateId); - if (!instance) - { - AZ_Error("prefab", false, "PrefabGroup(%s) Could not instantiate prefab", prefabGroup->GetName().c_str()); - return {}; - } - - auto* instanceToTemplateInterface = AZ::Interface::Get(); - if (!instanceToTemplateInterface) - { - AZ_Error("prefab", false, "Could not get InstanceToTemplateInterface"); - return {}; - } - - // fill out a JSON DOM + const rapidjson::Document& generatedInstanceDom = prefabSystemComponentInterface->FindTemplateDom(templateId); auto proceduralPrefab = AZStd::make_unique(rapidjson::kObjectType); - instanceToTemplateInterface->GenerateDomForInstance(*proceduralPrefab.get(), *instance.get()); + proceduralPrefab->CopyFrom(generatedInstanceDom, proceduralPrefab->GetAllocator(), true); + return proceduralPrefab; } diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp index 2708f95f92..043e034062 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp @@ -170,7 +170,7 @@ namespace ScriptCanvasEditor ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0)) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Level", 0x9aeacc13)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/script-canvas/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/scripting/script-canvas/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorScriptCanvasComponent::m_scriptCanvasAssetHolder, "Script Canvas Asset", "Script Canvas asset associated with this component") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorScriptCanvasComponent::m_variableOverrides, "Properties", "Script Canvas Graph Properties") diff --git a/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp b/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp index 940a2626f5..259efef392 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp @@ -226,7 +226,6 @@ namespace ScriptCanvasEditor auto dataTypeIter = m_containerDataTypeSets.find(workingCrc); if (dataTypeIter == m_containerDataTypeSets.end()) { - // No idea wtf do here we've managed to put ourselves into an invalid state AZ_Error("ScriptCanvas", false, "Unknown partial type found in Container Creation. Aborting."); close(); break; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui index 15998bd83b..b5965c9746 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui @@ -184,8 +184,5 @@ - - -
diff --git a/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp b/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp index 215efd4656..427856f3a3 100644 --- a/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp +++ b/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp @@ -56,7 +56,7 @@ namespace StartingPointInput ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/InputConfig.svg") ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game")) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/input/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/input/") ->DataElement(AZ::Edit::UIHandlers::Default, &InputConfigurationComponent::m_inputEventBindingsAsset, "Input to event bindings", "Asset containing input to event binding information.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material index c0de5969b2..d2acc2516a 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material +++ b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material @@ -4,12 +4,6 @@ "parentMaterial": "", "propertyLayoutVersion": 1, "properties": { - "macroColor": { - "useTexture": false - }, - "macroNormal": { - "useTexture": false - }, "baseColor": { "color": [ 0.18, 0.18, 0.18 ], "useTexture": false diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype index 191fa7a731..ec04412fe6 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -133,67 +133,6 @@ } } ], - "macroColor": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Macro color texture map", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_macroColorMap" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true - } - - ], - "macroNormal": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Macro normal texture map", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_macroNormalMap" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "flipX", - "displayName": "Flip X Channel", - "description": "Flip tangent direction for this normal map.", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderInput", - "id": "m_flipMacroNormalX" - } - }, - { - "id": "flipY", - "displayName": "Flip Y Channel", - "description": "Flip bitangent direction for this normal map.", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderInput", - "id": "m_flipMacroNormalY" - } - } - ], "baseColor": [ { "id": "color", @@ -380,22 +319,6 @@ } ], "functors": [ - { - "type": "UseTexture", - "args": { - "textureProperty": "macroColor.textureMap", - "useTextureProperty": "macroColor.useTexture", - "shaderOption": "o_macroColor_useTexture" - } - }, - { - "type": "UseTexture", - "args": { - "textureProperty": "macroNormal.textureMap", - "useTextureProperty": "macroNormal.useTexture", - "shaderOption": "o_macroNormal_useTexture" - } - }, { "type": "UseTexture", "args": { diff --git a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype index 0a4f6d0229..3cdab8da10 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype @@ -4,42 +4,59 @@ "version": 1, "groups": [ { - "id": "settings", - "displayName": "Settings" + "name": "baseColor", + "displayName": "Base Color", + "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." + }, + { + "name": "normal", + "displayName": "Normal", + "description": "Properties related to configuring surface normal." } ], "properties": { - "macroColor": [ + "baseColor": [ { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true + "name": "textureMap", + "displayName": "Texture", + "description": "Base color of the macro material", + "type": "Image" } - ], - "macroNormal": [ + "normal": [ { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", + "name": "textureMap", + "displayName": "Texture", + "description": "Texture for defining surface normal direction. These will override normals generated from the geometry.", + "type": "Image" + }, + { + "name": "flipX", + "displayName": "Flip X Channel", + "description": "Flip tangent direction for this normal map.", "type": "Bool", - "defaultValue": true + "defaultValue": false + }, + { + "name": "flipY", + "displayName": "Flip Y Channel", + "description": "Flip bitangent direction for this normal map.", + "type": "Bool", + "defaultValue": false + }, + { + "name": "factor", + "displayName": "Factor", + "description": "Strength factor for scaling the values", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "softMax": 2.0 } ] } }, "shaders": [ - { - "file": "../../Shaders/Terrain/TerrainPBR_ForwardPass.shader" - }, - { - "file": "../../Shaders/Terrain/Terrain_Shadowmap.shader" - }, - { - "file": "../../Shaders/Terrain/Terrain_DepthPass.shader" - } ], "functors": [ ] diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index 95d1a73bbd..72c1af953c 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -26,8 +26,24 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject float m_heightScale; }; + struct MacroMaterialData + { + float2 m_uvMin; + float2 m_uvMax; + float m_normalFactor; + bool m_flipNormalX; + bool m_flipNormalY; + uint m_mapsInUse; + }; + TerrainData m_terrainData; + MacroMaterialData m_macroMaterialData[4]; + uint m_macroMaterialCount; + + Texture2D m_macroColorMap[4]; + Texture2D m_macroNormalMap[4]; + // The below shouldn't be in this SRG but needs to be for now because the lighting functions depend on them. //! Reflection Probe (smallest probe volume that overlaps the object position) @@ -101,14 +117,6 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial MaxAnisotropy = 16; }; - // Macro Color - Texture2D m_macroColorMap; - - // Macro normal - Texture2D m_macroNormalMap; - bool m_flipMacroNormalX; - bool m_flipMacroNormalY; - // Base Color float3 m_baseColor; float m_baseColorFactor; @@ -130,8 +138,6 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial } option bool o_useTerrainSmoothing = false; -option bool o_macroColor_useTexture = true; -option bool o_macroNormal_useTexture = true; option bool o_baseColor_useTexture = true; option bool o_specularF0_useTexture = true; option bool o_normal_useTexture = true; diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index df2a801969..91e367500b 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -68,7 +68,6 @@ VSOutput TerrainPBR_MainPassVS(VertexInput IN) ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) { // ------- Surface ------- - Surface surface; // Position @@ -83,21 +82,39 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) // ------- Normal ------- float3 macroNormal = IN.m_normal; - if (o_macroNormal_useTexture) - { - macroNormal = GetNormalInputTS(TerrainMaterialSrg::m_macroNormalMap, TerrainMaterialSrg::m_sampler, - origUv, TerrainMaterialSrg::m_flipMacroNormalX, TerrainMaterialSrg::m_flipMacroNormalY, CreateIdentity3x3(), true, 1.0); - } + // ------- Macro Color / Normal ------- + float3 macroColor = TerrainMaterialSrg::m_baseColor.rgb; + [unroll] for (uint i = 0; i < 4; ++i) + { + float2 macroUvMin = ObjectSrg::m_macroMaterialData[i].m_uvMin; + float2 macroUvMax = ObjectSrg::m_macroMaterialData[i].m_uvMax; + float2 macroUv = lerp(macroUvMin, macroUvMax, IN.m_uv); + if (macroUv.x >= 0.0 && macroUv.x <= 1.0 && macroUv.y >= 0.0 && macroUv.y <= 1.0) + { + if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 1) > 0) + { + macroColor = GetBaseColorInput(ObjectSrg::m_macroColorMap[i], TerrainMaterialSrg::m_sampler, macroUv, macroColor, true); + } + if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 2) > 0) + { + bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX; + bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY; + bool factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor; + macroNormal = GetNormalInputTS(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, + macroUv, flipX, flipY, CreateIdentity3x3(), true, factor); + } + break; + } + } + float3 detailNormal = GetNormalInputTS(TerrainMaterialSrg::m_normalMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_flipNormalX, TerrainMaterialSrg::m_flipNormalY, CreateIdentity3x3(), o_normal_useTexture, TerrainMaterialSrg::m_normalFactor); detailNormal = ReorientTangentSpaceNormal(macroNormal, detailNormal); surface.normal = lerp(detailNormal, macroNormal, detailFactor); surface.normal = normalize(surface.normal); - - // ------- Macro Color ------- - float3 macroColor = GetBaseColorInput(TerrainMaterialSrg::m_macroColorMap, TerrainMaterialSrg::m_sampler, origUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); + surface.vertexNormal = normalize(IN.m_normal); // ------- Base Color ------- float3 detailColor = GetBaseColorInput(TerrainMaterialSrg::m_baseColorMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 560fe1eb60..d7300cdc48 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -49,13 +49,25 @@ namespace Terrain namespace MaterialInputs { + // Terrain material static const char* const HeightmapImage("settings.heightmapImage"); + + // Macro material + static const char* const MacroColorTextureMap("baseColor.textureMap"); + static const char* const MacroNormalTextureMap("normal.textureMap"); + static const char* const MacroNormalFlipX("normal.flipX"); + static const char* const MacroNormalFlipY("normal.flipY"); + static const char* const MacroNormalFactor("normal.factor"); } namespace ShaderInputs { static const char* const ModelToWorld("m_modelToWorld"); static const char* const TerrainData("m_terrainData"); + static const char* const MacroMaterialData("m_macroMaterialData"); + static const char* const MacroMaterialCount("m_macroMaterialCount"); + static const char* const MacroColorMap("m_macroColorMap"); + static const char* const MacroNormalMap("m_macroNormalMap"); } @@ -71,8 +83,6 @@ namespace Terrain void TerrainFeatureProcessor::Activate() { - m_areaData = {}; - m_dirtyRegion = AZ::Aabb::CreateNull(); Initialize(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); } @@ -94,6 +104,10 @@ namespace Terrain { AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material."); } + else + { + PrepareMaterialData(); + } } } ); @@ -107,11 +121,17 @@ namespace Terrain void TerrainFeatureProcessor::Deactivate() { + TerrainMacroMaterialNotificationBus::Handler::BusDisconnect(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect(); m_patchModel = {}; m_areaData = {}; + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_sectorData.clear(); + m_macroMaterials.Clear(); + m_materialAssetLoader = {}; + m_materialInstance = {}; } void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) @@ -126,7 +146,7 @@ namespace Terrain void TerrainFeatureProcessor::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) { - if (dataChangedMask != TerrainDataChangedMask::HeightData && dataChangedMask != TerrainDataChangedMask::Settings) + if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) == 0) { return; } @@ -140,14 +160,21 @@ namespace Terrain m_dirtyRegion.AddAabb(regionToUpdate); m_dirtyRegion.Clamp(worldBounds); - AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); + const AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); AZ::Vector2 queryResolution = AZ::Vector2(1.0f); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + // Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes. + m_areaData.m_rebuildSectors = m_areaData.m_rebuildSectors || + m_areaData.m_terrainBounds.GetMin().GetX() != worldBounds.GetMin().GetX() || + m_areaData.m_terrainBounds.GetMin().GetY() != worldBounds.GetMin().GetY() || + m_areaData.m_terrainBounds.GetMax().GetX() != worldBounds.GetMax().GetX() || + m_areaData.m_terrainBounds.GetMax().GetY() != worldBounds.GetMax().GetY() || + m_areaData.m_sampleSpacing != queryResolution.GetX(); + m_areaData.m_transform = transform; - m_areaData.m_heightScale = worldBounds.GetZExtent(); m_areaData.m_terrainBounds = worldBounds; m_areaData.m_heightmapImageWidth = aznumeric_cast(worldBounds.GetXExtent() / queryResolution.GetX()); m_areaData.m_heightmapImageHeight = aznumeric_cast(worldBounds.GetYExtent() / queryResolution.GetY()); @@ -155,7 +182,93 @@ namespace Terrain m_areaData.m_updateHeight = aznumeric_cast(m_dirtyRegion.GetYExtent() / queryResolution.GetY()); // Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension. m_areaData.m_sampleSpacing = queryResolution.GetX(); - m_areaData.m_propertiesDirty = true; + m_areaData.m_heightmapUpdated = true; + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) + { + MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + materialData.m_bounds = region; + + UpdateMacroMaterialData(materialData, material); + + // Update all sectors in region. + ForOverlappingSectors(materialData.m_bounds, + [&](SectorData& sectorData) { + if (sectorData.m_macroMaterials.size() < sectorData.m_macroMaterials.max_size()) + { + sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(&materialData)); + } + } + ); + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance macroMaterial) + { + if (macroMaterial) + { + MacroMaterialData& data = FindOrCreateMacroMaterial(entityId); + UpdateMacroMaterialData(data, macroMaterial); + } + else + { + RemoveMacroMaterial(entityId); + } + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) + { + MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + for (SectorData& sectorData : m_sectorData) + { + bool overlapsOld = sectorData.m_aabb.Overlaps(materialData.m_bounds); + bool overlapsNew = sectorData.m_aabb.Overlaps(newRegion); + if (overlapsOld && !overlapsNew) + { + // Remove the macro material from this sector + for (uint16_t& idx : sectorData.m_macroMaterials) + { + if (m_macroMaterials.GetData(idx).m_entityId == entityId) + { + idx = sectorData.m_macroMaterials.back(); + sectorData.m_macroMaterials.pop_back(); + } + } + } + else if (overlapsNew && !overlapsOld) + { + // Add the macro material to this sector + if (sectorData.m_macroMaterials.size() < MaxMaterialsPerSector) + { + sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(&materialData)); + } + } + } + m_areaData.m_macroMaterialsUpdated = true; + materialData.m_bounds = newRegion; + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) + { + MacroMaterialData* materialData = FindMacroMaterial(entityId); + + if (materialData) + { + uint16_t destroyedMaterialIndex = m_macroMaterials.GetIndexForData(materialData); + ForOverlappingSectors(materialData->m_bounds, + [&](SectorData& sectorData) { + for (uint16_t& idx : sectorData.m_macroMaterials) + { + if (idx == destroyedMaterialIndex) + { + idx = sectorData.m_macroMaterials.back(); + sectorData.m_macroMaterials.pop_back(); + } + } + }); + } + + m_areaData.m_macroMaterialsUpdated = true; } void TerrainFeatureProcessor::UpdateTerrainData() @@ -165,9 +278,9 @@ namespace Terrain uint32_t width = m_areaData.m_updateWidth; uint32_t height = m_areaData.m_updateHeight; const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; - float queryResolution = m_areaData.m_sampleSpacing; + const float queryResolution = m_areaData.m_sampleSpacing; - AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1); + const AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1); if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != worldSize) { @@ -176,7 +289,7 @@ namespace Terrain height = worldSize.m_height; m_dirtyRegion = worldBounds; - AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); + const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( AZ::RHI::ImageBindFlags::ShaderRead, width, height, AZ::RHI::Format::R16_UNORM ); @@ -210,9 +323,9 @@ namespace Terrain AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f); - float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits::max()); - uint16_t uint16Height = aznumeric_cast(expandedHeight); + const float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f); + const float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits::max()); + const uint16_t uint16Height = aznumeric_cast(expandedHeight); pixels.push_back(uint16Height); } @@ -241,118 +354,248 @@ namespace Terrain m_dirtyRegion = AZ::Aabb::CreateNull(); } + void TerrainFeatureProcessor::PrepareMaterialData() + { + const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); + + m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); + AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); + + m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); + AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); + + m_macroMaterialDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::MacroMaterialData)); + AZ_Error(TerrainFPName, m_macroMaterialDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroMaterialData); + + m_macroMaterialCountIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::MacroMaterialCount)); + AZ_Error(TerrainFPName, m_macroMaterialCountIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroMaterialCount); + + m_macroColorMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroColorMap)); + AZ_Error(TerrainFPName, m_macroColorMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroColorMap); + + m_macroNormalMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroNormalMap)); + AZ_Error(TerrainFPName, m_macroNormalMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroNormalMap); + + m_heightmapPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); + AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); + + TerrainMacroMaterialRequestBus::EnumerateHandlers( + [&](TerrainMacroMaterialRequests* handler) + { + MaterialInstance macroMaterial; + AZ::Aabb bounds; + handler->GetTerrainMacroMaterialData(macroMaterial, bounds); + AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId()); + OnTerrainMacroMaterialCreated(entityId, macroMaterial, bounds); + return true; + } + ); + TerrainMacroMaterialNotificationBus::Handler::BusConnect(); + } + + void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material) + { + // Since we're using an actual macro material instance for now, get the values from it that we care about. + const auto materialLayout = material->GetMaterialPropertiesLayout(); + + const AZ::RPI::MaterialPropertyIndex macroColorTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroColorTextureMap)); + AZ_Error(TerrainFPName, macroColorTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroColorTextureMap); + + const AZ::RPI::MaterialPropertyIndex macroNormalTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalTextureMap)); + AZ_Error(TerrainFPName, macroNormalTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalTextureMap); + + const AZ::RPI::MaterialPropertyIndex macroNormalFlipXIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipX)); + AZ_Error(TerrainFPName, macroNormalFlipXIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipX); + + const AZ::RPI::MaterialPropertyIndex macroNormalFlipYIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipY)); + AZ_Error(TerrainFPName, macroNormalFlipYIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipY); + + const AZ::RPI::MaterialPropertyIndex macroNormalFactorIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFactor)); + AZ_Error(TerrainFPName, macroNormalFactorIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFactor); + + macroMaterialData.m_colorImage = material->GetPropertyValue(macroColorTextureMapIndex).GetValue>(); + macroMaterialData.m_normalImage = material->GetPropertyValue(macroNormalTextureMapIndex).GetValue>(); + macroMaterialData.m_normalFlipX = material->GetPropertyValue(macroNormalFlipXIndex).GetValue(); + macroMaterialData.m_normalFlipY = material->GetPropertyValue(macroNormalFlipYIndex).GetValue(); + macroMaterialData.m_normalFactor = material->GetPropertyValue(macroNormalFactorIndex).GetValue(); + + if (macroMaterialData.m_bounds.IsValid()) + { + m_areaData.m_macroMaterialsUpdated = true; + } + } + void TerrainFeatureProcessor::ProcessSurfaces(const FeatureProcessor::RenderPacket& process) { AZ_PROFILE_FUNCTION(AzRender); + + const AZ::Aabb& terrainBounds = m_areaData.m_terrainBounds; - if (!m_areaData.m_terrainBounds.IsValid()) + if (!terrainBounds.IsValid()) { return; } - - if (m_areaData.m_propertiesDirty && m_materialInstance && m_materialInstance->CanCompile()) + + if (m_materialInstance && m_materialInstance->CanCompile()) { - UpdateTerrainData(); - - m_areaData.m_propertiesDirty = false; - m_sectorData.clear(); - - AZ::RPI::MaterialPropertyIndex heightmapPropertyIndex = - m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); - AZ_Error(TerrainFPName, heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); - AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; - m_materialInstance->SetPropertyValue(heightmapPropertyIndex, heightmapImage); - m_materialInstance->Compile(); - - const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); - - m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); - AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); - - m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); - AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); - - float xFirstPatchStart = - m_areaData.m_terrainBounds.GetMin().GetX() - fmod(m_areaData.m_terrainBounds.GetMin().GetX(), GridMeters); - float xLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetX() - fmod(m_areaData.m_terrainBounds.GetMax().GetX(), GridMeters); - float yFirstPatchStart = - m_areaData.m_terrainBounds.GetMin().GetY() - fmod(m_areaData.m_terrainBounds.GetMin().GetY(), GridMeters); - float yLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetY() - fmod(m_areaData.m_terrainBounds.GetMax().GetY(), GridMeters); - - for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) + if (m_areaData.m_rebuildSectors) { - for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) + // Something about the whole world changed, so the sectors need to be rebuilt + + m_areaData.m_rebuildSectors = false; + + m_sectorData.clear(); + const float xFirstPatchStart = terrainBounds.GetMin().GetX() - fmod(terrainBounds.GetMin().GetX(), GridMeters); + const float xLastPatchStart = terrainBounds.GetMax().GetX() - fmod(terrainBounds.GetMax().GetX(), GridMeters); + const float yFirstPatchStart = terrainBounds.GetMin().GetY() - fmod(terrainBounds.GetMin().GetY(), GridMeters); + const float yLastPatchStart = terrainBounds.GetMax().GetY() - fmod(terrainBounds.GetMax().GetY(), GridMeters); + + const auto& materialAsset = m_materialInstance->GetAsset(); + const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); + + for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) { - const auto& materialAsset = m_materialInstance->GetAsset(); - auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); - auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); - if (!objectSrg) + for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) { - AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); - continue; - } - - { // Update SRG - - AZStd::array uvMin = { 0.0f, 0.0f }; - AZStd::array uvMax = { 1.0f, 1.0f }; - - uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); - uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); - - uvMax[0] = - (float)(((xPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); - uvMax[1] = - (float)(((yPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); - - AZStd::array uvStep = + auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); + if (!objectSrg) { - 1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight, - }; - - AZ::Transform transform = m_areaData.m_transform; - transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); - - AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); - - objectSrg->SetConstant(m_modelToWorldIndex, matrix3x4); - - ShaderTerrainData terrainDataForSrg; - terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; - terrainDataForSrg.m_heightScale = m_areaData.m_heightScale; - terrainDataForSrg.m_uvMin = uvMin; - terrainDataForSrg.m_uvMax = uvMax; - terrainDataForSrg.m_uvStep = uvStep; - objectSrg->SetConstant(m_terrainDataIndex, terrainDataForSrg); - - objectSrg->Compile(); - } - - m_sectorData.push_back(); - SectorData& sectorData = m_sectorData.back(); - - for (auto& lod : m_patchModel->GetLods()) - { - AZ::RPI::ModelLod& modelLod = *lod.get(); - sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); - AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); - - // set the shader option to select forward pass IBL specular if necessary - if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) - { - AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); + continue; } - uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; - drawPacket.SetStencilRef(stencilRef); - drawPacket.Update(*GetParentScene(), true); + + m_sectorData.push_back(); + SectorData& sectorData = m_sectorData.back(); + + for (auto& lod : m_patchModel->GetLods()) + { + AZ::RPI::ModelLod& modelLod = *lod.get(); + sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); + AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); + + // set the shader option to select forward pass IBL specular if necessary + if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) + { + AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + } + const uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; + drawPacket.SetStencilRef(stencilRef); + drawPacket.Update(*GetParentScene(), true); + } + + sectorData.m_aabb = + AZ::Aabb::CreateFromMinMax( + AZ::Vector3(xPatch, yPatch, terrainBounds.GetMin().GetZ()), + AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, terrainBounds.GetMax().GetZ()) + ); + sectorData.m_srg = objectSrg; + } + } + + if (m_areaData.m_macroMaterialsUpdated) + { + // sectors were rebuilt, so any cached macro material data needs to be regenerated + for (SectorData& sectorData : m_sectorData) + { + for (MacroMaterialData& macroMaterialData : m_macroMaterials.GetDataVector()) + { + if (macroMaterialData.m_bounds.Overlaps(sectorData.m_aabb)) + { + sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(¯oMaterialData)); + if (sectorData.m_macroMaterials.size() == MaxMaterialsPerSector) + { + break; + } + } + } + } + } + } + + if (m_areaData.m_heightmapUpdated) + { + UpdateTerrainData(); + + const AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; + m_materialInstance->SetPropertyValue(m_heightmapPropertyIndex, heightmapImage); + m_materialInstance->Compile(); + } + + if (m_areaData.m_heightmapUpdated || m_areaData.m_macroMaterialsUpdated) + { + // Currently when anything in the heightmap changes we're updating all the srgs, but this could probably + // be optimized to only update the srgs that changed. + + m_areaData.m_heightmapUpdated = false; + m_areaData.m_macroMaterialsUpdated = false; + + for (SectorData& sectorData : m_sectorData) + { + ShaderTerrainData terrainDataForSrg; + + const float xPatch = sectorData.m_aabb.GetMin().GetX(); + const float yPatch = sectorData.m_aabb.GetMin().GetY(); + + terrainDataForSrg.m_uvMin = { + (xPatch - terrainBounds.GetMin().GetX()) / terrainBounds.GetXExtent(), + (yPatch - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() + }; + + terrainDataForSrg.m_uvMax = { + ((xPatch + GridMeters) - terrainBounds.GetMin().GetX()) / terrainBounds.GetXExtent(), + ((yPatch + GridMeters) - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() + }; + + terrainDataForSrg.m_uvStep = + { + 1.0f / m_areaData.m_heightmapImageWidth, + 1.0f / m_areaData.m_heightmapImageHeight, + }; + + AZ::Transform transform = m_areaData.m_transform; + transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); + + terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; + terrainDataForSrg.m_heightScale = terrainBounds.GetZExtent(); + + sectorData.m_srg->SetConstant(m_terrainDataIndex, terrainDataForSrg); + + AZStd::array macroMaterialData; + for (uint32_t i = 0; i < sectorData.m_macroMaterials.size(); ++i) + { + const MacroMaterialData& materialData = m_macroMaterials.GetData(sectorData.m_macroMaterials.at(i)); + ShaderMacroMaterialData& shaderData = macroMaterialData.at(i); + const AZ::Aabb& materialBounds = materialData.m_bounds; + + shaderData.m_uvMin = { + (xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), + (yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + }; + shaderData.m_uvMax = { + ((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), + ((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + }; + shaderData.m_normalFactor = materialData.m_normalFactor; + shaderData.m_flipNormalX = materialData.m_normalFlipX; + shaderData.m_flipNormalY = materialData.m_normalFlipY; + + const AZ::RHI::ImageView* colorImageView = materialData.m_colorImage ? materialData.m_colorImage->GetImageView() : nullptr; + sectorData.m_srg->SetImageView(m_macroColorMapIndex, colorImageView, i); + + const AZ::RHI::ImageView* normalImageView = materialData.m_normalImage ? materialData.m_normalImage->GetImageView() : nullptr; + sectorData.m_srg->SetImageView(m_macroNormalMapIndex, normalImageView, i); + + // set flags for which images are used. + shaderData.m_mapsInUse = (colorImageView ? ColorImageUsed : 0) | (normalImageView ? NormalImageUsed : 0); } - sectorData.m_aabb = - AZ::Aabb::CreateFromMinMax( - AZ::Vector3(xPatch, yPatch, m_areaData.m_terrainBounds.GetMin().GetZ()), - AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, m_areaData.m_terrainBounds.GetMax().GetZ()) - ); - sectorData.m_srg = objectSrg; + sectorData.m_srg->SetConstantArray(m_macroMaterialDataIndex, macroMaterialData); + sectorData.m_srg->SetConstant(m_macroMaterialCountIndex, aznumeric_cast(sectorData.m_macroMaterials.size())); + + const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); + sectorData.m_srg->SetConstant(m_modelToWorldIndex, matrix3x4); + + sectorData.m_srg->Compile(); } } } @@ -366,12 +609,20 @@ namespace Terrain { if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0) { - AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); - AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); - AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); + const AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); + const AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); + const AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); - float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); - float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / (GridMeters * 4.0f)))); + const float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); + + // This will be configurable later + const float minDistanceForLod0 = (GridMeters * 4.0f); + + // For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD + // is exactly half the resolution of the last. + const float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); + + // All cameras should render the same LOD so effects like shadows are consistent. lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); } } @@ -382,7 +633,7 @@ namespace Terrain AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); if (viewFrustum.IntersectAabb(sectorData.m_aabb) != AZ::IntersectResult::Exterior) { - uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); + const uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); view->AddDrawPacket(sectorData.m_drawPackets.at(lodToRender).GetRHIDrawPacket()); } } @@ -395,9 +646,8 @@ namespace Terrain patchdata.m_uvs.clear(); patchdata.m_indices.clear(); - uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. - size_t size = gridVertices * gridVertices; - size *= size; + const uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. + const size_t size = gridVertices * gridVertices; patchdata.m_positions.reserve(size); patchdata.m_uvs.reserve(size); @@ -417,10 +667,10 @@ namespace Terrain { for (uint16_t x = 0; x < gridSize; ++x) { - uint16_t topLeft = y * gridVertices + x; - uint16_t topRight = topLeft + 1; - uint16_t bottomLeft = (y + 1) * gridVertices + x; - uint16_t bottomRight = bottomLeft + 1; + const uint16_t topLeft = y * gridVertices + x; + const uint16_t topRight = topLeft + 1; + const uint16_t bottomLeft = (y + 1) * gridVertices + x; + const uint16_t bottomRight = bottomLeft + 1; patchdata.m_indices.emplace_back(topLeft); patchdata.m_indices.emplace_back(topRight); @@ -469,14 +719,14 @@ namespace Terrain PatchData patchData; InitializeTerrainPatch(gridSize, gridSpacing, patchData); - auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); - auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); + const auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); + const auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); - auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT); - auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs"); + const auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT); + const auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs"); - auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); - auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); + const auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); + const auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); if (!positionsOutcome.IsSuccess() || !uvsOutcome.IsSuccess() || !indicesOutcome.IsSuccess()) { @@ -514,7 +764,7 @@ namespace Terrain return success; } - void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const AZ::Data::Instance& material) + void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const MaterialInstance& material) { for (auto& sectorData : m_sectorData) { @@ -530,4 +780,57 @@ namespace Terrain // This will control the max rendering size. Actual terrain size can be much // larger but this will limit how much is rendered. } + + TerrainFeatureProcessor::MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId) + { + for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) + { + if (data.m_entityId == entityId) + { + return &data; + } + } + return nullptr; + } + + TerrainFeatureProcessor::MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId) + { + MacroMaterialData* dataPtr = FindMacroMaterial(entityId); + if (dataPtr != nullptr) + { + return *dataPtr; + } + + const uint16_t slotId = m_macroMaterials.GetFreeSlotIndex(); + AZ_Assert(slotId != m_macroMaterials.NoFreeSlot, "Ran out of indices for macro materials"); + + MacroMaterialData& data = m_macroMaterials.GetData(slotId); + data.m_entityId = entityId; + return data; + } + + void TerrainFeatureProcessor::RemoveMacroMaterial(AZ::EntityId entityId) + { + for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) + { + if (data.m_entityId == entityId) + { + m_macroMaterials.RemoveData(&data); + return; + } + } + AZ_Assert(false, "Entity Id not found in m_macroMaterials.") + } + + template + void TerrainFeatureProcessor::ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback) + { + for (SectorData& sectorData : m_sectorData) + { + if (sectorData.m_aabb.Overlaps(bounds)) + { + callback(sectorData); + } + } + } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index d8df9b328c..d9f15f2d47 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -11,11 +11,13 @@ #include #include +#include #include #include #include #include +#include namespace AZ::RPI { @@ -25,6 +27,7 @@ namespace AZ::RPI } class Material; class Model; + class StreamingImage; } namespace Terrain @@ -33,6 +36,7 @@ namespace Terrain : public AZ::RPI::FeatureProcessor , private AZ::RPI::MaterialReloadNotificationBus::Handler , private AzFramework::Terrain::TerrainDataNotificationBus::Handler + , private TerrainMacroMaterialNotificationBus::Handler { public: AZ_RTTI(TerrainFeatureProcessor, "{D7DAC1F9-4A9F-4D3C-80AE-99579BF8AB1C}", AZ::RPI::FeatureProcessor); @@ -52,6 +56,15 @@ namespace Terrain void SetWorldSize(AZ::Vector2 sizeInMeters); private: + + using MaterialInstance = AZ::Data::Instance; + static constexpr uint32_t MaxMaterialsPerSector = 4; + + enum MacroMaterialFlags + { + ColorImageUsed = 0b01, + NormalImageUsed = 0b10, + }; struct ShaderTerrainData // Must align with struct in Object Srg { @@ -61,7 +74,17 @@ namespace Terrain float m_sampleSpacing; float m_heightScale; }; - + + struct ShaderMacroMaterialData + { + AZStd::array m_uvMin; + AZStd::array m_uvMax; + float m_normalFactor; + uint32_t m_flipNormalX{ 0 }; // bool in shader + uint32_t m_flipNormalY{ 0 }; // bool in shader + uint32_t m_mapsInUse{ 0b00 }; // 0b01 = color, 0b10 = normal + }; + struct VertexPosition { float m_posx; @@ -81,21 +104,56 @@ namespace Terrain AZStd::vector m_indices; }; + struct SectorData + { + AZ::Data::Instance m_srg; // Hold on to ref so it's not dropped + AZ::Aabb m_aabb; + AZStd::fixed_vector m_drawPackets; + AZStd::fixed_vector m_macroMaterials; + }; + + struct MacroMaterialData + { + AZ::EntityId m_entityId; + AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); + + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; + bool m_normalFlipX{ false }; + bool m_normalFlipY{ false }; + float m_normalFactor{ 0.0f }; + }; + // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... - void OnMaterialReinitialized(const AZ::Data::Instance& material) override; + void OnMaterialReinitialized(const MaterialInstance& material) override; // AzFramework::Terrain::TerrainDataNotificationBus overrides... void OnTerrainDataDestroyBegin() override; void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + // TerrainMacroMaterialNotificationBus overrides... + void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) override; + void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance material) override; + void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; + void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override; + void Initialize(); void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata); bool InitializePatchModel(); void UpdateTerrainData(); + void PrepareMaterialData(); + void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material); void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); + MacroMaterialData* FindMacroMaterial(AZ::EntityId entityId); + MacroMaterialData& FindOrCreateMacroMaterial(AZ::EntityId entityId); + void RemoveMacroMaterial(AZ::EntityId entityId); + + template + void ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback); + AZ::Outcome> CreateBufferAsset( const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName); @@ -105,10 +163,15 @@ namespace Terrain static constexpr float GridMeters{ GridSpacing * GridSize }; AZStd::unique_ptr m_materialAssetLoader; - AZ::Data::Instance m_materialInstance; + MaterialInstance m_materialInstance; AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex; AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex; + AZ::RHI::ShaderInputConstantIndex m_macroMaterialDataIndex; + AZ::RHI::ShaderInputConstantIndex m_macroMaterialCountIndex; + AZ::RHI::ShaderInputImageIndex m_macroColorMapIndex; + AZ::RHI::ShaderInputImageIndex m_macroNormalMapIndex; + AZ::RPI::MaterialPropertyIndex m_heightmapPropertyIndex; AZ::Data::Instance m_patchModel; @@ -117,26 +180,22 @@ namespace Terrain { AZ::Transform m_transform{ AZ::Transform::CreateIdentity() }; AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() }; - float m_heightScale{ 0.0f }; AZ::Data::Instance m_heightmapImage; uint32_t m_heightmapImageWidth{ 0 }; uint32_t m_heightmapImageHeight{ 0 }; uint32_t m_updateWidth{ 0 }; uint32_t m_updateHeight{ 0 }; - bool m_propertiesDirty{ true }; float m_sampleSpacing{ 0.0f }; + bool m_heightmapUpdated{ true }; + bool m_macroMaterialsUpdated{ true }; + bool m_rebuildSectors{ true }; }; TerrainAreaData m_areaData; AZ::Aabb m_dirtyRegion{ AZ::Aabb::CreateNull() }; - struct SectorData - { - AZ::Data::Instance m_srg; // Hold on to ref so it's not dropped - AZ::Aabb m_aabb; - AZStd::fixed_vector m_drawPackets; - }; - AZStd::vector m_sectorData; + + AZ::Render::IndexedDataVector m_macroMaterials; }; } diff --git a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp index abcdce99b9..f6e6a1254c 100644 --- a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp +++ b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp @@ -757,9 +757,7 @@ namespace TextureAtlasBuilder if (input.m_presetName.empty()) { - // Default to the TextureAtlas preset which is currently set to use compression for all platforms except for iOS. - // Currently the only fully supported compression for iOS is PVRTC which requires the texture to be square and a power of 2. - // Due to this limitation, we default to using no compression for iOS until ASTC is fully supported + // Default to the TextureAtlas preset which is currently set to use compression const AZStd::string defaultPresetName = "UserInterface_Compressed"; input.m_presetName = defaultPresetName; } diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_a_pressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_a_pressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_a_pressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_a_unpressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_a_unpressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_a_unpressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_b_pressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_b_pressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_b_pressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_b_unpressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_b_unpressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_b_unpressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_x_pressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_x_pressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_x_pressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_x_unpressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_x_unpressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_x_unpressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_y_pressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_y_pressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_y_pressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_y_unpressed.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_y_unpressed.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_button_y_unpressed.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_thumbstick_centre.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_thumbstick_centre.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_thumbstick_centre.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_thumbstick_radial.tif.exportsettings b/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_thumbstick_radial.tif.exportsettings deleted file mode 100644 index 1415bea891..0000000000 --- a/Gems/VirtualGamepad/Assets/UI/Textures/VirtualGamepad/virtual_gamepad_thumbstick_radial.tif.exportsettings +++ /dev/null @@ -1 +0,0 @@ -/autooptimizefile=0 /dns=1 /preset=ReferenceImage_Linear /reduce=0 /ser=0 diff --git a/Registry/AssetProcessorPlatformConfig.setreg b/Registry/AssetProcessorPlatformConfig.setreg index 3e90b063b5..ddc465c201 100644 --- a/Registry/AssetProcessorPlatformConfig.setreg +++ b/Registry/AssetProcessorPlatformConfig.setreg @@ -106,7 +106,7 @@ // "exclude": "mac" // } - "ScanFolder Game": { + "ScanFolder Project/Assets": { "watch": "@PROJECTROOT@", "display": "@PROJECTNAME@", "recursive": 1, @@ -129,6 +129,11 @@ "order": 30000, "include": "tools,renderer" }, + "ScanFolder Engine/Registry": { + "watch": "@ENGINEROOT@/Registry", + "recursive": 1, + "order": 40000 + }, // Excludes files that match the pattern or glob // if you use a pattern, remember to escape your backslashes (\\) diff --git a/Registry/bootstrap.setreg b/Registry/bootstrap.setreg index 3a15d59450..b2bcdd7f3f 100644 --- a/Registry/bootstrap.setreg +++ b/Registry/bootstrap.setreg @@ -17,7 +17,7 @@ "remote_port": 45643, "connect_to_remote": 0, "windows_connect_to_remote": 1, - "linux_connect_to_remote": 0, + "linux_connect_to_remote": 1, "provo_connect_to_remote": 1, "salem_connect_to_remote": 0, "jasper_connect_to_remote": 0, @@ -29,7 +29,7 @@ "salem_wait_for_connect": 0, "jasper_wait_for_connect": 0, "windows_wait_for_connect": 1, - "linux_wait_for_connect": 0, + "linux_wait_for_connect": 1, "android_wait_for_connect": 0, "ios_wait_for_connect": 0, "mac_wait_for_connect": 0, diff --git a/Registry/setregbuilder.assetprocessor.setreg b/Registry/setregbuilder.assetprocessor.setreg index 25b51470bf..d67b6047f6 100644 --- a/Registry/setregbuilder.assetprocessor.setreg +++ b/Registry/setregbuilder.assetprocessor.setreg @@ -20,7 +20,13 @@ "Excludes": [ "/Amazon/AzCore/Runtime", - "/Amazon/AzCore/Bootstrap/project_path" + "/Amazon/AzCore/Bootstrap/engine_path", + "/Amazon/AzCore/Bootstrap/project_path", + "/Amazon/AzCore/Bootstrap/project_cache_path", + "/Amazon/AzCore/Bootstrap/project_user_path", + "/Amazon/AzCore/Bootstrap/project_log_path", + "/Amazon/Project/Settings/Build/project_build_path", + "/O3DE/Runtime" ] } } diff --git a/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg b/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg index a42f65efb4..05f6314da4 100644 --- a/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg +++ b/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg @@ -1,14 +1,21 @@ { - "Amazon": - { - "${Name}.Assets": - { - "SourcePaths": - [ - "Assets", - "ShaderLib", - "Shaders" - ] + "Amazon": { + "AssetProcessor": { + "ScanFolder Project/ShaderLib": { + "watch": "@PROJECTROOT@/ShaderLib", + "recursive": 1, + "order": 1 + }, + "ScanFolder Project/Shaders": { + "watch": "@PROJECTROOT@/Shaders", + "recurisve": 1, + "order": 2 + }, + "ScanFolder Project/Registry": { + "watch": "@PROJECTROOT@/Registry", + "recursive": 1, + "order": 3 + } } } -} \ No newline at end of file +} diff --git a/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg b/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg index a42f65efb4..05f6314da4 100644 --- a/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg +++ b/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg @@ -1,14 +1,21 @@ { - "Amazon": - { - "${Name}.Assets": - { - "SourcePaths": - [ - "Assets", - "ShaderLib", - "Shaders" - ] + "Amazon": { + "AssetProcessor": { + "ScanFolder Project/ShaderLib": { + "watch": "@PROJECTROOT@/ShaderLib", + "recursive": 1, + "order": 1 + }, + "ScanFolder Project/Shaders": { + "watch": "@PROJECTROOT@/Shaders", + "recurisve": 1, + "order": 2 + }, + "ScanFolder Project/Registry": { + "watch": "@PROJECTROOT@/Registry", + "recursive": 1, + "order": 3 + } } } -} \ No newline at end of file +} diff --git a/Tools/LyTestTools/ly_test_tools/__init__.py b/Tools/LyTestTools/ly_test_tools/__init__.py index 58340342ab..972e97d488 100755 --- a/Tools/LyTestTools/ly_test_tools/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/__init__.py @@ -16,7 +16,7 @@ ALL_PLATFORM_OPTIONS = ['android', 'ios', 'linux', 'mac', 'windows'] ALL_LAUNCHER_OPTIONS = ['android', 'base', 'linux', 'mac', 'windows', 'windows_editor', 'windows_dedicated', 'windows_generic'] ANDROID = False IOS = False # Not implemented - see SPEC-2505 -LINUX = sys.platform.startswith('linux') # Not implemented - see SPEC-2501 +LINUX = sys.platform.startswith('linux') MAC = sys.platform.startswith('darwin') WINDOWS = sys.platform.startswith('win') @@ -54,9 +54,11 @@ elif LINUX: HOST_OS_PLATFORM = 'linux' HOST_OS_EDITOR = 'linux_editor' HOST_OS_DEDICATED_SERVER = 'linux_dedicated' - from ly_test_tools.launchers.platforms.linux.launcher import (LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher) + HOST_OS_GENERIC_EXECUTABLE = 'linux_generic' + from ly_test_tools.launchers.platforms.linux.launcher import (LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher, LinuxGenericLauncher) LAUNCHERS['linux'] = LinuxLauncher LAUNCHERS['linux_editor'] = LinuxEditor LAUNCHERS['linux_dedicated'] = DedicatedLinuxLauncher + LAUNCHERS['linux_generic'] = LinuxGenericLauncher else: logger.warning(f'WARNING: LyTestTools only supports Windows, Mac, and Linux. Unexpectedly detected HOST_OS_PLATFORM: "{HOST_OS_PLATFORM}".') diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py index 77d58e3a48..fccc94bdcc 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py @@ -265,7 +265,7 @@ class AbstractResourceLocator(object): Return path to AssetProcessor's log file using the project bin dir :return: path to 'AP_Gui.log' file in folder """ - return os.path.join(self.ap_log_dir(), 'AP_Gui.log') + return os.path.join(self.ap_log_dir(), 'AP_GUI.log') def project_cache(self): """ diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py index cdbd379183..fea60cc9cb 100644 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py @@ -23,9 +23,6 @@ class _LinuxResourceManager(AbstractResourceLocator): """ Override for locating resources in a Linux operating system running LyTestTools. """ - def __init__(self, build_directory: str, project: str): - pass - def platform_config_file(self): """ :return: path to the platform config file diff --git a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py index 1c0be299a8..f8245495fa 100755 --- a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py +++ b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py @@ -31,45 +31,59 @@ def kill_processes_named(names, ignore_extensions=False): Kills all processes with a given name :param names: string process name, or list of strings of process name - :param ignore_extensions: ignore trailing file extension + :param ignore_extensions: ignore trailing file extensions. By default 'abc.exe' will not match 'abc'. Note that + enabling this will cause 'abc.exe' to match 'abc', 'abc.bat', and 'abc.sh', though 'abc.GameLauncher.exe' + will not match 'abc.DedicatedServer' """ if not names: return - names = [names] if isinstance(names, str) else names + name_set = set() + if isinstance(names, str): + name_set.add(names) + else: + name_set.update(names) if ignore_extensions: - names = [_remove_extension(name) for name in names] + # both exact matches and extensionless + stripped_names = set() + for name in name_set: + stripped_names.add(_remove_extension(name)) + name_set.update(stripped_names) # remove any blank names, which may empty the list - names = list(filter(lambda x: not x.isspace(), names)) - if not names: + name_set = set(filter(lambda x: not x.isspace(), name_set)) + if not name_set: return - logger.info(f"Killing all processes named {names}") - process_list_to_kill = [] + logger.info(f"Killing all processes named {name_set}") + process_set_to_kill = set() for process in _safe_get_processes(['name', 'pid']): try: proc_name = process.name() except psutil.AccessDenied: - logger.info(f"Process {process} permissions error during kill_processes_named()", exc_info=True) + logger.warning(f"Process {process} permissions error during kill_processes_named()", exc_info=True) continue except psutil.ProcessLookupError: - logger.debug(f"Process {process} could not be killed during kill_processes_named() and was likely already stopped", exc_info=True) + logger.debug(f"Process {process} could not be killed during kill_processes_named() and was likely already " + f"stopped", exc_info=True) continue except psutil.NoSuchProcess: logger.debug(f"Process '{process}' was active when list of processes was requested but it was not found " f"during kill_processes_named()", exc_info=True) continue + if proc_name in name_set: + logger.debug(f"Found process with name {proc_name}.") + process_set_to_kill.add(process) + if ignore_extensions: - proc_name = _remove_extension(proc_name) + extensionless_name = _remove_extension(proc_name) + if extensionless_name in name_set: + process_set_to_kill.add(process) - if proc_name in names: - logger.debug(f"Found process with name {proc_name}. Attempting to kill...") - process_list_to_kill.append(process) - - _safe_kill_process_list(process_list_to_kill) + if process_set_to_kill: + _safe_kill_processes(process_set_to_kill) def kill_processes_started_from(path): @@ -90,7 +104,7 @@ def kill_processes_started_from(path): if process_path.lower().startswith(path.lower()): process_list.append(process) - _safe_kill_process_list(process_list) + _safe_kill_processes(process_list) else: logger.warning(f"Path:'{path}' not found") @@ -118,7 +132,7 @@ def kill_processes_with_name_not_started_from(name, path): logger.info("%s -> %s" % (os.path.dirname(process_path.lower()), path)) proccesses_to_kill.append(process) - _safe_kill_process_list(proccesses_to_kill) + _safe_kill_processes(proccesses_to_kill) else: logger.warning(f"Path:'{path}' not found") @@ -151,10 +165,12 @@ def process_exists(name, ignore_extensions=False): :return: A boolean determining whether the process is alive or not """ name = name.lower() - if ignore_extensions: - name = _remove_extension(name) if name.isspace(): return False + + if ignore_extensions: + name_extensionless = _remove_extension(name) + for process in _safe_get_processes(["name"]): try: proc_name = process.name().lower() @@ -165,10 +181,17 @@ def process_exists(name, ignore_extensions=False): except psutil.AccessDenied as e: logger.info(f"Permissions issue on {process} during process_exists check", exc_info=True) continue - if ignore_extensions: - proc_name = _remove_extension(proc_name) - if proc_name == name: + + if proc_name == name: # abc.exe matches abc.exe return True + if ignore_extensions: + proc_name_extensionless = _remove_extension(proc_name) + if proc_name_extensionless == name: # abc matches abc.exe + return True + if proc_name == name_extensionless: # abc.exe matches abc + return True + # don't check proc_name_extensionless against name_extensionless: abc.exe and abc.exe are already tested, + # however xyz.Gamelauncher should not match xyz.DedicatedServer return False @@ -341,17 +364,14 @@ def _safe_kill_process(proc): except Exception: # purposefully broad logger.warning("Unexpected exception while terminating process", exc_info=True) -def _safe_kill_process_list(proc_list): + +def _safe_kill_processes(processes): """ Kills a given process without raising an error - :param proc_list: The process list to kill + :param processes: An iterable of processes to kill """ - - def on_terminate(proc): - print(f"process '{proc.name()}' with id '{proc.pid}' terminated with exit code {proc.returncode}") - - for proc in proc_list: + for proc in processes: try: logger.info(f"Terminating process '{proc.name()}' with id '{proc.pid}'") proc.kill() @@ -360,12 +380,14 @@ def _safe_kill_process_list(proc_list): except psutil.NoSuchProcess: logger.debug("Termination request ignored, process was already terminated during iteration", exc_info=True) except Exception: # purposefully broad - logger.warning("Unexpected exception while terminating process", exc_info=True) + logger.warning("Unexpected exception ignored while terminating process", exc_info=True) + def on_terminate(proc): + logger.info(f"process '{proc.name()}' with id '{proc.pid}' terminated with exit code {proc.returncode}") try: - psutil.wait_procs(proc_list, timeout=30, callback=on_terminate) + psutil.wait_procs(processes, timeout=30, callback=on_terminate) except Exception: # purposefully broad - logger.warning("Unexpected exception while waiting for process to terminate", exc_info=True) + logger.warning("Unexpected exception while waiting for processes to terminate", exc_info=True) def _terminate_and_confirm_dead(proc): @@ -383,7 +405,7 @@ def _terminate_and_confirm_dead(proc): def _remove_extension(filename): """ - Returns a file name without its extension + Returns a file name without its extension, if any is present :param filename: The name of a file :return: The name of the file without the extension @@ -465,3 +487,17 @@ def close_windows_process(pid, timeout=20, raise_on_missing=False): # Wait for asyncronous termination waiter.wait_for(lambda: pid not in psutil.pids(), timeout=timeout, exc=TimeoutError(f"Process {pid} never terminated")) + + +def get_display_env(): + """ + Fetches environment variables with an appropriate display (monitor) configured, + useful for subprocess calls to UI applications + :return: A dictionary containing environment variables (per os.environ) + """ + env = os.environ.copy() + if not ly_test_tools.WINDOWS: + if 'DISPLAY' not in env.keys(): + # assume Display 1 is available in another session + env['DISPLAY'] = ':1' + return env diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py index 42adee91ea..75eaa65e94 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py @@ -208,13 +208,6 @@ class AndroidLauncher(Launcher): with self.android_vfs_setreg_path.open('w') as android_vfs_setreg: json.dump(vfs_settings, android_vfs_setreg, indent=4) - self.workspace.settings.modify_platform_setting('r_AssetProcessorShaderCompiler', 1) - self.workspace.settings.modify_platform_setting('r_ShadersAsyncCompiling', 0) - self.workspace.settings.modify_platform_setting('r_ShadersRemoteCompiler', 1) - self.workspace.settings.modify_platform_setting('r_ShadersAllowCompilation', 1) - self.workspace.settings.modify_platform_setting('r_ShadersAsyncActivation', 0) - self.workspace.settings.modify_platform_setting('r_ShaderCompilerServer', '127.0.0.1') - self.workspace.settings.modify_platform_setting('r_ShaderCompilerPort', '61453') self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", '127.0.0.1') def launch(self): diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py index 210519d197..112629b349 100644 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py @@ -13,6 +13,7 @@ import subprocess import ly_test_tools.environment.waiter import ly_test_tools.launchers.exceptions +import ly_test_tools.environment.process_utils as process_utils from ly_test_tools.launchers.platforms.base import Launcher from ly_test_tools.launchers.exceptions import TeardownError, ProcessNotStartedError @@ -68,7 +69,7 @@ class LinuxLauncher(Launcher): """ command = [self.binary_path()] + self.args self._tmpout = TemporaryFile() - self._proc = subprocess.Popen(command, stdout=self._tmpout, stderr=self._tmpout, universal_newlines=True) + self._proc = subprocess.Popen(command, stdout=self._tmpout, stderr=self._tmpout, universal_newlines=True, env=process_utils.get_display_env()) log.debug(f"Started Linux Launcher with command: {command}") def get_output(self, encoding="utf-8"): @@ -172,7 +173,6 @@ class LinuxLauncher(Launcher): self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"') self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"') - self.workspace.settings.modify_platform_setting("r_AssetProcessorShaderCompiler", 1) self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip) self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip) @@ -222,3 +222,28 @@ class LinuxEditor(LinuxLauncher): """ assert self.workspace.project is not None return os.path.join(self.workspace.paths.build_directory(), "Editor") + + +class LinuxGenericLauncher(LinuxLauncher): + + def __init__(self, build, exe_file_name, args=None): + super(LinuxLauncher, self).__init__(build, args) + self.exe_file_name = exe_file_name + self.expected_executable_path = os.path.join( + self.workspace.paths.build_directory(), f"{self.exe_file_name}") + + if not os.path.exists(self.expected_executable_path): + raise ProcessNotStartedError( + f"Unable to locate executable '{self.exe_file_name}' " + f"in path: '{self.expected_executable_path}'") + + def binary_path(self): + """ + Return full path to the executable file for this build's configuration and project + Relies on the build_directory() in self.workspace.paths to be accurate + + :return: full path to the given exe file + """ + assert self.workspace.project is not None, ( + 'Project cannot be NoneType - please specify a project name string.') + return self.expected_executable_path diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py index 4f904b0a40..d18431ba82 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py @@ -172,8 +172,6 @@ class WinLauncher(Launcher): self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"') self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"') - self.workspace.settings.modify_platform_setting("r_AssetProcessorShaderCompiler", 1) - self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip) self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py index 57454930eb..682fcd3560 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py @@ -7,21 +7,22 @@ SPDX-License-Identifier: Apache-2.0 OR MIT A class to control functionality of Lumberyard's asset processor. The class manages a workspace's asset processor and asset configurations. """ -import os -import datetime import logging -import subprocess -import socket -import time -import tempfile -import shutil -import stat -from typing import List, Tuple +import os import psutil +import shutil +import socket +import stat +import subprocess +import tempfile +import time + +from typing import List, Tuple import ly_test_tools -import ly_test_tools.environment.waiter as waiter import ly_test_tools.environment.file_system as file_system +import ly_test_tools.environment.process_utils as process_utils +import ly_test_tools.environment.waiter as waiter import ly_test_tools.o3de.pipeline_utils as utils from ly_test_tools.o3de.ap_log_parser import APLogParser @@ -177,24 +178,26 @@ class AssetProcessor(object): """ Read the a port chosen by AP from the log """ - start_time = time.time() - read_port_timeout = 10 - while (time.time() - start_time) < read_port_timeout: + port = None + + def _get_port_from_log(): + nonlocal port if not os.path.exists(self._workspace.paths.ap_gui_log()): - logger.debug(f"Log at {self._workspace.paths.ap_gui_log()} doesn't exist, sleeping") - else: - log = APLogParser(self._workspace.paths.ap_gui_log()) - if len(log.runs): - try: - port = log.runs[-1][port_type] - if port: - logger.info(f"Read port type {port_type} : {port}") - return port - except Exception: # intentionally broad - pass - time.sleep(1) - logger.warning(f"Failed to read port type {port_type}") - return 0 + return False + + log = APLogParser(self._workspace.paths.ap_gui_log()) + if len(log.runs): + try: + port = log.runs[-1][port_type] + logger.debug(f"Read port type {port_type} : {port}") + return True + except Exception as ex: # intentionally broad + logger.debug("Failed to read port from file", exc_info=ex) + return False + + err = AssetProcessorError(f"Failed to read port type {port_type} from {self._workspace.paths.ap_gui_log()}") + waiter.wait_for(_get_port_from_log, timeout=10, exc=err) + return port def set_control_connection(self, connection): self._control_connection = connection @@ -206,13 +209,8 @@ class AssetProcessor(object): """ if not self._control_connection: control_timeout = 60 - try: - return self.connect_socket("Control Connection", self.read_control_port, - set_port_method=self.set_control_connection, timeout=control_timeout) - except AssetProcessorError as e: - # We dont want a failure of our test socket connection to fail the entire test automatically. - logger.error(f"Failed to connect control socket with error {e}") - pass + return self.connect_socket("Control Connection", self.read_control_port, + set_port_method=self.set_control_connection, timeout=control_timeout) return True, None def using_temp_workspace(self): @@ -227,34 +225,40 @@ class AssetProcessor(object): :param set_port_method: If set, method to call with the established connection :param timeout: Max seconds to attempt connection for """ - - connection_timeout = timeout connect_port = read_port_method() - logger.debug(f"Waiting for connection to AP {port_name}: {host}:{connect_port}, " - f"{connection_timeout} seconds remaining") - start_time = time.time() - while (time.time() - start_time) < connection_timeout: + logger.debug(f"Attempting to for connect to AP {port_name}: {host}:{connect_port} for {timeout} seconds") + + def _attempt_connection(): + nonlocal connect_port + if self._ap_proc.poll() is not None: + raise AssetProcessorError(f"Asset processor exited early with errorcode: {self._ap_proc.returncode}") + connection_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - connection_socket.settimeout(10.0) + connection_socket.settimeout(timeout) try: connection_socket.connect((host, connect_port)) logger.debug(f"Connection to AP {port_name} was successful") if set_port_method is not None: set_port_method(connection_socket) - return True, None - except Exception: # Purposefully broad - # Short delay to prevent immediate failure due to slower starting applications such as debug builds - time.sleep(0.01) + return True + except Exception as ex: # Purposefully broad + logger.debug(f"Failed to connect to {host}:{connect_port}", exc_info=ex) if not connect_port or not self.using_temp_workspace(): # If we're not using a temp workspace with a fresh log it's possible we're reading a port from # a previous run and the log just hasn't written yet, we need to keep checking the log for a new # port to use - new_connect_port = read_port_method() - if new_connect_port != connect_port: - logger.debug( - f"Read new connect port for {port_name}: {host}:{new_connect_port}") - connect_port = new_connect_port - raise AssetProcessorError(f"Could not connect to AP {port_name}") + try: + new_connect_port = read_port_method() + if new_connect_port != connect_port: + logger.debug(f"Found new connect port for {port_name}: {host}:{new_connect_port}") + connect_port = new_connect_port + except Exception as read_exception: # Purposefully broad + logger.debug(f"Failed to read port data", exc_info=read_exception) + return False + + err = AssetProcessorError(f"Could not connect to AP {port_name} on {host}:{connect_port}") + waiter.wait_for(_attempt_connection, timeout=timeout, exc=err) + return True, None def stop(self, timeout=60): """ @@ -436,15 +440,14 @@ class AssetProcessor(object): extra_params=None, add_gem_scan_folders=None, add_config_scan_folders=None, decode=True, expect_failure=False, quitonidle=False, connect_to_ap=False, accept_input=True, run_until_idle=True, scan_folder_pattern=None): - ap_path = self._workspace.paths.asset_processor() + ap_path = os.path.abspath(self._workspace.paths.asset_processor()) + ap_exe_path = os.path.dirname(ap_path) extra_gui_params = [] if quitonidle: extra_gui_params.append("--quitonidle") if accept_input: extra_gui_params.append("--acceptInput") - ap_exe_path = os.path.dirname(self._workspace.paths.asset_processor()) - logger.info("Starting asset processor") if self.process_exists(): logger.error("Asset processor already started. Stop first") @@ -483,20 +486,34 @@ class AssetProcessor(object): logger.warning(f"Cannot capture output when leaving AP connection open.") logger.info(f"Launching AP with command: {command}") - self._ap_proc = subprocess.Popen(command, cwd=ap_exe_path) + try: + self._ap_proc = subprocess.Popen(command, cwd=ap_exe_path, env=process_utils.get_display_env()) + time.sleep(1) + if self._ap_proc.poll() is not None: + raise AssetProcessorError(f"AssetProcessor immediately quit with errorcode {self._ap_proc.returncode}") - if accept_input and not quitonidle: - self.connect_control() + if accept_input: + self.connect_control() - if connect_to_ap: - self.connect_listen() + if connect_to_ap: + self.connect_listen() - if quitonidle: - waiter.wait_for(lambda: not self.process_exists(), timeout=timeout) - elif run_until_idle and accept_input: - if not self.wait_for_idle(): - return False, None - return True, None + if quitonidle: + waiter.wait_for(lambda: not self.process_exists(), timeout=timeout, + exc=AssetProcessorError(f"Failed to quit on idle within {timeout} seconds")) + elif run_until_idle and accept_input: + if not self.wait_for_idle(): + return False, None + return True, None + except BaseException as be: # purposefully broad + logger.exception("Exception while starting Asset Processor", be) + # clean up to avoid leaking open AP process to future tests + try: + if self._ap_proc: + self._ap_proc.kill() + except Exception as ex: + logger.exception("Ignoring exception while trying to terminate Asset Processor", ex) + raise be # raise whatever prompted us to clean up def connect_listen(self, timeout=DEFAULT_TIMEOUT_SECONDS): # Wait for the AP we launched to be ready to accept a connection @@ -574,21 +591,17 @@ class AssetProcessor(object): expect_failure=False): """ In case of a timeout, the asset processor and associated processes are killed and the function returns False. - + :param timeout: seconds to wait before aborting :param capture_output = Capture output which will be returned in the second of the return pair :param decode: decode byte strings from captured output to utf-8 :param expect_failure: asset processing is expected to fail, so don't error on a failure, and assert on no failure. """ logger.info(f"Launching AP with command: {command}") - start = datetime.datetime.now() - try: - duration = datetime.timedelta(seconds=timeout) - except TypeError: - logger.warning("Cannot set timeout value of '{}' seconds, defaulting to {} hours".format( - timeout, DEFAULT_TIMEOUT_HOURS)) - duration = datetime.timedelta(hours=DEFAULT_TIMEOUT_HOURS) - timeout = duration.total_seconds() + start = time.time() + if type(timeout) not in [int, float] or timeout < 1: + logger.warning(f"Invalid timeout {timeout} - defaulting to {DEFAULT_TIMEOUT_SECONDS} seconds") + timeout = DEFAULT_TIMEOUT_SECONDS run_result = subprocess.run(command, close_fds=True, timeout=timeout, capture_output=capture_output) output_list = None @@ -609,8 +622,7 @@ class AssetProcessor(object): elif expect_failure: logger.error(f"{command} was expected to fail, but instead ran without failure.") return True, output_list - logger.info( - f"{command} completed successfully in {(datetime.datetime.now() - start).seconds} seconds") + logger.info(f"{command} completed successfully in {time.time() - start} seconds") return True, output_list def set_failure_log_folder(self, log_root): @@ -743,14 +755,14 @@ class AssetProcessor(object): :return: Absolute path of added scan folder """ if os.path.isabs(folder_name): - if not folder_name in self._override_scan_folders: + if folder_name not in self._override_scan_folders: self._override_scan_folders.append(folder_name) logger.info(f'Adding override scan folder {folder_name}') return folder_name else: if not self._temp_asset_root: - logger.warning(f"Can't create scan folder, no temporary asset workspace has been created") - return + logger.warning(f"Can not create scan folder, no temporary asset workspace has been created") + return "" scan_folder = os.path.join(self._temp_asset_root if self._temp_asset_root else self._workspace.paths.engine_root(), folder_name) if not os.path.isdir(scan_folder): @@ -802,9 +814,9 @@ class AssetProcessor(object): if not use_current_root: self.create_temp_asset_root() test_asset_root = os.path.join(self._temp_asset_root, self._workspace.project if relative_asset_root is None - else relative_asset_root) + else relative_asset_root) test_folder = os.path.join(test_asset_root, function_name if existing_function_name is None - else existing_function_name) + else existing_function_name) if not os.path.isdir(test_folder): os.makedirs(test_folder) if add_scan_folder: diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py index 34ea5f4eb8..f30ed2f233 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py @@ -9,8 +9,7 @@ import logging import os import subprocess -import ly_test_tools -from ly_test_tools.environment.process_utils import kill_processes_named as kill_processes_named +import ly_test_tools.environment.process_utils as process_utils logger = logging.getLogger(__name__) @@ -23,7 +22,7 @@ def start_asset_processor(bin_dir): :return: A subprocess.Popen object for the AssetProcessor process. """ os.chdir(bin_dir) - asset_processor = subprocess.Popen(['AssetProcessor.exe']) + asset_processor = subprocess.Popen(['AssetProcessor'], env=process_utils.get_display_env()) return_code = asset_processor.poll() if return_code is not None and return_code != 0: @@ -40,11 +39,9 @@ def kill_asset_processor(): :return: None """ - - kill_processes_named('AssetProcessor_tmp', ignore_extensions=True) - kill_processes_named('AssetProcessor', ignore_extensions=True) - kill_processes_named('AssetProcessorBatch', ignore_extensions=True) - kill_processes_named('AssetBuilder', ignore_extensions=True) - kill_processes_named('rc', ignore_extensions=True) - kill_processes_named('Lua Editor', ignore_extensions=True) - + process_utils.kill_processes_named('AssetProcessor_tmp', ignore_extensions=True) + process_utils.kill_processes_named('AssetProcessor', ignore_extensions=True) + process_utils.kill_processes_named('AssetProcessorBatch', ignore_extensions=True) + process_utils.kill_processes_named('AssetBuilder', ignore_extensions=True) + process_utils.kill_processes_named('rc', ignore_extensions=True) + process_utils.kill_processes_named('Lua Editor', ignore_extensions=True) diff --git a/Tools/LyTestTools/tests/integ/sanity_tests.py b/Tools/LyTestTools/tests/integ/sanity_tests.py index e27398c477..2e46d822c7 100755 --- a/Tools/LyTestTools/tests/integ/sanity_tests.py +++ b/Tools/LyTestTools/tests/integ/sanity_tests.py @@ -63,7 +63,6 @@ class TestAutomatedTestingProject(object): # Clean up processes after the test is finished process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True) - @pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Editor currently only functions on Windows") def test_StartEditor_Sanity(self, project): """ The `test_StartEditor_Sanity` test function is similar to the previous example with minor adjustments. A @@ -86,7 +85,7 @@ class TestAutomatedTestingProject(object): # Call the Editor executable with editor.start(): # Wait for the process to exist - waiter.wait_for(lambda: process_utils.process_exists("Editor", ignore_extensions=True)) + waiter.wait_for(lambda: process_utils.process_exists("Editor.exe", ignore_extensions=True)) finally: # Clean up processes after the test is finished process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True) diff --git a/Tools/LyTestTools/tests/unit/test_asset_processor.py b/Tools/LyTestTools/tests/unit/test_asset_processor.py index f487cc8537..743ca9a2e4 100755 --- a/Tools/LyTestTools/tests/unit/test_asset_processor.py +++ b/Tools/LyTestTools/tests/unit/test_asset_processor.py @@ -45,6 +45,7 @@ class TestAssetProcessor(object): @mock.patch('subprocess.Popen') @mock.patch('ly_test_tools.o3de.asset_processor.AssetProcessor.connect_socket') @mock.patch('ly_test_tools.o3de.asset_processor.ASSET_PROCESSOR_PLATFORM_MAP', {'foo': 'bar'}) + @mock.patch('time.sleep', mock.MagicMock()) def test_Start_NoneRunning_ProcStarted(self, mock_connect, mock_popen, mock_workspace): mock_ap_path = 'mock_ap_path' mock_workspace.asset_processor_platform = 'foo' @@ -54,14 +55,15 @@ class TestAssetProcessor(object): under_test = ly_test_tools.o3de.asset_processor.AssetProcessor(mock_workspace) under_test.enable_asset_processor_platform = mock.MagicMock() under_test.wait_for_idle = mock.MagicMock() + mock_proc_object = mock.MagicMock() + mock_proc_object.poll.return_value = None + mock_popen.return_value = mock_proc_object under_test.start(connect_to_ap=True) assert under_test._ap_proc is not None - mock_popen.assert_called_once_with([mock_ap_path, '--zeroAnalysisMode', - f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', - '--logDir', under_test.log_root(), - '--acceptInput', '--platforms', 'bar'], cwd=os.path.dirname(mock_ap_path)) + mock_popen.assert_called_once() + assert '--zeroAnalysisMode' in mock_popen.call_args[0][0] mock_connect.assert_called() @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager') @@ -114,7 +116,7 @@ class TestAssetProcessor(object): assert result mock_run.assert_called_once_with([apb_path, - f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', + f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', '--logDir', under_test.log_root()], close_fds=True, capture_output=False, timeout=1) @@ -150,10 +152,8 @@ class TestAssetProcessor(object): result, _ = under_test.batch_process(None, False) assert not result - mock_run.assert_called_once_with([apb_path, - f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', - '--logDir', under_test.log_root()], - close_fds=True, capture_output=False, timeout=28800.0) + mock_run.assert_called_once() + assert f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"' in mock_run.call_args[0][0] @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager') diff --git a/Tools/LyTestTools/tests/unit/test_launcher_android.py b/Tools/LyTestTools/tests/unit/test_launcher_android.py index d2f700e35e..d1adccb096 100755 --- a/Tools/LyTestTools/tests/unit/test_launcher_android.py +++ b/Tools/LyTestTools/tests/unit/test_launcher_android.py @@ -238,7 +238,7 @@ class TestAndroidLauncher: launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"]) launcher.configure_settings() - assert mock_workspace.settings.modify_platform_setting.call_count == 8 + assert mock_workspace.settings.modify_platform_setting.call_count == 1 @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict') @mock.patch('ly_test_tools.environment.process_utils.check_output') diff --git a/Tools/LyTestTools/tests/unit/test_process_utils.py b/Tools/LyTestTools/tests/unit/test_process_utils.py index de40fb8e34..bd6a79fb09 100755 --- a/Tools/LyTestTools/tests/unit/test_process_utils.py +++ b/Tools/LyTestTools/tests/unit/test_process_utils.py @@ -223,7 +223,7 @@ class TestCloseWindowsProcess(unittest.TestCase): mock_enum.assert_called_once() -class Test(unittest.TestCase): +class TestProcessMatching(unittest.TestCase): @mock.patch("ly_test_tools.environment.process_utils._safe_get_processes") def test_ProcExists_HasExtension_Found(self, mock_get_proc): @@ -261,18 +261,55 @@ class Test(unittest.TestCase): self.assertTrue(result) proc_mock.name.assert_called() - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') - def test_KillProcNamed_MockKill_SilentSuccess(self, mock_get_proc): + def test_KillProcNamed_ExactMatch_Killed(self, mock_get_proc, mock_kill_proc): + name = "dummy.exe" + proc_mock = mock.MagicMock() + proc_mock.name.return_value = name + mock_get_proc.return_value = [proc_mock] + + process_utils.kill_processes_named("dummy.exe", ignore_extensions=False) + mock_kill_proc.assert_called() + proc_mock.name.assert_called() + + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') + @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') + def test_KillProcNamed_NearMatch_Ignore(self, mock_get_proc, mock_kill_proc): + name = "dummy.exe" + proc_mock = mock.MagicMock() + proc_mock.name.return_value = name + mock_get_proc.return_value = [proc_mock] + + process_utils.kill_processes_named("dummy", ignore_extensions=False) + mock_kill_proc.assert_not_called() + proc_mock.name.assert_called() + + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') + @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') + def test_KillProcNamed_NearMatchIgnoreExtension_Kill(self, mock_get_proc, mock_kill_proc): name = "dummy.exe" proc_mock = mock.MagicMock() proc_mock.name.return_value = name mock_get_proc.return_value = [proc_mock] process_utils.kill_processes_named("dummy", ignore_extensions=True) + mock_kill_proc.assert_called() proc_mock.name.assert_called() - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') + @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') + def test_KillProcNamed_ExactMatchIgnoreExtension_Killed(self, mock_get_proc, mock_kill_proc): + name = "dummy.exe" + proc_mock = mock.MagicMock() + proc_mock.name.return_value = name + mock_get_proc.return_value = [proc_mock] + + process_utils.kill_processes_named("dummy.exe", ignore_extensions=True) + mock_kill_proc.assert_called() + proc_mock.name.assert_called() + + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes', mock.MagicMock) @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') @mock.patch('os.path.exists') def test_KillProcFrom_MockKill_SilentSuccess(self, mock_path, mock_get_proc): @@ -293,7 +330,7 @@ class Test(unittest.TestCase): mock_kill.assert_called() - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes', mock.MagicMock) @mock.patch('psutil.Process') def test_KillProcPid_NoProc_SilentPass(self, mock_psutil): mock_proc = mock.MagicMock() @@ -302,7 +339,7 @@ class Test(unittest.TestCase): process_utils.kill_process_with_pid(1) - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes', mock.MagicMock) @mock.patch('psutil.Process') def test_KillProcPidRaiseOnMissing_NoProc_Raises(self, mock_psutil): mock_proc = mock.MagicMock() @@ -339,7 +376,7 @@ class Test(unittest.TestCase): mock_wait_procs.side_effect = psutil.PermissionError() proc_mock = mock.MagicMock() - process_utils._safe_kill_process_list(proc_mock) + process_utils._safe_kill_processes(proc_mock) mock_wait_procs.assert_called() mock_log_warn.assert_called() diff --git a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake index c1a770d1ff..9dbdbbd8aa 100644 --- a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake +++ b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake @@ -16,7 +16,7 @@ ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zst ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) # platform-specific: -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-android TARGETS tiff PACKAGE_HASH 252b99e5886ec59fdccf38603c1399dd3fc02d878641aba35a7f8d2504065a06) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev4-android TARGETS TIFF PACKAGE_HASH 2c62cdf34a8ee6c7eb091d05d98f60b4da7634c74054d4dbb8736886182f4589) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-android TARGETS freetype PACKAGE_HASH df9e4d559ea0f03b0666b48c79813b1cd4d9624429148a249865de9f5c2c11cd) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.9.50-rev1-android TARGETS AWSNativeSDK PACKAGE_HASH 33771499f9080cbaab613459927e52911e68f94fa356397885e85005efbd1490) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-android TARGETS Lua PACKAGE_HASH 1f638e94a17a87fe9e588ea456d5893876094b4db191234380e4c4eb9e06c300) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index fb02a80088..d6fdc5cd9b 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -20,16 +20,14 @@ ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-linux TARGETS AWSGameLiftServerSDK PACKAGE_HASH a8149a95bd100384af6ade97e2b21a56173740d921e6c3da8188cd51554d39af) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-linux TARGETS tiff PACKAGE_HASH 19791da0a370470a6c187199f97c2c46efcc2d89146e2013775fb3600fd7317d) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-linux TARGETS TIFF PACKAGE_HASH 2377f48b2ebc2d1628d9f65186c881544c92891312abe478a20d10b85877409a) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-linux TARGETS freetype PACKAGE_HASH 3f10c703d9001ecd2bb51a3bd003d3237c02d8f947ad0161c0252fdc54cbcf97) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev6-linux TARGETS AWSNativeSDK PACKAGE_HASH 490291e4c8057975c3ab86feb971b8a38871c58bac5e5d86abdd1aeb7141eec4) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-linux TARGETS PhysX PACKAGE_HASH a110249cbef4f266b0002c4ee9a71f59f373040cefbe6b82f1e1510c811edde6) -ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-linux TARGETS etc2comp PACKAGE_HASH 9283aa5db5bb7fb90a0ddb7a9f3895317c8ebe8044943124bbb3673a41407430) ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-linux TARGETS mcpp PACKAGE_HASH df7a998d0bc3fedf44b5bdebaf69ddad6033355b71a590e8642445ec77bc6c41) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux TARGETS mikkelsen PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index e80956530a..41df718b71 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -21,17 +21,15 @@ ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev1-multiplatform ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 3f77367dbb0342136ec4ebbd44bc1fedf7198089a0f83c5631248530769b2be6) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-mac TARGETS SPIRVCross PACKAGE_HASH 78c6376ed2fd195b9b1f5fb2b56e5267a32c3aa21fb399e905308de470eb4515) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-mac TARGETS tiff PACKAGE_HASH b6f3040319f5bfe465d7e3f9b12ceed0dc951e66e05562beaac1c8da3b1b5d3f) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-mac TARGETS TIFF PACKAGE_HASH c2615ccdadcc0e1d6c5ed61e5965c4d3a82193d206591b79b805c3b3ff35a4bf) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-mac TARGETS freetype PACKAGE_HASH f159b346ac3251fb29cb8dd5f805c99b0015ed7fdb3887f656945ca701a61d0d) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev5-mac TARGETS AWSNativeSDK PACKAGE_HASH ffb890bd9cf23afb429b9214ad9bac1bf04696f07a0ebb93c42058c482ab2f01) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-mac TARGETS PhysX PACKAGE_HASH 5e092a11d5c0a50c4dd99bb681a04b566a4f6f29aa08443d9bffc8dc12c27c8e) -ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-mac TARGETS etc2comp PACKAGE_HASH 1966ab101c89db7ecf30984917e0a48c0d02ee0e4d65b798743842b9469c0818) ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-mac TARGETS mcpp PACKAGE_HASH be9558905c9c49179ef3d7d84f0a5472415acdf7fe2d76eb060d9431723ddf2e) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-mac TARGETS mikkelsen PACKAGE_HASH 83af99ca8bee123684ad254263add556f0cf49486c0b3e32e6d303535714e505) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-mac TARGETS googletest PACKAGE_HASH cbf020d5ef976c5db8b6e894c6c63151ade85ed98e7c502729dd20172acae5a8) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 7e112c405e..cccd2591e8 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -21,18 +21,16 @@ ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev1-multiplatform ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 803e10b94006b834cbbdd30f562a8ddf04174c2cb6956c8399ec164ef8418d1f) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-windows TARGETS tiff PACKAGE_HASH ff03464ca460fc34a8406b2a0c548ad221b10e40480b0abb954f1e649c20bad0) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-windows TARGETS TIFF PACKAGE_HASH c6000a906e6d2a0816b652e93dfbeab41c9ed73cdd5a613acd53e553d0510b60) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-windows TARGETS freetype PACKAGE_HASH 9809255f1c59b07875097aa8d8c6c21c97c47a31fb35e30f2bb93188e99a85ff) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-windows TARGETS AWSNativeSDK PACKAGE_HASH a900e80f7259e43aed5c847afee2599ada37f29db70505481397675bcbb6c76c) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-windows TARGETS PhysX PACKAGE_HASH 0c5ffbd9fa588e5cf7643721a7cfe74d0fe448bf82252d39b3a96d06dfca2298) -ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-windows TARGETS etc2comp PACKAGE_HASH fc9ae937b2ec0d42d5e7d0e9e8c80e5e4d257673fb33bc9b7d6db76002117123) ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-windows TARGETS mcpp PACKAGE_HASH 794789aba639bfe2f4e8fcb4424d679933dd6290e523084aa0a4e287ac44acb2) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-windows TARGETS mikkelsen PACKAGE_HASH 872c4d245a1c86139aa929f2b465b63ea4ea55b04ced50309135dd4597457a4e) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-windows TARGETS googletest PACKAGE_HASH 7e8f03ae8a01563124e3daa06386f25a2b311c10bb95bff05cae6c41eff83837) diff --git a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake index abfba29e5a..8042c888c7 100644 --- a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake +++ b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake @@ -17,7 +17,7 @@ ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS gla ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) # platform-specific: -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-ios TARGETS tiff PACKAGE_HASH d864beb0c955a55f28c2a993843afb2ecf6e01519ddfc857cedf34fc5db68d49) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-ios TARGETS TIFF PACKAGE_HASH e9067e88649fb6e93a926d9ed38621a9fae360a2e6f6eb24ebca63c1bc7761ea) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-ios TARGETS freetype PACKAGE_HASH 3ac3c35e056ae4baec2e40caa023d76a7a3320895ef172b6655e9261b0dc2e29) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-ios TARGETS AWSNativeSDK PACKAGE_HASH d10e7496ca705577032821011beaf9f2507689f23817bfa0ed4d2a2758afcd02) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-ios TARGETS Lua PACKAGE_HASH c2d3c4e67046c293049292317a7d60fdb8f23effeea7136aefaef667163e5ffe) diff --git a/cmake/Tools/layout_tool.py b/cmake/Tools/layout_tool.py index 62e3922770..8093f72e44 100755 --- a/cmake/Tools/layout_tool.py +++ b/cmake/Tools/layout_tool.py @@ -120,10 +120,7 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ warning_count += _warn(f"'system_{platform_name_lower}_{asset_type}.cfg' is missing from {str(layout_path)}") system_config_values = None else: - system_config_values = common.get_config_file_values(str(platform_system_cfg_file), ['r_ShadersRemoteCompiler', - 'r_ShadersAllowCompilation', - 'r_AssetProcessorShaderCompiler', - 'r_ShaderCompilerServer']) + system_config_values = common.get_config_file_values(str(platform_system_cfg_file), []) if bootstrap_values: @@ -146,24 +143,23 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ elif system_config_values is not None: - shaders_remote_compiler = system_config_values.get('r_ShadersRemoteCompiler') or '0' + shaders_remote_compiler = '0' asset_processor_shader_compiler = system_config_values.get('r_AssetProcessorShaderCompiler') or '0' - shader_compiler_server = system_config_values.get('r_ShaderCompilerServer') or LOCAL_HOST + shader_compiler_server = LOCAL_HOST shaders_allow_compilation = system_config_values.get('r_ShadersAllowCompilation') def _validate_remote_shader_settings(): - if shader_compiler_server == LOCAL_HOST: - if asset_processor_shader_compiler != '1': - return _warn(f"Connection to the remote shader compiler (r_ShaderCompilerServer) is not properly " - f"set in system_{platform_name_lower}_{asset_type}.cfg. If it is set to {LOCAL_HOST}, then " - f"r_AssetProcessorShaderCompiler must be set to 1.") + if asset_processor_shader_compiler != '1': + return _warn(f"Connection to the remote shader compiler (r_ShaderCompilerServer) is not properly " + f"set in system_{platform_name_lower}_{asset_type}.cfg. If it is set to {LOCAL_HOST}, then " + f"r_AssetProcessorShaderCompiler must be set to 1.") - else: - if _validate_remote_ap(remote_ip, remote_connect, False) > 0: - return _warn(f"The system_{platform_name_lower}_{asset_type}.cfg file is configured to connect to the" - f" shader compiler server through the remote connection to the Asset Processor.") + else: + if _validate_remote_ap(remote_ip, remote_connect, False) > 0: + return _warn(f"The system_{platform_name_lower}_{asset_type}.cfg file is configured to connect to the" + f" shader compiler server through the remote connection to the Asset Processor.") return 0 # Validation steps based on the asset mode @@ -181,16 +177,9 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ warning_count += _warn("No pak files found for PAK mode deployment") # Check if the shader paks are set if has_shader_pak: - # If the shader paks are set, make sure that the remote shader compiler connection settings are set - # or that it is going through AP - if shaders_remote_compiler == '1': - warning_count += _warn(f"Shader paks are set for project {project_name} but remote shader compiling " - f"(r_ShadersRemoteCompiler) is still enabled " - f"for it in system_{platform_name_lower}_{asset_type}.cfg.") - else: - # Since we are not connecting to the shader compiler, also make sure bootstrap is not configured to - # connect to Asset Processor remotely - warning_count += _validate_remote_ap(remote_ip, remote_connect, False) + # Since we are not connecting to the shader compiler, also make sure bootstrap is not configured to + # connect to Asset Processor remotely + warning_count += _validate_remote_ap(remote_ip, remote_connect, False) if shaders_allow_compilation is not None and shaders_allow_compilation == '1': warning_count += _warn(f"Shader paks are set for project {project_name} but shader compiling " diff --git a/editor.cfg b/editor.cfg index 843a52824d..a8719590c5 100644 --- a/editor.cfg +++ b/editor.cfg @@ -1,33 +1,12 @@ -- Settings stored here are only used in the Editor --- Disable the Missing Asset Resolver by default -ed_MissingAssetResolver = 0 -e_ShadowsCache=0 -r_MotionBlur=0 -r_HDRVignetting=0 --- For feature-test compatibility -mn_FatalErrorOnInvalidEntity=0 - -- Do not warn on Pak file access issues sys_PakWarnOnPakAccessFailures=0 --- By default, disable any possible stereo output that might have been enabled via a GEM/other config file so that --- the editor does not startup in stereo mode (which prevents actually editing the environment) -r_StereoMode=0 -r_StereoOutput=0 - -- When editing terrain in the editor, the highest-detail octree nodes for any edited sector will be rendered until -- the level is exported and saved, which can cause an artificial increase in the number of nodes that can get -- queued for visibility checks. These numbers need to be set high enough to account for those increases. --- The CheckOcclusionQueueSize should be at least (terrain height * terrain width) / (32 * 32) in size. --- (Each queue entry is 64 bytes of RAM) -e_CheckOcclusionQueueSize=32768 - --- The CheckOcclusionOutputQueueSize should be at least double the above queue size for safety. --- (Each queue entry is 64 bytes of RAM) -e_CheckOcclusionOutputQueueSize=65536 - -- Enable warnings when asset loads take longer than the given millisecond threshold cl_assetLoadWarningEnable=true cl_assetLoadWarningMsThreshold=100 diff --git a/engine.json b/engine.json index 63bddc9548..2b56e255fc 100644 --- a/engine.json +++ b/engine.json @@ -53,7 +53,6 @@ "Gems/Multiplayer", "Gems/MultiplayerCompression", "Gems/NvCloth", - "Gems/PBSreferenceMaterials", "Gems/PhysX", "Gems/PhysXDebug", "Gems/PhysXSamples", diff --git a/scripts/build/Platform/Windows/deploy_cdk_applications.cmd b/scripts/build/Platform/Windows/deploy_cdk_applications.cmd index 2845707923..006e0158c0 100644 --- a/scripts/build/Platform/Windows/deploy_cdk_applications.cmd +++ b/scripts/build/Platform/Windows/deploy_cdk_applications.cmd @@ -57,7 +57,7 @@ IF ERRORLEVEL 1 ( exit /b 1 ) -CALL :DeployCDKApplication AWSCore --all +CALL :DeployCDKApplication AWSCore "-c disable_access_log=true --all" IF ERRORLEVEL 1 ( exit /b 1 ) diff --git a/scripts/build/bootstrap/incremental_build_util.py b/scripts/build/bootstrap/incremental_build_util.py index 101e31b5db..ff243ab02b 100644 --- a/scripts/build/bootstrap/incremental_build_util.py +++ b/scripts/build/bootstrap/incremental_build_util.py @@ -18,6 +18,8 @@ from contextlib import contextmanager import threading import _thread +from botocore.config import Config + DEFAULT_REGION = 'us-west-2' DEFAULT_DISK_SIZE = 300 DEFAULT_DISK_TYPE = 'gp2' @@ -173,10 +175,27 @@ def get_region_name(): def get_ec2_client(region): - client = boto3.client('ec2', region_name=region) + client_config = Config( + region_name=region, + retries={ + 'mode': 'standard' + } + ) + client = boto3.client('ec2', config=client_config) return client +def get_ec2_resource(region): + resource_config = Config( + region_name=region, + retries={ + 'mode': 'standard' + } + ) + resource = boto3.resource('ec2', config=resource_config) + return resource + + def get_ec2_instance_id(): try: instance_id = urllib.request.urlopen('http://169.254.169.254/latest/meta-data/instance-id').read() @@ -233,6 +252,18 @@ def find_snapshot_id(ec2_client, snapshot_hint, repository_name, project, pipeli snapshot_id = snapshot['SnapshotId'] return snapshot_id + +def offline_drive(disk_number=1): + """Use diskpart to offline a Windows drive""" + with tempfile.NamedTemporaryFile(delete=False) as f: + f.write(f""" + select disk {disk_number} + offline disk + """.encode('utf-8')) + subprocess.run(['diskpart', '/s', f.name]) + os.unlink(f.name) + + def create_volume(ec2_client, availability_zone, snapshot_hint, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type): # The actual EBS default calculation for IOps is a floating point number, the closest approxmiation is 4x of the disk size for simplicity mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type) @@ -291,23 +322,26 @@ def create_volume(ec2_client, availability_zone, snapshot_hint, repository_name, def mount_volume_to_device(created): print('Mounting volume...') if os.name == 'nt': - f = tempfile.NamedTemporaryFile(delete=False) - f.write(""" - select disk 1 - online disk - attribute disk clear readonly - """.encode('utf-8')) # assume disk # for now + # Verify drive is in an offline state. + # Some Windows configs will automatically set new drives as online causing diskpart setup script to fail. + offline_drive() - if created: - print('Creating filesystem on new volume') - f.write("""create partition primary - select partition 1 - format quick fs=ntfs - assign - active - """.encode('utf-8')) + with tempfile.NamedTemporaryFile(delete=False) as f: + f.write(""" + select disk 1 + online disk + attribute disk clear readonly + """.encode('utf-8')) # assume disk # for now - f.close() + if created: + print('Creating filesystem on new volume') + f.write(""" + create partition primary + select partition 1 + format quick fs=ntfs + assign + active + """.encode('utf-8')) subprocess.call(['diskpart', '/s', f.name]) @@ -358,14 +392,7 @@ def unmount_volume_from_device(): print('Unmounting EBS volume from device...') if os.name == 'nt': kill_processes(MOUNT_PATH + 'workspace') - f = tempfile.NamedTemporaryFile(delete=False) - f.write(""" - select disk 1 - offline disk - """.encode('utf-8')) - f.close() - subprocess.call('diskpart /s %s' % f.name) - os.unlink(f.name) + offline_drive() else: kill_processes(MOUNT_PATH) subprocess.call(['umount', '-f', MOUNT_PATH]) @@ -395,14 +422,11 @@ def detach_volume_from_ec2_instance(volume, ec2_instance_id, force, timeout_dura def mount_ebs(snapshot_hint, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type): - session = boto3.session.Session() - region = session.region_name - if region is None: - region = DEFAULT_REGION + region = get_region_name() ec2_client = get_ec2_client(region) ec2_instance_id = get_ec2_instance_id() ec2_availability_zone = get_availability_zone() - ec2_resource = boto3.resource('ec2', region_name=region) + ec2_resource = get_ec2_resource(region) ec2_instance = ec2_resource.Instance(ec2_instance_id) for volume in ec2_instance.volumes.all(): @@ -469,7 +493,7 @@ def mount_ebs(snapshot_hint, repository_name, project, pipeline, branch, platfor def unmount_ebs(): region = get_region_name() ec2_instance_id = get_ec2_instance_id() - ec2_resource = boto3.resource('ec2', region_name=region) + ec2_resource = get_ec2_resource(region) ec2_instance = ec2_resource.Instance(ec2_instance_id) if os.path.isfile('envinject.properties'): diff --git a/scripts/o3de/o3de/engine_template.py b/scripts/o3de/o3de/engine_template.py index 802641bd3a..cba7539aaa 100755 --- a/scripts/o3de/o3de/engine_template.py +++ b/scripts/o3de/o3de/engine_template.py @@ -59,6 +59,14 @@ binary_file_ext = { '.motionset' } +cpp_file_ext = { + '.cpp', + '.h', + '.hpp', + '.hxx', + '.inl' +} + expect_license_info_ext = { '.cpp', '.h', @@ -402,7 +410,7 @@ def create_template(source_path: pathlib.Path, # if no template path, error if not template_path: logger.info(f'Template path empty. Using source name {source_name}') - template_path = source_name + template_path = pathlib.Path(source_name) if not template_path.is_absolute(): default_templates_folder = manifest.get_registered(default_folder='templates') template_path = default_templates_folder / template_path @@ -518,21 +526,52 @@ def create_template(source_path: pathlib.Path, replacements.append((source_name.upper(), '${NameUpper}')) replacements.append((source_name, '${Name}')) replacements.append((sanitized_source_name, '${SanitizedCppName}')) + sanitized_name_index = len(replacements) - 1 - def _transform_into_template(s_data: object) -> (bool, str): + def _is_cpp_file(file_path: pathlib.Path) -> bool: + """ + Internal helper method to check if a file is a C++ file based + on its extension, so we can determine if we need to prefer + the ${SanitizedCppName} + :param file_path: The input file path + :return: bool: Whether or not the input file path has a C++ extension + """ + name, ext = os.path.splitext(file_path) + + return ext.lower() in cpp_file_ext + + def _transform_into_template(s_data: object, + prefer_sanitized_name: bool = False) -> (bool, str): """ Internal function to transform any data into templated data :param s_data: the input data, this could be file data or file name data + :param prefer_sanitized_name: Optionally swap the sanitized name with the normal name + This can be necessary when creating the template, the source + name and sanitized source name might be the same, but C++ + files will need to prefer the sanitized version, or else + there might be compile errors (e.g. '-' characters in the name) :return: bool: whether or not the returned data MAY need to be transformed to instantiate it t_data: potentially transformed data 0 for success or non 0 failure code """ + def swap_sanitized_name_and_normal(): + replacements[sanitized_name_index-1], replacements[sanitized_name_index] = \ + replacements[sanitized_name_index], replacements[sanitized_name_index-1] + # copy the src data to the transformed data, then operate only on transformed data t_data = str(s_data) + # If we need to prefer the sanitized name, then swap it for the normal + if prefer_sanitized_name: + swap_sanitized_name_and_normal() + # run all the replacements for replacement in replacements: t_data = t_data.replace(replacement[0], replacement[1]) + # Once we are done running the replacements, reset the list if we had modified it + if prefer_sanitized_name: + swap_sanitized_name_and_normal() + if not keep_license_text: t_data = _replace_license_text(t_data) @@ -704,7 +743,7 @@ def create_template(source_path: pathlib.Path, # open the file and attempt to transform it with open(entry_abs, 'r') as s: source_data = s.read() - templated, source_data = _transform_into_template(source_data) + templated, source_data = _transform_into_template(source_data, _is_cpp_file(entry_abs)) # if the file type is a file that we expect to fins license header and we don't find any # warn that the we didn't find the license info, this makes it easy to make sure we didn't @@ -840,7 +879,7 @@ def create_template(source_path: pathlib.Path, # open the file and attempt to transform it with open(entry_abs, 'r') as s: source_data = s.read() - templated, source_data = _transform_into_template(source_data) + templated, source_data = _transform_into_template(source_data, _is_cpp_file(entry_abs)) # if the file type is a file that we expect to fins license header and we don't find any # warn that the we didn't find the license info, this makes it easy to make sure we didn't diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index 7e504d29cd..b665727a4e 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -13,8 +13,10 @@ import json import logging import os import pathlib +import shutil +import hashlib -from o3de import validation +from o3de import validation, utils logger = logging.getLogger() logging.basicConfig() @@ -135,12 +137,12 @@ def get_o3de_manifest() -> pathlib.Path: json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()}) json_data.update({'default_third_party_folder': default_third_party_folder.as_posix()}) + json_data.update({'engines': []}) json_data.update({'projects': []}) json_data.update({'external_subdirectories': []}) json_data.update({'templates': []}) json_data.update({'restricted': []}) json_data.update({'repos': []}) - json_data.update({'engines': []}) default_restricted_folder_json = default_restricted_folder / 'restricted.json' if not default_restricted_folder_json.is_file(): @@ -197,11 +199,11 @@ def load_o3de_manifest(manifest_path: pathlib.Path = None) -> dict: def save_o3de_manifest(json_data: dict, manifest_path: pathlib.Path = None) -> bool: """ - Save the json dictionary to the supplied manifest file or ~/.o3de/o3de_manifest.json if manifest_path is None + Save the json dictionary to the supplied manifest file or ~/.o3de/o3de_manifest.json if None - :param json_data: dictionary to save in json format at the file path - :param manifest_path: optional path to manifest file to save - """ + :param json_data: dictionary to save in json format at the file path + :param manifest_path: optional path to manifest file to save + """ if not manifest_path: manifest_path = get_o3de_manifest() with manifest_path.open('w') as s: @@ -213,7 +215,6 @@ def save_o3de_manifest(json_data: dict, manifest_path: pathlib.Path = None) -> b return False - def get_gems_from_subdirectories(external_subdirs: list) -> list: ''' Helper Method for scanning a set of external subdirectories for gem.json files @@ -235,7 +236,6 @@ def get_gems_from_subdirectories(external_subdirs: list) -> list: return gem_directories -# Data query methods def get_engines() -> list: json_data = load_o3de_manifest() engine_list = json_data['engines'] if 'engines' in json_data else [] @@ -421,6 +421,48 @@ def get_templates_for_generic_creation(): # temporary until we have a better wa return list(filter(filter_project_and_gem_templates_out, get_all_templates())) +def get_json_file_path(object_typename: str, + object_path: str or pathlib.Path) -> pathlib.Path: + if not object_typename or not object_path: + logger.error('Must specify an object typename and object path.') + return None + + object_path = pathlib.Path(object_path).resolve() + return object_path / f'{object_typename}.json' + + +def get_json_data_file(object_json: pathlib.Path, + object_typename: str, + object_validator: callable) -> dict or None: + if not object_typename: + logger.error('Missing object typename.') + return None + + if not object_json or not object_json.is_file(): + logger.error(f'Invalid {object_typename} json {object_json} supplied or file missing.') + return None + + if not object_validator or not object_validator(object_json): + logger.error(f'{object_typename} json {object_json} is not valid or could not be validated.') + return None + + with object_json.open('r') as f: + try: + object_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{object_json} failed to load: {e}') + else: + return object_json_data + + return None + +def get_json_data(object_typename: str, + object_path: str or pathlib.Path, + object_validator: callable) -> dict or None: + object_json = get_json_file_path(object_typename, object_path) + + return get_json_data_file(object_json, object_typename, object_validator) + def get_engine_json_data(engine_name: str = None, engine_path: str or pathlib.Path = None) -> dict or None: @@ -431,28 +473,7 @@ def get_engine_json_data(engine_name: str = None, if engine_name and not engine_path: engine_path = get_registered(engine_name=engine_name) - if not engine_path: - logger.error(f'Engine Path {engine_path} has not been registered.') - return None - - engine_path = pathlib.Path(engine_path).resolve() - engine_json = engine_path / 'engine.json' - if not engine_json.is_file(): - logger.error(f'Engine json {engine_json} is not present.') - return None - if not validation.valid_o3de_engine_json(engine_json): - logger.error(f'Engine json {engine_json} is not valid.') - return None - - with engine_json.open('r') as f: - try: - engine_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{engine_json} failed to load: {str(e)}') - else: - return engine_json_data - - return None + return get_json_data('engine', engine_path, validation.valid_o3de_engine_json) def get_project_json_data(project_name: str = None, @@ -464,28 +485,7 @@ def get_project_json_data(project_name: str = None, if project_name and not project_path: project_path = get_registered(project_name=project_name) - if not project_path: - logger.error(f'Project Path {project_path} has not been registered.') - return None - - project_path = pathlib.Path(project_path).resolve() - project_json = project_path / 'project.json' - if not project_json.is_file(): - logger.error(f'Project json {project_json} is not present.') - return None - if not validation.valid_o3de_project_json(project_json): - logger.error(f'Project json {project_json} is not valid.') - return None - - with project_json.open('r') as f: - try: - project_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{project_json} failed to load: {str(e)}') - else: - return project_json_data - - return None + return get_json_data('project', project_path, validation.valid_o3de_project_json) def get_gem_json_data(gem_name: str = None, gem_path: str or pathlib.Path = None, @@ -497,28 +497,7 @@ def get_gem_json_data(gem_name: str = None, gem_path: str or pathlib.Path = None if gem_name and not gem_path: gem_path = get_registered(gem_name=gem_name, project_path=project_path) - if not gem_path: - logger.error(f'Gem Path {gem_path} has not been registered.') - return None - - gem_path = pathlib.Path(gem_path).resolve() - gem_json = gem_path / 'gem.json' - if not gem_json.is_file(): - logger.error(f'Gem json {gem_json} is not present.') - return None - if not validation.valid_o3de_gem_json(gem_json): - logger.error(f'Gem json {gem_json} is not valid.') - return None - - with gem_json.open('r') as f: - try: - gem_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{gem_json} failed to load: {str(e)}') - else: - return gem_json_data - - return None + return get_json_data('gem', gem_path, validation.valid_o3de_gem_json) def get_template_json_data(template_name: str = None, template_path: str or pathlib.Path = None, @@ -530,28 +509,7 @@ def get_template_json_data(template_name: str = None, template_path: str or path if template_name and not template_path: template_path = get_registered(template_name=template_name, project_path=project_path) - if not template_path: - logger.error(f'Template Path {template_path} has not been registered.') - return None - - template_path = pathlib.Path(template_path).resolve() - template_json = template_path / 'template.json' - if not template_json.is_file(): - logger.error(f'Template json {template_json} is not present.') - return None - if not validation.valid_o3de_template_json(template_json): - logger.error(f'Template json {template_json} is not valid.') - return None - - with template_json.open('r') as f: - try: - template_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{template_json} failed to load: {str(e)}') - else: - return template_json_data - - return None + return get_json_data('template', template_path, validation.valid_o3de_template_json) def get_restricted_json_data(restricted_name: str = None, restricted_path: str or pathlib.Path = None, @@ -563,29 +521,23 @@ def get_restricted_json_data(restricted_name: str = None, restricted_path: str o if restricted_name and not restricted_path: restricted_path = get_registered(restricted_name=restricted_name, project_path=project_path) - if not restricted_path: - logger.error(f'Restricted Path {restricted_path} has not been registered.') + return get_json_data('restricted', restricted_path, validation.valid_o3de_restricted_json) + +def get_repo_json_data(repo_uri: str) -> dict or None: + if not repo_uri: + logger.error('Must specify a Repo Uri.') return None - restricted_path = pathlib.Path(restricted_path).resolve() - restricted_json = restricted_path / 'restricted.json' - if not restricted_json.is_file(): - logger.error(f'Restricted json {restricted_json} is not present.') - return None - if not validation.valid_o3de_restricted_json(restricted_json): - logger.error(f'Restricted json {restricted_json} is not valid.') - return None + repo_json = get_repo_path(repo_uri=repo_uri) - with restricted_json.open('r') as f: - try: - restricted_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{restricted_json} failed to load: {str(e)}') - else: - return restricted_json_data + return get_json_data_file(repo_json, "Repo", validation.valid_o3de_repo_json) - return None +def get_repo_path(repo_uri: str, cache_folder: str = None) -> pathlib.Path: + if not cache_folder: + cache_folder = get_o3de_cache_folder() + repo_sha256 = hashlib.sha256(repo_uri.encode()) + return cache_folder / str(repo_sha256.hexdigest() + '.json') def get_registered(engine_name: str = None, project_name: str = None, @@ -721,9 +673,7 @@ def get_registered(engine_name: str = None, elif isinstance(repo_name, str): cache_folder = get_o3de_cache_folder() for repo_uri in json_data['repos']: - repo_uri = pathlib.Path(repo_uri).resolve() - repo_sha256 = hashlib.sha256(repo_uri.encode()) - cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') + cache_file = get_repo_path(repo_uri=repo_uri, cache_folder=cache_folder) if cache_file.is_file(): repo = pathlib.Path(cache_file).resolve() with repo.open('r') as f: diff --git a/scripts/o3de/o3de/validation.py b/scripts/o3de/o3de/validation.py index 8fcc0d7e7d..5c683f0667 100644 --- a/scripts/o3de/o3de/validation.py +++ b/scripts/o3de/o3de/validation.py @@ -27,7 +27,6 @@ def valid_o3de_repo_json(file_name: str or pathlib.Path) -> bool: test = json_data['origin'] except (json.JSONDecodeError, KeyError) as e: return False - return True diff --git a/scripts/o3de/tests/unit_test_engine_template.py b/scripts/o3de/tests/unit_test_engine_template.py index 16ac24d74c..ed1c8258d3 100755 --- a/scripts/o3de/tests/unit_test_engine_template.py +++ b/scripts/o3de/tests/unit_test_engine_template.py @@ -31,17 +31,17 @@ TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE = """ #include #include -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}Requests + class ${SanitizedCppName}Requests { public: - AZ_RTTI(${Name}Requests, "{${Random_Uuid}}"); - virtual ~${Name}Requests() = default; + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; // Put your public methods here }; - class ${Name}BusTraits + class ${SanitizedCppName}BusTraits : public AZ::EBusTraits { public: @@ -52,31 +52,31 @@ namespace ${Name} ////////////////////////////////////////////////////////////////////////// }; - using ${Name}RequestBus = AZ::EBus<${Name}Requests, ${Name}BusTraits>; - using ${Name}Interface = AZ::Interface<${Name}Requests>; + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; -} // namespace ${Name} +} // namespace ${SanitizedCppName} """ TEST_TEMPLATED_CONTENT_WITH_LICENSE = CPP_LICENSE_TEXT + TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITHOUT_LICENSE = string.Template( - TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE).safe_substitute({'Name': "TestTemplate"}) + TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE).safe_substitute({'SanitizedCppName': "TestTemplate"}) TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITH_LICENSE = string.Template( - TEST_TEMPLATED_CONTENT_WITH_LICENSE).safe_substitute({'Name': "TestTemplate"}) + TEST_TEMPLATED_CONTENT_WITH_LICENSE).safe_substitute({'SanitizedCppName': "TestTemplate"}) TEST_CONCRETE_TESTPROJECT_TEMPLATE_CONTENT_WITHOUT_LICENSE = string.Template( - TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE).safe_substitute({'Name': "TestProject"}) + TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE).safe_substitute({'SanitizedCppName': "TestProject"}) TEST_CONCRETE_TESTPROJECT_TEMPLATE_CONTENT_WITH_LICENSE = string.Template( - TEST_TEMPLATED_CONTENT_WITH_LICENSE).safe_substitute({'Name': "TestProject"}) + TEST_TEMPLATED_CONTENT_WITH_LICENSE).safe_substitute({'SanitizedCppName': "TestProject"}) TEST_CONCRETE_TESTGEM_TEMPLATE_CONTENT_WITHOUT_LICENSE = string.Template( - TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE).safe_substitute({'Name': "TestGem"}) + TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE).safe_substitute({'SanitizedCppName': "TestGem"}) TEST_CONCRETE_TESTGEM_TEMPLATE_CONTENT_WITH_LICENSE = string.Template( - TEST_TEMPLATED_CONTENT_WITH_LICENSE).safe_substitute({'Name': "TestGem"}) + TEST_TEMPLATED_CONTENT_WITH_LICENSE).safe_substitute({'SanitizedCppName': "TestGem"}) TEST_TEMPLATE_JSON_CONTENTS = """\ { diff --git a/system_android_android.cfg b/system_android_android.cfg index d5bfddeb73..10ab95abc4 100644 --- a/system_android_android.cfg +++ b/system_android_android.cfg @@ -6,40 +6,14 @@ sys_PakLogInvalidFileAccess=1 r_WidthAndHeightAsFractionOfScreenSize=1.0 r_MaxWidth=1280 r_MaxHeight=1080 - r_fullscreen=0 +r_ShadersAllowCompilation=1 -- Enable to prevent log spam, can cause missed messages -- log_spamdelay=1 --- DXGL ---r_Batching=0 - --- Enabling aync shader compiling causes a hang on some Android devices. -r_ShadersAsyncCompiling=0 -r_ShadersRemoteCompiler=1 -r_ShadersAllowCompilation=1 -r_ShadersAsyncActivation=0 - --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=1 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 - --- For Shader Compiler server running on other machines - 61453 is the default port -r_ShaderCompilerPort=61453 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. -r_GraphicsQuality = 2 - -s_FileCacheManagerSize=262144 - -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 -- Localization Settings -sys_localization_format=0 \ No newline at end of file +sys_localization_format=0 diff --git a/system_ios_ios.cfg b/system_ios_ios.cfg index 190ad4a121..abf08d0209 100644 --- a/system_ios_ios.cfg +++ b/system_ios_ios.cfg @@ -3,26 +3,7 @@ ------------------------ r_FullScreen=1 - r_ShadersAllowCompilation=1 -r_ShadersAsyncActivation=0 -r_ShadersAsyncCompiling=0 -r_ShadersRemoteCompiler=1 -r_ShadersUseLLVMDirectXCompiler=1 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. -r_GraphicsQuality = 0 - --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=1 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 - ---r_ShaderCompilerPort=61453 ------------------------ -- System @@ -39,16 +20,6 @@ sys_physics_CPU=0 -- log_spamdelay=1 log_IncludeTime=1 ------------------------- --- Audio ------------------------- -s_FileCacheManagerSize=262144 ------------------------- --- Auxiliary Geometry ------------------------- -r_enableAuxGeom=0 -r_auxGeom=0 - -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 diff --git a/system_linux_pc.cfg b/system_linux_pc.cfg index 614bc44b49..5fec6b8dfd 100644 --- a/system_linux_pc.cfg +++ b/system_linux_pc.cfg @@ -10,8 +10,6 @@ sys_PakLogInvalidFileAccess = 0 -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 -gm_disconnectDetection = 1 - -- Localization Settings sys_localization_format=0 @@ -20,27 +18,7 @@ r_width = 1280 r_height = 720 r_fullscreen = 0 -r_ShadersAsyncCompiling = 3 -r_ShadersAsyncActivation = 3 -r_ShadersAsyncMaxThreads = 16 -r_ShadersRemoteCompiler = 1 r_ShadersAllowCompilation = 1 --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=0 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): ---r_ShaderCompilerServer=127.0.0.1 ---r_ShaderCompilerPort = 61453 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. Autodetection not yet implemented for this platform. -r_GraphicsQuality = 1 - --- Texture streaming not supported on opengl -r_TexturesStreaming=0 - -- Display FPS r_displayInfo = 3 diff --git a/system_mac_mac.cfg b/system_mac_mac.cfg index 22fbca6e1e..ef145c890e 100644 --- a/system_mac_mac.cfg +++ b/system_mac_mac.cfg @@ -3,9 +3,6 @@ sys_float_exceptions=0 log_IncludeTime=1 sys_PakLogInvalidFileAccess=0 --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. Currently overwritten by hardcoded value for this platform. -r_GraphicsQuality = 0 - r_width=1280 r_height=720 r_fullscreen=0 @@ -13,25 +10,7 @@ r_fullscreen=0 -- Enable to prevent log spam, can cause missed messages -- log_spamdelay=1 --- DXGL -r_Batching=0 - -r_ShadersAsyncCompiling=0 -r_ShadersRemoteCompiler=1 r_ShadersAllowCompilation=1 -r_ShadersAsyncActivation=0 -r_ShadersUseLLVMDirectXCompiler=1 - --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=1 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 - ---r_ShaderCompilerPort=61453 -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 diff --git a/system_windows_pc.cfg b/system_windows_pc.cfg index 05210e02e0..aa53ed9323 100644 --- a/system_windows_pc.cfg +++ b/system_windows_pc.cfg @@ -11,27 +11,7 @@ r_fullscreen = 0 -- Enable to prevent log spam, can cause missed messages -- log_spamdelay=1 -r_ShadersAsyncCompiling = 3 -r_ShadersAsyncActivation = 3 -r_ShadersAsyncMaxThreads = 16 -r_ShadersRemoteCompiler = 0 r_ShadersAllowCompilation = 1 --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=0 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 ---r_ShaderCompilerPort = 61453 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. Autodetection not yet implemented for this platform. -r_GraphicsQuality = 4 - ---r_driver=GL ---r_ShadersGL4=1 - -- Localization Settings sys_localization_format=0