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/exclude.filetag b/Assets/Engine/exclude.filetag index 52534a87d8..3528454ec4 100644 --- a/Assets/Engine/exclude.filetag +++ b/Assets/Engine/exclude.filetag @@ -125,17 +125,6 @@ - - - - - - - - - - - @@ -207,27 +196,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/Editor/Scripts/scene_mesh_to_prefab.py b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py new file mode 100644 index 0000000000..4aeff06f18 --- /dev/null +++ b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py @@ -0,0 +1,184 @@ +# +# 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 +# +# +import os, traceback, binascii, sys, json, pathlib +import azlmbr.math +import azlmbr.bus + +# +# SceneAPI Processor +# + + +def log_exception_traceback(): + exc_type, exc_value, exc_tb = sys.exc_info() + data = traceback.format_exception(exc_type, exc_value, exc_tb) + print(str(data)) + +def get_mesh_node_names(sceneGraph): + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + meshDataList = [] + node = sceneGraph.get_root() + children = [] + paths = [] + + while node.IsValid(): + # store children to process after siblings + if sceneGraph.has_node_child(node): + children.append(sceneGraph.get_node_child(node)) + + nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node)) + paths.append(nodeName.get_path()) + + # store any node that has mesh data content + nodeContent = sceneGraph.get_node_content(node) + if nodeContent.CastWithTypeName('MeshData'): + if sceneGraph.is_node_end_point(node) is False: + if (len(nodeName.get_path())): + meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node))) + + # advance to next node + if sceneGraph.has_node_sibling(node): + node = sceneGraph.get_node_sibling(node) + elif children: + node = children.pop() + else: + node = azlmbr.scene.graph.NodeIndex() + + return meshDataList, paths + +def update_manifest(scene): + import json + import uuid, os + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + graph = sceneData.SceneGraph(scene.graph) + # Get a list of all the mesh nodes, as well as all the nodes + mesh_name_list, all_node_paths = get_mesh_node_names(graph) + scene_manifest = sceneData.SceneManifest() + + clean_filename = scene.sourceFilename.replace('.', '_') + + # Compute the filename of the scene file + source_basepath = scene.watchFolder + source_relative_path = os.path.dirname(os.path.relpath(clean_filename, source_basepath)) + source_filename_only = os.path.basename(clean_filename) + + created_entities = [] + previous_entity_id = azlmbr.entity.InvalidEntityId + + # Loop every mesh node in the scene + for activeMeshIndex in range(len(mesh_name_list)): + mesh_name = mesh_name_list[activeMeshIndex] + mesh_path = mesh_name.get_path() + # Create a unique mesh group name using the filename + node name + mesh_group_name = '{}_{}'.format(source_filename_only, mesh_name.get_name()) + # Remove forbidden filename characters from the name since this will become a file on disk later + mesh_group_name = "".join(char for char in mesh_group_name if char not in "|<>:\"/?*\\") + # Add the MeshGroup to the manifest and give it a unique ID + mesh_group = scene_manifest.add_mesh_group(mesh_group_name) + mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, source_filename_only + mesh_path)) + '}' + # Set our current node as the only node that is included in this MeshGroup + scene_manifest.mesh_group_select_node(mesh_group, mesh_path) + + # Explicitly remove all other nodes to prevent implicit inclusions + for node in all_node_paths: + if node != mesh_path: + scene_manifest.mesh_group_unselect_node(mesh_group, node) + + # Create an editor entity + entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name) + # Add an EditorMeshComponent to the entity + editor_mesh_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "AZ::Render::EditorMeshComponent") + # Set the ModelAsset assetHint to the relative path of the input asset + the name of the MeshGroup we just created + the azmodel extension + # The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel + # The assetHint will be converted to an AssetId later during prefab loading + json_update = json.dumps({ + "Controller": { "Configuration": { "ModelAsset": { + "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}} + }); + # Apply the JSON above to the component we created + result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update) + + if not result: + raise RuntimeError("UpdateComponentForEntity failed for Mesh component") + + # Get the transform component + transform_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0") + + # Set this entity to be a child of the last entity we created + # This is just an example of how to do parenting and isn't necessarily useful to parent everything like this + if previous_entity_id is not None: + transform_json = json.dumps({ + "Parent Entity" : previous_entity_id.to_json() + }); + + # Apply the JSON update + result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, transform_component, transform_json) + + if not result: + raise RuntimeError("UpdateComponentForEntity failed for Transform component") + + # Update the last entity id for next time + previous_entity_id = entity_id + + # Keep track of the entity we set up, we'll add them all to the prefab we're creating later + created_entities.append(entity_id) + + # Create a prefab with all our entities + prefab_filename = source_filename_only + ".prefab" + created_template_id = azlmbr.prefab.PrefabSystemScriptingBus(azlmbr.bus.Broadcast, "CreatePrefab", created_entities, prefab_filename) + + if created_template_id == azlmbr.prefab.InvalidTemplateId: + raise RuntimeError("CreatePrefab {} failed".format(prefab_filename)) + + # Convert the prefab to a JSON string + output = azlmbr.prefab.PrefabLoaderScriptingBus(azlmbr.bus.Broadcast, "SaveTemplateToString", created_template_id) + + if output.IsSuccess(): + jsonString = output.GetValue() + uuid = azlmbr.math.Uuid_CreateRandom().ToString() + jsonResult = json.loads(jsonString) + # Add a PrefabGroup to the manifest and store the JSON on it + scene_manifest.add_prefab_group(source_filename_only, uuid, jsonResult) + else: + raise RuntimeError("SaveTemplateToString failed for template id {}, prefab {}".format(created_template_id, prefab_filename)) + + # Convert the manifest to a JSON string and return it + new_manifest = scene_manifest.export() + + return new_manifest + +sceneJobHandler = None + +def on_update_manifest(args): + try: + scene = args[0] + return update_manifest(scene) + except RuntimeError as err: + print (f'ERROR - {err}') + log_exception_traceback() + except: + log_exception_traceback() + + global sceneJobHandler + sceneJobHandler = None + +# try to create SceneAPI handler for processing +try: + import azlmbr.scene as sceneApi + if (sceneJobHandler == None): + sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler() + sceneJobHandler.connect() + sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest) +except: + sceneJobHandler = None diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake index 0d281661b9..30740a489d 100644 --- a/AutomatedTesting/Gem/Code/enabled_gems.cmake +++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake @@ -21,7 +21,6 @@ set(ENABLED_GEMS QtForPython PythonAssetBuilder Metastream - Camera EMotionFX AtomTressFX @@ -53,6 +52,6 @@ set(ENABLED_GEMS AWSCore AWSClientAuth AWSMetrics - + PrefabBuilder AudioSystem ) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt index 02aaa42597..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 @@ -65,4 +60,18 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT COMPONENT Atom ) + ly_add_pytest( + NAME AutomatedTesting::Atom_TestSuite_Main_GPU_Optimized + TEST_SUITE main + TEST_REQUIRES gpu + TEST_SERIAL + TIMEOUT 1200 + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_GPU_Optimized.py + RUNTIME_DEPENDENCIES + AssetProcessor + AutomatedTesting.Assets + Editor + COMPONENT + Atom + ) endif() 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 new file mode 100644 index 0000000000..568768e12e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py @@ -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 +""" +import os + +import pytest + +import ly_test_tools.environment.file_system as file_system +from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite +from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots +from .atom_utils.atom_component_helper import create_screenshots_archive, golden_images_directory + +DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' + + +@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +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" + test_screenshots = [] # Gets set by setup() + screenshot_directory = "" # Gets set by setup() + + # Clear existing test screenshots before starting test. + def setup(self, workspace): + screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) + test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)] + file_system.delete(test_screenshots, True, True) + + from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module + + golden_images = [os.path.join(golden_images_directory(), screenshot_name)] + for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images): + compare_screenshots(test_screenshot, golden_screenshot) + create_screenshots_archive(screenshot_directory) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py index 47b2204d56..c29a391be4 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -14,33 +14,66 @@ 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 + + @pytest.mark.test_case_id("C36525664") + class AtomEditorComponents_PostFXGradientWeightModifierAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded 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 58b72ef01a..11832f8846 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py @@ -3,13 +3,54 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright SPDX-License-Identifier: Apache-2.0 OR MIT -File to assist with common hydra component functions used across various Atom tests. """ +import datetime import os +import zipfile -from editor_python_test_tools.editor_test_helper import EditorTestHelper -helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") +def create_screenshots_archive(screenshot_path): + """ + Creates a new zip file archive at archive_path containing all files listed within archive_path. + :param screenshot_path: location containing the files to archive, the zip archive file will also be saved here. + :return: None, but creates a new zip file archive inside path containing all of the files inside archive_path. + """ + files_to_archive = [] + + # Search for .png and .ppm files to add to the zip archive file. + for (folder_name, sub_folders, file_names) in os.walk(screenshot_path): + for file_name in file_names: + if file_name.endswith(".png") or file_name.endswith(".ppm"): + file_path = os.path.join(folder_name, file_name) + files_to_archive.append(file_path) + + # Setup variables for naming the zip archive file. + timestamp = datetime.datetime.now().timestamp() + formatted_timestamp = datetime.datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d_%H-%M-%S") + screenshots_file = os.path.join(screenshot_path, f'screenshots_{formatted_timestamp}.zip') + + # Write all of the valid .png and .ppm files to the archive file. + with zipfile.ZipFile(screenshots_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive: + for file_path in files_to_archive: + file_name = os.path.basename(file_path) + zip_archive.write(file_path, file_name) + + +def golden_images_directory(): + """ + Uses this file location to return the valid location for golden image files. + :return: The path to the golden_images directory, but raises an IOError if the golden_images directory is missing. + """ + current_file_directory = os.path.join(os.path.dirname(__file__)) + golden_images_dir = os.path.join(current_file_directory, '..', 'golden_images') + + if not os.path.exists(golden_images_dir): + raise IOError( + f'golden_images" directory was not found at path "{golden_images_dir}"' + f'Please add a "golden_images" directory inside: "{current_file_directory}"' + ) + + return golden_images_dir def create_basic_atom_level(level_name): @@ -31,6 +72,9 @@ def create_basic_atom_level(level_name): import azlmbr.object import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.editor_test_helper import EditorTestHelper + + helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") # Create a new level. new_level_name = level_name @@ -69,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/atom_utils/material_editor_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py index ef0a592df0..b21c74de19 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py @@ -211,7 +211,7 @@ class Timeout: return time.time() > self.die_after -screenshotsFolder = os.path.join(azlmbr.paths.devroot, "AtomTest", "Cache" "pc", "Screenshots") +screenshotsFolder = os.path.join(azlmbr.paths.products, "Screenshots") class ScreenshotHelper: diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py index 602e7564b3..bbc8463152 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py @@ -17,7 +17,7 @@ import azlmbr.legacy.general as general import azlmbr.editor as editor import azlmbr.render as render -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) +sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import TestHelper diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py index 751f425916..7ecdc6859b 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py @@ -14,7 +14,7 @@ import azlmbr.math as math import azlmbr.paths import azlmbr.legacy.general as general -sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests")) +sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra from Atom.atom_utils.atom_constants import LIGHT_TYPES 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_PostFXGradientWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py new file mode 100644 index 0000000000..d38e96739d --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py @@ -0,0 +1,179 @@ +""" +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_gradient_weight_creation = ( + "PostFX Gradient Weight Modifier Entity successfully created", + "PostFX Gradient Weight Modifier Entity failed to be created") + postfx_gradient_weight_component = ( + "Entity has a PostFX Gradient Weight Modifier component", + "Entity failed to find PostFX Gradient Weight Modifier component") + postfx_gradient_weight_disabled = ( + "PostFX Gradient Weight Modifier component disabled", + "PostFX Gradient Weight Modifier component was not disabled.") + postfx_layer_component = ( + "Entity has a PostFX Layer component", + "Entity did not have an PostFX Layer component") + postfx_gradient_weight_enabled = ( + "PostFX Gradient Weight Modifier component enabled", + "PostFX Gradient 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_PostFXGradientWeightModifier_AddedToEntity(): + """ + Summary: + Tests the PostFX Gradient 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 Gradient Weight Modifier entity with no components. + 2) Add a PostFX Gradient Weight Modifier component to PostFX Gradient Weight Modifier entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify PostFX Gradient Weight Modifier component not enabled. + 6) Add PostFX Layer component since it is required by the PostFX Gradient Weight Modifier component. + 7) Verify PostFX Gradient Weight Modifier component is enabled. + 8) Enter/Exit game mode. + 9) Test IsHidden. + 10) Test IsVisible. + 11) Delete PostFX Gradient Weight Modifier entity. + 12) UNDO deletion. + 13) REDO deletion. + 14) 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 Gradient Weight Modifier entity with no components. + postfx_gradient_weight_name = "PostFX Gradient Weight Modifier" + postfx_gradient_weight_entity = EditorEntity.create_editor_entity(postfx_gradient_weight_name) + Report.critical_result(Tests.postfx_gradient_weight_creation, postfx_gradient_weight_entity.exists()) + + # 2. Add a PostFX Gradient Weight Modifier component to PostFX Gradient Weight Modifier entity. + postfx_gradient_weight_component = postfx_gradient_weight_entity.add_component(postfx_gradient_weight_name) + Report.critical_result( + Tests.postfx_gradient_weight_component, + postfx_gradient_weight_entity.has_component(postfx_gradient_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_gradient_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_gradient_weight_entity.exists()) + + # 5. Verify PostFX Gradient Weight Modifier component not enabled. + Report.result(Tests.postfx_gradient_weight_disabled, not postfx_gradient_weight_component.is_enabled()) + + # 6. Add PostFX Layer component since it is required by the PostFX Gradient Weight Modifier component. + postfx_layer_name = "PostFX Layer" + postfx_gradient_weight_entity.add_component(postfx_layer_name) + Report.result(Tests.postfx_layer_component, postfx_gradient_weight_entity.has_component(postfx_layer_name)) + + # 7. Verify PostFX Gradient Weight Modifier component is enabled. + Report.result(Tests.postfx_gradient_weight_enabled, postfx_gradient_weight_component.is_enabled()) + + # 8. 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) + + # 9. Test IsHidden. + postfx_gradient_weight_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, postfx_gradient_weight_entity.is_hidden() is True) + + # 10. Test IsVisible. + postfx_gradient_weight_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, postfx_gradient_weight_entity.is_visible() is True) + + # 11. Delete PostFX Gradient Weight Modifier entity. + postfx_gradient_weight_entity.delete() + Report.result(Tests.entity_deleted, not postfx_gradient_weight_entity.exists()) + + # 12. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, postfx_gradient_weight_entity.exists()) + + # 13. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not postfx_gradient_weight_entity.exists()) + + # 14. 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_PostFXGradientWeightModifier_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_AtomGPU_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py new file mode 100644 index 0000000000..92c555127a --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py @@ -0,0 +1,292 @@ +""" +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 +""" + + +# fmt: off +class Tests : + camera_component_added = ("Camera component was added", "Camera component wasn't added") + camera_fov_set = ("Camera component FOV property set", "Camera component FOV property wasn't set") + directional_light_component_added = ("Directional Light component added", "Directional Light component wasn't added") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + global_skylight_component_added = ("Global Skylight (IBL) component added", "Global Skylight (IBL) component wasn't added") + global_skylight_diffuse_image_set = ("Global Skylight Diffuse Image property set", "Global Skylight Diffuse Image property wasn't set") + global_skylight_specular_image_set = ("Global Skylight Specular Image property set", "Global Skylight Specular Image property wasn't set") + ground_plane_material_asset_set = ("Ground Plane Material Asset was set", "Ground Plane Material Asset wasn't set") + ground_plane_material_component_added = ("Ground Plane Material component added", "Ground Plane Material component wasn't added") + ground_plane_mesh_asset_set = ("Ground Plane Mesh Asset property was set", "Ground Plane Mesh Asset property wasn't set") + hdri_skybox_component_added = ("HDRi Skybox component added", "HDRi Skybox component wasn't added") + hdri_skybox_cubemap_texture_set = ("HDRi Skybox Cubemap Texture property set", "HDRi Skybox Cubemap Texture property wasn't set") + mesh_component_added = ("Mesh component added", "Mesh component wasn't added") + no_assert_occurred = ("No asserts detected", "Asserts were detected") + no_error_occurred = ("No errors detected", "Errors were detected") + secondary_grid_spacing = ("Secondary Grid Spacing set", "Secondary Grid Spacing not set") + sphere_material_component_added = ("Sphere Material component added", "Sphere Material component wasn't added") + sphere_material_set = ("Sphere Material Asset was set", "Sphere Material Asset wasn't set") + sphere_mesh_asset_set = ("Sphere Mesh Asset was set", "Sphere Mesh Asset wasn't set") + viewport_set = ("Viewport set to correct size", "Viewport not set to correct size") +# fmt: on + + +def AtomGPU_BasicLevelSetup_SetsUpLevel(): + """ + Summary: + Sets up a level to match the AtomBasicLevelSetup.ppm golden image then takes a screenshot to verify the setup. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The scene can be setup for a basic level. + The test screenshot matches the appearance of the AtomBasicLevelSetup.ppm golden image. + + Test Steps: + 1. Close error windows and display helpers then update the viewport size. + 2. Create Default Level Entity. + 3. Create Grid Entity as a child entity of the Default Level Entity. + 4. Add Grid component to Grid Entity and set Secondary Grid Spacing. + 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity. + 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity. + 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity. + 8. Set the Cubemap Texture property of the HDRi Skybox component. + 9. Set the Diffuse Image property of the Global Skylight (IBL) component. + 10. Set the Specular Image property of the Global Skylight (IBL) component. + 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity. + 12. Set the Material Asset property of the Material component for the Ground Plane Entity. + 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property. + 14. Create a Directional Light Entity as a child entity of the Default Level Entity. + 15. Add Directional Light component to Directional Light Entity and set entity rotation. + 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component. + 17. Set the Material Asset property of the Material component for the Sphere Entity. + 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component. + 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component. + 20. Set the Camera Entity rotation value and set the Camera component Field of View value. + 21. Enter game mode. + 22. Take screenshot. + 23. Exit game mode. + 24. Look for errors. + + :return: None + """ + + import os + from math import isclose + + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.paths + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + from Atom.atom_utils.screenshot_utils import ScreenshotHelper + + MATERIAL_COMPONENT_NAME = "Material" + MESH_COMPONENT_NAME = "Mesh" + SCREENSHOT_NAME = "AtomBasicLevelSetup" + SCREEN_WIDTH = 1280 + SCREEN_HEIGHT = 720 + DEGREE_RADIAN_FACTOR = 0.0174533 + + def initial_viewport_setup(screen_width, screen_height): + general.set_viewport_size(screen_width, screen_height) + general.update_viewport() + result = isclose( + a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and isclose( + a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1) + + return result + + 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. Close error windows and display helpers then update the viewport size. + helper.close_error_windows() + helper.close_display_helpers() + general.update_viewport() + Report.critical_result(Tests.viewport_set, initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)) + + # 2. Create Default Level Entity. + default_level_entity_name = "Default Level" + default_level_entity = EditorEntity.create_editor_entity_at( + math.Vector3(0.0, 0.0, 0.0), default_level_entity_name) + + # 3. Create Grid Entity as a child entity of the Default Level Entity. + grid_name = "Grid" + grid_entity = EditorEntity.create_editor_entity(grid_name, default_level_entity.id) + + # 4. Add Grid component to Grid Entity and set Secondary Grid Spacing. + grid_component = grid_entity.add_component(grid_name) + secondary_grid_spacing_property = "Controller|Configuration|Secondary Grid Spacing" + secondary_grid_spacing_value = 1.0 + grid_component.set_component_property_value(secondary_grid_spacing_property, secondary_grid_spacing_value) + secondary_grid_spacing_set = grid_component.get_component_property_value( + secondary_grid_spacing_property) == secondary_grid_spacing_value + Report.result(Tests.secondary_grid_spacing, secondary_grid_spacing_set) + + # 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity. + global_skylight_name = "Global Skylight (IBL)" + global_skylight_entity = EditorEntity.create_editor_entity(global_skylight_name, default_level_entity.id) + + # 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity. + hdri_skybox_name = "HDRi Skybox" + hdri_skybox_component = global_skylight_entity.add_component(hdri_skybox_name) + Report.result(Tests.hdri_skybox_component_added, global_skylight_entity.has_component(hdri_skybox_name)) + + # 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity. + global_skylight_component = global_skylight_entity.add_component(global_skylight_name) + Report.result(Tests.global_skylight_component_added, global_skylight_entity.has_component(global_skylight_name)) + + # 8. Set the Cubemap Texture property of the HDRi Skybox component. + global_skylight_image_asset_path = os.path.join( + "LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage") + global_skylight_image_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False) + hdri_skybox_cubemap_texture_property = "Controller|Configuration|Cubemap Texture" + hdri_skybox_component.set_component_property_value( + hdri_skybox_cubemap_texture_property, global_skylight_image_asset) + Report.result( + Tests.hdri_skybox_cubemap_texture_set, + hdri_skybox_component.get_component_property_value( + hdri_skybox_cubemap_texture_property) == global_skylight_image_asset) + + # 9. Set the Diffuse Image property of the Global Skylight (IBL) component. + # Re-use the same image that was used in the previous test step. + global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image" + global_skylight_component.set_component_property_value( + global_skylight_diffuse_image_property, global_skylight_image_asset) + Report.result( + Tests.global_skylight_diffuse_image_set, + global_skylight_component.get_component_property_value( + global_skylight_diffuse_image_property) == global_skylight_image_asset) + + # 10. Set the Specular Image property of the Global Skylight (IBL) component. + # Re-use the same image that was used in the previous test step. + global_skylight_specular_image_property = "Controller|Configuration|Specular Image" + global_skylight_component.set_component_property_value( + global_skylight_specular_image_property, global_skylight_image_asset) + global_skylight_specular_image_set = global_skylight_component.get_component_property_value( + global_skylight_specular_image_property) + Report.result( + Tests.global_skylight_specular_image_set, global_skylight_specular_image_set == global_skylight_image_asset) + + # 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity. + ground_plane_name = "Ground Plane" + ground_plane_entity = EditorEntity.create_editor_entity(ground_plane_name, default_level_entity.id) + ground_plane_material_component = ground_plane_entity.add_component(MATERIAL_COMPONENT_NAME) + Report.result( + Tests.ground_plane_material_component_added, ground_plane_entity.has_component(MATERIAL_COMPONENT_NAME)) + + # 12. Set the Material Asset property of the Material component for the Ground Plane Entity. + ground_plane_entity.set_local_uniform_scale(32.0) + ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial") + ground_plane_material_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False) + ground_plane_material_asset_property = "Default Material|Material Asset" + ground_plane_material_component.set_component_property_value( + ground_plane_material_asset_property, ground_plane_material_asset) + Report.result( + Tests.ground_plane_material_asset_set, + ground_plane_material_component.get_component_property_value( + ground_plane_material_asset_property) == ground_plane_material_asset) + + # 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property. + ground_plane_mesh_component = ground_plane_entity.add_component(MESH_COMPONENT_NAME) + Report.result(Tests.mesh_component_added, ground_plane_entity.has_component(MESH_COMPONENT_NAME)) + ground_plane_mesh_asset_path = os.path.join("Objects", "plane.azmodel") + ground_plane_mesh_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False) + ground_plane_mesh_asset_property = "Controller|Configuration|Mesh Asset" + ground_plane_mesh_component.set_component_property_value( + ground_plane_mesh_asset_property, ground_plane_mesh_asset) + Report.result( + Tests.ground_plane_mesh_asset_set, + ground_plane_mesh_component.get_component_property_value( + ground_plane_mesh_asset_property) == ground_plane_mesh_asset) + + # 14. Create a Directional Light Entity as a child entity of the Default Level Entity. + directional_light_name = "Directional Light" + directional_light_entity = EditorEntity.create_editor_entity_at( + math.Vector3(0.0, 0.0, 10.0), directional_light_name, default_level_entity.id) + + # 15. Add Directional Light component to Directional Light Entity and set entity rotation. + directional_light_entity.add_component(directional_light_name) + directional_light_entity_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0) + directional_light_entity.set_local_rotation(directional_light_entity_rotation) + Report.result( + Tests.directional_light_component_added, directional_light_entity.has_component(directional_light_name)) + + # 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component. + sphere_entity = EditorEntity.create_editor_entity_at( + math.Vector3(0.0, 0.0, 1.0), "Sphere", default_level_entity.id) + sphere_material_component = sphere_entity.add_component(MATERIAL_COMPONENT_NAME) + Report.result(Tests.sphere_material_component_added, sphere_entity.has_component(MATERIAL_COMPONENT_NAME)) + + # 17. Set the Material Asset property of the Material component for the Sphere Entity. + sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial") + sphere_material_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False) + sphere_material_asset_property = "Default Material|Material Asset" + sphere_material_component.set_component_property_value(sphere_material_asset_property, sphere_material_asset) + Report.result(Tests.sphere_material_set, sphere_material_component.get_component_property_value( + sphere_material_asset_property) == sphere_material_asset) + + # 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component. + sphere_mesh_component = sphere_entity.add_component(MESH_COMPONENT_NAME) + sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel") + sphere_mesh_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False) + sphere_mesh_asset_property = "Controller|Configuration|Mesh Asset" + sphere_mesh_component.set_component_property_value(sphere_mesh_asset_property, sphere_mesh_asset) + Report.result(Tests.sphere_mesh_asset_set, sphere_mesh_component.get_component_property_value( + sphere_mesh_asset_property) == sphere_mesh_asset) + + # 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component. + camera_name = "Camera" + camera_entity = EditorEntity.create_editor_entity_at( + math.Vector3(5.5, -12.0, 9.0), camera_name, default_level_entity.id) + camera_component = camera_entity.add_component(camera_name) + Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name)) + + # 20. Set the Camera Entity rotation value and set the Camera component Field of View value. + camera_entity_rotation = math.Vector3( + DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0) + camera_entity.set_local_rotation(camera_entity_rotation) + camera_fov_property = "Controller|Configuration|Field of view" + camera_fov_value = 60.0 + camera_component.set_component_property_value(camera_fov_property, camera_fov_value) + azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id) + Report.result(Tests.camera_fov_set, camera_component.get_component_property_value( + camera_fov_property) == camera_fov_value) + + # 21. Enter game mode. + helper.enter_game_mode(Tests.enter_game_mode) + helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0) + + # 22. Take screenshot. + ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{SCREENSHOT_NAME}.ppm") + + # 23. Exit game mode. + helper.exit_game_mode(Tests.exit_game_mode) + helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0) + + # 24. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + Report.result(Tests.no_assert_occurred, not error_tracer.has_asserts) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomGPU_BasicLevelSetup_SetsUpLevel) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py index 88a1ef4c7b..9f8f6c44b2 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py @@ -16,7 +16,7 @@ import time import azlmbr.math as math import azlmbr.paths -sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests")) +sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) import Atom.atom_utils.material_editor_utils as material_editor @@ -27,10 +27,10 @@ TEST_MATERIAL_1 = "001_DefaultWhite.material" TEST_MATERIAL_2 = "002_BaseColorLerp.material" TEST_MATERIAL_3 = "003_MetalMatte.material" TEST_DATA_PATH = os.path.join( - azlmbr.paths.devroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases" + azlmbr.paths.engroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases" ) MATERIAL_TYPE_PATH = os.path.join( - azlmbr.paths.devroot, "Gems", "Atom", "Feature", "Common", "Assets", + azlmbr.paths.engroot, "Gems", "Atom", "Feature", "Common", "Assets", "Materials", "Types", "StandardPBR.materialtype", ) CACHE_FILE_EXTENSION = ".azmaterial" @@ -61,7 +61,7 @@ def run(): print(f"Material opened: {material_editor.is_open(document_id)}") # Verify if the test material exists initially - target_path = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL) + target_path = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL) print(f"Test asset doesn't exist initially: {not os.path.exists(target_path)}") # 2) Test Case: Creating a New Material Using Existing One @@ -109,10 +109,10 @@ def run(): # Assign new color to the material file and save the document as copy expected_color_1 = math.Color(0.5, 0.5, 0.5, 1.0) material_editor.set_property(document_id, property_name, expected_color_1) - target_path_1 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_1) + target_path_1 = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL_1) cache_file_name_1 = os.path.splitext(NEW_MATERIAL_1) # Example output: ('test_material_1', '.material') cache_file_1 = f"{cache_file_name_1[0]}{CACHE_FILE_EXTENSION}" - target_path_1_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_1) + target_path_1_cache = os.path.join(azlmbr.paths.products, "materials", cache_file_1) material_editor.save_document_as_copy(document_id, target_path_1) material_editor.wait_for_condition(lambda: os.path.exists(target_path_1_cache), 4.0) @@ -120,10 +120,10 @@ def run(): # Assign new color to the material file save the document as child expected_color_2 = math.Color(0.75, 0.75, 0.75, 1.0) material_editor.set_property(document_id, property_name, expected_color_2) - target_path_2 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_2) + target_path_2 = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL_2) cache_file_name_2 = os.path.splitext(NEW_MATERIAL_1) # Example output: ('test_material_2', '.material') cache_file_2 = f"{cache_file_name_2[0]}{CACHE_FILE_EXTENSION}" - target_path_2_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_2) + target_path_2_cache = os.path.join(azlmbr.paths.products, "materials", cache_file_2) material_editor.save_document_as_child(document_id, target_path_2) material_editor.wait_for_condition(lambda: os.path.exists(target_path_2_cache), 4.0) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py index 4f7edeba75..fbd9f3459a 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py @@ -10,7 +10,7 @@ import sys import azlmbr.legacy.general as general -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) +sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.editor_test_helper import EditorTestHelper @@ -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 62a122a723..93e78a7c0a 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py @@ -17,7 +17,7 @@ import azlmbr.math as math import azlmbr.paths import azlmbr.editor as editor -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) +sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.editor_test_helper import EditorTestHelper @@ -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/Atom/tests/hydra_GPUTest_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py index 4a3ae8c85d..1c3e6226c1 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py @@ -14,7 +14,7 @@ import azlmbr.math as math import azlmbr.paths import azlmbr.legacy.general as general -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) +sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py index 1d3ca11618..fcce6eab37 100755 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py @@ -42,7 +42,6 @@ class TestEditorAutomation(object): "editor command line arg bar", "editor command line arg baz", "editor engroot set", - "editor devroot set", "path resolved worked" ] diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py index bd8791fad6..c6ae65612f 100755 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py @@ -20,12 +20,6 @@ if (engroot is not None and len(engroot) is not 0): print ('engroot is {}'.format(engroot)) print ('editor engroot set') -# make sure the @devroot@ exists as a azlmbr.paths property -devroot = azlmbr.paths.devroot -if (devroot is not None and len(devroot) != 0): - print ('devroot is {}'.format(devroot)) - print ('editor devroot set') - # resolving a basic path path = azlmbr.paths.resolve_path('@engroot@/engineassets/texturemsg/defaultsolids.mtl') if (len(path) != 0 and path.find('@engroot@') == -1): diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py index 5b7f2f42c1..c5ca4de603 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py @@ -16,7 +16,7 @@ import azlmbr.entity as entity import azlmbr.math as math import azlmbr.paths -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) +sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests')) from automatedtesting_shared.editor_test_helper import EditorTestHelper diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg index 3adc32d20a..ccc605cec9 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg @@ -1,2 +1,2 @@ # this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test -pyRunFile @devroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done \ No newline at end of file +pyRunFile @engroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg index 6230dfe0fa..3cbd84c1b5 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg @@ -1,2 +1,2 @@ # this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test -pyRunFile @devroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.py \ No newline at end of file +pyRunFile @engroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.py \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index 154b5730d7..783f71e06c 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -361,3 +361,19 @@ class EditorEntity: :return: True if "isVisible" is enabled, False otherwise. """ return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id) + + def set_local_uniform_scale(self, scale_float) -> None: + """ + Sets the "SetLocalUniformScale" value on the entity. + :param scale_float: value for "SetLocalUniformScale" to set to. + :return: None + """ + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", self.id, scale_float) + + def set_local_rotation(self, vector3_rotation) -> None: + """ + Sets the "SetLocalRotation" value on the entity. + :param vector3_rotation: The math.Vector3 value to use for rotation on the entity (uses radians). + :return: None + """ + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, vector3_rotation) 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 cc217f81da..1b094b1cfa 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -4,18 +4,18 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ + +import json +import math import os import time -import math +import traceback +from typing import Callable, Tuple import azlmbr import azlmbr.legacy.general as general import azlmbr.debug -import json -import traceback - -from typing import Callable, Tuple class FailFast(Exception): """ @@ -127,6 +127,30 @@ class TestHelper: if ret: return True + @staticmethod + def close_error_windows(): + """ + Closes Error Report and Error Log windows that block focus if they are visible. + :return: None + """ + if general.is_pane_visible("Error Report"): + general.close_pane("Error Report") + if general.is_pane_visible("Error Log"): + general.close_pane("Error Log") + + @staticmethod + def close_display_helpers(): + """ + Closes helper gizmos, anti-aliasing, and FPS meters. + :return: None + """ + if general.is_helpers_shown(): + general.toggle_helpers() + general.idle_wait(1.0) + general.idle_wait(1.0) + general.run_console("r_displayInfo=0") + general.idle_wait(1.0) + class Timeout: # type: (float) -> None @@ -149,6 +173,7 @@ class Timeout: def timed_out(self): return time.time() > self.die_after + class Report: _results = [] _exception = None @@ -290,8 +315,8 @@ class Report: Report.info(" x: {:.2f}, y: {:.2f}, z: {:.2f}".format(vector3.x, vector3.y, vector3.z)) if magnitude is not None: Report.info(" magnitude: {:.2f}".format(magnitude)) - - + + ''' Utility for scope tracing errors and warnings. Usage: @@ -303,7 +328,7 @@ Usage: Report.result(Tests.warnings_not_found_in_section, not section_tracer.has_warnings) -''' +''' class Tracer: def __init__(self): self.warnings = [] @@ -349,10 +374,10 @@ class Tracer: self.line = args[1] self.function = args[2] self.message = args[3] - + def __str__(self): return f"Assert: [{self.filename}:{self.function}:{self.line}]: {self.message}" - + def __repr__(self): return f"[Assert: {self.message}]" @@ -360,21 +385,21 @@ class Tracer: def __init__(self, args): self.window = args[0] self.message = args[1] - + def _on_warning(self, args): warningInfo = Tracer.WarningInfo(args) self.warnings.append(warningInfo) Report.info("Tracer caught Warning: %s" % warningInfo.message) self.has_warnings = True return False - + def _on_error(self, args): errorInfo = Tracer.ErrorInfo(args) self.errors.append(errorInfo) Report.info("Tracer caught Error: %s" % errorInfo.message) self.has_errors = True return False - + def _on_assert(self, args): assertInfo = Tracer.AssertInfo(args) self.asserts.append(assertInfo) @@ -436,6 +461,7 @@ class AngleHelper: def vector3_str(vector3): return "(x: {:.2f}, y: {:.2f}, z: {:.2f})".format(vector3.x, vector3.y, vector3.z) - + + def aabb_str(aabb): return "[Min: %s, Max: %s]" % (vector3_str(aabb.min), vector3_str(aabb.max)) 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 - - # 5) Check to see if lead and follower behaved as expected - Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided) + # 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)) - # 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/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py index e51be58ec6..84c661873c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py @@ -109,7 +109,7 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): # 6) Save and export to engine general.save_level() general.export_to_engine() - pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak") + pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") Report.result(Tests.saved_and_exported, os.path.exists(pak_path)) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py index 7a0abdd969..de2554034f 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py @@ -131,7 +131,7 @@ def DynamicSliceInstanceSpawner_External_E2E(): # 6) Save and export to engine general.save_level() general.export_to_engine() - pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak") + pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") Report.result(Tests.saved_and_exported, os.path.exists(pak_path)) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py index f56c0b836e..bf6501f469 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py @@ -155,7 +155,7 @@ def LayerBlender_E2E_Editor(): # 6) Save and export to engine general.save_level() general.export_to_engine() - pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak") + pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") Report.result(Tests.saved_and_exported, os.path.exists(pak_path)) 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/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py index 65ebf8e59e..957536fffb 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py @@ -17,7 +17,7 @@ import azlmbr.vegetation as vegetation import azlmbr.areasystem as areasystem import azlmbr.paths -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) +sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests')) import editor_python_test_tools.hydra_editor_utils as hydra @@ -25,7 +25,7 @@ def create_surface_entity(name, center_point, box_size_x, box_size_y, box_size_z # Create a "flat surface" entity to use as a plantable vegetation surface surface_entity = hydra.Entity(name) surface_entity.create_entity( - center_point, + center_point, ["Box Shape", "Shape Surface Tag Emitter"] ) if surface_entity.id.IsValid(): @@ -56,7 +56,7 @@ def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_ # Create a vegetation area entity to use as our test vegetation spawner spawner_entity = hydra.Entity(name) spawner_entity.create_entity( - center_point, + center_point, ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] ) if spawner_entity.id.IsValid(): 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/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace b/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace index 188fee9f05..05140f340b 100644 --- a/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace +++ b/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace @@ -1,3 +1,3 @@ [General] version=1 -startScript="ImportActor -filename \"@assets@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\n" +startScript="ImportActor -filename \"@products@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\n" diff --git a/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace b/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace index e429d74a57..870b9a8579 100644 --- a/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace +++ b/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace @@ -1,3 +1,3 @@ [General] version=1 -startScript="ImportActor -filename \"@assets@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_skeleton_newgeo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@assets@/Levels/Physics/C15096734_PhysxMaterials_DefaultMaterialLibrary/custom_motionset.motionset\"\nLoadAnimGraph -filename \"@assets@/Levels/Physics/C15096734_PhysxMaterials_DefaultMaterialLibrary/rin_physics.animgraph\"\n" +startScript="ImportActor -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_skeleton_newgeo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/custom_motionset.motionset\"\nLoadAnimGraph -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_physics.animgraph\"\n" 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/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override index 7065f0dfeb..209e9a9feb 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override @@ -109,7 +109,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physxsystemconfiguration.setreg b/AutomatedTesting/Registry/physxsystemconfiguration.setreg index 83aad307a6..2ade83d769 100644 --- a/AutomatedTesting/Registry/physxsystemconfiguration.setreg +++ b/AutomatedTesting/Registry/physxsystemconfiguration.setreg @@ -103,7 +103,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png b/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png new file mode 100644 index 0000000000..4f52364cb3 --- /dev/null +++ b/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:827a63985273229050bf4f63030bcc666f045091fe81cf8157a9ca23b40074b6 +size 3214 diff --git a/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx new file mode 100644 index 0000000000..d9deb899f0 --- /dev/null +++ b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8d24963e6e8765205bc79cbe2304fc39f1245ee75249e2834a71c96c3cab824 +size 22700 diff --git a/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo new file mode 100644 index 0000000000..fe18a16cb5 --- /dev/null +++ b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo @@ -0,0 +1,8 @@ +{ + "values": [ + { + "$type": "ScriptProcessorRule", + "scriptFilename": "Editor/Scripts/scene_mesh_to_prefab.py" + } + ] +} \ No newline at end of file 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 5884795413..bdfac373eb 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -33,7 +33,6 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE Legacy::CryCommon - 3rdParty::zlib PUBLIC 3rdParty::Qt::Core 3rdParty::Qt::Gui @@ -103,9 +102,8 @@ ly_add_target( 3rdParty::Qt::Gui 3rdParty::Qt::Widgets 3rdParty::Qt::Concurrent - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::squish-ccr - 3rdParty::zlib 3rdParty::AWSNativeSDK::STS Legacy::CryCommon Legacy::EditorCommon diff --git a/Code/Editor/Controls/FolderTreeCtrl.cpp b/Code/Editor/Controls/FolderTreeCtrl.cpp index b1cbb9414e..1464580ffc 100644 --- a/Code/Editor/Controls/FolderTreeCtrl.cpp +++ b/Code/Editor/Controls/FolderTreeCtrl.cpp @@ -278,17 +278,16 @@ void CFolderTreeCtrl::LoadTreeRec(const QString& currentFolder) void CFolderTreeCtrl::AddItem(const QString& path) { - QString folder; - QString fileNameWithoutExtension; - QString ext; - - Path::Split(path, folder, fileNameWithoutExtension, ext); + AZ::IO::FixedMaxPath folder{ AZ::IO::PathView(path.toUtf8().constData()) }; + AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Stem(); + folder = folder.ParentPath(); auto regex = QRegExp(m_fileNameSpec, Qt::CaseInsensitive, QRegExp::Wildcard); if (regex.exactMatch(path)) { - CTreeItem* folderTreeItem = CreateFolderItems(folder); - folderTreeItem->AddChild(fileNameWithoutExtension, path, eTreeImage_File); + CTreeItem* folderTreeItem = CreateFolderItems(QString::fromUtf8(folder.c_str(), static_cast(folder.Native().size()))); + folderTreeItem->AddChild(QString::fromUtf8(fileNameWithoutExtension.c_str(), + static_cast(fileNameWithoutExtension.Native().size())), path, eTreeImage_File); } } 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/Core/QtEditorApplication_linux.cpp b/Code/Editor/Core/QtEditorApplication_linux.cpp index 2fd4ef629e..5491134170 100644 --- a/Code/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Core/QtEditorApplication_linux.cpp @@ -19,10 +19,16 @@ namespace Editor if (GetIEditor()->IsInGameMode()) { #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); + // We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode. + // TODO To have this call here might be not be perfect. + AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents); + + // Now handle the rest of the events. + AzFramework::XcbEventHandlerBus::Broadcast( + &AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); #endif return true; } return false; } -} +} // namespace Editor diff --git a/Code/Editor/Core/Tests/test_Main.cpp b/Code/Editor/Core/Tests/test_Main.cpp index 3d3e286f18..16b0aa92cf 100644 --- a/Code/Editor/Core/Tests/test_Main.cpp +++ b/Code/Editor/Core/Tests/test_Main.cpp @@ -33,6 +33,7 @@ public: protected: void SetupEnvironment() override { + AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance()); m_allocatorScope.ActivateAllocators(); m_cryPak = new NiceMock(); @@ -49,6 +50,7 @@ protected: { delete m_cryPak; m_allocatorScope.DeactivateAllocators(); + DetachEditorCoreAZEnvironment(); } private: diff --git a/Code/Editor/Core/Tests/test_PathUtil.cpp b/Code/Editor/Core/Tests/test_PathUtil.cpp index 83e50a5947..cade19eb81 100644 --- a/Code/Editor/Core/Tests/test_PathUtil.cpp +++ b/Code/Editor/Core/Tests/test_PathUtil.cpp @@ -5,22 +5,29 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include "EditorDefs.h" -#include -#include "Util/PathUtil.h" -#include - -TEST(PathUtil, GamePathToFullPath_DoesNotBufferOverflow) +#include +#include +namespace UnitTest { - // There are no test assertions in this test because the purpose is just to verify that the test runs without crashing - QString pngExtension(".png"); + class PathUtil + : public ScopedAllocatorSetupFixture + { + }; + + TEST_F(PathUtil, GamePathToFullPath_DoesNotBufferOverflow) + { + // There are no test assertions in this test because the purpose is just to verify that the test runs without crashing + QString pngExtension(".png"); - // Create a string of lenth AZ_MAX_PATH_LEN that ends in .png - QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x'); - longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension); - Path::GamePathToFullPath(longStringMaxPath); + // Create a string of length AZ_MAX_PATH_LEN that ends in .png + QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x'); + longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension); + AZ_TEST_START_TRACE_SUPPRESSION; + Path::GamePathToFullPath(longStringMaxPath); + AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; - QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x'); - longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension); - Path::GamePathToFullPath(longStringMaxPathPlusOne); + QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x'); + longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension); + Path::GamePathToFullPath(longStringMaxPathPlusOne); + } } diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index b5eb4229df..4f2e995ac4 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -57,6 +57,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzToolsFramework #include @@ -2124,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(); @@ -2642,7 +2645,7 @@ void CCryEditApp::OnFileResaveSlices() sliceAssetInfos.reserve(5000); AZ::Data::AssetCatalogRequests::AssetEnumerationCB sliceCountCb = [&sliceAssetInfos]([[maybe_unused]] const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info) { - // Only add slices and nothing that has been temporarily added to the catalog with a macro in it (ie @devroot@) + // Only add slices and nothing that has been temporarily added to the catalog with a macro in it (ie @engroot@) if (info.m_assetType == azrtti_typeid() && info.m_relativePath[0] != '@') { sliceAssetInfos.push_back(info); @@ -3019,6 +3022,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam bool bIsDocModified = GetIEditor()->GetDocument()->IsModified(); OnSwitchPhysics(); GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified); + + if (usePrefabSystemForLevels) + { + auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get(); + if (rootSpawnableInterface) + { + rootSpawnableInterface->ProcessSpawnableQueue(); + } + } } const QScopedValueRollback rollback(m_creatingNewLevel); diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp index 07d946c609..4944c3bff5 100644 --- a/Code/Editor/CryEditDoc.cpp +++ b/Code/Editor/CryEditDoc.cpp @@ -1108,7 +1108,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) if (QFileInfo(filename).isRelative()) { // Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that. - fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@devassets@"))).absoluteFilePath(fullPathName)); + fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@projectroot@"))).absoluteFilePath(fullPathName)); } if (!CFileUtil::OverwriteFile(fullPathName)) @@ -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")) @@ -2159,7 +2159,7 @@ bool CCryEditDoc::LoadXmlArchiveArray(TDocMultiArchive& arrXmlAr, const QString& xmlAr.bLoading = true; // bound to the level folder, as if it were the assets folder. - // this mounts (whateverlevelname.ly) as @assets@/Levels/whateverlevelname/ and thus it works... + // this mounts (whateverlevelname.ly) as @products@/Levels/whateverlevelname/ and thus it works... bool openLevelPakFileSuccess = pIPak->OpenPack(levelPath.toUtf8().data(), absoluteLevelPath.toUtf8().data()); if (!openLevelPakFileSuccess) { diff --git a/Code/Editor/Dialogs/PythonScriptsDialog.cpp b/Code/Editor/Dialogs/PythonScriptsDialog.cpp index e95fb90c0a..35047947ac 100644 --- a/Code/Editor/Dialogs/PythonScriptsDialog.cpp +++ b/Code/Editor/Dialogs/PythonScriptsDialog.cpp @@ -91,7 +91,7 @@ CPythonScriptsDialog::CPythonScriptsDialog(QWidget* parent) { AZ::IO::Path newSourcePath = jsonSourcePathPointer; // Resolve any file aliases first - Do not use ResolvePath() as that assumes - // any relative path is underneath the @assets@ alias + // any relative path is underneath the @products@ alias if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr) { AZ::IO::FixedMaxPath replacedAliasPath; diff --git a/Code/Editor/EditorFileMonitor.cpp b/Code/Editor/EditorFileMonitor.cpp index 7feb9d32a8..311c42befa 100644 --- a/Code/Editor/EditorFileMonitor.cpp +++ b/Code/Editor/EditorFileMonitor.cpp @@ -14,6 +14,8 @@ // Editor #include "CryEdit.h" +#include + ////////////////////////////////////////////////////////////////////////// CEditorFileMonitor::CEditorFileMonitor() { @@ -177,26 +179,14 @@ void CEditorFileMonitor::OnFileMonitorChange(const SFileChangeInfo& rChange) // Make file relative to PrimaryCD folder. QString filename = rChange.filename; - // Remove game directory if present in path. - const QString rootPath = - QDir::fromNativeSeparators(QString::fromLatin1(Path::GetEditingRootFolder().c_str())); - if (filename.startsWith(rootPath, Qt::CaseInsensitive)) - { - filename = filename.right(filename.length() - rootPath.length()); - } - - // Make sure there is no leading slash - if (!filename.isEmpty() && (filename[0] == '\\' || filename[0] == '/')) - { - filename = filename.mid(1); - } + // Make path relative to the the project directory + AZ::IO::Path projectPath{ AZ::Utils::GetProjectPath() }; + AZ::IO::FixedMaxPath projectRelativeFilePath = AZ::IO::PathView(filename.toUtf8().constData()).LexicallyProximate( + projectPath); - if (!filename.isEmpty()) + if (!projectRelativeFilePath.empty()) { - //remove game name. Make it relative to the game folder - const QString filenameRelGame = RemoveGameName(filename); - const int extIndex = filename.lastIndexOf('.'); - const QString ext = filename.right(filename.length() - 1 - extIndex); + AZ::IO::PathView ext = projectRelativeFilePath.Extension(); // Check for File Monitor callback std::vector::iterator iter; @@ -207,15 +197,11 @@ void CEditorFileMonitor::OnFileMonitorChange(const SFileChangeInfo& rChange) // We compare against length of callback string, so we get directory matches as well as full filenames if (sCallback.pListener) { - if (sCallback.extension == "*" || ext.compare(sCallback.extension, Qt::CaseInsensitive) == 0) + if (sCallback.extension == "*" || AZ::IO::PathView(sCallback.extension.toUtf8().constData()) == ext) { - if (filenameRelGame.compare(sCallback.item, Qt::CaseInsensitive) == 0) - { - sCallback.pListener->OnFileChange(qPrintable(filenameRelGame), IFileChangeListener::EChangeType(rChange.changeType)); - } - else if (filename.compare(sCallback.item, Qt::CaseInsensitive) == 0) + if (AZ::IO::PathView(sCallback.item.toUtf8().constData()) == projectRelativeFilePath) { - sCallback.pListener->OnFileChange(qPrintable(filename), IFileChangeListener::EChangeType(rChange.changeType)); + sCallback.pListener->OnFileChange(qPrintable(projectRelativeFilePath.c_str()), IFileChangeListener::EChangeType(rChange.changeType)); } } } diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 29cf348ab5..ce4a0a2e33 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -95,7 +95,8 @@ namespace SandboxEditor cameras.AddCamera(m_firstPersonPanCamera); cameras.AddCamera(m_firstPersonTranslateCamera); cameras.AddCamera(m_firstPersonScrollCamera); - cameras.AddCamera(m_pivotCamera); + cameras.AddCamera(m_firstPersonFocusCamera); + cameras.AddCamera(m_orbitCamera); }); return controller; @@ -111,6 +112,7 @@ namespace SandboxEditor viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::BeginCursorCapture); } }; + const auto showCursor = [viewportId = m_viewportId] { if (SandboxEditor::CameraCaptureCursorForLook()) @@ -133,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 = [] { @@ -153,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 = [] { @@ -165,90 +167,111 @@ namespace SandboxEditor return SandboxEditor::CameraBoostMultiplier(); }; - m_firstPersonScrollCamera = AZStd::make_shared(); + m_firstPersonScrollCamera = AZStd::make_shared(); m_firstPersonScrollCamera->m_scrollSpeedFn = [] { return SandboxEditor::CameraScrollSpeed(); }; - m_pivotCamera = AZStd::make_shared(SandboxEditor::CameraPivotChannelId()); + const auto pivotFn = [] + { + // use the manipulator transform as the pivot point + AZStd::optional entityPivot; + AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( + entityPivot, AzToolsFramework::GetEntityContextId(), + &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); - m_pivotCamera->SetPivotFn( - []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) + if (entityPivot.has_value()) { - // use the manipulator transform as the pivot point - AZStd::optional entityPivot; - AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( - entityPivot, AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); - - // otherwise just use the identity - return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation(); + return entityPivot->GetTranslation(); + } + + // otherwise just use the identity + return AZ::Vector3::CreateZero(); + }; + + m_firstPersonFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook); + + m_firstPersonFocusCamera->SetPivotFn(pivotFn); + + m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId()); + + 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_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_orbitFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusOrbit); + + m_orbitFocusCamera->SetPivotFn(pivotFn); + + 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() @@ -257,12 +280,14 @@ namespace SandboxEditor m_firstPersonTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); m_firstPersonPanCamera->SetPanInputChannelId(SandboxEditor::CameraFreePanChannelId()); m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId()); - - 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_firstPersonFocusCamera->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 e691ca1c89..9cfd6f3554 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -41,13 +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_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_firstPersonScrollCamera; + AZStd::shared_ptr m_firstPersonFocusCamera; + 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/EditorPreferencesDialog.cpp b/Code/Editor/EditorPreferencesDialog.cpp index c56df15908..a1859b0f14 100644 --- a/Code/Editor/EditorPreferencesDialog.cpp +++ b/Code/Editor/EditorPreferencesDialog.cpp @@ -264,6 +264,9 @@ void EditorPreferencesDialog::SetFilter(const QString& filter) else if (m_currentPageItem) { m_currentPageItem->UpdateEditorFilter(ui->propertyEditor, m_filter); + + // Refresh the Stylesheet - when using search functionality. + AzQtComponents::StyleManager::repolishStyleSheet(this); } } diff --git a/Code/Editor/EditorPreferencesPageFiles.cpp b/Code/Editor/EditorPreferencesPageFiles.cpp index 4be3269d42..c3423c4e5b 100644 --- a/Code/Editor/EditorPreferencesPageFiles.cpp +++ b/Code/Editor/EditorPreferencesPageFiles.cpp @@ -14,6 +14,7 @@ // Editor #include "Settings.h" +#include "EditorViewportSettings.h" @@ -43,17 +44,16 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize) ->Field("MaxCount", &AutoBackup::m_maxCount) ->Field("RemindTime", &AutoBackup::m_remindTime); - serialize.Class() + serialize.Class() ->Version(1) - ->Field("Max number of items displayed", &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch); + ->Field("MaxEntriesShownCount", &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch); serialize.Class() ->Version(1) ->Field("Files", &CEditorPreferencesPage_Files::m_files) ->Field("Editors", &CEditorPreferencesPage_Files::m_editors) ->Field("AutoBackup", &CEditorPreferencesPage_Files::m_autoBackup) - ->Field("AssetBrowserSearch", &CEditorPreferencesPage_Files::m_assetBrowserSearch); - + ->Field("AssetBrowserSettings", &CEditorPreferencesPage_Files::m_assetBrowserSettings); AZ::EditContext* editContext = serialize.GetEditContext(); if (editContext) @@ -85,9 +85,10 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize) ->Attribute(AZ::Edit::Attributes::Max, 100) ->DataElement(AZ::Edit::UIHandlers::SpinBox, &AutoBackup::m_remindTime, "Remind Time", "Auto Remind Every (Minutes)"); - editContext->Class("Asset Browser Search View", "Asset Browser Search View") - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items", - "Maximum number of displayed items displayed in the Search View") + editContext->Class("Asset Browser Settings", "Asset Browser Settings") + ->DataElement( + AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items", + "Maximum number of items to display in the Search View.") ->Attribute(AZ::Edit::Attributes::Min, 50) ->Attribute(AZ::Edit::Attributes::Max, 5000); @@ -97,7 +98,7 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize) ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_files, "Files", "File Preferences") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_editors, "External Editors", "External Editors") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_autoBackup, "Auto Backup", "Auto Backup") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSearch, "Asset Browser Search", "Asset Browser Search"); + ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSettings, "Asset Browser Settings","Asset Browser Settings"); } } @@ -117,6 +118,7 @@ QIcon& CEditorPreferencesPage_Files::GetIcon() void CEditorPreferencesPage_Files::OnApply() { using namespace AzToolsFramework::SliceUtilities; + auto sliceSettings = AZ::UserSettings::CreateFind(AZ_CRC("SliceUserSettings", 0x055b32eb), AZ::UserSettings::CT_LOCAL); sliceSettings->m_autoNumber = m_files.m_autoNumberSlices; sliceSettings->m_saveLocation = m_files.m_saveLocation; @@ -137,7 +139,7 @@ void CEditorPreferencesPage_Files::OnApply() gSettings.autoBackupMaxCount = m_autoBackup.m_maxCount; gSettings.autoRemindTime = m_autoBackup.m_remindTime; - gSettings.maxNumberOfItemsShownInSearch = m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch; + SandboxEditor::SetMaxItemsShownInAssetBrowserSearch(m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch); } void CEditorPreferencesPage_Files::InitializeSettings() @@ -163,5 +165,5 @@ void CEditorPreferencesPage_Files::InitializeSettings() m_autoBackup.m_maxCount = gSettings.autoBackupMaxCount; m_autoBackup.m_remindTime = gSettings.autoRemindTime; - m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch = gSettings.maxNumberOfItemsShownInSearch; + m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch = SandboxEditor::MaxItemsShownInAssetBrowserSearch(); } diff --git a/Code/Editor/EditorPreferencesPageFiles.h b/Code/Editor/EditorPreferencesPageFiles.h index 368cd91fc3..9022032edc 100644 --- a/Code/Editor/EditorPreferencesPageFiles.h +++ b/Code/Editor/EditorPreferencesPageFiles.h @@ -69,18 +69,15 @@ private: int m_remindTime; }; - struct AssetBrowserSearch + struct AssetBrowserSettings { - AZ_TYPE_INFO(AssetBrowserSearch, "{9FBFCD24-9452-49DF-99F4-2711443CEAAE}") - - int m_maxNumberOfItemsShownInSearch; + AZ_TYPE_INFO(AssetBrowserSettings, "{5F407EC4-BBD1-4A87-92DB-D938D7127BB0}") + AZ::u64 m_maxNumberOfItemsShownInSearch; }; Files m_files; ExternalEditors m_editors; AutoBackup m_autoBackup; - AssetBrowserSearch m_assetBrowserSearch; + AssetBrowserSettings m_assetBrowserSettings; QIcon m_icon; }; - - diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index 55b631e1f6..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,13 @@ 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() ->Version(1) @@ -143,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") @@ -185,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", @@ -196,24 +197,27 @@ 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 orbit") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames); editContext->Class("Viewport Preferences", "Viewport Preferences") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)) ->DataElement( - AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, - "Camera Movement Settings", "Camera Movement Settings") + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, "Camera Movement Settings", + "Camera Movement Settings") ->DataElement( AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraInputSettings, "Camera Input Settings", "Camera Input Settings"); @@ -264,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); @@ -275,12 +279,13 @@ 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( &SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Events::OnEditorModularViewportCameraComposerSettingsChanged); @@ -299,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(); @@ -310,10 +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 01dc4664f6..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,13 @@ 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; }; CameraMovementSettings m_cameraMovementSettings; diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index fe8efecd17..2b54622d1c 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -15,6 +15,7 @@ namespace SandboxEditor { + constexpr AZStd::string_view AssetBrowserMaxItemsShownInSearchSetting = "/Amazon/Preferences/Editor/AssetBrowser/MaxItemsShowInSearch"; constexpr AZStd::string_view GridSnappingSetting = "/Amazon/Preferences/Editor/GridSnapping"; constexpr AZStd::string_view GridSizeSetting = "/Amazon/Preferences/Editor/GridSize"; constexpr AZStd::string_view AngleSnappingSetting = "/Amazon/Preferences/Editor/AngleSnapping"; @@ -28,7 +29,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 +45,13 @@ 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 void SetRegistry(const AZStd::string_view setting, T&& value) @@ -109,6 +111,16 @@ namespace SandboxEditor return AZStd::make_unique(); } + AZ::u64 MaxItemsShownInAssetBrowserSearch() + { + return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast(50)); + } + + void SetMaxItemsShownInAssetBrowserSearch(const AZ::u64 numberOfItemsShown) + { + SetRegistry(AssetBrowserMaxItemsShownInSearchSetting, numberOfItemsShown); + } + bool GridSnappingEnabled() { return GetRegistry(GridSnappingSetting, false); @@ -239,14 +251,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() @@ -403,14 +415,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() @@ -433,33 +445,43 @@ namespace SandboxEditor SetRegistry(CameraFreePanIdSetting, cameraFreePanId); } - AzFramework::InputChannelId CameraPivotLookChannelId() + AzFramework::InputChannelId CameraOrbitLookChannelId() + { + return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str()); + } + + void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId) + { + SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId); + } + + AzFramework::InputChannelId CameraOrbitDollyChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotLookIdSetting, AZStd::string("mouse_button_left")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str()); } - void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId) + void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId) { - SetRegistry(CameraPivotLookIdSetting, cameraPivotLookId); + SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId); } - AzFramework::InputChannelId CameraPivotDollyChannelId() + AzFramework::InputChannelId CameraOrbitPanChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotDollyIdSetting, AZStd::string("mouse_button_right")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str()); } - void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId) + void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId) { - SetRegistry(CameraPivotDollyIdSetting, cameraPivotDollyId); + SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId); } - AzFramework::InputChannelId CameraPivotPanChannelId() + AzFramework::InputChannelId CameraFocusChannelId() { - return AzFramework::InputChannelId(GetRegistry(CameraPivotPanIdSetting, AZStd::string("mouse_button_middle")).c_str()); + return AzFramework::InputChannelId(GetRegistry(CameraFocusIdSetting, AZStd::string("keyboard_key_alphanumeric_X")).c_str()); } - void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId) + void SetCameraFocusChannelId(AZStd::string_view cameraFocusId) { - SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId); + SetRegistry(CameraFocusIdSetting, cameraFocusId); } } // namespace SandboxEditor diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index d1271017c6..c6f51cf461 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -32,6 +32,9 @@ namespace SandboxEditor //! event will fire when a value in the settings registry (editorpreferences.setreg) is modified. SANDBOX_API AZStd::unique_ptr CreateEditorViewportSettingsCallbacks(); + SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch(); + SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown); + SANDBOX_API bool GridSnappingEnabled(); SANDBOX_API void SetGridSnapping(bool enabled); @@ -71,8 +74,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 +122,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,12 +131,15 @@ 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 CameraOrbitDollyChannelId(); + SANDBOX_API void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId); - SANDBOX_API AzFramework::InputChannelId CameraPivotDollyChannelId(); - SANDBOX_API void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId); + SANDBOX_API AzFramework::InputChannelId CameraOrbitPanChannelId(); + SANDBOX_API void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId); - SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId(); - SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId); + SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId(); + SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId); } // namespace SandboxEditor 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..ee7e9a8e96 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); @@ -106,16 +108,11 @@ CObjectManager::CObjectManager() m_objectsByName.reserve(1024); LoadRegistry(); - - AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( - AzToolsFramework::GetEntityContextId()); } ////////////////////////////////////////////////////////////////////////// CObjectManager::~CObjectManager() { - AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); - m_bExiting = true; SaveRegistry(); DeleteAllObjects(); @@ -2306,29 +2303,6 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC } } -void CObjectManager::EnteredComponentMode(const AZStd::vector& /*componentModeTypes*/) -{ - // 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*/) -{ - // show translate/rotate/scale gizmo again - if (IGizmoManager* gizmoManager = GetGizmoManager()) - { - if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject()) - { - gizmoManager->AddGizmo(new CAxisGizmo(selectedObject)); - } - } -} - ////////////////////////////////////////////////////////////////////////// namespace { diff --git a/Code/Editor/Objects/ObjectManager.h b/Code/Editor/Objects/ObjectManager.h index 0ad5d8323e..7389dfa6a1 100644 --- a/Code/Editor/Objects/ObjectManager.h +++ b/Code/Editor/Objects/ObjectManager.h @@ -20,8 +20,8 @@ #include "ObjectManagerEventBus.h" #include -#include #include +#include #include // forward declarations. @@ -58,7 +58,6 @@ public: */ class CObjectManager : public IObjectManager - , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler { public: //! Selection functor callback. @@ -329,10 +328,6 @@ private: void FindDisplayableObjects(DisplayContext& dc, bool bDisplay); - // EditorComponentModeNotificationBus - void EnteredComponentMode(const AZStd::vector& componentModeTypes) override; - void LeftComponentMode(const AZStd::vector& componentModeTypes) override; - private: typedef std::map Objects; Objects m_objects; diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index b9e4878879..41e950a058 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -1895,7 +1895,7 @@ void SandboxIntegrationManager::MakeSliceFromEntities(const AzToolsFramework::En AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants, &AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, entities); - const AZStd::string slicesAssetsPath = "@devassets@/Slices"; + const AZStd::string slicesAssetsPath = "@projectroot@/Slices"; if (!gEnv->pFileIO->Exists(slicesAssetsPath.c_str())) { 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/Plugins/EditorAssetImporter/AssetImporterWindow.cpp b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp index 98bf228391..0ff9467f33 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp @@ -30,6 +30,7 @@ class CXTPDockingPaneLayout; // Needed for settings.h #include #include #include +#include #include #include #include @@ -47,41 +48,6 @@ class CXTPDockingPaneLayout; // Needed for settings.h const char* AssetImporterWindow::s_documentationWebAddress = "http://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer.html"; const AZ::Uuid AssetImporterWindow::s_browseTag = AZ::Uuid::CreateString("{C240D2E1-BFD2-4FFA-BB5B-CC0FA389A5D3}"); -void MakeUserFriendlySourceAssetPath(QString& out, const QString& sourcePath) -{ - char devAssetsRoot[AZ_MAX_PATH_LEN] = { 0 }; - if (!gEnv->pFileIO->ResolvePath("@devroot@", devAssetsRoot, AZ_MAX_PATH_LEN)) - { - out = sourcePath; - return; - } - - AZStd::replace(devAssetsRoot, devAssetsRoot + AZ_MAX_PATH_LEN- 1, AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR); - - // Find if the sourcePathArray is a sub directory of the devAssets folder - // Keep reference to sourcePathArray long enough to use in PathView - QByteArray sourcePathArray = sourcePath.toUtf8(); - AZ::IO::PathView sourcePathRootView(sourcePathArray.data()); - AZ::IO::PathView devAssetsRootView(devAssetsRoot); - auto [sourcePathIter, devAssetsIter] = AZStd::mismatch(sourcePathRootView.begin(), sourcePathRootView.end(), - devAssetsRootView.begin(), devAssetsRootView.end()); - // If the devAssets path iterator is not equal to the end, then there was a mismistch while comparing it - // against the source path indicating that the source path is not a sub-directory - if (devAssetsIter != devAssetsRootView.end()) - { - out = sourcePath; - return; - } - - int offset = aznumeric_cast(strlen(devAssetsRoot)); - if (sourcePath.at(offset) == AZ_CORRECT_FILESYSTEM_SEPARATOR) - { - ++offset; - } - out = sourcePath.right(sourcePath.length() - offset); - -} - AssetImporterWindow::AssetImporterWindow() : AssetImporterWindow(nullptr) { @@ -102,7 +68,7 @@ AssetImporterWindow::AssetImporterWindow(QWidget* parent) AssetImporterWindow::~AssetImporterWindow() { - AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex, + AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex, "Processing overlay (and potentially background thread) still active at destruction."); AZ_Assert(!m_processingOverlay, "Processing overlay (and potentially background thread) still active at destruction."); } @@ -133,7 +99,7 @@ void AssetImporterWindow::OpenFile(const AZStd::string& filePath) QMessageBox::warning(this, "In progress", "Unable to close one or more windows at this time."); return; } - + OpenFileInternal(filePath); } @@ -146,7 +112,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev) if (m_processingOverlay) { - AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex, + AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex, "Processing overlay present, but not the index in the overlay for it."); if (m_processingOverlay->HasProcessingCompleted()) { @@ -157,7 +123,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev) } else { - QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.", + QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.", QMessageBox::Ok, QMessageBox::Ok); ev->ignore(); return; @@ -165,7 +131,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev) } else { - QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.", + QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.", QMessageBox::Ok, QMessageBox::Ok); ev->ignore(); return; @@ -199,7 +165,9 @@ void AssetImporterWindow::Init() // Load the style sheets AzQtComponents::StylesheetPreprocessor styleSheetProcessor(nullptr); - AZStd::string mainWindowQSSPath = Path::GetEditingRootFolder() + "\\Editor\\Styles\\AssetImporterWindow.qss"; + auto mainWindowQSSPath = AZ::IO::Path(AZ::Utils::GetEnginePath()) / "Assets"; + mainWindowQSSPath /= "Editor/Styles/AssetImporterWindow.qss"; + mainWindowQSSPath.MakePreferred(); QFile mainWindowStyleSheetFile(mainWindowQSSPath.c_str()); if (mainWindowStyleSheetFile.open(QFile::ReadOnly)) { @@ -212,7 +180,7 @@ void AssetImporterWindow::Init() { ui->m_actionInspect->setVisible(false); } - + ResetMenuAccess(WindowState::InitialNothingLoaded); // Setup the overlay system, and set the root to be the root display. The root display has the browse, @@ -220,7 +188,7 @@ void AssetImporterWindow::Init() m_overlay.reset(aznew AZ::SceneAPI::UI::OverlayWidget(this)); m_rootDisplay.reset(aznew ImporterRootDisplay(m_serializeContext)); connect(m_rootDisplay.data(), &ImporterRootDisplay::UpdateClicked, this, &AssetImporterWindow::UpdateClicked); - + connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerAdded, this, &AssetImporterWindow::OverlayLayerAdded); connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerRemoved, this, &AssetImporterWindow::OverlayLayerRemoved); @@ -242,7 +210,7 @@ void AssetImporterWindow::Init() AZStd::string joinedExtensions; AzFramework::StringFunc::Join(joinedExtensions, extensions.begin(), extensions.end(), " or "); - AZStd::string firstLineText = + AZStd::string firstLineText = AZStd::string::format( "%s files are available for use after placing them in any folder within your game project. " "These files will automatically be processed and may be accessed via the Asset Browser. Learn more...", @@ -250,13 +218,13 @@ void AssetImporterWindow::Init() ui->m_initialPromptFirstLine->setText(firstLineText.c_str()); - AZStd::string secondLineText = + AZStd::string secondLineText = AZStd::string::format("To adjust the %s settings, right-click the file in the Asset Browser and select \"Edit Settings\" from the context menu.", joinedExtensions.c_str()); ui->m_initialPromptSecondLine->setText(secondLineText.c_str()); } else { - AZStd::string firstLineText = + AZStd::string firstLineText = AZStd::string::format( "Files are available for use after placing them in any folder within your game project. " "These files will automatically be processed and may be accessed via the Asset Browser. Learn more...", s_documentationWebAddress); @@ -282,12 +250,12 @@ void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath) auto asyncLoadHandler = AZStd::make_shared( s_browseTag, [this, filePath]() - { - m_assetImporterDocument->LoadScene(filePath); + { + m_assetImporterDocument->LoadScene(filePath); }, [this]() { - HandleAssetLoadingCompleted(); + HandleAssetLoadingCompleted(); }, this); m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Loading, s_browseTag)); @@ -304,7 +272,7 @@ bool AssetImporterWindow::IsAllowedToChangeSourceFile() return true; } - QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes", + QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes", "You have unsaved changes. Do you want to discard those changes?", QMessageBox::StandardButton::Discard | QMessageBox::StandardButton::Cancel, this); messageBox.exec(); @@ -406,7 +374,7 @@ void AssetImporterWindow::OnSceneResetRequested() else { m_assetImporterDocument->ClearScene(); - AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'", + AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'", result.GetResult() == ProcessingResult::Failure ? "Failure" : "Ignored"); } }, @@ -456,7 +424,7 @@ void AssetImporterWindow::OnInspect() // make sure the inspector doesn't outlive the AssetImporterWindow, since we own the data it will be inspecting. auto* theInspectWidget = aznew AZ::SceneAPI::UI::SceneGraphInspectWidget(*m_assetImporterDocument->GetScene()); QObject::connect(this, &QObject::destroyed, theInspectWidget, [theInspectWidget]() { theInspectWidget->window()->close(); } ); - + m_overlay->PushLayer(label, theInspectWidget, "Scene Inspector", buttons); } @@ -483,7 +451,7 @@ void AssetImporterWindow::OverlayLayerRemoved() else { ResetMenuAccess(WindowState::InitialNothingLoaded); - + ui->m_initialBrowseContainer->show(); m_rootDisplay->hide(); } @@ -533,8 +501,9 @@ void AssetImporterWindow::HandleAssetLoadingCompleted() m_fullSourcePath = m_assetImporterDocument->GetScene()->GetSourceFilename(); SetTitle(m_fullSourcePath.c_str()); - QString userFriendlyFileName; - MakeUserFriendlySourceAssetPath(userFriendlyFileName, m_fullSourcePath.c_str()); + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); + AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath); + auto userFriendlyFileName = QString::fromUtf8(relativeSourcePath.c_str(), static_cast(relativeSourcePath.Native().size())); m_rootDisplay->SetSceneDisplay(userFriendlyFileName, m_assetImporterDocument->GetScene()); // Once we've browsed to something successfully, we need to hide the initial browse button layer and diff --git a/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp b/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp index b1d07af41c..5ee54667b9 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp +++ b/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp @@ -7,9 +7,11 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -50,22 +52,15 @@ namespace AZ return nullptr; } - AZStd::string cleanPath = filePath; - if (AzFramework::StringFunc::Path::IsRelative(filePath.c_str())) + AZ::IO::Path enginePath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - const char* absolutePath = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(absolutePath, - &AzToolsFramework::AssetSystemRequestBus::Events::GetAbsoluteDevRootFolderPath); - AZ_Assert(absolutePath, "Unable to retrieve the dev folder path"); - AzFramework::StringFunc::Path::Join(absolutePath, cleanPath.c_str(), cleanPath); - } - else - { - // Normalizing is not needed if the path is relative as Join(...) will also normalize. - AzFramework::StringFunc::Path::Normalize(cleanPath); + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); } - auto sceneIt = m_scenes.find(cleanPath); + AZ::IO::Path cleanPath = (enginePath / filePath).LexicallyNormal(); + + auto sceneIt = m_scenes.find(cleanPath.Native()); if (sceneIt != m_scenes.end()) { AZStd::shared_ptr scene = sceneIt->second.lock(); @@ -98,14 +93,14 @@ namespace AZ } AZStd::shared_ptr scene = - AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath, sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor, SceneAPI::SceneCore::LoadingComponent::TYPEINFO_Uuid()); + AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath.Native(), sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor, SceneAPI::SceneCore::LoadingComponent::TYPEINFO_Uuid()); if (!scene) { AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load the requested scene."); return nullptr; } - m_scenes.emplace(AZStd::move(cleanPath), scene); + m_scenes.emplace(AZStd::move(cleanPath.Native()), scene); return scene; } diff --git a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt index cd9f2e79c7..609482b581 100644 --- a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt +++ b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt @@ -39,7 +39,6 @@ ly_add_target( EDITOR_COMMON_IMPORTS BUILD_DEPENDENCIES PRIVATE - 3rdParty::zlib 3rdParty::Qt::Core 3rdParty::Qt::Widgets Legacy::CryCommon diff --git a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp index 6b454474be..a53ce966c4 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp @@ -46,7 +46,6 @@ namespace ProjectSettingsTool , LastPathBus::Handler() , m_ui(new Ui::ProjectSettingsToolWidget()) , m_reconfigureProcess() - , m_devRoot(GetDevRoot()) , m_projectRoot(GetProjectRoot()) , m_projectName(GetProjectName()) , m_plistsInitVector( diff --git a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h index 1c9aa4b1bf..daa5bea27c 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h +++ b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h @@ -147,7 +147,6 @@ namespace ProjectSettingsTool // The process used to reconfigure settings QProcess m_reconfigureProcess; - AZStd::string m_devRoot; AZStd::string m_projectRoot; AZStd::string m_projectName; diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp b/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp index 0e171adf04..d5b94469cc 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp @@ -27,37 +27,31 @@ namespace } template - StringType GetAbsoluteDevRoot() + StringType GetAbsoluteEngineRoot() { - const char* devRoot = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - devRoot, - &AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevRootFolderPath); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); - if (!devRoot) + if (engineRoot.empty()) { return ""; } - StringType devRootString(devRoot); - ToUnixPath(devRootString); - return devRootString; + StringType engineRootString(engineRoot.c_str()); + ToUnixPath(engineRootString); + return engineRootString; } template StringType GetAbsoluteProjectRoot() { - const char* projectRoot = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - projectRoot, - &AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevGameFolderPath); + AZ::IO::FixedMaxPath projectRoot = AZ::Utils::GetProjectPath(); - if (!projectRoot) + if (projectRoot.empty()) { return ""; } - StringType projectRootString(projectRoot); + StringType projectRootString(projectRoot.c_str()); ToUnixPath(projectRootString); return projectRootString; } @@ -87,9 +81,9 @@ namespace ProjectSettingsTool return reinterpret_cast(func); } - AZStd::string GetDevRoot() + AZStd::string GetEngineRoot() { - return GetAbsoluteDevRoot(); + return GetAbsoluteEngineRoot(); } AZStd::string GetProjectRoot() { @@ -104,7 +98,7 @@ namespace ProjectSettingsTool QString SelectXmlFromFileDialog(const QString& currentFile) { // The selected file must be relative to this path - QString defaultPath = GetAbsoluteDevRoot(); + QString defaultPath = GetAbsoluteEngineRoot(); QString startPath; // Choose the starting path for file dialog @@ -139,7 +133,7 @@ namespace ProjectSettingsTool QString SelectImageFromFileDialog(const QString& currentFile) { - QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteDevRoot(), ::GetProjectName()); + QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteEngineRoot(), ::GetProjectName()); QString startPath; @@ -188,7 +182,7 @@ namespace ProjectSettingsTool // Android if (group <= ImageGroup::AndroidPortrait) { - root = GetDevRoot() + "/Code/Tools/Android/ProjectBuilder/app_"; + root = GetEngineRoot() + "/Code/Tools/Android/ProjectBuilder/app_"; } //Ios else diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Utils.h b/Code/Editor/Plugins/ProjectSettingsTool/Utils.h index 4226d534a6..050fb3b85b 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/Utils.h +++ b/Code/Editor/Plugins/ProjectSettingsTool/Utils.h @@ -17,7 +17,7 @@ namespace ProjectSettingsTool { void* ConvertFunctorToVoid(AZStd::pair(*func)(const QString&)); - AZStd::string GetDevRoot(); + AZStd::string GetEngineRoot(); AZStd::string GetProjectRoot(); AZStd::string GetProjectName(); 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/Settings.cpp b/Code/Editor/Settings.cpp index 05a6960695..d548cffb52 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -10,6 +10,7 @@ #include "EditorDefs.h" #include "Settings.h" +#include "EditorViewportSettings.h" // Qt #include @@ -487,7 +488,6 @@ void SEditorSettings::Save() SaveValue("Settings", "AutoBackupTime", autoBackupTime); SaveValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount); SaveValue("Settings", "AutoRemindTime", autoRemindTime); - SaveValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch); SaveValue("Settings", "CameraMoveSpeed", cameraMoveSpeed); SaveValue("Settings", "CameraRotateSpeed", cameraRotateSpeed); SaveValue("Settings", "StylusMode", stylusMode); @@ -682,7 +682,6 @@ void SEditorSettings::Load() LoadValue("Settings", "AutoBackupTime", autoBackupTime); LoadValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount); LoadValue("Settings", "AutoRemindTime", autoRemindTime); - LoadValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch); LoadValue("Settings", "CameraMoveSpeed", cameraMoveSpeed); LoadValue("Settings", "CameraRotateSpeed", cameraRotateSpeed); LoadValue("Settings", "StylusMode", stylusMode); @@ -935,8 +934,9 @@ void SEditorSettings::LoadDefaultGamePaths() searchPaths[EDITOR_PATH_MATERIALS].push_back((Path::GetEditingGameDataFolder() + "/Materials").c_str()); } - AZStd::string iconsPath; - AZ::StringFunc::Path::Join(Path::GetEditingRootFolder().c_str(), "Editor/UI/Icons", iconsPath); + auto iconsPath = AZ::IO::Path(AZ::Utils::GetEnginePath()) / "Assets"; + iconsPath /= "Editor/UI/Icons"; + iconsPath.MakePreferred(); searchPaths[EDITOR_PATH_UI_ICONS].push_back(iconsPath.c_str()); } @@ -1173,7 +1173,7 @@ AzToolsFramework::ConsoleColorTheme SEditorSettings::GetConsoleColorTheme() cons return consoleBackgroundColorTheme; } -int SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const +AZ::u64 SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const { - return SEditorSettings::maxNumberOfItemsShownInSearch; + return SandboxEditor::MaxItemsShownInAssetBrowserSearch(); } diff --git a/Code/Editor/Settings.h b/Code/Editor/Settings.h index 8bf22b43e5..426d2300d3 100644 --- a/Code/Editor/Settings.h +++ b/Code/Editor/Settings.h @@ -279,7 +279,7 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING SettingOutcome GetValue(const AZStd::string_view path) override; SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) override; AzToolsFramework::ConsoleColorTheme GetConsoleColorTheme() const override; - int GetMaxNumberOfItemsShownInSearchView() const override; + AZ::u64 GetMaxNumberOfItemsShownInSearchView() const override; void ConvertPath(const AZStd::string_view sourcePath, AZStd::string& category, AZStd::string& attribute); @@ -353,14 +353,6 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING int autoRemindTime; ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - // Asset Browser Search View. - ////////////////////////////////////////////////////////////////////////// - //! Current maximum number of items that can be displayed in the AssetBrowser Search View. - int maxNumberOfItemsShownInSearch; - ////////////////////////////////////////////////////////////////////////// - - //! If true preview windows is displayed when browsing geometries. bool bPreviewGeometryWindow; diff --git a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp index b510315995..d7901e338a 100644 --- a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp +++ b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp @@ -269,7 +269,7 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange() // Enable/disable the 'remove'/'update' button properly. bool bNoSelection = !m_ui->m_renderList->selectionModel()->hasSelection(); m_ui->BATCH_RENDER_REMOVE_SEQ->setEnabled(bNoSelection ? false : true); - + CheckForEnableUpdateButton(); if (bNoSelection) @@ -360,7 +360,7 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange() cvarsText += item.cvars[static_cast(i)]; cvarsText += "\r\n"; } - m_ui->m_cvarsEdit->setPlainText(cvarsText); + m_ui->m_cvarsEdit->setPlainText(cvarsText); } void CSequenceBatchRenderDialog::CheckForEnableUpdateButton() @@ -494,7 +494,7 @@ void CSequenceBatchRenderDialog::OnSavePreset() } void CSequenceBatchRenderDialog::stashActiveViewportResolution() -{ +{ // stash active resolution in global vars activeViewportWidth = resolutions[0][0]; activeViewportHeight = resolutions[0][1]; @@ -502,7 +502,7 @@ void CSequenceBatchRenderDialog::stashActiveViewportResolution() if (activeViewport) { activeViewport->GetDimensions(&activeViewportWidth, &activeViewportHeight); - } + } } void CSequenceBatchRenderDialog::OnGo() @@ -640,7 +640,7 @@ void CSequenceBatchRenderDialog::OnResolutionSelected() int defaultH; const QString currentCustomResText = m_ui->m_resolutionCombo->currentText(); GetResolutionFromCustomResText(currentCustomResText.toStdString().c_str(), defaultW, defaultH); - + CCustomResolutionDlg resDlg(defaultW, defaultH, this); if (resDlg.exec() == QDialog::Accepted) { @@ -752,7 +752,7 @@ bool CSequenceBatchRenderDialog::LoadOutputOptions(const QString& pathname) { const QString customResText = resolutionNode->getContent(); m_ui->m_resolutionCombo->setItemText(curSel, customResText); - + GetResolutionFromCustomResText(customResText.toStdString().c_str(), m_customResW, m_customResH); } m_ui->m_resolutionCombo->setCurrentIndex(curSel); @@ -907,12 +907,12 @@ void CSequenceBatchRenderDialog::CaptureItemStart() folder += "/"; folder += itemText; - // If this is a relative path, prepend the @assets@ folder to match where the Renderer is going + // If this is a relative path, prepend the @products@ folder to match where the Renderer is going // to dump the frame buffer image captures. if (AzFramework::StringFunc::Path::IsRelative(folder.toUtf8().data())) { AZStd::string absolutePath; - AZStd::string assetsRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@"); + AZStd::string assetsRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@"); AzFramework::StringFunc::Path::Join(assetsRoot.c_str(), folder.toUtf8().data(), absolutePath); folder = absolutePath.c_str(); } @@ -962,7 +962,7 @@ void CSequenceBatchRenderDialog::CaptureItemStart() m_renderContext.cvarDisplayInfoBU = cvarDebugInfo->GetIVal(); if (renderItem.disableDebugInfo && cvarDebugInfo->GetIVal()) { - const int DISPLAY_INFO_OFF = 0; + const int DISPLAY_INFO_OFF = 0; cvarDebugInfo->Set(DISPLAY_INFO_OFF); } } @@ -1100,13 +1100,13 @@ void CSequenceBatchRenderDialog::OnUpdateEnd(IAnimSequence* sequence) sequence->SetActiveDirector(m_renderContext.pActiveDirectorBU); const auto imageFormat = m_ui->m_imageFormatCombo->currentText(); - + SRenderItem renderItem = m_renderItems[m_renderContext.currentItemIndex]; if (m_bFFMPEGCommandAvailable && renderItem.bCreateVideo) { // Create a video using the ffmpeg plug-in from captured images. m_renderContext.processingFFMPEG = true; - + AZStd::string outputFolder = m_renderContext.captureOptions.folder; auto future = QtConcurrent::run( [renderItem, outputFolder, imageFormat] @@ -1238,7 +1238,7 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout() } void CSequenceBatchRenderDialog::OnKickIdle() -{ +{ if (m_renderContext.captureState == CaptureState::WarmingUpAfterResChange) { OnUpdateWarmingUpAfterResChange(); @@ -1254,7 +1254,7 @@ void CSequenceBatchRenderDialog::OnKickIdle() else if (m_renderContext.captureState == CaptureState::Capturing) { OnUpdateCapturing(); - } + } else if (m_renderContext.captureState == CaptureState::End) { OnUpdateEnd(m_renderContext.endingSequence); diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index eeb6912acf..baca69d628 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -1195,7 +1195,7 @@ bool CFileUtil::IsFileExclusivelyAccessable(const QString& strFilePath) ////////////////////////////////////////////////////////////////////////// bool CFileUtil::CreatePath(const QString& strPath) { -#if defined(AZ_PLATFORM_MAC) +#if !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS bool pathCreated = true; QString cleanPath = QDir::cleanPath(strPath); @@ -1252,7 +1252,7 @@ bool CFileUtil::CreatePath(const QString& strPath) } return true; -#endif +#endif // !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/Util/PathUtil.cpp b/Code/Editor/Util/PathUtil.cpp index c9e6823824..ca3481ddae 100644 --- a/Code/Editor/Util/PathUtil.cpp +++ b/Code/Editor/Util/PathUtil.cpp @@ -11,9 +11,9 @@ #include "PathUtil.h" -#include // for AZ_MAX_PATH_LEN +#include +#include #include // for ebus events -#include #include #include #include @@ -179,7 +179,7 @@ namespace Path EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot); return QString(engineRoot); } - + ////////////////////////////////////////////////////////////////////////// QString& ReplaceFilename(const QString& strFilepath, const QString& strFilename, QString& strOutputFilename, bool bCallCaselessPath) { @@ -216,30 +216,21 @@ namespace Path ////////////////////////////////////////////////////////////////////////// QString GetResolvedUserSandboxFolder() { - char resolvedPath[AZ_MAX_PATH_LEN] = { 0 }; - gEnv->pFileIO->ResolvePath(GetUserSandboxFolder().toUtf8().data(), resolvedPath, AZ_MAX_PATH_LEN); - return QString::fromLatin1(resolvedPath); + AZ::IO::FixedMaxPath userSandboxFolderPath; + gEnv->pFileIO->ResolvePath(userSandboxFolderPath, GetUserSandboxFolder().toUtf8().constData()); + return QString::fromUtf8(userSandboxFolderPath.c_str(), static_cast(userSandboxFolderPath.Native().size())); } // internal function, you should use GetEditingGameDataFolder instead. AZStd::string GetGameAssetsFolder() { - const char* resultValue = nullptr; - EBUS_EVENT_RESULT(resultValue, AzToolsFramework::AssetSystemRequestBus, GetAbsoluteDevGameFolderPath); - if (!resultValue) - { - if ((gEnv) && (gEnv->pFileIO)) - { - resultValue = gEnv->pFileIO->GetAlias("@devassets@"); - } - } - - if (!resultValue) + AZ::IO::Path projectPath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - resultValue = "."; + settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); } - return resultValue; + return projectPath.Native(); } /// Get the data folder @@ -258,26 +249,6 @@ namespace Path return str; } - //! Get the root folder (in source control or other writable assets) where you should save root data. - AZStd::string GetEditingRootFolder() - { - const char* resultValue = nullptr; - EBUS_EVENT_RESULT(resultValue, AzToolsFramework::AssetSystemRequestBus, GetAbsoluteDevRootFolderPath); - - if (!resultValue) - { - if ((gEnv) && (gEnv->pFileIO)) - { - resultValue = gEnv->pFileIO->GetAlias("@devassets@"); - } - } - if (!resultValue) - { - resultValue = "."; - } - return resultValue; - } - AZStd::string MakeModPathFromGamePath(const char* relGamePath) { @@ -335,165 +306,60 @@ namespace Path return ""; } - bool relPathfound = false; + bool relPathFound = false; AZStd::string relativePath; AZStd::string fullAssetPath(fullPath.toUtf8().data()); - EBUS_EVENT_RESULT(relPathfound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullAssetPath, relativePath); + EBUS_EVENT_RESULT(relPathFound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullAssetPath, relativePath); - if (relPathfound) + if (relPathFound) { // do not normalize this path, it will already be an appropriate asset ID. return CaselessPaths(relativePath.c_str()); } - char rootpath[_MAX_PATH] = { 0 }; - azstrcpy(rootpath, _MAX_PATH, Path::GetEditingRootFolder().c_str()); - - if (bRelativeToGameFolder) - { - azstrcpy(rootpath, _MAX_PATH, Path::GetEditingGameDataFolder().c_str()); - } - - QString rootPathNormalized(rootpath); - QString srcPathNormalized(fullPath); - -#if defined(AZ_PLATFORM_WINDOWS) - // avoid confusing PathRelativePathTo - rootPathNormalized.replace('/', '\\'); - srcPathNormalized.replace('/', '\\'); -#endif + AZ::IO::FixedMaxPath rootPath = bRelativeToGameFolder ? AZ::Utils::GetProjectPath() : AZ::Utils::GetEnginePath(); + AZ::IO::FixedMaxPath resolvedFullPath; + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedFullPath, fullPath.toUtf8().constData()); // Create relative path - char resolvedSrcPath[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(srcPathNormalized.toUtf8().data(), resolvedSrcPath, AZ_MAX_PATH_LEN); - QByteArray path = QDir(rootPathNormalized).relativeFilePath(resolvedSrcPath).toUtf8(); - if (path.isEmpty()) - { - return fullPath; - } - // The following code is required because the windows PathRelativePathTo function will always return "./SomePath" instead of just "SomePath" - // Only remove single dot (.) and slash parts of a path, never the double dot (..) - const char* pBuffer = path.data(); - bool bHasDot = false; - while (*pBuffer && pBuffer != path.end()) - { - switch (*pBuffer) - { - case '.': - if (bHasDot) - { - // Found a double dot, rewind and stop removing - pBuffer--; - break; - } - // Fall through intended - case '/': - case '\\': - bHasDot = (*pBuffer == '.'); - pBuffer++; - continue; - } - break; - } - - QString relPath = pBuffer; - return CaselessPaths(relPath); + return CaselessPaths(resolvedFullPath.LexicallyProximate(rootPath).MakePreferred().c_str()); } QString GamePathToFullPath(const QString& path) { using namespace AzToolsFramework; - AZ_Warning("GamePathToFullPath", path.size() <= AZ_MAX_PATH_LEN, "Path exceeds maximum path length of %d", AZ_MAX_PATH_LEN); - if ((gEnv) && (gEnv->pFileIO) && gEnv->pCryPak && path.size() <= AZ_MAX_PATH_LEN) + AZ_Warning("GamePathToFullPath", path.size() <= AZ::IO::MaxPathLength, "Path exceeds maximum path length of %zu", AZ::IO::MaxPathLength); + if (path.size() <= AZ::IO::MaxPathLength) { // first, adjust the file name for mods: - bool fullPathfound = false; - AZStd::string assetFullPath; - AZStd::string adjustedFilePath = path.toUtf8().data(); - AssetSystemRequestBus::BroadcastResult(fullPathfound, &AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, adjustedFilePath, assetFullPath); - if (fullPathfound) + bool fullPathFound = false; + AZ::IO::Path assetFullPath; + AZ::IO::Path adjustedFilePath = path.toUtf8().constData(); + AssetSystemRequestBus::BroadcastResult(fullPathFound, &AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, + adjustedFilePath.Native(), assetFullPath.Native()); + if (fullPathFound) { - //if the bus message succeeds than normalize and lowercase the path - AzFramework::StringFunc::Path::Normalize(assetFullPath); - return assetFullPath.c_str(); + //if the bus message succeeds than normalize + return assetFullPath.LexicallyNormal().c_str(); } - // if the bus message didn't succeed, 'guess' the source assets: + // if the bus message didn't succeed, check if he path exist as a resolved path else { // Not all systems have been converted to use local paths. Some editor files save XML files directly, and a full or correctly aliased path is already passed in. // If the path passed in exists already, then return the resolved filepath if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(adjustedFilePath.c_str())) { - char resolvedPath[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 }; - AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(adjustedFilePath.c_str(), resolvedPath, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength); - return QString::fromUtf8(resolvedPath); + AZ::IO::FixedMaxPath resolvedPath; + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedPath, adjustedFilePath); + return QString::fromUtf8(resolvedPath.c_str(), static_cast(resolvedPath.Native().size())); } - // if we get here it means that the Asset Processor does not know about this file. most of the time we should never get here - // the rest of this code just does a bunch of heuristic guesses in case of missing files or if the user has hand-edited - // the asset cache by moving files in via some other means or external process. - if (adjustedFilePath[0] != '@') - { - const char* prefix = (adjustedFilePath[0] == '/' || adjustedFilePath[0] == '\\') ? "@devassets@" : "@devassets@/"; - adjustedFilePath = prefix + adjustedFilePath; - } - - char szAdjustedFile[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 }; - gEnv->pFileIO->ResolvePath(adjustedFilePath.c_str(), szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile)); - - if ((azstrnicmp(szAdjustedFile, "@devassets@", 11) == 0) && ((szAdjustedFile[11] == '/') || (szAdjustedFile[11] == '\\'))) - { - if (!gEnv->pCryPak->IsFileExist(szAdjustedFile)) - { - AZStd::string newName(szAdjustedFile); - AzFramework::StringFunc::Replace(newName, "@devassets@", "@devroot@/engine", false); - - if (gEnv->pCryPak->IsFileExist(newName.c_str())) - { - azstrcpy(szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile), newName.c_str()); - } - else - { - // getting tricky here, try @devroot@ alone, in case its 'editor' - AzFramework::StringFunc::Replace(newName, "@devassets@", "@devroot@", false); - if (gEnv->pCryPak->IsFileExist(szAdjustedFile)) - { - azstrcpy(szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile), newName.c_str()); - } - // give up, best guess is just @devassets@ - } - } - } - - // we should very rarely actually get to this point in the code. - - // szAdjustedFile may contain an alias at this point. (@assets@/blah.whatever) - // there is a case in which the loose asset exists only within a pak file for some reason - // this is not recommended but it is possible.in that case, we want to return the original szAdjustedFile - // without touching it or resolving it so that crypak can open it successfully. - char adjustedPath[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 }; - if (gEnv->pFileIO->ResolvePath(szAdjustedFile, adjustedPath, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength)) // resolve to full path - { - if ((gEnv->pCryPak->IsFileExist(adjustedPath)) || (!gEnv->pCryPak->IsFileExist(szAdjustedFile))) - { - // note that if we get here, then EITHER - // the file exists as a loose asset in the actual adjusted path - // OR the file does not exist in the original passed-in aliased name (like '@assets@/whatever') - // in which case we may as well just resolve the path to a full path and return it. - assetFullPath = adjustedPath; - AzFramework::StringFunc::Path::Normalize(assetFullPath); - azstrcpy(szAdjustedFile, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength, assetFullPath.c_str()); - } - // if the above case succeeded then it means that the file does NOT exist loose - // but DOES exist in a pak, in which case we leave szAdjustedFile with the alias on the front of it, meaning - // fopens via crypak will actually succeed. - } - return szAdjustedFile; + return path; } } else { - return ""; + return QString{}; } } diff --git a/Code/Editor/Util/PathUtil.h b/Code/Editor/Util/PathUtil.h index 2c49031155..163d74e358 100644 --- a/Code/Editor/Util/PathUtil.h +++ b/Code/Editor/Util/PathUtil.h @@ -44,9 +44,6 @@ namespace Path //! always returns a full path EDITOR_CORE_API AZStd::string GetEditingGameDataFolder(); - //! Get the root folder (in source control or other writable assets) where you should save root data. - EDITOR_CORE_API AZStd::string GetEditingRootFolder(); - //! Set the current mod NAME for editing purposes. After doing this the above functions will take this into account //! name only, please! EDITOR_CORE_API void SetModName(const char* input); @@ -69,93 +66,6 @@ namespace Path return strPath; } - //! Split full file name to path and filename - //! @param filepath [IN] Full file name inclusing path. - //! @param path [OUT] Extracted file path. - //! @param file [OUT] Extracted file (with extension). - inline void Split(const QString& filepath, QString& path, QString& file) - { - char path_buffer[_MAX_PATH]; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; -#ifdef AZ_COMPILER_MSVC - _splitpath_s(filepath.toUtf8().data(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext)); - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0); - path = path_buffer; - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), 0, 0, fname, ext); -#else - _splitpath(filepath.toUtf8().data(), drive, dir, fname, ext); - _makepath(path_buffer, drive, dir, 0, 0); - path = path_buffer; - _makepath(path_buffer, 0, 0, fname, ext); -#endif - file = path_buffer; - } - inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& file) - { - char path_buffer[_MAX_PATH]; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; -#ifdef AZ_COMPILER_MSVC - _splitpath_s(filepath.c_str(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), 0, 0, 0, 0); - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0); - path = path_buffer; - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), 0, 0, fname, ext); -#else - _splitpath(filepath.c_str(), drive, dir, fname, ext); - _makepath(path_buffer, drive, dir, 0, 0); - path = path_buffer; - _makepath(path_buffer, 0, 0, fname, ext); -#endif - file = path_buffer; - } - - //! Split full file name to path and filename - //! @param filepath [IN] Full file name inclusing path. - //! @param path [OUT] Extracted file path. - //! @param filename [OUT] Extracted file (without extension). - //! @param ext [OUT] Extracted files extension. - inline void Split(const QString& filepath, QString& path, QString& filename, QString& fext) - { - char path_buffer[_MAX_PATH]; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; -#ifdef AZ_COMPILER_MSVC - _splitpath_s(filepath.toUtf8().data(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext)); - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0); -#else - _splitpath(filepath.toUtf8().data(), drive, dir, fname, ext); - _makepath(path_buffer, drive, dir, 0, 0); -#endif - path = path_buffer; - filename = fname; - fext = ext; - } - inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& filename, AZStd::string& fext) - { - char path_buffer[_MAX_PATH]; - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; -#ifdef AZ_COMPILER_MSVC - _splitpath_s(filepath.c_str(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext)); - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0); -#else - _splitpath(filepath.c_str(), drive, dir, fname, ext); - _makepath(path_buffer, drive, dir, 0, 0); -#endif - path = path_buffer; - filename = fname; - fext = ext; - } - //! Split path into segments //! @param filepath [IN] path inline QStringList SplitIntoSegments(const QString& path) 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..79e1f79697 100644 --- a/Code/Framework/AzCore/AzCore/Console/Console.cpp +++ b/Code/Framework/AzCore/AzCore/Console/Console.cpp @@ -225,8 +225,16 @@ namespace AZ ConsoleCommandContainer commandSubset; - for (ConsoleFunctorBase* curr = m_head; curr != nullptr; curr = curr->m_next) + for (const auto& functor : m_commands) { + if (functor.second.empty()) + { + continue; + } + + // Filter functors registered with the same name + const ConsoleFunctorBase* curr = functor.second.front(); + if ((curr->GetFlags() & ConsoleFunctorFlags::IsInvisible) == ConsoleFunctorFlags::IsInvisible) { // Filter functors marked as invisible @@ -236,7 +244,12 @@ namespace AZ if (StringFunc::StartsWith(curr->m_name, command, false)) { AZLOG_INFO("- %s : %s\n", curr->m_name, curr->m_desc); - commandSubset.push_back(curr->m_name); + + if (commandSubset.size() < MaxConsoleCommandPlusArgsLength) + { + commandSubset.push_back(curr->m_name); + } + if (matches) { matches->push_back(curr->m_name); @@ -271,7 +284,10 @@ namespace AZ { for (auto& curr : m_commands) { - visitor(curr.second.front()); + if (!curr.second.empty()) + { + visitor(curr.second.front()); + } } } @@ -336,6 +352,11 @@ namespace AZ { iter->second.erase(iter2); } + + if (iter->second.empty()) + { + m_commands.erase(iter); + } } functor->Unlink(m_head); functor->m_console = nullptr; @@ -476,15 +497,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 +532,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 +637,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/StackTracer.h b/Code/Framework/AzCore/AzCore/Debug/StackTracer.h index 430fd69168..2d09c16e99 100644 --- a/Code/Framework/AzCore/AzCore/Debug/StackTracer.h +++ b/Code/Framework/AzCore/AzCore/Debug/StackTracer.h @@ -40,6 +40,12 @@ namespace AZ static unsigned int Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount = 0, void* nativeThread = 0); }; + class StackConverter + { + public: + static unsigned int FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext); + }; + class SymbolStorage { public: 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..cde8c36a4e 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp @@ -31,6 +31,8 @@ namespace AZ { namespace Debug { + struct StackFrame; + namespace Platform { #if defined(AZ_ENABLE_DEBUG_TOOLS) @@ -224,6 +226,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); } @@ -549,17 +553,19 @@ namespace AZ { StackFrame frames[25]; - // Without StackFrame explicit alignment frames array is aligned to 4 bytes - // which causes the stack tracing to fail. - //size_t bla = AZStd::alignment_of::value; - //printf("Alignment value %d address 0x%08x : 0x%08x\n",bla,frames); SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)]; + unsigned int numFrames = 0; if (!nativeContext) { - suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrinCallstack). + suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrintCallstack). + numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount); } - unsigned int numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount, nativeContext); + else + { + numFrames = StackConverter::FromNative(frames, AZ_ARRAY_SIZE(frames), nativeContext); + } + if (numFrames) { SymbolStorage::DecodeFrames(frames, numFrames, lines); 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/IO/FileIO.h b/Code/Framework/AzCore/AzCore/IO/FileIO.h index 6406025417..25dd422a47 100644 --- a/Code/Framework/AzCore/AzCore/IO/FileIO.h +++ b/Code/Framework/AzCore/AzCore/IO/FileIO.h @@ -148,7 +148,7 @@ namespace AZ virtual AZ::u64 ModificationTime(HandleType fileHandle) = 0; virtual AZ::u64 ModificationTime(const char* filePath) = 0; - /// Get the size of the file. Returns Success if we report size. + /// Get the size of the file. Returns Success if we report size. virtual Result Size(const char* filePath, AZ::u64& size) = 0; virtual Result Size(HandleType fileHandle, AZ::u64& size) = 0; @@ -198,7 +198,7 @@ namespace AZ /// note: the callback will contain the full concatenated path (filePath + slash + fileName) /// not just the individual file name found. /// note: if the file path of the found file corresponds to a registered ALIAS, the longest matching alias will be returned - /// so expect return values like @assets@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections. + /// so expect return values like @products@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections. /// note: if rootPath is specified the implementation has the option of substituting it for the current directory /// as would be the case on a file server. typedef AZStd::function FindFilesCallbackType; @@ -206,13 +206,18 @@ namespace AZ // Alias system - /// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @root@, etc. + /// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @products@, etc. virtual void SetAlias(const char* alias, const char* path) = 0; /// ClearAlias - Removes an alias from the path resolution system virtual void ClearAlias(const char* alias) = 0; /// GetAlias - Returns the destination path for a given alias, or nullptr if the alias does not exist virtual const char* GetAlias(const char* alias) const = 0; + /// SetDeprecateAlias - Adds a deprecated alias with path resolution which points to a new alias + /// When the DeprecatedAlias is used an Error is logged and the alias is resolved to the path + /// specified by the new alais + virtual void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) = 0; + /// Shorten the given path if it contains an alias. it will always pick the longest alias match. /// note that it re-uses the buffer, since the data can only get smaller and we don't want to internally allocate memory if we /// can avoid it. @@ -230,8 +235,8 @@ namespace AZ //! ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath, //! also ensures that the path is absolute - //! NOTE: If the path does not start with an alias then the resolved value of the @assets@ is used - //! which has the effect of making the path relative to the @assets@/ folder + //! NOTE: If the path does not start with an alias then the resolved value of the @products@ is used + //! which has the effect of making the path relative to the @products@/ folder //! returns true if path was resolved, false otherwise //! note that all of the above file-finding and opening functions automatically resolve the path before operating //! so you should not need to call this except in very exceptional circumstances where you absolutely need to diff --git a/Code/Framework/AzCore/AzCore/IO/IStreamer.h b/Code/Framework/AzCore/AzCore/IO/IStreamer.h index d959fa716c..438384e687 100644 --- a/Code/Framework/AzCore/AzCore/IO/IStreamer.h +++ b/Code/Framework/AzCore/AzCore/IO/IStreamer.h @@ -42,8 +42,8 @@ namespace AZ::IO // These functions can't be called after a request has been queued. // - //! Creates a request to read a file. - //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@. + //! Creates a request to read a file. + //! @param relativePath Relative path to the file to load. This can include aliases such as @products@. //! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes. //! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes. //! @param readSize The number of bytes to read from the file at the relative path. @@ -62,9 +62,9 @@ namespace AZ::IO IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium, size_t offset = 0) = 0; - //! Sets a request to the read command. + //! Sets a request to the read command. //! @param request The request that will store the read command. - //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file to load. This can include aliases such as @products@. //! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes. //! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes. //! @param readSize The number of bytes to read from the file at the relative path. @@ -84,8 +84,8 @@ namespace AZ::IO IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium, size_t offset = 0) = 0; - //! Creates a request to the read command. - //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@. + //! Creates a request to the read command. + //! @param relativePath Relative path to the file to load. This can include aliases such as @products@. //! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will //! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult //! to claim the pointer and use the provided allocator to release the memory at a later point. @@ -106,9 +106,9 @@ namespace AZ::IO IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium, size_t offset = 0) = 0; - //! Sets a request to the read command. + //! Sets a request to the read command. //! @param request The request that will store the read command. - //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file to load. This can include aliases such as @products@. //! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will //! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult //! to claim the pointer and use the provided allocator to release the memory at a later point. @@ -138,7 +138,7 @@ namespace AZ::IO //! @result A smart pointer to the newly created request with the cancel command. virtual FileRequestPtr Cancel(FileRequestPtr target) = 0; - //! Sets a request to the cancel command. + //! Sets a request to the cancel command. //! When this request completes it's not guaranteed to have canceled the target request. Not all requests can be canceled and requests //! that already processing may complete. It's recommended to let the target request handle the completion of the request as normal //! and handle cancellation by checking the status on the target request is set to IStreamerTypes::RequestStatus::Canceled. @@ -177,7 +177,7 @@ namespace AZ::IO //! DestroyDedicatedCache is called. Typical use of a dedicated cache is for files that have their own compression //! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This //! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches. - //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@. //! @return A smart pointer to the newly created request with the command to create a dedicated cache. virtual FileRequestPtr CreateDedicatedCache(AZStd::string_view relativePath) = 0; @@ -186,25 +186,25 @@ namespace AZ::IO //! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This //! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches. //! @param request The request that will store the command to create a dedicated cache. - //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@. //! @return A reference to the provided request. virtual FileRequestPtr& CreateDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0; //! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details. - //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@. //! @return A smart pointer to the newly created request with the command to destroy a dedicated cache. virtual FileRequestPtr DestroyDedicatedCache(AZStd::string_view relativePath) = 0; //! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details. //! @param request The request that will store the command to destroy a dedicated cache. - //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@. //! @return A reference to the provided request. virtual FileRequestPtr& DestroyDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0; //! Clears a file from all caches in use by Streamer. //! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming //! processing. This can result in a noticeable interruption. - //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@. //! @return A smart pointer to the newly created request with the command to flush a file from all caches. virtual FileRequestPtr FlushCache(AZStd::string_view relativePath) = 0; @@ -212,7 +212,7 @@ namespace AZ::IO //! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming //! processing. This can result in a noticeable interruption. //! @param request The request that will store the command to flush a file from all caches. - //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@. + //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@. //! @return A reference to the provided request. virtual FileRequestPtr& FlushCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0; @@ -334,7 +334,7 @@ namespace AZ::IO // //! Collect statistics from all the components that make up Streamer. - //! This is thread safe in the sense that it won't crash. + //! This is thread safe in the sense that it won't crash. //! Data is collected lockless from involved threads and might be slightly //! out of date in some cases. //! @param statistics The container where statistics will be added to. diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.h b/Code/Framework/AzCore/AzCore/IO/Path/Path.h index 24f26daa51..3b5c224957 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.h +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.h @@ -98,6 +98,11 @@ namespace AZ::IO //! made from the internal string constexpr AZStd::fixed_string FixedMaxPathString() const noexcept; + // as_posix + //! Replicates the behavior of the Python pathlib as_posix method + //! by replacing the Windows Path Separator with the Posix Path Seperator + constexpr AZStd::fixed_string FixedMaxPathStringAsPosix() const noexcept; + // decomposition //! Given a windows path of "C:\O3DE\foo\bar\name.txt" and a posix path of //! "/O3DE/foo/bar/name.txt" @@ -178,7 +183,7 @@ namespace AZ::IO //! Normalizes a path in a purely lexical manner. //! # Path separators are converted to their preferred path separator //! # Path parts of "." are collapsed to nothing empty - //! # Paths parts of ".." are removed if there is a preceding directory + //! # Paths parts of ".." are removed if there is a preceding directory //! The preceding directory is also removed //! # Runs of Two or more path separators are collapsed into one path separator //! unless the path begins with two path separators @@ -238,7 +243,7 @@ namespace AZ::IO // iterators //! Returns an iterator to the beginning of the path that can be used to traverse the path - //! according to the following + //! according to the following //! 1. Root name - (0 or 1) //! 2. Root directory - (0 or 1) //! 3. Filename - (0 or more) @@ -253,24 +258,23 @@ namespace AZ::IO template friend class BasicPath; friend struct AZStd::hash; + struct PathIterable; - template - static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base); + static constexpr void MakeRelativeTo(PathIterable& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept; - struct PathIterable; //! Returns a structure that provides a view of the path parts which can be used for iteration //! Only the path parts that correspond to creating an normalized path is returned //! This function is useful for returning a "view" into a normalized path without the need //! to allocate memory for the heap static constexpr PathIterable GetNormalPathParts(const AZ::IO::PathView& path) noexcept; - // joins the input path to the Path Iterable structure using similiar logic to Path::Append - // If the input path is absolute it will replace the current PathIterable otherwise - // the input path will be appended to the Path Iterable structure - // For example a PathIterable with parts = ['C:', '/', 'foo'] - // If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar'] - // If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar'] - // If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ] - // If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ] + //! joins the input path to the Path Iterable structure using similiar logic to Path::Append + //! If the input path is absolute it will replace the current PathIterable otherwise + //! the input path will be appended to the Path Iterable structure + //! For example a PathIterable with parts = ['C:', '/', 'foo'] + //! If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar'] + //! If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar'] + //! If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ] + //! If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ] static constexpr void AppendNormalPathParts(PathIterable& pathIterableResult, const AZ::IO::PathView& path) noexcept; constexpr int ComparePathView(const PathView& other) const; @@ -325,32 +329,32 @@ namespace AZ::IO constexpr BasicPath(BasicPath&& other) = default; // Conversion constructor for other types of BasicPath instantiations - constexpr BasicPath(const PathView& other); + constexpr BasicPath(const PathView& other) noexcept; // String constructors //! Constructs a Path by copying the pathString to its internal string //! The preferred separator is to the OS default path separator constexpr BasicPath(const string_type& pathString) noexcept; //! Constructs a Path by copying the pathString to its internal string - //! The preferred separator it set to the parameter + //! The preferred separator is set to the parameter constexpr BasicPath(const string_type& pathString, const char preferredSeparator) noexcept; //! Constructs a Path by moving the pathString to its internal string //! The preferred separator is to the OS default path separator constexpr BasicPath(string_type&& pathString) noexcept; //! Constructs a Path by copying the pathString to its internal string - //! The preferred separator it set to the parameter + //! The preferred separator is set to the parameter constexpr BasicPath(string_type&& pathString, const char preferredSeparator) noexcept; //! Constructs a Path by constructing it's internal out of a string_view //! The preferred separator is to the OS default path separator constexpr BasicPath(AZStd::string_view src) noexcept; //! Constructs a Path by constructing it's internal out of a string_view - //! The preferred separators it set to the parameter + //! The preferred separator is set to the parameter constexpr BasicPath(AZStd::string_view src, const char preferredSeparator) noexcept; //! Constructs a Path by constructing it's internal out of a value_type* //! The preferred separator is to the OS default path separator constexpr BasicPath(const value_type* pathString) noexcept; //! Constructs a Path by constructing it's internal out of a value_type* - //! The preferred separator it set to the parameter + //! The preferred separator is set to the parameter constexpr BasicPath(const value_type* pathString, const char preferredSeparator) noexcept; //! Constructs a empty Path with the preferred separator set to the parameter explicit constexpr BasicPath(const char preferredSeparator) noexcept; @@ -371,7 +375,7 @@ namespace AZ::IO constexpr BasicPath& operator=(BasicPath&& other) = default; // conversion assignment operator - constexpr BasicPath& operator=(const PathView& pathView); + constexpr BasicPath& operator=(const PathView& pathView) noexcept; constexpr BasicPath& operator=(const string_type& str) noexcept; constexpr BasicPath& operator=(string_type&& str) noexcept; constexpr BasicPath& operator=(AZStd::string_view str) noexcept; @@ -477,6 +481,12 @@ namespace AZ::IO //! made from the internal string constexpr AZStd::fixed_string FixedMaxPathString() const; + // as_posix + //! Replicates the behavior of the Python pathlib as_posix method + //! by replacing the Windows Path Separator with the Posix Path Seperator + AZStd::string StringAsPosix() const; + constexpr AZStd::fixed_string FixedMaxPathStringAsPosix() const noexcept; + // compare //! Performs a compare of each of the path parts for equivalence //! Each part of the path is compare using string comparison @@ -574,7 +584,7 @@ namespace AZ::IO //! Normalizes a path in a purely lexical manner. //! # Path separators are converted to their preferred path separator //! # Path parts of "." are collapsed to nothing empty - //! # Paths parts of ".." are removed if there is a preceding directory + //! # Paths parts of ".." are removed if there is a preceding directory //! The preceding directory is also removed //! # Runs of Two or more path separators are collapsed into one path separator //! unless the path begins with two path separators @@ -616,7 +626,7 @@ namespace AZ::IO // iterators //! Returns an iterator to the beginning of the path that can be used to traverse the path - //! according to the following + //! according to the following //! 1. Root name - (0 or 1) //! 2. Root directory - (0 or 1) //! 3. Filename - (0 or more) diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl index 0147ad3356..1d654c1502 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl @@ -240,6 +240,14 @@ namespace AZ::IO return AZStd::fixed_string(m_path.begin(), m_path.end()); } + // as_posix + constexpr AZStd::fixed_string PathView::FixedMaxPathStringAsPosix() const noexcept + { + AZStd::fixed_string resultPath(m_path.begin(), m_path.end()); + AZStd::replace(resultPath.begin(), resultPath.end(), AZ::IO::WindowsPathSeparator, AZ::IO::PosixPathSeparator); + return resultPath; + } + // decomposition constexpr auto PathView::RootName() const -> PathView { @@ -473,8 +481,7 @@ namespace AZ::IO return lhs.Compare(rhs) >= 0; } - template - constexpr void PathView::MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) + constexpr void PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept { const bool exactCaseCompare = path.m_preferred_separator == PosixPathSeparator || base.m_preferred_separator == PosixPathSeparator; @@ -492,13 +499,11 @@ namespace AZ::IO if (int res = Internal::ComparePathSegment(*pathParser, *pathParserBase, exactCaseCompare); res != 0) { - pathResult.m_path = AZStd::string_view{}; return; } } else if (CheckIterMismatchAtBase()) { - pathResult.m_path = AZStd::string_view{}; return; } @@ -512,7 +517,6 @@ namespace AZ::IO } if (CheckIterMismatchAtBase()) { - pathResult.m_path = AZStd::string_view{}; return; } } @@ -530,7 +534,7 @@ namespace AZ::IO // If there is no mismatch, return ".". if (!pathParser && !pathParserBase) { - pathResult.m_path = AZStd::string_view{ "." }; + pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot); return; } @@ -539,27 +543,25 @@ namespace AZ::IO int elemCount = parser::DetermineLexicalElementCount(pathParserBase); if (elemCount < 0) { - pathResult.m_path = AZStd::string_view{}; return; } // if elemCount == 0 and (pathParser == end() || pathParser->empty()), returns path("."); otherwise if (elemCount == 0 && (pathParser.AtEnd() || *pathParser == "")) { - pathResult.m_path = AZStd::string_view{ "." }; + pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot); return; } // return a path constructed with 'n' dot-dot elements, followed by the // elements of '*this' after the mismatch. - pathResult = PathResultType(path.m_preferred_separator); while (elemCount--) { - pathResult /= ".."; + pathIterable.emplace_back("..", parser::PathPartKind::PK_DotDot); } for (; pathParser; ++pathParser) { - pathResult /= *pathParser; + pathIterable.emplace_back(*pathParser, parser::ClassifyPathPart(pathParser)); } } @@ -673,7 +675,7 @@ namespace AZ::IO // Basic Path implementation template - constexpr BasicPath::BasicPath(const PathView& other) + constexpr BasicPath::BasicPath(const PathView& other) noexcept : m_path(other.m_path) , m_preferred_separator(other.m_preferred_separator) {} @@ -726,6 +728,7 @@ namespace AZ::IO : m_path(first, last) , m_preferred_separator(preferredSeparator) {} + template constexpr BasicPath::operator PathView() const noexcept { @@ -733,7 +736,7 @@ namespace AZ::IO } template - constexpr auto BasicPath::operator=(const PathView& other) -> BasicPath& + constexpr auto BasicPath::operator=(const PathView& other) noexcept -> BasicPath& { m_path = other.m_path; m_preferred_separator = other.m_preferred_separator; @@ -974,13 +977,13 @@ namespace AZ::IO template constexpr auto BasicPath::MakePreferred() -> BasicPath& { - if (m_preferred_separator != '/') + if (m_preferred_separator != PosixPathSeparator) { - AZStd::replace(m_path.begin(), m_path.end(), '/', m_preferred_separator); + AZStd::replace(m_path.begin(), m_path.end(), PosixPathSeparator, m_preferred_separator); } else { - AZStd::replace(m_path.begin(), m_path.end(), '\\', m_preferred_separator); + AZStd::replace(m_path.begin(), m_path.end(), WindowsPathSeparator, m_preferred_separator); } return *this; } @@ -1033,6 +1036,24 @@ namespace AZ::IO return AZStd::fixed_string(m_path.begin(), m_path.end()); } + // as_posix + // Returns a copy of the path with the path separators converted to PosixPathSeparator + template + AZStd::string BasicPath::StringAsPosix() const + { + AZStd::string resultPath(m_path.begin(), m_path.end()); + AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator); + return resultPath; + } + + template + constexpr AZStd::fixed_string BasicPath::FixedMaxPathStringAsPosix() const noexcept + { + AZStd::fixed_string resultPath(m_path.begin(), m_path.end()); + AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator); + return resultPath; + } + template constexpr void BasicPath::swap(BasicPath& rhs) noexcept { @@ -1234,6 +1255,7 @@ namespace AZ::IO { pathResult /= pathPartView; } + return pathResult; } @@ -1241,7 +1263,13 @@ namespace AZ::IO constexpr auto BasicPath::LexicallyRelative(const PathView& base) const -> BasicPath { BasicPath pathResult(m_preferred_separator); - static_cast(*this).MakeRelativeTo(pathResult, *this, base); + PathView::PathIterable pathIterable; + PathView::MakeRelativeTo(pathIterable, *this, base); + for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable) + { + pathResult /= pathPartView; + } + return pathResult; } @@ -1355,7 +1383,7 @@ namespace AZ::IO return !basePathParts.empty() || !thisPathParts.IsAbsolute(); } - constexpr FixedMaxPath PathView::LexicallyNormal() const + constexpr auto PathView::LexicallyNormal() const -> FixedMaxPath { FixedMaxPath pathResult(m_preferred_separator); PathIterable pathIterable = GetNormalPathParts(*this); @@ -1367,21 +1395,28 @@ namespace AZ::IO return pathResult; } - constexpr FixedMaxPath PathView::LexicallyRelative(const PathView& base) const + constexpr auto PathView::LexicallyRelative(const PathView& base) const -> FixedMaxPath { FixedMaxPath pathResult(m_preferred_separator); - MakeRelativeTo(pathResult, *this, base); + PathIterable pathIterable; + MakeRelativeTo(pathIterable, *this, base); + for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable) + { + pathResult /= pathPartView; + } + return pathResult; } - constexpr FixedMaxPath PathView::LexicallyProximate(const PathView& base) const + constexpr auto PathView::LexicallyProximate(const PathView& base) const -> FixedMaxPath { - FixedMaxPath result = LexicallyRelative(base); - if (result.empty()) + FixedMaxPath pathResult = LexicallyRelative(base); + if (pathResult.empty()) { return FixedMaxPath(*this); } - return result; + + return pathResult; } } diff --git a/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl b/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl index a1faa29b31..e700ab0196 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl @@ -49,8 +49,8 @@ namespace AZ::IO constexpr void clear() noexcept; - friend constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView&) noexcept -> PathIterable; friend constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView&) noexcept -> void; + friend constexpr auto PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView&, const AZ::IO::PathView&) noexcept -> void; PartKindArray m_parts{}; size_t m_size{}; }; 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); - - //! 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); - } + //! @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. + //! @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 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); + //! @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); + + //! 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); - } -} - -#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H -#pragma once + const Vector3& point, + const Vector3& segmentStart, + const Vector3& segmentEnd, + float& proportion, + Vector3& closestPointOnSegment); + } // namespace Intersect +} // namespace AZ + +#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/Math/Uuid.cpp b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp index d875e28a1e..410d235a37 100644 --- a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp @@ -135,18 +135,12 @@ namespace AZ { stringLength = strlen(uuidString); } - if (stringLength > MaxPermissiveStringSize) - { - if (!skipWarnings) - { - AZ_Warning("Math", false, "Can't create UUID from string length %zu over maximum %zu", stringLength, MaxPermissiveStringSize); - } - return Uuid::CreateNull(); - } + size_t newLength{ 0 }; char createString[MaxPermissiveStringSize]; - for (size_t curPos = 0; curPos < stringLength; ++curPos) + // Loop until we get to the end of the string OR stop once we've accumulated a full UUID string worth of data + for (size_t curPos = 0; curPos < stringLength && newLength < ValidUuidStringLength; ++curPos) { char curChar = uuidString[curPos]; switch (curChar) diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.h b/Code/Framework/AzCore/AzCore/Math/Uuid.h index ea920e45c8..6b77e7ec3e 100644 --- a/Code/Framework/AzCore/AzCore/Math/Uuid.h +++ b/Code/Framework/AzCore/AzCore/Math/Uuid.h @@ -42,8 +42,9 @@ namespace AZ //VER_AZ_RANDOM_CRC32 = 6, // 0 1 1 0 }; + static constexpr int ValidUuidStringLength = 32; /// Number of characters (data only, no extra formatting) in a valid UUID string static const size_t MaxStringBuffer = 39; /// 32 Uuid + 4 dashes + 2 brackets + 1 terminate - + Uuid() {} Uuid(const char* string, size_t stringLength = 0) { *this = CreateString(string, stringLength); } 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/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index e1bd717e7b..36f66312d8 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -546,7 +546,7 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory(); registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native()); - // Engine root folder - corresponds to the @engroot@ and @devroot@ aliases + // Engine root folder - corresponds to the @engroot@ and @engroot@ aliases AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry); registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native()); @@ -570,7 +570,7 @@ namespace AZ::SettingsRegistryMergeUtils assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME); } - // Project path - corresponds to the @devassets@ alias + // Project path - corresponds to the @projectroot@ alias // NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded. path = engineRoot / projectPathValue; @@ -662,7 +662,7 @@ namespace AZ::SettingsRegistryMergeUtils } else { - // Cache: root - same as the @root@ alias, this is the starting path for cache files. + // Cache: root - same as the @products@ alias, this is the starting path for cache files. path = normalizedProjectPath / "Cache"; registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native()); path /= assetPlatform; 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/UnitTest/Mocks/MockFileIOBase.h b/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h index c9b7d44e1f..d7d0789c1a 100644 --- a/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h +++ b/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h @@ -52,6 +52,7 @@ namespace AZ MOCK_METHOD2(SetAlias, void(const char* alias, const char* path)); MOCK_METHOD1(ClearAlias, void(const char* alias)); MOCK_CONST_METHOD1(GetAlias, const char*(const char* alias)); + MOCK_METHOD2(SetDeprecatedAlias, void(AZStd::string_view, AZStd::string_view)); MOCK_CONST_METHOD2(ConvertToAlias, AZStd::optional(char* inOutBuffer, AZ::u64 bufferLength)); MOCK_CONST_METHOD2(ConvertToAlias, bool(AZ::IO::FixedMaxPath& aliasPath, const AZ::IO::PathView& path)); MOCK_CONST_METHOD3(ResolvePath, bool(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize)); diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp index c4dd24be35..2031d14d08 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp @@ -51,6 +51,20 @@ namespace AZ::Utils return executableDirectory; } + AZStd::optional ConvertToAbsolutePath(AZStd::string_view path) + { + AZ::IO::FixedMaxPathString absolutePath; + AZ::IO::FixedMaxPathString srcPath{ path }; + if (ConvertToAbsolutePath(srcPath.c_str(), absolutePath.data(), absolutePath.capacity())) + { + // Fix the size value of the fixed string by calculating the c-string length using char traits + absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data())); + return srcPath; + } + + return AZStd::nullopt; + } + AZ::IO::FixedMaxPathString GetEngineManifestPath() { AZ::IO::FixedMaxPath o3deManifestPath = GetO3deManifestDirectory(); diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.h b/Code/Framework/AzCore/AzCore/Utils/Utils.h index d8d4290f7f..c147e38bfb 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.h +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.h @@ -104,6 +104,7 @@ namespace AZ // Attempts the supplied path to an absolute path. //! Returns nullopt if path cannot be converted to an absolute path AZStd::optional ConvertToAbsolutePath(AZStd::string_view path); + bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 absolutePathMaxSize); //! Save a string to a file. Otherwise returns a failure with error message. AZ::Outcome WriteFile(AZStd::string_view content, AZStd::string_view filePath); 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/CMakeLists.txt b/Code/Framework/AzCore/CMakeLists.txt index 60c5f50594..96ed838ccc 100644 --- a/Code/Framework/AzCore/CMakeLists.txt +++ b/Code/Framework/AzCore/CMakeLists.txt @@ -39,7 +39,7 @@ ly_add_target( 3rdParty::Lua 3rdParty::RapidJSON 3rdParty::RapidXML - 3rdParty::zlib + 3rdParty::ZLIB 3rdParty::zstd 3rdParty::cityhash ${AZ_CORE_PIX_BUILD_DEPENDENCIES} diff --git a/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp b/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp index 1333006c4c..9a3f825bfc 100644 --- a/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp +++ b/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp @@ -60,23 +60,34 @@ namespace AZ return writeStorage ? AZStd::make_optional(writeStorage) : AZStd::nullopt; } - AZStd::optional ConvertToAbsolutePath(AZStd::string_view path) + bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) { - AZ::IO::FixedMaxPathString absolutePath; - AZ::IO::FixedMaxPathString srcPath{ path }; - if (AZ::Android::Utils::IsApkPath(srcPath.c_str())) + if (AZ::Android::Utils::IsApkPath(path)) { - return srcPath; + azstrcpy(absolutePath, maxLength, path); + return true; } - if(char* result = realpath(srcPath.c_str(), absolutePath.data()); result) +#ifdef PATH_MAX + static constexpr size_t UnixMaxPathLength = PATH_MAX; +#else + // Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System + static constexpr size_t UnixMaxPathLength = 4096; +#endif + if (!AZ::IO::PathView(path).IsAbsolute()) { - // Fix the size value of the fixed string by calculating the c-string length using char traits - absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data())); - return absolutePath; + // note that realpath fails if the path does not exist and actually changes the return value + // to be the actual place that FAILED, which we don't want. + // if we fail, we'd prefer to fall through and at least use the original path. + char absolutePathBuffer[UnixMaxPathLength]; + if (const char* result = realpath(path, absolutePathBuffer); result != nullptr) + { + azstrcpy(absolutePath, maxLength, absolutePathBuffer); + return true; + } } - - return AZStd::nullopt; + azstrcpy(absolutePath, maxLength, path); + return AZ::IO::PathView(absolutePath).IsAbsolute(); } } } diff --git a/Code/Framework/AzCore/Platform/Common/Unimplemented/AzCore/Debug/StackTracer_Unimplemented.cpp b/Code/Framework/AzCore/Platform/Common/Unimplemented/AzCore/Debug/StackTracer_Unimplemented.cpp index 0c091a7242..a751eb505c 100644 --- a/Code/Framework/AzCore/Platform/Common/Unimplemented/AzCore/Debug/StackTracer_Unimplemented.cpp +++ b/Code/Framework/AzCore/Platform/Common/Unimplemented/AzCore/Debug/StackTracer_Unimplemented.cpp @@ -17,6 +17,11 @@ namespace AZ return false; } + unsigned int StackConverter::FromNative(StackFrame*, unsigned int, void*) + { + return 0; + } + void SymbolStorage::LoadModuleData(const void*, unsigned int) {} diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Debug/StackTracer_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Debug/StackTracer_UnixLike.cpp index f66a9d3b18..1948bbf2fe 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Debug/StackTracer_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Debug/StackTracer_UnixLike.cpp @@ -78,6 +78,12 @@ StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned return count; } +unsigned int StackConverter::FromNative([[maybe_unused]] StackFrame* frames, [[maybe_unused]] unsigned int maxNumOfFrames, [[maybe_unused]] void* nativeContext) +{ + AZ_Assert(false, "StackConverter::FromNative() is not supported for UnixLike platform yet"); + return 0; +} + void SymbolStorage::DecodeFrames(const StackFrame* frames, unsigned int numFrames, StackLine* textLines) { diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp index 2e31936057..7327c8f152 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp @@ -47,23 +47,32 @@ namespace AZ AZ::IO::FixedMaxPath path{pass->pw_dir}; return path.Native(); } - + return {}; } - AZStd::optional ConvertToAbsolutePath(AZStd::string_view path) + bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) { - AZ::IO::FixedMaxPathString absolutePath; - AZ::IO::FixedMaxPathString srcPath{ path }; - - if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result) +#ifdef PATH_MAX + static constexpr size_t UnixMaxPathLength = PATH_MAX; +#else + // Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System + static constexpr size_t UnixMaxPathLength = 4096; +#endif + if (!AZ::IO::PathView(path).IsAbsolute()) { - // Fix the size value of the fixed string by calculating the c-string length using char traits - absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data())); - return absolutePath; + // note that realpath fails if the path does not exist and actually changes the return value + // to be the actual place that FAILED, which we don't want. + // if we fail, we'd prefer to fall through and at least use the original path. + char absolutePathBuffer[UnixMaxPathLength]; + if (const char* result = realpath(path, absolutePathBuffer); result != nullptr) + { + azstrcpy(absolutePath, maxLength, absolutePathBuffer); + return true; + } } - - return AZStd::nullopt; + azstrcpy(absolutePath, maxLength, path); + return AZ::IO::PathView(absolutePath).IsAbsolute(); } } // namespace Utils } // namespace AZ 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/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp index bcba24b768..24786e2bc9 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp @@ -67,19 +67,12 @@ namespace AZ return AZStd::nullopt; } - AZStd::optional ConvertToAbsolutePath(AZStd::string_view path) + bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) { - AZ::IO::FixedMaxPathString absolutePath; - AZ::IO::FixedMaxPathString srcPath{ path }; - char* result = _fullpath(absolutePath.data(), srcPath.c_str(), absolutePath.capacity()); - // Force update of the fixed_string size() value - absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data())); - if (result) - { - return absolutePath; - } - - return AZStd::nullopt; + char* result = _fullpath(absolutePath, path, maxLength); + return result != nullptr; } + + } } diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp b/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp index b2a1410bf5..90362a3ce8 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp @@ -1048,9 +1048,9 @@ cleanup: unsigned int StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount, void* nativeThread) { -#if defined(AZ_ENABLE_DEBUG_TOOLS) unsigned int numFrames = 0; +#if defined(AZ_ENABLE_DEBUG_TOOLS) if (nativeThread == NULL) { ++suppressCount; // Skip current call @@ -1079,9 +1079,8 @@ cleanup: STACKFRAME64 sf; memset(&sf, 0, sizeof(STACKFRAME64)); - DWORD imageType; + DWORD imageType = IMAGE_FILE_MACHINE_AMD64; - imageType = IMAGE_FILE_MACHINE_AMD64; sf.AddrPC.Offset = context.Rip; sf.AddrPC.Mode = AddrModeFlat; sf.AddrFrame.Offset = context.Rsp; @@ -1090,8 +1089,7 @@ cleanup: sf.AddrStack.Mode = AddrModeFlat; EnterCriticalSection(&g_csDbgHelpDll); - s32 frame = -(s32)suppressCount; - for (; frame < (s32)maxNumOfFrames; ++frame) + for (s32 frame = -static_cast(suppressCount); frame < static_cast(maxNumOfFrames); ++frame) { if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, &context, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0)) { @@ -1111,15 +1109,68 @@ cleanup: } LeaveCriticalSection(&g_csDbgHelpDll); - } - return numFrames; + } #else - (void)frames; - (void)maxNumOfFrames; - (void)suppressCount; - (void)nativeThread; - return 0; + AZ_UNUSED(frames); + AZ_UNUSED(maxNumOfFrames); + AZ_UNUSED(suppressCount); + AZ_UNUSED(nativeThread); #endif // AZ_ENABLE_DEBUG_TOOLS + + return numFrames; + } + + unsigned int StackConverter::FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext) + { + unsigned int numFrames = 0; + +#if defined(AZ_ENABLE_DEBUG_TOOLS) + if (!g_dbgHelpLoaded) + { + LoadDbgHelp(); + } + + HANDLE hThread; + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS); + + PCONTEXT nativeContextType = reinterpret_cast(nativeContext); + STACKFRAME64 sf; + memset(&sf, 0, sizeof(STACKFRAME64)); + + DWORD imageType = IMAGE_FILE_MACHINE_AMD64; + + sf.AddrPC.Offset = nativeContextType->Rip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrFrame.Offset = nativeContextType->Rsp; + sf.AddrFrame.Mode = AddrModeFlat; + sf.AddrStack.Offset = nativeContextType->Rsp; + sf.AddrStack.Mode = AddrModeFlat; + + EnterCriticalSection(&g_csDbgHelpDll); + for (unsigned int frame = 0; frame < maxNumOfFrames; ++frame) + { + if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, nativeContext, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0)) + { + break; + } + + if (sf.AddrPC.Offset == sf.AddrReturn.Offset) + { + // "StackWalk64-Endless-Callstack!" + break; + } + + frames[numFrames++].m_programCounter = sf.AddrPC.Offset; + } + + LeaveCriticalSection(&g_csDbgHelpDll); +#else + AZ_UNUSED(frames); + AZ_UNUSED(maxNumOfFrames); + AZ_UNUSED(nativeContext); +#endif + + return numFrames; } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzCore/Tests/AZStd/Hashed.cpp b/Code/Framework/AzCore/Tests/AZStd/Hashed.cpp index b92dee44b6..c982868c4f 100644 --- a/Code/Framework/AzCore/Tests/AZStd/Hashed.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/Hashed.cpp @@ -1413,7 +1413,7 @@ namespace UnitTest >; TYPED_TEST_CASE(HashedSetDifferentAllocatorFixture, SetTemplateConfigs); -#if GTEST_OS_SUPPORTS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST TYPED_TEST(HashedSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages) { using ContainerType = typename TypeParam::ContainerType; @@ -1435,7 +1435,7 @@ namespace UnitTest } }, ".*"); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST template class HashedMapContainers @@ -1811,7 +1811,7 @@ namespace UnitTest >; TYPED_TEST_CASE(HashedMapDifferentAllocatorFixture, MapTemplateConfigs); -#if GTEST_OS_SUPPORTS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST TYPED_TEST(HashedMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages) { using ContainerType = typename TypeParam::ContainerType; @@ -1833,7 +1833,7 @@ namespace UnitTest } } , ".*"); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST namespace HashedContainerTransparentTestInternal { 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/AZStd/Ordered.cpp b/Code/Framework/AzCore/Tests/AZStd/Ordered.cpp index ca7d5cba42..26838eeb63 100644 --- a/Code/Framework/AzCore/Tests/AZStd/Ordered.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/Ordered.cpp @@ -1095,7 +1095,7 @@ namespace UnitTest >; TYPED_TEST_CASE(TreeSetDifferentAllocatorFixture, SetTemplateConfigs); -#if GTEST_OS_SUPPORTS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST TYPED_TEST(TreeSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages) { using ContainerType = typename TypeParam::ContainerType; @@ -1117,7 +1117,7 @@ namespace UnitTest } }, ".*"); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST TYPED_TEST(TreeSetDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer) { @@ -1516,7 +1516,7 @@ namespace UnitTest >; TYPED_TEST_CASE(TreeMapDifferentAllocatorFixture, MapTemplateConfigs); -#if GTEST_OS_SUPPORTS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST TYPED_TEST(TreeMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages) { using ContainerType = typename TypeParam::ContainerType; @@ -1538,7 +1538,7 @@ namespace UnitTest } }, ".*"); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST TYPED_TEST(TreeMapDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer) { diff --git a/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp b/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp index 407cd3c258..6b9202133c 100644 --- a/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp @@ -1595,7 +1595,7 @@ namespace UnitTest } }; -#if GTEST_OS_SUPPORTS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST TEST_F(ThreadEventsDeathTest, UsingClientBus_AvoidsDeadlock) { EXPECT_EXIT( @@ -1608,5 +1608,5 @@ namespace UnitTest , ::testing::ExitedWithCode(0),".*"); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST } diff --git a/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp b/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp index 3e6376323c..e33dbce9c1 100644 --- a/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp +++ b/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp @@ -736,7 +736,10 @@ namespace UnitTest auto& assetManager = AssetManager::Instance(); AssetBusCallbacks callbacks{}; + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning callbacks.SetOnAssetReadyCallback([&, AssetNoRefB](const Asset&, AssetBusCallbacks&) + AZ_POP_DISABLE_WARNING { // This callback should run inside the "main thread" dispatch events loop auto loadAsset = assetManager.GetAsset(AZ::Uuid(AssetNoRefB), AssetLoadBehavior::Default); 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/Console/ConsoleTests.cpp b/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp index d91ec5ba58..e01129a7bc 100644 --- a/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp +++ b/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp @@ -288,6 +288,21 @@ namespace AZ AZStd::string completeCommand = console->AutoCompleteCommand("testVec3"); AZ_TEST_ASSERT(completeCommand == "testVec3"); } + + // Duplicate names + { + // Register two cvars with the same name + auto id = AZ::TypeId(); + auto flag = AZ::ConsoleFunctorFlags::Null; + auto signature = AZ::ConsoleFunctor::FunctorSignature(); + AZ::ConsoleFunctor cvarOne(*console, "testAutoCompleteDuplication", "", flag, id, signature); + AZ::ConsoleFunctor cvarTwo(*console, "testAutoCompleteDuplication", "", flag, id, signature); + + // Autocomplete given name expecting one match (not two) + AZStd::vector matches; + AZStd::string completeCommand = console->AutoCompleteCommand("testAutoCompleteD", &matches); + AZ_TEST_ASSERT(matches.size() == 1 && completeCommand == "testAutoCompleteDuplication"); + } } TEST_F(ConsoleTests, ConsoleFunctor_FreeFunctorExecutionTest) diff --git a/Code/Framework/AzCore/Tests/Debug/LocalFileEventLoggerTests.cpp b/Code/Framework/AzCore/Tests/Debug/LocalFileEventLoggerTests.cpp index 242ac0e65d..d4b3351dbe 100644 --- a/Code/Framework/AzCore/Tests/Debug/LocalFileEventLoggerTests.cpp +++ b/Code/Framework/AzCore/Tests/Debug/LocalFileEventLoggerTests.cpp @@ -109,7 +109,10 @@ namespace AZ::Debug AZStd::thread threads[totalThreads]; for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex) { + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning threads[threadIndex] = AZStd::thread([&startLogging, &messages]() + AZ_POP_DISABLE_WARNING { while (!startLogging) { @@ -226,7 +229,10 @@ namespace AZ::Debug AZStd::thread threads[totalThreads]; for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex) { + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning threads[threadIndex] = AZStd::thread([&startLogging, &message, &totalRecordsWritten]() + AZ_POP_DISABLE_WARNING { AZ_UNUSED(message); diff --git a/Code/Framework/AzCore/Tests/Debug/UnhandledExceptions.cpp b/Code/Framework/AzCore/Tests/Debug/UnhandledExceptions.cpp new file mode 100644 index 0000000000..cd9055085f --- /dev/null +++ b/Code/Framework/AzCore/Tests/Debug/UnhandledExceptions.cpp @@ -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 + * + */ + +#include + +namespace UnitTest +{ + class UnhandledExceptions + : public ScopedAllocatorSetupFixture + { + + public: + void causeAccessViolation() + { + int* someVariable = reinterpret_cast(0); + *someVariable = 0; + } + }; + +#if GTEST_HAS_DEATH_TEST + TEST_F(UnhandledExceptions, Handle) + { + EXPECT_DEATH(causeAccessViolation(), ""); + } +#endif +} 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/FileIOBaseTestTypes.h b/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h index 26b3df55bb..5897b3aaee 100644 --- a/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h +++ b/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h @@ -426,6 +426,10 @@ public: return nullptr; } + void SetDeprecatedAlias(AZStd::string_view, AZStd::string_view) override + { + } + void ClearAlias(const char* ) override { } AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64) const override diff --git a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp index 34076a10f6..cf239a0821 100644 --- a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp +++ b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp @@ -698,7 +698,7 @@ AZ_POP_DISABLE_WARNING using PathViewLexicallyProximateFixture = PathLexicallyFixture; - TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPathIfNot) + TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPath) { const auto& testParams = GetParam(); AZ::IO::PathView testPath(testParams.m_testPathString, testParams.m_preferredSeparator); diff --git a/Code/Framework/AzCore/Tests/Memory/LeakDetection.cpp b/Code/Framework/AzCore/Tests/Memory/LeakDetection.cpp index b4f129bb48..09255ce16f 100644 --- a/Code/Framework/AzCore/Tests/Memory/LeakDetection.cpp +++ b/Code/Framework/AzCore/Tests/Memory/LeakDetection.cpp @@ -144,14 +144,13 @@ namespace UnitTest } }; -#if GTEST_OS_SUPPORTS_DEATH_TEST - // SPEC-2669: Disabled since it is causing hangs on Linux +#if GTEST_HAS_DEATH_TEST TEST_F(AllocatorsTestFixtureLeakDetectionDeathTest_SKIPCODECOVERAGE, AllocatorLeak) { // testing that the TraceBusHook will fail on cause the test to die EXPECT_DEATH(TestAllocatorLeak(), ""); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Testing ScopedAllocatorSetupFixture. Testing that detects leaks diff --git a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp index 22fb6379d2..e312e2058d 100644 --- a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp +++ b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp @@ -597,7 +597,10 @@ namespace AZ::IO path.InitFromAbsolutePath(m_dummyFilepath); request->CreateRead(nullptr, buffer.get(), fileSize, path, 0, fileSize); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [&fileSize, this](const FileRequest& request) + AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); auto& readRequest = AZStd::get(request.GetCommand()); @@ -639,7 +642,10 @@ namespace AZ::IO path.InitFromAbsolutePath(m_dummyFilepath); request->CreateRead(nullptr, buffer, unalignedSize + 4, path, unalignedOffset, unalignedSize); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [unalignedOffset, unalignedSize, this](const FileRequest& request) + AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); auto& readRequest = AZStd::get(request.GetCommand()); @@ -784,7 +790,10 @@ namespace AZ::IO requests[i] = m_context->GetNewInternalRequest(); requests[i]->CreateRead(nullptr, buffers[i].get(), chunkSize, path, i * chunkSize, chunkSize); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [chunkSize, i](const FileRequest& request) + AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); auto& readRequest = AZStd::get(request.GetCommand()); @@ -970,7 +979,10 @@ namespace AZ::IO i * chunkSize )); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [numChunks, &numCallbacks, &waitForReads](FileRequestHandle request) + AZ_POP_DISABLE_WARNING { IStreamer* streamer = Interface::Get(); if (streamer) @@ -1038,7 +1050,10 @@ namespace AZ::IO i * chunkSize )); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [numChunks, &waitForReads, &waitForSingleRead, &numReadCallbacks]([[maybe_unused]] FileRequestHandle request) + AZ_POP_DISABLE_WARNING { numReadCallbacks++; if (numReadCallbacks == 1) @@ -1059,7 +1074,10 @@ namespace AZ::IO for (size_t i = 0; i < numChunks; ++i) { cancels.push_back(m_streamer->Cancel(requests[numChunks - i - 1])); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [&numCancelCallbacks, &waitForCancels, numChunks](FileRequestHandle request) + AZ_POP_DISABLE_WARNING { auto result = Interface::Get()->GetRequestStatus(request); EXPECT_EQ(result, IStreamerTypes::RequestStatus::Completed); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp index cc4d31eca9..9d4af1def5 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp @@ -327,7 +327,7 @@ namespace JsonSerializationTests SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get()); } -#if GTEST_OS_SUPPORTS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST using JsonSerializationDeathTests = JsonRegistrationContextTests; TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts) { @@ -338,5 +338,6 @@ namespace JsonSerializationTests }, ".*" ); } -#endif // GTEST_OS_SUPPORTS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST + } //namespace JsonSerializationTests 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/StringFunc.cpp b/Code/Framework/AzCore/Tests/StringFunc.cpp index 68821ba3f9..dc4bc40e5b 100644 --- a/Code/Framework/AzCore/Tests/StringFunc.cpp +++ b/Code/Framework/AzCore/Tests/StringFunc.cpp @@ -363,7 +363,10 @@ namespace AZ { constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" }; size_t visitIndex{}; + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token) + AZ_POP_DISABLE_WARNING { if (visitIndex > visitTokens.size()) { @@ -389,7 +392,10 @@ namespace AZ { constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" }; size_t visitIndex = visitTokens.size() - 1; + AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token) + AZ_POP_DISABLE_WARNING { if (visitIndex > visitTokens.size()) { diff --git a/Code/Framework/AzCore/Tests/UUIDTests.cpp b/Code/Framework/AzCore/Tests/UUIDTests.cpp index a18dc33c4f..64b9048a62 100644 --- a/Code/Framework/AzCore/Tests/UUIDTests.cpp +++ b/Code/Framework/AzCore/Tests/UUIDTests.cpp @@ -247,4 +247,27 @@ namespace UnitTest Uuid right = Uuid::CreateStringPermissive(permissiveStr1); EXPECT_EQ(left, right); } + + TEST_F(UuidTests, CreateStringPermissive_StringWithExtraData_Succeeds) + { + const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}"; + Uuid left = Uuid::CreateString(uuidStr); + + const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World"; + Uuid right = Uuid::CreateStringPermissive(permissiveStr); + EXPECT_EQ(left, right); + + } + + TEST_F(UuidTests, CreateStringPermissive_StringWithLotsOfExtraData_Succeeds) + { + const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}"; + Uuid left = Uuid::CreateString(uuidStr); + + const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World this is a really long string " + "with lots of extra data to make sure we can parse a long string without failing as long as the uuid is in " + "the beginning of the string then we should succeed"; + Uuid right = Uuid::CreateStringPermissive(permissiveStr); + EXPECT_EQ(left, right); + } } diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index e155594aa0..cc6000209f 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -72,14 +72,16 @@ set(FILES Debug/AssetTracking.cpp Debug/LocalFileEventLoggerTests.cpp Debug/Trace.cpp + Debug/UnhandledExceptions.cpp 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 +196,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/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index e72e2de472..1f99a594fa 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -77,11 +77,15 @@ namespace AzFramework { + namespace ApplicationInternal { static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem"; static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures"; static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage"; + static constexpr const char* DeprecatedFileIOAliasesRoot = "/O3DE/AzCore/FileIO/DeprecatedAliases"; + static constexpr const char* DeprecatedFileIOAliasesOldAliasKey = "OldAlias"; + static constexpr const char* DeprecatedFileIOAliasesNewAliasKey = "NewAlias"; } Application::Application() @@ -563,6 +567,68 @@ namespace AzFramework } } + struct DeprecatedAliasesKeyVisitor + : AZ::SettingsRegistryInterface::Visitor + { + using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse; + using VisitAction = AZ::SettingsRegistryInterface::VisitAction; + using Type = AZ::SettingsRegistryInterface::Type; + + using AZ::SettingsRegistryInterface::Visitor::Visit; + + VisitResponse Traverse(AZStd::string_view path, AZStd::string_view, + VisitAction action, Type type) override + { + if (action == AZ::SettingsRegistryInterface::VisitAction::Begin) + { + if (type == AZ::SettingsRegistryInterface::Type::Array) + { + m_parentArrayPath = path; + } + + // Strip off last path segment from json path and check if is a child element of the array + if (AZ::StringFunc::TokenizeLast(path, '/'); + m_parentArrayPath == path) + { + m_aliases.emplace_back(); + } + } + else if (action == AZ::SettingsRegistryInterface::VisitAction::End) + { + if (type == AZ::SettingsRegistryInterface::Type::Array) + { + m_parentArrayPath = AZStd::string{}; + } + } + + return AZ::SettingsRegistryInterface::VisitResponse::Continue; + } + + void Visit(AZStd::string_view, AZStd::string_view valueName, Type, AZStd::string_view value) override + { + if (!m_aliases.empty()) + { + if (valueName == ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey) + { + m_aliases.back().m_oldAlias = value; + } + else if (valueName == ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey) + { + m_aliases.back().m_newAlias = value; + } + } + } + + struct AliasPair + { + AZStd::string m_oldAlias; + AZStd::string m_newAlias; + }; + AZStd::vector m_aliases; + + private: + AZStd::string m_parentArrayPath; + }; static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase) { @@ -610,9 +676,8 @@ namespace AzFramework void Application::SetFileIOAliases() { - if (m_archiveFileIO) + if (auto fileIoBase = m_archiveFileIO.get(); fileIoBase) { - auto fileIoBase = m_archiveFileIO.get(); // Set up the default file aliases based on the settings registry fileIoBase->SetAlias("@engroot@", GetEngineRoot()); fileIoBase->SetAlias("@projectroot@", GetEngineRoot()); @@ -620,29 +685,20 @@ namespace AzFramework { AZ::IO::FixedMaxPath pathAliases; - if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder)) - { - fileIoBase->SetAlias("@projectcache@", pathAliases.c_str()); - } pathAliases.clear(); if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) { - fileIoBase->SetAlias("@assets@", pathAliases.c_str()); - fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str()); - fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@ + fileIoBase->SetAlias("@products@", pathAliases.c_str()); } pathAliases.clear(); if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) { fileIoBase->SetAlias("@engroot@", pathAliases.c_str()); - fileIoBase->SetAlias("@devroot@", pathAliases.c_str()); // Deprecated - Use @engroot@ } pathAliases.clear(); if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath)) { - fileIoBase->SetAlias("@devassets@", pathAliases.c_str()); // Deprecated - Use @projectsourceassets@ fileIoBase->SetAlias("@projectroot@", pathAliases.c_str()); - fileIoBase->SetAlias("@projectsourceassets@", (pathAliases / "Assets").c_str()); } } @@ -663,6 +719,15 @@ namespace AzFramework } fileIoBase->SetAlias("@log@", projectLogPath.c_str()); fileIoBase->CreatePath(projectLogPath.c_str()); + + DeprecatedAliasesKeyVisitor visitor; + if (m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot)) + { + for (const auto& [oldAlias, newAlias] : visitor.m_aliases) + { + fileIoBase->SetDeprecatedAlias(oldAlias, newAlias); + } + } } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index 8064ba6669..e5a42aabbc 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -1121,7 +1121,7 @@ namespace AZ::IO if (AZ::IO::FixedMaxPath pathBindRoot; !AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, szBindRoot)) { - AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@assets@"); + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@products@"); desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String(); } else @@ -1807,9 +1807,9 @@ namespace AZ::IO if (m_eRecordFileOpenList != IArchive::RFOM_Disabled) { // we only want to record ASSET access - // assets are identified as files that are relative to the resolved @assets@ alias path + // assets are identified as files that are relative to the resolved @products@ alias path auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); - const char* aliasValue = fileIoBase->GetAlias("@assets@"); + const char* aliasValue = fileIoBase->GetAlias("@products@"); if (AZ::IO::FixedMaxPath resolvedFilePath; fileIoBase->ResolvePath(resolvedFilePath, szFilename) diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp index 85ce0b6f9a..9e6e1034ea 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp @@ -546,6 +546,16 @@ namespace AZ::IO realUnderlyingFileIO->GetAlias(alias); } + void ArchiveFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) + { + FileIOBase* realUnderlyingFileIO = FileIOBase::GetDirectInstance(); + if (!realUnderlyingFileIO) + { + return; + } + realUnderlyingFileIO->SetDeprecatedAlias(oldAlias, newAlias); + } + AZStd::optional ArchiveFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const { if ((!inOutBuffer) || (bufferLength == 0)) diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h index 21cef18a7a..7fd4e15e3a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h @@ -63,6 +63,7 @@ namespace AZ::IO IO::Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override; void SetAlias(const char* alias, const char* path) override; void ClearAlias(const char* alias) override; + void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override; AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override; bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override; using FileIOBase::ConvertToAlias; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp index d7a92efbf6..7b483dc5de 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp @@ -186,8 +186,8 @@ namespace AZ::IO { // filter out the stuff which does not match. - // the problem here is that szDir might be something like "@assets@/levels/*" - // but our archive might be mounted at the root, or at some other folder at like "@assets@" or "@assets@/levels/mylevel" + // the problem here is that szDir might be something like "@products@/levels/*" + // but our archive might be mounted at the root, or at some other folder at like "@products@" or "@products@/levels/mylevel" // so there's really no way to filter out opening the pack and looking at the files inside. // however, the bind root is not part of the inner zip entry name either // and the ZipDir::FindFile actually expects just the chopped off piece. @@ -202,22 +202,22 @@ namespace AZ::IO // Example: - // "@assets@\\levels\\*" <--- szDir - // "@assets@\\" <--- mount point + // "@products@\\levels\\*" <--- szDir + // "@products@\\" <--- mount point // ~~~~~~~~~~~ Common part // "levels\\*" <---- remainder that is not in common // "" <--- mount point remainder. In this case, we should scan the contents of the pak for the remainder // Example: - // "@assets@\\levels\\*" <--- szDir - // "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak) + // "@products@\\levels\\*" <--- szDir + // "@products@\\levels\\mylevel\\" <--- mount point (its level.pak) // ~~~~~~~~~~~~~~~~~~ common part // "*" <---- remainder that is not in common // "mylevel\\" <--- mount point remainder. // example: - // "@assets@\\levels\\otherlevel\\*" <--- szDir - // "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak) + // "@products@\\levels\\otherlevel\\*" <--- szDir + // "@products@\\levels\\mylevel\\" <--- mount point (its level.pak) // "otherlevel\\*" <---- remainder // "mylevel\\" <--- mount point remainder. @@ -249,7 +249,7 @@ namespace AZ::IO // which means we may search inside the pack. ScanInZip(it->pZip.get(), sourcePathRemainder.Native()); } - + } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp b/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp index 0a6116d313..4c6ed0363e 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp @@ -94,7 +94,7 @@ namespace AZ::IO::Internal } AZStd::smatch matches; - const AZStd::regex lodRegex("@assets@\\\\(.*)_lod[0-9]+(\\.cgfm?)"); + const AZStd::regex lodRegex("@products@\\\\(.*)_lod[0-9]+(\\.cgfm?)"); if (!AZStd::regex_match(szPath, matches, lodRegex) || matches.size() != 3) { // The current file is not a valid LOD file diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp index 9b1946701c..e6b8211c28 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp @@ -725,7 +725,7 @@ namespace AzFramework if (!info.m_relativePath.empty()) { - const char* devAssetRoot = fileIO->GetAlias("@devassets@"); + const char* devAssetRoot = fileIO->GetAlias("@projectroot@"); if (devAssetRoot) { AZ::Data::AssetStreamInfo streamInfo; diff --git a/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp b/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp index 725f71a3c9..5d82cf488a 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp +++ b/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp @@ -133,6 +133,8 @@ namespace AzFramework behaviorContext->Class("Entity") ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) ->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "entity") ->Constructor() ->Constructor() ->Constructor() diff --git a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp index a7c6b18061..cc31df3dbc 100644 --- a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp +++ b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp @@ -61,7 +61,7 @@ namespace AzFramework AZ::IO::Path& gemAbsPath = gemInfo.m_absoluteSourcePaths.emplace_back(value); // Resolve any file aliases first - Do not use ResolvePath() as that assumes - // any relative path is underneath the @assets@ alias + // any relative path is underneath the @products@ alias if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr) { AZ::IO::FixedMaxPath replacedAliasPath; 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/IO/LocalFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp index 49e16dcb90..c1b9c941bc 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp @@ -12,10 +12,12 @@ #include #include #include +#include #include #include #include #include +#include #include namespace AZ @@ -292,7 +294,7 @@ namespace AZ void LocalFileIO::CheckInvalidWrite([[maybe_unused]] const char* path) { #if defined(AZ_ENABLE_TRACING) - const char* assetAliasPath = GetAlias("@assets@"); + const char* assetAliasPath = GetAlias("@products@"); if (path && assetAliasPath) { const AZ::IO::PathView pathView(path); @@ -478,17 +480,15 @@ namespace AZ return false; } - if (IsAbsolutePath(path)) + if (AZ::IO::PathView(path).HasRootPath()) { size_t pathLen = strlen(path); if (pathLen + 1 < resolvedPathSize) { azstrncpy(resolvedPath, resolvedPathSize, path, pathLen + 1); - //see if the absolute path uses @assets@ or @root@, if it does lowercase the relative part - [[maybe_unused]] bool lowercasePath = LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@assets@")) - || LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@root@")) - || LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@projectplatformcache@")); + //see if the absolute path matches the resolved value of @products@, if it does lowercase the relative part + LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@products@")); ToUnixSlashes(resolvedPath, resolvedPathSize); return true; @@ -499,34 +499,39 @@ namespace AZ } } - char rootedPathBuffer[AZ_MAX_PATH_LEN] = {0}; + constexpr AZStd::string_view productAssetAlias = "@products@"; + // Add plus one for the path separator: / + constexpr size_t MaxPathSizeWithProductAssetAlias = AZ::IO::MaxPathLength + productAssetAlias.size() + 1; + using RootedPathString = AZStd::fixed_string; + RootedPathString rootedPathBuffer; const char* rootedPath = path; - // if the path does not begin with an alias, then it is assumed to begin with @assets@ + // if the path does not begin with an alias, then it is assumed to begin with @products@ if (path[0] != '@') { - if (GetAlias("@assets@")) + if (GetAlias("@products@")) { - const int rootLength = 9;// strlen("@assets@/") - azstrncpy(rootedPathBuffer, AZ_MAX_PATH_LEN, "@assets@/", rootLength); - size_t pathLen = strlen(path); - size_t rootedPathBufferlength = rootLength + pathLen + 1;// +1 for null terminator - if (rootedPathBufferlength > resolvedPathSize) + + if (const size_t requiredSize = productAssetAlias.size() + strlen(path) + 1; + requiredSize > rootedPathBuffer.capacity()) { - AZ_Assert(rootedPathBufferlength < resolvedPathSize, "Constructed path length is wrong:%s", rootedPathBuffer);//path constructed is wrong - size_t remainingSize = resolvedPathSize - rootLength - 1;// - 1 for null terminator - azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN, path, remainingSize); - rootedPathBuffer[resolvedPathSize - 1] = '\0'; + AZ_Error("FileIO", false, "Prepending the %.*s alias to the input path results in a path longer than the" + " AZ::IO::MaxPathLength + the alias size of %zu. The size of the potential failed path is %zu", + AZ_STRING_ARG(productAssetAlias), rootedPathBuffer.capacity(), requiredSize) } else { - azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN - rootLength, path, pathLen + 1); + rootedPathBuffer = RootedPathString::format("%.*s/%s", AZ_STRING_ARG(productAssetAlias), path); } } else { - ConvertToAbsolutePath(path, rootedPathBuffer, AZ_MAX_PATH_LEN); + if (ConvertToAbsolutePath(path, rootedPathBuffer.data(), rootedPathBuffer.capacity())) + { + // Recalculate the internal string length + rootedPathBuffer.resize_no_construct(AZStd::char_traits::length(rootedPathBuffer.data())); + } } - rootedPath = rootedPathBuffer; + rootedPath = rootedPathBuffer.c_str(); } if (ResolveAliases(rootedPath, resolvedPath, resolvedPathSize)) @@ -561,11 +566,57 @@ namespace AZ const char* LocalFileIO::GetAlias(const char* key) const { - const auto it = m_aliases.find(key); - if (it != m_aliases.end()) + if (const auto it = m_aliases.find(key); it != m_aliases.end()) { return it->second.c_str(); } + else if (const auto deprecatedIt = m_deprecatedAliases.find(key); + deprecatedIt != m_deprecatedAliases.end()) + { + AZ_Error("FileIO", false, R"(Alias "%s" is deprecated. Please use alias "%s" instead)", + key, deprecatedIt->second.c_str()); + AZStd::string_view aliasValue = deprecatedIt->second; + // Contains the list of aliases resolved so far + // If max_size is hit, than an error is logged and nullptr is returned + using VisitedAliasSet = AZStd::fixed_unordered_set; + VisitedAliasSet visitedAliasSet; + while (aliasValue.starts_with("@")) + { + if (visitedAliasSet.contains(aliasValue)) + { + AZ_Error("FileIO", false, "Cycle found with for alias %.*s when trying to resolve deprecated alias %s", + AZ_STRING_ARG(aliasValue), key); + return nullptr; + } + + if(visitedAliasSet.size() == visitedAliasSet.max_size()) + { + AZ_Error("FileIO", false, "Unable to resolve path to deprecated alias %s within %zu steps", + key, visitedAliasSet.max_size()); + return nullptr; + } + + // Add the current alias value to the visited set + visitedAliasSet.emplace(aliasValue); + + // Check if the alias value corresponds to another alias + if (auto resolvedIter = m_aliases.find(aliasValue); resolvedIter != m_aliases.end()) + { + aliasValue = resolvedIter->second; + } + else if (resolvedIter = m_deprecatedAliases.find(aliasValue); + resolvedIter != m_deprecatedAliases.end()) + { + aliasValue = resolvedIter->second; + } + else + { + return nullptr; + } + } + + return aliasValue.data(); + } return nullptr; } @@ -574,6 +625,11 @@ namespace AZ m_aliases.erase(key); } + void LocalFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) + { + m_deprecatedAliases[oldAlias] = newAlias; + } + AZStd::optional LocalFileIO::ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const { size_t longestMatch = 0; @@ -675,7 +731,9 @@ namespace AZ : string_view_pair{}; size_t requiredResolvedPathSize = pathView.size() - aliasKey.size() + aliasValue.size() + 1; - AZ_Assert(path != resolvedPath && resolvedPathSize >= requiredResolvedPathSize, "Resolved path is incorrect"); + AZ_Assert(path != resolvedPath, "ResolveAliases does not support inplace update of the path"); + AZ_Assert(resolvedPathSize >= requiredResolvedPathSize, "Resolved path size %llu not large enough. It needs to be %zu", + resolvedPathSize, requiredResolvedPathSize); // we assert above, but we also need to properly handle the case when the resolvedPath buffer size // is too small to copy the source into. if (path == resolvedPath || (resolvedPathSize < requiredResolvedPathSize)) @@ -699,13 +757,9 @@ namespace AZ resolvedPath[resolvedPathLen] = '\0'; // If the path started with one of the "asset cache" path aliases, lowercase the path - const char* assetAliasPath = GetAlias("@assets@"); - const char* rootAliasPath = GetAlias("@root@"); - const char* projectPlatformCacheAliasPath = GetAlias("@projectplatformcache@"); + const char* projectPlatformCacheAliasPath = GetAlias("@products@"); - const bool lowercasePath = (assetAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, assetAliasPath)) || - (rootAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, rootAliasPath)) || - (projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath)); + const bool lowercasePath = projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath); if (lowercasePath) { @@ -822,5 +876,10 @@ namespace AZ return pathStr + "/"; } + + bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const + { + return AZ::Utils::ConvertToAbsolutePath(path, absolutePath, maxLength); + } } // namespace IO } // namespace AZ diff --git a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h index a9db55b320..a5ee1519a2 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h +++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h @@ -61,6 +61,8 @@ namespace AZ void SetAlias(const char* alias, const char* path) override; void ClearAlias(const char* alias) override; const char* GetAlias(const char* alias) const override; + void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override; + AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override; bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override; using FileIOBase::ConvertToAlias; @@ -71,7 +73,7 @@ namespace AZ bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const override; bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const; - + private: SystemFile* GetFilePointerFromHandle(HandleType fileHandle); @@ -79,7 +81,6 @@ namespace AZ AZStd::optional ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const; bool ResolveAliases(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const; - bool IsAbsolutePath(const char* path) const; bool LowerIfBeginsWith(char* inOutBuffer, AZ::u64 bufferLen, const char* alias) const; @@ -91,6 +92,7 @@ namespace AZ AZStd::atomic m_nextHandle; AZStd::unordered_map m_openFiles; AZStd::unordered_map m_aliases; + AZStd::unordered_map m_deprecatedAliases; void CheckInvalidWrite(const char* path); }; diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp index 041e5baf4a..9e05cd5cb9 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp @@ -49,14 +49,14 @@ namespace AZ s_IOLog.append(m_name); s_IOLog.append("\r\n"); } - + void Append(const char* line) { s_IOLog.append(AZStd::string::format("%u ", m_fileOperation)); s_IOLog.append(line); s_IOLog.append("\r\n"); } - + ~LogCall() { s_IOLog.append(AZStd::string::format("%u End ", m_fileOperation)); @@ -251,7 +251,7 @@ namespace AZ REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath).c_str()); return ResultCode::Error; } - + size = response.m_size; REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size=%u. return Success", filePath, size).c_str()); return ResultCode::Success; @@ -793,6 +793,12 @@ namespace AZ REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ClearAlias(alias=%s)", alias?alias:"nullptr").c_str()); } + void NetworkFileIO::SetDeprecatedAlias([[maybe_unused]] AZStd::string_view oldAlias, [[maybe_unused]] AZStd::string_view newAlias) + { + REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::SetDeprecatedAlias(oldAlias=%.*s, newAlias=%.*s)", + AZ_STRING_ARG(oldAlias), AZ_STRING_ARG(newAlias)).c_str()); + } + AZStd::optional NetworkFileIO::ConvertToAlias(char* inOutBuffer, [[maybe_unused]] AZ::u64 bufferLength) const { REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(inOutBuffer=%s, bufferLength=%u)", inOutBuffer?inOutBuffer:"nullptr", bufferLength).c_str()); @@ -927,7 +933,7 @@ namespace AZ { m_cacheLookaheadPos = filePosition - CacheStartFilePosition(); } - + void RemoteFileCache::SyncCheck() { #ifdef REMOTEFILEIO_SYNC_CHECK @@ -955,7 +961,7 @@ namespace AZ AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle); REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle).c_str()); } - + if (responce.m_offset != m_filePosition) { AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset); @@ -1028,7 +1034,7 @@ namespace AZ { REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Close(fileHandle=%u)", fileHandle).c_str()); Result returnValue = NetworkFileIO::Close(fileHandle); - + if (returnValue == ResultCode::Success) { AZStd::lock_guard lock(m_remoteFileCacheGuard); @@ -1160,7 +1166,7 @@ namespace AZ REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Read(fileHandle=%u, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str()); AZStd::lock_guard lock(m_remoteFileCacheGuard); RemoteFileCache& cache = GetCache(fileHandle); - + AZ::u64 remainingBytesToRead = size; AZ::u64 bytesReadFromCache = 0; AZ::u64 remainingBytesInCache = cache.RemainingBytes(); @@ -1263,7 +1269,7 @@ namespace AZ RemoteFileCache& cache = GetCache(fileHandle); if (cache.m_cacheLookaheadBuffer.size() && cache.RemainingBytes()) { - // find out where we are + // find out where we are AZ::u64 seekPosition = cache.CacheFilePosition(); // note, seeks are predicted, and do not ask for a response. @@ -1361,6 +1367,14 @@ namespace AZ } } + void RemoteFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) + { + if (m_excludedFileIO) + { + m_excludedFileIO->SetDeprecatedAlias(oldAlias, newAlias); + } + } + AZStd::optional RemoteFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const { return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(inOutBuffer, bufferLength) : strlen(inOutBuffer); diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h index d91e59bebc..77e91e1978 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h +++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h @@ -102,6 +102,7 @@ namespace AZ Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override; void SetAlias(const char* alias, const char* path) override; void ClearAlias(const char* alias) override; + void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override; AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override; bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override; using FileIOBase::ConvertToAlias; @@ -194,6 +195,7 @@ namespace AZ void SetAlias(const char* alias, const char* path) override; const char* GetAlias(const char* alias) const override; void ClearAlias(const char* alias) override; + void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override; AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override; bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override; using FileIOBase::ConvertToAlias; diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp index 6837807f4e..a24860fd34 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp @@ -35,6 +35,7 @@ namespace AzFramework ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingAnd::Config::GetNameLabelOverride) ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names", "The source input channel names that will be mapped to the output input channel name.") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } } diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp index bc47065c05..7c983766c8 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp @@ -35,6 +35,7 @@ namespace AzFramework ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingOr::Config::GetNameLabelOverride) ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names", "The source input channel names that will be mapped to the output input channel name.") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } } 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/IMatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h new file mode 100644 index 0000000000..22b65f8340 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h @@ -0,0 +1,63 @@ +/* + * 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 +{ + //! IMatchmakingRequests + //! Pure virtual session interface class to abstract the details of session handling from application code. + class IMatchmakingRequests + { + public: + AZ_RTTI(IMatchmakingRequests, "{BC0B74DA-A448-4F40-9B50-9D73142829D5}"); + + IMatchmakingRequests() = default; + virtual ~IMatchmakingRequests() = default; + + // Registers a player's acceptance or rejection of a proposed matchmaking. + // @param acceptMatchRequest The request of AcceptMatch operation + virtual void AcceptMatch(const AcceptMatchRequest& acceptMatchRequest) = 0; + + // Create a game match for a group of players. + // @param startMatchmakingRequest The request of StartMatchmaking operation + // @return A unique identifier for a matchmaking ticket + virtual AZStd::string StartMatchmaking(const StartMatchmakingRequest& startMatchmakingRequest) = 0; + + // Cancels a matchmaking ticket that is currently being processed. + // @param stopMatchmakingRequest The request of StopMatchmaking operation + virtual void StopMatchmaking(const StopMatchmakingRequest& stopMatchmakingRequest) = 0; + }; + + //! IMatchmakingAsyncRequests + //! Async version of IMatchmakingRequests + class IMatchmakingAsyncRequests + { + public: + AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}"); + + IMatchmakingAsyncRequests() = default; + virtual ~IMatchmakingAsyncRequests() = default; + + // AcceptMatch Async + // @param acceptMatchRequest The request of AcceptMatch operation + virtual void AcceptMatchAsync(const AcceptMatchRequest& acceptMatchRequest) = 0; + + // StartMatchmaking Async + // @param startMatchmakingRequest The request of StartMatchmaking operation + virtual void StartMatchmakingAsync(const StartMatchmakingRequest& startMatchmakingRequest) = 0; + + // StopMatchmaking Async + // @param stopMatchmakingRequest The request of StopMatchmaking operation + virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h new file mode 100644 index 0000000000..ad61971a11 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h @@ -0,0 +1,62 @@ +/* + * 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 AzFramework +{ + //! MatchmakingAsyncRequestNotifications + //! The notifications correspond to matchmaking async requests + class MatchmakingAsyncRequestNotifications + : public AZ::EBusTraits + { + public: + // Safeguard handler for multi-threaded use case + using MutexType = AZStd::recursive_mutex; + + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + // OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes + virtual void OnAcceptMatchAsyncComplete() = 0; + + // OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes + // @param matchmakingTicketId The unique identifier for the matchmaking ticket + virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0; + + // OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes + virtual void OnStopMatchmakingAsyncComplete() = 0; + }; + using MatchmakingAsyncRequestNotificationBus = AZ::EBus; + + //! MatchmakingNotifications + //! The matchmaking notifications to listen for performing required operations + class MatchAcceptanceNotifications + : public AZ::EBusTraits + { + public: + // Safeguard handler for multi-threaded use case + using MutexType = AZStd::recursive_mutex; + + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + // OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE + virtual void OnMatchAcceptance() = 0; + }; + using MatchAcceptanceNotificationBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.cpp b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.cpp new file mode 100644 index 0000000000..b6c8c55fa4 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.cpp @@ -0,0 +1,78 @@ +/* + * 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 + +namespace AzFramework +{ + void AcceptMatchRequest::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("acceptMatch", &AcceptMatchRequest::m_acceptMatch) + ->Field("playerIds", &AcceptMatchRequest::m_playerIds) + ->Field("ticketId", &AcceptMatchRequest::m_ticketId); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("AcceptMatchRequest", "The container for AcceptMatch request parameters") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_acceptMatch, "AcceptMatch", + "Player response to accept or reject match") + ->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_playerIds, "PlayerIds", + "A list of unique identifiers for players delivering the response") + ->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_ticketId, "TicketId", + "A unique identifier for a matchmaking ticket"); + } + } + } + + void StartMatchmakingRequest::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("ticketId", &StartMatchmakingRequest::m_ticketId); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("StartMatchmakingRequest", "The container for StartMatchmaking request parameters") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &StartMatchmakingRequest::m_ticketId, "TicketId", + "A unique identifier for a matchmaking ticket"); + } + } + } + + void StopMatchmakingRequest::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("ticketId", &StopMatchmakingRequest::m_ticketId); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("StopMatchmakingRequest", "The container for StopMatchmaking request parameters") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &StopMatchmakingRequest::m_ticketId, "TicketId", + "A unique identifier for a matchmaking ticket"); + } + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h new file mode 100644 index 0000000000..9169a83588 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h @@ -0,0 +1,67 @@ +/* + * 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 AZ +{ + class ReflectContext; +} + +namespace AzFramework +{ + //! AcceptMatchRequest + //! The container for AcceptMatch request parameters. + struct AcceptMatchRequest + { + AZ_RTTI(AcceptMatchRequest, "{AD289D76-CEE2-424F-847E-E62AA83B7D79}"); + static void Reflect(AZ::ReflectContext* context); + + AcceptMatchRequest() = default; + virtual ~AcceptMatchRequest() = default; + + // Player response to accept or reject match + bool m_acceptMatch; + // A list of unique identifiers for players delivering the response + AZStd::vector m_playerIds; + // A unique identifier for a matchmaking ticket + AZStd::string m_ticketId; + }; + + //! StartMatchmakingRequest + //! The container for StartMatchmaking request parameters. + struct StartMatchmakingRequest + { + AZ_RTTI(StartMatchmakingRequest, "{70B47776-E8E7-4993-BEC3-5CAEC3D48E47}"); + static void Reflect(AZ::ReflectContext* context); + + StartMatchmakingRequest() = default; + virtual ~StartMatchmakingRequest() = default; + + // A unique identifier for a matchmaking ticket + AZStd::string m_ticketId; + }; + + //! StopMatchmakingRequest + //! The container for StopMatchmaking request parameters. + struct StopMatchmakingRequest + { + AZ_RTTI(StopMatchmakingRequest, "{6132E293-65EF-4DC2-A8A0-00269697229D}"); + static void Reflect(AZ::ReflectContext* context); + + StopMatchmakingRequest() = default; + virtual ~StopMatchmakingRequest() = default; + + // A unique identifier for a matchmaking ticket + AZStd::string m_ticketId; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Physics/RigidBody.h b/Code/Framework/AzFramework/AzFramework/Physics/RigidBody.h deleted file mode 100644 index 13d29b6bb9..0000000000 --- a/Code/Framework/AzFramework/AzFramework/Physics/RigidBody.h +++ /dev/null @@ -1,234 +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 -{ - class ReflectContext; -} - -namespace Physics -{ - class ShapeConfiguration; - class World; - class Shape; - - /// Default values used for initializing RigidBodySettings. - /// These can be modified by Physics Implementation gems. // O3DE_DEPRECATED(LY-114472) - DefaultRigidBodyConfiguration values are not shared across modules. - // Use RigidBodyConfiguration default values. - struct DefaultRigidBodyConfiguration - { - static float m_mass; - static bool m_computeInertiaTensor; - static float m_linearDamping; - static float m_angularDamping; - static float m_sleepMinEnergy; - static float m_maxAngularVelocity; - }; - - enum class MassComputeFlags : AZ::u8 - { - NONE = 0, - - //! Flags indicating whether a certain mass property should be auto-computed or not. - COMPUTE_MASS = 1, - COMPUTE_INERTIA = 1 << 1, - COMPUTE_COM = 1 << 2, - - //! If set, non-simulated shapes will also be included in the mass properties calculation. - INCLUDE_ALL_SHAPES = 1 << 3, - - DEFAULT = COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS - }; - - class RigidBodyConfiguration - : public WorldBodyConfiguration - { - public: - AZ_CLASS_ALLOCATOR(RigidBodyConfiguration, AZ::SystemAllocator, 0); - AZ_RTTI(RigidBodyConfiguration, "{ACFA8900-8530-4744-AF00-AA533C868A8E}", WorldBodyConfiguration); - static void Reflect(AZ::ReflectContext* context); - - enum PropertyVisibility : AZ::u16 - { - InitialVelocities = 1 << 0, ///< Whether the initial linear and angular velocities are visible. - InertiaProperties = 1 << 1, ///< Whether the whole category of inertia properties (mass, compute inertia, - ///< inertia tensor etc) is visible. - Damping = 1 << 2, ///< Whether linear and angular damping are visible. - SleepOptions = 1 << 3, ///< Whether the sleep threshold and start asleep options are visible. - Interpolation = 1 << 4, ///< Whether the interpolation option is visible. - Gravity = 1 << 5, ///< Whether the effected by gravity option is visible. - Kinematic = 1 << 6, ///< Whether the option to make the body kinematic is visible. - ContinuousCollisionDetection = 1 << 7, ///< Whether the option to enable continuous collision detection is visible. - MaxVelocities = 1 << 8 ///< Whether upper limits on velocities are visible. - }; - - RigidBodyConfiguration() = default; - RigidBodyConfiguration(const RigidBodyConfiguration& settings) = default; - - // Visibility functions. - AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const; - void SetPropertyVisibility(PropertyVisibility property, bool isVisible); - - AZ::Crc32 GetInitialVelocitiesVisibility() const; - /// Returns whether the whole category of inertia settings (mass, inertia, center of mass offset etc) is visible. - AZ::Crc32 GetInertiaSettingsVisibility() const; - /// Returns whether the individual inertia tensor field is visible or is hidden because the compute inertia option is selected. - AZ::Crc32 GetInertiaVisibility() const; - /// Returns whether the mass field is visible or is hidden because compute mass option is selected. - AZ::Crc32 GetMassVisibility() const; - /// Returns whether the individual centre of mass offset field is visible or is hidden because compute CoM option is selected. - AZ::Crc32 GetCoMVisibility() const; - AZ::Crc32 GetDampingVisibility() const; - AZ::Crc32 GetSleepOptionsVisibility() const; - AZ::Crc32 GetInterpolationVisibility() const; - AZ::Crc32 GetGravityVisibility() const; - AZ::Crc32 GetKinematicVisibility() const; - AZ::Crc32 GetCCDVisibility() const; - AZ::Crc32 GetMaxVelocitiesVisibility() const; - MassComputeFlags GetMassComputeFlags() const; - void SetMassComputeFlags(MassComputeFlags flags); - - bool IsCCDEnabled() const; - - // Basic initial settings. - AZ::Vector3 m_initialLinearVelocity = AZ::Vector3::CreateZero(); - AZ::Vector3 m_initialAngularVelocity = AZ::Vector3::CreateZero(); - AZ::Vector3 m_centerOfMassOffset = AZ::Vector3::CreateZero(); - - // Simulation parameters. - float m_mass = DefaultRigidBodyConfiguration::m_mass; - AZ::Matrix3x3 m_inertiaTensor = AZ::Matrix3x3::CreateIdentity(); - float m_linearDamping = DefaultRigidBodyConfiguration::m_linearDamping; - float m_angularDamping = DefaultRigidBodyConfiguration::m_angularDamping; - float m_sleepMinEnergy = DefaultRigidBodyConfiguration::m_sleepMinEnergy; - float m_maxAngularVelocity = DefaultRigidBodyConfiguration::m_maxAngularVelocity; - - // Visibility settings. - AZ::u16 m_propertyVisibilityFlags = (std::numeric_limits::max)(); - - bool m_startAsleep = false; - bool m_interpolateMotion = false; - bool m_gravityEnabled = true; - bool m_simulated = true; - bool m_kinematic = false; - bool m_ccdEnabled = false; ///< Whether continuous collision detection is enabled. - float m_ccdMinAdvanceCoefficient = 0.15f; ///< Coefficient affecting how granularly time is subdivided in CCD. - bool m_ccdFrictionEnabled = false; ///< Whether friction is applied when resolving CCD collisions. - - bool m_computeCenterOfMass = true; - bool m_computeInertiaTensor = true; - bool m_computeMass = true; - - //! If set, non-simulated shapes will also be included in the mass properties calculation. - bool m_includeAllShapesInMassCalculation = false; - }; - - /// Dynamic rigid body. - class RigidBody - : public WorldBody - { - public: - - AZ_CLASS_ALLOCATOR(RigidBody, AZ::SystemAllocator, 0); - AZ_RTTI(RigidBody, "{156E459F-7BB7-4B4E-ADA0-2130D96B7E80}", WorldBody); - - public: - RigidBody() = default; - explicit RigidBody(const RigidBodyConfiguration& settings); - - - virtual void AddShape(AZStd::shared_ptr shape) = 0; - virtual void RemoveShape(AZStd::shared_ptr shape) = 0; - virtual AZ::u32 GetShapeCount() { return 0; } - virtual AZStd::shared_ptr GetShape(AZ::u32 /*index*/) { return nullptr; } - - virtual AZ::Vector3 GetCenterOfMassWorld() const = 0; - virtual AZ::Vector3 GetCenterOfMassLocal() const = 0; - - virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0; - virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0; - - virtual float GetMass() const = 0; - virtual float GetInverseMass() const = 0; - virtual void SetMass(float mass) = 0; - virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0; - - /// Retrieves the velocity at center of mass; only linear velocity, no rotational velocity contribution. - virtual AZ::Vector3 GetLinearVelocity() const = 0; - virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0; - virtual AZ::Vector3 GetAngularVelocity() const = 0; - virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0; - virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0; - virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0; - virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0; - virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0; - - virtual float GetLinearDamping() const = 0; - virtual void SetLinearDamping(float damping) = 0; - virtual float GetAngularDamping() const = 0; - virtual void SetAngularDamping(float damping) = 0; - - virtual bool IsAwake() const = 0; - virtual void ForceAsleep() = 0; - virtual void ForceAwake() = 0; - virtual float GetSleepThreshold() const = 0; - virtual void SetSleepThreshold(float threshold) = 0; - - virtual bool IsKinematic() const = 0; - virtual void SetKinematic(bool kinematic) = 0; - virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0; - - virtual bool IsGravityEnabled() const = 0; - virtual void SetGravityEnabled(bool enabled) = 0; - virtual void SetSimulationEnabled(bool enabled) = 0; - virtual void SetCCDEnabled(bool enabled) = 0; - - //! Recalculates mass, inertia and center of mass based on the flags passed. - //! @param flags MassComputeFlags specifying which properties should be recomputed. - //! @param centerOfMassOffsetOverride Optional override of the center of mass. Note: This parameter will be ignored if COMPUTE_COM is passed in flags. - //! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags. - //! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags. - virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT, - const AZ::Vector3* centerOfMassOffsetOverride = nullptr, - const AZ::Matrix3x3* inertiaTensorOverride = nullptr, - const float* massOverride = nullptr) = 0; - }; - - /// Bitwise operators for MassComputeFlags - inline MassComputeFlags operator|(MassComputeFlags lhs, MassComputeFlags rhs) - { - return aznumeric_cast(aznumeric_cast(lhs) | aznumeric_cast(rhs)); - } - - inline MassComputeFlags operator&(MassComputeFlags lhs, MassComputeFlags rhs) - { - return aznumeric_cast(aznumeric_cast(lhs) & aznumeric_cast(rhs)); - } - - /// Static rigid body. - class RigidBodyStatic - : public WorldBody - { - public: - AZ_CLASS_ALLOCATOR(RigidBodyStatic, AZ::SystemAllocator, 0); - AZ_RTTI(RigidBodyStatic, "{13A677BB-7085-4EDB-BCC8-306548238692}", WorldBody); - - virtual void AddShape(const AZStd::shared_ptr& shape) = 0; - virtual AZ::u32 GetShapeCount() { return 0; } - virtual AZStd::shared_ptr GetShape(AZ::u32 /*index*/) { return nullptr; } - }; -} // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h b/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h index 3c0a1afa1d..e9bf8a7307 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h @@ -89,9 +89,9 @@ namespace AzPhysics //! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags. //! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags. virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT, - const AZ::Vector3* centerOfMassOffsetOverride = nullptr, - const AZ::Matrix3x3* inertiaTensorOverride = nullptr, - const float* massOverride = nullptr) = 0; + const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(), + const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(), + const float massOverride = 1.0f) = 0; }; } // namespace AzPhysics diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h index 0323686442..bdc3fe1444 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h @@ -10,98 +10,11 @@ #include #include -#include #include -#include +#include namespace AzFramework { - struct SessionConfig; - - //! CreateSessionRequest - //! The container for CreateSession request parameters. - struct CreateSessionRequest - { - AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}"); - static void Reflect(AZ::ReflectContext* context); - - CreateSessionRequest() = default; - virtual ~CreateSessionRequest() = default; - - // A unique identifier for a player or entity creating the session. - AZStd::string m_creatorId; - - // A collection of custom properties for a session. - AZStd::unordered_map m_sessionProperties; - - // A descriptive label that is associated with a session. - AZStd::string m_sessionName; - - // The maximum number of players that can be connected simultaneously to the session. - uint64_t m_maxPlayer = 0; - }; - - //! SearchSessionsRequest - //! The container for SearchSessions request parameters. - struct SearchSessionsRequest - { - AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}"); - static void Reflect(AZ::ReflectContext* context); - - SearchSessionsRequest() = default; - virtual ~SearchSessionsRequest() = default; - - // String containing the search criteria for the session search. If no filter expression is included, the request returns results - // for all active sessions. - AZStd::string m_filterExpression; - - // Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order. - AZStd::string m_sortExpression; - - // The maximum number of results to return. - uint8_t m_maxResult = 0; - - // A token that indicates the start of the next sequential page of results. - AZStd::string m_nextToken; - }; - - //! SearchSessionsResponse - //! The container for SearchSession request results. - struct SearchSessionsResponse - { - AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}"); - static void Reflect(AZ::ReflectContext* context); - - SearchSessionsResponse() = default; - virtual ~SearchSessionsResponse() = default; - - // A collection of sessions that match the search criteria and sorted in specific order. - AZStd::vector m_sessionConfigs; - - // A token that indicates the start of the next sequential page of results. - AZStd::string m_nextToken; - }; - - //! JoinSessionRequest - //! The container for JoinSession request parameters. - struct JoinSessionRequest - { - AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}"); - static void Reflect(AZ::ReflectContext* context); - - JoinSessionRequest() = default; - virtual ~JoinSessionRequest() = default; - - // A unique identifier for the session. - AZStd::string m_sessionId; - - // A unique identifier for a player. Player IDs are developer-defined. - AZStd::string m_playerId; - - // Developer-defined information related to a player. - AZStd::string m_playerData; - }; - //! ISessionRequests //! Pure virtual session interface class to abstract the details of session handling from application code. class ISessionRequests 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/Session/ISessionRequests.cpp b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.cpp similarity index 98% rename from Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp rename to Code/Framework/AzFramework/AzFramework/Session/SessionRequests.cpp index 5536d9a47d..a7ffaa2491 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.cpp @@ -6,9 +6,10 @@ * */ +#include #include #include -#include +#include #include namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.h new file mode 100644 index 0000000000..1ae018e1ef --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.h @@ -0,0 +1,107 @@ +/* + * 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 AZ +{ + class ReflectContext; +} + +namespace AzFramework +{ + struct SessionConfig; + + //! CreateSessionRequest + //! The container for CreateSession request parameters. + struct CreateSessionRequest + { + AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}"); + static void Reflect(AZ::ReflectContext* context); + + CreateSessionRequest() = default; + virtual ~CreateSessionRequest() = default; + + // A unique identifier for a player or entity creating the session. + AZStd::string m_creatorId; + + // A collection of custom properties for a session. + AZStd::unordered_map m_sessionProperties; + + // A descriptive label that is associated with a session. + AZStd::string m_sessionName; + + // The maximum number of players that can be connected simultaneously to the session. + uint64_t m_maxPlayer = 0; + }; + + //! SearchSessionsRequest + //! The container for SearchSessions request parameters. + struct SearchSessionsRequest + { + AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}"); + static void Reflect(AZ::ReflectContext* context); + + SearchSessionsRequest() = default; + virtual ~SearchSessionsRequest() = default; + + // String containing the search criteria for the session search. If no filter expression is included, the request returns results + // for all active sessions. + AZStd::string m_filterExpression; + + // Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order. + AZStd::string m_sortExpression; + + // The maximum number of results to return. + uint8_t m_maxResult = 0; + + // A token that indicates the start of the next sequential page of results. + AZStd::string m_nextToken; + }; + + //! SearchSessionsResponse + //! The container for SearchSession request results. + struct SearchSessionsResponse + { + AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}"); + static void Reflect(AZ::ReflectContext* context); + + SearchSessionsResponse() = default; + virtual ~SearchSessionsResponse() = default; + + // A collection of sessions that match the search criteria and sorted in specific order. + AZStd::vector m_sessionConfigs; + + // A token that indicates the start of the next sequential page of results. + AZStd::string m_nextToken; + }; + + //! JoinSessionRequest + //! The container for JoinSession request parameters. + struct JoinSessionRequest + { + AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}"); + static void Reflect(AZ::ReflectContext* context); + + JoinSessionRequest() = default; + virtual ~JoinSessionRequest() = default; + + // A unique identifier for the session. + AZStd::string m_sessionId; + + // A unique identifier for a player. Player IDs are developer-defined. + AZStd::string m_playerId; + + // Developer-defined information related to a player. + AZStd::string m_playerData; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h index bb1f137ba2..72a3031e3e 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h @@ -61,6 +61,10 @@ namespace AzFramework //! be deleted and the spawnable asset to be released. This call is automatically done when //! AssignRootSpawnable is called while a root spawnable is assigned. virtual void ReleaseRootSpawnable() = 0; + //! Force processing all SpawnableEntitiesManager requests immediately + //! This is useful when loading a different level while SpawnableEntitiesManager still has + //! pending requests + virtual void ProcessSpawnableQueue() = 0; }; using RootSpawnableInterface = AZ::Interface; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index f6130c9e31..957786c6df 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -45,8 +45,7 @@ namespace AzFramework void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { - m_entitiesManager.ProcessQueue( - SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); + ProcessSpawnableQueue(); RootSpawnableNotificationBus::ExecuteQueuedEvents(); } @@ -121,6 +120,12 @@ namespace AzFramework m_rootSpawnableId = AZ::Data::AssetId(); } + void SpawnableSystemComponent::ProcessSpawnableQueue() + { + m_entitiesManager.ProcessQueue( + SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + void SpawnableSystemComponent::OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) { @@ -161,6 +166,8 @@ namespace AzFramework void SpawnableSystemComponent::Deactivate() { + ProcessSpawnableQueue(); + m_registryChangeHandler.Disconnect(); AZ::TickBus::Handler::BusDisconnect(); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index 5b5fb1b7ee..74e255d624 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -75,6 +75,7 @@ namespace AzFramework uint64_t AssignRootSpawnable(AZ::Data::Asset rootSpawnable) override; void ReleaseRootSpawnable() override; + void ProcessSpawnableQueue() override; // // RootSpawnbleNotificationBus diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index a3eb4d8329..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,36 +619,45 @@ 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; - const auto pivotDirection = targetCamera.m_offset.GetNormalized(); - nextCamera.m_offset -= pivotDirection * delta; - const auto pivotDot = targetCamera.m_offset.Dot(nextCamera.m_offset); - const auto distance = nextCamera.m_offset.GetLength() * AZ::GetSign(pivotDot); + // handle case where pivot and offset may be the same to begin with + // choose negative y-axis for offset to default to moving the camera backwards from the pivot (standard centered pivot behavior) + const auto pivotDirection = [&targetCamera] + { + if (const auto offsetLength = targetCamera.m_offset.GetLength(); AZ::IsCloseMag(offsetLength, 0.0f)) + { + return -AZ::Vector3::CreateAxisY(); + } + else + { + return targetCamera.m_offset / offsetLength; + } + }(); - const auto minDistance = 0.01f; - if (distance < minDistance || pivotDot < 0.0f) + nextCamera.m_offset -= pivotDirection * delta; + if (pivotDirection.Dot(nextCamera.m_offset) < 0.0f) { - nextCamera.m_offset = pivotDirection * minDistance; + nextCamera.m_offset = pivotDirection * 0.001f; } 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 @@ -657,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 { @@ -686,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)) @@ -697,7 +706,7 @@ namespace AzFramework return !Idle(); } - Camera ScrollTranslationCameraInput::StepCamera( + Camera LookScrollTranslationCameraInput::StepCamera( const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta, @@ -771,6 +780,73 @@ namespace AzFramework return camera; } + FocusCameraInput::FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn) + : m_focusChannelId(focusChannelId) + , m_offsetFn(offsetFn) + { + } + + bool FocusCameraInput::HandleEvents( + const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) + { + if (const auto* input = AZStd::get_if(&event)) + { + if (input->m_channelId == m_focusChannelId && input->m_state == InputChannel::State::Began) + { + BeginActivation(); + } + } + + return !Idle(); + } + + Camera FocusCameraInput::StepCamera( + const Camera& targetCamera, + [[maybe_unused]] const ScreenVector& cursorDelta, + [[maybe_unused]] float scrollDelta, + [[maybe_unused]] float deltaTime) + { + if (Beginning()) + { + // as the camera starts, record the camera we would like to end up as + m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation())); + const auto angles = + EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn()))); + m_nextCamera.m_pitch = angles.GetX(); + m_nextCamera.m_yaw = angles.GetZ(); + m_nextCamera.m_pivot = targetCamera.m_pivot; + } + + // end the behavior when the camera is in alignment + if (AZ::IsCloseMag(targetCamera.m_pitch, m_nextCamera.m_pitch) && AZ::IsCloseMag(targetCamera.m_yaw, m_nextCamera.m_yaw)) + { + EndActivation(); + } + + return m_nextCamera; + } + + void FocusCameraInput::SetPivotFn(PivotFn pivotFn) + { + m_pivotFn = AZStd::move(pivotFn); + } + + void FocusCameraInput::SetFocusInputChannelId(const InputChannelId& focusChannelId) + { + m_focusChannelId = focusChannelId; + } + + bool CustomCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta) + { + return m_handleEventsFn(*this, event, cursorDelta, scrollDelta); + } + + Camera CustomCameraInput::StepCamera( + const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime) + { + return m_stepCameraFn(*this, targetCamera, cursorDelta, scrollDelta, deltaTime); + } + InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize) { const auto& inputChannelId = inputChannel.GetInputChannelId(); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index c44d951292..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,40 +596,96 @@ 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; } + //! Callback to use for FocusCameraInput when a free look camera is being used. + //! @note This is when offset is zero. + inline AZ::Vector3 FocusLook(float) + { + return AZ::Vector3::CreateZero(); + } + + //! Callback to use for FocusCameraInput when a orbit camera is being used. + //! @note This is when offset is non zero. + inline AZ::Vector3 FocusOrbit(const float length) + { + return AZ::Vector3::CreateAxisY(-length); + } + + using FocusOffsetFn = AZStd::function; + + //! A focus behavior to align the camera view to the position returned by the pivot function. + //! @note This only alters the camera orientation, the translation is unaffected. + class FocusCameraInput : public CameraInput + { + public: + using PivotFn = AZStd::function; + + FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn); + + // 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; + + //! Override the default behavior for how a pivot point is calculated. + void SetPivotFn(PivotFn pivotFn); + + void SetFocusInputChannelId(const InputChannelId& focusChannelId); + + private: + InputChannelId m_focusChannelId; //!< Input channel to begin the focus camera input. + Camera m_nextCamera; + PivotFn m_pivotFn; + FocusOffsetFn m_offsetFn; + }; + + //! Provides a CameraInput type that can be implemented without needing to create a new type deriving from CameraInput. + //! This can be very useful for specific use cases that are less generally applicable. + class CustomCameraInput : public CameraInput + { + public: + // 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; + + //! HandleEvents delegates directly to m_handleEventsFn. + AZStd::function m_handleEventsFn; + //! StepCamera delegates directly to m_stepCameraFn. + AZStd::function m_stepCameraFn; + }; + //! Map from a generic InputChannel event to a camera specific InputEvent. InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize); } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index e752e5ae04..c5616d84c0 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -167,6 +167,10 @@ set(FILES Logging/MissingAssetLogger.cpp Logging/MissingAssetLogger.h Logging/MissingAssetNotificationBus.h + Matchmaking/IMatchmakingRequests.h + Matchmaking/MatchmakingRequests.cpp + Matchmaking/MatchmakingRequests.h + Matchmaking/MatchmakingNotifications.h Scene/Scene.h Scene/Scene.inl Scene/Scene.cpp @@ -181,8 +185,9 @@ set(FILES Script/ScriptRemoteDebugging.cpp Script/ScriptRemoteDebugging.h Session/ISessionHandlingRequests.h - Session/ISessionRequests.cpp Session/ISessionRequests.h + Session/SessionRequests.cpp + Session/SessionRequests.h Session/SessionConfig.cpp Session/SessionConfig.h Session/SessionNotifications.h diff --git a/Code/Framework/AzFramework/CMakeLists.txt b/Code/Framework/AzFramework/CMakeLists.txt index 59393d90a1..b22586162b 100644 --- a/Code/Framework/AzFramework/CMakeLists.txt +++ b/Code/Framework/AzFramework/CMakeLists.txt @@ -29,7 +29,6 @@ ly_add_target( AZ::AzCore PUBLIC AZ::GridMate - 3rdParty::zlib 3rdParty::zstd 3rdParty::lz4 ) diff --git a/Code/Framework/AzFramework/Platform/Android/AzFramework/IO/LocalFileIO_Android.cpp b/Code/Framework/AzFramework/Platform/Android/AzFramework/IO/LocalFileIO_Android.cpp index b454755cd4..bd5ba39d76 100644 --- a/Code/Framework/AzFramework/Platform/Android/AzFramework/IO/LocalFileIO_Android.cpp +++ b/Code/Framework/AzFramework/Platform/Android/AzFramework/IO/LocalFileIO_Android.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -42,10 +41,10 @@ namespace AZ { Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath) { - char resolvedSourcePath[AZ_MAX_PATH_LEN]; - char resolvedDestPath[AZ_MAX_PATH_LEN]; - ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN); - ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN); + char resolvedSourcePath[AZ::IO::MaxPathLength]; + char resolvedDestPath[AZ::IO::MaxPathLength]; + ResolvePath(sourceFilePath, resolvedSourcePath, AZ::IO::MaxPathLength); + ResolvePath(destinationFilePath, resolvedDestPath, AZ::IO::MaxPathLength); if (AZ::Android::Utils::IsApkPath(sourceFilePath) || AZ::Android::Utils::IsApkPath(destinationFilePath)) { @@ -77,18 +76,17 @@ namespace AZ { ANDROID_IO_PROFILE_SECTION_ARGS("FindFiles:%s", filePath); - char resolvedPath[AZ_MAX_PATH_LEN]; - ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN); + char resolvedPath[AZ::IO::MaxPathLength]; + ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength); AZStd::string pathWithoutSlash = RemoveTrailingSlash(resolvedPath); bool isInAPK = AZ::Android::Utils::IsApkPath(pathWithoutSlash.c_str()); + AZ::IO::FixedMaxPath tempBuffer; if (isInAPK) { AZ::IO::FixedMaxPath strippedPath = AZ::Android::Utils::StripApkPrefix(pathWithoutSlash.c_str()); - char tempBuffer[AZ_MAX_PATH_LEN] = {0}; - AZ::Android::APKFileHandler::ParseDirectory(strippedPath.c_str(), [&](const char* name) { AZStd::string_view filenameView = name; @@ -98,10 +96,9 @@ namespace AZ AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath); foundFilePath += name; // if aliased, de-alias! - azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str()); - ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN); + ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath }); - if (!callback(tempBuffer)) + if (!callback(tempBuffer.c_str())) { return false; } @@ -115,10 +112,6 @@ namespace AZ if (dir != nullptr) { - // because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to - // use a static buffer here. - char tempBuffer[AZ_MAX_PATH_LEN]; - // clear the errno state so we can distinguish between errors and end of stream errno = 0; struct dirent* entry = readdir(dir); @@ -133,10 +126,9 @@ namespace AZ AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath); foundFilePath += entry->d_name; // if aliased, de-alias! - azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str()); - ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN); + ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath }); - if (!callback(tempBuffer)) + if (!callback(tempBuffer.c_str())) { break; } @@ -163,8 +155,8 @@ namespace AZ Result LocalFileIO::CreatePath(const char* filePath) { - char resolvedPath[AZ_MAX_PATH_LEN]; - ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN); + char resolvedPath[AZ::IO::MaxPathLength]; + ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength); if (AZ::Android::Utils::IsApkPath(resolvedPath)) { @@ -201,33 +193,5 @@ namespace AZ mkdir(pathBuffer.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error; } - - bool LocalFileIO::IsAbsolutePath(const char* path) const - { - return path && path[0] == '/'; - } - - bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const - { - if (AZ::Android::Utils::IsApkPath(path)) - { - azstrncpy(absolutePath, maxLength, path, maxLength); - return true; - } - AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN"); - if (!IsAbsolutePath(path)) - { - // note that realpath fails if the path does not exist and actually changes the return value - // to be the actual place that FAILED, which we don't want. - // if we fail, we'd prefer to fall through and at least use the original path. - const char* result = realpath(path, absolutePath); - if (result) - { - return true; - } - } - azstrcpy(absolutePath, maxLength, path); - return IsAbsolutePath(absolutePath); - } } // namespace IO }//namespace AZ diff --git a/Code/Framework/AzFramework/Platform/Common/UnixLike/AzFramework/IO/LocalFileIO_UnixLike.cpp b/Code/Framework/AzFramework/Platform/Common/UnixLike/AzFramework/IO/LocalFileIO_UnixLike.cpp index 844464681a..cbcf4a3f56 100644 --- a/Code/Framework/AzFramework/Platform/Common/UnixLike/AzFramework/IO/LocalFileIO_UnixLike.cpp +++ b/Code/Framework/AzFramework/Platform/Common/UnixLike/AzFramework/IO/LocalFileIO_UnixLike.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include namespace AZ @@ -19,11 +19,11 @@ namespace AZ { Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath) { - char resolvedSourceFilePath[AZ_MAX_PATH_LEN] = {0}; - ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ_MAX_PATH_LEN); + char resolvedSourceFilePath[AZ::IO::MaxPathLength] = {0}; + ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ::IO::MaxPathLength); - char resolvedDestinationFilePath[AZ_MAX_PATH_LEN] = {0}; - ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ_MAX_PATH_LEN); + char resolvedDestinationFilePath[AZ::IO::MaxPathLength] = {0}; + ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ::IO::MaxPathLength); // Use standard C++ method of file copy. { @@ -45,17 +45,15 @@ namespace AZ Result LocalFileIO::FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) { - char resolvedPath[AZ_MAX_PATH_LEN] = {0}; - ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN); + char resolvedPath[AZ::IO::MaxPathLength] = {0}; + ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength); AZStd::string withoutSlash = RemoveTrailingSlash(resolvedPath); DIR* dir = opendir(withoutSlash.c_str()); if (dir != nullptr) { - // because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to - // use a static buffer here. - char tempBuffer[AZ_MAX_PATH_LEN]; + AZ::IO::FixedMaxPath tempBuffer; errno = 0; struct dirent* entry = readdir(dir); @@ -70,10 +68,9 @@ namespace AZ AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath); foundFilePath += entry->d_name; // if aliased, dealias! - azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str()); - ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN); + ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath }); - if (!callback(tempBuffer)) + if (!callback(tempBuffer.c_str())) { break; } @@ -92,8 +89,8 @@ namespace AZ Result LocalFileIO::CreatePath(const char* filePath) { - char resolvedPath[AZ_MAX_PATH_LEN] = {0}; - ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN); + char resolvedPath[AZ::IO::MaxPathLength] = {0}; + ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength); // create all paths up to that directory. // its not an error if the path exists. @@ -125,28 +122,5 @@ namespace AZ mkdir(buf.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error; } - - bool LocalFileIO::IsAbsolutePath(const char* path) const - { - return path && path[0] == '/'; - } - - bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const - { - AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN"); - if (!IsAbsolutePath(path)) - { - // note that realpath fails if the path does not exist and actually changes the return value - // to be the actual place that FAILED, which we don't want. - // if we fail, we'd prefer to fall through and at least use the original path. - const char* result = realpath(path, absolutePath); - if (result) - { - return true; - } - } - azstrcpy(absolutePath, maxLength, path); - return IsAbsolutePath(absolutePath); - } } // namespace IO } // namespace AZ diff --git a/Code/Framework/AzFramework/Platform/Common/WinAPI/AzFramework/IO/LocalFileIO_WinAPI.cpp b/Code/Framework/AzFramework/Platform/Common/WinAPI/AzFramework/IO/LocalFileIO_WinAPI.cpp index 64787d4951..7ff8a96c09 100644 --- a/Code/Framework/AzFramework/Platform/Common/WinAPI/AzFramework/IO/LocalFileIO_WinAPI.cpp +++ b/Code/Framework/AzFramework/Platform/Common/WinAPI/AzFramework/IO/LocalFileIO_WinAPI.cpp @@ -47,7 +47,7 @@ namespace AZ if (hFind != INVALID_HANDLE_VALUE) { - // because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to + // because the absolute path might actually be SHORTER than the alias ("D:/o3de" -> "@engroot@"), we need to // use a static buffer here. char tempBuffer[AZ_MAX_PATH_LEN]; do @@ -133,36 +133,5 @@ namespace AZ return SystemFile::CreateDir(buf.c_str()) ? ResultCode::Success : ResultCode::Error; } - - bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const - { - char* result = _fullpath(absolutePath, path, maxLength); - size_t len = ::strlen(absolutePath); - if (len > 0) - { - // strip trailing slash - if (absolutePath[len - 1] == '/' || absolutePath[len - 1] == '\\') - { - absolutePath[len - 1] = 0; - } - - // For some reason, at least on windows, _fullpath returns a lowercase drive letter even though other systems like Qt, use upper case. - if (len > 2) - { - if (absolutePath[1] == ':') - { - absolutePath[0] = (char)toupper(absolutePath[0]); - } - } - } - return result != nullptr; - } - - bool LocalFileIO::IsAbsolutePath(const char* path) const - { - char drive[16] = { 0 }; - _splitpath_s(path, drive, 16, nullptr, 0, nullptr, 0, nullptr, 0); - return strlen(drive) > 0; - } } // namespace IO }//namespace AZ diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h index c6307755a7..251342093a 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h @@ -23,10 +23,12 @@ namespace AzFramework virtual ~XcbEventHandler() = default; virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0; + + // ATTN This is used as a workaround for RAW Input events when using the Editor. + virtual void PollSpecialEvents(){}; }; - class XcbEventHandlerBusTraits - : public AZ::EBusTraits + class XcbEventHandlerBusTraits : public AZ::EBusTraits { public: ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp index c971c3b793..de326df200 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp @@ -20,6 +20,15 @@ namespace AzFramework { + // xcb-xkb does not provide a generic event type, so we define our own. + // These fields are enough to get to the xkbType field, which can then be + // read to typecast the event to the right concrete type. + struct XcbXkbGenericEventT + { + uint8_t response_type; + uint8_t xkbType; + }; + XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice) : InputDeviceKeyboard::Implementation(inputDevice) { @@ -39,19 +48,22 @@ namespace AzFramework return; } - XcbStdFreePtr xkbUseExtensionReply{ - xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr) - }; - if (!xkbUseExtensionReply) + int initializeXkbExtensionSuccess = xkb_x11_setup_xkb_extension( + connection, + 1, + 0, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + nullptr, + nullptr, + &m_xkbEventCode, + nullptr + ); + + if (!initializeXkbExtensionSuccess) { AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension"); return; } - if (!xkbUseExtensionReply->supported) - { - AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension"); - return; - } m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection); @@ -59,6 +71,43 @@ namespace AzFramework m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)); m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId)); + const uint16_t affectMap = + XCB_XKB_MAP_PART_KEY_TYPES + | XCB_XKB_MAP_PART_KEY_SYMS + | XCB_XKB_MAP_PART_MODIFIER_MAP + | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS + | XCB_XKB_MAP_PART_KEY_ACTIONS + | XCB_XKB_MAP_PART_KEY_BEHAVIORS + | XCB_XKB_MAP_PART_VIRTUAL_MODS + | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP + ; + + const uint16_t selectedEvents = + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY + | XCB_XKB_EVENT_TYPE_MAP_NOTIFY + | XCB_XKB_EVENT_TYPE_STATE_NOTIFY + ; + + XcbStdFreePtr error{xcb_request_check( + connection, + xcb_xkb_select_events( + connection, + /* deviceSpec = */ XCB_XKB_ID_USE_CORE_KBD, + /* affectWhich = */ selectedEvents, + /* clear = */ 0, + /* selectAll = */ selectedEvents, + /* affectMap = */ affectMap, + /* map = */ affectMap, + /* details = */ nullptr + ) + )}; + + if (error) + { + AZ_Warning("ApplicationLinux", false, "failed to select notify events from XKB"); + return; + } + m_initialized = true; } @@ -70,15 +119,17 @@ namespace AzFramework bool XcbInputDeviceKeyboard::HasTextEntryStarted() const { - return false; + return m_hasTextEntryStarted; } void XcbInputDeviceKeyboard::TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) { + m_hasTextEntryStarted = true; } void XcbInputDeviceKeyboard::TextEntryStop() { + m_hasTextEntryStarted = false; } void XcbInputDeviceKeyboard::TickInputDevice() @@ -93,30 +144,45 @@ namespace AzFramework return; } - switch (event->response_type & ~0x80) - { - case XCB_KEY_PRESS: + const auto responseType = event->response_type & ~0x80; + if (responseType == XCB_KEY_PRESS) { - auto* keyPress = reinterpret_cast(event); + const auto* keyPress = reinterpret_cast(event); + { + auto text = TextFromKeycode(m_xkbState.get(), keyPress->detail); + if (!text.empty()) + { + QueueRawTextEvent(AZStd::move(text)); + } + } - const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail); - if (key) + if (const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail)) { QueueRawKeyEvent(*key, true); } - break; } - case XCB_KEY_RELEASE: + else if (responseType == XCB_KEY_RELEASE) { - auto* keyRelease = reinterpret_cast(event); + const auto* keyRelease = reinterpret_cast(event); const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail); if (key) { QueueRawKeyEvent(*key, false); } - break; } + else if (responseType == m_xkbEventCode) + { + const auto* xkbEvent = reinterpret_cast(event); + switch (xkbEvent->xkbType) + { + case XCB_XKB_STATE_NOTIFY: + { + const auto* stateNotifyEvent = reinterpret_cast(event); + UpdateState(stateNotifyEvent); + break; + } + } } } @@ -268,4 +334,34 @@ namespace AzFramework default: return nullptr; } } + + AZStd::string XcbInputDeviceKeyboard::TextFromKeycode(xkb_state* state, xkb_keycode_t code) + { + // Find out how much of a buffer we need + const size_t size = xkb_state_key_get_utf8(state, code, nullptr, 0); + if (!size) + { + return {}; + } + // xkb_state_key_get_utf8 will null-terminate the resulting string, and + // will truncate the result to `size - 1` if there is not enough space + // for the null byte. The first call returns the size of the resulting + // string without including the null byte. AZStd::string internally + // includes space for the null byte, but that is not included in its + // `size()`. xkb_state_key_get_utf8 will always set `buf[size - 1] = + // 0`, so add 1 to `chars.size()` to include that internal null byte in + // the string. + AZStd::string chars; + chars.resize_no_construct(size); + xkb_state_key_get_utf8(state, code, chars.data(), chars.size() + 1); + return chars; + } + + void XcbInputDeviceKeyboard::UpdateState(const xcb_xkb_state_notify_event_t* state) + { + if (m_initialized) + { + xkb_state_update_mask(m_xkbState.get(), state->baseMods, state->latchedMods, state->lockedMods, state->baseGroup, state->latchedGroup, state->lockedGroup); + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h index de65941a67..00383abf6c 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h @@ -13,6 +13,8 @@ #include #include +struct xcb_xkb_state_notify_event_t; + namespace AzFramework { class XcbInputDeviceKeyboard @@ -37,10 +39,16 @@ namespace AzFramework private: [[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const; + static AZStd::string TextFromKeycode(xkb_state* state, xkb_keycode_t code); + + void UpdateState(const xcb_xkb_state_notify_event_t* state); + XcbUniquePtr m_xkbContext; XcbUniquePtr m_xkbKeymap; XcbUniquePtr m_xkbState; int m_coreDeviceId{-1}; + uint8_t m_xkbEventCode{0}; bool m_initialized{false}; + bool m_hasTextEntryStarted{false}; }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp new file mode 100644 index 0000000000..56f21e6533 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp @@ -0,0 +1,652 @@ +/* + * 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 + +namespace AzFramework +{ + xcb_window_t GetSystemCursorFocusWindow() + { + void* systemCursorFocusWindow = nullptr; + AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult( + systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow); + + if (!systemCursorFocusWindow) + { + return XCB_NONE; + } + + // TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit + // machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit. + + return static_cast(reinterpret_cast(systemCursorFocusWindow)); + } + + xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr; + xcb_screen_t* XcbInputDeviceMouse::s_xcbScreen = nullptr; + bool XcbInputDeviceMouse::m_xfixesInitialized = false; + bool XcbInputDeviceMouse::m_xInputInitialized = false; + + XcbInputDeviceMouse::XcbInputDeviceMouse(InputDeviceMouse& inputDevice) + : InputDeviceMouse::Implementation(inputDevice) + , m_systemCursorState(SystemCursorState::Unknown) + , m_systemCursorPositionNormalized(0.5f, 0.5f) + , m_prevConstraintWindow(XCB_NONE) + , m_focusWindow(XCB_NONE) + , m_cursorShown(true) + { + XcbEventHandlerBus::Handler::BusConnect(); + + SetSystemCursorState(SystemCursorState::Unknown); + } + + XcbInputDeviceMouse::~XcbInputDeviceMouse() + { + XcbEventHandlerBus::Handler::BusDisconnect(); + + SetSystemCursorState(SystemCursorState::Unknown); + } + + InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice) + { + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + if (!interface) + { + AZ_Warning("XcbInput", false, "XCB interface not available"); + return nullptr; + } + + s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection(); + if (!s_xcbConnection) + { + AZ_Warning("XcbInput", false, "XCB connection not available"); + return nullptr; + } + + const xcb_setup_t* xcbSetup = xcb_get_setup(s_xcbConnection); + s_xcbScreen = xcb_setup_roots_iterator(xcbSetup).data; + if (!s_xcbScreen) + { + AZ_Warning("XcbInput", false, "XCB screen not available"); + return nullptr; + } + + // Initialize XFixes extension which we use to create pointer barriers. + if (!InitializeXFixes()) + { + AZ_Warning("XcbInput", false, "XCB XFixes initialization failed"); + return nullptr; + } + + // Initialize XInput extension which is used to get RAW Input events. + if (!InitializeXInput()) + { + AZ_Warning("XcbInput", false, "XCB XInput initialization failed"); + return nullptr; + } + + return aznew XcbInputDeviceMouse(inputDevice); + } + + bool XcbInputDeviceMouse::IsConnected() const + { + return true; + } + + void XcbInputDeviceMouse::CreateBarriers(xcb_window_t window, bool create) + { + // Don't create any barriers if we are debugging. This will cause artifacts but better then + // a confined cursor during debugging. + if (AZ::Debug::Trace::IsDebuggerPresent()) + { + AZ_Warning("XcbInput", false, "Debugger running. Barriers will not be created."); + return; + } + + if (create) + { + // Destroy barriers if they are active already. + if (!m_activeBarriers.empty()) + { + for (const auto& barrier : m_activeBarriers) + { + xcb_xfixes_delete_pointer_barrier_checked(s_xcbConnection, barrier.id); + } + + m_activeBarriers.clear(); + } + + // Get window information. + const XcbStdFreePtr xcbGeometryReply{ xcb_get_geometry_reply( + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + + if (!xcbGeometryReply) + { + return; + } + + const xcb_translate_coordinates_cookie_t translate_coord = + xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0); + + const XcbStdFreePtr xkbTranslateCoordReply{ xcb_translate_coordinates_reply( + s_xcbConnection, translate_coord, NULL) }; + + if (!xkbTranslateCoordReply) + { + return; + } + + const int16_t x0 = xkbTranslateCoordReply->dst_x < 0 ? 0 : xkbTranslateCoordReply->dst_x; + const int16_t y0 = xkbTranslateCoordReply->dst_y < 0 ? 0 : xkbTranslateCoordReply->dst_y; + const int16_t x1 = xkbTranslateCoordReply->dst_x + xcbGeometryReply->width; + const int16_t y1 = xkbTranslateCoordReply->dst_y + xcbGeometryReply->height; + + // ATTN For whatever reason, when making an exact rectangle the pointer will escape the top right corner in some cases. Adding + // an offset to the lines so that they cross each other prevents that. + const int16_t offset = 30; + + // Create the left barrier info. + m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_POSITIVE_X, x0, Clamp(y0 - offset), + x0, Clamp(y1 + offset) }); + + // Create the right barrier info. + m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_NEGATIVE_X, x1, Clamp(y0 - offset), + x1, Clamp(y1 + offset) }); + + // Create the top barrier info. + m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_POSITIVE_Y, Clamp(x0 - offset), y0, + Clamp(x1 + offset), y0 }); + + // Create the bottom barrier info. + m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_NEGATIVE_Y, Clamp(x0 - offset), y1, + Clamp(x1 + offset), y1 }); + + // Create the xfixes barriers. + for (const auto& barrier : m_activeBarriers) + { + xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked( + s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL); + const XcbStdFreePtr xkbError{ xcb_request_check(s_xcbConnection, cookie) }; + + AZ_Warning( + "XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0, + barrier.x1, barrier.y1); + } + } + else + { + for (const auto& barrier : m_activeBarriers) + { + xcb_xfixes_delete_pointer_barrier_checked(s_xcbConnection, barrier.id); + } + + m_activeBarriers.clear(); + } + + xcb_flush(s_xcbConnection); + } + + bool XcbInputDeviceMouse::InitializeXFixes() + { + m_xfixesInitialized = false; + + // We don't have to free query_extension_reply according to xcb documentation. + const xcb_query_extension_reply_t* query_extension_reply = xcb_get_extension_data(s_xcbConnection, &xcb_xfixes_id); + if (!query_extension_reply || !query_extension_reply->present) + { + return m_xfixesInitialized; + } + + const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0); + + xcb_generic_error_t* error = NULL; + const XcbStdFreePtr xkbQueryRequestReply{ xcb_xfixes_query_version_reply( + s_xcbConnection, query_cookie, &error) }; + + if (!xkbQueryRequestReply || error) + { + if (error) + { + AZ_Warning("XcbInput", false, "Retrieving XFixes version failed : Error code %d", error->error_code); + free(error); + } + return m_xfixesInitialized; + } + else if (xkbQueryRequestReply->major_version < 5) + { + AZ_Warning("XcbInput", false, "XFixes version fails the minimum version check (%d<5)", xkbQueryRequestReply->major_version); + return m_xfixesInitialized; + } + + m_xfixesInitialized = true; + + return m_xfixesInitialized; + } + + bool XcbInputDeviceMouse::InitializeXInput() + { + m_xInputInitialized = false; + + // We don't have to free query_extension_reply according to xcb documentation. + const xcb_query_extension_reply_t* query_extension_reply = xcb_get_extension_data(s_xcbConnection, &xcb_input_id); + if (!query_extension_reply || !query_extension_reply->present) + { + return m_xInputInitialized; + } + + const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2); + + xcb_generic_error_t* error = NULL; + const XcbStdFreePtr xkbQueryRequestReply{ xcb_input_xi_query_version_reply( + s_xcbConnection, query_version_cookie, &error) }; + + if (!xkbQueryRequestReply || error) + { + if (error) + { + AZ_Warning("XcbInput", false, "Retrieving XInput version failed : Error code %d", error->error_code); + free(error); + } + return m_xInputInitialized; + } + else if (xkbQueryRequestReply->major_version < 2) + { + AZ_Warning("XcbInput", false, "XInput version fails the minimum version check (%d<5)", xkbQueryRequestReply->major_version); + return m_xInputInitialized; + } + + m_xInputInitialized = true; + + return m_xInputInitialized; + } + + void XcbInputDeviceMouse::SetEnableXInput(bool enable) + { + struct + { + xcb_input_event_mask_t head; + int mask; + } mask; + + mask.head.deviceid = XCB_INPUT_DEVICE_ALL; + mask.head.mask_len = 1; + + if (enable) + { + mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | + XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS | + XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE; + } + else + { + mask.mask = XCB_NONE; + } + + xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head); + + xcb_flush(s_xcbConnection); + } + + void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState) + { + if (systemCursorState != m_systemCursorState) + { + m_systemCursorState = systemCursorState; + + m_focusWindow = GetSystemCursorFocusWindow(); + + HandleCursorState(m_focusWindow, systemCursorState); + } + } + + void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState) + { + bool confined = false, cursorShown = true; + switch (systemCursorState) + { + case SystemCursorState::ConstrainedAndHidden: + { + //!< Constrained to the application's main window and hidden + confined = true; + cursorShown = false; + } + break; + case SystemCursorState::ConstrainedAndVisible: + { + //!< Constrained to the application's main window and visible + confined = true; + } + break; + case SystemCursorState::UnconstrainedAndHidden: + { + //!< Free to move outside the main window but hidden while inside + cursorShown = false; + } + break; + case SystemCursorState::UnconstrainedAndVisible: + { + //!< Free to move outside the application's main window and visible + } + case SystemCursorState::Unknown: + default: + break; + } + + // ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE + // We need however the window id to reset the cursor. + if (XCB_NONE == window && (confined || cursorShown)) + { + // Reuse the previous window to reset states. + window = m_prevConstraintWindow; + m_prevConstraintWindow = XCB_NONE; + } + else + { + // Remember the window we used to modify cursor and barrier states. + m_prevConstraintWindow = window; + } + + SetEnableXInput(!cursorShown); + + CreateBarriers(window, confined); + ShowCursor(window, cursorShown); + } + + SystemCursorState XcbInputDeviceMouse::GetSystemCursorState() const + { + return m_systemCursorState; + } + + void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized) + { + // TODO Basically not done at all. Added only the basic functions needed. + const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + + if (!xkbGeometryReply) + { + return; + } + + const int16_t x = static_cast(positionNormalized.GetX() * xkbGeometryReply->width); + const int16_t y = static_cast(positionNormalized.GetY() * xkbGeometryReply->height); + + xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y); + + xcb_flush(s_xcbConnection); + } + + void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) + { + const xcb_window_t window = GetSystemCursorFocusWindow(); + if (XCB_NONE == window) + { + return; + } + + SetSystemCursorPositionNormalizedInternal(window, positionNormalized); + } + + AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalizedInternal(xcb_window_t window) const + { + AZ::Vector2 position = AZ::Vector2::CreateZero(); + + const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window); + + const XcbStdFreePtr xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) }; + + if (!xkbQueryPointerReply) + { + return position; + } + + const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + + if (!xkbGeometryReply) + { + return position; + } + + AZ_Assert(xkbGeometryReply->width != 0, "xkbGeometry response width must be non-zero. (%d)", xkbGeometryReply->width); + const float normalizedCursorPostionX = static_cast(xkbQueryPointerReply->win_x) / xkbGeometryReply->width; + + AZ_Assert(xkbGeometryReply->height != 0, "xkbGeometry response height must be non-zero. (%d)", xkbGeometryReply->height); + const float normalizedCursorPostionY = static_cast(xkbQueryPointerReply->win_y) / xkbGeometryReply->height; + + position = AZ::Vector2(normalizedCursorPostionX, normalizedCursorPostionY); + + return position; + } + + AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const + { + const xcb_window_t window = GetSystemCursorFocusWindow(); + if (XCB_NONE == window) + { + return AZ::Vector2::CreateZero(); + } + + return GetSystemCursorPositionNormalizedInternal(window); + } + + void XcbInputDeviceMouse::TickInputDevice() + { + ProcessRawEventQueues(); + } + + void XcbInputDeviceMouse::ShowCursor(xcb_window_t window, bool show) + { + xcb_void_cookie_t cookie; + if (show) + { + cookie = xcb_xfixes_show_cursor_checked(s_xcbConnection, window); + } + else + { + cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window); + } + + const XcbStdFreePtr xkbError{ xcb_request_check(s_xcbConnection, cookie) }; + + if (xkbError) + { + AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code); + + return; + } + + // ATTN In the following part we will when cursor gets hidden store the position of the cursor in screen space + // not window space. We use that to re-position when showing the cursor again. Is this the correct + // behavior? + + const bool cursorWasHidden = !m_cursorShown; + m_cursorShown = show; + if (!m_cursorShown) + { + m_cursorHiddenPosition = GetSystemCursorPositionNormalizedInternal(s_xcbScreen->root); + + SetSystemCursorPositionNormalized(AZ::Vector2(0.5f, 0.5f)); + } + else if (cursorWasHidden) + { + SetSystemCursorPositionNormalizedInternal(s_xcbScreen->root, m_cursorHiddenPosition); + } + + xcb_flush(s_xcbConnection); + } + + void XcbInputDeviceMouse::HandleButtonPressEvents(uint32_t detail, bool pressed) + { + bool isWheel; + float wheelDirection; + const auto* button = InputChannelFromMouseEvent(detail, isWheel, wheelDirection); + if (button) + { + QueueRawButtonEvent(*button, pressed); + } + if (isWheel) + { + float axisValue = MAX_XI_WHEEL_SENSITIVITY * wheelDirection; + QueueRawMovementEvent(InputDeviceMouse::Movement::Z, axisValue); + } + } + + void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event) + { + const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast(event); + + m_systemCursorPosition[0] = mouseMotionEvent->event_x; + m_systemCursorPosition[1] = mouseMotionEvent->event_y; + } + + void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event) + { + const xcb_ge_generic_event_t* genericEvent = reinterpret_cast(event); + switch (genericEvent->event_type) + { + case XCB_INPUT_RAW_BUTTON_PRESS: + { + const xcb_input_raw_button_press_event_t* mouseButtonEvent = + reinterpret_cast(event); + HandleButtonPressEvents(mouseButtonEvent->detail, true); + } + break; + case XCB_INPUT_RAW_BUTTON_RELEASE: + { + const xcb_input_raw_button_release_event_t* mouseButtonEvent = + reinterpret_cast(event); + HandleButtonPressEvents(mouseButtonEvent->detail, false); + } + break; + case XCB_INPUT_RAW_MOTION: + { + const xcb_input_raw_motion_event_t* mouseMotionEvent = reinterpret_cast(event); + + int axisLen = xcb_input_raw_button_press_axisvalues_length(mouseMotionEvent); + const xcb_input_fp3232_t* axisvalues = xcb_input_raw_button_press_axisvalues_raw(mouseMotionEvent); + for (int i = 0; i < axisLen; ++i) + { + const float axisValue = fp3232ToFloat(axisvalues[i]); + + switch (i) + { + case 0: + QueueRawMovementEvent(InputDeviceMouse::Movement::X, axisValue); + break; + case 1: + QueueRawMovementEvent(InputDeviceMouse::Movement::Y, axisValue); + break; + } + } + } + break; + } + } + + void XcbInputDeviceMouse::PollSpecialEvents() + { + while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection)) + { + // TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events. + switch (genericEvent->response_type & ~0x80) + { + case XCB_GE_GENERIC: + { + const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast(genericEvent); + + // Only handle raw inputs if we have focus. + // Handle Raw Input events first. + if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) || + (geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || + (geGenericEvent->event_type == XCB_INPUT_RAW_MOTION)) + { + HandleRawInputEvents(geGenericEvent); + + free(genericEvent); + } + } + break; + } + } + } + + void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event) + { + switch (event->response_type & ~0x80) + { + // QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events. + case XCB_GE_GENERIC: + { + const xcb_ge_generic_event_t* genericEvent = reinterpret_cast(event); + + // Handling RAW Inputs here works in GameMode but not in Editor mode because QT is + // not handling RAW input events and passing to. + if (!m_cursorShown) + { + // Handle Raw Input events first. + if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) || + (genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION)) + { + HandleRawInputEvents(genericEvent); + } + } + else + { + switch (genericEvent->event_type) + { + case XCB_INPUT_BUTTON_PRESS: + { + const xcb_input_button_press_event_t* mouseButtonEvent = + reinterpret_cast(genericEvent); + HandleButtonPressEvents(mouseButtonEvent->detail, true); + } + break; + case XCB_INPUT_BUTTON_RELEASE: + { + const xcb_input_button_release_event_t* mouseButtonEvent = + reinterpret_cast(genericEvent); + HandleButtonPressEvents(mouseButtonEvent->detail, false); + } + break; + case XCB_INPUT_MOTION: + { + HandlePointerMotionEvents(event); + } + break; + } + } + } + break; + case XCB_FOCUS_IN: + { + const xcb_focus_in_event_t* focusInEvent = reinterpret_cast(event); + if (m_focusWindow != focusInEvent->event) + { + m_focusWindow = focusInEvent->event; + HandleCursorState(m_focusWindow, m_systemCursorState); + } + } + break; + case XCB_FOCUS_OUT: + { + const xcb_focus_out_event_t* focusOutEvent = reinterpret_cast(event); + HandleCursorState(focusOutEvent->event, SystemCursorState::UnconstrainedAndVisible); + + ProcessRawEventQueues(); + ResetInputChannelStates(); + + m_focusWindow = XCB_NONE; + } + break; + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h new file mode 100644 index 0000000000..106d204ca9 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h @@ -0,0 +1,193 @@ +/* + * 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 + +// The maximum number of raw input axis this mouse device supports. +constexpr uint32_t MAX_XI_RAW_AXIS = 2; + +// The sensitivity of the wheel. +constexpr float MAX_XI_WHEEL_SENSITIVITY = 140.0f; + +namespace AzFramework +{ + class XcbInputDeviceMouse + : public InputDeviceMouse::Implementation + , public XcbEventHandlerBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(XcbInputDeviceMouse, AZ::SystemAllocator, 0); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Constructor + //! \param[in] inputDevice Reference to the input device being implemented + XcbInputDeviceMouse(InputDeviceMouse& inputDevice); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Destructor + virtual ~XcbInputDeviceMouse(); + + static XcbInputDeviceMouse::Implementation* Create(InputDeviceMouse& inputDevice); + + protected: + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputDeviceMouse::Implementation::IsConnected + bool IsConnected() const override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputDeviceMouse::Implementation::SetSystemCursorState + void SetSystemCursorState(SystemCursorState systemCursorState) override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputDeviceMouse::Implementation::GetSystemCursorState + SystemCursorState GetSystemCursorState() const override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputDeviceMouse::Implementation::SetSystemCursorPositionNormalized + void SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputDeviceMouse::Implementation::GetSystemCursorPositionNormalized + AZ::Vector2 GetSystemCursorPositionNormalized() const override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice + void TickInputDevice() override; + + //! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode. + void PollSpecialEvents() override; + + //! Handle X11 events. + void HandleXcbEvent(xcb_generic_event_t* event) override; + + //! Initialize XFixes extension. Used for barriers. + static bool InitializeXFixes(); + + //! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor. + static bool InitializeXInput(); + + //! Enables/Disables XInput Raw Input events. + void SetEnableXInput(bool enable); + + //! Create barriers. + void CreateBarriers(xcb_window_t window, bool create); + + //! Helper function. + void SystemCursorStateToLogic(SystemCursorState systemCursorState, bool& confined, bool& cursorShown); + + //! Shows/Hides the cursor. + void ShowCursor(xcb_window_t window, bool show); + + //! Get the normalized cursor position. The coordinates returned are relative to the specified window. + AZ::Vector2 GetSystemCursorPositionNormalizedInternal(xcb_window_t window) const; + + //! Set the normalized cursor position. The normalized position will be relative to the specified window. + void SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized); + + //! Handle button press/release events. + void HandleButtonPressEvents(uint32_t detail, bool pressed); + + //! Handle motion notify events. + void HandlePointerMotionEvents(const xcb_generic_event_t* event); + + //! Will set cursor states and confinement modes. + void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState); + + //! Will handle all raw input events. + void HandleRawInputEvents(const xcb_ge_generic_event_t* event); + + //! Convert XInput fp1616 to float. + inline float fp1616ToFloat(xcb_input_fp1616_t value) const + { + return static_cast((value >> 16) + (value & 0xffff) / 0xffff); + } + + //! Convert XInput fp3232 to float. + inline float fp3232ToFloat(xcb_input_fp3232_t value) const + { + return static_cast(value.integral) + static_cast(value.frac / (float)(1ull << 32)); + } + + const InputChannelId* InputChannelFromMouseEvent(xcb_button_t button, bool& isWheel, float& direction) const + { + isWheel = false; + direction = 1.0f; + switch (button) + { + case XCB_BUTTON_INDEX_1: + return &InputDeviceMouse::Button::Left; + case XCB_BUTTON_INDEX_2: + return &InputDeviceMouse::Button::Right; + case XCB_BUTTON_INDEX_3: + return &InputDeviceMouse::Button::Middle; + case XCB_BUTTON_INDEX_4: + isWheel = true; + direction = 1.0f; + break; + case XCB_BUTTON_INDEX_5: + isWheel = true; + direction = -1.0f; + break; + default: + break; + } + + return nullptr; + } + + // Barriers work only with positive values. We clamp here to zero. + inline int16_t Clamp(int16_t value) const + { + return value < 0 ? 0 : value; + } + + private: + //! The current system cursor state + SystemCursorState m_systemCursorState; + + //! The cursor position before it got hidden. + AZ::Vector2 m_cursorHiddenPosition; + + AZ::Vector2 m_systemCursorPositionNormalized; + uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS]; + + static xcb_connection_t* s_xcbConnection; + static xcb_screen_t* s_xcbScreen; + + //! Will be true if the xfixes extension could be initialized. + static bool m_xfixesInitialized; + + //! Will be true if the xinput2 extension could be initialized. + static bool m_xInputInitialized; + + //! The window that had focus + xcb_window_t m_prevConstraintWindow; + + //! The current window that has focus + xcb_window_t m_focusWindow; + + //! Will be true if the cursor is shown else false. + bool m_cursorShown; + + struct XFixesBarrierProperty + { + xcb_xfixes_barrier_t id; + uint32_t direction; + int16_t x0, y0, x1, y1; + }; + + //! Array that holds barrier information used to confine the cursor. + std::vector m_activeBarriers; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index 6ab97fd616..af5b3af3d8 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -8,24 +8,31 @@ #include #include -#include #include +#include +#include #include namespace AzFramework { [[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow"; - static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages - static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified - static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event + static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages + static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified + static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event + +#define _NET_WM_STATE_REMOVE 0l +#define _NET_WM_STATE_ADD 1l +#define _NET_WM_STATE_TOGGLE 2l //////////////////////////////////////////////////////////////////////////////////////////////// - XcbNativeWindow::XcbNativeWindow() + XcbNativeWindow::XcbNativeWindow() : NativeWindow::Implementation() + , m_xcbConnection(nullptr) + , m_xcbRootScreen(nullptr) + , m_xcbWindow(XCB_NONE) { - if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); - xcbConnectionManager != nullptr) + if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr) { m_xcbConnection = xcbConnectionManager->GetXcbConnection(); } @@ -33,89 +40,184 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - XcbNativeWindow::~XcbNativeWindow() = default; + XcbNativeWindow::~XcbNativeWindow() + { + if (XCB_NONE != m_xcbWindow) + { + xcb_destroy_window(m_xcbConnection, m_xcbWindow); + } + } //////////////////////////////////////////////////////////////////////////////////////////////// - void XcbNativeWindow::InitWindow(const AZStd::string& title, - const WindowGeometry& geometry, - const WindowStyleMasks& styleMasks) + void XcbNativeWindow::InitWindow(const AZStd::string& title, const WindowGeometry& geometry, const WindowStyleMasks& styleMasks) { - // Get the parent window + // Get the parent window const xcb_setup_t* xcbSetup = xcb_get_setup(m_xcbConnection); - xcb_screen_t* xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data; - xcb_window_t xcbParentWindow = xcbRootScreen->root; + m_xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data; + xcb_window_t xcbParentWindow = m_xcbRootScreen->root; // Create an XCB window from the connection m_xcbWindow = xcb_generate_id(m_xcbConnection); uint16_t borderWidth = 0; const uint32_t mask = styleMasks.m_platformAgnosticStyleMask; - if ((mask & WindowStyleMasks::WINDOW_STYLE_BORDERED) || - (mask & WindowStyleMasks::WINDOW_STYLE_RESIZEABLE)) + if ((mask & WindowStyleMasks::WINDOW_STYLE_BORDERED) || (mask & WindowStyleMasks::WINDOW_STYLE_RESIZEABLE)) { borderWidth = s_DefaultXcbWindowBorderWidth; } uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; - - const uint32_t interestedEvents = - XCB_EVENT_MASK_STRUCTURE_NOTIFY - | XCB_EVENT_MASK_BUTTON_PRESS - | XCB_EVENT_MASK_BUTTON_RELEASE - | XCB_EVENT_MASK_KEY_PRESS - | XCB_EVENT_MASK_KEY_RELEASE - | XCB_EVENT_MASK_POINTER_MOTION - ; - uint32_t valueList[] = { xcbRootScreen->black_pixel, - interestedEvents }; + + const uint32_t interestedEvents = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE; + uint32_t valueList[] = { m_xcbRootScreen->black_pixel, interestedEvents }; xcb_void_cookie_t xcbCheckResult; - xcbCheckResult = xcb_create_window_checked(m_xcbConnection, - XCB_COPY_FROM_PARENT, - m_xcbWindow, - xcbParentWindow, - aznumeric_cast(geometry.m_posX), - aznumeric_cast(geometry.m_posY), - aznumeric_cast(geometry.m_width), - aznumeric_cast(geometry.m_height), - borderWidth, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - xcbRootScreen->root_visual, - eventMask, - valueList); + xcbCheckResult = xcb_create_window_checked( + m_xcbConnection, XCB_COPY_FROM_PARENT, m_xcbWindow, xcbParentWindow, aznumeric_cast(geometry.m_posX), + aznumeric_cast(geometry.m_posY), aznumeric_cast(geometry.m_width), aznumeric_cast(geometry.m_height), + borderWidth, XCB_WINDOW_CLASS_INPUT_OUTPUT, m_xcbRootScreen->root_visual, eventMask, valueList); AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to create xcb window."); SetWindowTitle(title); - // Setup the window close event - const static char* wmProtocolString = "WM_PROTOCOLS"; - - xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString); - xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr); - AZ_Error(XcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString); - m_xcbAtomProtocols = replyProtocol->atom; - - const static char* wmDeleteWindowString = "WM_DELETE_WINDOW"; - xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString); - xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr); - AZ_Error(XcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString); - m_xcbAtomDeleteWindow = replyDeleteWindow->atom; - - xcbCheckResult = xcb_change_property_checked(m_xcbConnection, - XCB_PROP_MODE_REPLACE, - m_xcbWindow, - m_xcbAtomProtocols, - XCB_ATOM_ATOM, - s_XcbFormatDataSize, - 1, - &m_xcbAtomDeleteWindow); - - AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to change the xcb atom property for WM_CLOSE event"); - + m_posX = geometry.m_posX; + m_posY = geometry.m_posY; m_width = geometry.m_width; m_height = geometry.m_height; + + InitializeAtoms(); + + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.type = _NET_REQUEST_FRAME_EXTENTS; + event.window = m_xcbWindow; + event.format = 32; + event.sequence = 0; + event.data.data32[0] = 0l; + event.data.data32[1] = 0l; + event.data.data32[2] = 0l; + event.data.data32[3] = 0l; + event.data.data32[4] = 0l; + xcbCheckResult = xcb_send_event( + m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (const char*)&event); + AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_REQUEST_FRAME_EXTENTS"); + + // The WM will be able to kill the application if it gets unresponsive. + int32_t pid = getpid(); + xcb_change_property(m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid); + + xcb_flush(m_xcbConnection); + } + + xcb_atom_t XcbNativeWindow::GetAtom(const char* atomName) + { + xcb_intern_atom_cookie_t intern_atom_cookie = xcb_intern_atom(m_xcbConnection, 0, strlen(atomName), atomName); + XcbStdFreePtr xkbinternAtom{ xcb_intern_atom_reply(m_xcbConnection, intern_atom_cookie, NULL) }; + + if (!xkbinternAtom) + { + AZ_Error(XcbErrorWindow, xkbinternAtom != nullptr, "Unable to query xcb '%s' atom", atomName); + return XCB_NONE; + } + + return xkbinternAtom->atom; + } + + int XcbNativeWindow::SetAtom(xcb_window_t window, xcb_atom_t atom, xcb_atom_t type, size_t len, void* data) + { + xcb_void_cookie_t cookie = xcb_change_property_checked(m_xcbConnection, XCB_PROP_MODE_REPLACE, window, atom, type, 32, len, data); + XcbStdFreePtr xkbError{ xcb_request_check(m_xcbConnection, cookie) }; + + if (!xkbError) + { + return 0; + } + + return xkbError->error_code; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + + void XcbNativeWindow::InitializeAtoms() + { + AZStd::vector Atoms; + + _NET_ACTIVE_WINDOW = GetAtom("_NET_ACTIVE_WINDOW"); + _NET_WM_BYPASS_COMPOSITOR = GetAtom("_NET_WM_BYPASS_COMPOSITOR"); + + // --------------------------------------------------------------------- + // Handle all WM Protocols atoms. + // + + WM_PROTOCOLS = GetAtom("WM_PROTOCOLS"); + + // This atom is used to close a window. Emitted when user clicks the close button. + WM_DELETE_WINDOW = GetAtom("WM_DELETE_WINDOW"); + + Atoms.push_back(WM_DELETE_WINDOW); + + xcb_change_property( + m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, WM_PROTOCOLS, XCB_ATOM_ATOM, 32, Atoms.size(), Atoms.data()); + + xcb_flush(m_xcbConnection); + + // --------------------------------------------------------------------- + // Handle all WM State atoms. + // + + _NET_WM_STATE = GetAtom("_NET_WM_STATE"); + _NET_WM_STATE_FULLSCREEN = GetAtom("_NET_WM_STATE_FULLSCREEN"); + _NET_WM_STATE_MAXIMIZED_VERT = GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"); + _NET_WM_STATE_MAXIMIZED_HORZ = GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"); + _NET_MOVERESIZE_WINDOW = GetAtom("_NET_MOVERESIZE_WINDOW"); + _NET_REQUEST_FRAME_EXTENTS = GetAtom("_NET_REQUEST_FRAME_EXTENTS"); + _NET_FRAME_EXTENTS = GetAtom("_NET_FRAME_EXTENTS"); + _NET_WM_PID = GetAtom("_NET_WM_PID"); + } + + void XcbNativeWindow::GetWMStates() + { + xcb_get_property_cookie_t cookie = xcb_get_property(m_xcbConnection, 0, m_xcbWindow, _NET_WM_STATE, XCB_ATOM_ATOM, 0, 1024); + + xcb_generic_error_t* error = nullptr; + XcbStdFreePtr xkbGetPropertyReply{ xcb_get_property_reply(m_xcbConnection, cookie, &error) }; + + if (!xkbGetPropertyReply || error || !((xkbGetPropertyReply->format == 32) && (xkbGetPropertyReply->type == XCB_ATOM_ATOM))) + { + AZ_Warning("ApplicationLinux", false, "Acquiring _NET_WM_STATE information from the WM failed."); + + if (error) + { + AZ_TracePrintf("Error", "Error code %d", error->error_code); + free(error); + } + return; + } + + m_fullscreenState = false; + m_horizontalyMaximized = false; + m_verticallyMaximized = false; + + const xcb_atom_t* states = static_cast(xcb_get_property_value(xkbGetPropertyReply.get())); + for (int i = 0; i < xkbGetPropertyReply->length; i++) + { + if (states[i] == _NET_WM_STATE_FULLSCREEN) + { + m_fullscreenState = true; + } + else if (states[i] == _NET_WM_STATE_MAXIMIZED_HORZ) + { + m_horizontalyMaximized = true; + } + else if (states[i] == _NET_WM_STATE_MAXIMIZED_VERT) + { + m_verticallyMaximized = true; + } + } } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -145,7 +247,7 @@ namespace AzFramework xcb_flush(m_xcbConnection); } XcbEventHandlerBus::Handler::BusDisconnect(); - } + } //////////////////////////////////////////////////////////////////////////////////////////////// NativeWindowHandle XcbNativeWindow::GetWindowHandle() const @@ -157,14 +259,9 @@ namespace AzFramework void XcbNativeWindow::SetWindowTitle(const AZStd::string& title) { xcb_void_cookie_t xcbCheckResult; - xcbCheckResult = xcb_change_property(m_xcbConnection, - XCB_PROP_MODE_REPLACE, - m_xcbWindow, - XCB_ATOM_WM_NAME, - XCB_ATOM_STRING, - 8, - static_cast(title.size()), - title.c_str()); + xcbCheckResult = xcb_change_property( + m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast(title.size()), + title.c_str()); AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title."); } @@ -174,7 +271,7 @@ namespace AzFramework const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height }; xcb_configure_window(m_xcbConnection, m_xcbWindow, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); - + m_width = clientAreaSize.m_width; m_height = clientAreaSize.m_height; } @@ -184,16 +281,77 @@ namespace AzFramework { // [GFX TODO][GHI - 2678] // Using 60 for now until proper support is added + return 60; } + bool XcbNativeWindow::GetFullScreenState() const + { + return m_fullscreenState; + } + + void XcbNativeWindow::SetFullScreenState(bool fullScreenState) + { + // TODO This is a pretty basic full-screen implementation using WM's _NET_WM_STATE_FULLSCREEN state. + // Do we have to provide also the old way? + + GetWMStates(); + + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.type = _NET_WM_STATE; + event.window = m_xcbWindow; + event.format = 32; + event.sequence = 0; + event.data.data32[0] = fullScreenState ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + event.data.data32[1] = _NET_WM_STATE_FULLSCREEN; + event.data.data32[2] = 0; + event.data.data32[3] = 1; + event.data.data32[4] = 0; + xcb_void_cookie_t xcbCheckResult = xcb_send_event( + m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (const char*)&event); + AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_WM_STATE_FULLSCREEN"); + + // Also try to disable/enable the compositor if possible. Might help in some cases. + const long _NET_WM_BYPASS_COMPOSITOR_HINT_ON = m_fullscreenState ? 1 : 0; + SetAtom(m_xcbWindow, _NET_WM_BYPASS_COMPOSITOR, XCB_ATOM_CARDINAL, 32, (char*)&_NET_WM_BYPASS_COMPOSITOR_HINT_ON); + + if (!fullScreenState) + { + if (m_horizontalyMaximized || m_verticallyMaximized) + { + printf("Remove maximized state.\n"); + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.type = _NET_WM_STATE; + event.window = m_xcbWindow; + event.format = 32; + event.sequence = 0; + event.data.data32[0] = _NET_WM_STATE_MAXIMIZED_VERT; + event.data.data32[1] = _NET_WM_STATE_MAXIMIZED_HORZ; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = 0; + xcb_void_cookie_t xcbCheckResult = xcb_send_event( + m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (const char*)&event); + AZ_Assert( + ValidateXcbResult(xcbCheckResult), "Failed to remove _NET_WM_STATE_MAXIMIZED_VERT | _NET_WM_STATE_MAXIMIZED_HORZ"); + } + } + + xcb_flush(m_xcbConnection); + m_fullscreenState = fullScreenState; + } + //////////////////////////////////////////////////////////////////////////////////////////////// bool XcbNativeWindow::ValidateXcbResult(xcb_void_cookie_t cookie) { bool result = true; if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie)) { - AZ_TracePrintf("Error","Error code %d", error->error_code); + AZ_TracePrintf("Error", "Error code %d", error->error_code); result = false; } return result; @@ -204,20 +362,20 @@ namespace AzFramework { switch (event->response_type & s_XcbResponseTypeMask) { - case XCB_CONFIGURE_NOTIFY: + case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t* cne = reinterpret_cast(event); - WindowSizeChanged(aznumeric_cast(cne->width), - aznumeric_cast(cne->height)); - + if ((cne->width != m_width) || (cne->height != m_height)) + { + WindowSizeChanged(aznumeric_cast(cne->width), aznumeric_cast(cne->height)); + } break; } - case XCB_CLIENT_MESSAGE: + case XCB_CLIENT_MESSAGE: { xcb_client_message_event_t* cme = reinterpret_cast(event); - if ((cme->type == m_xcbAtomProtocols) && - (cme->format == s_XcbFormatDataSize) && - (cme->data.data32[0] == m_xcbAtomDeleteWindow)) + + if ((cme->type == WM_PROTOCOLS) && (cme->format == s_XcbFormatDataSize) && (cme->data.data32[0] == WM_DELETE_WINDOW)) { Deactivate(); @@ -238,7 +396,8 @@ namespace AzFramework if (m_activated) { - WindowNotificationBus::Event(reinterpret_cast(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height); + WindowNotificationBus::Event( + reinterpret_cast(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height); } } } diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h index 36737e24e8..38462dcb08 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h @@ -27,15 +27,16 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// // NativeWindow::Implementation - void InitWindow(const AZStd::string& title, - const WindowGeometry& geometry, - const WindowStyleMasks& styleMasks) override; + void InitWindow(const AZStd::string& title, const WindowGeometry& geometry, const WindowStyleMasks& styleMasks) override; void Activate() override; void Deactivate() override; NativeWindowHandle GetWindowHandle() const override; void SetWindowTitle(const AZStd::string& title) override; void ResizeClientArea(WindowSize clientAreaSize) override; - uint32_t GetDisplayRefreshRate() const override; + uint32_t GetDisplayRefreshRate() const override; + + bool GetFullScreenState() const override; + void SetFullScreenState(bool fullScreenState) override; //////////////////////////////////////////////////////////////////////////////////////////// // XcbEventHandlerBus::Handler @@ -44,10 +45,46 @@ namespace AzFramework private: bool ValidateXcbResult(xcb_void_cookie_t cookie); void WindowSizeChanged(const uint32_t width, const uint32_t height); + int SetAtom(xcb_window_t window, xcb_atom_t atom, xcb_atom_t type, size_t len, void* data); + + // Initialize one atom. + xcb_atom_t GetAtom(const char* atomName); + + // Initialize all used atoms. + void InitializeAtoms(); + void GetWMStates(); + + xcb_connection_t* m_xcbConnection = nullptr; + xcb_screen_t* m_xcbRootScreen = nullptr; + xcb_window_t m_xcbWindow = 0; + int32_t m_posX; + int32_t m_posY; + bool m_fullscreenState = false; + bool m_horizontalyMaximized = false; + bool m_verticallyMaximized = false; - xcb_connection_t* m_xcbConnection = nullptr; - xcb_window_t m_xcbWindow = 0; - xcb_atom_t m_xcbAtomProtocols; - xcb_atom_t m_xcbAtomDeleteWindow; + // Use exact atom names for easy readability and usage. + xcb_atom_t WM_PROTOCOLS; + xcb_atom_t WM_DELETE_WINDOW; + // This atom is used to activate a window. + xcb_atom_t _NET_ACTIVE_WINDOW; + // This atom is use to bypass a compositor. Used during fullscreen mode. + xcb_atom_t _NET_WM_BYPASS_COMPOSITOR; + // This atom is used to change the state of a window using the WM. + xcb_atom_t _NET_WM_STATE; + // This atom is used to enable/disable fullscreen mode of a window. + xcb_atom_t _NET_WM_STATE_FULLSCREEN; + // This atom is used to extend the window to max vertically. + xcb_atom_t _NET_WM_STATE_MAXIMIZED_VERT; + // This atom is used to extend the window to max horizontally. + xcb_atom_t _NET_WM_STATE_MAXIMIZED_HORZ; + // This atom is used to position and resize a window. + xcb_atom_t _NET_MOVERESIZE_WINDOW; + // This atom is used to request the extent of the window. + xcb_atom_t _NET_REQUEST_FRAME_EXTENTS; + // This atom is used to identify the reply event for _NET_REQUEST_FRAME_EXTENTS + xcb_atom_t _NET_FRAME_EXTENTS; + // This atom is used to allow WM to kill app if not responsive anymore + xcb_atom_t _NET_WM_PID; }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake b/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake index 68de710fa1..48afccc0d0 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake @@ -12,6 +12,8 @@ set(FILES AzFramework/XcbConnectionManager.h AzFramework/XcbInputDeviceKeyboard.cpp AzFramework/XcbInputDeviceKeyboard.h + AzFramework/XcbInputDeviceMouse.cpp + AzFramework/XcbInputDeviceMouse.h AzFramework/XcbInterface.h AzFramework/XcbNativeWindow.cpp AzFramework/XcbNativeWindow.h diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Linux.cpp new file mode 100644 index 0000000000..e2c3c2d575 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Linux.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 + * + */ + +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + +namespace AzFramework +{ + InputDeviceMouse::Implementation* InputDeviceMouse::Implementation::Create(InputDeviceMouse& inputDevice) + { +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + return XcbInputDeviceMouse::Create(inputDevice); +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND +#error "Linux Window Manager Wayland not supported." + return nullptr; +#else +#error "Linux Window Manager not recognized." + return nullptr; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake index b08c3b310d..66211c6085 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake @@ -22,8 +22,10 @@ if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") PRIVATE 3rdParty::X11::xcb 3rdParty::X11::xcb_xkb + 3rdParty::X11::xcb_xfixes 3rdParty::X11::xkbcommon 3rdParty::X11::xkbcommon_X11 + xcb-xinput ) elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland") diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake index 4f06aa5c57..206563fdda 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake @@ -22,8 +22,8 @@ set(FILES AzFramework/Windowing/NativeWindow_Linux.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp + AzFramework/Input/Devices/Mouse/InputDeviceMouse_Linux.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp - ../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp AzFramework/Input/User/LocalUserId_Platform.h ../Common/Default/AzFramework/Input/User/LocalUserId_Default.h diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/IO/LocalFileIO_Windows.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/IO/LocalFileIO_Windows.cpp index b8f62a2b77..173ec61263 100644 --- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/IO/LocalFileIO_Windows.cpp +++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/IO/LocalFileIO_Windows.cpp @@ -7,26 +7,24 @@ */ #include #include +#include +#include #include -namespace AZ +namespace AZ::IO { - namespace IO + Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath) { + AZ::IO::FixedMaxPath resolvedSourcePath; + ResolvePath(resolvedSourcePath, sourceFilePath); + AZ::IO::FixedMaxPath resolvedDestPath; + ResolvePath(resolvedDestPath, destinationFilePath); - Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath) - { - char resolvedSourcePath[AZ_MAX_PATH_LEN]; - ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN); - char resolvedDestPath[AZ_MAX_PATH_LEN]; - ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN); + AZStd::fixed_wstring resolvedSourcePathW; + AZStd::fixed_wstring resolvedDestPathW; + AZStd::to_wstring(resolvedSourcePathW, resolvedSourcePath.Native()); + AZStd::to_wstring(resolvedDestPathW, resolvedDestPath.Native()); - if (::CopyFileA(resolvedSourcePath, resolvedDestPath, false) == 0) - { - return ResultCode::Error; - } - - return ResultCode::Success; - } - } // namespace IO -}//namespace AZ + return ::CopyFileW(resolvedSourcePathW.c_str(), resolvedDestPathW.c_str(), false) != 0 ? ResultCode::Success : ResultCode::Error; + } +}//namespace AZ::IO 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/Application.cpp b/Code/Framework/AzFramework/Tests/Application.cpp index 313b5e3b56..9ad072cba5 100644 --- a/Code/Framework/AzFramework/Tests/Application.cpp +++ b/Code/Framework/AzFramework/Tests/Application.cpp @@ -26,7 +26,7 @@ protected: } if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr) { - fileIoBase->SetAlias("@assets@", m_tempDirectory.GetDirectory()); + fileIoBase->SetAlias("@products@", m_tempDirectory.GetDirectory()); } } diff --git a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp index 8e88ccfc39..37babb49a8 100644 --- a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp +++ b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp @@ -50,7 +50,7 @@ namespace UnitTest m_application->Start({}); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); } @@ -262,7 +262,7 @@ namespace UnitTest pArchive.reset(); EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders.c_str())); - EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders.c_str())); + EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders.c_str())); EXPECT_TRUE(archive->IsFileExist(fileInArchiveFile)); } @@ -353,7 +353,7 @@ namespace UnitTest // and be able to IMMEDIATELY // * read the file in the subfolder // * enumerate the folders (including that subfolder) even though they are 'virtual', not real folders on physical media - // * all of the above even though the mount point for the archive is @assets@ wheras the physical pack lives in @usercache@ + // * all of the above even though the mount point for the archive is @products@ wheras the physical pack lives in @usercache@ // finally, we're going to repeat the above test but with files mounted with subfolders // so for example, the pack will contain levelinfo.xml at the root of it // but it will be mounted at a subfolder (levels/mylevel). @@ -388,7 +388,7 @@ namespace UnitTest pArchive.reset(); EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders)); - EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders)); + EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders)); // ---- BARRAGE OF TESTS EXPECT_TRUE(archive->IsFileExist("levels\\mylevel\\levelinfo.xml")); EXPECT_TRUE(archive->IsFileExist("levels//mylevel//levelinfo.xml")); @@ -484,7 +484,7 @@ namespace UnitTest pArchive.reset(); EXPECT_TRUE(IsPackValid(testArchivePath_withMountPoint)); - EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2", testArchivePath_withMountPoint)); + EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2", testArchivePath_withMountPoint)); // ---- BARRAGE OF TESTS EXPECT_TRUE(archive->IsFileExist("uniquename\\mylevel2\\levelinfo.xml")); @@ -543,7 +543,7 @@ namespace UnitTest archive->ClosePack(testArchivePath_withMountPoint); // --- test to make sure that when you iterate only the first component is found, so bury it deep and ask for the root - EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint)); + EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint)); found_mylevel_folder = false; handle = archive->FindFirst("uniquename\\*"); @@ -574,9 +574,9 @@ namespace UnitTest found_mylevel_folder = false; // now make sure no red herrings appear - // for example, if a file is mounted at "@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4" - // and the file "@assets@\\somethingelse" is requested it should not be found - // in addition if the file "@assets@\\uniquename\\mylevel3" is requested it should not be found + // for example, if a file is mounted at "@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4" + // and the file "@products@\\somethingelse" is requested it should not be found + // in addition if the file "@products@\\uniquename\\mylevel3" is requested it should not be found handle = archive->FindFirst("somethingelse\\*"); EXPECT_FALSE(static_cast(handle)); @@ -610,7 +610,7 @@ namespace UnitTest cpfio.Remove(genericArchiveFileName); // create the asset alias directory - cpfio.CreatePath("@assets@"); + cpfio.CreatePath("@products@"); // create generic file @@ -635,11 +635,11 @@ namespace UnitTest pArchive.reset(); EXPECT_TRUE(IsPackValid(genericArchiveFileName)); - EXPECT_TRUE(archive->OpenPack("@assets@", genericArchiveFileName)); + EXPECT_TRUE(archive->OpenPack("@products@", genericArchiveFileName)); // ---- BARRAGE OF TESTS EXPECT_TRUE(cpfio.Exists("testfile.xml")); - EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml")); // this should be hte same file + EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml")); // this should be hte same file EXPECT_TRUE(!cpfio.Exists("@log@/testfile.xml")); EXPECT_TRUE(!cpfio.Exists("@usercache@/testfile.xml")); EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml")); @@ -685,9 +685,9 @@ namespace UnitTest EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle)); EXPECT_TRUE(!cpfio.IsDirectory("testfile.xml")); - EXPECT_TRUE(cpfio.IsDirectory("@assets@")); + EXPECT_TRUE(cpfio.IsDirectory("@products@")); EXPECT_TRUE(cpfio.IsReadOnly("testfile.xml")); - EXPECT_TRUE(cpfio.IsReadOnly("@assets@/testfile.xml")); + EXPECT_TRUE(cpfio.IsReadOnly("@products@/testfile.xml")); EXPECT_TRUE(!cpfio.IsReadOnly("@log@/unittesttemp/realfileforunittest.xml")); @@ -714,10 +714,10 @@ namespace UnitTest // find files test. AZ::IO::FixedMaxPath resolvedTestFilePath; - EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@assets@/testfile.xml"))); + EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@products@/testfile.xml"))); bool foundIt = false; // note that this file exists only in the archive. - cpfio.FindFiles("@assets@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName) + cpfio.FindFiles("@products@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName) { AZ::IO::FixedMaxPath resolvedFoundPath; EXPECT_TRUE(cpfio.ResolvePath(resolvedFoundPath, AZ::IO::PathView(foundName))); @@ -734,10 +734,10 @@ namespace UnitTest // The following test is disabled because it will trigger an AZ_ERROR which will affect the outcome of this entire test - // EXPECT_NE(ResultCode::Success, cpfio.Remove("@assets@/testfile.xml")); // may not delete archive files + // EXPECT_NE(ResultCode::Success, cpfio.Remove("@products@/testfile.xml")); // may not delete archive files // make sure it works with and without alias: - EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml")); + EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml")); EXPECT_TRUE(cpfio.Exists("testfile.xml")); EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml")); @@ -788,22 +788,22 @@ namespace UnitTest EXPECT_TRUE(archive->ClosePack(realNameBuf)); // change its actual location: - EXPECT_TRUE(archive->OpenPack("@assets@", realNameBuf)); - EXPECT_TRUE(archive->IsFileExist("@assets@/foundit.dat")); + EXPECT_TRUE(archive->OpenPack("@products@", realNameBuf)); + EXPECT_TRUE(archive->IsFileExist("@products@/foundit.dat")); EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous location! - EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk)); - EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat")); + EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk)); + EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat")); EXPECT_TRUE(archive->ClosePack(realNameBuf)); // try sub-folders - EXPECT_TRUE(archive->OpenPack("@assets@/mystuff", realNameBuf)); - EXPECT_TRUE(archive->IsFileExist("@assets@/mystuff/foundit.dat")); - EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat")); // do not find it in the previous locations! + EXPECT_TRUE(archive->OpenPack("@products@/mystuff", realNameBuf)); + EXPECT_TRUE(archive->IsFileExist("@products@/mystuff/foundit.dat")); + EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat")); // do not find it in the previous locations! EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous locations! - EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk)); - EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk)); - EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat")); // non-existent file - EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/notfoundit.dat")); // non-existent file + EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk)); + EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk)); + EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat")); // non-existent file + EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/notfoundit.dat")); // non-existent file EXPECT_TRUE(archive->ClosePack(realNameBuf)); } @@ -861,7 +861,7 @@ namespace UnitTest AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance(); ASSERT_NE(nullptr, ioBase); - const char* assetsPath = ioBase->GetAlias("@assets@"); + const char* assetsPath = ioBase->GetAlias("@products@"); ASSERT_NE(nullptr, assetsPath); auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds"; @@ -872,7 +872,7 @@ namespace UnitTest // it normalizes the string, so the slashes flip and everything is lowercased. AZ::IO::FixedMaxPath resolvedAddedPath; AZ::IO::FixedMaxPath resolvedResourcePath; - EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@assets@/textures/test.dds")); + EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@products@/textures/test.dds")); EXPECT_TRUE(ioBase->ReplaceAlias(resolvedResourcePath, reslist->GetFirst())); EXPECT_EQ(resolvedAddedPath, resolvedResourcePath); reslist->Clear(); 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/FileIO.cpp b/Code/Framework/AzFramework/Tests/FileIO.cpp index dbee109978..ca7c46b66c 100644 --- a/Code/Framework/AzFramework/Tests/FileIO.cpp +++ b/Code/Framework/AzFramework/Tests/FileIO.cpp @@ -802,6 +802,51 @@ namespace UnitTest AZ_TEST_STOP_TRACE_SUPPRESSION(1); } + TEST_F(AliasTest, GetAlias_LogsError_WhenAccessingDeprecatedAlias_Succeeds) + { + AZ::IO::LocalFileIO local; + + AZ::IO::FixedMaxPathString aliasFolder; + EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity())); + aliasFolder.resize_no_construct(AZStd::char_traits::length(aliasFolder.data())); + + local.SetAlias("@test@", aliasFolder.c_str()); + local.SetDeprecatedAlias("@deprecated@", "@test@"); + local.SetDeprecatedAlias("@deprecatednonexistent@", "@nonexistent@"); + local.SetDeprecatedAlias("@deprecatedsecond@", "@deprecated@"); + local.SetDeprecatedAlias("@deprecatednonaliaspath@", aliasFolder); + + AZ_TEST_START_TRACE_SUPPRESSION; + const char* testAlias = local.GetAlias("@test@"); + ASSERT_NE(nullptr, testAlias); + EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias)); + AZ_TEST_STOP_TRACE_SUPPRESSION(0); + + // Validate that accessing Deprecated Alias results in AZ_Error + AZ_TEST_START_TRACE_SUPPRESSION; + testAlias = local.GetAlias("@deprecated@"); + ASSERT_NE(nullptr, testAlias); + EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias)); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + testAlias = local.GetAlias("@deprecatednonexistent@"); + EXPECT_EQ(nullptr, testAlias); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + testAlias = local.GetAlias("@deprecatedsecond@"); + ASSERT_NE(nullptr, testAlias); + EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias)); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + testAlias = local.GetAlias("@deprecatednonaliaspath@"); + ASSERT_NE(nullptr, testAlias); + EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias)); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + class SmartMoveTests : public FolderFixture { diff --git a/Code/Framework/AzFramework/Tests/FileTagTests.cpp b/Code/Framework/AzFramework/Tests/FileTagTests.cpp index 134e7aad6d..2b09a5638c 100644 --- a/Code/Framework/AzFramework/Tests/FileTagTests.cpp +++ b/Code/Framework/AzFramework/Tests/FileTagTests.cpp @@ -27,7 +27,7 @@ namespace UnitTest const char DummyFile[] = "dummy.txt"; const char AnotherDummyFile[] = "Foo/Dummy.txt"; - + const char DummyPattern[] = R"(^(.+)_([a-z]+)\..+$)"; const char MatchingPatternFile[] = "Foo/dummy_abc.txt"; const char NonMatchingPatternFile[] = "Foo/dummy_a8c.txt"; @@ -75,7 +75,7 @@ namespace UnitTest : public AllocatorsFixture { public: - + void SetUp() override { AllocatorsFixture::SetUp(); @@ -89,7 +89,7 @@ namespace UnitTest const char* testAssetRoot = m_tempDirectory.GetDirectory(); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); @@ -98,7 +98,7 @@ namespace UnitTest AZ::IO::FileIOBase::SetInstance(nullptr); AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO.get()); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", testAssetRoot); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", testAssetRoot); m_data->m_excludeFileQueryManager = AZStd::make_unique(FileTagType::Exclude); m_data->m_includeFileQueryManager = AZStd::make_unique(FileTagType::Include); @@ -114,7 +114,7 @@ namespace UnitTest AZStd::vector includedWildcardTags = { DummyFileTags[DummyFileTagIndex::GIdx] }; EXPECT_TRUE(m_data->m_fileTagManager.AddFilePatternTags(DummyWildcard, FilePatternType::Wildcard, FileTagType::Include, includedWildcardTags).IsSuccess()); - + AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", ExcludeFile, FileTagAsset::Extension()).c_str(), m_data->m_excludeFile); AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", IncludeFile, FileTagAsset::Extension()).c_str(), m_data->m_includeFile); @@ -184,7 +184,7 @@ namespace UnitTest TEST_F(FileTagTest, FileTags_QueryByAbsoluteFilePath_Valid) { AZStd::string absoluteDummyFilePath = DummyFile; - EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath)); + EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath)); AZStd::set tags = m_data->m_excludeFileQueryManager->GetTags(absoluteDummyFilePath); @@ -196,7 +196,7 @@ namespace UnitTest ASSERT_EQ(tags.size(), 0); AZStd::string absoluteAnotherDummyFilePath = AnotherDummyFile; - EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath)); + EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath)); tags = m_data->m_includeFileQueryManager->GetTags(absoluteAnotherDummyFilePath); ASSERT_EQ(tags.size(), 2); @@ -213,7 +213,7 @@ namespace UnitTest // Set the customized alias AZStd::string customizedAliasFilePath; - const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@"); + const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@"); AzFramework::StringFunc::AssetDatabasePath::Join(assetsAlias, "foo", customizedAliasFilePath); AZ::IO::FileIOBase::GetInstance()->SetAlias("@customizedalias@", customizedAliasFilePath.c_str()); @@ -305,7 +305,7 @@ namespace UnitTest m_data->m_excludeFileQueryManager->ClearData(); EXPECT_TRUE(m_data->m_excludeFileQueryManager->Load(m_data->m_excludeFile)); - + AZStd::set outputTags = m_data->m_excludeFileQueryManager->GetTags(MatchingWildcardFile); EXPECT_EQ(outputTags.size(), 2); diff --git a/Code/Framework/AzFramework/Tests/GenAppDescriptors.cpp b/Code/Framework/AzFramework/Tests/GenAppDescriptors.cpp index 169834249f..665c06b571 100644 --- a/Code/Framework/AzFramework/Tests/GenAppDescriptors.cpp +++ b/Code/Framework/AzFramework/Tests/GenAppDescriptors.cpp @@ -6,81 +6,19 @@ * */ +#include #include #include #include -#include -#include +#include +#include namespace UnitTest { - using namespace AZ; - - class FileIOBaseRAII - { - public: - FileIOBaseRAII(AZ::IO::FileIOBase& fileIO) - : m_prevFileIO(AZ::IO::FileIOBase::GetInstance()) - { - AZ::IO::FileIOBase::SetInstance(&fileIO); - } - - ~FileIOBaseRAII() - { - AZ::IO::FileIOBase::SetInstance(m_prevFileIO); - } - private: - AZ::IO::FileIOBase* m_prevFileIO; - }; - class GenAppDescriptors : public AllocatorsTestFixture { public: - - void run() - { - struct Config - { - const char* platformName; - const char* configName; - const char* libSuffix; - }; - - ComponentApplication app; - - SerializeContext serializeContext; - AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app); - AZ::Entity::Reflect(&serializeContext); - DynamicModuleDescriptor::Reflect(&serializeContext); - - AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity"); - - const Config config = {"Platform", "Config", "libSuffix"}; - - AZ::ComponentApplication::Descriptor descriptor; - - if (config.libSuffix && config.libSuffix[0]) - { - FakePopulateModules(descriptor, config.libSuffix); - } - - const AZStd::string filename = AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName); - - IO::FileIOStream stream(filename.c_str(), IO::OpenMode::ModeWrite); - ObjectStream* objStream = ObjectStream::Create(&stream, serializeContext, ObjectStream::ST_XML); - bool descWriteOk = objStream->WriteClass(&descriptor); - (void)descWriteOk; - AZ_Warning("ComponentApplication", descWriteOk, "Failed to write memory descriptor to application descriptor file %s!", filename.c_str()); - bool entityWriteOk = objStream->WriteClass(&dummySystemEntity); - (void)entityWriteOk; - AZ_Warning("ComponentApplication", entityWriteOk, "Failed to write system entity to application descriptor file %s!", filename.c_str()); - bool flushOk = objStream->Finalize(); - (void)flushOk; - AZ_Warning("ComponentApplication", flushOk, "Failed finalizing application descriptor file %s!", filename.c_str()); - - } - void FakePopulateModules(AZ::ComponentApplication::Descriptor& desc, const char* libSuffix) { static const char* modules[] = @@ -100,10 +38,44 @@ namespace UnitTest } }; - TEST_F(GenAppDescriptors, Test) + TEST_F(GenAppDescriptors, WriteDescriptor_ToXML_Succeeds) { - AZ::IO::LocalFileIO fileIO; - FileIOBaseRAII restoreFileIOScope(fileIO); - run(); + struct Config + { + const char* platformName; + const char* configName; + const char* libSuffix; + }; + + AzFramework::Application app; + + AZ::SerializeContext serializeContext; + AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app); + AZ::Entity::Reflect(&serializeContext); + AZ::DynamicModuleDescriptor::Reflect(&serializeContext); + + AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity"); + + const Config config = {"Platform", "Config", "libSuffix"}; + + AZ::ComponentApplication::Descriptor descriptor; + + if (config.libSuffix && config.libSuffix[0]) + { + FakePopulateModules(descriptor, config.libSuffix); + } + + AZ::Test::ScopedAutoTempDirectory tempDirectory; + const auto filename = AZ::IO::Path(tempDirectory.GetDirectory()) / + AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName); + + AZ::IO::FileIOStream stream(filename.c_str(), AZ::IO::OpenMode::ModeWrite); + auto objStream = AZ::ObjectStream::Create(&stream, serializeContext, AZ::ObjectStream::ST_XML); + const bool descWriteOk = objStream->WriteClass(&descriptor); + EXPECT_TRUE(descWriteOk) << "Failed to write memory descriptor to application descriptor file " << filename.c_str() << "!"; + const bool entityWriteOk = objStream->WriteClass(&dummySystemEntity); + EXPECT_TRUE(entityWriteOk) << "Failed to write system entity to application descriptor file " << filename.c_str() << "!"; + const bool flushOk = objStream->Finalize(); + EXPECT_TRUE(flushOk) << "Failed finalizing application descriptor file " << filename.c_str() << "!"; } } diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h new file mode 100644 index 0000000000..77acb1c3e4 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h @@ -0,0 +1,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 + * + */ + +#pragma once + +#include +#include + +inline testing::PolymorphicMatcher> StrEq(const AZStd::string& str) +{ + return ::testing::MakePolymorphicMatcher(testing::internal::StrEqualityMatcher(str, true, true)); +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp index 64c3967fdc..b15809a4c6 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -28,6 +28,10 @@ xcb_generic_event_t* xcb_poll_for_event(xcb_connection_t* c) { return MockXcbInterface::Instance()->xcb_poll_for_event(c); } +xcb_generic_error_t* xcb_request_check(xcb_connection_t* c, xcb_void_cookie_t cookie) +{ + return MockXcbInterface::Instance()->xcb_request_check(c, cookie); +} // ---------------------------------------------------------------------------- // xcb-xkb @@ -39,6 +43,10 @@ xcb_xkb_use_extension_reply_t* xcb_xkb_use_extension_reply(xcb_connection_t* c, { return MockXcbInterface::Instance()->xcb_xkb_use_extension_reply(c, cookie, e); } +xcb_void_cookie_t xcb_xkb_select_events(xcb_connection_t* c, xcb_xkb_device_spec_t deviceSpec, uint16_t affectWhich, uint16_t clear, uint16_t selectAll, uint16_t affectMap, uint16_t map, const void* details) +{ + return MockXcbInterface::Instance()->xcb_xkb_select_events(c, deviceSpec, affectWhich, clear, selectAll, affectMap, map, details); +} // ---------------------------------------------------------------------------- // xkb-x11 @@ -46,7 +54,7 @@ int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t* connection) { return MockXcbInterface::Instance()->xkb_x11_get_core_keyboard_device_id(connection); } -struct xkb_keymap* xkb_x11_keymap_new_from_device(struct xkb_context* context, xcb_connection_t* connection, int32_t device_id, enum xkb_keymap_compile_flags flags) +xkb_keymap* xkb_x11_keymap_new_from_device(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags) { return MockXcbInterface::Instance()->xkb_x11_keymap_new_from_device(context, connection, device_id, flags); } @@ -54,28 +62,58 @@ xkb_state* xkb_x11_state_new_from_device(xkb_keymap* keymap, xcb_connection_t* c { return MockXcbInterface::Instance()->xkb_x11_state_new_from_device(keymap, connection, device_id); } +int xkb_x11_setup_xkb_extension( + xcb_connection_t* connection, + uint16_t major_xkb_version, + uint16_t minor_xkb_version, + xkb_x11_setup_xkb_extension_flags flags, + uint16_t* major_xkb_version_out, + uint16_t* minor_xkb_version_out, + uint8_t* base_event_out, + uint8_t* base_error_out) +{ + return MockXcbInterface::Instance()->xkb_x11_setup_xkb_extension( + connection, major_xkb_version, minor_xkb_version, flags, major_xkb_version_out, minor_xkb_version_out, base_event_out, + base_error_out); +} // ---------------------------------------------------------------------------- // xkbcommon -xkb_context* xkb_context_new(enum xkb_context_flags flags) +xkb_context* xkb_context_new(xkb_context_flags flags) { return MockXcbInterface::Instance()->xkb_context_new(flags); } -void xkb_context_unref(xkb_context *context) +void xkb_context_unref(xkb_context* context) { return MockXcbInterface::Instance()->xkb_context_unref(context); } -void xkb_keymap_unref(xkb_keymap *keymap) +void xkb_keymap_unref(xkb_keymap* keymap) { return MockXcbInterface::Instance()->xkb_keymap_unref(keymap); } -void xkb_state_unref(xkb_state *state) +void xkb_state_unref(xkb_state* state) { return MockXcbInterface::Instance()->xkb_state_unref(state); } -xkb_keysym_t xkb_state_key_get_one_sym(xkb_state *state, xkb_keycode_t key) +xkb_keysym_t xkb_state_key_get_one_sym(xkb_state* state, xkb_keycode_t key) { return MockXcbInterface::Instance()->xkb_state_key_get_one_sym(state, key); } +int xkb_state_key_get_utf8(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size) +{ + return MockXcbInterface::Instance()->xkb_state_key_get_utf8(state, key, buffer, size); +} +xkb_state_component xkb_state_update_mask( + xkb_state* state, + xkb_mod_mask_t depressed_mods, + xkb_mod_mask_t latched_mods, + xkb_mod_mask_t locked_mods, + xkb_layout_index_t depressed_layout, + xkb_layout_index_t latched_layout, + xkb_layout_index_t locked_layout) +{ + return MockXcbInterface::Instance()->xkb_state_update_mask( + state, depressed_mods, latched_mods, locked_mods, depressed_layout, latched_layout, locked_layout); +} } diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h index ecda005830..b57751344e 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -17,6 +17,7 @@ #include #undef explicit #include +#include #include "Printers.h" @@ -35,6 +36,7 @@ struct xkb_keymap struct xkb_state { + xkb_mod_mask_t m_modifiers{}; }; class MockXcbInterface @@ -48,7 +50,7 @@ public: MockXcbInterface(MockXcbInterface&&) = delete; MockXcbInterface& operator=(const MockXcbInterface&) = delete; MockXcbInterface& operator=(MockXcbInterface&&) = delete; - ~MockXcbInterface() + virtual ~MockXcbInterface() { self = nullptr; } @@ -59,22 +61,27 @@ public: MOCK_CONST_METHOD2(xcb_connect, xcb_connection_t*(const char* displayname, int* screenp)); MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c)); MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c)); + MOCK_CONST_METHOD2(xcb_request_check, xcb_generic_error_t*(xcb_connection_t* c, xcb_void_cookie_t cookie)); // xcb-xkb MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor)); MOCK_CONST_METHOD3(xcb_xkb_use_extension_reply, xcb_xkb_use_extension_reply_t*(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD8(xcb_xkb_select_events, xcb_void_cookie_t(xcb_connection_t* c, xcb_xkb_device_spec_t deviceSpec, uint16_t affectWhich, uint16_t clear, uint16_t selectAll, uint16_t affectMap, uint16_t map, const void* details)); // xkb-x11 MOCK_CONST_METHOD1(xkb_x11_get_core_keyboard_device_id, int32_t(xcb_connection_t* connection)); MOCK_CONST_METHOD4(xkb_x11_keymap_new_from_device, xkb_keymap*(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags)); MOCK_CONST_METHOD3(xkb_x11_state_new_from_device, xkb_state*(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id)); + MOCK_CONST_METHOD8(xkb_x11_setup_xkb_extension, int(xcb_connection_t* connection, uint16_t major_xkb_version, uint16_t minor_xkb_version, xkb_x11_setup_xkb_extension_flags flags, uint16_t* major_xkb_version_out, uint16_t* minor_xkb_version_out, uint8_t* base_event_out, uint8_t* base_error_out)); // xkbcommon MOCK_CONST_METHOD1(xkb_context_new, xkb_context*(xkb_context_flags flags)); MOCK_CONST_METHOD1(xkb_context_unref, void(xkb_context* context)); MOCK_CONST_METHOD1(xkb_keymap_unref, void(xkb_keymap* keymap)); MOCK_CONST_METHOD1(xkb_state_unref, void(xkb_state* state)); - MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state *state, xkb_keycode_t key)); + MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state* state, xkb_keycode_t key)); + MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)); + MOCK_CONST_METHOD7(xkb_state_update_mask, xkb_state_component(xkb_state* state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout)); private: static inline MockXcbInterface* self = nullptr; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.cpp new file mode 100644 index 0000000000..937cb04e14 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.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 "XcbBaseTestFixture.h" + +namespace AzFramework +{ + void XcbBaseTestFixture::SetUp() + { + using testing::Return; + using testing::_; + + testing::Test::SetUp(); + + EXPECT_CALL(m_interface, xcb_connect(_, _)) + .WillOnce(Return(&m_connection)); + EXPECT_CALL(m_interface, xcb_disconnect(&m_connection)) + .Times(1); + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h new file mode 100644 index 0000000000..8e9b008fc1 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.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 "MockXcbInterface.h" + +namespace AzFramework +{ + // Sets up mock behavior for the xcb library, providing an xcb_connection_t that is returned from a call to xcb_connect + class XcbBaseTestFixture + : public testing::Test + { + public: + void SetUp() override; + + protected: + testing::NiceMock m_interface; + xcb_connection_t m_connection{}; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp index 1b494c9525..76d00eda50 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include @@ -13,8 +14,12 @@ #include #include +#include #include "MockXcbInterface.h" +#include "Matchers.h" #include "Actions.h" +#include "XcbBaseTestFixture.h" +#include "XcbTestApplication.h" template xcb_generic_event_t MakeEvent(T event) @@ -24,26 +29,136 @@ xcb_generic_event_t MakeEvent(T event) namespace AzFramework { - TEST(XcbInputDeviceKeyboard, InputChannelsUpdateStateFromXcbEvents) + // Sets up default behavior for mock keyboard responses to xcb methods + class XcbInputDeviceKeyboardTests + : public XcbBaseTestFixture { - using testing::Return; - using testing::Eq; - using testing::_; - MockXcbInterface interface; + public: + void SetUp() override + { + using testing::Return; + using testing::SetArgPointee; + using testing::_; + + XcbBaseTestFixture::SetUp(); + EXPECT_CALL(m_interface, xkb_context_new(XKB_CONTEXT_NO_FLAGS)) + .WillOnce(Return(&m_xkbContext)); + EXPECT_CALL(m_interface, xkb_context_unref(&m_xkbContext)) + .Times(1); + + EXPECT_CALL(m_interface, xkb_x11_keymap_new_from_device(&m_xkbContext, &m_connection, s_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)) + .WillOnce(Return(&m_xkbKeymap)); + EXPECT_CALL(m_interface, xkb_keymap_unref(&m_xkbKeymap)) + .Times(1); + + EXPECT_CALL(m_interface, xkb_x11_state_new_from_device(&m_xkbKeymap, &m_connection, s_coreDeviceId)) + .WillOnce(Return(&m_xkbState)); + EXPECT_CALL(m_interface, xkb_state_unref(&m_xkbState)) + .Times(1); + + ON_CALL(m_interface, xkb_x11_setup_xkb_extension(&m_connection, 1, 0, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, _, _, _, _)) + .WillByDefault(DoAll( + SetArgPointee<6>(s_xkbEventCode), // Set the "base_event_out" argument to the xkbEventCode, the value to identify XKB events + Return(1) + )); + + constexpr unsigned int xcbXkbSelectEventsSequence = 342; + ON_CALL(m_interface, xcb_xkb_select_events(&m_connection, _, _, _, _, _, _, _)) + .WillByDefault(Return(xcb_void_cookie_t{/*.sequence = */ xcbXkbSelectEventsSequence})); + ON_CALL(m_interface, xcb_request_check(&m_connection, testing::Field(&xcb_void_cookie_t::sequence, testing::Eq(xcbXkbSelectEventsSequence)))) + .WillByDefault(Return(nullptr)); // indicates success + + ON_CALL(m_interface, xkb_x11_get_core_keyboard_device_id(&m_connection)) + .WillByDefault(Return(s_coreDeviceId)); + + ON_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey)) + .WillByDefault(Return(XKB_KEY_a)) + ; + ON_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForShiftLKey)) + .WillByDefault(Return(XKB_KEY_Shift_L)) + ; + + ON_CALL(m_interface, xkb_state_update_mask(&m_xkbState, _, _, _, _, _, _)) + .WillByDefault(testing::Invoke(this, &XcbInputDeviceKeyboardTests::UpdateStateMask)); + + ON_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForAKey, nullptr, 0)) + .WillByDefault(Return(1)); + ON_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithoutShift, s_keycodeForAKey, _, 2)) + .WillByDefault(DoAll( + SetArgPointee<2>('a'), + Return(1) + )); + ON_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithShift, s_keycodeForAKey, _, 2)) + .WillByDefault(DoAll( + SetArgPointee<2>('A'), + Return(1) + )); + + ON_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForShiftLKey, nullptr, 0)) + .WillByDefault(Return(0)); + } + + private: + xkb_state_component UpdateStateMask( + xkb_state* state, + xkb_mod_mask_t depressed_mods, + xkb_mod_mask_t latched_mods, + xkb_mod_mask_t locked_mods, + xkb_layout_index_t depressed_layout, + xkb_layout_index_t latched_layout, + xkb_layout_index_t locked_layout) + { + state->m_modifiers = depressed_mods | locked_mods; + return {}; + } + + protected: + xkb_context m_xkbContext{}; + xkb_keymap m_xkbKeymap{}; + xkb_state m_xkbState{}; + const testing::Matcher m_matchesStateWithoutShift = testing::AllOf(&m_xkbState, testing::Field(&xkb_state::m_modifiers, 0)); + const testing::Matcher m_matchesStateWithShift = testing::AllOf(&m_xkbState, testing::Field(&xkb_state::m_modifiers, XCB_MOD_MASK_SHIFT)); + + static constexpr int32_t s_coreDeviceId{1}; + static constexpr uint8_t s_xkbEventCode{85}; + + 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 + }; + }; - xcb_connection_t connection{}; - xkb_context xkbContext{}; - xkb_keymap xkbKeymap{}; - xkb_state xkbState{}; - const int32_t coreDeviceId{1}; + class InputTextNotificationListener + : public InputTextNotificationBus::Handler + { + public: + InputTextNotificationListener() + { + BusConnect(); + } + MOCK_METHOD2(OnInputTextEvent, void(const AZStd::string& /*textUTF8*/, bool& /*o_hasBeenConsumed*/)); + }; - constexpr xcb_keycode_t keycodeForAKey = 38; + TEST_F(XcbInputDeviceKeyboardTests, InputChannelsUpdateStateFromXcbEvents) + { + using testing::DoAll; + using testing::Eq; + using testing::Return; + using testing::SetArgPointee; + using testing::_; const AZStd::array events { MakeEvent(xcb_key_press_event_t{ /*.response_type = */ XCB_KEY_PRESS, - /*.detail = */ keycodeForAKey, + /*.detail = */ s_keycodeForAKey, /*.sequence = */ 0, /*.time = */ 0, /*.root = */ 0, @@ -59,7 +174,7 @@ namespace AzFramework }), MakeEvent(xcb_key_release_event_t{ /*.response_type = */ XCB_KEY_RELEASE, - /*.detail = */ keycodeForAKey, + /*.detail = */ s_keycodeForAKey, /*.sequence = */ 0, /*.time = */ 0, /*.root = */ 0, @@ -75,71 +190,249 @@ namespace AzFramework }), }; - EXPECT_CALL(interface, xcb_connect(_, _)) - .WillOnce(Return(&connection)); - EXPECT_CALL(interface, xcb_disconnect(&connection)) - .Times(1); - - EXPECT_CALL(interface, xkb_context_new(XKB_CONTEXT_NO_FLAGS)) - .WillOnce(Return(&xkbContext)); - EXPECT_CALL(interface, xkb_context_unref(&xkbContext)) - .Times(1); - - EXPECT_CALL(interface, xkb_x11_keymap_new_from_device(&xkbContext, &connection, coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)) - .WillOnce(Return(&xkbKeymap)); - EXPECT_CALL(interface, xkb_keymap_unref(&xkbKeymap)) - .Times(1); - - EXPECT_CALL(interface, xkb_x11_state_new_from_device(&xkbKeymap, &connection, coreDeviceId)) - .WillOnce(Return(&xkbState)); - EXPECT_CALL(interface, xkb_state_unref(&xkbState)) - .Times(1); - - EXPECT_CALL(interface, xcb_xkb_use_extension(&connection, 1, 0)); - EXPECT_CALL(interface, xcb_xkb_use_extension_reply(&connection, _, _)) - .WillOnce(ReturnMalloc( - /* .response_type =*/static_cast(XCB_XKB_USE_EXTENSION), - /* .supported =*/ static_cast(1)) - ); - EXPECT_CALL(interface, xkb_x11_get_core_keyboard_device_id(&connection)) - .WillRepeatedly(Return(coreDeviceId)); - // Set the expectations for the events that will be generated // nullptr entries represent when the event queue is empty, and will cause // PumpSystemEventLoopUntilEmpty to return // event pointers are freed by the calling code, so we malloc new copies // here - EXPECT_CALL(interface, xcb_poll_for_event(&connection)) + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) .WillOnce(ReturnMalloc(events[0])) .WillOnce(Return(nullptr)) .WillOnce(ReturnMalloc(events[1])) .WillOnce(Return(nullptr)) ; - EXPECT_CALL(interface, xkb_state_key_get_one_sym(&xkbState, keycodeForAKey)) - .WillOnce(Return(XKB_KEY_a)) - .WillOnce(Return(XKB_KEY_a)) - ; + 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) + { + using testing::DoAll; + using testing::Eq; + using testing::Return; + using testing::SetArgPointee; + using testing::_; + + // press a + // release a + // press shift + // press a + // release a + // release shift + const AZStd::array events + { + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + // Pressing a modifier key will generate a key press event followed + // by a state notify event + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ s_keycodeForShiftLKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_xkb_state_notify_event_t{ + /*.response_type = */ s_xkbEventCode, + /*.xkbType = */ XCB_XKB_STATE_NOTIFY, + /*.sequence = */ 0, + /*.time = */ 0, + /*.deviceID = */ s_coreDeviceId, + /*.mods = */ XCB_MOD_MASK_SHIFT, + /*.baseMods = */ XCB_MOD_MASK_SHIFT, + /*.latchedMods = */ 0, + /*.lockedMods = */ 0, + /*.group = */ 0, + /*.baseGroup = */ 0, + /*.latchedGroup = */ 0, + /*.lockedGroup = */ 0, + /*.compatState = */ XCB_MOD_MASK_SHIFT, + /*.grabMods = */ XCB_MOD_MASK_SHIFT, + /*.compatGrabMods = */ XCB_MOD_MASK_SHIFT, + /*.lookupMods = */ XCB_MOD_MASK_SHIFT, + /*.compatLoockupMods = */ XCB_MOD_MASK_SHIFT, + /*.ptrBtnState = */ 0, + /*.changed = */ 0, + /*.keycode = */ s_keycodeForShiftLKey, + /*.eventType = */ XCB_KEY_PRESS, + /*.requestMajor = */ 0, + /*.requestMinor = */ 0, + }), + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ s_keycodeForShiftLKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_xkb_state_notify_event_t{ + /*.response_type = */ s_xkbEventCode, + /*.xkbType = */ XCB_XKB_STATE_NOTIFY, + /*.sequence = */ 0, + /*.time = */ 0, + /*.deviceID = */ s_coreDeviceId, + /*.mods = */ 0, + /*.baseMods = */ 0, + /*.latchedMods = */ 0, + /*.lockedMods = */ 0, + /*.group = */ 0, + /*.baseGroup = */ 0, + /*.latchedGroup = */ 0, + /*.lockedGroup = */ 0, + /*.compatState = */ 0, + /*.grabMods = */ 0, + /*.compatGrabMods = */ 0, + /*.lookupMods = */ 0, + /*.compatLoockupMods = */ 0, + /*.ptrBtnState = */ 0, + /*.changed = */ 0, + /*.keycode = */ s_keycodeForShiftLKey, + /*.eventType = */ XCB_KEY_RELEASE, + /*.requestMajor = */ 0, + /*.requestMinor = */ 0, + }), + }; + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // event pointers are freed by the calling code, so we malloc new copies + // here + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) + .WillOnce(ReturnMalloc(events[0])) // press a + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[1])) // release a + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[2])) // press shift + .WillOnce(ReturnMalloc(events[3])) // state notify shift is down + .WillOnce(ReturnMalloc(events[4])) // press a + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[5])) // release a + .WillOnce(ReturnMalloc(events[6])) // release shift + .WillOnce(ReturnMalloc(events[7])) // state notify shift is up + .WillRepeatedly(Return(nullptr)) + ; + + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForAKey, nullptr, 0)) + .Times(2); + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithoutShift, s_keycodeForAKey, _, 2)) + .Times(1); + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithShift, s_keycodeForAKey, _, 2)) + .Times(1); + + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForShiftLKey, nullptr, 0)) + .Times(1); + + InputTextNotificationListener textListener; + EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1); + EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1); + + m_application.Start(); + + for (int i = 0; i < 4; ++i) + { + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); + } } } // 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 aebd28222b..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 @@ -9,9 +9,13 @@ set(FILES Actions.h Main.cpp + Matchers.h MockXcbInterface.cpp MockXcbInterface.h Printers.cpp Printers.h + XcbBaseTestFixture.cpp + XcbBaseTestFixture.h XcbInputDeviceKeyboardTests.cpp + XcbTestApplication.h ) diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 407ab1d21f..2dc32d14d5 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -569,11 +569,12 @@ namespace UnitTest FillSpawnable(NumEntities); CreateEntityReferences(refScheme); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + AZ_POP_DISABLE_WARNING { - AZ_UNUSED(refScheme); - AZ_UNUSED(NumEntities); ValidateEntityReferences(refScheme, NumEntities, entities); }; @@ -591,11 +592,12 @@ namespace UnitTest FillSpawnable(NumEntities); CreateEntityReferences(refScheme); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + AZ_POP_DISABLE_WARNING { - AZ_UNUSED(refScheme); - AZ_UNUSED(NumEntities); ValidateEntityReferences(refScheme, NumEntities, entities); }; @@ -720,11 +722,12 @@ namespace UnitTest FillSpawnable(NumEntities); CreateEntityReferences(refScheme); + AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the + // capture. Newer versions issue unused warning auto callback = [this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + AZ_POP_DISABLE_WARNING { - AZ_UNUSED(refScheme); - AZ_UNUSED(NumEntities); ValidateEntityReferences(refScheme, NumEntities, entities); }; diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index 424132b624..0cce93d751 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -38,12 +38,12 @@ namespace AzGameFramework { // fall back to checking Project Cache Root. enginePakPath /= "engine.pak"; - enginePakOpened = m_archive->OpenPack("@projectproductassets@", enginePakPath.Native()); + enginePakOpened = m_archive->OpenPack("@products@", enginePakPath.Native()); } if (!enginePakOpened) { enginePakPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "engine.pak"; - m_archive->OpenPack("@projectproductassets@", enginePakPath.Native()); + m_archive->OpenPack("@products@", enginePakPath.Native()); } } @@ -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/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp b/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp index 3fdfa042a5..9f496c3e0c 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp @@ -107,7 +107,7 @@ namespace AzNetworking if (AZ::IO::FileIOBase::GetInstance() != nullptr) { char buffer[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@assets@/", buffer, sizeof(buffer)); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/", buffer, sizeof(buffer)); assetDir = AZStd::string(buffer); } diff --git a/Code/Framework/AzNetworking/CMakeLists.txt b/Code/Framework/AzNetworking/CMakeLists.txt index 7f9d856eb9..a0a3871201 100644 --- a/Code/Framework/AzNetworking/CMakeLists.txt +++ b/Code/Framework/AzNetworking/CMakeLists.txt @@ -25,7 +25,6 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE AZ::AzCore - 3rdParty::zlib 3rdParty::zstd 3rdParty::OpenSSL PUBLIC 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/AzTest/AzTest/AzTest.cpp b/Code/Framework/AzTest/AzTest/AzTest.cpp index 3b809d2c96..85e7f7e6b3 100644 --- a/Code/Framework/AzTest/AzTest/AzTest.cpp +++ b/Code/Framework/AzTest/AzTest/AzTest.cpp @@ -90,19 +90,16 @@ namespace AZ } } - //! Filter out integration tests from the test run - void excludeIntegTests() - { - AddExcludeFilter("INTEG_*"); - AddExcludeFilter("Integ_*"); - } - void ApplyGlobalParameters(int* argc, char** argv) { - // this is a hook that can be used to apply any other global non-google parameters - // that we use. + // this is a hook that can be used to apply any other global parameters that we use. AZ_UNUSED(argc); AZ_UNUSED(argv); + + // Disable gtest catching unhandled exceptions, instead, AzTestRunner will do it through: + // AZ::Debug::Trace::HandleExceptions(true). This gives us a stack trace when the exception + // is thrown (googletest does not). + testing::FLAGS_gtest_catch_exceptions = false; } //! Print out parameters that are not used by the framework @@ -160,7 +157,6 @@ namespace AZ } ::testing::InitGoogleMock(&argc, argv); - AZ::Test::excludeIntegTests(); AZ::Test::ApplyGlobalParameters(&argc, argv); AZ::Test::printUnusedParametersWarning(argc, argv); AZ::Test::addTestEnvironments(m_envs); @@ -281,7 +277,6 @@ namespace AZ } } - AZ::Test::excludeIntegTests(); AZ::Test::printUnusedParametersWarning(argc, argv); return RUN_ALL_TESTS(); diff --git a/Code/Framework/AzTest/AzTest/AzTest.h b/Code/Framework/AzTest/AzTest/AzTest.h index 352db1a0b5..4b038cabd4 100644 --- a/Code/Framework/AzTest/AzTest/AzTest.h +++ b/Code/Framework/AzTest/AzTest/AzTest.h @@ -104,7 +104,6 @@ namespace AZ void addTestEnvironment(ITestEnvironment* env); void addTestEnvironments(std::vector envs); - void excludeIntegTests(); //! A hook that can be used to read any other misc parameters and remove them before google sees them. //! Note that this modifies argc and argv to delete the parameters it consumes. @@ -266,7 +265,6 @@ namespace AZ ::testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners(); \ listeners.Append(new AZ::Test::OutputEventListener); \ } \ - AZ::Test::excludeIntegTests(); \ AZ::Test::ApplyGlobalParameters(&argc, argv); \ AZ::Test::printUnusedParametersWarning(argc, argv); \ AZ::Test::addTestEnvironments({TEST_ENV}); \ 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/EditorAssetSystemAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h index eab2d5e1a0..c18ddc2f67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h @@ -47,14 +47,6 @@ namespace AzToolsFramework //! Retrieve the absolute path for the Asset Database Location virtual bool GetAbsoluteAssetDatabaseLocation(AZStd::string& /*result*/) { return false; } - - //! Retrieve the absolute folder path to the current game's source assets (the ones that go into source control) - //! This may include the current mod path, if a mod is being edited by the editor - virtual const char* GetAbsoluteDevGameFolderPath() = 0; - - //! Retrieve the absolute folder path to the current developer root ('dev'), which contains source artifacts - //! and is generally checked into source control. - virtual const char* GetAbsoluteDevRootFolderPath() = 0; /// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path. /// asset paths never mention their alias and are relative to the asset cache root 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..966b9f8478 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. @@ -41,8 +43,7 @@ namespace AzToolsFramework }; //! Provides a bus to notify when the different editor modes are entered/exit. - class ViewportEditorModeNotifications - : public AZ::EBusTraits + class ViewportEditorModeNotifications : public AZ::EBusTraits { public: ////////////////////////////////////////////////////////////////////////// @@ -52,15 +53,21 @@ 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) + virtual void OnEditorModeActivated( + [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) { } //! Notifies subscribers of the a given viewport to the deactivation of the specified editor mode. - virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) + virtual void OnEditorModeDeactivated( + [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) { } }; + using ViewportEditorModeNotificationsBus = AZ::EBus; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 717e0c6f8a..fbd066ec6e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ #include #include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer' needs to have dll-interface to be used by clients of class 'QFileInfo' @@ -251,6 +253,7 @@ namespace AzToolsFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), @@ -269,7 +272,8 @@ namespace AzToolsFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), - azrtti_typeid() + azrtti_typeid(), + azrtti_typeid() }); return components; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp index cc3153ed8e..86b32d4379 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp @@ -354,26 +354,6 @@ namespace AzToolsFramework } } - const char* AssetSystemComponent::GetAbsoluteDevGameFolderPath() - { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO) - { - return fileIO->GetAlias("@devassets@"); - } - return ""; - } - - const char* AssetSystemComponent::GetAbsoluteDevRootFolderPath() - { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO) - { - return fileIO->GetAlias("@devroot@"); - } - return ""; - } - void AssetSystemComponent::OnSystemTick() { AssetSystemBus::ExecuteQueuedEvents(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h index 4774b96be4..fcd697393c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h @@ -56,8 +56,6 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::AssetSystemRequestBus::Handler overrides bool GetAbsoluteAssetDatabaseLocation(AZStd::string& result) override; - const char* GetAbsoluteDevGameFolderPath() override; - const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override; bool GenerateRelativeSourcePath( const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 55dcbb1532..f84e6bd81c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -53,7 +53,7 @@ namespace AzToolsFramework private slots: void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: - int m_numberOfItemsDisplayed = 50; + AZ::u64 m_numberOfItemsDisplayed = 0; int m_displayedItemsCounter = 0; QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp index e09dd183f8..a159882f72 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp @@ -137,7 +137,7 @@ namespace AzToolsFramework AssetEditorWidgetUserSettings::AssetEditorWidgetUserSettings() { char assetRoot[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", assetRoot, AZ_MAX_PATH_LEN); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", assetRoot, AZ_MAX_PATH_LEN); m_lastSavePath = assetRoot; } @@ -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/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index b68d086892..d2a88df544 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include AZ_DEFINE_BUDGET(AzToolsFramework); @@ -70,6 +72,8 @@ namespace AzToolsFramework Components::EditorSelectionAccentSystemComponent::CreateDescriptor(), EditorEntityContextComponent::CreateDescriptor(), EditorEntityFixupComponent::CreateDescriptor(), + EntityUtilityComponent::CreateDescriptor(), + ContainerEntitySystemComponent::CreateDescriptor(), FocusModeSystemComponent::CreateDescriptor(), SliceMetadataEntityContextComponent::CreateDescriptor(), SliceRequestComponent::CreateDescriptor(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp index df93e924b8..07e025fc60 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp @@ -137,6 +137,7 @@ namespace AzToolsFramework if (componentTypeIt == m_activeComponentTypes.end()) { m_activeComponentTypes.push_back(componentType); + m_viewportUiHandlers.emplace_back(componentType); } // see if we already have a ComponentModeBuilder for the specific component on this entity @@ -203,6 +204,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 +218,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 @@ -226,6 +226,7 @@ namespace AzToolsFramework if (!m_entitiesAndComponentModes.empty()) { RefreshActions(); + PopulateViewportUi(); } // if entering ComponentMode not as an undo/redo step (an action was @@ -286,16 +287,12 @@ namespace AzToolsFramework componentModeCommand.release(); } + // remove the component mode viewport border + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder); + // 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 @@ -310,6 +307,7 @@ namespace AzToolsFramework } m_entitiesAndComponentModeBuilders.clear(); m_activeComponentTypes.clear(); + m_viewportUiHandlers.clear(); m_componentMode = false; m_selectedComponentModeIndex = 0; @@ -394,6 +392,24 @@ namespace AzToolsFramework return m_activeComponentTypes.size() > 1; } + static ComponentModeViewportUi* FindViewportUiHandlerForType( + AZStd::vector& viewportUiHandlers, const AZ::Uuid& componentType) + { + auto handler = AZStd::find_if( + viewportUiHandlers.begin(), viewportUiHandlers.end(), + [componentType](const ComponentModeViewportUi& handler) + { + return handler.GetComponentType() == componentType; + }); + + if (handler == viewportUiHandlers.end()) + { + return nullptr; + } + + return handler; + } + bool ComponentModeCollection::ActiveComponentModeChanged(const AZ::Uuid& previousComponentType) { if (m_activeComponentTypes[m_selectedComponentModeIndex] != previousComponentType) @@ -419,6 +435,20 @@ namespace AzToolsFramework // replace the current component mode by invoking the builder // for the new 'active' component mode componentMode.m_componentMode = componentModeBuilder->m_componentModeBuilder(); + + // populate the viewport UI with the new component mode + PopulateViewportUi(); + + // set the appropriate viewportUiHandler to active + if (auto viewportUiHandler = + FindViewportUiHandlerForType(m_viewportUiHandlers, m_activeComponentTypes[m_selectedComponentModeIndex])) + { + viewportUiHandler->SetComponentModeViewportUiActive(true); + } + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, + componentMode.m_componentMode->GetComponentModeName().c_str()); } RefreshActions(); @@ -528,5 +558,18 @@ namespace AzToolsFramework } } + void ComponentModeCollection::PopulateViewportUi() + { + // update viewport UI for new component type + if (m_selectedComponentModeIndex < m_activeComponentTypes.size()) + { + // iterate over all entities and their active Component Mode, populate viewport UI for the new mode + for (auto& entityAndComponentMode : m_entitiesAndComponentModes) + { + // build viewport UI based on current state + entityAndComponentMode.m_componentMode->PopulateViewportUi(); + } + } + } } // namespace ComponentModeFramework } // namespace AzToolsFramework 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/EditorBaseComponentMode.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp index 9e043a5c86..f449478306 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp @@ -55,7 +55,7 @@ namespace AzToolsFramework GetEntityComponentIdPair(), elementIdsToDisplay); // create the component mode border with the specific name for this component mode ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateComponentModeBorder, + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, GetComponentModeName()); // set the EntityComponentId for this ComponentMode to active in the ComponentModeViewportUi system ComponentModeViewportUiRequestBus::Event( 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 new file mode 100644 index 0000000000..2d7d9dc511 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h @@ -0,0 +1,73 @@ +/* + * 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 +{ + //! Outcome object that returns an error message in case of failure to allow caller to handle internal errors. + using ContainerEntityOperationResult = AZ::Outcome; + + //! ContainerEntityInterface + //! An entity registered as Container is just like a regular entity when open. If its state is changed + //! to closed, all descendants of the entity will be treated as part of the entity itself. Selecting any + //! descendant will result in the container being selected, and descendants will be hidden until the + //! container is opened. + class ContainerEntityInterface + { + public: + AZ_RTTI(ContainerEntityInterface, "{0A877C3A-726C-4FD2-BAFE-A2B9F1DE78E4}"); + + //! Registers the entity as a container. The container will be closed by default. + //! @param entityId The entityId that will be registered as a container. + virtual ContainerEntityOperationResult RegisterEntityAsContainer(AZ::EntityId entityId) = 0; + + //! Unregisters the entity as a container. + //! The system will retain the closed state in case the entity is registered again later, but + //! if queried the entity will no longer behave as a container. + //! @param entityId The entityId that will be unregistered as a container. + virtual ContainerEntityOperationResult UnregisterEntityAsContainer(AZ::EntityId entityId) = 0; + + //! Returns whether the entity id provided is registered as a container. + virtual bool IsContainer(AZ::EntityId entityId) const = 0; + + //! Sets the open state of the container entity provided. + //! @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 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 + //! verifying whether the entityId is registered as a container beforehand, since the container's + //! open behavior is exactly the same as the one of a regular entity. + //! @return False if the entityId is registered as a container, and its state is closed. True otherwise. + virtual bool IsContainerOpen(AZ::EntityId entityId) const = 0; + + //! Detects if one of the ancestors of entityId is a closed container entity. + //! @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/ContainerEntityNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h new file mode 100644 index 0000000000..95608feefb --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h @@ -0,0 +1,41 @@ +/* + * 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 +{ + //! Used to notify changes of state for Container Entities. + class ContainerEntityNotifications + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = AzFramework::EntityContextId; + ////////////////////////////////////////////////////////////////////////// + + //! Triggered when a container entity status changes. + //! @param entityId The entity whose status has changed. + //! @param open The open state the container was changed to. + virtual void OnContainerEntityStatusChanged([[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] bool open) {} + + protected: + ~ContainerEntityNotifications() = default; + }; + + using ContainerEntityNotificationBus = AZ::EBus; + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp new file mode 100644 index 0000000000..61b257a189 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp @@ -0,0 +1,200 @@ +/* + * 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 + +namespace AzToolsFramework +{ + void ContainerEntitySystemComponent::Activate() + { + AZ::Interface::Register(this); + EditorEntityContextNotificationBus::Handler::BusConnect(); + } + + void ContainerEntitySystemComponent::Deactivate() + { + EditorEntityContextNotificationBus::Handler::BusDisconnect(); + AZ::Interface::Unregister(this); + } + + void ContainerEntitySystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context) + { + } + + void ContainerEntitySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("ContainerEntityService")); + } + + ContainerEntityOperationResult ContainerEntitySystemComponent::RegisterEntityAsContainer(AZ::EntityId entityId) + { + if (IsContainer(entityId)) + { + return AZ::Failure(AZStd::string( + "ContainerEntitySystemComponent error - trying to register entity as container twice.")); + } + + m_containers.insert(entityId); + + return AZ::Success(); + } + + ContainerEntityOperationResult ContainerEntitySystemComponent::UnregisterEntityAsContainer(AZ::EntityId entityId) + { + if (!IsContainer(entityId)) + { + return AZ::Failure(AZStd::string( + "ContainerEntitySystemComponent error - trying to unregister entity that is not a container.")); + } + + m_containers.erase(entityId); + + return AZ::Success(); + } + + bool ContainerEntitySystemComponent::IsContainer(AZ::EntityId entityId) const + { + return m_containers.contains(entityId); + } + + ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpen(AZ::EntityId entityId, bool open) + { + if (!IsContainer(entityId)) + { + return AZ::Failure(AZStd::string( + "ContainerEntitySystemComponent error - cannot set open state of entity that was not registered as container.")); + } + + if(open) + { + m_openContainers.insert(entityId); + } + else + { + m_openContainers.erase(entityId); + } + + ContainerEntityNotificationBus::Broadcast(&ContainerEntityNotificationBus::Events::OnContainerEntityStatusChanged, entityId, open); + + return AZ::Success(); + } + + bool ContainerEntitySystemComponent::IsContainerOpen(AZ::EntityId entityId) const + { + // Non-container entities behave the same as open containers. This saves the caller an additional check. + if(!m_containers.contains(entityId)) + { + return true; + } + + // If the entity is a container, return its state. + return m_openContainers.contains(entityId); + } + + 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()) + { + if (!IsContainerOpen(entityId)) + { + // If one of the ancestors is a container and it's closed, keep track of its id. + // We only return of the higher closed container in the hierarchy. + highestSelectableEntityId = entityId; + } + + AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId); + } + + 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 new file mode 100644 index 0000000000..7a11e05096 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.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 +#include + +namespace AzToolsFramework +{ + //! System Component to track Container Entity registration and open state. + //! An entity registered as Container is just like a regular entity when open. If its state is changed + //! to closed, all descendants of the entity will be treated as part of the entity itself. Selecting any + //! descendant will result in the container being selected, and descendants will be hidden until the + //! container is opened. + class ContainerEntitySystemComponent final + : public AZ::Component + , private ContainerEntityInterface + , private EditorEntityContextNotificationBus::Handler + { + public: + AZ_COMPONENT(ContainerEntitySystemComponent, "{74349759-B36B-44A6-B89F-F45D7111DD11}"); + + ContainerEntitySystemComponent() = default; + virtual ~ContainerEntitySystemComponent() = default; + + // AZ::Component overrides ... + void Activate() override; + void Deactivate() override; + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + + // ContainerEntityInterface overrides ... + ContainerEntityOperationResult RegisterEntityAsContainer(AZ::EntityId entityId) override; + ContainerEntityOperationResult UnregisterEntityAsContainer(AZ::EntityId entityId) override; + bool IsContainer(AZ::EntityId entityId) const 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. + AZStd::unordered_set m_openContainers; //!< All entities in this set are open containers. + }; + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Debug/TraceContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Debug/TraceContext.h index 988963eaca..3ba826804d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Debug/TraceContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Debug/TraceContext.h @@ -39,7 +39,7 @@ namespace AzToolsFramework // the TraceContextLogFormatter. // // Usage example: - // const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@devassets@"); + // const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@projectroot@"); // AZ_TraceContext("Game folder", gameFolder); // // for (int i=0; i; 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); + if (!visible) + { + return false; + } + + // Detect if the Entity is Locked bool locked = false; - EditorEntityInfoRequestBus::EventResult( - locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + 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; + } - return visible && !locked; + // 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/Entity/EntityUtilityComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp new file mode 100644 index 0000000000..7fb998f012 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp @@ -0,0 +1,351 @@ +/* + * 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 + +namespace AzToolsFramework +{ + void ComponentDetails::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("TypeInfo", &ComponentDetails::m_typeInfo) + ->Field("BaseClasses", &ComponentDetails::m_baseClasses); + + serializeContext->RegisterGenericType>(); + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "entity") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Property("TypeInfo", BehaviorValueProperty(&ComponentDetails::m_typeInfo)) + ->Property("BaseClasses", BehaviorValueProperty(&ComponentDetails::m_baseClasses)) + ->Method("__repr__", [](const ComponentDetails& obj) + { + std::ostringstream result; + bool first = true; + + for (const auto& baseClass : obj.m_baseClasses) + { + if (!first) + { + result << ", "; + } + + first = false; + result << baseClass.c_str(); + } + + return AZStd::string::format("%s, Base Classes: <%s>", obj.m_typeInfo.c_str(), result.str().c_str()); + }) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString); + } + } + + AZ::EntityId EntityUtilityComponent::CreateEditorReadyEntity(const AZStd::string& entityName) + { + auto* newEntity = m_entityContext->CreateEntity(entityName.c_str()); + + if (!newEntity) + { + AZ_Error("EditorEntityUtility", false, "Failed to create new entity %s", entityName.c_str()); + return AZ::EntityId(); + } + + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *newEntity); + + newEntity->Init(); + auto newEntityId = newEntity->GetId(); + + m_createdEntities.emplace_back(newEntityId); + + return newEntityId; + } + + AZ::TypeId GetComponentTypeIdFromName(const AZStd::string& typeName) + { + // Try to create a TypeId first. We won't show any warnings if this fails as the input might be a class name instead + AZ::TypeId typeId = AZ::TypeId::CreateStringPermissive(typeName.data()); + + // If the typeId is null, try a lookup by class name + if (typeId.IsNull()) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + auto typeNameCrc = AZ::Crc32(typeName.data()); + auto typeUuidList = serializeContext->FindClassId(typeNameCrc); + + // TypeId is invalid or class name is invalid + if (typeUuidList.empty()) + { + AZ_Error("EntityUtilityComponent", false, "Provided type %s is either an invalid TypeId or does not match any class names", typeName.c_str()); + return AZ::TypeId::CreateNull(); + } + + typeId = typeUuidList[0]; + } + + return typeId; + } + + AZ::Component* FindComponentHelper(AZ::EntityId entityId, const AZ::TypeId& typeId, AZ::ComponentId componentId, bool createComponent = false) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId); + + if (!entity) + { + AZ_Error("EntityUtilityComponent", false, "Invalid entityId %s", entityId.ToString().c_str()); + return nullptr; + } + + AZ::Component* component = nullptr; + if (componentId != AZ::InvalidComponentId) + { + component = entity->FindComponent(componentId); + } + else + { + component = entity->FindComponent(typeId); + } + + if (!component && createComponent) + { + component = entity->CreateComponent(typeId); + } + + if (!component) + { + AZ_Error( + "EntityUtilityComponent", false, "Failed to find component (%s) on entity %s (%s)", + componentId != AZ::InvalidComponentId ? AZStd::to_string(componentId).c_str() + : typeId.ToString().c_str(), + entityId.ToString().c_str(), + entity->GetName().c_str()); + return nullptr; + } + + return component; + } + + AzFramework::BehaviorComponentId EntityUtilityComponent::GetOrAddComponentByTypeName(AZ::EntityId entityId, const AZStd::string& typeName) + { + AZ::TypeId typeId = GetComponentTypeIdFromName(typeName); + + if (typeId.IsNull()) + { + return AzFramework::BehaviorComponentId(AZ::InvalidComponentId); + } + + AZ::Component* component = FindComponentHelper(entityId, typeId, AZ::InvalidComponentId, true); + + return component ? AzFramework::BehaviorComponentId(component->GetId()) : + AzFramework::BehaviorComponentId(AZ::InvalidComponentId); + } + + bool EntityUtilityComponent::UpdateComponentForEntity(AZ::EntityId entityId, AzFramework::BehaviorComponentId componentId, const AZStd::string& json) + { + if (!componentId.IsValid()) + { + AZ_Error("EntityUtilityComponent", false, "Invalid componentId passed to UpdateComponentForEntity"); + return false; + } + + AZ::Component* component = FindComponentHelper(entityId, AZ::TypeId::CreateNull(), componentId); + + if (!component) + { + return false; + } + + using namespace AZ::JsonSerializationResult; + + AZ::JsonDeserializerSettings settings = AZ::JsonDeserializerSettings{}; + settings.m_reporting = []([[maybe_unused]] AZStd::string_view message, ResultCode result, AZStd::string_view) -> auto + { + if (result.GetProcessing() == Processing::Halted) + { + AZ_Error("EntityUtilityComponent", false, "JSON %s\n", message.data()); + } + else if (result.GetOutcome() > Outcomes::PartialDefaults) + { + AZ_Warning("EntityUtilityComponent", false, "JSON %s\n", message.data()); + } + return result; + }; + + rapidjson::Document doc; + doc.Parse(json.data(), json.size()); + ResultCode resultCode = AZ::JsonSerialization::Load(*component, doc, settings); + + return resultCode.GetProcessing() != Processing::Halted; + } + + AZStd::string EntityUtilityComponent::GetComponentDefaultJson(const AZStd::string& typeName) + { + AZ::TypeId typeId = GetComponentTypeIdFromName(typeName); + + if (typeId.IsNull()) + { + // GetComponentTypeIdFromName already does error handling + return ""; + } + + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId); + + if (!classData) + { + AZ_Error("EntityUtilityComponent", false, "Failed to find ClassData for typeId %s (%s)", typeId.ToString().c_str(), typeName.c_str()); + return ""; + } + + void* component = classData->m_factory->Create("Component"); + rapidjson::Document document; + AZ::JsonSerializerSettings settings; + settings.m_keepDefaults = true; + + auto resultCode = AZ::JsonSerialization::Store(document, document.GetAllocator(), component, nullptr, typeId, settings); + + // Clean up the allocated component ASAP, we don't need it anymore + classData->m_factory->Destroy(component); + + if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Error("EntityUtilityComponent", false, "Failed to serialize component to json (%s): %s", + typeName.c_str(), resultCode.ToString(typeName).c_str()) + return ""; + } + + AZStd::string jsonString; + AZ::Outcome outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString); + + if (!outcome.IsSuccess()) + { + AZ_Error("EntityUtilityComponent", false, "Failed to write component json to string: %s", outcome.GetError().c_str()); + return ""; + } + + return jsonString; + } + + AZStd::vector EntityUtilityComponent::FindMatchingComponents(const AZStd::string& searchTerm) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + if (m_typeInfo.empty()) + { + serializeContext->EnumerateDerived( + [this, serializeContext](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid& /*typeId*/) + { + auto& typeInfo = m_typeInfo.emplace_back(classData->m_typeId, classData->m_name, AZStd::vector{}); + + serializeContext->EnumerateBase( + [&typeInfo](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) + { + if (classData) + { + AZStd::get<2>(typeInfo).emplace_back(classData->m_name); + } + return true; + }, + classData->m_typeId); + + return true; + }); + } + + AZStd::vector matches; + + for (const auto& [typeId, typeName, baseClasses] : m_typeInfo) + { + if (AZStd::wildcard_match(searchTerm, typeName)) + { + ComponentDetails details; + details.m_typeInfo = AZStd::string::format("%s %s", typeId.ToString().c_str(), typeName.c_str()); + details.m_baseClasses = baseClasses; + + matches.emplace_back(AZStd::move(details)); + } + } + + return matches; + } + + void EntityUtilityComponent::ResetEntityContext() + { + for (AZ::EntityId entityId : m_createdEntities) + { + m_entityContext->DestroyEntityById(entityId); + } + + m_createdEntities.clear(); + m_entityContext->ResetContext(); + } + + void EntityUtilityComponent::Reflect(AZ::ReflectContext* context) + { + ComponentDetails::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + + if (auto* behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("InvalidComponentId", BehaviorConstant(AZ::InvalidComponentId)) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Entity") + ->Attribute(AZ::Script::Attributes::Module, "entity"); + + behaviorContext->EBus("EntityUtilityBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Category, "Entity") + ->Attribute(AZ::Script::Attributes::Module, "entity") + ->Event("CreateEditorReadyEntity", &EntityUtilityBus::Events::CreateEditorReadyEntity) + ->Event("GetOrAddComponentByTypeName", &EntityUtilityBus::Events::GetOrAddComponentByTypeName) + ->Event("UpdateComponentForEntity", &EntityUtilityBus::Events::UpdateComponentForEntity) + ->Event("FindMatchingComponents", &EntityUtilityBus::Events::FindMatchingComponents) + ->Event("GetComponentDefaultJson", &EntityUtilityBus::Events::GetComponentDefaultJson) + ; + } + } + + void EntityUtilityComponent::Activate() + { + m_entityContext = AZStd::make_unique(UtilityEntityContextId); + m_entityContext->InitContext(); + EntityUtilityBus::Handler::BusConnect(); + } + + void EntityUtilityComponent::Deactivate() + { + EntityUtilityBus::Handler::BusDisconnect(); + m_entityContext = nullptr; + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h new file mode 100644 index 0000000000..af7c353163 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h @@ -0,0 +1,90 @@ +/* + * 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 AzToolsFramework +{ + struct ComponentDetails + { + AZ_TYPE_INFO(AzToolsFramework::ComponentDetails, "{107D8379-4AD4-4547-BEE1-184B120F23E9}"); + + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_typeInfo; + AZStd::vector m_baseClasses; + }; + + // This ebus is intended to provide behavior-context friendly APIs to create and manage entities + struct EntityUtilityTraits : AZ::EBusTraits + { + AZ_RTTI(AzToolsFramework::EntityUtilityTraits, "{A6305CAE-C825-43F9-A44D-E503910912AF}"); + + virtual ~EntityUtilityTraits() = default; + + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + + // Creates an entity with the default editor components attached and initializes the entity + virtual AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) = 0; + + virtual AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) = 0; + + virtual bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) = 0; + + // Gets a JSON string containing describing the default serialization state of the specified component + virtual AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) = 0; + + // Returns a list of matching component type names. Supports wildcard search terms + virtual AZStd::vector FindMatchingComponents(const AZStd::string& searchTerm) = 0; + + virtual void ResetEntityContext() = 0; + }; + + using EntityUtilityBus = AZ::EBus; + + struct EntityUtilityComponent : AZ::Component + , EntityUtilityBus::Handler + { + inline const static AZ::Uuid UtilityEntityContextId = AZ::Uuid("{9C277B88-E79E-4F8A-BAFF-A4C175BD565F}"); + + AZ_COMPONENT(EntityUtilityComponent, "{47205907-A0EA-4FFF-A620-04D20C04A379}"); + + AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) override; + AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) override; + bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) override; + AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) override; + AZStd::vector FindMatchingComponents(const AZStd::string& searchTerm) override; + void ResetEntityContext() override; + + static void Reflect(AZ::ReflectContext* context); + + protected: + void Activate() override; + void Deactivate() override; + + // Our own entity context. This API is intended mostly for use in Asset Builders where there is no editor context + // Additionally, an entity context is needed when using the Behavior Entity class + AZStd::unique_ptr m_entityContext; + + // TypeId, TypeName, Vector + AZStd::vector>> m_typeInfo; + + // Keep track of the entities we create so they can be reset + AZStd::vector m_createdEntities; + }; +}; // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 75e3bab60f..5455e8d773 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -10,6 +10,7 @@ #include #include +#include #include #include 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..44ff603c0c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -71,11 +71,7 @@ namespace AzToolsFramework return; } - m_focusRoot = entityId; - FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot); - - if (auto tracker = AZ::Interface::Get(); - tracker != nullptr) + if (auto tracker = AZ::Interface::Get()) { if (!m_focusRoot.IsValid() && entityId.IsValid()) { @@ -86,6 +82,10 @@ namespace AzToolsFramework tracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus); } } + + AZ::EntityId previousFocusEntityId = m_focusRoot; + m_focusRoot = entityId; + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); } void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) 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/Logger/TraceLogger.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp index e81aaed6c9..ce73826388 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp @@ -66,7 +66,7 @@ namespace AzToolsFramework StringFunc::Path::Join(resolveBuffer, "log", logDirectory); fileIO->SetAlias("@log@", logDirectory.c_str()); - fileIO->CreatePath("@root@"); + fileIO->CreatePath("@products@"); fileIO->CreatePath("@user@"); fileIO->CreatePath("@log@"); 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..a84d6bf706 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, false); + } + } + }; + + 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 4e58bbdf05..a79b9eb73d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -8,11 +8,14 @@ #include +#include +#include #include #include #include #include #include +#include namespace AzToolsFramework::Prefab { @@ -27,15 +30,79 @@ 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(); } + void PrefabFocusHandler::Initialize() + { + m_containerEntityInterface = AZ::Interface::Get(); + AZ_Assert( + m_containerEntityInterface, + "Prefab - PrefabFocusHandler - " + "Container Entity Interface could not be found. " + "Check that it is being correctly initialized."); + + m_focusModeInterface = AZ::Interface::Get(); + AZ_Assert( + m_focusModeInterface, + "Prefab - PrefabFocusHandler - " + "Focus Mode Interface could not be found. " + "Check that it is being correctly initialized."); + + m_instanceEntityMapperInterface = AZ::Interface::Get(); + AZ_Assert( + m_instanceEntityMapperInterface, + "Prefab - PrefabFocusHandler - " + "Instance Entity Mapper Interface could not be found. " + "Check that it is being correctly initialized."); + } + 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; @@ -44,7 +111,7 @@ namespace AzToolsFramework::Prefab PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); - if(!prefabEditorEntityOwnershipInterface) + if (!prefabEditorEntityOwnershipInterface) { return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error " "(PrefabEditorEntityOwnershipInterface unavailable).")); @@ -60,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()) @@ -79,8 +134,16 @@ namespace AzToolsFramework::Prefab return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on.")); } + if (!m_isInitialized) + { + Initialize(); + } + if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get()) { + // Close all container entities in the old path + CloseInstanceContainers(m_instanceFocusVector); + m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); @@ -89,26 +152,21 @@ 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 - if (FocusModeInterface* focusModeInterface = AZ::Interface::Get()) - { - focusModeInterface->SetFocusRoot(containerEntityId); - } + m_focusModeInterface->SetFocusRoot(containerEntityId); + // Refresh path variables RefreshInstanceFocusList(); + + // Open all container entities in the new path + OpenInstanceContainers(m_instanceFocusVector); + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } @@ -126,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()) @@ -156,8 +225,16 @@ namespace AzToolsFramework::Prefab void PrefabFocusHandler::OnEntityStreamLoadSuccess() { + if (!m_isInitialized) + { + 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() @@ -184,4 +261,26 @@ namespace AzToolsFramework::Prefab } } + void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector& instances) const + { + for (const InstanceOptionalReference& instance : instances) + { + if (instance.has_value()) + { + m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true); + } + } + } + + void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector& instances) const + { + for (const InstanceOptionalReference& instance : instances) + { + if (instance.has_value()) + { + m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false); + } + } + } + } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 2ccec36882..80b7a6859c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -13,8 +13,15 @@ #include #include #include +#include #include +namespace AzToolsFramework +{ + class ContainerEntityInterface; + class FocusModeInterface; +} + namespace AzToolsFramework::Prefab { class InstanceEntityMapperInterface; @@ -22,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: @@ -30,11 +38,17 @@ namespace AzToolsFramework::Prefab PrefabFocusHandler(); ~PrefabFocusHandler(); + 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; @@ -46,12 +60,19 @@ namespace AzToolsFramework::Prefab PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance); void RefreshInstanceFocusList(); + void OpenInstanceContainers(const AZStd::vector& instances) const; + void CloseInstanceContainers(const AZStd::vector& instances) const; + InstanceOptionalReference m_focusedInstance; TemplateId m_focusedTemplateId; AZStd::vector m_instanceFocusVector; AZ::IO::Path m_instanceFocusPath; - InstanceEntityMapperInterface* m_instanceEntityMapperInterface; + ContainerEntityInterface* m_containerEntityInterface = nullptr; + FocusModeInterface* m_focusModeInterface = nullptr; + InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; + + bool m_isInitialized = false; }; } // namespace AzToolsFramework::Prefab 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/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index 09439724cd..07bda45c8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -50,17 +50,19 @@ namespace AzToolsFramework auto settingsRegistry = AZ::SettingsRegistry::Get(); AZ_Assert(settingsRegistry, "Settings registry is not set"); - + [[maybe_unused]] bool result = settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); AZ_Warning("Prefab", result, "Couldn't retrieve project root path"); m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred(); AZ::Interface::Register(this); + m_scriptingPrefabLoader.Connect(this); } void PrefabLoader::UnregisterPrefabLoaderInterface() { + m_scriptingPrefabLoader.Disconnect(); AZ::Interface::Unregister(this); } @@ -568,7 +570,7 @@ namespace AzToolsFramework (pathStr.find_first_of(AZ_FILESYSTEM_INVALID_CHARACTERS) == AZStd::string::npos) && (pathStr.back() != '\\' && pathStr.back() != '/'); } - + AZ::IO::Path PrefabLoader::GetFullPath(AZ::IO::PathView path) { AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred(); @@ -596,26 +598,38 @@ namespace AzToolsFramework { // The asset system provided us with a valid root folder and relative path, so return it. fullPath = AZ::IO::Path(rootFolder) / assetInfo.m_relativePath; + return fullPath; } else { - // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic. + // attempt to find the absolute from the Cache folder + AZStd::string assetRootFolder; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(assetRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder); + } + fullPath = AZ::IO::Path(assetRootFolder) / path; + if (fullPath.IsAbsolute() && AZ::IO::SystemFile::Exists(fullPath.c_str())) + { + return fullPath; + } + } - // Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow - // the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside - // a unit test, so just execute the fallback logic without an error. - [[maybe_unused]] bool assetProcessorReady = false; - AzFramework::AssetSystemRequestBus::BroadcastResult( - assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady); + // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic. - AZ_Error( - "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.", - AZ_STRING_ARG(path.Native())); + // Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow + // the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside + // a unit test, so just execute the fallback logic without an error. + [[maybe_unused]] bool assetProcessorReady = false; + AzFramework::AssetSystemRequestBus::BroadcastResult( + assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady); - // If a relative path was passed in, make it relative to the project root. - fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator); - } + AZ_Error( + "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.", + AZ_STRING_ARG(path.Native())); + // If a relative path was passed in, make it relative to the project root. + fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator); return fullPath; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h index 15f201e027..da2c3f3161 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace AZ { @@ -114,6 +115,7 @@ namespace AzToolsFramework void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override; private: + /** * Copies the template dom provided and manipulates it into the proper format to be saved to disk. * @param templateRef The template whose dom we want to transform into the proper format to be saved to disk. @@ -177,6 +179,7 @@ namespace AzToolsFramework AZStd::optional> StoreTemplateIntoFileFormat(TemplateId templateId); PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + ScriptingPrefabLoader m_scriptingPrefabLoader; AZ::IO::Path m_projectPathWithOsSeparator; AZ::IO::Path m_projectPathWithSlashSeparator; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h index a428f8a7b9..b4586ad5bf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h @@ -99,7 +99,6 @@ namespace AzToolsFramework // Generates a new path static AZ::IO::Path GeneratePath(); }; - } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h new file mode 100644 index 0000000000..6e9d64b147 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h @@ -0,0 +1,45 @@ +/* + * 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 +{ + namespace Prefab + { + // Ebus for script-friendly APIs for the prefab loader + struct PrefabLoaderScriptingTraits : AZ::EBusTraits + { + AZ_TYPE_INFO(PrefabLoaderScriptingTraits, "{C344B7D8-8299-48C9-8450-26E1332EA011}"); + + virtual ~PrefabLoaderScriptingTraits() = default; + + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + + /** + * Saves a Prefab Template into the provided output string. + * Converts Prefab Template form into .prefab form by collapsing nested Template info + * into a source path and patches. + * @param templateId Id of the template to be saved + * @return Will contain the serialized template json on success + */ + virtual AZ::Outcome SaveTemplateToString(TemplateId templateId) = 0; + }; + + using PrefabLoaderScriptingBus = AZ::EBus; + + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 038f36eac9..ee34b628bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -43,6 +43,12 @@ namespace AzToolsFramework m_instanceToTemplateInterface = AZ::Interface::Get(); AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceToTemplateInterface"); + m_prefabFocusInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusInterface, "Could not get PrefabFocusInterface on PrefabPublicHandler construction."); + + m_prefabFocusPublicInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusPublicInterface, "Could not get PrefabFocusPublicInterface on PrefabPublicHandler construction."); + m_prefabLoaderInterface = AZ::Interface::Get(); AZ_Assert(m_prefabLoaderInterface, "Could not get PrefabLoaderInterface on PrefabPublicHandler construction."); @@ -257,9 +263,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); } } @@ -551,6 +558,13 @@ namespace AzToolsFramework PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) { + // If the parent is invalid, parent to the container of the currently focused prefab. + if (!parentId.IsValid()) + { + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); + } + InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId); if (!owningInstanceOfParentEntity) { @@ -967,13 +981,13 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("No entities to duplicate.")); } - const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds); - if (entityIdsNoLevelInstance.empty()) + const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + if (entityIdsNoFocusContainer.empty()) { - return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the level instance.")); + return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance.")); } - if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance)) + if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer)) { return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation." "Change your selection to contain entities in the same instance.")); @@ -981,7 +995,7 @@ namespace AzToolsFramework // We've already verified the entities are all owned by the same instance, // so we can just retrieve our instance from the first entity in the list. - AZ::EntityId firstEntityIdToDuplicate = entityIdsNoLevelInstance[0]; + AZ::EntityId firstEntityIdToDuplicate = entityIdsNoFocusContainer[0]; InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate); if (!commonOwningInstance.has_value()) { @@ -1001,7 +1015,7 @@ namespace AzToolsFramework // This will cull out any entities that have ancestors in the list, since we will end up duplicating // the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances - AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoLevelInstance); + AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoFocusContainer); AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1038,10 +1052,10 @@ namespace AzToolsFramework DuplicateNestedEntitiesInInstance(commonOwningInstance->get(), entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap); - PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication"); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication", false); command->SetParent(undoBatch.GetUndoBatch()); command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); - command->RedoBatched(); + command->Redo(); DuplicateNestedInstancesInInstance(commonOwningInstance->get(), instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap); @@ -1097,7 +1111,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(); @@ -1105,19 +1119,21 @@ namespace AzToolsFramework PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants) { - const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds); + // Remove the container entity of the focused prefab from the list, if it is included. + const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); - if (entityIdsNoLevelInstance.empty()) + if (entityIdsNoFocusContainer.empty()) { return AZ::Success(); } - if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance)) + // All entities in this list need to belong to the same prefab instance for the operation to be valid. + if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer)) { return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation.")); } - AZ::EntityId firstEntityIdToDelete = entityIdsNoLevelInstance[0]; + AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0]; InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete); // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you @@ -1127,8 +1143,15 @@ namespace AzToolsFramework commonOwningInstance = commonOwningInstance->get().GetParentInstance(); } + // We only allow explicit deletions for entities inside the currently focused prefab. + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + if (&m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId)->get() != &commonOwningInstance->get()) + { + return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited.")); + } + // Retrieve entityList from entityIds - EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoLevelInstance); + EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer); AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1185,7 +1208,7 @@ namespace AzToolsFramework } else { - for (AZ::EntityId entityId : entityIdsNoLevelInstance) + for (AZ::EntityId entityId : entityIdsNoFocusContainer) { InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); // If this is the container entity, it actually represents the instance so get its owner @@ -1226,9 +1249,12 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity.")); } - if (IsLevelInstanceContainerEntity(containerEntityId)) + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + if (containerEntityId == m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { - return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance.")); + return AZ::Failure(AZStd::string("Cannot detach focused Prefab Instance.")); } InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId); @@ -1297,7 +1323,7 @@ namespace AzToolsFramework Prefab::PrefabDom instanceDomAfter; m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance); - PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment"); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment", false); command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId); command->SetParent(undoBatch.GetUndoBatch()); { @@ -1451,9 +1477,14 @@ namespace AzToolsFramework AZStd::queue entityQueue; + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + AZ::EntityId focusedPrefabContainerEntityId = + m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); for (auto inputEntity : inputEntities) { - if (inputEntity && !IsLevelInstanceContainerEntity(inputEntity->GetId())) + if (inputEntity && inputEntity->GetId() != focusedPrefabContainerEntityId) { entityQueue.push(inputEntity); } @@ -1547,19 +1578,19 @@ namespace AzToolsFramework return AZ::Success(); } - EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutLevelInstance( + EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer( const EntityIdList& entityIds) const { - EntityIdList outEntityIds; - outEntityIds.reserve(entityIds.size()); // Actual size could be smaller. + EntityIdList outEntityIds(entityIds); + + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); - for (const AZ::EntityId& entityId : entityIds) + if (auto iter = AZStd::find(outEntityIds.begin(), outEntityIds.end(), focusedInstanceContainerEntityId); iter != outEntityIds.end()) { - if (!IsLevelInstanceContainerEntity(entityId)) - { - outEntityIds.emplace_back(entityId); - } + outEntityIds.erase(iter); } + return outEntityIds; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index f3c0e67d46..a9dadc3336 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -74,7 +74,7 @@ namespace AzToolsFramework Instance& commonRootEntityOwningInstance, EntityList& outEntities, AZStd::vector& outInstances) const; - EntityIdList GenerateEntityIdListWithoutLevelInstance(const EntityIdList& entityIds) const; + EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const; InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; @@ -187,6 +187,8 @@ namespace AzToolsFramework InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 896e40d929..b1fdf9784d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -36,12 +37,14 @@ namespace AzToolsFramework m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface(); m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface(); m_prefabPublicRequestHandler.Connect(); + m_prefabSystemScriptingHandler.Connect(this); AZ::SystemTickBus::Handler::BusConnect(); } void PrefabSystemComponent::Deactivate() { AZ::SystemTickBus::Handler::BusDisconnect(); + m_prefabSystemScriptingHandler.Disconnect(); m_prefabPublicRequestHandler.Disconnect(); m_prefabPublicHandler.UnregisterPrefabPublicHandlerInterface(); m_instanceToTemplatePropagator.UnregisterInstanceToTemplateInterface(); @@ -58,13 +61,24 @@ namespace AzToolsFramework AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context); PrefabPublicRequestHandler::Reflect(context); PrefabLoader::Reflect(context); + PrefabSystemScriptingHandler::Reflect(context); - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) + if (AZ::SerializeContext* serialize = azrtti_cast(context)) { serialize->Class()->Version(1); } + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + + behaviorContext->EBus("PrefabLoaderScriptingBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "prefab") + ->Attribute(AZ::Script::Attributes::Category, "Prefab") + ->Event("SaveTemplateToString", &PrefabLoaderScriptingBus::Events::SaveTemplateToString); + ; + } + AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast(context); if (jsonRegistration) { @@ -145,7 +159,7 @@ namespace AzToolsFramework newInstance->SetTemplateId(newTemplateId); } } - + void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude) { UpdatePrefabInstances(templateId, immediate, instanceToExclude); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index f640eb2f1b..480bb83121 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace AZ { @@ -219,7 +220,7 @@ namespace AzToolsFramework const AZStd::vector& entities, AZStd::vector>&& instancesToConsume, AZ::IO::PathView filePath, AZStd::unique_ptr containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override; - + PrefabDom& FindTemplateDom(TemplateId templateId) override; /** @@ -244,7 +245,7 @@ namespace AzToolsFramework private: AZ_DISABLE_COPY_MOVE(PrefabSystemComponent); - + /** * Builds a new Prefab Template out of entities and instances and returns the first instance comprised of * these entities and instances. @@ -412,6 +413,8 @@ namespace AzToolsFramework // Handler of the public Prefab requests. PrefabPublicRequestHandler m_prefabPublicRequestHandler; + + PrefabSystemScriptingHandler m_prefabSystemScriptingHandler; }; } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h index 54ca951841..66d85ccee9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h @@ -78,8 +78,7 @@ namespace AzToolsFramework AZStd::unique_ptr containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) = 0; }; - - + } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h new file mode 100644 index 0000000000..f83bd2c6f1 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.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 +#include +#include +#include +#include + +namespace AzToolsFramework +{ + namespace Prefab + { + // Bus that exposes a script-friendly interface to the PrefabSystemComponent + struct PrefabSystemScriptingEbusTraits : AZ::EBusTraits + { + using MutexType = AZ::NullMutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + + virtual TemplateId CreatePrefabTemplate( + const AZStd::vector& entityIds, const AZStd::string& filePath) = 0; + }; + + using PrefabSystemScriptingBus = AZ::EBus; + + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp new file mode 100644 index 0000000000..9d0d0547ae --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.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 +#include +#include +#include +#include +#include +#include +#include + +namespace AzToolsFramework::Prefab +{ + void PrefabSystemScriptingHandler::Reflect(AZ::ReflectContext* context) + { + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("InvalidTemplateId", BehaviorConstant(InvalidTemplateId)) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "prefab") + ->Attribute(AZ::Script::Attributes::Category, "Prefab"); + + behaviorContext->EBus("PrefabSystemScriptingBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "prefab") + ->Attribute(AZ::Script::Attributes::Category, "Prefab") + ->Event("CreatePrefab", &PrefabSystemScriptingBus::Events::CreatePrefabTemplate); + } + } + + void PrefabSystemScriptingHandler::Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface) + { + AZ_Assert(prefabSystemComponentInterface != nullptr, "prefabSystemComponentInterface must not be null"); + m_prefabSystemComponentInterface = prefabSystemComponentInterface; + PrefabSystemScriptingBus::Handler::BusConnect(); + } + + void PrefabSystemScriptingHandler::Disconnect() + { + PrefabSystemScriptingBus::Handler::BusDisconnect(); + } + + TemplateId PrefabSystemScriptingHandler::CreatePrefabTemplate(const AZStd::vector& entityIds, const AZStd::string& filePath) + { + AZStd::vector entities; + + for (const auto& entityId : entityIds) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId); + + AZ_Warning( + "PrefabSystemComponent", entity, "EntityId %s was not found and will not be added to the prefab", + entityId.ToString().c_str()); + + if (entity) + { + entities.push_back(entity); + } + } + + bool result = false; + [[maybe_unused]] AZ::EntityId commonRoot; + EntityList topLevelEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(result, &AzToolsFramework::ToolsApplicationRequestBus::Events::FindCommonRootInactive, + entities, commonRoot, &topLevelEntities); + + auto containerEntity = AZStd::make_unique(); + containerEntity->CreateComponent(); + + for (AZ::Entity* entity : topLevelEntities) + { + AzToolsFramework::Components::TransformComponent* transformComponent = + entity->FindComponent(); + + if (transformComponent) + { + transformComponent->SetParent(containerEntity->GetId()); + } + } + + auto prefab = m_prefabSystemComponentInterface->CreatePrefab( + entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)), AZStd::move(containerEntity)); + + if (!prefab) + { + AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str()); + return InvalidTemplateId; + } + + return prefab->GetTemplateId(); + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h new file mode 100644 index 0000000000..809690bf35 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.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 + +namespace AzToolsFramework +{ + namespace Prefab + { + class PrefabSystemScriptingHandler + : PrefabSystemScriptingBus::Handler + { + public: + static void Reflect(AZ::ReflectContext* context); + + PrefabSystemScriptingHandler() = default; + + void Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface); + void Disconnect(); + + private: + AZ_DISABLE_COPY(PrefabSystemScriptingHandler); + + ////////////////////////////////////////////////////////////////////////// + // PrefabSystemScriptingBus implementation + TemplateId CreatePrefabTemplate(const AZStd::vector& entityIds, const AZStd::string& filePath) override; + ////////////////////////////////////////////////////////////////////////// + + PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + }; + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index b298304e3b..6c96209d56 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -17,17 +17,16 @@ namespace AzToolsFramework { PrefabUndoBase::PrefabUndoBase(const AZStd::string& undoOperationName) : UndoSystem::URSequencePoint(undoOperationName) - , m_changed(true) - , m_templateId(InvalidTemplateId) { m_instanceToTemplateInterface = AZ::Interface::Get(); AZ_Assert(m_instanceToTemplateInterface, "Failed to grab instance to template interface"); } //PrefabInstanceUndo - PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName) + PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation) : PrefabUndoBase(undoOperationName) { + m_useImmediatePropagation = useImmediatePropagation; } void PrefabUndoInstance::Capture( @@ -43,17 +42,12 @@ namespace AzToolsFramework void PrefabUndoInstance::Undo() { - m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true); + m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, m_useImmediatePropagation); } void PrefabUndoInstance::Redo() { - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true); - } - - void PrefabUndoInstance::RedoBatched() - { - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId); + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, m_useImmediatePropagation); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h index 8669024df7..0946a36951 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h @@ -29,14 +29,15 @@ namespace AzToolsFramework bool Changed() const override { return m_changed; } protected: - TemplateId m_templateId; + TemplateId m_templateId = InvalidTemplateId; PrefabDom m_redoPatch; PrefabDom m_undoPatch; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; - bool m_changed; + bool m_changed = true; + bool m_useImmediatePropagation = true; }; //! handles the addition and removal of entities from instances @@ -44,7 +45,7 @@ namespace AzToolsFramework : public PrefabUndoBase { public: - explicit PrefabUndoInstance(const AZStd::string& undoOperationName); + explicit PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation = true); void Capture( const PrefabDom& initialState, @@ -53,7 +54,6 @@ namespace AzToolsFramework void Undo() override; void Redo() override; - void RedoBatched(); }; //! handles entity updates, such as when the values on an entity change diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp index 9803b55324..31a0c60bcb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp @@ -23,10 +23,10 @@ namespace AzToolsFramework PrefabDom instanceDomAfterUpdate; PrefabDomUtils::StoreInstanceInPrefabDom(instance, instanceDomAfterUpdate); - PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage); + PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage, false); state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId()); state->SetParent(undoBatch); - state->RedoBatched(); + state->Redo(); } LinkId CreateLink( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp new file mode 100644 index 0000000000..465d83e94b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp @@ -0,0 +1,144 @@ +/* + * 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 AZ::Prefab +{ + static constexpr const char s_useProceduralPrefabsKey[] = "/O3DE/Preferences/Prefabs/UseProceduralPrefabs"; + + // ProceduralPrefabAsset + + ProceduralPrefabAsset::ProceduralPrefabAsset(const AZ::Data::AssetId& assetId) + : AZ::Data::AssetData(assetId) + , m_templateId(AzToolsFramework::Prefab::InvalidTemplateId) + { + } + + void ProceduralPrefabAsset::Reflect(AZ::ReflectContext* context) + { + PrefabDomData::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) + { + serializeContext->Class() + ->Version(1) + ->Field("Template Name", &ProceduralPrefabAsset::m_templateName) + ->Field("Template ID", &ProceduralPrefabAsset::m_templateId); + } + } + + const AZStd::string& ProceduralPrefabAsset::GetTemplateName() const + { + return m_templateName; + } + + void ProceduralPrefabAsset::SetTemplateName(AZStd::string templateName) + { + m_templateName = AZStd::move(templateName); + } + + AzToolsFramework::Prefab::TemplateId ProceduralPrefabAsset::GetTemplateId() const + { + return m_templateId; + } + + void ProceduralPrefabAsset::SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId) + { + m_templateId = templateId; + } + + bool ProceduralPrefabAsset::UseProceduralPrefabs() + { + bool useProceduralPrefabs = false; + bool result = AZ::SettingsRegistry::Get()->GetObject(useProceduralPrefabs, s_useProceduralPrefabsKey); + return result && useProceduralPrefabs; + } + + // PrefabDomData + + void PrefabDomData::Reflect(AZ::ReflectContext* context) + { + if (auto* jsonContext = azrtti_cast(context)) + { + jsonContext->Serializer()->HandlesType(); + } + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + } + + void PrefabDomData::CopyValue(const rapidjson::Value& inputValue) + { + m_prefabDom.CopyFrom(inputValue, m_prefabDom.GetAllocator()); + } + + const AzToolsFramework::Prefab::PrefabDom& PrefabDomData::GetValue() const + { + return m_prefabDom; + } + + // PrefabDomDataJsonSerializer + + AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Load( + void* outputValue, + [[maybe_unused]] const AZ::Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + AZ::JsonDeserializerContext& context) + { + AZ_Assert(outputValueTypeId == azrtti_typeid(), + "PrefabDomDataJsonSerializer Load against output typeID that was not PrefabDomData"); + AZ_Assert(outputValue, "PrefabDomDataJsonSerializer Load against null output"); + + namespace JSR = AZ::JsonSerializationResult; + JSR::ResultCode result(JSR::Tasks::ReadField); + + if (inputValue.IsObject() == false) + { + result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing object")); + return context.Report(result, "Prefab should be an object."); + } + + if (inputValue.MemberCount() < 1) + { + result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing members")); + return context.Report(result, "Prefab should have multiple members."); + } + + auto* outputVariable = reinterpret_cast(outputValue); + outputVariable->CopyValue(inputValue); + return context.Report(result, "Loaded procedural prefab"); + } + + AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Store( + rapidjson::Value& outputValue, + const void* inputValue, + [[maybe_unused]] const void* defaultValue, + [[maybe_unused]] const AZ::Uuid& valueTypeId, + AZ::JsonSerializerContext& context) + { + AZ_Assert(inputValue, "Input value for PrefabDomDataJsonSerializer can't be null."); + AZ_Assert(azrtti_typeid() == valueTypeId, + "Unable to Serialize because the provided type is not PrefabGroup::PrefabDomData."); + + const PrefabDomData* prefabDomData = reinterpret_cast(inputValue); + + namespace JSR = AZ::JsonSerializationResult; + JSR::ResultCode result(JSR::Tasks::WriteValue); + outputValue.SetObject(); + outputValue.CopyFrom(prefabDomData->GetValue(), context.GetJsonAllocator()); + return context.Report(result, "Stored procedural prefab"); + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h new file mode 100644 index 0000000000..dd57389b7b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h @@ -0,0 +1,90 @@ +/* + * 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 AZ::Prefab +{ + //! A wrapper around the JSON DOM type so that the assets can read in and write out + //! JSON directly since Prefabs are JSON serialized entity-component data + class PrefabDomData final + { + public: + AZ_RTTI(PrefabDomData, "{C73A3360-D772-4D41-9118-A039BF9340C1}"); + AZ_CLASS_ALLOCATOR(PrefabDomData, AZ::SystemAllocator, 0); + + PrefabDomData() = default; + ~PrefabDomData() = default; + + static void Reflect(AZ::ReflectContext* context); + + void CopyValue(const rapidjson::Value& inputValue); + const AzToolsFramework::Prefab::PrefabDom& GetValue() const; + + private: + AzToolsFramework::Prefab::PrefabDom m_prefabDom; + }; + + //! Registered to help read/write JSON for the PrefabDomData::m_prefabDom + class PrefabDomDataJsonSerializer final + : public AZ::BaseJsonSerializer + { + public: + AZ_RTTI(PrefabDomDataJsonSerializer, "{9FC48652-A00B-4EFA-8FD9-345A8E625439}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR(PrefabDomDataJsonSerializer, AZ::SystemAllocator, 0); + + ~PrefabDomDataJsonSerializer() override = default; + + AZ::JsonSerializationResult::Result Load( + void* outputValue, + const AZ::Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + AZ::JsonDeserializerContext& context) override; + + AZ::JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, + const void* inputValue, + const void* defaultValue, + const AZ::Uuid& valueTypeId, + AZ::JsonSerializerContext& context) override; + }; + + //! An asset type to register templates into the Prefab system so that they + //! can instantiate like Authored Prefabs + class ProceduralPrefabAsset + : public AZ::Data::AssetData + { + public: + AZ_CLASS_ALLOCATOR(ProceduralPrefabAsset, AZ::SystemAllocator, 0); + AZ_RTTI(ProceduralPrefabAsset, "{9B7C8459-471E-4EAD-A363-7990CC4065A9}", AZ::Data::AssetData); + + static bool UseProceduralPrefabs(); + + ProceduralPrefabAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId()); + ~ProceduralPrefabAsset() override = default; + ProceduralPrefabAsset(const ProceduralPrefabAsset& rhs) = delete; + ProceduralPrefabAsset& operator=(const ProceduralPrefabAsset& rhs) = delete; + + const AZStd::string& GetTemplateName() const; + void SetTemplateName(AZStd::string templateName); + + AzToolsFramework::Prefab::TemplateId GetTemplateId() const; + void SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId); + + static void Reflect(AZ::ReflectContext* context); + + private: + AZStd::string m_templateName; + AzToolsFramework::Prefab::TemplateId m_templateId; + }; + +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp new file mode 100644 index 0000000000..bee8a8bc3b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp @@ -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 + * + */ + +#include + +namespace AzToolsFramework::Prefab +{ + void ScriptingPrefabLoader::Connect(PrefabLoaderInterface* prefabLoaderInterface) + { + AZ_Assert(prefabLoaderInterface, "prefabLoaderInterface must not be null"); + + m_prefabLoaderInterface = prefabLoaderInterface; + PrefabLoaderScriptingBus::Handler::BusConnect(); + } + + void ScriptingPrefabLoader::Disconnect() + { + PrefabLoaderScriptingBus::Handler::BusDisconnect(); + } + + AZ::Outcome ScriptingPrefabLoader::SaveTemplateToString(TemplateId templateId) + { + AZStd::string json; + + if (m_prefabLoaderInterface->SaveTemplateToString(templateId, json)) + { + return AZ::Success(json); + } + + return AZ::Failure(); + } +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h new file mode 100644 index 0000000000..ec6219909e --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h @@ -0,0 +1,42 @@ +/* + * 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 +{ + namespace Prefab + { + /** + * The Scripting Prefab Loader handles scripting-friendly API requests for the prefab loader + */ + class ScriptingPrefabLoader + : private PrefabLoaderScriptingBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(ScriptingPrefabLoader, AZ::SystemAllocator, 0); + AZ_RTTI(ScriptingPrefabLoader, "{ABC3C989-4D4F-41E7-B25B-B0FEF97177E6}"); + + void Connect(PrefabLoaderInterface* prefabLoaderInterface); + void Disconnect(); + + private: + + ////////////////////////////////////////////////////////////////////////// + // PrefabLoaderRequestBus implementation + AZ::Outcome SaveTemplateToString(TemplateId templateId) override; + ////////////////////////////////////////////////////////////////////////// + + PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; + }; + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceRequestComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceRequestComponent.cpp index 36f647e4c2..99d0768cf6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceRequestComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceRequestComponent.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -78,13 +79,9 @@ namespace AzToolsFramework AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants, &AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, AzToolsFramework::EntityIdList{ entityId }); - // Retrieve the game folder so we can use that as a root with the passed in relative path - const char* gameFolder = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameFolder, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath); - // Join our relative path with the game folder to get a full path to the desired asset - AZStd::string assetFullPath; - AzFramework::StringFunc::Path::Join(gameFolder, assetPath, assetFullPath); + AZ::IO::FixedMaxPath assetFullPath = AZ::Utils::GetProjectPath(); + assetFullPath /= assetPath; // Call SliceUtilities::MakeNewSlice with all user input prompts disabled bool success = AzToolsFramework::SliceUtilities::MakeNewSlice(entitiesAndDescendants, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp index d8b5fe0a15..9d2c58a717 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp @@ -718,7 +718,7 @@ namespace AzToolsFramework if (!fullPathFound) { - assetFullPath = AZStd::string::format("@devassets@/%s", sliceAssetPath.c_str()); + assetFullPath = AZStd::string::format("@projectroot@/%s", sliceAssetPath.c_str()); } return Commit(assetFullPath.c_str(), preSaveCallback, postSaveCallback, sliceCommitFlags); @@ -1020,13 +1020,13 @@ namespace AzToolsFramework AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); AZ_Assert(fileIO, "File IO is not initialized."); - AZStd::string devAssetPath = fileIO->GetAlias("@devassets@"); + AZStd::string devAssetPath = fileIO->GetAlias("@projectroot@"); AZStd::string userPath = fileIO->GetAlias("@user@"); AZStd::string tempPath = fullPath; EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, devAssetPath); EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, userPath); EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, tempPath); - AzFramework::StringFunc::Replace(tempPath, "@devassets@", devAssetPath.c_str()); + AzFramework::StringFunc::Replace(tempPath, "@projectroot@", devAssetPath.c_str()); AzFramework::StringFunc::Replace(tempPath, devAssetPath.c_str(), userPath.c_str()); tempPath.append(".slicetemp"); 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/EditorLayerComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp index f58f273f11..01b1213473 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp @@ -378,11 +378,11 @@ namespace AzToolsFramework // If this layer is being loaded, it won't have a level save dependency yet, so clear that flag. m_mustSaveLevelWhenLayerSaves = false; QString fullPathName = levelPakFile; - if (fullPathName.contains("@devassets@")) + if (fullPathName.contains("@projectroot@")) { AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); // Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that. - fullPathName.replace("@devassets@", fileIO->GetAlias("@devassets@")); + fullPathName.replace("@projectroot@", fileIO->GetAlias("@projectroot@")); } QFileInfo fileNameInfo(fullPathName); 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/EditorEntityUi/EditorEntityUiHandlerBase.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp index a0da20ebf0..3cdcade1b0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.cpp @@ -37,51 +37,71 @@ namespace AzToolsFramework return m_handlerId; } - QString EditorEntityUiHandlerBase::GenerateItemInfoString(AZ::EntityId /*entityId*/) const + QString EditorEntityUiHandlerBase::GenerateItemInfoString([[maybe_unused]] AZ::EntityId entityId) const { return QString(); } - QString EditorEntityUiHandlerBase::GenerateItemTooltip(AZ::EntityId /*entityId*/) const + QString EditorEntityUiHandlerBase::GenerateItemTooltip([[maybe_unused]] AZ::EntityId entityId) const { return QString(); } - QIcon EditorEntityUiHandlerBase::GenerateItemIcon(AZ::EntityId /*entityId*/) const + QIcon EditorEntityUiHandlerBase::GenerateItemIcon([[maybe_unused]] AZ::EntityId entityId) const { return QIcon(); } - bool EditorEntityUiHandlerBase::CanToggleLockVisibility(AZ::EntityId /*entityId*/) const + bool EditorEntityUiHandlerBase::CanToggleLockVisibility([[maybe_unused]] AZ::EntityId entityId) const { return true; } - bool EditorEntityUiHandlerBase::CanRename(AZ::EntityId /*entityId*/) const + bool EditorEntityUiHandlerBase::CanRename([[maybe_unused]] AZ::EntityId entityId) const { return true; } - void EditorEntityUiHandlerBase::PaintItemBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const + void EditorEntityUiHandlerBase::PaintItemBackground( + [[maybe_unused]] QPainter* painter, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const { } - void EditorEntityUiHandlerBase::PaintDescendantBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/, - const QModelIndex& /*descendantIndex*/) const + void EditorEntityUiHandlerBase::PaintDescendantBackground( + [[maybe_unused]] QPainter* painter, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index, + [[maybe_unused]] const QModelIndex& descendantIndex) const { } - void EditorEntityUiHandlerBase::PaintDescendantBranchBackground(QPainter* /*painter*/, const QTreeView* /*view*/, const QRect& /*rect*/, - const QModelIndex& /*index*/, const QModelIndex& /*descendantIndex*/) const + void EditorEntityUiHandlerBase::PaintDescendantBranchBackground( + [[maybe_unused]] QPainter* painter, + [[maybe_unused]] const QTreeView* view, + [[maybe_unused]] const QRect& rect, + [[maybe_unused]] const QModelIndex& index, + [[maybe_unused]] const QModelIndex& descendantIndex) const { } - void EditorEntityUiHandlerBase::PaintItemForeground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const + void EditorEntityUiHandlerBase::PaintItemForeground( + [[maybe_unused]] QPainter* painter, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const { } - void EditorEntityUiHandlerBase::PaintDescendantForeground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/, - const QModelIndex& /*descendantIndex*/) const + void EditorEntityUiHandlerBase::PaintDescendantForeground( + [[maybe_unused]] QPainter* painter, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index, + [[maybe_unused]] const QModelIndex& descendantIndex) const + { + } + + void EditorEntityUiHandlerBase::OnDoubleClick([[maybe_unused]] AZ::EntityId entityId) const { } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h index c37b099272..9554ad01fc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h @@ -61,6 +61,9 @@ namespace AzToolsFramework virtual void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QModelIndex& descendantIndex) const; + //! Triggered when the entity is double clicked in the Outliner. + virtual void OnDoubleClick(AZ::EntityId entityId) const; + private: EditorEntityUiHandlerId m_handlerId = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 2872c1bce3..234696ff2a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -88,6 +88,7 @@ namespace AzToolsFramework EntityOutlinerListModel::~EntityOutlinerListModel() { + ContainerEntityNotificationBus::Handler::BusDisconnect(); EditorEntityInfoNotificationBus::Handler::BusDisconnect(); EditorEntityContextNotificationBus::Handler::BusDisconnect(); ToolsApplicationEvents::Bus::Handler::BusDisconnect(); @@ -105,6 +106,12 @@ namespace AzToolsFramework EntityCompositionNotificationBus::Handler::BusConnect(); AZ::EntitySystemBus::Handler::BusConnect(); + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId); + m_editorEntityUiInterface = AZ::Interface::Get(); AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize."); @@ -1333,12 +1340,26 @@ namespace AzToolsFramework emit EnableSelectionUpdates(true); } - void EntityOutlinerListModel::OnEntityRuntimeActivationChanged(AZ::EntityId entityId, bool activeOnStart) + void EntityOutlinerListModel::OnEntityRuntimeActivationChanged(AZ::EntityId entityId, [[maybe_unused]] bool activeOnStart) { - AZ_UNUSED(activeOnStart); QueueEntityUpdate(entityId); } + void EntityOutlinerListModel::OnContainerEntityStatusChanged(AZ::EntityId entityId, [[maybe_unused]] bool open) + { + QModelIndex changedIndex = GetIndexFromEntity(entityId); + + // Trigger a refresh of all direct children so that they can be shown or hidden appropriately. + int numChildren = rowCount(changedIndex); + if (numChildren > 0) + { + emit dataChanged(index(0, 0, changedIndex), index(numChildren - 1, ColumnCount - 1, changedIndex)); + } + + // Always expand containers + QueueEntityToExpand(entityId, true); + } + void EntityOutlinerListModel::OnEntityInfoUpdatedRemoveChildBegin([[maybe_unused]] AZ::EntityId parentId, [[maybe_unused]] AZ::EntityId childId) { //add/remove operations trigger selection change signals which assert and break undo/redo operations in progress in inspector etc. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 4b13aa6d27..7b946c6304 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -54,6 +55,7 @@ namespace AzToolsFramework , private EntityCompositionNotificationBus::Handler , private EditorEntityRuntimeActivationChangeNotificationBus::Handler , private AZ::EntitySystemBus::Handler + , private ContainerEntityNotificationBus::Handler { Q_OBJECT; @@ -216,6 +218,9 @@ namespace AzToolsFramework // EditorEntityRuntimeActivationChangeNotificationBus::Handler void OnEntityRuntimeActivationChanged(AZ::EntityId entityId, bool activeOnStart) override; + // ContainerEntityNotificationBus overrides ... + void OnContainerEntityStatusChanged(AZ::EntityId entityId, bool open) override; + // Drag/Drop of components from Component Palette. bool dropMimeDataComponentPalette(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.cpp index 841a01f5e5..600ca284f4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include "EntityOutlinerListModel.hxx" namespace AzToolsFramework @@ -19,6 +21,10 @@ namespace AzToolsFramework EntityOutlinerSortFilterProxyModel::EntityOutlinerSortFilterProxyModel(QObject* pParent) : QSortFilterProxyModel(pParent) { + m_containerEntityInterface = AZ::Interface::Get(); + AZ_Assert( + m_containerEntityInterface != nullptr, + "EntityOutlinerContainerProxyModel requires a ContainerEntityInterface instance on construction."); } void EntityOutlinerSortFilterProxyModel::UpdateFilter() @@ -26,8 +32,24 @@ namespace AzToolsFramework invalidateFilter(); } + void EntityOutlinerSortFilterProxyModel::setSourceModel(QAbstractItemModel* sourceModel) + { + QSortFilterProxyModel::setSourceModel(sourceModel); + + m_listModel = qobject_cast(sourceModel); + AZ_Assert(m_listModel != nullptr, "EntityOutlinerContainerProxyModel requires an EntityOutlinerListModel as its source ."); + } + bool EntityOutlinerSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { + // Retrieve the entityId of the parent entity + AZ::EntityId parentEntityId = m_listModel->GetEntityFromIndex(sourceParent); + + if(!m_containerEntityInterface->IsContainerOpen(parentEntityId)) + { + return false; + } + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); QVariant visibilityData = sourceModel()->data(index, EntityOutlinerListModel::VisibilityRole); return visibilityData.isValid() ? visibilityData.toBool() : true; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.hxx index b7e1a3f869..c6e1410688 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.hxx @@ -19,11 +19,12 @@ namespace AzToolsFramework { + class ContainerEntityInterface; + class EntityOutlinerListModel; - /*! - * Enables the Outliner to filter entries based on search string. - * Enables the Outliner to do custom sorting on entries. - */ + //! Enables the Outliner to filter entries based on search string. + //! Enables the Outliner to do custom sorting on entries. + //! Enforces the correct rendering for container entities. class EntityOutlinerSortFilterProxyModel : public QSortFilterProxyModel { @@ -37,12 +38,15 @@ namespace AzToolsFramework void UpdateFilter(); // Qt overrides + void setSourceModel(QAbstractItemModel* sourceModel) override; bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; void sort(int column, Qt::SortOrder order) override; private: QString m_filterName; + EntityOutlinerListModel* m_listModel = nullptr; + ContainerEntityInterface* m_containerEntityInterface = nullptr; }; } 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 689e5b5dc4..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 @@ -198,6 +199,7 @@ namespace AzToolsFramework m_proxyModel = aznew EntityOutlinerSortFilterProxyModel(this); m_proxyModel->setSourceModel(m_listModel); + m_gui->m_objectTree->setModel(m_proxyModel); // Link up signals for informing the model of tree changes using the proxy as an intermediary @@ -291,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(); @@ -302,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(); @@ -323,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; } @@ -551,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. @@ -905,8 +914,12 @@ namespace AzToolsFramework } } - void EntityOutlinerWidget::OnTreeItemDoubleClicked(const QModelIndex& /*index*/) + void EntityOutlinerWidget::OnTreeItemDoubleClicked(const QModelIndex& index) { + if (AZ::EntityId entityId = GetEntityIdFromIndex(index); auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId)) + { + entityUiHandler->OnDoubleClick(entityId); + } } void EntityOutlinerWidget::OnTreeItemExpanded(const QModelIndex& index) @@ -1123,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() @@ -1142,7 +1163,7 @@ namespace AzToolsFramework { QTimer::singleShot(1, this, [this]() { m_gui->m_objectTree->setUpdatesEnabled(true); - m_gui->m_objectTree->expand(m_proxyModel->index(0,0)); + m_gui->m_objectTree->expandToDepth(0); }); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx index 78aced3587..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 @@ -41,6 +41,7 @@ namespace AzToolsFramework { class EditorEntityUiInterface; class EntityOutlinerListModel; + class EntityOutlinerContainerProxyModel; class EntityOutlinerSortFilterProxyModel; namespace EntityOutliner @@ -58,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 { @@ -100,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; @@ -117,6 +120,7 @@ namespace AzToolsFramework Ui::EntityOutlinerWidgetUI* m_gui; EntityOutlinerListModel* m_listModel; + EntityOutlinerContainerProxyModel* m_containerModel; EntityOutlinerSortFilterProxyModel* m_proxyModel; AZ::u64 m_selectionContextId; AZStd::vector m_selectedEntityIds; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 34152f8287..16e3c047b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -22,8 +23,13 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -34,7 +40,6 @@ #include #include - #include #include #include @@ -51,14 +56,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; @@ -89,6 +93,13 @@ namespace AzToolsFramework PrefabIntegrationManager::PrefabIntegrationManager() { + s_containerEntityInterface = AZ::Interface::Get(); + if (s_containerEntityInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get ContainerEntityInterface on PrefabIntegrationManager construction."); + return; + } + s_editorEntityUiInterface = AZ::Interface::Get(); if (s_editorEntityUiInterface == nullptr) { @@ -117,14 +128,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); @@ -135,6 +147,7 @@ namespace AzToolsFramework AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); + EditorEventsBus::Handler::BusDisconnect(); EditorContextMenuBus::Handler::BusDisconnect(); } @@ -163,12 +176,16 @@ namespace AzToolsFramework AzFramework::ApplicationRequests::Bus::BroadcastResult( prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + // Create Prefab { if (!selectedEntities.empty()) { - // Hide if the only selected entity is the Level Container - if (selectedEntities.size() > 1 || !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])) + // Hide if the only selected entity is the Focused Instance Container + if (selectedEntities.size() > 1 || + selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { bool layerInSelection = false; @@ -210,6 +227,16 @@ namespace AzToolsFramework instantiateAction, &QAction::triggered, instantiateAction, [] { ContextMenu_InstantiatePrefab(); }); } + // Instantiate Procedural Prefab + if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs()) + { + QAction* action = menu->addAction(QObject::tr("Instantiate Procedural Prefab...")); + action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab.")); + + QObject::connect( + action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); }); + } + menu->addSeparator(); bool itemWasShown = false; @@ -223,21 +250,16 @@ 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.")); + QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); + editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); - QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { - ContextMenu_EditPrefab(selectedEntity); - }); + QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { + ContextMenu_EditPrefab(selectedEntity); + }); - itemWasShown = true; - } + itemWasShown = true; } // Save Prefab @@ -266,8 +288,9 @@ namespace AzToolsFramework QAction* deleteAction = menu->addAction(QObject::tr("Delete")); QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); }); - if (selectedEntities.size() == 0 || - (selectedEntities.size() == 1 && s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0]))) + + if (selectedEntities.empty() || + (selectedEntities.size() == 1 && selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))) { deleteAction->setDisabled(true); } @@ -275,22 +298,27 @@ namespace AzToolsFramework // Detach Prefab if (selectedEntities.size() == 1) { - AZ::EntityId selectedEntity = selectedEntities[0]; + AZ::EntityId selectedEntityId = selectedEntities[0]; - if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) && - !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity)) + if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) && + selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab...")); QObject::connect( detachPrefabAction, &QAction::triggered, detachPrefabAction, - [selectedEntity] + [selectedEntityId] { - ContextMenu_DetachPrefab(selectedEntity); + ContextMenu_DetachPrefab(selectedEntityId); }); } } } + 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); @@ -307,15 +335,23 @@ namespace AzToolsFramework // temporarily null after QFileDialogs close, which we need in order to // be able to parent our message dialogs properly QWidget* activeWindow = QApplication::activeWindow(); - const AZStd::string prefabFilesPath = "@devassets@/Prefabs"; + const AZStd::string prefabFilesPath = "@projectroot@/Prefabs"; + + // Remove focused instance container entity if it's part of the list + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + auto focusedContainerIter = AZStd::find( + selectedEntities.begin(), selectedEntities.end(), + s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)); + if (focusedContainerIter != selectedEntities.end()) + { + selectedEntities.erase(focusedContainerIter); + } - // Remove Level entity if it's part of the list - - auto levelContainerIter = - AZStd::find(selectedEntities.begin(), selectedEntities.end(), s_prefabPublicInterface->GetLevelInstanceContainerEntityId()); - if (levelContainerIter != selectedEntities.end()) + if (selectedEntities.empty()) { - selectedEntities.erase(levelContainerIter); + return; } // Set default folder for prefabs @@ -427,9 +463,41 @@ namespace AzToolsFramework } } + void PrefabIntegrationManager::ContextMenu_InstantiateProceduralPrefab() + { + AZStd::string prefabAssetPath; + bool hasUserForProceduralPrefabAsset = QueryUserForProceduralPrefabAsset(prefabAssetPath); + + if (hasUserForProceduralPrefabAsset) + { + AZ::EntityId parentId; + AZ::Vector3 position = AZ::Vector3::CreateZero(); + + EntityIdList selectedEntities; + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities); + if (selectedEntities.size() == 1) + { + parentId = selectedEntities.front(); + AZ::TransformBus::EventResult(position, parentId, &AZ::TransformInterface::GetWorldTranslation); + } + else + { + // otherwise return since it needs to be inside an authored prefab + return; + } + + // Instantiating from context menu always puts the instance at the root level + auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabAssetPath, parentId, position); + if (!createPrefabOutcome.IsSuccess()) + { + WarnUserOfError("Prefab Instantiation Error", createPrefabOutcome.GetError()); + } + } + } + void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { - s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity); + s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity); } void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity) @@ -682,6 +750,33 @@ namespace AzToolsFramework return true; } + bool PrefabIntegrationManager::QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath) + { + using namespace AzToolsFramework; + auto selection = AssetBrowser::AssetSelectionModel::AssetTypeSelection(azrtti_typeid()); + EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection); + + if (!selection.IsValid()) + { + return false; + } + + auto product = azrtti_cast(selection.GetResult()); + if (product == nullptr) + { + return false; + } + + outPrefabAssetPath = product->GetRelativePath(); + + auto asset = AZ::Data::AssetManager::Instance().GetAsset( + product->GetAssetId(), + azrtti_typeid(), + AZ::Data::AssetLoadBehavior::Default); + + return asset.BlockUntilLoadComplete() != AZ::Data::AssetData::AssetStatus::Error; + } + void PrefabIntegrationManager::WarnUserOfError(AZStd::string_view title, AZStd::string_view message) { QWidget* activeWindow = QApplication::activeWindow(); @@ -1057,6 +1152,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::OnPrefabComponentActivate(AZ::EntityId entityId) { + // Register entity to appropriate UI Handler for UI overrides if (s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) { s_editorEntityUiInterface->RegisterEntity(entityId, m_levelRootUiHandler.GetHandlerId()); @@ -1064,11 +1160,32 @@ namespace AzToolsFramework else { s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId()); + + bool prefabWipFeaturesEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + + if (prefabWipFeaturesEnabled) + { + // Register entity as a container + s_containerEntityInterface->RegisterEntityAsContainer(entityId); + } } } void PrefabIntegrationManager::OnPrefabComponentDeactivate(AZ::EntityId entityId) { + bool prefabWipFeaturesEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + + if (prefabWipFeaturesEnabled && !s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) + { + // Unregister entity as a container + s_containerEntityInterface->UnregisterEntityAsContainer(entityId); + } + + // Unregister entity from UI Handler s_editorEntityUiInterface->UnregisterEntity(entityId); } @@ -1290,7 +1407,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 6f66b1f514..e8c10c150a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -26,9 +26,11 @@ namespace AzToolsFramework { + class ContainerEntityInterface; + namespace Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabLoaderInterface; //! Structure for saving/retrieving user settings related to prefab workflows. @@ -49,6 +51,7 @@ namespace AzToolsFramework class PrefabIntegrationManager final : public EditorContextMenuBus::Handler + , public EditorEventsBus::Handler , public AssetBrowser::AssetBrowserSourceDropBus::Handler , public PrefabInstanceContainerNotificationBus::Handler , public PrefabIntegrationInterface @@ -62,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; @@ -89,6 +95,7 @@ namespace AzToolsFramework // Context menu item handlers static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities); static void ContextMenu_InstantiatePrefab(); + static void ContextMenu_InstantiateProceduralPrefab(); static void ContextMenu_EditPrefab(AZ::EntityId containerEntity); static void ContextMenu_SavePrefab(AZ::EntityId containerEntity); static void ContextMenu_DeleteSelected(); @@ -99,6 +106,7 @@ namespace AzToolsFramework const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow, AZStd::string& outPrefabName, AZStd::string& outPrefabFilePath); static bool QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath); + static bool QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath); static void WarnUserOfError(AZStd::string_view title, AZStd::string_view message); // Path and filename generation @@ -134,8 +142,9 @@ namespace AzToolsFramework static const AZStd::string s_prefabFileExtension; + 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 c802850c4b..ccad85e32b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -8,7 +8,9 @@ #include -#include +#include + +#include #include #include @@ -33,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; } } @@ -81,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); } @@ -103,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; } @@ -176,12 +178,6 @@ namespace AzToolsFramework AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); - // We hide the root instance container entity from the Outliner, so avoid drawing its full container on children - if (m_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) - { - return; - } - const QTreeView* outlinerTreeView(qobject_cast(option.widget)); const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1; const int curveRectSize = m_prefabCapsuleRadius * 2; @@ -189,7 +185,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; } @@ -317,4 +313,17 @@ namespace AzToolsFramework return Internal_GetLastVisibleChild(model, lastChild); } + + void PrefabUiHandler::OnDoubleClick(AZ::EntityId entityId) const + { + bool prefabWipFeaturesEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + + if (prefabWipFeaturesEnabled) + { + // Focus on this prefab + 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 547c100eb1..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; }; @@ -29,16 +29,17 @@ namespace AzToolsFramework PrefabUiHandler(); ~PrefabUiHandler() override = default; - // EditorEntityUiHandler... + // EditorEntityUiHandler overrides ... QString GenerateItemInfoString(AZ::EntityId entityId) const override; QString GenerateItemTooltip(AZ::EntityId entityId) const override; QIcon GenerateItemIcon(AZ::EntityId entityId) const override; void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QModelIndex& descendantIndex) const override; + 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 + +// 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 -#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 57e6c9ff7a..6399a64635 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -9,12 +9,14 @@ #include "EditorHelpers.h" #include +#include #include #include #include #include -#include #include +#include +#include #include #include #include @@ -122,6 +124,11 @@ namespace AzToolsFramework "EditorHelpers - " "Focus Mode Interface could not be found. " "Check that it is being correctly initialized."); + + AZStd::vector> invalidClicks; + invalidClicks.push_back(AZStd::make_unique("Not in focus")); + invalidClicks.push_back(AZStd::make_unique()); + m_invalidClicks = AZStd::make_unique(AZStd::move(invalidClicks)); } AZ::EntityId EditorHelpers::HandleMouseInteraction( @@ -185,15 +192,35 @@ namespace AzToolsFramework } } - // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. - if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor)) + // verify if the entity Id corresponds to an entity that is focused; if not, halt selection. + if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { + if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() && + mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down || + mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick) + { + m_invalidClicks->AddInvalidClick(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); + } + 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. + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) + { + return containerEntityInterface->FindHighestSelectableEntity(entityIdUnderCursor); + } + return entityIdUnderCursor; } + void EditorHelpers::Display2d( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + m_invalidClicks->Display2d(viewportInfo, debugDisplay); + } + void EditorHelpers::DisplayHelpers( const AzFramework::ViewportInfo& viewportInfo, const AzFramework::CameraState& cameraState, @@ -208,7 +235,7 @@ namespace AzToolsFramework { const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex); - if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex)) + if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId)) { continue; } @@ -254,4 +281,24 @@ namespace AzToolsFramework } } } + + bool EditorHelpers::IsSelectableInViewport(const AZ::EntityId entityId) const + { + return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId); + } + + bool EditorHelpers::IsSelectableAccordingToFocusMode(const AZ::EntityId entityId) const + { + return m_focusModeInterface->IsInFocusSubTree(entityId); + } + + bool EditorHelpers::IsSelectableAccordingToContainerEntities(const AZ::EntityId entityId) const + { + if (const auto* 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..6623221bdc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include namespace AzFramework { @@ -58,8 +61,27 @@ namespace AzToolsFramework AzFramework::DebugDisplayRequests& debugDisplay, const AZStd::function& showIconCheck); + //! Handle 2d drawing for EditorHelper functionality. + void Display2d( + const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay); + + //! 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) const; + private: + //! Returns whether the entityId can be selected in the viewport according + //! to the current Editor Focus Mode setup. + bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId) const; + + //! Returns whether the entityId can be selected in the viewport according + //! to the current Container Entity setup. + bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId) const; + + AZStd::unique_ptr m_invalidClicks; //!< Display for invalid click behavior. + const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers. - const FocusModeInterface* m_focusModeInterface = nullptr; + const FocusModeInterface* m_focusModeInterface = nullptr; //!< API to interact with focus mode functionality. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index ccb72e1d50..c73742da4d 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]() - { - 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]() + [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(); }); @@ -3607,6 +3560,8 @@ namespace AzToolsFramework DrawAxisGizmo(viewportInfo, debugDisplay); m_boxSelect.Display2d(viewportInfo, debugDisplay); + + m_editorHelpers->Display2d(viewportInfo, debugDisplay); } void EditorTransformComponentSelection::RefreshSelectedEntityIds() @@ -3707,22 +3662,67 @@ 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); + switch (mode) + { + case ViewportEditorMode::Component: + { + SetAllViewportUiVisible(false); - EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); - EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); - ToolsApplicationNotificationBus::Handler::BusDisconnect(); + EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); + EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); + ToolsApplicationNotificationBus::Handler::BusDisconnect(); + } + break; + case ViewportEditorMode::Focus: + { + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode"); + } + break; + case ViewportEditorMode::Default: + case ViewportEditorMode::Pick: + // noop + break; + } } - void EditorTransformComponentSelection::LeftComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes) + void EditorTransformComponentSelection::OnEditorModeDeactivated( + const ViewportEditorModesInterface& editorModeState, const ViewportEditorMode mode) { - SetAllViewportUiVisible(true); + switch (mode) + { + case ViewportEditorMode::Component: + { + SetAllViewportUiVisible(true); - ToolsApplicationNotificationBus::Handler::BusConnect(); - EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); - EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); + ToolsApplicationNotificationBus::Handler::BusConnect(); + EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); + EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); + + // note: when leaving component mode, we check if we're still in focus mode (i.e. component mode was + // started from within focus mode), if we are, ensure we create/update the viewport border (as leaving + // component mode will attempt to remove it) + if (editorModeState.IsModeActive(ViewportEditorMode::Focus)) + { + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode"); + } + } + break; + case ViewportEditorMode::Focus: + { + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder); + } + break; + case ViewportEditorMode::Default: + case ViewportEditorMode::Pick: + // noop + break; + } } 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/ViewportSelection/InvalidClicks.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.cpp new file mode 100644 index 0000000000..dfff4da08f --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.cpp @@ -0,0 +1,142 @@ +/* + * 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 + +AZ_CVAR(float, ed_invalidClickRadius, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum invalid click radius to expand to"); +AZ_CVAR(float, ed_invalidClickDuration, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Duration to display the invalid click feedback"); +AZ_CVAR(float, ed_invalidClickMessageSize, 0.8f, nullptr, AZ::ConsoleFunctorFlags::Null, "Size of text for invalid message"); +AZ_CVAR( + float, + ed_invalidClickMessageVerticalOffset, + 30.0f, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Vertical offset from cursor of invalid click message"); + +namespace AzToolsFramework +{ + void ExpandingFadingCircles::Begin(const AzFramework::ScreenPoint& screenPoint) + { + FadingCircle fadingCircle; + fadingCircle.m_position = screenPoint; + fadingCircle.m_opacity = 1.0f; + fadingCircle.m_radius = 0.0f; + m_fadingCircles.push_back(fadingCircle); + } + + void ExpandingFadingCircles::Update(const float deltaTime) + { + for (auto& fadingCircle : m_fadingCircles) + { + fadingCircle.m_opacity = AZStd::max(fadingCircle.m_opacity - (deltaTime / ed_invalidClickDuration), 0.0f); + fadingCircle.m_radius += deltaTime * ed_invalidClickRadius; + } + + m_fadingCircles.erase( + AZStd::remove_if( + m_fadingCircles.begin(), m_fadingCircles.end(), + [](const FadingCircle& fadingCircle) + { + return fadingCircle.m_opacity <= 0.0f; + }), + m_fadingCircles.end()); + } + + bool ExpandingFadingCircles::Updating() + { + return !m_fadingCircles.empty(); + } + + void ExpandingFadingCircles::Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + const AZ::Vector2 viewportSize = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId).m_viewportSize; + + for (const auto& fadingCircle : m_fadingCircles) + { + const auto position = AzFramework::Vector2FromScreenPoint(fadingCircle.m_position) / viewportSize; + debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, fadingCircle.m_opacity)); + debugDisplay.DrawWireCircle2d(position, fadingCircle.m_radius * 0.005f, 0.0f); + } + } + + void FadingText::Begin(const AzFramework::ScreenPoint& screenPoint) + { + m_opacity = 1.0f; + m_invalidClickPosition = screenPoint; + } + + void FadingText::Update(const float deltaTime) + { + m_opacity -= deltaTime / ed_invalidClickDuration; + } + + bool FadingText::Updating() + { + return m_opacity >= 0.0f; + } + + void FadingText::Display( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + if (constexpr float MinOpacity = 0.05f; m_opacity >= MinOpacity) + { + debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, m_opacity)); + debugDisplay.Draw2dTextLabel( + aznumeric_cast(m_invalidClickPosition.m_x), + aznumeric_cast(m_invalidClickPosition.m_y) - ed_invalidClickMessageVerticalOffset, ed_invalidClickMessageSize, + m_message.c_str(), true); + } + } + + void InvalidClicks::AddInvalidClick(const AzFramework::ScreenPoint& screenPoint) + { + AZ::TickBus::Handler::BusConnect(); + + for (auto& invalidClickBehavior : m_invalidClickBehaviors) + { + invalidClickBehavior->Begin(screenPoint); + } + } + + void InvalidClicks::OnTick(const float deltaTime, [[maybe_unused]] const AZ::ScriptTimePoint time) + { + for (auto& invalidClickBehavior : m_invalidClickBehaviors) + { + invalidClickBehavior->Update(deltaTime); + } + + const auto updating = AZStd::any_of( + m_invalidClickBehaviors.begin(), m_invalidClickBehaviors.end(), + [](const auto& invalidClickBehavior) + { + return invalidClickBehavior->Updating(); + }); + + if (!updating && AZ::TickBus::Handler::BusIsConnected()) + { + AZ::TickBus::Handler::BusDisconnect(); + } + } + + void InvalidClicks::Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + debugDisplay.DepthTestOff(); + + for (const auto& invalidClickBehavior : m_invalidClickBehaviors) + { + invalidClickBehavior->Display(viewportInfo, debugDisplay); + } + + debugDisplay.DepthTestOn(); + } +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.h new file mode 100644 index 0000000000..55a6d614ea --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.h @@ -0,0 +1,108 @@ +/* + * 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 AzFramework +{ + class DebugDisplayRequests; + struct ViewportInfo; +} // namespace AzFramework + +namespace AzToolsFramework +{ + namespace ViewportInteraction + { + struct MouseInteractionEvent; + } + + //! An interface to provide invalid click feedback in the editor viewport. + class InvalidClick + { + public: + virtual ~InvalidClick() = default; + + //! Begin the feedback. + //! @param screenPoint The position of the click in screen coordinates. + virtual void Begin(const AzFramework::ScreenPoint& screenPoint) = 0; + //! Update the invalid click feedback + virtual void Update(float deltaTime) = 0; + //! Report if the click feedback is running or not (returning false will signal the TickBus can be disconnected from). + virtual bool Updating() = 0; + //! Display the click feedback in the viewport. + virtual void Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) = 0; + }; + + //! Display expanding fading circles for every click of the mouse that is invalid. + class ExpandingFadingCircles : public InvalidClick + { + public: + void Begin(const AzFramework::ScreenPoint& screenPoint) override; + void Update(float deltaTime) override; + bool Updating() override; + void Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + private: + //! Stores a circle representation with a lifetime to grow and fade out over time. + struct FadingCircle + { + AzFramework::ScreenPoint m_position; + float m_radius; + float m_opacity; + }; + + using FadingCircles = AZStd::vector; + FadingCircles m_fadingCircles; //!< Collection of fading circles to draw for clicks that have no effect. + }; + + //! Display fading text where an invalid click happened. + //! @note There is only one fading text, each click will update its position. + class FadingText : public InvalidClick + { + public: + explicit FadingText(AZStd::string message) + : m_message(AZStd::move(message)) + { + } + + void Begin(const AzFramework::ScreenPoint& screenPoint) override; + void Update(float deltaTime) override; + bool Updating() override; + void Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + private: + AZStd::string m_message; //!< Message to display for fading text. + float m_opacity = 1.0f; //!< The opacity of the invalid click message. + AzFramework::ScreenPoint m_invalidClickPosition; //!< The position to display the invalid click message. + }; + + //! Interface to begin invalid click feedback (will run all added InvalidClick behaviors). + class InvalidClicks : private AZ::TickBus::Handler + { + public: + explicit InvalidClicks(AZStd::vector> invalidClickBehaviors) + : m_invalidClickBehaviors(AZStd::move(invalidClickBehaviors)) + { + } + + //! Add an invalid click and activate one or more of the added invalid click behaviors. + void AddInvalidClick(const AzFramework::ScreenPoint& screenPoint); + + //! Handle 2d drawing for EditorHelper functionality. + void Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay); + + private: + //! AZ::TickBus overrides ... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + AZStd::vector> m_invalidClickBehaviors; //!< Invalid click behaviors to run. + }; +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 80cc7941b0..a289d914d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -290,9 +290,9 @@ namespace AzToolsFramework::ViewportUi::Internal return false; } - void ViewportUiDisplay::CreateComponentModeBorder(const AZStd::string& borderTitle) + void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle) { - AZStd::string styleSheet = AZStd::string::format( + const AZStd::string styleSheet = AZStd::string::format( "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, TopHighlightBorderSize, HighlightBorderColor); m_uiOverlay.setStyleSheet(styleSheet.c_str()); @@ -303,7 +303,7 @@ namespace AzToolsFramework::ViewportUi::Internal m_componentModeBorderText.setText(borderTitle.c_str()); } - void ViewportUiDisplay::RemoveComponentModeBorder() + void ViewportUiDisplay::RemoveViewportBorder() { m_componentModeBorderText.setVisible(false); m_uiOverlay.setStyleSheet("border: none;"); @@ -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() @@ -420,6 +420,7 @@ namespace AzToolsFramework::ViewportUi::Internal m_uiMainWindow.setVisible(true); m_uiOverlay.setVisible(true); } + m_uiMainWindow.setMask(region); } @@ -437,6 +438,7 @@ namespace AzToolsFramework::ViewportUi::Internal { return element->second; } + return ViewportUiElementInfo{ nullptr, InvalidViewportUiElementId, false }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h index aafaf61ff3..5020241815 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h @@ -89,8 +89,8 @@ namespace AzToolsFramework::ViewportUi::Internal AZStd::shared_ptr GetViewportUiElement(ViewportUiElementId elementId); bool IsViewportUiElementVisible(ViewportUiElementId elementId); - void CreateComponentModeBorder(const AZStd::string& borderTitle); - void RemoveComponentModeBorder(); + void CreateViewportBorder(const AZStd::string& borderTitle); + void RemoveViewportBorder(); private: void PrepareWidgetForViewportUi(QPointer widget); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp index 255d11f561..1f14b12b7d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp @@ -240,14 +240,14 @@ namespace AzToolsFramework::ViewportUi } } - void ViewportUiManager::CreateComponentModeBorder(const AZStd::string& borderTitle) + void ViewportUiManager::CreateViewportBorder(const AZStd::string& borderTitle) { - m_viewportUi->CreateComponentModeBorder(borderTitle); + m_viewportUi->CreateViewportBorder(borderTitle); } - void ViewportUiManager::RemoveComponentModeBorder() + void ViewportUiManager::RemoveViewportBorder() { - m_viewportUi->RemoveComponentModeBorder(); + m_viewportUi->RemoveViewportBorder(); } void ViewportUiManager::PressButton(ClusterId clusterId, ButtonId buttonId) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h index 9ec6648451..ce7e5aafe9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h @@ -50,8 +50,8 @@ namespace AzToolsFramework::ViewportUi void RegisterTextFieldCallback(TextFieldId textFieldId, AZ::Event::Handler& handler) override; void RemoveTextField(TextFieldId textFieldId) override; void SetTextFieldVisible(TextFieldId textFieldId, bool visible) override; - void CreateComponentModeBorder(const AZStd::string& borderTitle) override; - void RemoveComponentModeBorder() override; + void CreateViewportBorder(const AZStd::string& borderTitle) override; + void RemoveViewportBorder() override; void PressButton(ClusterId clusterId, ButtonId buttonId) override; void PressButton(SwitcherId switcherId, ButtonId buttonId) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h index 108d23950a..3c6f7094cb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h @@ -78,7 +78,7 @@ namespace AzToolsFramework::ViewportUi virtual void RegisterSwitcherEventHandler(SwitcherId switcherId, AZ::Event::Handler& handler) = 0; //! Removes a cluster from the Viewport UI system. virtual void RemoveCluster(ClusterId clusterId) = 0; - //! + //! Removes a switcher from the Viewport UI system. virtual void RemoveSwitcher(SwitcherId switcherId) = 0; //! Sets the visibility of the cluster. virtual void SetClusterVisible(ClusterId clusterId, bool visible) = 0; @@ -96,12 +96,12 @@ namespace AzToolsFramework::ViewportUi //! Sets the visibility of the text field. virtual void SetTextFieldVisible(TextFieldId textFieldId, bool visible) = 0; //! Create the highlight border for Component Mode. - virtual void CreateComponentModeBorder(const AZStd::string& borderTitle) = 0; + virtual void CreateViewportBorder(const AZStd::string& borderTitle) = 0; //! Remove the highlight border for Component Mode. - virtual void RemoveComponentModeBorder() = 0; - //! Invoke a button press in a cluster. + virtual void RemoveViewportBorder() = 0; + //! Invoke a button press on a cluster. virtual void PressButton(ClusterId clusterId, ButtonId buttonId) = 0; - //! + //! Invoke a button press on a switcher. virtual void PressButton(SwitcherId switcherId, ButtonId buttonId) = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index f09d4f9b72..5db65f89f4 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 @@ -114,6 +116,10 @@ set(FILES Component/EditorLevelComponentAPIBus.h Component/EditorLevelComponentAPIComponent.cpp Component/EditorLevelComponentAPIComponent.h + ContainerEntity/ContainerEntityInterface.h + ContainerEntity/ContainerEntityNotificationBus.h + ContainerEntity/ContainerEntitySystemComponent.cpp + ContainerEntity/ContainerEntitySystemComponent.h Editor/EditorContextMenuBus.h Editor/EditorSettingsAPIBus.h Entity/EditorEntityStartStatus.h @@ -148,6 +154,8 @@ set(FILES Entity/SliceEditorEntityOwnershipService.h Entity/SliceEditorEntityOwnershipService.cpp Entity/SliceEditorEntityOwnershipServiceBus.h + Entity/EntityUtilityComponent.h + Entity/EntityUtilityComponent.cpp Fingerprinting/TypeFingerprinter.h Fingerprinting/TypeFingerprinter.cpp FocusMode/FocusModeInterface.h @@ -545,6 +553,8 @@ set(FILES ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp ViewportSelection/EditorVisibleEntityDataCache.h ViewportSelection/EditorVisibleEntityDataCache.cpp + ViewportSelection/InvalidClicks.h + ViewportSelection/InvalidClicks.cpp ViewportSelection/ViewportEditorModeTracker.cpp ViewportSelection/ViewportEditorModeTracker.h ToolsFileUtils/ToolsFileUtils.h @@ -638,13 +648,22 @@ 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 Prefab/PrefabLoaderInterface.h + Prefab/PrefabLoaderScriptingBus.h + Prefab/ScriptingPrefabLoader.h + Prefab/ScriptingPrefabLoader.cpp Prefab/PrefabSystemComponent.h Prefab/PrefabSystemComponent.cpp Prefab/PrefabSystemComponentInterface.h + Prefab/PrefabSystemScriptingBus.h + Prefab/PrefabSystemScriptingHandler.h + Prefab/PrefabSystemScriptingHandler.cpp Prefab/Instance/Instance.h Prefab/Instance/Instance.cpp Prefab/Instance/InstanceSerializer.h @@ -667,6 +686,8 @@ set(FILES Prefab/Instance/TemplateInstanceMapperInterface.h Prefab/Link/Link.h Prefab/Link/Link.cpp + Prefab/Procedural/ProceduralPrefabAsset.h + Prefab/Procedural/ProceduralPrefabAsset.cpp Prefab/PrefabPublicHandler.h Prefab/PrefabPublicHandler.cpp Prefab/PrefabPublicInterface.h diff --git a/Code/Framework/AzToolsFramework/Tests/ArchiveTests.cpp b/Code/Framework/AzToolsFramework/Tests/ArchiveTests.cpp index 66520c804c..395e4c9049 100644 --- a/Code/Framework/AzToolsFramework/Tests/ArchiveTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ArchiveTests.cpp @@ -131,13 +131,13 @@ namespace UnitTest m_app.reset(aznew ToolsTestApplication("ArchiveComponentTest")); m_app->Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr) { - fileIoBase->SetAlias("@assets@", m_tempDir.GetDirectory()); + fileIoBase->SetAlias("@products@", m_tempDir.GetDirectory()); } } diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 24488f3ed3..827737f561 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -37,10 +37,10 @@ namespace // anonymous bool Search(const AzToolsFramework::AssetFileInfoList& assetList, const AZ::Data::AssetId& assetId) { - return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(), - [&](AzToolsFramework::AssetFileInfo fileInfo) - { - return fileInfo.m_assetId == assetId; + return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(), + [&](AzToolsFramework::AssetFileInfo fileInfo) + { + return fileInfo.m_assetId == assetId; }); } } @@ -74,11 +74,11 @@ namespace UnitTest m_application->Start(AzFramework::Application::Descriptor()); - // By default @assets@ is setup to include the platform at the end. But this test is going to + // By default @products@ is setup to include the platform at the end. But this test is going to // loop over platforms and it will be included as part of the relative path of the file. // So the asset folder for these tests have to point to the cache project root folder, which // doesn't include the platform. - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str()); for (int idx = 0; idx < s_totalAssets; idx++) { @@ -158,17 +158,17 @@ namespace UnitTest m_assetRegistry->RegisterAssetDependency(assets[5], AZ::Data::ProductDependency(assets[6], 0)); m_assetRegistry->RegisterAssetDependency(assets[6], AZ::Data::ProductDependency(assets[7], 0)); - // asset8 -> asset6 + // asset8 -> asset6 m_assetRegistry->RegisterAssetDependency(assets[8], AZ::Data::ProductDependency(assets[6], 0)); - // asset10 -> asset11 + // asset10 -> asset11 m_assetRegistry->RegisterAssetDependency(assets[10], AZ::Data::ProductDependency(assets[11], 0)); - // asset11 -> asset10 + // asset11 -> asset10 m_assetRegistry->RegisterAssetDependency(assets[11], AZ::Data::ProductDependency(assets[10], 0)); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); @@ -203,10 +203,6 @@ namespace UnitTest const AZStd::string engroot = AZ::Test::GetEngineRootPath(); AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str()); - - AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath()); - assetRoot /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); } void TearDown() override @@ -219,7 +215,7 @@ namespace UnitTest delete m_application; } - AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override + AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override { auto foundIter = m_assetRegistry->m_assetIdToInfo.find(id); if (foundIter != m_assetRegistry->m_assetIdToInfo.end()) @@ -540,7 +536,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[7])); EXPECT_TRUE(Search(assetList, assets[8])); - // Removing the android flag from the asset should still produce the same result + // Removing the android flag from the asset should still produce the same result m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -564,7 +560,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[3])); EXPECT_TRUE(Search(assetList, assets[4])); - // Adding the android flag again to the asset + // Adding the android flag again to the asset m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID); @@ -624,7 +620,7 @@ namespace UnitTest EXPECT_EQ(assetList1.m_fileInfoList[0].m_assetId, assetList2.m_fileInfoList[0].m_assetId); EXPECT_GE(assetList2.m_fileInfoList[0].m_modificationTime, assetList1.m_fileInfoList[0].m_modificationTime); // file mod time should change - + // file hash should not change for (int idx = 0; idx < 5; idx++) { @@ -680,7 +676,7 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[validFileIndex], AzFramework::PlatformFlags::Platform_PC, m_assetsPath[invalidFileIndex]); const AzFramework::AssetSeedList& oldSeedList = m_assetSeedManager->GetAssetSeedList(); - + for (const auto& seedInfo : oldSeedList) { if (seedInfo.m_assetId == assets[validFileIndex]) diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h b/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h index 2678c2f57c..ba3e38ba8f 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h +++ b/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h @@ -18,8 +18,6 @@ namespace UnitTests { public: MOCK_METHOD1(GetAbsoluteAssetDatabaseLocation, bool(AZStd::string&)); - MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ()); - MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ()); MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath)); MOCK_METHOD3(GenerateRelativeSourcePath, bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder)); diff --git a/Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp b/Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp new file mode 100644 index 0000000000..fce44d4833 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp @@ -0,0 +1,252 @@ +/* + * 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 UnitTest +{ + // Global variables for communicating between Lua test code and C++ + AZ::EntityId g_globalEntityId = AZ::EntityId{}; + AZStd::string g_globalString = ""; + AzFramework::BehaviorComponentId g_globalComponentId = {}; + AZStd::vector g_globalComponentDetails = {}; + bool g_globalBool = false; + + class EntityUtilityComponentTests + : public ToolsApplicationFixture + { + void InitProperties() + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + ASSERT_NE(componentApplicationRequests, nullptr); + + auto behaviorContext = componentApplicationRequests->GetBehaviorContext(); + + ASSERT_NE(behaviorContext, nullptr); + + behaviorContext->Property("g_globalEntityId", BehaviorValueProperty(&g_globalEntityId)); + behaviorContext->Property("g_globalString", BehaviorValueProperty(&g_globalString)); + behaviorContext->Property("g_globalComponentId", BehaviorValueProperty(&g_globalComponentId)); + behaviorContext->Property("g_globalBool", BehaviorValueProperty(&g_globalBool)); + behaviorContext->Property("g_globalComponentDetails", BehaviorValueProperty(&g_globalComponentDetails)); + + g_globalEntityId = AZ::EntityId{}; + g_globalString = AZStd::string{}; + g_globalComponentId = AzFramework::BehaviorComponentId{}; + g_globalBool = false; + g_globalComponentDetails = AZStd::vector{}; + } + + void SetUpEditorFixtureImpl() override + { + InitProperties(); + } + + void TearDownEditorFixtureImpl() override + { + g_globalString.set_capacity(0); // Free all memory + g_globalComponentDetails.set_capacity(0); + } + }; + + TEST_F(EntityUtilityComponentTests, CreateEntity) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + my_entity = Entity(g_globalEntityId) + g_globalString = my_entity:GetName() + )LUA"); + + EXPECT_NE(g_globalEntityId, AZ::EntityId{}); + EXPECT_STREQ(g_globalString.c_str(), "test"); + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + ASSERT_NE(entity, nullptr); + + // Test cleaning up, make sure the entity is destroyed + AzToolsFramework::EntityUtilityBus::Broadcast(&AzToolsFramework::EntityUtilityBus::Events::ResetEntityContext); + + entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + ASSERT_EQ(entity, nullptr); + } + + TEST_F(EntityUtilityComponentTests, CreateEntityEmptyName) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("") + )LUA"); + + EXPECT_NE(g_globalEntityId, AZ::EntityId{}); + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + ASSERT_NE(entity, nullptr); + } + + TEST_F(EntityUtilityComponentTests, FindComponent) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0 TransformComponent") + )LUA"); + + EXPECT_TRUE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, InvalidComponentName) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ThisIsNotAComponent-Error") + )LUA"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, InvalidComponentId) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "{1234-hello-world-this-is-not-an-id}") + )LUA"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // Should get 1 error stating the type id is not valid + EXPECT_FALSE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, CreateComponent) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ScriptEditorComponent") + )LUA"); + + EXPECT_TRUE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, UpdateComponent) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + comp_id = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(g_globalEntityId, "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent") + json_update = [[ + { + "Transform Data": { "Rotate": [0.0, 0.1, 180.0] } + } + ]] + g_globalBool = EntityUtilityBus.Broadcast.UpdateComponentForEntity(g_globalEntityId, comp_id, json_update); + )LUA"); + + EXPECT_TRUE(g_globalBool); + EXPECT_NE(g_globalEntityId, AZ::EntityId(AZ::EntityId::InvalidEntityId)); + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + auto* transformComponent = entity->FindComponent(); + + ASSERT_NE(transformComponent, nullptr); + + AZ::Vector3 localRotation = transformComponent->GetLocalRotationQuaternion().GetEulerDegrees(); + + EXPECT_EQ(localRotation, AZ::Vector3(.0f, 0.1f, 180.0f)); + } + + TEST_F(EntityUtilityComponentTests, GetComponentJson) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("ScriptEditorComponent") + )LUA"); + + EXPECT_STRNE(g_globalString.c_str(), ""); + } + + TEST_F(EntityUtilityComponentTests, GetComponentJsonDoesNotExist) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("404") + )LUA"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // 1 error: Failed to find component id for type name 404 + + EXPECT_STREQ(g_globalString.c_str(), ""); + } + + TEST_F(EntityUtilityComponentTests, SearchComponents) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("Transform*") + )LUA"); + + // There should be 2 transform components + EXPECT_EQ(g_globalComponentDetails.size(), 2); + } + + TEST_F(EntityUtilityComponentTests, SearchComponentsNotFound) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("404") + )LUA"); + + EXPECT_EQ(g_globalComponentDetails.size(), 0); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/EntityTestbed.h b/Code/Framework/AzToolsFramework/Tests/EntityTestbed.h index 2128b3676a..1bf51045c0 100644 --- a/Code/Framework/AzToolsFramework/Tests/EntityTestbed.h +++ b/Code/Framework/AzToolsFramework/Tests/EntityTestbed.h @@ -181,8 +181,8 @@ namespace UnitTest const char* dir = m_componentApplication->GetExecutableFolder(); - m_localFileIO.SetAlias("@assets@", dir); - m_localFileIO.SetAlias("@devassets@", dir); + m_localFileIO.SetAlias("@products@", dir); + m_localFileIO.SetAlias("@projectroot@", dir); } void Destroy() 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]); - - 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); - } + m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); - // 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]); - - 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); - } + m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]); - // 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]); - - 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); + 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); } 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/PlatformAddressedAssetCatalogTests.cpp b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp index 67d40be376..9c226cbb1b 100644 --- a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp @@ -22,7 +22,7 @@ #include #include -namespace +namespace { static const int s_totalAssets = 12; } @@ -53,15 +53,15 @@ namespace UnitTest m_application->Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); - // By default @assets@ is setup to include the platform at the end. But this test is going to + // By default @products@ is setup to include the platform at the end. But this test is going to // loop over all platforms and it will be included as part of the relative path of the file. // So the asset folder for these tests have to point to the cache project root folder, which // doesn't include the platform. - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str()); for (int platformNum = AzFramework::PlatformId::PC; platformNum < AzFramework::PlatformId::NumPlatformIds; ++platformNum) { 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/Prefab/PrefabScriptingTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp new file mode 100644 index 0000000000..7e61c50629 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp @@ -0,0 +1,173 @@ +/* + * 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 UnitTest +{ + TemplateId g_globalTemplateId = {}; + AZStd::string g_globalPrefabString = ""; + + class PrefabScriptingTest : public PrefabTestFixture + { + void InitProperties() const + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + ASSERT_NE(componentApplicationRequests, nullptr); + + auto behaviorContext = componentApplicationRequests->GetBehaviorContext(); + + ASSERT_NE(behaviorContext, nullptr); + + behaviorContext->Property("g_globalTemplateId", BehaviorValueProperty(&g_globalTemplateId)); + behaviorContext->Property("g_globalPrefabString", BehaviorValueProperty(&g_globalPrefabString)); + + g_globalTemplateId = TemplateId{}; + g_globalPrefabString = AZStd::string{}; + } + + void SetUpEditorFixtureImpl() override + { + InitProperties(); + } + + void TearDownEditorFixtureImpl() override + { + g_globalPrefabString.set_capacity(0); // Free all memory + } + }; + + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + entities:push_back(my_id) + g_globalTemplateId = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab") + )LUA"); + + EXPECT_NE(g_globalTemplateId, TemplateId{}); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + ASSERT_NE(prefabSystemComponentInterface, nullptr); + + TemplateReference templateRef = prefabSystemComponentInterface->FindTemplate(g_globalTemplateId); + + EXPECT_TRUE(templateRef); + } + + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab_NoEntities) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + g_globalTemplateId = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab") + )LUA"); + + EXPECT_NE(g_globalTemplateId, TemplateId{}); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + ASSERT_NE(prefabSystemComponentInterface, nullptr); + + TemplateReference templateRef = prefabSystemComponentInterface->FindTemplate(g_globalTemplateId); + + EXPECT_TRUE(templateRef); + } + + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab_NoPath) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + template_id = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "") + )LUA"); + /* + error: PrefabSystemComponent::CreateTemplateFromInstance - Attempted to create a prefab template from an instance without a source file path. Unable to proceed. + error: Failed to create a Template associated with file path during CreatePrefab. + error: Failed to create prefab + */ + AZ_TEST_STOP_TRACE_SUPPRESSION(3); + } + + TEST_F(PrefabScriptingTest, PrefabScripting_SaveToString) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + entities:push_back(my_id) + template_id = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab") + my_result = PrefabLoaderScriptingBus.Broadcast.SaveTemplateToString(template_id) + + if my_result:IsSuccess() then + g_globalPrefabString = my_result:GetValue() + end + )LUA"); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + prefabSystemComponentInterface->RemoveAllTemplates(); + + EXPECT_STRNE(g_globalPrefabString.c_str(), ""); + TemplateId templateFromString = AZ::Interface::Get()->LoadTemplateFromString(g_globalPrefabString); + + EXPECT_NE(templateFromString, InvalidTemplateId); + + // Create another entity for comparison purposes + AZ::EntityId entityId; + AzToolsFramework::EntityUtilityBus::BroadcastResult( + entityId, &AzToolsFramework::EntityUtilityBus::Events::CreateEditorReadyEntity, "test"); + + AZ::Entity* testEntity = AZ::Interface::Get()->FindEntity(entityId); + + // Instantiate the prefab we saved + AZStd::unique_ptr instance = prefabSystemComponentInterface->InstantiatePrefab(templateFromString); + + EXPECT_NE(instance, nullptr); + + AZStd::vector loadedEntities; + + // Get the entities from the instance + instance->GetConstEntities( + [&loadedEntities](const AZ::Entity& entity) + { + loadedEntities.push_back(&entity); + return true; + }); + + // Make sure the instance has an entity with the same number of components as our test entity + EXPECT_EQ(loadedEntities.size(), 1); + EXPECT_EQ(loadedEntities[0]->GetComponents().size(), testEntity->GetComponents().size()); + + g_globalPrefabString.set_capacity(0); // Free all memory + } + +} diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp new file mode 100644 index 0000000000..0b9e73bbac --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp @@ -0,0 +1,135 @@ +/* + * 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 UnitTest +{ + class ProceduralPrefabAssetTest + : public PrefabTestFixture + { + void SetUpEditorFixtureImpl() override + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + ASSERT_NE(componentApplicationRequests, nullptr); + + auto* behaviorContext = componentApplicationRequests->GetBehaviorContext(); + ASSERT_NE(behaviorContext, nullptr); + + auto* jsonRegistrationContext = componentApplicationRequests->GetJsonRegistrationContext(); + ASSERT_NE(jsonRegistrationContext, nullptr); + + auto* serializeContext = componentApplicationRequests->GetSerializeContext(); + ASSERT_NE(serializeContext, nullptr); + + AZ::Prefab::ProceduralPrefabAsset::Reflect(serializeContext); + AZ::Prefab::ProceduralPrefabAsset::Reflect(behaviorContext); + AZ::Prefab::ProceduralPrefabAsset::Reflect(jsonRegistrationContext); + } + + void TearDownEditorFixtureImpl() override + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + componentApplicationRequests->GetJsonRegistrationContext()->EnableRemoveReflection(); + AZ::Prefab::ProceduralPrefabAsset::Reflect(componentApplicationRequests->GetJsonRegistrationContext()); + } + }; + + TEST_F(ProceduralPrefabAssetTest, ReflectContext_AccessMethods_Works) + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + auto* serializeContext = componentApplicationRequests->GetSerializeContext(); + EXPECT_TRUE(!serializeContext->CreateAny(azrtti_typeid()).empty()); + EXPECT_TRUE(!serializeContext->CreateAny(azrtti_typeid()).empty()); + + auto* jsonRegistrationContext = componentApplicationRequests->GetJsonRegistrationContext(); + EXPECT_TRUE(jsonRegistrationContext->GetSerializerForSerializerType(azrtti_typeid())); + } + + TEST_F(ProceduralPrefabAssetTest, ProceduralPrefabAsset_AccessMethods_Works) + { + const auto templateId = TemplateId(1); + const auto prefabString = "fake.prefab"; + + AZ::Prefab::ProceduralPrefabAsset asset{}; + asset.SetTemplateId(templateId); + EXPECT_EQ(asset.GetTemplateId(), templateId); + + asset.SetTemplateName(prefabString); + EXPECT_EQ(asset.GetTemplateName(), prefabString); + } + + TEST_F(ProceduralPrefabAssetTest, PrefabDomData_AccessMethods_Works) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("boolValue", true, dom.GetAllocator()); + + AZ::Prefab::PrefabDomData prefabDomData; + prefabDomData.CopyValue(dom); + + const AzToolsFramework::Prefab::PrefabDom& result = prefabDomData.GetValue(); + EXPECT_TRUE(result.HasMember("boolValue")); + EXPECT_TRUE(result.FindMember("boolValue")->value.GetBool()); + } + + TEST_F(ProceduralPrefabAssetTest, PrefabDomDataJsonSerializer_Load_Works) + { + AZ::Prefab::PrefabDomData prefabDomData; + + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("member", "value", dom.GetAllocator()); + + AZ::Prefab::PrefabDomDataJsonSerializer prefabDomDataJsonSerializer; + AZ::JsonDeserializerSettings settings; + settings.m_reporting = [](auto, auto, auto) + { + AZ::JsonSerializationResult::ResultCode result(AZ::JsonSerializationResult::Tasks::ReadField); + return result; + }; + AZ::JsonDeserializerContext context{ settings }; + + auto result = prefabDomDataJsonSerializer.Load(&prefabDomData, azrtti_typeid(prefabDomData), dom, context); + EXPECT_EQ(result.GetResultCode().GetOutcome(), AZ::JsonSerializationResult::Outcomes::DefaultsUsed); + EXPECT_TRUE(prefabDomData.GetValue().HasMember("member")); + EXPECT_STREQ(prefabDomData.GetValue().FindMember("member")->value.GetString(), "value"); + } + + TEST_F(ProceduralPrefabAssetTest, PrefabDomDataJsonSerializer_Store_Works) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("member", "value", dom.GetAllocator()); + + AZ::Prefab::PrefabDomData prefabDomData; + prefabDomData.CopyValue(dom); + + AZ::Prefab::PrefabDomDataJsonSerializer prefabDomDataJsonSerializer; + AzToolsFramework::Prefab::PrefabDom outputValue; + AZ::JsonSerializerSettings settings; + settings.m_reporting = [](auto, auto, auto) + { + AZ::JsonSerializationResult::ResultCode result(AZ::JsonSerializationResult::Tasks::WriteValue); + return result; + }; + AZ::JsonSerializerContext context{ settings, outputValue.GetAllocator() }; + auto result = prefabDomDataJsonSerializer.Store(outputValue, &prefabDomData, nullptr, azrtti_typeid(prefabDomData), context); + EXPECT_EQ(result.GetResultCode().GetOutcome(), AZ::JsonSerializationResult::Outcomes::DefaultsUsed); + EXPECT_TRUE(outputValue.HasMember("member")); + EXPECT_STREQ(outputValue.FindMember("member")->value.GetString(), "value"); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h index 93e86374db..01b40fbb16 100644 --- a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h +++ b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h @@ -142,8 +142,6 @@ namespace UnitTest /* * AssetSystemRequestBus */ - const char* GetAbsoluteDevGameFolderPath() override { return ""; } - const char* GetAbsoluteDevRootFolderPath() override { return ""; } bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return false; } bool GenerateRelativeSourcePath( [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 11ea5a0b38..ef6774f620 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -27,6 +27,7 @@ set(FILES Entity/EditorEntityHelpersTests.cpp Entity/EditorEntitySearchComponentTests.cpp Entity/EditorEntitySelectionTests.cpp + Entity/EntityUtilityComponentTests.cpp EntityIdQLabelTests.cpp EntityInspectorTests.cpp EntityOwnershipService/EntityOwnershipServiceTestFixture.cpp @@ -36,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 @@ -91,6 +95,8 @@ set(FILES Prefab/SpawnableSortEntitiesTestFixture.cpp Prefab/SpawnableSortEntitiesTestFixture.h Prefab/SpawnableSortEntitiesTests.cpp + Prefab/PrefabScriptingTests.cpp + Prefab/ProceduralPrefabAssetTests.cpp PropertyIntCtrlCommonTests.h PropertyIntSliderCtrlTests.cpp PropertyIntSpinCtrlTests.cpp diff --git a/Code/Framework/GridMate/Tests/Carrier.cpp b/Code/Framework/GridMate/Tests/Carrier.cpp index 5b18a80221..29550d8b68 100644 --- a/Code/Framework/GridMate/Tests/Carrier.cpp +++ b/Code/Framework/GridMate/Tests/Carrier.cpp @@ -333,7 +333,7 @@ namespace UnitTest }; template - class Integ_CarrierAsyncHandshakeTestTemplate + class CarrierAsyncHandshakeTestTemplate : public GridMateMPTestFixture , protected SocketProvider { @@ -761,7 +761,7 @@ namespace UnitTest }; template - class Integ_CarrierDisconnectDetectionTestTemplate + class CarrierDisconnectDetectionTestTemplate : public GridMateMPTestFixture , protected SocketProvider { @@ -846,7 +846,7 @@ namespace UnitTest * Sends reliable messages across different channels to each other */ template - class Integ_CarrierMultiChannelTestTemplate + class CarrierMultiChannelTestTemplate : public GridMateMPTestFixture , protected SocketProvider { @@ -950,7 +950,7 @@ namespace UnitTest * Stress tests multiple simultaneous Carriers */ template - class Integ_CarrierMultiStressTestTemplate + class CarrierMultiStressTestTemplate : public GridMateMPTestFixture , protected SocketProvider { @@ -977,7 +977,7 @@ namespace UnitTest public: void run() { - AZ_TracePrintf("GridMate", "Integ_CarrierMultiStressTest\n\n"); + AZ_TracePrintf("GridMate", "CarrierMultiStressTest\n\n"); // initialize transport const int k_numChannels = 1; @@ -1108,7 +1108,7 @@ namespace UnitTest /*** Congestion control back pressure test */ template - class Integ_CarrierBackpressureTestTemplate + class CarrierBackpressureTestTemplate : public GridMateMPTestFixture , protected SocketProvider , public CarrierEventBus::Handler @@ -1380,7 +1380,7 @@ namespace UnitTest }; template - class Integ_CarrierACKTestTemplate + class CarrierACKTestTemplate : public GridMateMPTestFixture , protected SocketProvider { @@ -1544,13 +1544,13 @@ namespace UnitTest //Create specific tests using CarrierBasicTest = CarrierBasicTestTemplate<>; using CarrierTest = CarrierTestTemplate<>; - using Integ_CarrierDisconnectDetectionTest = Integ_CarrierDisconnectDetectionTestTemplate<>; - using Integ_CarrierAsyncHandshakeTest = Integ_CarrierAsyncHandshakeTestTemplate<>; - using Integ_CarrierStressTest = CarrierStressTestTemplate<>; - using Integ_CarrierMultiChannelTest = Integ_CarrierMultiChannelTestTemplate<>; - using Integ_CarrierMultiStressTest = Integ_CarrierMultiStressTestTemplate<>; - using Integ_CarrierBackpressureTest = Integ_CarrierBackpressureTestTemplate<>; - using Integ_CarrierACKTest = Integ_CarrierACKTestTemplate<>; + using DISABLED_CarrierDisconnectDetectionTest = CarrierDisconnectDetectionTestTemplate<>; + using DISABLED_CarrierAsyncHandshakeTest = CarrierAsyncHandshakeTestTemplate<>; + using DISABLED_CarrierStressTest = CarrierStressTestTemplate<>; + using DISABLED_CarrierMultiChannelTest = CarrierMultiChannelTestTemplate<>; + using DISABLED_CarrierMultiStressTest = CarrierMultiStressTestTemplate<>; + using DISABLED_CarrierBackpressureTest = CarrierBackpressureTestTemplate<>; + using DISABLED_CarrierACKTest = CarrierACKTestTemplate<>; #if AZ_TRAIT_GRIDMATE_TEST_WITH_SECURE_SOCKET_DRIVER @@ -1658,20 +1658,20 @@ namespace UnitTest using SecureProviderBadHost = SecureDriverProvider>; using SecureProviderBadBoth = SecureDriverProvider, SecureSocketHandshakeDrop>; - using Integ_CarrierSecureSocketHandshakeTestClient = CarrierBasicTestTemplate; - using Integ_CarrierSecureSocketHandshakeTestHost = CarrierBasicTestTemplate; - using Integ_CarrierSecureSocketHandshakeTestBoth = CarrierBasicTestTemplate; + using DISABLED_CarrierSecureSocketHandshakeTestClient = CarrierBasicTestTemplate; + using DISABLED_CarrierSecureSocketHandshakeTestHost = CarrierBasicTestTemplate; + using DISABLED_CarrierSecureSocketHandshakeTestBoth = CarrierBasicTestTemplate; //Create secure socket variants of tests using CarrierBasicTestSecure = CarrierBasicTestTemplate>; using CarrierTestSecure = CarrierTestTemplate>; - using Integ_CarrierDisconnectDetectionTestSecure = Integ_CarrierDisconnectDetectionTestTemplate>; - using Integ_CarrierAsyncHandshakeTestSecure = Integ_CarrierAsyncHandshakeTestTemplate>; - using Integ_CarrierStressTestSecure = CarrierStressTestTemplate>; - using Integ_CarrierMultiChannelTestSecure = Integ_CarrierMultiChannelTestTemplate>; - using Integ_CarrierMultiStressTestSecure = Integ_CarrierMultiStressTestTemplate>; - using Integ_CarrierBackpressureTestSecure = Integ_CarrierBackpressureTestTemplate>; - using Integ_CarrierACKTestSecure = Integ_CarrierACKTestTemplate>; + using DISABLED_CarrierDisconnectDetectionTestSecure = CarrierDisconnectDetectionTestTemplate>; + using DISABLED_CarrierAsyncHandshakeTestSecure = CarrierAsyncHandshakeTestTemplate>; + using DISABLED_CarrierStressTestSecure = CarrierStressTestTemplate>; + using DISABLED_CarrierMultiChannelTestSecure = CarrierMultiChannelTestTemplate>; + using DISABLED_CarrierMultiStressTestSecure = CarrierMultiStressTestTemplate>; + using DISABLED_CarrierBackpressureTestSecure = CarrierBackpressureTestTemplate>; + using DISABLED_CarrierACKTestSecure = CarrierACKTestTemplate>; #endif } @@ -1720,30 +1720,30 @@ GM_TEST_SUITE(CarrierSuite) GM_TEST(CarrierBasicTest) GM_TEST(CarrierTest) #endif //AZ_TRAIT_GRIDMATE_UNIT_TEST_DISABLE_CARRIER_SESSION_TESTS -GM_TEST(Integ_CarrierAsyncHandshakeTest) +GM_TEST(DISABLED_CarrierAsyncHandshakeTest) #if !defined(AZ_DEBUG_BUILD) // this test is a little slow for debug -GM_TEST(Integ_CarrierStressTest) -GM_TEST(Integ_CarrierMultiStressTest) +GM_TEST(DISABLED_CarrierStressTest) +GM_TEST(DISABLED_CarrierMultiStressTest) #endif -GM_TEST(Integ_CarrierMultiChannelTest) -GM_TEST(Integ_CarrierBackpressureTest) -GM_TEST(Integ_CarrierACKTest) +GM_TEST(DISABLED_CarrierMultiChannelTest) +GM_TEST(DISABLED_CarrierBackpressureTest) +GM_TEST(DISABLED_CarrierACKTest) #if AZ_TRAIT_GRIDMATE_TEST_WITH_SECURE_SOCKET_DRIVER -GM_TEST(CarrierBasicTestSecure) -GM_TEST(Integ_CarrierSecureSocketHandshakeTestClient) -GM_TEST(Integ_CarrierSecureSocketHandshakeTestHost) -GM_TEST(Integ_CarrierSecureSocketHandshakeTestBoth) +GM_TEST(DISABLED_CarrierBasicTestSecure) +GM_TEST(DISABLED_CarrierSecureSocketHandshakeTestClient) +GM_TEST(DISABLED_CarrierSecureSocketHandshakeTestHost) +GM_TEST(DISABLED_CarrierSecureSocketHandshakeTestBoth) GM_TEST(CarrierTestSecure) -GM_TEST(Integ_CarrierAsyncHandshakeTestSecure) +GM_TEST(DISABLED_CarrierAsyncHandshakeTestSecure) #if !defined(AZ_DEBUG_BUILD) // this test is a little slow for debug -GM_TEST(Integ_CarrierStressTestSecure) -GM_TEST(Integ_CarrierMultiStressTestSecure) +GM_TEST(DISABLED_CarrierStressTestSecure) +GM_TEST(DISABLED_CarrierMultiStressTestSecure) #endif -GM_TEST(Integ_CarrierMultiChannelTestSecure) -GM_TEST(Integ_CarrierBackpressureTestSecure) -GM_TEST(Integ_CarrierACKTestSecure) +GM_TEST(DISABLED_CarrierMultiChannelTestSecure) +GM_TEST(DISABLED_CarrierBackpressureTestSecure) +GM_TEST(DISABLED_CarrierACKTestSecure) #endif diff --git a/Code/Framework/GridMate/Tests/CarrierStreamSocketDriverTests.cpp b/Code/Framework/GridMate/Tests/CarrierStreamSocketDriverTests.cpp index 8ec3ad540f..dd0d6f1b2b 100644 --- a/Code/Framework/GridMate/Tests/CarrierStreamSocketDriverTests.cpp +++ b/Code/Framework/GridMate/Tests/CarrierStreamSocketDriverTests.cpp @@ -172,7 +172,7 @@ public: namespace UnitTest { - class Integ_CarrierStreamBasicTest + class DISABLED_CarrierStreamBasicTest : public GridMateMPTestFixture , protected SocketDriverSupplier { @@ -330,7 +330,7 @@ namespace UnitTest } }; - class Integ_CarrierStreamAsyncHandshakeTest + class DISABLED_CarrierStreamAsyncHandshakeTest : public GridMateMPTestFixture , protected SocketDriverSupplier { @@ -462,7 +462,7 @@ namespace UnitTest } }; - class Integ_CarrierStreamStressTest + class CarrierStreamStressTest : public GridMateMPTestFixture , protected SocketDriverSupplier , public ::testing::Test @@ -470,7 +470,7 @@ namespace UnitTest public: }; - TEST_F(Integ_CarrierStreamStressTest, Stress_Test) + TEST_F(CarrierStreamStressTest, DISABLED_Stress_Test) { CarrierStreamCallbacksHandler clientCB, serverCB; UnitTest::TestCarrierDesc serverCarrierDesc, clientCarrierDesc; @@ -581,7 +581,7 @@ namespace UnitTest ////////////////////////////////////////////////////////////////////////// } - class Integ_CarrierStreamTest + class DISABLED_CarrierStreamTest : public GridMateMPTestFixture , protected SocketDriverSupplier { @@ -783,7 +783,7 @@ namespace UnitTest } }; - class Integ_CarrierStreamDisconnectDetectionTest + class DISABLED_CarrierStreamDisconnectDetectionTest : public GridMateMPTestFixture , protected SocketDriverSupplier { @@ -873,7 +873,7 @@ namespace UnitTest } }; - class Integ_CarrierStreamMultiChannelTest + class DISABLED_CarrierStreamMultiChannelTest : public GridMateMPTestFixture , protected SocketDriverSupplier { @@ -999,8 +999,8 @@ namespace UnitTest } GM_TEST_SUITE(CarrierStreamSuite) - GM_TEST(Integ_CarrierStreamBasicTest) - GM_TEST(Integ_CarrierStreamTest) - GM_TEST(Integ_CarrierStreamAsyncHandshakeTest) - GM_TEST(Integ_CarrierStreamMultiChannelTest) + GM_TEST(DISABLED_CarrierStreamBasicTest) + GM_TEST(DISABLED_CarrierStreamTest) + GM_TEST(DISABLED_CarrierStreamAsyncHandshakeTest) + GM_TEST(DISABLED_CarrierStreamMultiChannelTest) GM_TEST_SUITE_END() diff --git a/Code/Framework/GridMate/Tests/Replica.cpp b/Code/Framework/GridMate/Tests/Replica.cpp index 83a3a1f5a1..5fef135347 100644 --- a/Code/Framework/GridMate/Tests/Replica.cpp +++ b/Code/Framework/GridMate/Tests/Replica.cpp @@ -6,7 +6,6 @@ * */ #include "Tests.h" -#include "TestProfiler.h" #include @@ -1888,12 +1887,12 @@ protected: }; //----------------------------------------------------------------------------- -class Integ_ReplicaGMTest +class ReplicaGMTest : public UnitTest::GridMateMPTestFixture , public ::testing::Test {}; -TEST_F(Integ_ReplicaGMTest, ReplicaTest) +TEST_F(ReplicaGMTest, DISABLED_ReplicaTest) { ReplicaChunkDescriptorTable::Get().RegisterChunkType(); ReplicaChunkDescriptorTable::Get().RegisterChunkType(); @@ -2157,7 +2156,7 @@ TEST_F(Integ_ReplicaGMTest, ReplicaTest) } } -class Integ_ForcedReplicaMigrationTest +class ForcedReplicaMigrationTest : public UnitTest::GridMateMPTestFixture , public ReplicaMgrCallbackBus::Handler , public MigratableReplica::MigratableReplicaDebugMsgs::EBus::Handler @@ -2186,8 +2185,8 @@ class Integ_ForcedReplicaMigrationTest } public: - Integ_ForcedReplicaMigrationTest() { ReplicaMgrCallbackBus::Handler::BusConnect(m_gridMate); } - ~Integ_ForcedReplicaMigrationTest() { ReplicaMgrCallbackBus::Handler::BusDisconnect(); } + ForcedReplicaMigrationTest() { ReplicaMgrCallbackBus::Handler::BusConnect(m_gridMate); } + ~ForcedReplicaMigrationTest() { ReplicaMgrCallbackBus::Handler::BusDisconnect(); } enum @@ -2205,11 +2204,11 @@ public: AZStd::unordered_map m_replicaOwnership; }; -const int Integ_ForcedReplicaMigrationTest::k_frameTimePerNodeMs; -const int Integ_ForcedReplicaMigrationTest::k_numFramesToRun; -const int Integ_ForcedReplicaMigrationTest::k_hostSendRateMs; +const int ForcedReplicaMigrationTest::k_frameTimePerNodeMs; +const int ForcedReplicaMigrationTest::k_numFramesToRun; +const int ForcedReplicaMigrationTest::k_hostSendRateMs; -TEST_F(Integ_ForcedReplicaMigrationTest, ForcedReplicaMigrationTest) +TEST_F(ForcedReplicaMigrationTest, DISABLED_ForcedReplicaMigrationTest) { ReplicaChunkDescriptorTable::Get().RegisterChunkType(); ReplicaChunkDescriptorTable::Get().RegisterChunkType(); @@ -2360,7 +2359,7 @@ TEST_F(Integ_ForcedReplicaMigrationTest, ForcedReplicaMigrationTest) MigratableReplica::MigratableReplicaDebugMsgs::EBus::Handler::BusDisconnect(); } -class Integ_ReplicaMigrationRequestTest +class ReplicaMigrationRequestTest : public UnitTest::GridMateMPTestFixture , public ::testing::Test { @@ -2516,7 +2515,7 @@ public: static const int k_hostSendTimeMs = k_frameTimePerNodeMs * TotalNodes * 4; // limiting host send rate to be x4 times slower than tick }; -TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest) +TEST_F(ReplicaMigrationRequestTest, DISABLED_ReplicaMigrationRequestTest) { /* Topology: @@ -2837,11 +2836,11 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest) } } -const int Integ_ReplicaMigrationRequestTest::k_frameTimePerNodeMs; -const int Integ_ReplicaMigrationRequestTest::k_hostSendTimeMs; +const int ReplicaMigrationRequestTest::k_frameTimePerNodeMs; +const int ReplicaMigrationRequestTest::k_hostSendTimeMs; -class Integ_PeerRejoinTest +class PeerRejoinTest : public UnitTest::GridMateMPTestFixture , public ReplicaMgrCallbackBus::Handler , public ::testing::Test @@ -2860,11 +2859,11 @@ class Integ_PeerRejoinTest } public: - Integ_PeerRejoinTest() { ReplicaMgrCallbackBus::Handler::BusConnect(m_gridMate); } - ~Integ_PeerRejoinTest() { ReplicaMgrCallbackBus::Handler::BusDisconnect(); } + PeerRejoinTest() { ReplicaMgrCallbackBus::Handler::BusConnect(m_gridMate); } + ~PeerRejoinTest() { ReplicaMgrCallbackBus::Handler::BusDisconnect(); } }; -TEST_F(Integ_PeerRejoinTest, PeerRejoinTest) +TEST_F(PeerRejoinTest, DISABLED_PeerRejoinTest) { ReplicaChunkDescriptorTable::Get().RegisterChunkType(); ReplicaChunkDescriptorTable::Get().RegisterChunkType(); @@ -3011,7 +3010,7 @@ TEST_F(Integ_PeerRejoinTest, PeerRejoinTest) } } -class Integ_ReplicationSecurityOptionsTest +class ReplicationSecurityOptionsTest : public UnitTest::GridMateMPTestFixture , public ::testing::Test { @@ -3156,7 +3155,7 @@ public: using TestChunkPtr = AZStd::intrusive_ptr ; }; -TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest) +TEST_F(ReplicationSecurityOptionsTest, DISABLED_ReplicationSecurityOptionsTest) { AZ_TracePrintf("GridMate", "\n"); @@ -3356,7 +3355,7 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest) Replica update time (msec): avg=4.94, min=1, max=9 (peers=40, replicas=16000, freq=10%, samples=4000) Replica update time (msec): avg=8.05, min=6, max=15 (peers=40, replicas=16000, freq=100%, samples=4000) */ -class Integ_ReplicaStressTest +class DISABLED_ReplicaStressTest : public UnitTest::GridMateMPTestFixture { public: @@ -3388,7 +3387,7 @@ public: static const int BASE_PORT = 44270; // TODO: Reduce the size or disable the test for platforms which can't allocate 2 GiB - Integ_ReplicaStressTest() + DISABLED_ReplicaStressTest() : UnitTest::GridMateMPTestFixture(2000u * 1024u * 1024u) {} @@ -3516,33 +3515,33 @@ public: virtual void RunStressTests(MPSession* sessions, vector >& replicas) { // testing 3 cases & waiting for system to settle in between - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); Wait(sessions, replicas, 50, FRAME_TIME); - TestProfiler::PrintProfilingTotal("GridMate"); + //TestProfiler::PrintProfilingTotal("GridMate"); Wait(sessions, replicas, 20, FRAME_TIME); - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.0); // no replicas are dirty - TestProfiler::PrintProfilingTotal("GridMate"); + //TestProfiler::PrintProfilingTotal("GridMate"); Wait(sessions, replicas, 20, FRAME_TIME); - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); TestReplicas(sessions, replicas, 1, FRAME_TIME, 1.0); // single burst dirty replicas Wait(sessions, replicas, 2, FRAME_TIME); - TestProfiler::PrintProfilingTotal("GridMate"); + //TestProfiler::PrintProfilingTotal("GridMate"); Wait(sessions, replicas, 20, FRAME_TIME); - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.1); // 10% of replicas are marked dirty every frame - TestProfiler::PrintProfilingTotal("GridMate"); + //TestProfiler::PrintProfilingTotal("GridMate"); Wait(sessions, replicas, 20, FRAME_TIME); - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); TestReplicas(sessions, replicas, 100, FRAME_TIME, 1.0); // every replica is marked dirty every frame - TestProfiler::PrintProfilingTotal("GridMate"); - TestProfiler::PrintProfilingSelf("GridMate"); + //TestProfiler::PrintProfilingTotal("GridMate"); + //TestProfiler::PrintProfilingSelf("GridMate"); - TestProfiler::StopProfiling(); + //TestProfiler::StopProfiling(); } virtual void MarkChanging(vector >& replicas, double freq) @@ -3623,8 +3622,8 @@ public: Replica update time (msec): avg=2.01, min=1, max=5 (peers=40, replicas=16000, freq=10%, samples=4000) Replica update time (msec): avg=4.61, min=3, max=10 (peers=40, replicas=16000, freq=50%, samples=4000) */ -class Integ_ReplicaStableStressTest - : public Integ_ReplicaStressTest +class DISABLED_ReplicaStableStressTest + : public DISABLED_ReplicaStressTest { public: @@ -3636,21 +3635,21 @@ public: void RunStressTests(MPSession* sessions, vector >& replicas) override { - Integ_ReplicaStressTest::MarkChanging(replicas, 0.1); // picks 10% of replicas + DISABLED_ReplicaStressTest::MarkChanging(replicas, 0.1); // picks 10% of replicas Wait(sessions, replicas, 20, FRAME_TIME); - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.1); - TestProfiler::PrintProfilingTotal("GridMate"); - TestProfiler::PrintProfilingSelf("GridMate"); + /*TestProfiler::PrintProfilingTotal("GridMate"); + TestProfiler::PrintProfilingSelf("GridMate");*/ - Integ_ReplicaStressTest::MarkChanging(replicas, 0.5); // picks 50% of replicas + DISABLED_ReplicaStressTest::MarkChanging(replicas, 0.5); // picks 50% of replicas Wait(sessions, replicas, 20, FRAME_TIME); - TestProfiler::StartProfiling(); + //TestProfiler::StartProfiling(); TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.5); - TestProfiler::PrintProfilingTotal("GridMate"); + /*TestProfiler::PrintProfilingTotal("GridMate"); TestProfiler::PrintProfilingSelf("GridMate"); - TestProfiler::StopProfiling(); + TestProfiler::StopProfiling();*/ } }; @@ -3666,7 +3665,7 @@ public: * expected |none |brst | capped |under cap |brst | capped | * */ -class Integ_ReplicaBandiwdthTest +class DISABLED_ReplicaBandiwdthTest : public UnitTest::GridMateMPTestFixture { public: @@ -3944,9 +3943,9 @@ GM_TEST_SUITE(ReplicaSuite) GM_TEST(InterpolatorTest) #if !defined(AZ_DEBUG_BUILD) // these tests are a little slow for debug -GM_TEST(Integ_ReplicaBandiwdthTest) -GM_TEST(Integ_ReplicaStressTest) -GM_TEST(Integ_ReplicaStableStressTest) +GM_TEST(DISABLED_ReplicaBandiwdthTest) +GM_TEST(DISABLED_ReplicaStressTest) +GM_TEST(DISABLED_ReplicaStableStressTest) #endif GM_TEST_SUITE_END() diff --git a/Code/Framework/GridMate/Tests/ReplicaBehavior.cpp b/Code/Framework/GridMate/Tests/ReplicaBehavior.cpp index f6f88d2dff..86f9f4605b 100644 --- a/Code/Framework/GridMate/Tests/ReplicaBehavior.cpp +++ b/Code/Framework/GridMate/Tests/ReplicaBehavior.cpp @@ -457,13 +457,13 @@ namespace ReplicaBehavior { Completed, }; - class Integ_SimpleBehaviorTest + class SimpleBehaviorTest : public UnitTest::GridMateMPTestFixture { public: //GM_CLASS_ALLOCATOR(SimpleBehaviorTest); - Integ_SimpleBehaviorTest() + SimpleBehaviorTest() : m_sessionCount(0) { } virtual int GetNumSessions() { return 0; } @@ -654,11 +654,11 @@ namespace ReplicaBehavior { * * This is a simple sanity check to ensure the logic sends the update when it's necessary. */ - class Integ_Replica_DontSendDataSets_WithNoDiffFromCtorData - : public Integ_SimpleBehaviorTest + class Replica_DontSendDataSets_WithNoDiffFromCtorData + : public SimpleBehaviorTest { public: - Integ_Replica_DontSendDataSets_WithNoDiffFromCtorData() + Replica_DontSendDataSets_WithNoDiffFromCtorData() : m_replicaIdDefault(InvalidReplicaId), m_replicaIdModified(InvalidReplicaId) { } @@ -774,9 +774,9 @@ namespace ReplicaBehavior { FilteredHook m_driller; }; - TEST(Integ_Replica_DontSendDataSets_WithNoDiffFromCtorData, Integ_Replica_DontSendDataSets_WithNoDiffFromCtorData) + TEST(Replica_DontSendDataSets_WithNoDiffFromCtorData, DISABLED_Replica_DontSendDataSets_WithNoDiffFromCtorData) { - Integ_Replica_DontSendDataSets_WithNoDiffFromCtorData tester; + Replica_DontSendDataSets_WithNoDiffFromCtorData tester; tester.run(); } @@ -784,11 +784,11 @@ namespace ReplicaBehavior { * This test checks the actual size of the replica as marshalled in the binary payload. * The assessment of the payload size is done using driller EBus. */ - class Integ_ReplicaDefaultDataSetDriller - : public Integ_SimpleBehaviorTest + class ReplicaDefaultDataSetDriller + : public SimpleBehaviorTest { public: - Integ_ReplicaDefaultDataSetDriller() + ReplicaDefaultDataSetDriller() : m_replicaId(InvalidReplicaId) { } @@ -815,7 +815,7 @@ namespace ReplicaBehavior { m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica); } - ~Integ_ReplicaDefaultDataSetDriller() override + ~ReplicaDefaultDataSetDriller() override { m_driller.BusDisconnect(); } @@ -880,11 +880,11 @@ namespace ReplicaBehavior { ReplicaId m_replicaId; }; - const int Integ_ReplicaDefaultDataSetDriller::NonDefaultValue; + const int ReplicaDefaultDataSetDriller::NonDefaultValue; - TEST(Integ_ReplicaDefaultDataSetDriller, Integ_ReplicaDefaultDataSetDriller) + TEST(ReplicaDefaultDataSetDriller, DISABLED_ReplicaDefaultDataSetDriller) { - Integ_ReplicaDefaultDataSetDriller tester; + ReplicaDefaultDataSetDriller tester; tester.run(); } @@ -892,11 +892,11 @@ namespace ReplicaBehavior { * This test checks the actual size of the replica as marshalled in the binary payload. * The assessment of the payload size is done using driller EBus. */ - class Integ_Replica_ComparePackingBoolsVsU8 - : public Integ_SimpleBehaviorTest + class Replica_ComparePackingBoolsVsU8 + : public SimpleBehaviorTest { public: - Integ_Replica_ComparePackingBoolsVsU8() + Replica_ComparePackingBoolsVsU8() : m_replicaBoolsId(InvalidReplicaId) , m_replicaU8Id(InvalidReplicaId) { @@ -928,7 +928,7 @@ namespace ReplicaBehavior { m_replicaU8Id = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica2); } - ~Integ_Replica_ComparePackingBoolsVsU8() override + ~Replica_ComparePackingBoolsVsU8() override { m_driller.BusDisconnect(); } @@ -1020,17 +1020,17 @@ namespace ReplicaBehavior { ReplicaId m_replicaU8Id; }; - TEST(Integ_Replica_ComparePackingBoolsVsU8, Integ_Replica_ComparePackingBoolsVsU8) + TEST(Replica_ComparePackingBoolsVsU8, DISABLED_Replica_ComparePackingBoolsVsU8) { - Integ_Replica_ComparePackingBoolsVsU8 tester; + Replica_ComparePackingBoolsVsU8 tester; tester.run(); } - class Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary - : public Integ_SimpleBehaviorTest + class CheckDataSetStreamIsntWrittenMoreThanNecessary + : public SimpleBehaviorTest { public: - Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary() + CheckDataSetStreamIsntWrittenMoreThanNecessary() : m_replicaId(InvalidReplicaId) { } @@ -1057,7 +1057,7 @@ namespace ReplicaBehavior { m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica); } - ~Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary() override + ~CheckDataSetStreamIsntWrittenMoreThanNecessary() override { m_driller.BusDisconnect(); } @@ -1117,17 +1117,17 @@ namespace ReplicaBehavior { ReplicaId m_replicaId; }; - TEST(Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary, Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary) + TEST(CheckDataSetStreamIsntWrittenMoreThanNecessary, DISABLED_CheckDataSetStreamIsntWrittenMoreThanNecessary) { - Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary tester; + CheckDataSetStreamIsntWrittenMoreThanNecessary tester; tester.run(); } - class Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty - : public Integ_SimpleBehaviorTest + class CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty + : public SimpleBehaviorTest { public: - Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty() + CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty() : m_replicaId(InvalidReplicaId) { } @@ -1154,7 +1154,7 @@ namespace ReplicaBehavior { m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica); } - ~Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty() override + ~CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty() override { m_driller.BusDisconnect(); } @@ -1213,17 +1213,17 @@ namespace ReplicaBehavior { ReplicaId m_replicaId; }; - TEST(Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty, Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty) + TEST(CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty, DISABLED_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty) { - Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty tester; + CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty tester; tester.run(); } - class Integ_CheckReplicaIsntSentWithNoChanges - : public Integ_SimpleBehaviorTest + class CheckReplicaIsntSentWithNoChanges + : public SimpleBehaviorTest { public: - Integ_CheckReplicaIsntSentWithNoChanges() + CheckReplicaIsntSentWithNoChanges() : m_replicaId(InvalidReplicaId) { } @@ -1248,7 +1248,7 @@ namespace ReplicaBehavior { m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica); } - ~Integ_CheckReplicaIsntSentWithNoChanges() override + ~CheckReplicaIsntSentWithNoChanges() override { m_driller.BusDisconnect(); } @@ -1323,17 +1323,17 @@ namespace ReplicaBehavior { ReplicaId m_replicaId; }; - TEST(Integ_CheckReplicaIsntSentWithNoChanges, Integ_CheckReplicaIsntSentWithNoChanges) + TEST(CheckReplicaIsntSentWithNoChanges, DISABLED_CheckReplicaIsntSentWithNoChanges) { - Integ_CheckReplicaIsntSentWithNoChanges tester; + CheckReplicaIsntSentWithNoChanges tester; tester.run(); } - class Integ_CheckEntityScriptReplicaIsntSentWithNoChanges - : public Integ_SimpleBehaviorTest + class CheckEntityScriptReplicaIsntSentWithNoChanges + : public SimpleBehaviorTest { public: - Integ_CheckEntityScriptReplicaIsntSentWithNoChanges() + CheckEntityScriptReplicaIsntSentWithNoChanges() : m_replicaId(InvalidReplicaId) { } @@ -1359,7 +1359,7 @@ namespace ReplicaBehavior { m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica); } - ~Integ_CheckEntityScriptReplicaIsntSentWithNoChanges() override + ~CheckEntityScriptReplicaIsntSentWithNoChanges() override { m_driller.BusDisconnect(); } @@ -1410,9 +1410,9 @@ namespace ReplicaBehavior { ReplicaId m_replicaId; }; - TEST(Integ_CheckEntityScriptReplicaIsntSentWithNoChanges, Integ_CheckEntityScriptReplicaIsntSentWithNoChanges) + TEST(CheckEntityScriptReplicaIsntSentWithNoChanges, DISABLED_CheckEntityScriptReplicaIsntSentWithNoChanges) { - Integ_CheckEntityScriptReplicaIsntSentWithNoChanges tester; + CheckEntityScriptReplicaIsntSentWithNoChanges tester; tester.run(); } diff --git a/Code/Framework/GridMate/Tests/ReplicaMedium.cpp b/Code/Framework/GridMate/Tests/ReplicaMedium.cpp index 61fe9d65b2..2e8d2a3a73 100644 --- a/Code/Framework/GridMate/Tests/ReplicaMedium.cpp +++ b/Code/Framework/GridMate/Tests/ReplicaMedium.cpp @@ -596,12 +596,12 @@ public: //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -class MPSession +class MPSessionMedium : public CarrierEventBus::Handler { public: - ~MPSession() override + ~MPSessionMedium() override { CarrierEventBus::Handler::BusDisconnect(); } @@ -708,14 +708,14 @@ enum class TestStatus Completed, }; -class Integ_SimpleTest +class SimpleTest : public UnitTest::GridMateMPTestFixture , public ::testing::Test { public: - //GM_CLASS_ALLOCATOR(Integ_SimpleTest); + //GM_CLASS_ALLOCATOR(SimpleTest); - Integ_SimpleTest() + SimpleTest() : m_sessionCount(0) { } virtual int GetNumSessions() { return 0; } @@ -858,15 +858,15 @@ public: } int m_sessionCount; - AZStd::array m_sessions; + AZStd::array m_sessions; AZStd::unique_ptr m_defaultSimulator; }; -class Integ_ReplicaChunkRPCExec - : public Integ_SimpleTest +class ReplicaChunkRPCExec + : public SimpleTest { public: - Integ_ReplicaChunkRPCExec() + ReplicaChunkRPCExec() : m_chunk(nullptr) , m_replicaId(0) { } @@ -893,7 +893,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_ReplicaChunkRPCExec, ReplicaChunkRPCExec) +TEST_F(ReplicaChunkRPCExec, DISABLED_ReplicaChunkRPCExec) { RunTickLoop([this](int tick) -> TestStatus { @@ -1050,8 +1050,8 @@ int DestroyRPCChunk::s_afterDestroyFromPrimaryCalls = 0; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -class Integ_ReplicaDestroyedInRPC - : public Integ_SimpleTest +class ReplicaDestroyedInRPC + : public SimpleTest { public: enum @@ -1080,7 +1080,7 @@ public: ReplicaId m_repId[2]; }; -TEST_F(Integ_ReplicaDestroyedInRPC, ReplicaDestroyedInRPC) +TEST_F(ReplicaDestroyedInRPC, DISABLED_ReplicaDestroyedInRPC) { RunTickLoop([this](int tick)->TestStatus { @@ -1129,11 +1129,11 @@ TEST_F(Integ_ReplicaDestroyedInRPC, ReplicaDestroyedInRPC) }); } -class Integ_ReplicaChunkAddWhileReplicated - : public Integ_SimpleTest +class ReplicaChunkAddWhileReplicated + : public SimpleTest { public: - Integ_ReplicaChunkAddWhileReplicated() + ReplicaChunkAddWhileReplicated() : m_replica(nullptr) , m_chunk(nullptr) , m_replicaId(0) @@ -1161,7 +1161,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_ReplicaChunkAddWhileReplicated, ReplicaChunkAddWhileReplicated) +TEST_F(ReplicaChunkAddWhileReplicated, DISABLED_ReplicaChunkAddWhileReplicated) { RunTickLoop([this](int tick)-> TestStatus { @@ -1203,11 +1203,11 @@ TEST_F(Integ_ReplicaChunkAddWhileReplicated, ReplicaChunkAddWhileReplicated) } -class Integ_ReplicaRPCValues - : public Integ_SimpleTest +class ReplicaRPCValues + : public SimpleTest { public: - Integ_ReplicaRPCValues() + ReplicaRPCValues() : m_replica(nullptr) , m_chunk(nullptr) , m_replicaId(0) @@ -1236,7 +1236,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_ReplicaRPCValues, ReplicaRPCValues) +TEST_F(ReplicaRPCValues, DISABLED_ReplicaRPCValues) { RunTickLoop([this](int tick)-> TestStatus { @@ -1257,11 +1257,11 @@ TEST_F(Integ_ReplicaRPCValues, ReplicaRPCValues) }); } -class Integ_FullRPCValues - : public Integ_SimpleTest +class FullRPCValues + : public SimpleTest { public: - Integ_FullRPCValues() + FullRPCValues() : m_replica(nullptr) , m_chunk(nullptr) , m_replicaId(0) @@ -1290,7 +1290,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_FullRPCValues, FullRPCValues) +TEST_F(FullRPCValues, DISABLED_FullRPCValues) { RunTickLoop([this](int tick)-> TestStatus { @@ -1364,11 +1364,11 @@ TEST_F(Integ_FullRPCValues, FullRPCValues) } -class Integ_ReplicaRemoveProxy - : public Integ_SimpleTest +class ReplicaRemoveProxy + : public SimpleTest { public: - Integ_ReplicaRemoveProxy() + ReplicaRemoveProxy() : m_replica(nullptr) , m_replicaId(0) { @@ -1395,7 +1395,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_ReplicaRemoveProxy, ReplicaRemoveProxy) +TEST_F(ReplicaRemoveProxy, DISABLED_ReplicaRemoveProxy) { RunTickLoop([this](int tick)-> TestStatus { @@ -1424,11 +1424,11 @@ TEST_F(Integ_ReplicaRemoveProxy, ReplicaRemoveProxy) } -class Integ_ReplicaChunkEvents - : public Integ_SimpleTest +class ReplicaChunkEvents + : public SimpleTest { public: - Integ_ReplicaChunkEvents() + ReplicaChunkEvents() : m_replicaId(InvalidReplicaId) , m_chunk(nullptr) , m_proxyChunk(nullptr) @@ -1463,7 +1463,7 @@ public: AllEventChunk::Ptr m_proxyChunk; }; -TEST_F(Integ_ReplicaChunkEvents, ReplicaChunkEvents) +TEST_F(ReplicaChunkEvents, DISABLED_ReplicaChunkEvents) { RunTickLoop([this](int tick)-> TestStatus { @@ -1501,11 +1501,11 @@ TEST_F(Integ_ReplicaChunkEvents, ReplicaChunkEvents) } -class Integ_ReplicaChunksBeyond32 - : public Integ_SimpleTest +class ReplicaChunksBeyond32 + : public SimpleTest { public: - Integ_ReplicaChunksBeyond32() + ReplicaChunksBeyond32() : m_replicaId(InvalidReplicaId) { } @@ -1537,7 +1537,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_ReplicaChunksBeyond32, ReplicaChunksBeyond32) +TEST_F(ReplicaChunksBeyond32, DISABLED_ReplicaChunksBeyond32) { RunTickLoop([this](int tick)-> TestStatus { @@ -1565,11 +1565,11 @@ TEST_F(Integ_ReplicaChunksBeyond32, ReplicaChunksBeyond32) } -class Integ_ReplicaChunkEventsDeactivate - : public Integ_SimpleTest +class ReplicaChunkEventsDeactivate + : public SimpleTest { public: - Integ_ReplicaChunkEventsDeactivate() + ReplicaChunkEventsDeactivate() : m_replica(nullptr) , m_replicaId(0) , m_chunk(nullptr) @@ -1604,7 +1604,7 @@ public: AllEventChunk::Ptr m_proxyChunk; }; -TEST_F(Integ_ReplicaChunkEventsDeactivate, ReplicaChunkEventsDeactivate) +TEST_F(ReplicaChunkEventsDeactivate, DISABLED_ReplicaChunkEventsDeactivate) { RunTickLoop([this](int tick)-> TestStatus { @@ -1649,11 +1649,11 @@ TEST_F(Integ_ReplicaChunkEventsDeactivate, ReplicaChunkEventsDeactivate) } -class Integ_ReplicaDriller - : public Integ_SimpleTest +class ReplicaDriller + : public SimpleTest { public: - Integ_ReplicaDriller() + ReplicaDriller() : m_replicaId(InvalidReplicaId) { } @@ -2007,7 +2007,7 @@ public: m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica); } - ~Integ_ReplicaDriller() override + ~ReplicaDriller() override { m_driller.BusDisconnect(); } @@ -2016,7 +2016,7 @@ public: ReplicaId m_replicaId; }; -TEST_F(Integ_ReplicaDriller, ReplicaDriller) +TEST_F(ReplicaDriller, DISABLED_ReplicaDriller) { RunTickLoop([this](int tick)-> TestStatus { @@ -2082,11 +2082,11 @@ TEST_F(Integ_ReplicaDriller, ReplicaDriller) } -class Integ_DataSetChangedTest - : public Integ_SimpleTest +class DataSetChangedTest + : public SimpleTest { public: - Integ_DataSetChangedTest() + DataSetChangedTest() : m_replica(nullptr) , m_replicaId(0) , m_chunk(nullptr) @@ -2115,7 +2115,7 @@ public: DataSetChunk::Ptr m_chunk; }; -TEST_F(Integ_DataSetChangedTest, DataSetChangedTest) +TEST_F(DataSetChangedTest, DISABLED_DataSetChangedTest) { RunTickLoop([this](int tick)-> TestStatus { @@ -2144,11 +2144,11 @@ TEST_F(Integ_DataSetChangedTest, DataSetChangedTest) } -class Integ_CustomHandlerTest - : public Integ_SimpleTest +class CustomHandlerTest + : public SimpleTest { public: - Integ_CustomHandlerTest() + CustomHandlerTest() : m_replica(nullptr) , m_replicaId(0) , m_chunk(nullptr) @@ -2181,7 +2181,7 @@ public: AZStd::scoped_ptr m_proxyHandler; }; -TEST_F(Integ_CustomHandlerTest, CustomHandlerTest) +TEST_F(CustomHandlerTest, DISABLED_CustomHandlerTest) { RunTickLoop([this](int tick)-> TestStatus { @@ -2234,11 +2234,11 @@ TEST_F(Integ_CustomHandlerTest, CustomHandlerTest) } -class Integ_NonConstMarshalerTest - : public Integ_SimpleTest +class NonConstMarshalerTest + : public SimpleTest { public: - Integ_NonConstMarshalerTest() + NonConstMarshalerTest() : m_replica(nullptr) , m_replicaId(0) , m_chunk(nullptr) @@ -2266,7 +2266,7 @@ public: NonConstMarshalerChunk::Ptr m_chunk; }; -TEST_F(Integ_NonConstMarshalerTest, NonConstMarshalerTest) +TEST_F(NonConstMarshalerTest, DISABLED_NonConstMarshalerTest) { RunTickLoop([this](int tick)-> TestStatus { @@ -2309,11 +2309,11 @@ TEST_F(Integ_NonConstMarshalerTest, NonConstMarshalerTest) } -class Integ_SourcePeerTest - : public Integ_SimpleTest +class SourcePeerTest + : public SimpleTest { public: - Integ_SourcePeerTest() + SourcePeerTest() : m_replica(nullptr) , m_replicaId(0) , m_chunk(nullptr) @@ -2343,7 +2343,7 @@ public: SourcePeerChunk::Ptr m_chunk2; }; -TEST_F(Integ_SourcePeerTest, SourcePeerTest) +TEST_F(SourcePeerTest, DISABLED_SourcePeerTest) { RunTickLoop([this](int tick)-> TestStatus { @@ -2404,8 +2404,8 @@ TEST_F(Integ_SourcePeerTest, SourcePeerTest) } -class Integ_SendWithPriority - : public Integ_SimpleTest +class SendWithPriority + : public SimpleTest { public: enum @@ -2438,8 +2438,8 @@ public: { public: ReplicaDrillerHook() - : m_expectedSendValue(Integ_SendWithPriority::kNumReplicas) - , m_expectedRecvValue(Integ_SendWithPriority::kNumReplicas) + : m_expectedSendValue(SendWithPriority::kNumReplicas) + , m_expectedRecvValue(SendWithPriority::kNumReplicas) { } @@ -2495,7 +2495,7 @@ public: PriorityChunk::Ptr m_chunks[kNumReplicas]; }; -TEST_F(Integ_SendWithPriority, SendWithPriority) +TEST_F(SendWithPriority, DISABLED_SendWithPriority) { RunTickLoop([this](int tick)-> TestStatus { @@ -2511,8 +2511,8 @@ TEST_F(Integ_SendWithPriority, SendWithPriority) } -class Integ_SuspendUpdatesTest - : public Integ_SimpleTest +class SuspendUpdatesTest + : public SimpleTest { public: enum @@ -2597,7 +2597,7 @@ public: unsigned int m_numRpcCalled = 0; }; -TEST_F(Integ_SuspendUpdatesTest, SuspendUpdatesTest) +TEST_F(SuspendUpdatesTest, DISABLED_SuspendUpdatesTest) { RunTickLoop([this](int tick)-> TestStatus { @@ -2657,7 +2657,7 @@ TEST_F(Integ_SuspendUpdatesTest, SuspendUpdatesTest) } -class Integ_BasicHostChunkDescriptorTest +class BasicHostChunkDescriptorTest : public UnitTest::GridMateMPTestFixture , public ::testing::Test { @@ -2694,17 +2694,17 @@ public: static int nProxyActivations; }; }; -int Integ_BasicHostChunkDescriptorTest::HostChunk::nPrimaryActivations = 0; -int Integ_BasicHostChunkDescriptorTest::HostChunk::nProxyActivations = 0; +int BasicHostChunkDescriptorTest::HostChunk::nPrimaryActivations = 0; +int BasicHostChunkDescriptorTest::HostChunk::nProxyActivations = 0; -TEST_F(Integ_BasicHostChunkDescriptorTest, BasicHostChunkDescriptorTest) +TEST_F(BasicHostChunkDescriptorTest, DISABLED_BasicHostChunkDescriptorTest) { AZ_TracePrintf("GridMate", "\n"); // Register test chunks ReplicaChunkDescriptorTable::Get().RegisterChunkType>(); - MPSession nodes[nNodes]; + MPSessionMedium nodes[nNodes]; // initialize transport int basePort = 4427; @@ -2791,8 +2791,8 @@ TEST_F(Integ_BasicHostChunkDescriptorTest, BasicHostChunkDescriptorTest) * Create and immedietly destroy primary replica * Test that it does not result in any network sync */ -class Integ_CreateDestroyPrimary - : public Integ_SimpleTest +class CreateDestroyPrimary + : public SimpleTest , public Debug::ReplicaDrillerBus::Handler { public: @@ -2827,7 +2827,7 @@ public: } }; -TEST_F(Integ_CreateDestroyPrimary, CreateDestroyPrimary) +TEST_F(CreateDestroyPrimary, DISABLED_CreateDestroyPrimary) { RunTickLoop([this](int tick)-> TestStatus { @@ -2861,7 +2861,7 @@ TEST_F(Integ_CreateDestroyPrimary, CreateDestroyPrimary) * The ReplicaTarget will prevent sending more updates. */ class ReplicaACKfeedbackTestFixture - : public Integ_SimpleTest + : public SimpleTest { public: ReplicaACKfeedbackTestFixture() @@ -2900,7 +2900,7 @@ public: size_t m_replicaBytesSentPrev = 0; ReplicaId m_replicaId; - Integ_ReplicaDriller::ReplicaDrillerHook m_driller; + ReplicaDriller::ReplicaDrillerHook m_driller; }; TEST_F(ReplicaACKfeedbackTestFixture, ReplicaACKfeedbackTest) diff --git a/Code/Framework/GridMate/Tests/Session.cpp b/Code/Framework/GridMate/Tests/Session.cpp index d4f56871c5..c793c3d450 100644 --- a/Code/Framework/GridMate/Tests/Session.cpp +++ b/Code/Framework/GridMate/Tests/Session.cpp @@ -40,7 +40,7 @@ namespace UnitTest } } - class Integ_LANSessionMatchmakingParamsTest + class DISABLED_LANSessionMatchmakingParamsTest : public GridMateMPTestFixture , public SessionEventBus::MultiHandler { @@ -52,7 +52,7 @@ namespace UnitTest } public: - Integ_LANSessionMatchmakingParamsTest(bool useIPv6 = false) + DISABLED_LANSessionMatchmakingParamsTest(bool useIPv6 = false) : m_hostSession(nullptr) , m_clientGridMate(nullptr) { @@ -71,7 +71,7 @@ namespace UnitTest AZ_TEST_ASSERT(GridMate::LANSessionServiceBus::FindFirstHandler(m_clientGridMate) != nullptr); ////////////////////////////////////////////////////////////////////////// } - ~Integ_LANSessionMatchmakingParamsTest() override + ~DISABLED_LANSessionMatchmakingParamsTest() override { SessionEventBus::MultiHandler::BusDisconnect(m_gridMate); SessionEventBus::MultiHandler::BusDisconnect(m_clientGridMate); @@ -192,7 +192,7 @@ namespace UnitTest IGridMate* m_clientGridMate; }; - class Integ_LANSessionTest + class DISABLED_LANSessionTest : public GridMateMPTestFixture { class TestPeerInfo @@ -264,7 +264,7 @@ namespace UnitTest }; public: - Integ_LANSessionTest(bool useIPv6 = false) + DISABLED_LANSessionTest(bool useIPv6 = false) { m_driverType = useIPv6 ? Driver::BSD_AF_INET6 : Driver::BSD_AF_INET; m_doSessionParamsTest = k_numMachines > 1; @@ -290,7 +290,7 @@ namespace UnitTest AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr); } } - ~Integ_LANSessionTest() override + ~DISABLED_LANSessionTest() override { StopGridMateService(m_peers[0].m_gridMate); @@ -555,15 +555,15 @@ namespace UnitTest bool m_doSessionParamsTest; }; - class Integ_LANSessionTestIPv6 - : public Integ_LANSessionTest + class DISABLED_LANSessionTestIPv6 + : public DISABLED_LANSessionTest { public: - Integ_LANSessionTestIPv6() - : Integ_LANSessionTest(true) {} + DISABLED_LANSessionTestIPv6() + : DISABLED_LANSessionTest(true) {} }; - class Integ_LANMultipleSessionTest + class DISABLED_LANMultipleSessionTest : public GridMateMPTestFixture , public SessionEventBus::Handler { @@ -620,7 +620,7 @@ namespace UnitTest m_sessions[i] = nullptr; } - Integ_LANMultipleSessionTest() + DISABLED_LANMultipleSessionTest() : GridMateMPTestFixture(200 * 1024 * 1024) { ////////////////////////////////////////////////////////////////////////// @@ -645,7 +645,7 @@ namespace UnitTest } } - ~Integ_LANMultipleSessionTest() override + ~DISABLED_LANMultipleSessionTest() override { GridMate::StopGridMateService(m_gridMates[0]); @@ -799,7 +799,7 @@ namespace UnitTest * Testing session with low latency. This is special mode usually used by tools and communication channels * where we try to response instantly on messages. */ - class Integ_LANLatencySessionTest + class DISABLED_LANLatencySessionTest : public GridMateMPTestFixture , public SessionEventBus::Handler { @@ -857,7 +857,7 @@ namespace UnitTest m_sessions[i] = nullptr; } - Integ_LANLatencySessionTest() + DISABLED_LANLatencySessionTest() #ifdef AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER : GridMateMPTestFixture(50 * 1024 * 1024) #endif @@ -884,7 +884,7 @@ namespace UnitTest } } - ~Integ_LANLatencySessionTest() override + ~DISABLED_LANLatencySessionTest() override { StopGridMateService(m_gridMates[0]); @@ -1162,7 +1162,7 @@ namespace UnitTest * 5. After host migration we drop the new host again. (after migration we have 3 members). * Session should be fully operational at the end with 3 members left. */ - class Integ_LANSessionMigarationTestTest + class LANSessionMigarationTestTest : public SessionEventBus::Handler , public GridMateMPTestFixture { @@ -1257,7 +1257,7 @@ namespace UnitTest } } - Integ_LANSessionMigarationTestTest() + LANSessionMigarationTestTest() { ////////////////////////////////////////////////////////////////////////// // Create all grid mates @@ -1283,7 +1283,7 @@ namespace UnitTest //StartDrilling("lanmigration"); } - ~Integ_LANSessionMigarationTestTest() override + ~LANSessionMigarationTestTest() override { StopGridMateService(m_gridMates[0]); @@ -1476,7 +1476,7 @@ namespace UnitTest * 5. We join a 2 new members to the session. * Session should be fully operational at the end with 4 members in it. */ - class Integ_LANSessionMigarationTestTest2 + class LANSessionMigarationTestTest2 : public SessionEventBus::Handler , public GridMateMPTestFixture { @@ -1571,7 +1571,7 @@ namespace UnitTest } } } - Integ_LANSessionMigarationTestTest2() + LANSessionMigarationTestTest2() { ////////////////////////////////////////////////////////////////////////// // Create all grid mates @@ -1597,7 +1597,7 @@ namespace UnitTest //StartDrilling("lanmigration2"); } - ~Integ_LANSessionMigarationTestTest2() override + ~LANSessionMigarationTestTest2() override { StopGridMateService(m_gridMates[0]); @@ -1816,7 +1816,7 @@ namespace UnitTest * 3. Add 2 new joins to the original session. * Original session should remain fully operational with 4 members in it. */ - class Integ_LANSessionMigarationTestTest3 + class LANSessionMigarationTestTest3 : public SessionEventBus::Handler , public GridMateMPTestFixture { @@ -1910,7 +1910,7 @@ namespace UnitTest } } } - Integ_LANSessionMigarationTestTest3() + LANSessionMigarationTestTest3() { ////////////////////////////////////////////////////////////////////////// // Create all grid mates @@ -1936,7 +1936,7 @@ namespace UnitTest //StartDrilling("lanmigration2"); } - ~Integ_LANSessionMigarationTestTest3() override + ~LANSessionMigarationTestTest3() override { StopGridMateService(m_gridMates[0]); @@ -2122,13 +2122,13 @@ namespace UnitTest } GM_TEST_SUITE(SessionSuite) -GM_TEST(Integ_LANSessionMatchmakingParamsTest) -GM_TEST(Integ_LANSessionTest) +GM_TEST(DISABLED_LANSessionMatchmakingParamsTest) +GM_TEST(DISABLED_LANSessionTest) #if (AZ_TRAIT_GRIDMATE_TEST_SOCKET_IPV6_SUPPORT_ENABLED) -GM_TEST(Integ_LANSessionTestIPv6) +GM_TEST(DISABLED_LANSessionTestIPv6) #endif -GM_TEST(Integ_LANMultipleSessionTest) -GM_TEST(Integ_LANLatencySessionTest) +GM_TEST(DISABLED_LANMultipleSessionTest) +GM_TEST(DISABLED_LANLatencySessionTest) // Manually enabled tests (require 2+ machines and online services) //GM_TEST(LANSessionMigarationTestTest) diff --git a/Code/Framework/GridMate/Tests/StreamSecureSocketDriverTests.cpp b/Code/Framework/GridMate/Tests/StreamSecureSocketDriverTests.cpp index a7e9a7ccda..1f05520457 100644 --- a/Code/Framework/GridMate/Tests/StreamSecureSocketDriverTests.cpp +++ b/Code/Framework/GridMate/Tests/StreamSecureSocketDriverTests.cpp @@ -110,7 +110,7 @@ namespace UnitTest std::array m_buffer; }; - class Integ_StreamSecureSocketDriverTestsBindSocketEmpty + class DISABLED_StreamSecureSocketDriverTestsBindSocketEmpty : public GridMateMPTestFixture { public: @@ -134,7 +134,7 @@ namespace UnitTest } }; - class Integ_StreamSecureSocketDriverTestsConnection + class DISABLED_StreamSecureSocketDriverTestsConnection : public GridMateMPTestFixture { public: @@ -146,7 +146,7 @@ namespace UnitTest } }; - class Integ_StreamSecureSocketDriverTestsConnectionAndHelloWorld + class DISABLED_StreamSecureSocketDriverTestsConnectionAndHelloWorld : public GridMateMPTestFixture { public: @@ -190,7 +190,7 @@ namespace UnitTest } }; - class Integ_StreamSecureSocketDriverTestsPingPong + class DISABLED_StreamSecureSocketDriverTestsPingPong : public GridMateMPTestFixture { public: @@ -425,13 +425,13 @@ namespace UnitTest void BuildStateMachine() { - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_TOP), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStateTop), AZ::HSM::InvalidStateId, TS_START); - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_START), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStateStart), TS_TOP); - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_SERVER_GET_PING), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStateServerGetPing), TS_TOP); - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_PING_GET_SERVER), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStatePingGetServer), TS_TOP); - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_SERVER_GET_PONG), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStateServerGetPong), TS_TOP); - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_PONG_GET_SERVER), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStatePongGetServer), TS_TOP); - m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_IN_ERROR), AZ::HSM::StateHandler(this, &Integ_StreamSecureSocketDriverTestsPingPong::OnStateInError), TS_TOP); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_TOP), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStateTop), AZ::HSM::InvalidStateId, TS_START); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_START), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStateStart), TS_TOP); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_SERVER_GET_PING), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStateServerGetPing), TS_TOP); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_PING_GET_SERVER), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStatePingGetServer), TS_TOP); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_SERVER_GET_PONG), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStateServerGetPong), TS_TOP); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_PONG_GET_SERVER), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStatePongGetServer), TS_TOP); + m_stateMachine.SetStateHandler(AZ_HSM_STATE_NAME(TS_IN_ERROR), AZ::HSM::StateHandler(this, &DISABLED_StreamSecureSocketDriverTestsPingPong::OnStateInError), TS_TOP); m_stateMachine.Start(); } @@ -486,10 +486,10 @@ namespace UnitTest } GM_TEST_SUITE(StreamSecureSocketDriverTests) - GM_TEST(Integ_StreamSecureSocketDriverTestsBindSocketEmpty); - GM_TEST(Integ_StreamSecureSocketDriverTestsConnection); - GM_TEST(Integ_StreamSecureSocketDriverTestsConnectionAndHelloWorld); - GM_TEST(Integ_StreamSecureSocketDriverTestsPingPong); + GM_TEST(DISABLED_StreamSecureSocketDriverTestsBindSocketEmpty); + GM_TEST(DISABLED_StreamSecureSocketDriverTestsConnection); + GM_TEST(DISABLED_StreamSecureSocketDriverTestsConnectionAndHelloWorld); + GM_TEST(DISABLED_StreamSecureSocketDriverTestsPingPong); GM_TEST_SUITE_END() #endif // AZ_TRAIT_GRIDMATE_ENABLE_OPENSSL diff --git a/Code/Framework/GridMate/Tests/StreamSocketDriverTests.cpp b/Code/Framework/GridMate/Tests/StreamSocketDriverTests.cpp index f9e9395fbf..9dd22bedc7 100644 --- a/Code/Framework/GridMate/Tests/StreamSocketDriverTests.cpp +++ b/Code/Framework/GridMate/Tests/StreamSocketDriverTests.cpp @@ -308,7 +308,7 @@ namespace UnitTest } }; - class Integ_StreamSocketDriverTestsTooManyConnections + class DISABLED_StreamSocketDriverTestsTooManyConnections : public GridMateMPTestFixture { public: @@ -529,7 +529,7 @@ GM_TEST_SUITE(StreamSocketDriverTests) GM_TEST(StreamSocketDriverTestsSimpleLockStepConnection); GM_TEST(StreamSocketDriverTestsEstablishConnectAndSend); GM_TEST(StreamSocketDriverTestsManyRandomPackets); - GM_TEST(Integ_StreamSocketDriverTestsTooManyConnections); + GM_TEST(DISABLED_StreamSocketDriverTestsTooManyConnections); GM_TEST(StreamSocketDriverTestsClientToInvalidServer); GM_TEST(StreamSocketDriverTestsManySends); diff --git a/Code/Framework/GridMate/Tests/TestProfiler.cpp b/Code/Framework/GridMate/Tests/TestProfiler.cpp deleted file mode 100644 index 26e9312ecf..0000000000 --- a/Code/Framework/GridMate/Tests/TestProfiler.cpp +++ /dev/null @@ -1,244 +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 "Tests.h" -#include "TestProfiler.h" - -#include -#include - -#include -#include - -using namespace GridMate; - -typedef set ProfilerSet; - -static bool CollectPerformanceCounters(const AZ::Debug::ProfilerRegister& reg, const AZStd::thread_id&, ProfilerSet& profilers, const char* systemId) -{ - if (reg.m_type != AZ::Debug::ProfilerRegister::PRT_TIME) - { - return true; - } - if (reg.m_systemId != AZ::Crc32(systemId)) - { - return true; - } - - const AZ::Debug::ProfilerRegister* profReg = ® - profilers.insert(profReg); - return true; -} - -static AZStd::string FormatString(const AZStd::string& pre, const AZStd::string& name, const AZStd::string& post, AZ::u64 time, AZ::u64 calls) -{ - AZStd::string units = "us"; - if (AZ::u64 divtime = time / 1000) - { - time = divtime; - units = "ms"; - } - return AZStd::string::format("%s%s %s %10llu%s (%llu calls)\n", pre.c_str(), name.c_str(), post.c_str(), time, units.c_str(), calls); -} - -struct TotalSortContainer -{ - TotalSortContainer(const AZ::Debug::ProfilerRegister* self = nullptr) - { - m_self = self; - } - - void Print(AZ::s32 level, const char* systemId) - { - if (m_self && level >= 0) - { - AZStd::string levelIndent; - for (AZ::s32 i = 0; i < level; i++) - { - levelIndent += (i == level - 1) ? "+---" : "| "; - } - AZStd::string name = m_self->m_name ? m_self->m_name : m_self->m_function; - AZStd::string outputTotal = FormatString(levelIndent, name, " Total:", m_self->m_timeData.m_time, m_self->m_timeData.m_calls); - AZ_Printf(systemId, outputTotal.c_str()); - - if (m_self->m_timeData.m_childrenTime || m_self->m_timeData.m_childrenCalls) - { - AZStd::string childIndent = levelIndent; - for (auto i = name.begin(); i != name.end(); ++i) - { - childIndent += " "; - } - childIndent[level * 4] = '|'; - - AZStd::string outputChild = FormatString(childIndent, "", "Child:", m_self->m_timeData.m_childrenTime, m_self->m_timeData.m_childrenCalls); - AZ_Printf(systemId, outputChild.c_str()); - - AZStd::string outputSelf = FormatString(childIndent, "", "Self :", m_self->m_timeData.m_time - m_self->m_timeData.m_childrenTime, m_self->m_timeData.m_calls); - AZ_Printf(systemId, outputSelf.c_str()); - } - } - - for (auto i = m_children.begin(); i != m_children.end(); ++i) - { - i->Print(level + 1, systemId); - } - } - - TotalSortContainer* Find(const AZ::Debug::ProfilerRegister* obj) - { - if (m_self == obj) - { - return this; - } - - for (TotalSortContainer& child : m_children) - { - TotalSortContainer* found = child.Find(obj); - if (found) - { - return found; - } - } - - return nullptr; - } - - struct TotalSorter - { - bool operator()(const TotalSortContainer& a, const TotalSortContainer& b) const - { - if (a.m_self->m_timeData.m_time == b.m_self->m_timeData.m_time) - { - return a.m_self > b.m_self; - } - return a.m_self->m_timeData.m_time > b.m_self->m_timeData.m_time; - } - }; - set m_children; - const AZ::Debug::ProfilerRegister* m_self; -}; - -void TestProfiler::StartProfiling() -{ - StopProfiling(); - - AZ::Debug::Profiler::Create(); -} - -void TestProfiler::StopProfiling() -{ - if (AZ::Debug::Profiler::IsReady()) - { - AZ::Debug::Profiler::Destroy(); - } -} - -void TestProfiler::PrintProfilingTotal(const char* systemId) -{ - if (!AZ::Debug::Profiler::IsReady()) - { - return; - } - - ProfilerSet profilers; - AZ::Debug::Profiler::Instance().ReadRegisterValues(AZStd::bind(&CollectPerformanceCounters, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::ref(profilers), systemId)); - - // Validate we wont get stuck in an infinite loop - TotalSortContainer root; - for (auto i = profilers.begin(); i != profilers.end(); ) - { - const AZ::Debug::ProfilerRegister* profile = *i; - if (profile->m_timeData.m_lastParent) - { - auto parent = profilers.find(profile->m_timeData.m_lastParent); - if (parent == profilers.end()) - { - // Error, just ignore this entry - i = profilers.erase(i); - continue; - } - } - ++i; - } - - // Put all root nodes into the final list - for (auto i = profilers.begin(); i != profilers.end(); ) - { - const AZ::Debug::ProfilerRegister* profile = *i; - if (!profile->m_timeData.m_lastParent) - { - root.m_children.insert(profile); - i = profilers.erase(i); - } - else - { - ++i; - } - } - - // Put all non-root nodes into the final list - while (!profilers.empty()) - { - for (auto i = profilers.begin(); i != profilers.end(); ) - { - const AZ::Debug::ProfilerRegister* profile = *i; - TotalSortContainer* found = root.Find(profile->m_timeData.m_lastParent); - if (found) - { - found->m_children.insert(profile); - i = profilers.erase(i); - } - else - { - ++i; - } - } - } - - AZ_Printf(systemId, "Profiling timers by total execution time:\n"); - root.Print(-1, systemId); -} - -void TestProfiler::PrintProfilingSelf(const char* systemId) -{ - if (!AZ::Debug::Profiler::IsReady()) - { - return; - } - - ProfilerSet profilers; - AZ::Debug::Profiler::Instance().ReadRegisterValues(AZStd::bind(&CollectPerformanceCounters, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::ref(profilers), systemId)); - - struct SelfSorter - { - bool operator()(const AZ::Debug::ProfilerRegister* a, const AZ::Debug::ProfilerRegister* b) const - { - auto aTime = a->m_timeData.m_time - a->m_timeData.m_childrenTime; - auto bTime = b->m_timeData.m_time - b->m_timeData.m_childrenTime; - - if (aTime == bTime) - { - return a > b; - } - return aTime > bTime; - } - }; - - set selfSorted; - for (auto& profiler : profilers) - { - selfSorted.insert(profiler); - } - - AZ_Printf(systemId, "Profiling timers by exclusive execution time:\n"); - for (auto profiler : selfSorted) - { - AZStd::string str = FormatString("", profiler->m_name ? profiler->m_name : profiler->m_function, "Self Time:", - profiler->m_timeData.m_time - profiler->m_timeData.m_childrenTime, profiler->m_timeData.m_calls); - AZ_Printf(systemId, str.c_str()); - } -} diff --git a/Code/Framework/GridMate/Tests/TestProfiler.h b/Code/Framework/GridMate/Tests/TestProfiler.h deleted file mode 100644 index 816c001645..0000000000 --- a/Code/Framework/GridMate/Tests/TestProfiler.h +++ /dev/null @@ -1,24 +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 - * - */ -#ifndef GM_TEST_PROFILER_H -#define GM_TEST_PROFILER_H - -namespace GridMate -{ - class TestProfiler - { - public: - static void StartProfiling(); - static void StopProfiling(); - - static void PrintProfilingTotal(const char* systemId); - static void PrintProfilingSelf(const char* systemId); - }; -} - -#endif diff --git a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake index 3ff67f8eda..cf1cd087ee 100644 --- a/Code/Framework/GridMate/Tests/gridmate_test_files.cmake +++ b/Code/Framework/GridMate/Tests/gridmate_test_files.cmake @@ -12,6 +12,7 @@ set(FILES Session.cpp Serialize.cpp Certificates.cpp + Replica.cpp ReplicaSmall.cpp ReplicaMedium.cpp ReplicaBehavior.cpp diff --git a/Code/Legacy/CryCommon/CryPath.h b/Code/Legacy/CryCommon/CryPath.h index 197c9b2d56..51b379ac51 100644 --- a/Code/Legacy/CryCommon/CryPath.h +++ b/Code/Legacy/CryCommon/CryPath.h @@ -520,14 +520,14 @@ namespace PathUtil unsigned int index = 0; if (relativePath.length() && relativePath[index] == '@') // already aliased { - if (AZ::StringFunc::Equal(relativePath.c_str(), "@assets@/", false, 9)) + if (AZ::StringFunc::Equal(relativePath.c_str(), "@products@/", false, 9)) { return relativePath.substr(9); // assets is assumed. } return relativePath; } - const char* rootValue = gEnv->pFileIO->GetAlias("@root@"); + const char* rootValue = gEnv->pFileIO->GetAlias("@products@"); if (rootValue) { stack_string rootPath(ToUnixPath(rootValue)); @@ -538,7 +538,7 @@ namespace PathUtil ) { stack_string chopped_string = relativePath.substr(rootPath.size()); - stack_string rooted = stack_string("@root@") + chopped_string; + stack_string rooted = stack_string("@products@") + chopped_string; return rooted; } } 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 9e1cf92267..ebfc866f4a 100644 --- a/Code/Legacy/CrySystem/CMakeLists.txt +++ b/Code/Legacy/CrySystem/CMakeLists.txt @@ -27,8 +27,7 @@ ly_add_target( 3rdParty::expat 3rdParty::lz4 3rdParty::md5 - 3rdParty::tiff - 3rdParty::zlib + 3rdParty::TIFF 3rdParty::zstd Legacy::CryCommon Legacy::CrySystem.XMLBinary diff --git a/Code/Legacy/CrySystem/ConsoleBatchFile.cpp b/Code/Legacy/CrySystem/ConsoleBatchFile.cpp index bdc8d6de7d..5b96b943a7 100644 --- a/Code/Legacy/CrySystem/ConsoleBatchFile.cpp +++ b/Code/Legacy/CrySystem/ConsoleBatchFile.cpp @@ -59,10 +59,10 @@ bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename) AZStd::string filename; - if (sFilename[0] != '@') // console config files are actually by default in @root@ instead of @assets@ + if (sFilename[0] != '@') // console config files are actually by default in @products@ instead of @products@ { // However, if we've passed in a relative or absolute path that matches an existing file name, - // don't change it. Only change it to "@root@/filename" and strip off any relative paths + // don't change it. Only change it to "@products@/filename" and strip off any relative paths // if the given pattern *didn't* match a file. if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(sFilename)) { @@ -70,7 +70,7 @@ bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename) } else { - filename = PathUtil::Make("@root@", PathUtil::GetFile(sFilename)); + filename = PathUtil::Make("@products@", PathUtil::GetFile(sFilename)); } } else diff --git a/Code/Legacy/CrySystem/DebugCallStack.cpp b/Code/Legacy/CrySystem/DebugCallStack.cpp index bea6c8c035..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; @@ -372,7 +375,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex) const char* logAlias = gEnv->pFileIO->GetAlias("@log@"); if (!logAlias) { - logAlias = gEnv->pFileIO->GetAlias("@root@"); + logAlias = gEnv->pFileIO->GetAlias("@products@"); } if (logAlias) { diff --git a/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp b/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp index 2e61760d8c..49af5080ec 100644 --- a/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp +++ b/Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp @@ -306,7 +306,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) } AZStd::string levelContainerPakPath; - AZ::StringFunc::Path::Join("@assets@", m_levelsFolder.c_str(), levelContainerPakPath); + AZ::StringFunc::Path::Join("@products@", m_levelsFolder.c_str(), levelContainerPakPath); if (subfolder && subfolder[0]) { AZ::StringFunc::Path::Join(levelContainerPakPath.c_str(), subfolder, levelContainerPakPath); 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/SystemCFG.cpp b/Code/Legacy/CrySystem/SystemCFG.cpp index c97345a518..7a643f2069 100644 --- a/Code/Legacy/CrySystem/SystemCFG.cpp +++ b/Code/Legacy/CrySystem/SystemCFG.cpp @@ -292,10 +292,10 @@ static bool ParseSystemConfig(const AZStd::string& strSysConfigFilePath, ILoadCo // to either root or assets/config. this is done so that code can just request a simple file name and get its data if ( !(file.Open(filename.c_str(), "rb")) && - !(file.Open((AZStd::string("@root@/") + filename).c_str(), "rb")) && - !(file.Open((AZStd::string("@assets@/") + filename).c_str(), "rb")) && - !(file.Open((AZStd::string("@assets@/config/") + filename).c_str(), "rb")) && - !(file.Open((AZStd::string("@assets@/config/spec/") + filename).c_str(), "rb")) + !(file.Open((AZStd::string("@products@/") + filename).c_str(), "rb")) && + !(file.Open((AZStd::string("@products@/") + filename).c_str(), "rb")) && + !(file.Open((AZStd::string("@products@/config/") + filename).c_str(), "rb")) && + !(file.Open((AZStd::string("@products@/config/spec/") + filename).c_str(), "rb")) ) { if (warnIfMissing) @@ -414,7 +414,7 @@ static bool ParseSystemConfig(const AZStd::string& strSysConfigFilePath, ILoadCo // replace '\\\\' with '\\' and '\\\"' with '\"' AZ::StringFunc::Replace(strValue, "\\\\", "\\"); AZ::StringFunc::Replace(strValue, "\\\"", "\""); - + pSink->OnLoadConfigurationEntry(strKey.c_str(), strValue.c_str(), strGroup.c_str()); } } diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index 3187095094..ee1c498a9a 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -796,7 +796,7 @@ void CSystem::OpenBasicPaks() bBasicPaksLoaded = true; // open pak files - constexpr AZStd::string_view paksFolder = "@assets@/*.pak"; // (@assets@ assumed) + constexpr AZStd::string_view paksFolder = "@products@/*.pak"; // (@products@ assumed) m_env.pCryPak->OpenPacks(paksFolder); InlineInitializationProcessing("CSystem::OpenBasicPaks OpenPacks( paksFolder.c_str() )"); @@ -805,7 +805,7 @@ void CSystem::OpenBasicPaks() // Open engine packs ////////////////////////////////////////////////////////////////////////// - const char* const assetsDir = "@assets@"; + const char* const assetsDir = "@products@"; // After game paks to have same search order as with files on disk m_env.pCryPak->OpenPack(assetsDir, "engine.pak"); @@ -874,7 +874,7 @@ void CSystem::OpenLanguageAudioPak([[maybe_unused]] const char* sLanguage) if (!AZ::StringFunc::Equal(sLocalizationFolder, "Languages", false)) { - sLocalizationFolder = "@assets@"; + sLocalizationFolder = "@products@"; } // load localized pak with crc32 filenames on consoles to save memory. @@ -1260,9 +1260,6 @@ AZ_POP_DISABLE_WARNING InlineInitializationProcessing("CSystem::Init Create console"); - // Need to load the engine.pak that includes the config files needed during initialization - m_env.pCryPak->OpenPack("@assets@", "engine.pak"); - InitFileSystem_LoadEngineFolders(startupParams); #if !defined(RELEASE) || defined(RELEASE_LOGGING) @@ -1276,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()) { @@ -1296,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/AWSNativeSDKInit/source/Platform/Android/InitializeCerts_Android.cpp b/Code/Tools/AWSNativeSDKInit/source/Platform/Android/InitializeCerts_Android.cpp index e0a6725ed4..24366498a5 100644 --- a/Code/Tools/AWSNativeSDKInit/source/Platform/Android/InitializeCerts_Android.cpp +++ b/Code/Tools/AWSNativeSDKInit/source/Platform/Android/InitializeCerts_Android.cpp @@ -27,7 +27,7 @@ namespace AWSNativeSDKInit void CopyCaCertBundle() { AZStd::vector contents; - AZStd::string certificatePath = "@assets@/certificates/aws/cacert.pem"; + AZStd::string certificatePath = "@products@/certificates/aws/cacert.pem"; AZStd::string publicStoragePath = AZ::Android::Utils::GetAppPublicStoragePath(); publicStoragePath.append("/certificates/aws/cacert.pem"); diff --git a/Code/Tools/AssetBundler/tests/tests_main.cpp b/Code/Tools/AssetBundler/tests/tests_main.cpp index 51dd0ce67c..21a6bf8a6f 100644 --- a/Code/Tools/AssetBundler/tests/tests_main.cpp +++ b/Code/Tools/AssetBundler/tests/tests_main.cpp @@ -291,6 +291,9 @@ namespace AssetBundler int main(int argc, char* argv[]) { + AZ::Debug::Trace::HandleExceptions(true); + AZ::Test::ApplyGlobalParameters(&argc, argv); + INVOKE_AZ_UNIT_TEST_MAIN(); AZ::AllocatorInstance::Create(); diff --git a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp index 9c9c517020..da2edcc51f 100644 --- a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace AssetBuilder { @@ -80,6 +81,7 @@ AZ::ComponentTypeList AssetBuilderApplication::GetRequiredSystemComponents() con azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), }); return components; diff --git a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp index 42a43ef84e..78244ab02c 100644 --- a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp @@ -683,31 +683,26 @@ void AssetBuilderComponent::ProcessJob(const AssetBuilderSDK::ProcessJobFunction AZ_Assert(settingsRegistry != nullptr, "SettingsRegistry must be ready for use in the AssetBuilder."); // The root path is the cache plus the platform name. - AZ::IO::FixedMaxPath newRoot(m_gameCache); + AZ::IO::FixedMaxPath newProjectCache(m_gameCache); // Check if the platform identifier is a valid "asset platform" // If so, use it, other wise use the OS default platform as a fail safe // This is to make sure the "debug platform" isn't added as a path segment - // the Cache Root folder + // the Cache ProjectCache folder if (AzFramework::PlatformHelper::GetPlatformIdFromName(request.m_platformInfo.m_identifier) != AzFramework::PlatformId::Invalid) { - newRoot /= request.m_platformInfo.m_identifier; + newProjectCache /= request.m_platformInfo.m_identifier; } else { - newRoot /= AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME); + newProjectCache /= AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME); } - // The asset path is root and the lower case game name. - AZ::IO::FixedMaxPath newAssets = newRoot; - // Now set the paths and run the job. { // Save out the prior paths. - ScopedAliasSetter assetAliasScope(*ioBase, "@assets@", newAssets.c_str()); - ScopedAliasSetter rootAliasScope(*ioBase, "@root@", newRoot.c_str()); - ScopedAliasSetter projectplatformCacheAliasScope(*ioBase, "@projectplatformcache@", newRoot.c_str()); + ScopedAliasSetter projectPlatformCacheAliasScope(*ioBase, "@products@", newProjectCache.c_str()); ScopedSettingsRegistrySetter cacheRootFolderScope(*settingsRegistry, - AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder, newRoot.Native()); + AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder, newProjectCache.Native()); // Invoke the Process Job function job(request, outResponse); diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp index 6eee3d6f2b..d7cdd30be8 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp @@ -27,7 +27,7 @@ namespace AssetProcessor , m_registryBuiltOnce(false) , m_registriesMutex(QMutex::Recursive) { - + for (const AssetBuilderSDK::PlatformInfo& info : m_platformConfig->GetEnabledPlatforms()) { m_platforms.push_back(QString::fromUtf8(info.m_identifier.c_str())); @@ -38,17 +38,9 @@ namespace AssetProcessor // save 30mb for this. Really large projects do get this big (and bigger) // if you don't do this, things get fragmented very fast. - m_saveBuffer.reserve(1024 * 1024 * 30); - - m_absoluteDevFolderPath[0] = 0; - m_absoluteDevGameFolderPath[0] = 0; - - AZStd::string engineRoot; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - azstrcpy(m_absoluteDevFolderPath, AZ_MAX_PATH_LEN, engineRoot.c_str()); + m_saveBuffer.reserve(1024 * 1024 * 30); - AZStd::string gameFolderPath{AssetUtilities::ComputeProjectPath().toUtf8().constData()}; - azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, gameFolderPath.c_str()); + AssetUtilities::ComputeProjectPath(); AssetUtilities::ComputeProjectCacheRoot(m_cacheRootDir); @@ -359,7 +351,7 @@ namespace AssetProcessor [[maybe_unused]] bool makeDirResult = AZ::IO::SystemFile::CreateDir(absPath.toUtf8().constData()); AZ_Warning(AssetProcessor::ConsoleChannel, makeDirResult, "Failed create folder %s", platformCacheDir.toUtf8().constData()); } - + // if we succeeded in doing this, then use "rename" to move the file over the previous copy. bool moved = AssetUtilities::MoveFileWithTimeout(tempRegistryFile, actualRegistryFile, 3); allCatalogsSaved = allCatalogsSaved && moved; @@ -382,7 +374,7 @@ namespace AssetProcessor } } } - + { // scoped to minimize the duration of this mutex lock QMutexLocker locker(&m_savingRegistryMutex); @@ -605,7 +597,7 @@ namespace AssetProcessor AZStd::string nameForMap(relativeFilePath.toUtf8().constData()); AZStd::to_lower(nameForMap.begin(), nameForMap.end()); - + m_sourceNameToSourceUUIDMap.insert({ nameForMap, sourceUuid }); } @@ -627,16 +619,6 @@ namespace AssetProcessor ////////////////////////////////////////////////////////////////////////// - const char* AssetCatalog::GetAbsoluteDevGameFolderPath() - { - return m_absoluteDevGameFolderPath; - } - - const char* AssetCatalog::GetAbsoluteDevRootFolderPath() - { - return m_absoluteDevFolderPath; - } - bool AssetCatalog::GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullSourceOrProductPath, AZStd::string& relativeProductPath) { ProcessGetRelativeProductPathFromFullSourceOrProductPathRequest(fullSourceOrProductPath, relativeProductPath); @@ -976,7 +958,7 @@ namespace AssetProcessor // regardless of which way we come into this function we must always use ConvertToRelativePath // to convert from whatever the input format is to a database path (which may include output prefix) - QString databaseName; + QString databaseName; QString scanFolder; if (!AzFramework::StringFunc::Path::IsRelative(sourcePath)) { @@ -1163,7 +1145,7 @@ namespace AssetProcessor AZStd::string AssetCatalog::GetAssetPathById(const AZ::Data::AssetId& id) { return GetAssetInfoById(id).m_relativePath; - + } AZ::Data::AssetId AssetCatalog::GetAssetIdByPath(const char* path, const AZ::Data::AssetType& typeToRegister, bool autoRegisterIfNotFound) @@ -1175,7 +1157,7 @@ namespace AssetProcessor AZStd::string relProductPath; GetRelativeProductPathFromFullSourceOrProductPath(path, relProductPath); QString tempPlatformName = GetDefaultAssetPlatform(); - + AZ::Data::AssetId assetId; { QMutexLocker locker(&m_registriesMutex); @@ -1344,7 +1326,7 @@ namespace AssetProcessor //remove aliases if present normalisedAssetPath = AssetUtilities::NormalizeAndRemoveAlias(normalisedAssetPath); - if (!normalisedAssetPath.isEmpty()) // this happens if it comes in as just for example "@assets@/" + if (!normalisedAssetPath.isEmpty()) // this happens if it comes in as just for example "@products@/" { AZStd::lock_guard lock(m_databaseMutex); @@ -1439,7 +1421,7 @@ namespace AssetProcessor relativePath = entry.m_sourceName; watchFolder = scanEntry.m_scanFolder; - + return true; } @@ -1489,7 +1471,7 @@ namespace AssetProcessor { return foundIter->second; } - + // we did not find it - try the backup mapping! AssetId legacyMapping = registryToUse.GetAssetIdByLegacyAssetId(assetId); if (legacyMapping.IsValid()) @@ -1533,7 +1515,7 @@ namespace AssetProcessor return !assetInfo.m_relativePath.empty(); } - + // Asset isn't in the DB or in the APM queue, we don't know what this asset ID is return false; } @@ -1588,7 +1570,7 @@ namespace AssetProcessor AZStd::string sourceFileFullPath; AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), sourceFileFullPath); assetInfo.m_sizeBytes = AZ::IO::SystemFile::Length(sourceFileFullPath.c_str()); - + assetInfo.m_assetType = AZ::Uuid::CreateNull(); // most source files don't have a type! // Go through the list of source assets and see if this asset's file path matches any of the filters diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h index 84e96bcf3e..e876e259fb 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h @@ -88,8 +88,6 @@ namespace AssetProcessor ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::AssetSystem::AssetSystemRequestBus::Handler overrides - const char* GetAbsoluteDevGameFolderPath() override; - const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override; //! Given a partial or full source file path, respond with its relative path and the watch folder it is relative to. @@ -215,8 +213,6 @@ namespace AssetProcessor AZStd::vector m_saveBuffer; // so that we don't realloc all the time - char m_absoluteDevFolderPath[AZ_MAX_PATH_LEN]; - char m_absoluteDevGameFolderPath[AZ_MAX_PATH_LEN]; QDir m_cacheRootDir; }; } diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp index 3a7bc6ef3e..4cd4489a4c 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp @@ -56,15 +56,8 @@ namespace AssetProcessor // cache this up front. Note that it can fail here, and will retry later. InitializeCacheRoot(); - m_absoluteDevFolderPath[0] = 0; - m_absoluteDevGameFolderPath[0] = 0; - QDir assetRoot; - if (AssetUtilities::ComputeAssetRoot(assetRoot)) - { - azstrcpy(m_absoluteDevFolderPath, AZ_MAX_PATH_LEN, assetRoot.absolutePath().toUtf8().constData()); - azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, AssetUtilities::ComputeProjectPath().toUtf8().constData()); - } + AssetUtilities::ComputeAssetRoot(assetRoot); using namespace AZStd::placeholders; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h index 376af5d773..891e091f91 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h @@ -440,8 +440,6 @@ namespace AssetProcessor AZStd::mutex m_sourceUUIDToSourceInfoMapMutex; QString m_normalizedCacheRootPath; - char m_absoluteDevFolderPath[AZ_MAX_PATH_LEN]; - char m_absoluteDevGameFolderPath[AZ_MAX_PATH_LEN]; QDir m_cacheRootDir; bool m_isCurrentlyScanning = false; bool m_quitRequested = false; diff --git a/Code/Tools/AssetProcessor/native/FileServer/fileServer.cpp b/Code/Tools/AssetProcessor/native/FileServer/fileServer.cpp index 30d8e5fada..c35f3a4b0f 100644 --- a/Code/Tools/AssetProcessor/native/FileServer/fileServer.cpp +++ b/Code/Tools/AssetProcessor/native/FileServer/fileServer.cpp @@ -95,7 +95,7 @@ void FileServer::ConnectionAdded(unsigned int connId, Connection* connection) Q_UNUSED(connection); // Connection has not completed negotiation yet, register to be notified - // when we know what platform is connected and map the @assets@ alias then + // when we know what platform is connected and map the @products@ alias then connect(connection, &Connection::AssetPlatformChanged, this, [this, connection]() { auto fileIO = m_fileIOs[connection->ConnectionId()]; @@ -114,8 +114,7 @@ void FileServer::ConnectionAdded(unsigned int connId, Connection* connection) projectCacheRoot = QDir(projectCacheRoot.absoluteFilePath(assetPlatform)); } const char* projectCachePath = projectCacheRoot.absolutePath().toUtf8().data(); - fileIO->SetAlias("@assets@", projectCachePath); - fileIO->SetAlias("@root@", projectCachePath); + fileIO->SetAlias("@products@", projectCachePath); if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { @@ -955,10 +954,10 @@ void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsig FileTreeResponse::FolderList folders; AZStd::vector untestedFolders; - if (fileIO->IsDirectory("@assets@")) + if (fileIO->IsDirectory("@products@")) { - folders.push_back("@assets@"); - untestedFolders.push_back("@assets@"); + folders.push_back("@products@"); + untestedFolders.push_back("@products@"); } if (fileIO->IsDirectory("@usercache@")) { @@ -975,11 +974,6 @@ void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsig folders.push_back("@log@"); untestedFolders.push_back("@log@"); } - if (fileIO->IsDirectory("@root@")) - { - folders.push_back("@root@"); - untestedFolders.push_back("@root@"); - } AZ::IO::Result res = ResultCode::Success; diff --git a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp index 5d2d0e77d3..2a1c4e4755 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp @@ -766,7 +766,7 @@ namespace AssetProcessor TEST_F(AssetCatalogTest_GetFullSourcePath, AliasedCachePath_ReturnsAbsolutePathToSource) { //feed it a path with alias and asset id - QString fileToCheck = "@assets@/subfolder3/randomfileoutput.random1"; + QString fileToCheck = "@products@/subfolder3/randomfileoutput.random1"; EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random")); } @@ -787,7 +787,7 @@ namespace AssetProcessor TEST_F(AssetCatalogTest_GetFullSourcePath, InvalidSourcePathContainingCacheAlias_ReturnsAbsolutePathToSource) { //feed it a path with alias and input name - QString fileToCheck = "@assets@/somerandomfile.random"; + QString fileToCheck = "@products@/somerandomfile.random"; EXPECT_TRUE(TestGetFullSourcePath(fileToCheck, m_data->m_temporarySourceDir, true, "subfolder3/somerandomfile.random")); } diff --git a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp index 4b70f94120..d7801abb3d 100644 --- a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp @@ -262,7 +262,7 @@ namespace UnitTests auto result = m_data->m_reporter->ComputeDestination(entryContainer, m_data->m_platformConfig.GetScanFolderByPath(scanFolderEntry.m_scanFolder.c_str()), source, destination, destInfo); - ASSERT_EQ(result.IsSuccess(), expectSuccess) << result.GetError().c_str(); + ASSERT_EQ(result.IsSuccess(), expectSuccess) << (!result.IsSuccess() ? result.GetError().c_str() : ""); if (expectSuccess) { 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/tests/test_main.cpp b/Code/Tools/AssetProcessor/native/tests/test_main.cpp index 57f14832aa..e0cdc8cb3c 100644 --- a/Code/Tools/AssetProcessor/native/tests/test_main.cpp +++ b/Code/Tools/AssetProcessor/native/tests/test_main.cpp @@ -29,6 +29,9 @@ int main(int argc, char* argv[]) { qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "1"); + AZ::Debug::Trace::HandleExceptions(true); + AZ::Test::ApplyGlobalParameters(&argc, argv); + // If "--unittest" is present on the command line, run unit testing // and return immediately. Otherwise, continue as normal. AZ::Test::addTestEnvironment(new BaseAssetProcessorTestEnvironment()); diff --git a/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp b/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp index 50a472c44d..c33fc59d97 100644 --- a/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp +++ b/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp @@ -62,7 +62,10 @@ namespace AssetProcessor { searchStr = searchStr.mid(0, subidPos); } - AZ::Uuid filterAsUuid = AZ::Uuid::CreateStringPermissive(searchStr.toUtf8(), 0); + + // Cap the string to some reasonable length, we don't want to try parsing an entire book + size_t cappedStringLength = searchStr.length() > 60 ? 60 : searchStr.length(); + AZ::Uuid filterAsUuid = AZ::Uuid::CreateStringPermissive(searchStr.toUtf8(), cappedStringLength); return DescendantMatchesFilter(*assetTreeItem, filter, filterAsUuid); } diff --git a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp index 5b15a6a552..a8bc714698 100644 --- a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp @@ -47,7 +47,7 @@ void RCcontrollerUnitTests::Reset() m_rcController.m_RCJobListModel.m_jobs.clear(); m_rcController.m_RCJobListModel.m_jobsInFlight.clear(); m_rcController.m_RCJobListModel.m_jobsInQueueLookup.clear(); - + m_rcController.m_pendingCriticalJobsPerPlatform.clear(); m_rcController.m_jobsCountPerPlatform.clear(); @@ -56,7 +56,7 @@ void RCcontrollerUnitTests::Reset() m_rcController.m_RCQueueSortModel.AttachToModel(&m_rcController.m_RCJobListModel); m_rcController.m_RCQueueSortModel.m_currentJobRunKeyToJobEntries.clear(); m_rcController.m_RCQueueSortModel.m_currentlyConnectedPlatforms.clear(); -} +} void RCcontrollerUnitTests::StartTest() { @@ -183,11 +183,11 @@ void RCcontrollerUnitTests::RunRCControllerTests() QStringList tempJobNames; // Note that while this is an OS-SPECIFIC path, this test does not actually invoke the file system - // or file operators, so is purely doing in-memory testing. So the path does not actually matter and the + // or file operators, so is purely doing in-memory testing. So the path does not actually matter and the // test should function on other operating systems too. // test - exact match - tempJobNames << "c:/somerandomfolder/dev/blah/test.dds"; + tempJobNames << "c:/somerandomfolder/dev/blah/test.dds"; tempJobNames << "c:/somerandomfolder/dev/blah/test.cre"; // must not match // test - NO MATCH @@ -218,7 +218,7 @@ void RCcontrollerUnitTests::RunRCControllerTests() QList createdJobs; - + for (QString name : tempJobNames) { @@ -270,7 +270,7 @@ void RCcontrollerUnitTests::RunRCControllerTests() // EXACT MATCH TEST (with prefixes and such) NetworkRequestID requestID(1, 1234); - m_rcController.OnRequestCompileGroup(requestID, "pc", "@assets@/blah/test.dds", AZ::Data::AssetId()); + m_rcController.OnRequestCompileGroup(requestID, "pc", "@products@/blah/test.dds", AZ::Data::AssetId()); QCoreApplication::processEvents(QEventLoop::AllEvents); // this should have matched exactly one item, and when we finish that item, it should terminate: @@ -626,9 +626,9 @@ void RCcontrollerUnitTests::RunRCControllerTests() jobdetailsB.m_jobEntry.m_watchFolderPath = scanFolderInfo.ScanPath(); jobdetailsB.m_jobEntry.m_jobKey = "TestJobB"; jobdetailsB.m_jobEntry.m_builderGuid = builderUuid; - + jobdetailsB.m_critical = true; //make jobB critical so that it will be analyzed first even though we want JobA to run first - + AssetBuilderSDK::SourceFileDependency sourceFileBDependency; sourceFileBDependency.m_sourceFileDependencyPath = "fileB.txt"; @@ -694,10 +694,10 @@ void RCcontrollerUnitTests::RunRCControllerTests() m_rcController.DispatchJobs(); UNIT_TEST_EXPECT_TRUE(UnitTestUtils::BlockUntil(allJobsCompleted, 5000)); - UNIT_TEST_EXPECT_TRUE(jobFinishedB); + UNIT_TEST_EXPECT_TRUE(jobFinishedB); - // Now test the use case where we have a cyclic dependency, - // although the order in which these job will start is not defined but we can ensure that + // Now test the use case where we have a cyclic dependency, + // although the order in which these job will start is not defined but we can ensure that // all the job finishes and RCController goes Idle allJobsCompleted = false; Reset(); @@ -728,8 +728,8 @@ void RCcontrollerUnitTests::RunRCControllerTests() jobdetailsD.m_jobEntry.m_builderGuid = builderUuid; AssetBuilderSDK::SourceFileDependency sourceFileDDependency; sourceFileDDependency.m_sourceFileDependencyPath = "fileD.txt"; - - //creating cyclic job order dependencies i.e JobC and JobD have order job dependency on each other + + //creating cyclic job order dependencies i.e JobC and JobD have order job dependency on each other AssetBuilderSDK::JobDependency jobDependencyC("TestJobC", "pc", AssetBuilderSDK::JobDependencyType::Order, sourceFileCDependency); AssetBuilderSDK::JobDependency jobDependencyD("TestJobD", "pc", AssetBuilderSDK::JobDependencyType::Order, sourceFileDDependency); jobdetailsC.m_jobDependencyList.push_back({ jobDependencyD }); diff --git a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h index f77c706421..c281b3050a 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h +++ b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h @@ -262,11 +262,10 @@ namespace UnitTestUtils AZ::IO::FileIOBase::SetInstance(m_localFileIO); - m_localFileIO->SetAlias("@assets@", (newDir + QString("/ALIAS/assets")).toUtf8().constData()); + m_localFileIO->SetAlias("@products@", (newDir + QString("/ALIAS/assets")).toUtf8().constData()); m_localFileIO->SetAlias("@log@", (newDir + QString("/ALIAS/logs")).toUtf8().constData()); m_localFileIO->SetAlias("@usercache@", (newDir + QString("/ALIAS/cache")).toUtf8().constData()); m_localFileIO->SetAlias("@user@", (newDir + QString("/ALIAS/user")).toUtf8().constData()); - m_localFileIO->SetAlias("@root@", (newDir + QString("/ALIAS/root")).toUtf8().constData()); } ~ScopedDir() diff --git a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp index 6f974a0ff6..40d3bd3caa 100644 --- a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp @@ -104,17 +104,17 @@ ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun() // The build process may leave behind some temporaries, try to delete them RemoveTemporaries(); - QDir devRoot; - AssetUtilities::ComputeAssetRoot(devRoot); + QDir projectAssetRoot; + AssetUtilities::ComputeAssetRoot(projectAssetRoot); #if defined(EXTERNAL_CRASH_REPORTING) - CrashHandler::ToolsCrashHandler::InitCrashHandler("AssetProcessor", devRoot.absolutePath().toStdString()); + CrashHandler::ToolsCrashHandler::InitCrashHandler("AssetProcessor", projectAssetRoot.absolutePath().toStdString()); #endif AssetProcessor::MessageInfoBus::Handler::BusConnect(); // we have to monitor both the cache folder and the database file and restart AP if either of them gets deleted // It is important to note that we are monitoring the parent folder and not the actual cache folder itself since // we want to handle the use case on Mac OS if the user moves the cache folder to the trash. - m_qtFileWatcher.addPath(devRoot.absolutePath()); + m_qtFileWatcher.addPath(projectAssetRoot.absolutePath()); QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); @@ -615,7 +615,6 @@ void GUIApplicationManager::DirectoryChanged([[maybe_unused]] QString path) void GUIApplicationManager::FileChanged(QString path) { - QDir devRoot = ApplicationManager::GetSystemRoot(); QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite"); 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/AzTestRunner/src/main.cpp b/Code/Tools/AzTestRunner/src/main.cpp index 0874efeff2..acb208d3e5 100644 --- a/Code/Tools/AzTestRunner/src/main.cpp +++ b/Code/Tools/AzTestRunner/src/main.cpp @@ -18,45 +18,35 @@ namespace AzTestRunner const int LIB_NOT_FOUND = 102; const int SYMBOL_NOT_FOUND = 103; - // note that MODULE_SKIPPED is not an error condition, but not 0 to indicate its not the - // same as successfully running tests and finding them. - const int MODULE_SKIPPED = 104; - const char* INTEG_BOOTSTRAP = "AzTestIntegBootstrap"; - //! display proper usage of the application void usage([[maybe_unused]] AZ::Test::Platform& platform) { std::stringstream ss; ss << "AzTestRunner\n" - "Runs AZ unit and integration tests. Exit code is the result from GoogleTest.\n" + "Runs AZ tests. Exit code is the result from GoogleTest.\n" "\n" "Usage:\n" - " AzTestRunner.exe (AzRunUnitTests|AzRunIntegTests) [--integ] [--wait-for-debugger] [--pause-on-completion] [google-test-args]\n" + " AzTestRunner.exe (AzRunUnitTests|AzRunBenchmarks) [--wait-for-debugger] [--pause-on-completion] [google-test-args]\n" "\n" "Options:\n" " : the module to test\n" " : the name of the aztest hook function to run in the \n" " 'AzRunUnitTests' will hook into unit tests\n" - " 'AzRunIntegTests' will hook into integration tests\n" - " --integ: tells runner to bootstrap the engine, needed for integration tests\n" - " Note: you can run unit tests with a bootstrapped engine (AzRunUnitTests --integ),\n" - " but running integration tests without a bootstrapped engine (AzRunIntegTests w/ no --integ) might not work.\n" + " 'AzRunBenchmarks' will hook into benchmark tests\n" " --wait-for-debugger: tells runner to wait for debugger to attach to process (on supported platforms)\n" " --pause-on-completion: tells the runner to pause after running the tests\n" " --quiet: disables stdout for minimal output while running tests\n" "\n" "Example:\n" - " AzTestRunner.exe CrySystem.dll AzRunUnitTests --pause-on-completion\n" - " AzTestRunner.exe CrySystem.dll AzRunIntegTests --integ\n" + " AzTestRunner.exe AzCore.Tests.dll AzRunUnitTests --pause-on-completion\n" "\n" "Exit Codes:\n" " 0 - all tests pass\n" " 1 - test failure\n" << " " << INCORRECT_USAGE << " - incorrect usage (see above)\n" << " " << LIB_NOT_FOUND << " - library/dll could not be loaded\n" - << " " << SYMBOL_NOT_FOUND << " - export symbol not found\n" - << " " << MODULE_SKIPPED << " - non-integ module was skipped (not an error)\n"; + << " " << SYMBOL_NOT_FOUND << " - export symbol not found\n"; std::cerr << ss.str() << std::endl; } @@ -82,7 +72,6 @@ namespace AzTestRunner // capture optional arguments bool waitForDebugger = false; - bool isInteg = false; bool pauseOnCompletion = false; bool quiet = false; for (int i = 0; i < argc; i++) @@ -93,12 +82,6 @@ namespace AzTestRunner AZ::Test::RemoveParameters(argc, argv, i, i); i--; } - else if (strcmp(argv[i], "--integ") == 0) - { - isInteg = true; - AZ::Test::RemoveParameters(argc, argv, i, i); - i--; - } else if (strcmp(argv[i], "--pause-on-completion") == 0) { pauseOnCompletion = true; @@ -172,47 +155,11 @@ namespace AzTestRunner if (result != 0) { module.reset(); - - if ((isInteg) && (result == SYMBOL_NOT_FOUND)) - { - // special case: It is not required to put an INTEG test inside every DLL - so if - // we failed to find the INTEG entry point in this DLL, its not an error. - // its only an error if we find it and there are no tests, or we find it and tests actually - // fail. - std::cerr << "INTEG module has no entry point and will be skipped: " << lib << std::endl; - return MODULE_SKIPPED; - } - return result; } platform.SuppressPopupWindows(); - // Grab a bootstrapper library if requested - std::shared_ptr bootstrap; - if (isInteg) - { - bootstrap = platform.GetModule(INTEG_BOOTSTRAP); - if (!bootstrap->IsValid()) - { - std::cerr << "FAILED to load bootstrapper" << std::endl; - return LIB_NOT_FOUND; - } - - // Initialize the bootstrapper - auto init = bootstrap->GetFunction("Initialize"); - if (init->IsValid()) - { - int initResult = (*init)(); - if (initResult != 0) - { - std::cerr << "Bootstrapper Initialize failed with code " << initResult << ", exiting" << std::endl; - return initResult; - } - } - } - - // run the test main function. if (testMainFunction->IsValid()) { @@ -231,22 +178,6 @@ namespace AzTestRunner // system allocator / etc. module.reset(); - // Shutdown the bootstrapper - if (bootstrap) - { - auto shutdown = bootstrap->GetFunction("Shutdown"); - if (shutdown->IsValid()) - { - int shutdownResult = (*shutdown)(); - if (shutdownResult != 0) - { - std::cerr << "Bootstrapper shutdown failed with code " << shutdownResult << ", exiting" << std::endl; - return shutdownResult; - } - } - bootstrap.reset(); - } - if (pauseOnCompletion) { AzTestRunner::pause_on_completion(); @@ -258,6 +189,8 @@ namespace AzTestRunner int wrapped_main(int argc/*=0*/, char** argv/*=nullptr*/) { + AZ::Debug::Trace::HandleExceptions(true); + if (argc>0 && argv!=nullptr) { return wrapped_command_arg_main(argc, argv); diff --git a/Code/Tools/CrashHandler/Tools/ToolsCrashHandler.cpp b/Code/Tools/CrashHandler/Tools/ToolsCrashHandler.cpp index 527feac6b4..2cf7705e96 100644 --- a/Code/Tools/CrashHandler/Tools/ToolsCrashHandler.cpp +++ b/Code/Tools/CrashHandler/Tools/ToolsCrashHandler.cpp @@ -56,7 +56,7 @@ namespace CrashHandler if (fileIO) { // If our devroot alias is available, use that - const char* devAlias = fileIO->GetAlias("@devroot@"); + const char* devAlias = fileIO->GetAlias("@engroot@"); if (devAlias) { return devAlias; 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,13 +143,23 @@ namespace O3DE::ProjectManager if (m_gemModel->DoGemsToBeAddedHaveRequirements()) { - GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, toBeAdded, this); - confirmRequirementsDialog->exec(); + GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, this); + if(confirmRequirementsDialog->exec() == QDialog::Rejected) + { + return EnableDisableGemsResult::Cancel; + } + } - if (confirmRequirementsDialog->GetButtonResult() != QDialogButtonBox::ApplyRole) + if (m_gemModel->HasDependentGemsToRemove()) + { + GemDependenciesDialog* dependenciesDialog = new GemDependenciesDialog(m_gemModel, this); + if(dependenciesDialog->exec() == QDialog::Rejected) { - return false; + return EnableDisableGemsResult::Cancel; } + + toBeAdded = m_gemModel->GatherGemsToBeAdded(); + toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); } for (const QModelIndex& modelIndex : toBeAdded) @@ -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); + + 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(); + 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) - { - width = parentWidget->width(); - } + FlowLayout* flowLayout = static_cast(layout()); - if (m_widget) + // remove old tags + QLayoutItem* layoutItem = nullptr; + while ((layoutItem = layout()->takeAt(0)) != nullptr) { - // Hide the old widget and request deletion. - m_widget->hide(); - m_widget->deleteLater(); + layoutItem->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) + foreach (const QString& tag, tags) { - // 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/ProjectManager/tests/main.cpp b/Code/Tools/ProjectManager/tests/main.cpp index 3dd2fb75df..c666b7ffd0 100644 --- a/Code/Tools/ProjectManager/tests/main.cpp +++ b/Code/Tools/ProjectManager/tests/main.cpp @@ -18,6 +18,9 @@ int runDefaultRunner(int argc, char* argv[]) int main(int argc, char* argv[]) { + AZ::Debug::Trace::HandleExceptions(true); + AZ::Test::ApplyGlobalParameters(&argc, argv); + if (argc == 1) { // if no parameters are provided, add the --unittests parameter diff --git a/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp b/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp index 91ca77fa7b..c7d85428b4 100644 --- a/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp +++ b/Code/Tools/PythonBindingsExample/tests/ApplicationTests.cpp @@ -87,7 +87,7 @@ namespace PythonBindingsExample TEST_F(PythonBindingsExampleTest, Application_ImportAzLmbrPaths_Works) { ApplicationParameters params; - params.m_pythonStatement = "import azlmbr.paths; print (azlmbr.paths.engroot); print (azlmbr.paths.devroot)"; + params.m_pythonStatement = "import azlmbr.paths; print (azlmbr.paths.engroot)"; EXPECT_TRUE(s_application->RunWithParameters(params)); } diff --git a/Code/Tools/PythonBindingsExample/tests/TestMain.cpp b/Code/Tools/PythonBindingsExample/tests/TestMain.cpp index 7399055ca5..4c0dddecad 100644 --- a/Code/Tools/PythonBindingsExample/tests/TestMain.cpp +++ b/Code/Tools/PythonBindingsExample/tests/TestMain.cpp @@ -23,6 +23,9 @@ int runDefaultRunner(int argc, char* argv[]) int main(int argc, char* argv[]) { + AZ::Debug::Trace::HandleExceptions(true); + AZ::Test::ApplyGlobalParameters(&argc, argv); + // ran with no parameters? if (argc == 1) { diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp index ab260eba4d..1efec493c1 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp @@ -194,8 +194,8 @@ namespace AZ if (baseClass) { m_behaviorClass = behaviorClass; + return true; } - return true; } return false; } diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp index b6c7eef90f..31b74405d2 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp @@ -47,6 +47,16 @@ namespace AZ return m_sourceGuid; } + void Scene::SetWatchFolder(const AZStd::string& watchFolder) + { + m_watchFolder = watchFolder; + } + + const AZStd::string& Scene::GetWatchFolder() const + { + return m_watchFolder; + } + void Scene::SetManifestFilename(const AZStd::string& name) { m_manifestFilename = name; @@ -111,6 +121,7 @@ namespace AZ ->Property("sourceGuid", BehaviorValueGetter(&Scene::m_sourceGuid), nullptr) ->Property("graph", BehaviorValueGetter(&Scene::m_graph), nullptr) ->Property("manifest", BehaviorValueGetter(&Scene::m_manifest), nullptr) + ->Property("watchFolder", BehaviorValueGetter(&Scene::m_watchFolder), nullptr) ->Constant("SceneOrientation_YUp", BehaviorConstant(SceneOrientation::YUp)) ->Constant("SceneOrientation_ZUp", BehaviorConstant(SceneOrientation::ZUp)) ->Constant("SceneOrientation_XUp", BehaviorConstant(SceneOrientation::XUp)) diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h index 49320c4592..0185c23178 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h +++ b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h @@ -34,6 +34,9 @@ namespace AZ const AZStd::string& GetSourceFilename() const; const Uuid& GetSourceGuid() const; + void SetWatchFolder(const AZStd::string& watchFolder); + const AZStd::string& GetWatchFolder() const; + void SetManifestFilename(const AZStd::string& name); void SetManifestFilename(AZStd::string&& name); const AZStd::string& GetManifestFilename() const; @@ -59,6 +62,7 @@ namespace AZ AZStd::string m_name; AZStd::string m_manifestFilename; AZStd::string m_sourceFilename; + AZStd::string m_watchFolder; Uuid m_sourceGuid; SceneGraph m_graph; SceneManifest m_manifest; diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp index cae0dc45d5..3d00dccec3 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp @@ -97,7 +97,7 @@ namespace AZ GraphObjectProxy* proxy = aznew GraphObjectProxy(graphObject); return proxy; } - return nullptr; + return aznew GraphObjectProxy(nullptr); }); } } diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp index 664c5f1272..8e3f06b4cb 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp @@ -36,8 +36,7 @@ namespace AZ { namespace Containers { - //! Protects from allocating too much memory. The choice of a 5MB threshold is arbitrary. - const size_t MaxSceneManifestFileSizeInBytes = 5 * 1024 * 1024; + const char ErrorWindowName[] = "SceneManifest"; diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h index 4386d4fd8c..f3838096ad 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h @@ -41,6 +41,8 @@ namespace AZ AZ_RTTI(SceneManifest, "{9274AD17-3212-4651-9F3B-7DCCB080E467}"); + static constexpr size_t MaxSceneManifestFileSizeInBytes = AZStd::numeric_limits::max(); + virtual ~SceneManifest(); static AZStd::shared_ptr SceneManifestConstDataConverter( diff --git a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp index a182e4f02d..c98fa63916 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -73,6 +74,10 @@ namespace AZ { } + void AssetImportRequest::GetGeneratedManifestExtension(AZStd::string& /*result*/) + { + } + void AssetImportRequest::GetSupportedFileExtensions(AZStd::unordered_set& /*extensions*/) { } @@ -103,14 +108,34 @@ namespace AZ AZ_UNUSED(value); } + void AssetImportRequest::GetManifestDependencyPaths(AZStd::vector&) + { + } + AZStd::shared_ptr AssetImportRequest::LoadSceneFromVerifiedPath(const AZStd::string& assetFilePath, const Uuid& sourceGuid, - RequestingApplication requester, const Uuid& loadingComponentUuid) + RequestingApplication requester, const Uuid& loadingComponentUuid) { AZStd::string sceneName; AzFramework::StringFunc::Path::GetFileName(assetFilePath.c_str(), sceneName); AZStd::shared_ptr scene = AZStd::make_shared(AZStd::move(sceneName)); AZ_Assert(scene, "Unable to create new scene for asset importing."); + Data::AssetInfo assetInfo; + AZStd::string watchFolder; + bool result = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceGuid, assetInfo, watchFolder); + + if (result) + { + scene->SetWatchFolder(watchFolder); + } + else + { + AZ_Error( + "AssetImportRequest", false, "Failed to get watch folder for source %s", + sourceGuid.ToString().c_str()); + } + // Unique pointer, will deactivate and clean up once going out of scope. SceneCore::EntityConstructor::EntityPointer loaders = SceneCore::EntityConstructor::BuildEntity("Scene Loading", loadingComponentUuid); diff --git a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h index 8b6e119f99..a2446c5ff8 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h +++ b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h @@ -78,6 +78,8 @@ namespace AZ virtual void GetSupportedFileExtensions(AZStd::unordered_set& extensions); //! Gets the file extension for the manifest. virtual void GetManifestExtension(AZStd::string& result); + //! Gets the file extension for the generated manifest. + virtual void GetGeneratedManifestExtension(AZStd::string& result); //! Before asset loading starts this is called to allow for any required initialization. virtual ProcessingResult PrepareForAssetLoading(Containers::Scene& scene, RequestingApplication requester); @@ -97,6 +99,23 @@ namespace AZ // Get scene processing project setting: UseCustomNormal virtual void AreCustomNormalsUsed(bool & value); + /*! + Optional method for reporting source file dependencies that may exist in the scene manifest + Paths is a vector of JSON Path strings, relative to the IRule object + For example, the following path: /scriptFilename + Would match with this manifest: + + { + "values": [ + { + "$type": "Test", + "scriptFilename": "file.py" + } + ] + } + */ + virtual void GetManifestDependencyPaths(AZStd::vector& paths); + //! Utility function to load an asset and manifest from file by using the EBus functions above. //! @param assetFilePath The absolute path to the source file (not the manifest). //! @param sourceGuid The guid assigned to the source file (not the manifest). diff --git a/Code/Tools/SceneAPI/SceneCore/Export/MtlMaterialExporter.cpp b/Code/Tools/SceneAPI/SceneCore/Export/MtlMaterialExporter.cpp index aaa76e5977..aa88dee90f 100644 --- a/Code/Tools/SceneAPI/SceneCore/Export/MtlMaterialExporter.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Export/MtlMaterialExporter.cpp @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -170,11 +172,14 @@ namespace AZ { using AzToolsFramework::AssetSystemRequestBus; - const char* path = nullptr; - AssetSystemRequestBus::BroadcastResult(path, &AssetSystemRequestBus::Events::GetAbsoluteDevGameFolderPath); - if (path) + AZ::IO::Path projectPath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - return path; + settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); + } + if (!projectPath.empty()) + { + return projectPath.Native(); } else { diff --git a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp index 39045da5b9..f7d3f073b9 100644 --- a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp @@ -56,8 +56,14 @@ namespace AZ result = s_extension; } + void ManifestImportRequestHandler::GetGeneratedManifestExtension(AZStd::string& result) + { + result = s_extension; + result.append(s_generated); + } + Events::LoadingResult ManifestImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, - const Uuid& /*guid*/, RequestingApplication /*requester*/) + const Uuid& /*guid*/, RequestingApplication /*requester*/) { AZStd::string manifestPath = path + s_extension; diff --git a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h index 7659f6908f..00c802cb6b 100644 --- a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h +++ b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h @@ -31,6 +31,7 @@ namespace AZ static void Reflect(ReflectContext* context); void GetManifestExtension(AZStd::string& result) override; + void GetGeneratedManifestExtension(AZStd::string& result) override; Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, RequestingApplication requester) override; diff --git a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp index 2e6503d195..8d413b6038 100644 --- a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp +++ b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -99,6 +102,7 @@ namespace AZ::SceneAPI::Behaviors { AZStd::string result; CallResult(result, FN_OnUpdateManifest, scene); + ScriptBuildingNotificationBusHandler::BusDisconnect(); return result; } @@ -110,6 +114,7 @@ namespace AZ::SceneAPI::Behaviors { ExportProductList result; CallResult(result, FN_OnPrepareForExport, scene, outputDirectory, platformIdentifier, productList); + ScriptBuildingNotificationBusHandler::BusDisconnect(); return result; } @@ -168,21 +173,9 @@ namespace AZ::SceneAPI::Behaviors UnloadPython(); } - bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene) + bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene, AZStd::string& scriptPath) { - if (m_editorPythonEventsInterface && !m_scriptFilename.empty()) - { - return true; - } - - // get project folder - auto settingsRegistry = AZ::SettingsRegistry::Get(); - AZ::IO::FixedMaxPath projectPath; - if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath)) - { - return false; - } - + int scriptDiscoveryAttempts = 0; const AZ::SceneAPI::Containers::SceneManifest& manifest = scene.GetManifest(); auto view = Containers::MakeDerivedFilterView(manifest.GetValueStorage()); for (const auto& scriptItem : view) @@ -194,9 +187,21 @@ namespace AZ::SceneAPI::Behaviors continue; } + ++scriptDiscoveryAttempts; + // check for file exist via absolute path if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str())) { + // get project folder + auto settingsRegistry = AZ::SettingsRegistry::Get(); + AZ::IO::FixedMaxPath projectPath; + if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath)) + { + AZ_Error("scene", false, "With (%s) could not find Project Path during script discovery.", + scene.GetManifestFilename().c_str()); + return false; + } + // check for script in the project folder AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename; if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str())) @@ -209,32 +214,47 @@ namespace AZ::SceneAPI::Behaviors scriptFilename = AZStd::move(projectScriptPath); } - // lazy load the Python interface - auto editorPythonEventsInterface = AZ::Interface::Get(); - if (editorPythonEventsInterface->IsPythonActive() == false) - { - const bool silenceWarnings = false; - if (editorPythonEventsInterface->StartPython(silenceWarnings) == false) - { - editorPythonEventsInterface = nullptr; - } - } + scriptPath = scriptFilename.c_str(); + break; + } - // both Python and the script need to be ready - if (editorPythonEventsInterface == nullptr || scriptFilename.empty()) - { - AZ_Warning("scene", false,"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;" - "please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.", - scene.GetManifestFilename().c_str(), scriptFilename.c_str()); + if (scriptPath.empty()) + { + AZ_Warning("scene", scriptDiscoveryAttempts == 0, + "The scene manifest (%s) attempted to use script rule, but no script file path could be found.", + scene.GetManifestFilename().c_str()); + return false; + } + + // already prepared the Python VM? + if (m_editorPythonEventsInterface) + { + return true; + } - return false; + // lazy load the Python interface + auto editorPythonEventsInterface = AZ::Interface::Get(); + if (editorPythonEventsInterface->IsPythonActive() == false) + { + const bool silenceWarnings = false; + if (editorPythonEventsInterface->StartPython(silenceWarnings) == false) + { + editorPythonEventsInterface = nullptr; } + } - m_editorPythonEventsInterface = editorPythonEventsInterface; - m_scriptFilename = scriptFilename.c_str(); - return true; + // both Python and the script need to be ready + if (editorPythonEventsInterface == nullptr) + { + AZ_Warning("scene", false, + "The scene manifest (%s) attempted to prepare Python but Python can not start", + scene.GetManifestFilename().c_str()); + + return false; } - return false; + + m_editorPythonEventsInterface = editorPythonEventsInterface; + return true; } void ScriptProcessorRuleBehavior::UnloadPython() @@ -251,11 +271,13 @@ namespace AZ::SceneAPI::Behaviors { using namespace AzToolsFramework; - auto executeCallback = [this, &context]() + AZStd::string scriptPath; + + auto executeCallback = [&context, &scriptPath]() { // set up script's hook callback EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename, - m_scriptFilename.c_str()); + scriptPath.c_str()); // call script's callback to allow extra products ExportProductList extraProducts; @@ -279,7 +301,7 @@ namespace AZ::SceneAPI::Behaviors } }; - if (LoadPython(context.GetScene())) + if (LoadPython(context.GetScene(), scriptPath)) { EditorPythonConsoleNotificationHandler logger; m_editorPythonEventsInterface->ExecuteWithLock(executeCallback); @@ -306,23 +328,19 @@ namespace AZ::SceneAPI::Behaviors { using namespace AzToolsFramework; - // This behavior persists on the same AssetBuilder. Clear the script file name so that if - // this builder processes a scene file with a script file name, and then later processes - // a scene without a script file name, it won't run the old script on the new scene. - m_scriptFilename.clear(); - if (action != ManifestAction::Update) { return Events::ProcessingResult::Ignored; } - if (LoadPython(scene)) + AZStd::string scriptPath; + if (LoadPython(scene, scriptPath)) { AZStd::string manifestUpdate; - auto executeCallback = [this, &scene, &manifestUpdate]() + auto executeCallback = [&scene, &manifestUpdate, &scriptPath]() { EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename, - m_scriptFilename.c_str()); + scriptPath.c_str()); ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest, scene); @@ -331,6 +349,9 @@ namespace AZ::SceneAPI::Behaviors EditorPythonConsoleNotificationHandler logger; m_editorPythonEventsInterface->ExecuteWithLock(executeCallback); + EntityUtilityBus::Broadcast(&EntityUtilityBus::Events::ResetEntityContext); + AZ::Interface::Get()->RemoveAllTemplates(); + // attempt to load the manifest string back to a JSON-scene-manifest auto sceneManifestLoader = AZStd::make_unique(); auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate); @@ -347,4 +368,8 @@ namespace AZ::SceneAPI::Behaviors return Events::ProcessingResult::Ignored; } + void ScriptProcessorRuleBehavior::GetManifestDependencyPaths(AZStd::vector& paths) + { + paths.emplace_back("/scriptFilename"); + } } // namespace AZ diff --git a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h index fa70a12b0d..a9ddf88df9 100644 --- a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h +++ b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h @@ -37,6 +37,7 @@ namespace AZ::SceneAPI::Behaviors , public Events::AssetImportRequestBus::Handler { public: + AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent); ~ScriptProcessorRuleBehavior() override = default; @@ -44,22 +45,21 @@ namespace AZ::SceneAPI::Behaviors SCENE_DATA_API void Activate() override; SCENE_DATA_API void Deactivate() override; static void Reflect(ReflectContext* context); - + // AssetImportRequestBus::Handler SCENE_DATA_API Events::ProcessingResult UpdateManifest( Containers::Scene& scene, ManifestAction action, RequestingApplication requester) override; - + SCENE_DATA_API void GetManifestDependencyPaths(AZStd::vector& paths) override; protected: - bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene); + bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene, AZStd::string& scriptPath); void UnloadPython(); bool DoPrepareForExport(Events::PreExportEventContext& context); private: AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr; - AZStd::string m_scriptFilename; struct ExportEventHandler; AZStd::shared_ptr m_exportEventHandler; diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index c815af3b7a..6cfe072f80 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -38,7 +38,7 @@ // SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data, // and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs. -// +// // If the slice contains legacy data, it will print out warnings / errors about the data that couldn't be serialized. // The prefab will be generated without that data. @@ -56,7 +56,7 @@ namespace AZ AZ_Error("SerializeContextTools", false, "Command line not available."); return false; } - + JsonSerializerSettings convertSettings; convertSettings.m_keepDefaults = commandLine->HasSwitch("keepdefaults"); convertSettings.m_registrationContext = application.GetJsonRegistrationContext(); @@ -82,7 +82,7 @@ namespace AZ // Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus // so that the OnCatalogLoaded event gets processed now, instead of during application shutdown. AZ::Data::AssetCatalogRequestBus::Broadcast( - &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); + &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); application.Tick(); AZStd::string logggingScratchBuffer; @@ -870,7 +870,7 @@ namespace AZ // Wait for the disconnect to finish. bool disconnected = false; - AzFramework::AssetSystemRequestBus::BroadcastResult(disconnected, + AzFramework::AssetSystemRequestBus::BroadcastResult(disconnected, &AzFramework::AssetSystem::AssetSystemRequests::WaitUntilAssetProcessorDisconnected, AZStd::chrono::seconds(30)); AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully."); diff --git a/Code/Tools/Standalone/CMakeLists.txt b/Code/Tools/Standalone/CMakeLists.txt index 78047dd6cf..12bdb8f453 100644 --- a/Code/Tools/Standalone/CMakeLists.txt +++ b/Code/Tools/Standalone/CMakeLists.txt @@ -38,3 +38,5 @@ ly_add_target( PRIVATE STANDALONETOOLS_ENABLE_LUA_IDE ) + +ly_add_dependencies(Editor LuaIDE) 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 198f29ebf7..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(0); + serialize->Class()->Version(2); if (AZ::EditContext* ec = serialize->GetEditContext()) { @@ -55,26 +55,44 @@ namespace AWSClientAuth ->Enum<(int)ProviderNameEnum::AWSCognitoIDP>("ProviderNameEnum_AWSCognitoIDP") ->Enum<(int)ProviderNameEnum::LoginWithAmazon>("ProviderNameEnum_LoginWithAmazon") ->Enum<(int)ProviderNameEnum::Google>("ProviderNameEnum_Google"); - behaviorContext->EBus("AuthenticationProviderRequestBus") ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) ->Event("Initialize", &AuthenticationProviderScriptCanvasRequestBus::Events::Initialize) - ->Event("IsSignedIn", &AuthenticationProviderScriptCanvasRequestBus::Events::IsSignedIn) - ->Event("GetAuthenticationTokens", &AuthenticationProviderScriptCanvasRequestBus::Events::GetAuthenticationTokens) + ->Event("IsSignedIn", &AuthenticationProviderScriptCanvasRequestBus::Events::IsSignedIn, + { { { "Provider name", "The identity provider name" } } }) + ->Event("GetAuthenticationTokens", &AuthenticationProviderScriptCanvasRequestBus::Events::GetAuthenticationTokens, + { { { "Provider name", "The identity provider name" } } }) ->Event( "PasswordGrantSingleFactorSignInAsync", - &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantSingleFactorSignInAsync) + &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantSingleFactorSignInAsync, + { { { "Provider name", "The identity provider" }, { "Username", "The client's username" }, { "Password", "The client's password" } } }) ->Event( "PasswordGrantMultiFactorSignInAsync", - &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantMultiFactorSignInAsync) + &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantMultiFactorSignInAsync, + { { { "Provider name", "The identity provider name" }, + { "Username", "The client's username" }, + { "Password", "The client's password" } } }) ->Event( "PasswordGrantMultiFactorConfirmSignInAsync", - &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantMultiFactorConfirmSignInAsync) - ->Event("DeviceCodeGrantSignInAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::DeviceCodeGrantSignInAsync) - ->Event("DeviceCodeGrantConfirmSignInAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::DeviceCodeGrantConfirmSignInAsync) - ->Event("RefreshTokensAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::RefreshTokensAsync) - ->Event("GetTokensWithRefreshAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::GetTokensWithRefreshAsync) - ->Event("SignOut", &AuthenticationProviderScriptCanvasRequestBus::Events::SignOut); + &AuthenticationProviderScriptCanvasRequestBus::Events::PasswordGrantMultiFactorConfirmSignInAsync, + { { { "Provider name", "The identity provider name" }, + { "Username", "The client's username" }, + { "Confirmation code", "The client's confirmation code" } } }) + ->Event( + "DeviceCodeGrantSignInAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::DeviceCodeGrantSignInAsync, + { { { "Provider name", "The identity provider name" } } }) + ->Event( + "DeviceCodeGrantConfirmSignInAsync", + &AuthenticationProviderScriptCanvasRequestBus::Events::DeviceCodeGrantConfirmSignInAsync, + { { { "Provider name", "The identity provider name" } } }) + ->Event( + "RefreshTokensAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::RefreshTokensAsync, + { { { "Provider name", "The identity provider name" } } }) + ->Event("GetTokensWithRefreshAsync", &AuthenticationProviderScriptCanvasRequestBus::Events::GetTokensWithRefreshAsync, + { { { "Provider name", "The identity provider name" } } }) + ->Event( + "SignOut", &AuthenticationProviderScriptCanvasRequestBus::Events::SignOut, + { { { "Provider name", "The identity provider name" } } }); behaviorContext->EBus("AWSCognitoAuthorizationRequestBus") ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) @@ -87,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/CMakeLists.txt b/Gems/AWSCore/Code/CMakeLists.txt index 808436b7fd..7559f4720b 100644 --- a/Gems/AWSCore/Code/CMakeLists.txt +++ b/Gems/AWSCore/Code/CMakeLists.txt @@ -6,18 +6,18 @@ # # -ly_get_list_relative_pal_filename(pal_editor_include_dir ${CMAKE_CURRENT_LIST_DIR}/Include/Private/Editor/Platform/${PAL_PLATFORM_NAME}) -ly_get_list_relative_pal_filename(pal_cafile_include_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Framework/Platform/${PAL_PLATFORM_NAME}) +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}) ly_add_target( NAME AWSCore.Static STATIC NAMESPACE Gem FILES_CMAKE awscore_files.cmake - ${pal_cafile_include_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake INCLUDE_DIRECTORIES PUBLIC Include/Public + ${pal_dir} PRIVATE Include/Private BUILD_DEPENDENCIES @@ -65,11 +65,11 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) NAMESPACE Gem FILES_CMAKE awscore_editor_files.cmake - ${pal_editor_include_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_editor_files.cmake INCLUDE_DIRECTORIES PRIVATE Include/Private - ${pal_editor_include_dir} + ${pal_dir} PUBLIC Include/Public BUILD_DEPENDENCIES @@ -164,12 +164,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE Gem FILES_CMAKE awscore_editor_tests_files.cmake - ${pal_editor_include_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake - Tests/Editor/Platform/${PAL_PLATFORM_NAME}/awscore_editor_tests_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_editor_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_editor_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE Include/Private - ${pal_editor_include_dir} + ${pal_dir} Include/Public Tests COMPILE_DEFINITIONS 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 9d8b0a5e93..516b42c0dd 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/AWSApiJobConfig.h @@ -13,12 +13,15 @@ #include #include +#include + // The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly. // AWSAllocator.h(70): warning C4996: 'std::allocator::pointer': warning STL4010: Various members of std::allocator are deprecated in C++17. // Use std::allocator_traits instead of accessing these members directly. // You can define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option") +#include #include #include #include @@ -93,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. @@ -136,10 +136,14 @@ namespace AWSCore Override> writeRateLimiter; Override> readRateLimiter; Override httpLibOverride; +#if AWSCORE_BACKWARD_INCOMPATIBLE_CHANGE + Override followRedirects; +#else Override followRedirects; +#endif Override caFile; - /// Applys settings changes made after first use. + /// Applies settings changes made after first use. virtual void ApplySettings(); ////////////////////////////////////////////////////////////////////////// @@ -210,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/Include/Private/Editor/Platform/Android/AWSCoreEditor_Traits_Android.h b/Gems/AWSCore/Code/Platform/Android/AWSCoreEditor_Traits_Android.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Android/AWSCoreEditor_Traits_Android.h rename to Gems/AWSCore/Code/Platform/Android/AWSCoreEditor_Traits_Android.h diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Android/AWSCoreEditor_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Android/AWSCoreEditor_Traits_Platform.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Android/AWSCoreEditor_Traits_Platform.h rename to Gems/AWSCore/Code/Platform/Android/AWSCoreEditor_Traits_Platform.h diff --git a/Gems/AWSCore/Code/Platform/Android/AWSCore_Traits_Android.h b/Gems/AWSCore/Code/Platform/Android/AWSCore_Traits_Android.h new file mode 100644 index 0000000000..2cacfb0d34 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Android/AWSCore_Traits_Android.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 + +#define AWSCORE_BACKWARD_INCOMPATIBLE_CHANGE 1 diff --git a/Gems/AWSCore/Code/Platform/Android/AWSCore_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Android/AWSCore_Traits_Platform.h new file mode 100644 index 0000000000..6beeb3442d --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Android/AWSCore_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/Gems/AWSCore/Code/Source/Framework/Platform/Android/GetCertsPath_Android.cpp b/Gems/AWSCore/Code/Platform/Android/GetCertsPath_Android.cpp similarity index 97% rename from Gems/AWSCore/Code/Source/Framework/Platform/Android/GetCertsPath_Android.cpp rename to Gems/AWSCore/Code/Platform/Android/GetCertsPath_Android.cpp index 9dbacce2dd..557e29c3b6 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/Android/GetCertsPath_Android.cpp +++ b/Gems/AWSCore/Code/Platform/Android/GetCertsPath_Android.cpp @@ -1,33 +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 - * - */ - -#include -// The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly. -// AWSAllocator.h(70): warning C4996: 'std::allocator::pointer': warning STL4010: Various members of std::allocator are deprecated in -// C++17. Use std::allocator_traits instead of accessing these members directly. You can define -// _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received -// this warning. -AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING -#include -#include - -namespace AWSCore -{ - namespace Platform - { - Aws::String GetCaCertBundlePath() - { - AZStd::string publicStoragePath = AZ::Android::Utils::GetAppPublicStoragePath(); - publicStoragePath.append("/certificates/aws/cacert.pem"); - - return publicStoragePath.c_str(); - } - } // namespace Platform -} +/* + * 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 +// The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly. +// AWSAllocator.h(70): warning C4996: 'std::allocator::pointer': warning STL4010: Various members of std::allocator are deprecated in +// C++17. Use std::allocator_traits instead of accessing these members directly. You can define +// _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received +// this warning. +AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option") +#include +AZ_POP_DISABLE_WARNING +#include +#include + +namespace AWSCore +{ + namespace Platform + { + Aws::String GetCaCertBundlePath() + { + AZStd::string publicStoragePath = AZ::Android::Utils::GetAppPublicStoragePath(); + publicStoragePath.append("/certificates/aws/cacert.pem"); + + return publicStoragePath.c_str(); + } + } // namespace Platform +} diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Android/platform_android_files.cmake b/Gems/AWSCore/Code/Platform/Android/platform_android_editor_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Android/platform_android_files.cmake rename to Gems/AWSCore/Code/Platform/Android/platform_android_editor_files.cmake diff --git a/Gems/AWSCore/Code/Tests/Editor/Platform/Linux/awscore_editor_tests_linux_files.cmake b/Gems/AWSCore/Code/Platform/Android/platform_android_editor_tests_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Tests/Editor/Platform/Linux/awscore_editor_tests_linux_files.cmake rename to Gems/AWSCore/Code/Platform/Android/platform_android_editor_tests_files.cmake diff --git a/Gems/AWSCore/Code/Platform/Android/platform_android_files.cmake b/Gems/AWSCore/Code/Platform/Android/platform_android_files.cmake new file mode 100644 index 0000000000..e5b02beeac --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Android/platform_android_files.cmake @@ -0,0 +1,13 @@ +# +# 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 +# +# + +set(FILES + AWSCore_Traits_Platform.h + AWSCore_Traits_Android.h + GetCertsPath_Android.cpp +) diff --git a/Gems/AWSCore/Code/Source/Framework/Platform/Common/GetCertsPath_Null.cpp b/Gems/AWSCore/Code/Platform/Common/GetCertsPath_Null.cpp similarity index 97% rename from Gems/AWSCore/Code/Source/Framework/Platform/Common/GetCertsPath_Null.cpp rename to Gems/AWSCore/Code/Platform/Common/GetCertsPath_Null.cpp index 5731327fb2..45bb45000f 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/Common/GetCertsPath_Null.cpp +++ b/Gems/AWSCore/Code/Platform/Common/GetCertsPath_Null.cpp @@ -1,28 +1,28 @@ -/* - * 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 -// The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly. -// AWSAllocator.h(70): warning C4996: 'std::allocator::pointer': warning STL4010: Various members of std::allocator are deprecated in -// C++17. Use std::allocator_traits instead of accessing these members directly. You can define -// _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received -// this warning. -AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - -namespace AWSCore -{ - namespace Platform - { - Aws::String GetCaCertBundlePath() - { - return ""; // no-op - } - } // namespace Platform -} // namespace GridMate +/* + * 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 +// The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly. +// AWSAllocator.h(70): warning C4996: 'std::allocator::pointer': warning STL4010: Various members of std::allocator are deprecated in +// C++17. Use std::allocator_traits instead of accessing these members directly. You can define +// _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received +// this warning. +AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option") +#include +AZ_POP_DISABLE_WARNING + +namespace AWSCore +{ + namespace Platform + { + Aws::String GetCaCertBundlePath() + { + return ""; // no-op + } + } // namespace Platform +} // namespace GridMate diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Linux/AWSCoreEditor_Traits_Linux.h b/Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Linux.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Linux/AWSCoreEditor_Traits_Linux.h rename to Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Linux.h diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Linux/AWSCoreEditor_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Platform.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Linux/AWSCoreEditor_Traits_Platform.h rename to Gems/AWSCore/Code/Platform/Linux/AWSCoreEditor_Traits_Platform.h diff --git a/Gems/AWSCore/Code/Platform/Linux/AWSCore_Traits_Linux.h b/Gems/AWSCore/Code/Platform/Linux/AWSCore_Traits_Linux.h new file mode 100644 index 0000000000..d7b1f32461 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Linux/AWSCore_Traits_Linux.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 + +#define AWSCORE_BACKWARD_INCOMPATIBLE_CHANGE 0 diff --git a/Gems/AWSCore/Code/Platform/Linux/AWSCore_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Linux/AWSCore_Traits_Platform.h new file mode 100644 index 0000000000..d6ef9ceb4d --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Linux/AWSCore_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/Gems/AWSCore/Code/Include/Private/Editor/Platform/Linux/platform_linux_files.cmake b/Gems/AWSCore/Code/Platform/Linux/platform_linux_editor_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Linux/platform_linux_files.cmake rename to Gems/AWSCore/Code/Platform/Linux/platform_linux_editor_files.cmake diff --git a/Gems/AWSCore/Code/Tests/Editor/Platform/Mac/awscore_editor_tests_mac_files.cmake b/Gems/AWSCore/Code/Platform/Linux/platform_linux_editor_tests_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Tests/Editor/Platform/Mac/awscore_editor_tests_mac_files.cmake rename to Gems/AWSCore/Code/Platform/Linux/platform_linux_editor_tests_files.cmake diff --git a/Gems/AWSCore/Code/Source/Framework/Platform/Linux/platform_linux_files.cmake b/Gems/AWSCore/Code/Platform/Linux/platform_linux_files.cmake similarity index 82% rename from Gems/AWSCore/Code/Source/Framework/Platform/Linux/platform_linux_files.cmake rename to Gems/AWSCore/Code/Platform/Linux/platform_linux_files.cmake index 0abbd1adb8..1661434740 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/Linux/platform_linux_files.cmake +++ b/Gems/AWSCore/Code/Platform/Linux/platform_linux_files.cmake @@ -7,5 +7,7 @@ # set(FILES + AWSCore_Traits_Platform.h + AWSCore_Traits_Linux.h ../Common/GetCertsPath_Null.cpp ) diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Mac/AWSCoreEditor_Traits_Mac.h b/Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Mac.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Mac/AWSCoreEditor_Traits_Mac.h rename to Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Mac.h diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Mac/AWSCoreEditor_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Platform.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Mac/AWSCoreEditor_Traits_Platform.h rename to Gems/AWSCore/Code/Platform/Mac/AWSCoreEditor_Traits_Platform.h diff --git a/Gems/AWSCore/Code/Platform/Mac/AWSCore_Traits_Mac.h b/Gems/AWSCore/Code/Platform/Mac/AWSCore_Traits_Mac.h new file mode 100644 index 0000000000..d7b1f32461 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Mac/AWSCore_Traits_Mac.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 + +#define AWSCORE_BACKWARD_INCOMPATIBLE_CHANGE 0 diff --git a/Gems/AWSCore/Code/Platform/Mac/AWSCore_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Mac/AWSCore_Traits_Platform.h new file mode 100644 index 0000000000..2d33c834f9 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Mac/AWSCore_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/Gems/AWSCore/Code/Include/Private/Editor/Platform/Mac/platform_mac_files.cmake b/Gems/AWSCore/Code/Platform/Mac/platform_mac_editor_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Mac/platform_mac_files.cmake rename to Gems/AWSCore/Code/Platform/Mac/platform_mac_editor_files.cmake diff --git a/Gems/AWSCore/Code/Source/Framework/Platform/Android/platform_android_files.cmake b/Gems/AWSCore/Code/Platform/Mac/platform_mac_editor_tests_files.cmake similarity index 84% rename from Gems/AWSCore/Code/Source/Framework/Platform/Android/platform_android_files.cmake rename to Gems/AWSCore/Code/Platform/Mac/platform_mac_editor_tests_files.cmake index 278e4a1c9d..07f96644ab 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/Android/platform_android_files.cmake +++ b/Gems/AWSCore/Code/Platform/Mac/platform_mac_editor_tests_files.cmake @@ -6,6 +6,5 @@ # # -set(FILES - GetCertsPath_Android.cpp +set(FILES ) diff --git a/Gems/AWSCore/Code/Source/Framework/Platform/Mac/platform_mac_files.cmake b/Gems/AWSCore/Code/Platform/Mac/platform_mac_files.cmake similarity index 82% rename from Gems/AWSCore/Code/Source/Framework/Platform/Mac/platform_mac_files.cmake rename to Gems/AWSCore/Code/Platform/Mac/platform_mac_files.cmake index 0abbd1adb8..3e5e99cc05 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/Mac/platform_mac_files.cmake +++ b/Gems/AWSCore/Code/Platform/Mac/platform_mac_files.cmake @@ -7,5 +7,7 @@ # set(FILES + AWSCore_Traits_Platform.h + AWSCore_Traits_Mac.h ../Common/GetCertsPath_Null.cpp ) diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Windows/AWSCoreEditor_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Platform.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Windows/AWSCoreEditor_Traits_Platform.h rename to Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Platform.h diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Windows/AWSCoreEditor_Traits_Windows.h b/Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Windows.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Windows/AWSCoreEditor_Traits_Windows.h rename to Gems/AWSCore/Code/Platform/Windows/AWSCoreEditor_Traits_Windows.h diff --git a/Gems/AWSCore/Code/Platform/Windows/AWSCore_Traits_Platform.h b/Gems/AWSCore/Code/Platform/Windows/AWSCore_Traits_Platform.h new file mode 100644 index 0000000000..f6ccf10b88 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Windows/AWSCore_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/Gems/AWSCore/Code/Platform/Windows/AWSCore_Traits_Windows.h b/Gems/AWSCore/Code/Platform/Windows/AWSCore_Traits_Windows.h new file mode 100644 index 0000000000..d7b1f32461 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Windows/AWSCore_Traits_Windows.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 + +#define AWSCORE_BACKWARD_INCOMPATIBLE_CHANGE 0 diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/Windows/platform_windows_files.cmake b/Gems/AWSCore/Code/Platform/Windows/platform_windows_editor_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/Windows/platform_windows_files.cmake rename to Gems/AWSCore/Code/Platform/Windows/platform_windows_editor_files.cmake diff --git a/Gems/AWSCore/Code/Platform/Windows/platform_windows_editor_tests_files.cmake b/Gems/AWSCore/Code/Platform/Windows/platform_windows_editor_tests_files.cmake new file mode 100644 index 0000000000..3b87d2f3bb --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Windows/platform_windows_editor_tests_files.cmake @@ -0,0 +1,19 @@ +# +# 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 +# +# + +set(FILES + ../../Tests/Editor/AWSCoreEditorSystemComponentTest.cpp + ../../Tests/Editor/Attribution/AWSCoreAttributionManagerTest.cpp + ../../Tests/Editor/Attribution/AWSCoreAttributionMetricTest.cpp + ../../Tests/Editor/Attribution/AWSCoreAttributionSystemComponentTest.cpp + ../../Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp + ../../Tests/Editor/UI/AWSCoreEditorMenuTest.cpp + ../../Tests/Editor/UI/AWSCoreEditorUIFixture.h + ../../Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp + ../../Tests/Editor/AWSCoreEditorManagerTest.cpp +) diff --git a/Gems/AWSCore/Code/Platform/Windows/platform_windows_files.cmake b/Gems/AWSCore/Code/Platform/Windows/platform_windows_files.cmake new file mode 100644 index 0000000000..aa0119d042 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/Windows/platform_windows_files.cmake @@ -0,0 +1,13 @@ +# +# 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 +# +# + +set(FILES + AWSCore_Traits_Platform.h + AWSCore_Traits_Windows.h + ../Common/GetCertsPath_Null.cpp +) diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/iOS/AWSCoreEditor_Traits_Platform.h b/Gems/AWSCore/Code/Platform/iOS/AWSCoreEditor_Traits_Platform.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/iOS/AWSCoreEditor_Traits_Platform.h rename to Gems/AWSCore/Code/Platform/iOS/AWSCoreEditor_Traits_Platform.h diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/iOS/AWSCoreEditor_Traits_iOS.h b/Gems/AWSCore/Code/Platform/iOS/AWSCoreEditor_Traits_iOS.h similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/iOS/AWSCoreEditor_Traits_iOS.h rename to Gems/AWSCore/Code/Platform/iOS/AWSCoreEditor_Traits_iOS.h diff --git a/Gems/AWSCore/Code/Platform/iOS/AWSCore_Traits_Platform.h b/Gems/AWSCore/Code/Platform/iOS/AWSCore_Traits_Platform.h new file mode 100644 index 0000000000..7384900938 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/iOS/AWSCore_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/Gems/AWSCore/Code/Platform/iOS/AWSCore_Traits_iOS.h b/Gems/AWSCore/Code/Platform/iOS/AWSCore_Traits_iOS.h new file mode 100644 index 0000000000..d7b1f32461 --- /dev/null +++ b/Gems/AWSCore/Code/Platform/iOS/AWSCore_Traits_iOS.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 + +#define AWSCORE_BACKWARD_INCOMPATIBLE_CHANGE 0 diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Platform/iOS/platform_ios_files.cmake b/Gems/AWSCore/Code/Platform/iOS/platform_ios_editor_files.cmake similarity index 100% rename from Gems/AWSCore/Code/Include/Private/Editor/Platform/iOS/platform_ios_files.cmake rename to Gems/AWSCore/Code/Platform/iOS/platform_ios_editor_files.cmake diff --git a/Gems/AWSCore/Code/Source/Framework/Platform/Windows/platform_windows_files.cmake b/Gems/AWSCore/Code/Platform/iOS/platform_ios_editor_tests_files.cmake similarity index 82% rename from Gems/AWSCore/Code/Source/Framework/Platform/Windows/platform_windows_files.cmake rename to Gems/AWSCore/Code/Platform/iOS/platform_ios_editor_tests_files.cmake index 0abbd1adb8..07f96644ab 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/Windows/platform_windows_files.cmake +++ b/Gems/AWSCore/Code/Platform/iOS/platform_ios_editor_tests_files.cmake @@ -6,6 +6,5 @@ # # -set(FILES - ../Common/GetCertsPath_Null.cpp +set(FILES ) diff --git a/Gems/AWSCore/Code/Source/Framework/Platform/iOS/platform_ios_files.cmake b/Gems/AWSCore/Code/Platform/iOS/platform_ios_files.cmake similarity index 82% rename from Gems/AWSCore/Code/Source/Framework/Platform/iOS/platform_ios_files.cmake rename to Gems/AWSCore/Code/Platform/iOS/platform_ios_files.cmake index 0abbd1adb8..c73796afe3 100644 --- a/Gems/AWSCore/Code/Source/Framework/Platform/iOS/platform_ios_files.cmake +++ b/Gems/AWSCore/Code/Platform/iOS/platform_ios_files.cmake @@ -7,5 +7,7 @@ # set(FILES + AWSCore_Traits_Platform.h + AWSCore_Traits_iOS.h ../Common/GetCertsPath_Null.cpp ) 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/Configuration/AWSCoreConfiguration.cpp b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp index 4653975a52..d51539a471 100644 --- a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp +++ b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp @@ -64,7 +64,7 @@ namespace AWSCore void AWSCoreConfiguration::InitSourceProjectFolderPath() { - auto sourceProjectFolder = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); + auto sourceProjectFolder = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@"); if (!sourceProjectFolder) { AZ_Error(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage); 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 a2c89a5af3..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(); } @@ -78,7 +78,7 @@ namespace AWSCore void AWSCoreEditorMenu::InitializeResourceMappingToolAction() { -#ifdef AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED +#if AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED AWSCoreResourceMappingToolAction* resourceMappingTool = new AWSCoreResourceMappingToolAction(QObject::tr(AWSResourceMappingToolActionText), this); QObject::connect(resourceMappingTool, &QAction::triggered, this, @@ -197,7 +197,7 @@ namespace AWSCore subMenu->addAction(AddExternalLinkAction( AWSMetricsAdvancedTopicsActionText, AWSMetricsAdvancedTopicsUrl, ":/Notifications/link.svg")); - AZStd::string priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@"); + AZStd::string priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@engroot@"); AZStd::string configFilePath = priorAlias + "\\Gems\\AWSMetrics\\Code\\" + AZ::SettingsRegistryInterface::RegistryFolder; AzFramework::StringFunc::Path::Normalize(configFilePath); @@ -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/AWSApiJobConfig.cpp b/Gems/AWSCore/Code/Source/Framework/AWSApiJobConfig.cpp index cc51213e09..0f15c862d5 100644 --- a/Gems/AWSCore/Code/Source/Framework/AWSApiJobConfig.cpp +++ b/Gems/AWSCore/Code/Source/Framework/AWSApiJobConfig.cpp @@ -12,16 +12,6 @@ #include #include -// The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly. -// AWSAllocator.h(70): warning C4996: 'std::allocator::pointer': warning STL4010: Various members of std::allocator are deprecated in C++17. -// Use std::allocator_traits instead of accessing these members directly. -// You can define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning. - -AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - - namespace AWSCore { void AwsApiJobConfig::ApplySettings() 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/Configuration/AWSCoreConfigurationTest.cpp b/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp index 6bde2c2fbf..e54a55f728 100644 --- a/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp +++ b/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp @@ -60,7 +60,7 @@ public: m_normalizedSourceProjectFolder.c_str(), AZ::SettingsRegistryInterface::RegistryFolder); AzFramework::StringFunc::Path::Normalize(m_normalizedSetRegFolderPath); - m_localFileIO->SetAlias("@devassets@", m_normalizedSourceProjectFolder.c_str()); + m_localFileIO->SetAlias("@projectroot@", m_normalizedSourceProjectFolder.c_str()); CreateTestSetRegFile(TEST_VALID_RESOURCE_MAPPING_SETREG); } @@ -122,7 +122,7 @@ private: TEST_F(AWSCoreConfigurationTest, InitConfig_NoSourceProjectFolderFound_ReturnEmptyConfigFilePath) { m_settingsRegistry->MergeSettingsFile(m_normalizedSetRegFilePath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}); - m_localFileIO->ClearAlias("@devassets@"); + m_localFileIO->ClearAlias("@projectroot@"); AZ_TEST_START_TRACE_SUPPRESSION; m_awsCoreConfiguration->InitConfig(); @@ -154,7 +154,7 @@ TEST_F(AWSCoreConfigurationTest, InitConfig_LoadValidSettingsRegistry_ReturnNonE TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_NoSourceProjectFolderFound_ReturnEmptyConfigFilePath) { m_settingsRegistry->MergeSettingsFile(m_normalizedSetRegFilePath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}); - m_localFileIO->ClearAlias("@devassets@"); + m_localFileIO->ClearAlias("@projectroot@"); m_awsCoreConfiguration->ReloadConfiguration(); auto actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath(); 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/Editor/Platform/Windows/awscore_editor_tests_windows_files.cmake b/Gems/AWSCore/Code/Tests/Editor/Platform/Windows/awscore_editor_tests_windows_files.cmake deleted file mode 100644 index 0d47bbe795..0000000000 --- a/Gems/AWSCore/Code/Tests/Editor/Platform/Windows/awscore_editor_tests_windows_files.cmake +++ /dev/null @@ -1,19 +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 -# -# - -set(FILES - ../../AWSCoreEditorSystemComponentTest.cpp - ../../Attribution/AWSCoreAttributionManagerTest.cpp - ../../Attribution/AWSCoreAttributionMetricTest.cpp - ../../Attribution/AWSCoreAttributionSystemComponentTest.cpp - ../../Attribution/AWSAttributionServiceApiTest.cpp - ../../UI/AWSCoreEditorMenuTest.cpp - ../../UI/AWSCoreEditorUIFixture.h - ../../UI/AWSCoreResourceMappingToolActionTest.cpp - ../../AWSCoreEditorManagerTest.cpp -) diff --git a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp index 40ed5b53b5..e9453a4a0a 100644 --- a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp @@ -32,7 +32,7 @@ class AWSCoreEditorMenuTest { AWSCoreEditorUIFixture::SetUp(); AWSCoreFixture::SetUp(); - m_localFileIO->SetAlias("@devroot@", "dummy engine root"); + m_localFileIO->SetAlias("@engroot@", "dummy engine root"); } void TearDown() 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/AWSGameLiftAcceptMatchRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftAcceptMatchRequest.h new file mode 100644 index 0000000000..415550b6bc --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftAcceptMatchRequest.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 + +namespace AWSGameLift +{ + //! AWSGameLiftAcceptMatchRequest + //! GameLift accept match request which corresponds to Amazon GameLift + //! Registers a player's acceptance or rejection of a proposed FlexMatch match. + //! AcceptMatchRequest + struct AWSGameLiftAcceptMatchRequest + : public AzFramework::AcceptMatchRequest + { + public: + AZ_RTTI(AWSGameLiftAcceptMatchRequest, "{8372B297-88E8-4C13-B31D-BE87236CA416}", AzFramework::AcceptMatchRequest); + static void Reflect(AZ::ReflectContext* context); + + AWSGameLiftAcceptMatchRequest() = default; + virtual ~AWSGameLiftAcceptMatchRequest() = default; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h index be2c766d7e..a9b06c5bc9 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h @@ -8,7 +8,7 @@ #pragma once -#include +#include namespace AWSGameLift { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionRequest.h index 75440868c9..4fab16ca70 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionRequest.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftCreateSessionRequest.h @@ -8,7 +8,7 @@ #pragma once -#include +#include namespace AWSGameLift { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftJoinSessionRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftJoinSessionRequest.h index 32f549c7d5..07d6da3870 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftJoinSessionRequest.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftJoinSessionRequest.h @@ -8,7 +8,7 @@ #pragma once -#include +#include namespace AWSGameLift { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSearchSessionsRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSearchSessionsRequest.h index 655c83252e..bfc2dc630e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSearchSessionsRequest.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftSearchSessionsRequest.h @@ -8,7 +8,7 @@ #pragma once -#include +#include namespace AWSGameLift { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h new file mode 100644 index 0000000000..ec3719c6d3 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.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 + +#include + +namespace AWSGameLift +{ + //! 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 + //! StartMatchmakingRequest + struct AWSGameLiftStartMatchmakingRequest + : public AzFramework::StartMatchmakingRequest + { + public: + AZ_RTTI(AWSGameLiftStartMatchmakingRequest, "{D273DF71-9C55-48C1-95F9-8D7B66B9CF3E}", AzFramework::StartMatchmakingRequest); + static void Reflect(AZ::ReflectContext* context); + + AWSGameLiftStartMatchmakingRequest() = default; + virtual ~AWSGameLiftStartMatchmakingRequest() = default; + + // 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; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStopMatchmakingRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStopMatchmakingRequest.h new file mode 100644 index 0000000000..81d811d32d --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStopMatchmakingRequest.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 + +namespace AWSGameLift +{ + //! AWSGameLiftStopMatchmakingRequest + //! GameLift stop matchmaking request which corresponds to Amazon GameLift + //! Cancels a matchmaking ticket or match backfill ticket that is currently being processed. + //! StopMatchmakingRequest + struct AWSGameLiftStopMatchmakingRequest + : public AzFramework::StopMatchmakingRequest + { + public: + AZ_RTTI(AWSGameLiftStopMatchmakingRequest, "{2766BC03-9F84-4346-A52B-49129BBAF38B}", AzFramework::StopMatchmakingRequest); + static void Reflect(AZ::ReflectContext* context); + + AWSGameLiftStopMatchmakingRequest() = default; + virtual ~AWSGameLiftStopMatchmakingRequest() = default; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h index 25445553f4..10269dc8c3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace AWSGameLift @@ -71,4 +72,26 @@ namespace AWSGameLift static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; }; using AWSGameLiftSessionRequestBus = AZ::EBus; + + // IMatchmakingAsyncRequests EBus wrapper for scripting + class AWSGameLiftMatchmakingAsyncRequests + : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using AWSGameLiftMatchmakingAsyncRequestBus = AZ::EBus; + + // IMatchmakingRequests EBus wrapper for scripting + class AWSGameLiftMatchmakingRequests + : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using AWSGameLiftMatchmakingRequestBus = AZ::EBus; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp new file mode 100644 index 0000000000..af0cf1c35c --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp @@ -0,0 +1,181 @@ +/* + * 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 + +namespace AWSGameLift +{ + AWSGameLiftClientLocalTicketTracker::AWSGameLiftClientLocalTicketTracker() + : m_status(TicketTrackerStatus::Idle) + , m_pollingPeriodInMS(AWSGameLiftClientDefaultPollingPeriodInMS) + { + } + + void AWSGameLiftClientLocalTicketTracker::ActivateTracker() + { + AZ::Interface::Register(this); + } + + void AWSGameLiftClientLocalTicketTracker::DeactivateTracker() + { + AZ::Interface::Unregister(this); + StopPolling(); + } + + void AWSGameLiftClientLocalTicketTracker::StartPolling( + const AZStd::string& ticketId, const AZStd::string& playerId) + { + AZStd::lock_guard lock(m_trackerMutex); + if (m_status == TicketTrackerStatus::Running) + { + 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)); + } + + void AWSGameLiftClientLocalTicketTracker::StopPolling() + { + AZStd::lock_guard lock(m_trackerMutex); + m_status = TicketTrackerStatus::Idle; + m_waitEvent.release(); + if (m_trackerThread.joinable()) + { + m_trackerThread.join(); + } + } + + void AWSGameLiftClientLocalTicketTracker::ProcessPolling( + const AZStd::string& ticketId, const AZStd::string& playerId) + { + while (m_status == TicketTrackerStatus::Running) + { + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); + if (gameliftClient) + { + Aws::GameLift::Model::DescribeMatchmakingRequest request; + request.AddTicketIds(ticketId.c_str()); + + auto describeMatchmakingOutcome = gameliftClient->DescribeMatchmaking(request); + if (describeMatchmakingOutcome.IsSuccess()) + { + if (describeMatchmakingOutcome.GetResult().GetTicketList().size() == 1) + { + auto ticket = describeMatchmakingOutcome.GetResult().GetTicketList().front(); + if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED) + { + 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) + { + 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 + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket %s is processing, %s.", + ticket.GetTicketId().c_str(), ticket.GetStatusReason().c_str()); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, "Unable to find expected ticket with id %s", ticketId.c_str()); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftErrorMessageTemplate, + describeMatchmakingOutcome.GetError().GetExceptionName().c_str(), + describeMatchmakingOutcome.GetError().GetMessage().c_str()); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftClientMissingErrorMessage); + } + m_waitEvent.try_acquire_for(AZStd::chrono::milliseconds(m_pollingPeriodInMS)); + } + } + + void AWSGameLiftClientLocalTicketTracker::RequestPlayerJoinMatch( + const Aws::GameLift::Model::MatchmakingTicket& ticket, const AZStd::string& playerId) + { + auto connectionInfo = ticket.GetGameSessionConnectionInfo(); + AzFramework::SessionConnectionConfig sessionConnectionConfig; + sessionConnectionConfig.m_ipAddress = connectionInfo.GetIpAddress().c_str(); + for (auto matchedPlayer : connectionInfo.GetMatchedPlayerSessions()) + { + if (playerId.compare(matchedPlayer.GetPlayerId().c_str()) == 0) + { + sessionConnectionConfig.m_playerSessionId = matchedPlayer.GetPlayerSessionId().c_str(); + break; + } + } + sessionConnectionConfig.m_port = static_cast(connectionInfo.GetPort()); + + if (!sessionConnectionConfig.m_playerSessionId.empty()) + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, + "Requesting and validating player session %s to connect to the match ...", + sessionConnectionConfig.m_playerSessionId.c_str()); + bool result = + AZ::Interface::Get()->RequestPlayerJoinSession(sessionConnectionConfig); + if (result) + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, + "Started connection process, and connection validation is in process."); + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, + "Failed to start connection process."); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, + "Player session id is missing for player % to join the match.", playerId.c_str()); + } + } +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h new file mode 100644 index 0000000000..23083e9fd9 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h @@ -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. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace AWSGameLift +{ + enum TicketTrackerStatus + { + Idle, + Running + }; + + //! AWSGameLiftClientLocalTicketTracker + //! GameLift client ticket tracker to describe submitted matchmaking ticket periodically, + //! and join player to the match once matchmaking ticket is complete. + //! For use in production, please see GameLifts guidance about matchmaking at volume. + //! The continuous polling approach here is only suitable for low volume matchmaking and is meant to aid with development only + class AWSGameLiftClientLocalTicketTracker + : public IAWSGameLiftMatchmakingInternalRequests + { + public: + static constexpr const char AWSGameLiftClientLocalTicketTrackerName[] = "AWSGameLiftClientLocalTicketTracker"; + // Set ticket polling period to 10 seconds + // https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-client.html#match-client-track + static constexpr const uint64_t AWSGameLiftClientDefaultPollingPeriodInMS = 10000; + + AWSGameLiftClientLocalTicketTracker(); + virtual ~AWSGameLiftClientLocalTicketTracker() = default; + + virtual void ActivateTracker(); + virtual void DeactivateTracker(); + + // IAWSGameLiftMatchmakingInternalRequests interface implementation + void StartPolling(const AZStd::string& ticketId, const AZStd::string& playerId) override; + void StopPolling() override; + + protected: + // For testing friendly access + uint64_t m_pollingPeriodInMS; + TicketTrackerStatus m_status; + + private: + void ProcessPolling(const AZStd::string& ticketId, const AZStd::string& playerId); + void RequestPlayerJoinMatch(const Aws::GameLift::Model::MatchmakingTicket& ticket, const AZStd::string& playerId); + + 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 f9249d1ad2..224f16481e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp @@ -17,11 +17,16 @@ #include #include +#include +#include #include #include #include #include #include +#include +#include +#include #include @@ -31,11 +36,6 @@ namespace AWSGameLift AZ_CVAR(AZ::CVarFixedString, cl_gameliftLocalEndpoint, "", nullptr, AZ::ConsoleFunctorFlags::Null, "The local endpoint to test with GameLiftLocal SDK."); #endif - AWSGameLiftClientManager::AWSGameLiftClientManager() - { - m_gameliftClient.reset(); - } - void AWSGameLiftClientManager::ActivateManager() { AZ::Interface::Register(this); @@ -46,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); @@ -62,8 +74,7 @@ namespace AWSGameLift bool AWSGameLiftClientManager::ConfigureGameLiftClient(const AZStd::string& region) { - m_gameliftClient.reset(); - + AZ::Interface::Get()->SetGameLiftClient(nullptr); Aws::Client::ClientConfiguration clientConfig; // Set up client endpoint or region AZStd::string localEndpoint = ""; @@ -103,7 +114,8 @@ namespace AWSGameLift AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientCredentialMissingErrorMessage); return false; } - m_gameliftClient = AZStd::make_shared(credentialResult.result, clientConfig); + AZ::Interface::Get()->SetGameLiftClient( + AZStd::make_shared(credentialResult.result, clientConfig)); return true; } @@ -112,6 +124,57 @@ namespace AWSGameLift return AZ::Uuid::CreateRandom().ToString(includeBrackets, includeDashes); } + void AWSGameLiftClientManager::AcceptMatch(const AzFramework::AcceptMatchRequest& acceptMatchRequest) + { + if (AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest)) + { + const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest = + static_cast(acceptMatchRequest); + AcceptMatchHelper(gameliftStartMatchmakingRequest); + } + } + + void AWSGameLiftClientManager::AcceptMatchAsync(const AzFramework::AcceptMatchRequest& 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) { AZStd::string result = ""; @@ -184,15 +247,15 @@ namespace AWSGameLift AZStd::string AWSGameLiftClientManager::CreateSessionHelper( const AWSGameLiftCreateSessionRequest& createSessionRequest) { - AZStd::shared_ptr gameLiftClient = m_gameliftClient; + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); AZStd::string result = ""; - if (!gameLiftClient) + if (!gameliftClient) { AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage); } else { - result = CreateSessionActivity::CreateSession(*gameLiftClient, createSessionRequest); + result = CreateSessionActivity::CreateSession(*gameliftClient, createSessionRequest); } return result; } @@ -200,7 +263,7 @@ namespace AWSGameLift AZStd::string AWSGameLiftClientManager::CreateSessionOnQueueHelper( const AWSGameLiftCreateSessionOnQueueRequest& createSessionOnQueueRequest) { - AZStd::shared_ptr gameliftClient = m_gameliftClient; + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); AZStd::string result; if (!gameliftClient) { @@ -255,7 +318,7 @@ namespace AWSGameLift bool AWSGameLiftClientManager::JoinSessionHelper(const AWSGameLiftJoinSessionRequest& joinSessionRequest) { - AZStd::shared_ptr gameliftClient = m_gameliftClient; + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); bool result = false; if (!gameliftClient) { @@ -335,8 +398,7 @@ namespace AWSGameLift AzFramework::SearchSessionsResponse AWSGameLiftClientManager::SearchSessionsHelper( const AWSGameLiftSearchSessionsRequest& searchSessionsRequest) const { - AZStd::shared_ptr gameliftClient = m_gameliftClient; - + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); AzFramework::SearchSessionsResponse response; if (!gameliftClient) { @@ -349,8 +411,110 @@ namespace AWSGameLift return response; } - void AWSGameLiftClientManager::SetGameLiftClient(AZStd::shared_ptr gameliftClient) + AZStd::string AWSGameLiftClientManager::StartMatchmaking(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest) { - m_gameliftClient.swap(gameliftClient); + AZStd::string response; + if (StartMatchmakingActivity::ValidateStartMatchmakingRequest(startMatchmakingRequest)) + { + const AWSGameLiftStartMatchmakingRequest& gameliftStartMatchmakingRequest = + static_cast(startMatchmakingRequest); + response = StartMatchmakingHelper(gameliftStartMatchmakingRequest); + } + + return response; + } + + void AWSGameLiftClientManager::StartMatchmakingAsync(const AzFramework::StartMatchmakingRequest& 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) + { + if (StopMatchmakingActivity::ValidateStopMatchmakingRequest(stopMatchmakingRequest)) + { + const AWSGameLiftStopMatchmakingRequest& gameliftStopMatchmakingRequest = + static_cast(stopMatchmakingRequest); + StopMatchmakingHelper(gameliftStopMatchmakingRequest); + } + } + + void AWSGameLiftClientManager::StopMatchmakingAsync(const AzFramework::StopMatchmakingRequest& 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 1b9296d20a..1f32b69f75 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h @@ -9,22 +9,67 @@ #pragma once #include -#include +#include -namespace Aws -{ - namespace GameLift - { - class GameLiftClient; - } -} +#include namespace AWSGameLift { + struct AWSGameLiftAcceptMatchRequest; struct AWSGameLiftCreateSessionRequest; struct AWSGameLiftCreateSessionOnQueueRequest; struct AWSGameLiftJoinSessionRequest; struct AWSGameLiftSearchSessionsRequest; + struct AWSGameLiftStartMatchmakingRequest; + struct AWSGameLiftStopMatchmakingRequest; + + // MatchAcceptanceNotificationBus EBus handler for scripting + class AWSGameLiftMatchAcceptanceNotificationBusHandler + : public AzFramework::MatchAcceptanceNotificationBus::Handler + , public AZ::BehaviorEBusHandler + { + public: + AZ_EBUS_BEHAVIOR_BINDER( + AWSGameLiftMatchAcceptanceNotificationBusHandler, + "{CBE057D3-F5CE-46D3-B02D-8A6A1446B169}", + AZ::SystemAllocator, + OnMatchAcceptance); + + void OnMatchAcceptance() override + { + Call(FN_OnMatchAcceptance); + } + }; + + // MatchmakingAsyncRequestNotificationBus EBus handler for scripting + class AWSGameLiftMatchmakingAsyncRequestNotificationBusHandler + : public AzFramework::MatchmakingAsyncRequestNotificationBus::Handler + , public AZ::BehaviorEBusHandler + { + public: + AZ_EBUS_BEHAVIOR_BINDER( + AWSGameLiftMatchmakingAsyncRequestNotificationBusHandler, + "{2045EE8F-2AB7-4ED0-9614-3496A1A43677}", + AZ::SystemAllocator, + OnAcceptMatchAsyncComplete, + OnStartMatchmakingAsyncComplete, + OnStopMatchmakingAsyncComplete); + + void OnAcceptMatchAsyncComplete() override + { + Call(FN_OnAcceptMatchAsyncComplete); + } + + void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) override + { + Call(FN_OnStartMatchmakingAsyncComplete, matchmakingTicketId); + } + + void OnStopMatchmakingAsyncComplete() override + { + Call(FN_OnStopMatchmakingAsyncComplete); + } + }; // SessionAsyncRequestNotificationBus EBus handler for scripting class AWSGameLiftSessionAsyncRequestNotificationBusHandler @@ -66,6 +111,8 @@ namespace AWSGameLift //! GameLift client manager to support game and player session related client requests class AWSGameLiftClientManager : public AWSGameLiftRequestBus::Handler + , public AWSGameLiftMatchmakingAsyncRequestBus::Handler + , public AWSGameLiftMatchmakingRequestBus::Handler , public AWSGameLiftSessionAsyncRequestBus::Handler , public AWSGameLiftSessionRequestBus::Handler { @@ -75,13 +122,11 @@ namespace AWSGameLift "Missing AWS region for GameLift client."; static constexpr const char AWSGameLiftClientCredentialMissingErrorMessage[] = "Missing AWS credential for GameLift client."; - static constexpr const char AWSGameLiftClientMissingErrorMessage[] = - "GameLift client is not configured yet."; static constexpr const char AWSGameLiftCreateSessionRequestInvalidErrorMessage[] = "Invalid GameLift CreateSession or CreateSessionOnQueue request."; - AWSGameLiftClientManager(); + AWSGameLiftClientManager() = default; virtual ~AWSGameLiftClientManager() = default; virtual void ActivateManager(); @@ -91,28 +136,35 @@ namespace AWSGameLift bool ConfigureGameLiftClient(const AZStd::string& region) override; AZStd::string CreatePlayerId(bool includeBrackets, bool includeDashes) override; + // AWSGameLiftMatchmakingAsyncRequestBus interface implementation + void AcceptMatchAsync(const AzFramework::AcceptMatchRequest& acceptMatchRequest) override; + void StartMatchmakingAsync(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest) override; + void StopMatchmakingAsync(const AzFramework::StopMatchmakingRequest& stopMatchmakingRequest) override; + // AWSGameLiftSessionAsyncRequestBus interface implementation void CreateSessionAsync(const AzFramework::CreateSessionRequest& createSessionRequest) override; void JoinSessionAsync(const AzFramework::JoinSessionRequest& joinSessionRequest) override; void SearchSessionsAsync(const AzFramework::SearchSessionsRequest& searchSessionsRequest) const override; void LeaveSessionAsync() override; + // AWSGameLiftMatchmakingRequestBus interface implementation + void AcceptMatch(const AzFramework::AcceptMatchRequest& acceptMatchRequest) override; + AZStd::string StartMatchmaking(const AzFramework::StartMatchmakingRequest& startMatchmakingRequest) override; + void StopMatchmaking(const AzFramework::StopMatchmakingRequest& stopMatchmakingRequest) override; + // AWSGameLiftSessionRequestBus interface implementation AZStd::string CreateSession(const AzFramework::CreateSessionRequest& createSessionRequest) override; bool JoinSession(const AzFramework::JoinSessionRequest& joinSessionRequest) override; AzFramework::SearchSessionsResponse SearchSessions(const AzFramework::SearchSessionsRequest& searchSessionsRequest) const override; void LeaveSession() override; - protected: - // Use for automation tests only to inject mock objects. - void SetGameLiftClient(AZStd::shared_ptr gameliftClient); - 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::shared_ptr m_gameliftClient; + 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 ceb7376b85..ed7830ce57 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -13,10 +14,13 @@ #include #include +#include #include #include #include #include +#include +#include #include @@ -24,23 +28,19 @@ namespace AWSGameLift { AWSGameLiftClientSystemComponent::AWSGameLiftClientSystemComponent() { - m_gameliftClientManager = AZStd::make_unique(); + m_gameliftManager = AZStd::make_unique(); + m_gameliftTicketTracker = AZStd::make_unique(); } void AWSGameLiftClientSystemComponent::Reflect(AZ::ReflectContext* context) { - ReflectCreateSessionRequest(context); - AWSGameLiftCreateSessionOnQueueRequest::Reflect(context); - AWSGameLiftCreateSessionRequest::Reflect(context); - AWSGameLiftJoinSessionRequest::Reflect(context); - AWSGameLiftSearchSessionsRequest::Reflect(context); - ReflectSearchSessionsResponse(context); + ReflectGameLiftMatchmaking(context); + ReflectGameLiftSession(context); if (AZ::SerializeContext* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(0) - ; + ->Version(1); if (AZ::EditContext* editContext = serialize->GetEditContext()) { @@ -50,8 +50,7 @@ namespace AWSGameLift "Create the GameLift client manager that handles communication between game clients and the GameLift service.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); } } @@ -63,33 +62,7 @@ namespace AWSGameLift {{{"Region", ""}}}) ->Event("CreatePlayerId", &AWSGameLiftRequestBus::Events::CreatePlayerId, {{{"IncludeBrackets", ""}, - {"IncludeDashes", ""}}}) - ; - behaviorContext->EBus("AWSGameLiftSessionAsyncRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") - ->Event("CreateSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::CreateSessionAsync, - {{{"CreateSessionRequest", ""}}}) - ->Event("JoinSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::JoinSessionAsync, - {{{"JoinSessionRequest", ""}}}) - ->Event("SearchSessionsAsync", &AWSGameLiftSessionAsyncRequestBus::Events::SearchSessionsAsync, - {{{"SearchSessionsRequest", ""}}}) - ->Event("LeaveSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::LeaveSessionAsync) - ; - behaviorContext - ->EBus("AWSGameLiftSessionAsyncRequestNotificationBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") - ->Handler() - ; - behaviorContext->EBus("AWSGameLiftSessionRequestBus") - ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") - ->Event("CreateSession", &AWSGameLiftSessionRequestBus::Events::CreateSession, - {{{"CreateSessionRequest", ""}}}) - ->Event("JoinSession", &AWSGameLiftSessionRequestBus::Events::JoinSession, - {{{"JoinSessionRequest", ""}}}) - ->Event("SearchSessions", &AWSGameLiftSessionRequestBus::Events::SearchSessions, - {{{"SearchSessionsRequest", ""}}}) - ->Event("LeaveSession", &AWSGameLiftSessionRequestBus::Events::LeaveSession) - ; + {"IncludeDashes", ""}}}); } } @@ -119,12 +92,88 @@ namespace AWSGameLift void AWSGameLiftClientSystemComponent::Activate() { - m_gameliftClientManager->ActivateManager(); + AZ::Interface::Register(this); + + m_gameliftClient.reset(); + m_gameliftManager->ActivateManager(); + m_gameliftTicketTracker->ActivateTracker(); } void AWSGameLiftClientSystemComponent::Deactivate() { - m_gameliftClientManager->DeactivateManager(); + m_gameliftTicketTracker->DeactivateTracker(); + m_gameliftManager->DeactivateManager(); + m_gameliftClient.reset(); + + AZ::Interface::Unregister(this); + } + + void AWSGameLiftClientSystemComponent::ReflectGameLiftMatchmaking(AZ::ReflectContext* context) + { + AWSGameLiftAcceptMatchRequest::Reflect(context); + AWSGameLiftStartMatchmakingRequest::Reflect(context); + AWSGameLiftStopMatchmakingRequest::Reflect(context); + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("AWSGameLiftMatchmakingAsyncRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Event("AcceptMatchAsync", &AWSGameLiftMatchmakingAsyncRequestBus::Events::AcceptMatchAsync, + { { { "AcceptMatchRequest", "" } } }) + ->Event("StartMatchmakingAsync", &AWSGameLiftMatchmakingAsyncRequestBus::Events::StartMatchmakingAsync, + { { { "StartMatchmakingRequest", "" } } }) + ->Event("StopMatchmakingAsync", &AWSGameLiftMatchmakingAsyncRequestBus::Events::StopMatchmakingAsync, + { { { "StopMatchmakingRequest", "" } } }); + + behaviorContext->EBus("AWSGameLiftMatchmakingAsyncRequestNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Handler(); + + behaviorContext->EBus("AWSGameLiftMatchmakingRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Event("AcceptMatch", &AWSGameLiftMatchmakingRequestBus::Events::AcceptMatch, { { { "AcceptMatchRequest", "" } } }) + ->Event("StartMatchmaking", &AWSGameLiftMatchmakingRequestBus::Events::StartMatchmaking, + { { { "StartMatchmakingRequest", "" } } }) + ->Event("StopMatchmaking", &AWSGameLiftMatchmakingRequestBus::Events::StopMatchmaking, + { { { "StopMatchmakingRequest", "" } } }); + + behaviorContext->EBus("AWSGameLiftMatchAcceptanceNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Handler(); + } + } + + void AWSGameLiftClientSystemComponent::ReflectGameLiftSession(AZ::ReflectContext* context) + { + ReflectCreateSessionRequest(context); + AWSGameLiftCreateSessionOnQueueRequest::Reflect(context); + AWSGameLiftCreateSessionRequest::Reflect(context); + AWSGameLiftJoinSessionRequest::Reflect(context); + AWSGameLiftSearchSessionsRequest::Reflect(context); + ReflectSearchSessionsResponse(context); + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("AWSGameLiftSessionAsyncRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Event("CreateSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::CreateSessionAsync, + { { { "CreateSessionRequest", "" } } }) + ->Event("JoinSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::JoinSessionAsync, { { { "JoinSessionRequest", "" } } }) + ->Event("SearchSessionsAsync", &AWSGameLiftSessionAsyncRequestBus::Events::SearchSessionsAsync, + { { { "SearchSessionsRequest", "" } } }) + ->Event("LeaveSessionAsync", &AWSGameLiftSessionAsyncRequestBus::Events::LeaveSessionAsync); + + behaviorContext->EBus("AWSGameLiftSessionAsyncRequestNotificationBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Handler(); + + behaviorContext->EBus("AWSGameLiftSessionRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Event("CreateSession", &AWSGameLiftSessionRequestBus::Events::CreateSession, { { { "CreateSessionRequest", "" } } }) + ->Event("JoinSession", &AWSGameLiftSessionRequestBus::Events::JoinSession, { { { "JoinSessionRequest", "" } } }) + ->Event("SearchSessions", &AWSGameLiftSessionRequestBus::Events::SearchSessions, { { { "SearchSessionsRequest", "" } } }) + ->Event("LeaveSession", &AWSGameLiftSessionRequestBus::Events::LeaveSession); + } } void AWSGameLiftClientSystemComponent::ReflectCreateSessionRequest(AZ::ReflectContext* context) @@ -161,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)) @@ -174,9 +224,25 @@ namespace AWSGameLift } } - void AWSGameLiftClientSystemComponent::SetGameLiftClientManager(AZStd::unique_ptr gameliftClientManager) + AZStd::shared_ptr AWSGameLiftClientSystemComponent::GetGameLiftClient() const + { + return m_gameliftClient; + } + + void AWSGameLiftClientSystemComponent::SetGameLiftClient(AZStd::shared_ptr gameliftClient) + { + m_gameliftClient.swap(gameliftClient); + } + + void AWSGameLiftClientSystemComponent::SetGameLiftClientManager(AZStd::unique_ptr gameliftManager) + { + m_gameliftManager.reset(); + m_gameliftManager = AZStd::move(gameliftManager); + } + + void AWSGameLiftClientSystemComponent::SetGameLiftClientTicketTracker(AZStd::unique_ptr gameliftTicketTracker) { - m_gameliftClientManager.reset(); - m_gameliftClientManager = AZStd::move(gameliftClientManager); + m_gameliftTicketTracker.reset(); + m_gameliftTicketTracker = AZStd::move(gameliftTicketTracker); } } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h index bac24d44fa..30d4ee0106 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace AWSGameLift { class AWSGameLiftClientManager; @@ -18,6 +21,7 @@ namespace AWSGameLift //! Gem client system component. Responsible for creating the gamelift client manager. class AWSGameLiftClientSystemComponent : public AZ::Component + , public IAWSGameLiftInternalRequests { public: AZ_COMPONENT(AWSGameLiftClientSystemComponent, "{d481c15c-732a-4eea-9853-4965ed1bc2be}"); @@ -32,6 +36,10 @@ namespace AWSGameLift static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + // IAWSGameLiftInternalRequests interface implementation + AZStd::shared_ptr GetGameLiftClient() const override; + void SetGameLiftClient(AZStd::shared_ptr gameliftClient) override; + protected: //////////////////////////////////////////////////////////////////////// // AZ::Component interface implementation @@ -40,13 +48,19 @@ namespace AWSGameLift void Deactivate() override; //////////////////////////////////////////////////////////////////////// - void SetGameLiftClientManager(AZStd::unique_ptr gameliftClientManager); + void SetGameLiftClientManager(AZStd::unique_ptr gameliftManager); + void SetGameLiftClientTicketTracker(AZStd::unique_ptr gameliftTicketTracker); private: + static void ReflectGameLiftMatchmaking(AZ::ReflectContext* context); + static void ReflectGameLiftSession(AZ::ReflectContext* context); + static void ReflectCreateSessionRequest(AZ::ReflectContext* context); static void ReflectSearchSessionsResponse(AZ::ReflectContext* context); - AZStd::unique_ptr m_gameliftClientManager; + AZStd::shared_ptr m_gameliftClient; + AZStd::unique_ptr m_gameliftManager; + AZStd::unique_ptr m_gameliftTicketTracker; }; } // namespace AWSGameLift 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/AWSGameLiftAcceptMatchRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftAcceptMatchRequest.cpp new file mode 100644 index 0000000000..8a8327b92b --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftAcceptMatchRequest.cpp @@ -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 + * + */ + +#include +#include +#include + +#include + +namespace AWSGameLift +{ + void AWSGameLiftAcceptMatchRequest::Reflect(AZ::ReflectContext* context) + { + AzFramework::AcceptMatchRequest::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("AWSGameLiftAcceptMatchRequest", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AWSGameLiftAcceptMatchRequest") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("AcceptMatch", BehaviorValueProperty(&AWSGameLiftAcceptMatchRequest::m_acceptMatch)) + ->Property("PlayerIds", BehaviorValueProperty(&AWSGameLiftAcceptMatchRequest::m_playerIds)) + ->Property("TicketId", BehaviorValueProperty(&AWSGameLiftAcceptMatchRequest::m_ticketId)); + } + } +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp new file mode 100644 index 0000000000..31e9c5eff7 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp @@ -0,0 +1,50 @@ +/* + * 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 + +namespace AWSGameLift +{ + void AWSGameLiftStartMatchmakingRequest::Reflect(AZ::ReflectContext* context) + { + AzFramework::StartMatchmakingRequest::Reflect(context); + AWSGameLiftPlayer::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("configurationName", &AWSGameLiftStartMatchmakingRequest::m_configurationName) + ->Field("players", &AWSGameLiftStartMatchmakingRequest::m_players); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("AWSGameLiftStartMatchmakingRequest", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &AWSGameLiftStartMatchmakingRequest::m_configurationName, "ConfigurationName", + "Name of the matchmaking configuration to use for this request") + ->DataElement(AZ::Edit::UIHandlers::Default, &AWSGameLiftStartMatchmakingRequest::m_players, "Players", + "Information on each player to be matched."); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AWSGameLiftStartMatchmakingRequest") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("TicketId", BehaviorValueProperty(&AWSGameLiftStartMatchmakingRequest::m_ticketId)) + ->Property("ConfigurationName", BehaviorValueProperty(&AWSGameLiftStartMatchmakingRequest::m_configurationName)) + ->Property("Players", BehaviorValueProperty(&AWSGameLiftStartMatchmakingRequest::m_players)); + } + } +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStopMatchmakingRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStopMatchmakingRequest.cpp new file mode 100644 index 0000000000..bcea1fa8fe --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStopMatchmakingRequest.cpp @@ -0,0 +1,41 @@ +/* + * 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 + +namespace AWSGameLift +{ + void AWSGameLiftStopMatchmakingRequest::Reflect(AZ::ReflectContext* context) + { + AzFramework::StopMatchmakingRequest::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("AWSGameLiftStopMatchmakingRequest", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AWSGameLiftStopMatchmakingRequest") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("TicketId", BehaviorValueProperty(&AWSGameLiftStopMatchmakingRequest::m_ticketId)); + } + } +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h new file mode 100644 index 0000000000..dbac9a798a --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.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 + +namespace Aws +{ + namespace GameLift + { + class GameLiftClient; + } +} // namespace Aws + +namespace AWSGameLift +{ + //! IAWSGameLiftRequests + //! GameLift Gem internal interface which is used to fetch gem global GameLift client + class IAWSGameLiftInternalRequests + { + public: + AZ_RTTI(IAWSGameLiftInternalRequests, "{DC0CC1C4-21EE-4A41-B428-D12D697F88A2}"); + + IAWSGameLiftInternalRequests() = default; + virtual ~IAWSGameLiftInternalRequests() = default; + + //! GetGameLiftClient + //! Get GameLift client to interact with Amazon GameLift service + //! @return Shared pointer to the GameLift client + virtual AZStd::shared_ptr GetGameLiftClient() const = 0; + + //! SetGameLiftClient + //! Set GameLift client to provided GameLift client + //! @param gameliftClient Input new GameLift client + virtual void SetGameLiftClient(AZStd::shared_ptr gameliftClient) = 0; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h new file mode 100644 index 0000000000..81d04314af --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.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 +#include + +namespace AWSGameLift +{ + //! IAWSGameLiftMatchmakingInternalRequests + //! GameLift Gem matchmaking internal interfaces which is used to communicate + //! with client side ticket tracker to sync matchmaking ticket data and join + //! player to the match + class IAWSGameLiftMatchmakingInternalRequests + { + public: + AZ_RTTI(IAWSGameLiftMatchmakingInternalRequests, "{C2DA440E-74E0-411E-813D-5880B50B0C9E}"); + + IAWSGameLiftMatchmakingInternalRequests() = default; + virtual ~IAWSGameLiftMatchmakingInternalRequests() = default; + + //! StartPolling + //! Request to start process for polling matchmaking ticket based on given ticket id and player id + //! @param ticketId The requested matchmaking ticket id + //! @param playerId The requested matchmaking player id + virtual void StartPolling(const AZStd::string& ticketId, const AZStd::string& playerId) = 0; + + //! StopPolling + //! Request to stop process for polling matchmaking ticket + virtual void StopPolling() = 0; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp new file mode 100644 index 0000000000..4dc4dd85f6 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp @@ -0,0 +1,391 @@ +/* + * 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 + +using namespace AWSGameLift; + +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; + +class TestAWSGameLiftClientLocalTicketTracker + : public AWSGameLiftClientLocalTicketTracker +{ +public: + TestAWSGameLiftClientLocalTicketTracker() = default; + virtual ~TestAWSGameLiftClientLocalTicketTracker() = default; + + void SetUp() + { + ActivateTracker(); + m_pollingPeriodInMS = TEST_RACKER_POLLING_PERIOD_MS; + } + + void TearDown() + { + DeactivateTracker(); + } + + bool IsTrackerIdle() + { + return m_status == TicketTrackerStatus::Idle; + } +}; + +class AWSGameLiftClientLocalTicketTrackerTest + : public AWSGameLiftClientFixture + , public IAWSGameLiftInternalRequests +{ +protected: + void SetUp() override + { + AWSGameLiftClientFixture::SetUp(); + + AZ::Interface::Register(this); + + m_gameliftClientMockPtr = AZStd::make_shared(); + m_gameliftClientTicketTracker = AZStd::make_unique(); + m_gameliftClientTicketTracker->SetUp(); + } + + void TearDown() override + { + m_gameliftClientTicketTracker->TearDown(); + m_gameliftClientTicketTracker.reset(); + m_gameliftClientMockPtr.reset(); + + AZ::Interface::Unregister(this); + + AWSGameLiftClientFixture::TearDown(); + } + + AZStd::shared_ptr GetGameLiftClient() const + { + return m_gameliftClientMockPtr; + } + + void SetGameLiftClient(AZStd::shared_ptr gameliftClient) + { + AZ_UNUSED(gameliftClient); + m_gameliftClientMockPtr.reset(); + } + + void WaitForProcessFinish(uint64_t expectedNum) + { + int processingTime = 0; + while (processingTime < TEST_WAIT_MAXIMUM_TIME_MS) + { + if (::UnitTest::TestRunner::Instance().m_numAssertsFailed == expectedNum) + { + AZ_TEST_STOP_TRACE_SUPPRESSION(expectedNum); + return; + } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(TEST_WAIT_BUFFER_TIME_MS)); + processingTime += TEST_WAIT_BUFFER_TIME_MS; + } + } + + void WaitForProcessFinish() + { + int processingTime = 0; + while (processingTime < TEST_WAIT_MAXIMUM_TIME_MS) + { + if (m_gameliftClientTicketTracker->IsTrackerIdle()) + { + return; + } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(TEST_WAIT_BUFFER_TIME_MS)); + processingTime += TEST_WAIT_BUFFER_TIME_MS; + } + } + +public: + AZStd::unique_ptr m_gameliftClientTicketTracker; + AZStd::shared_ptr m_gameliftClientMockPtr; +}; + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithoutClientSetup_GetExpectedErrors) +{ + AZ::Interface::Get()->SetGameLiftClient(nullptr); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_MultipleCallsWithoutClientSetup_GetExpectedErrors) +{ + AZ::Interface::Get()->SetGameLiftClient(nullptr); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButWithFailedOutcome_GetExpectedErrors) +{ + Aws::Client::AWSError error; + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(error); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithMoreThanOneTicket_GetExpectedErrors) +{ + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(Aws::GameLift::Model::MatchmakingTicket()); + result.AddTicketList(Aws::GameLift::Model::MatchmakingTicket()); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithCompleteStatus_ProcessStopsAndGetExpectedResult) +{ + 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 ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + SessionHandlingClientRequestsMock handlerMock; + EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(true)); + + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButNoPlayerSession_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButFailedToJoinMatch_ProcessStopsAndGetExpectedError) +{ + 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 ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + SessionHandlingClientRequestsMock handlerMock; + EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(false)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketTimeOut_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::TIMED_OUT); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketFailed_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::FAILED); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketCancelled_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::CANCELLED); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallAndTicketCompleteAtLast_ProcessContinuesAndStop) +{ + Aws::GameLift::Model::MatchmakingTicket ticket1; + ticket1.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::QUEUED); + + 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)); + + SessionHandlingClientRequestsMock handlerMock; + EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(true)); + + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + 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 caa5113a25..1c3e8726fd 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp @@ -19,12 +19,14 @@ #include #include +#include #include #include #include #include - -#include +#include +#include +#include using namespace AWSGameLift; @@ -117,38 +119,19 @@ public: MOCK_METHOD0(GetDefaultConfig, AWSCore::AwsApiJobConfig*()); }; -class TestAWSGameLiftClientManager - : public AWSGameLiftClientManager -{ -public: - TestAWSGameLiftClientManager() - { - m_gameliftClientMockPtr = nullptr; - } - ~TestAWSGameLiftClientManager() - { - m_gameliftClientMockPtr = nullptr; - } - - void SetUpMockClient() - { - m_gameliftClientMockPtr = AZStd::make_shared(); - SetGameLiftClient(m_gameliftClientMockPtr); - } - - AZStd::shared_ptr m_gameliftClientMockPtr; -}; - class AWSGameLiftClientManagerTest : public AWSGameLiftClientFixture + , public IAWSGameLiftInternalRequests { protected: void SetUp() override { AWSGameLiftClientFixture::SetUp(); - m_gameliftClientManager = AZStd::make_unique(); - m_gameliftClientManager->SetUpMockClient(); + AZ::Interface::Register(this); + + m_gameliftClientMockPtr = AZStd::make_shared(); + m_gameliftClientManager = AZStd::make_unique(); m_gameliftClientManager->ActivateManager(); } @@ -156,10 +139,24 @@ protected: { m_gameliftClientManager->DeactivateManager(); m_gameliftClientManager.reset(); + m_gameliftClientMockPtr.reset(); + + AZ::Interface::Unregister(this); AWSGameLiftClientFixture::TearDown(); } + AZStd::shared_ptr GetGameLiftClient() const + { + return m_gameliftClientMockPtr; + } + + void SetGameLiftClient(AZStd::shared_ptr gameliftClient) + { + AZ_UNUSED(gameliftClient); + m_gameliftClientMockPtr.reset(); + } + AWSGameLiftSearchSessionsRequest GetValidSearchSessionsRequest() { AWSGameLiftSearchSessionsRequest request; @@ -211,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"; @@ -229,10 +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::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; @@ -319,7 +350,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSession_CallWithValidRequest_GetSucce Aws::GameLift::Model::CreateGameSessionResult result; result.SetGameSession(Aws::GameLift::Model::GameSession()); Aws::GameLift::Model::CreateGameSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); m_gameliftClientManager->CreateSession(request); @@ -331,7 +362,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSession_CallWithValidRequest_GetError request.m_aliasId = "dummyAlias"; Aws::Client::AWSError error; Aws::GameLift::Model::CreateGameSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -357,7 +388,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionAsync_CallWithValidRequest_Get Aws::GameLift::Model::CreateGameSessionResult result; result.SetGameSession(Aws::GameLift::Model::GameSession()); Aws::GameLift::Model::CreateGameSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -373,7 +404,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionAsync_CallWithValidRequest_Get request.m_aliasId = "dummyAlias"; Aws::Client::AWSError error; Aws::GameLift::Model::CreateGameSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -403,7 +434,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueue_CallWithValidRequest_G Aws::GameLift::Model::StartGameSessionPlacementResult result; result.SetGameSessionPlacement(Aws::GameLift::Model::GameSessionPlacement()); Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); m_gameliftClientManager->CreateSession(request); @@ -416,7 +447,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueue_CallWithValidRequest_G request.m_placementId = "dummyPlacementId"; Aws::Client::AWSError error; Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -434,7 +465,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueueAsync_CallWithValidRequ Aws::GameLift::Model::StartGameSessionPlacementResult result; result.SetGameSessionPlacement(Aws::GameLift::Model::GameSessionPlacement()); Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -451,7 +482,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueueAsync_CallWithValidRequ request.m_placementId = "dummyPlacementId"; Aws::Client::AWSError error; Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -489,7 +520,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequestButNoReques Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -505,7 +536,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequest_GetErrorOu request.m_playerId = "dummyPlayerId"; Aws::Client::AWSError error; Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -524,7 +555,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequestAndRequestH Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); auto response = m_gameliftClientManager->JoinSession(request); @@ -541,7 +572,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequestAndRequestH Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); auto response = m_gameliftClientManager->JoinSession(request); @@ -567,7 +598,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequestButNoR Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -586,7 +617,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequest_GetEr request.m_playerId = "dummyPlayerId"; Aws::Client::AWSError error; Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -608,7 +639,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequestAndReq Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -628,7 +659,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequestAndReq Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -642,7 +673,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessions_CallWithValidRequestAndError Aws::Client::AWSError error; Aws::GameLift::Model::SearchGameSessionsOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -657,7 +688,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessions_CallWithValidRequestAndSucce AWSGameLiftSearchSessionsRequest request = GetValidSearchSessionsRequest(); Aws::GameLift::Model::SearchGameSessionsOutcome outcome = GetValidSearchGameSessionsOutcome(); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -703,7 +734,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessionsAsync_CallWithValidRequestAnd Aws::Client::AWSError error; Aws::GameLift::Model::SearchGameSessionsOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -724,7 +755,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessionsAsync_CallWithValidRequestAnd EXPECT_CALL(coreHandlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); Aws::GameLift::Model::SearchGameSessionsOutcome outcome = GetValidSearchGameSessionsOutcome(); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -767,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 a161168159..6ba05747aa 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h @@ -9,22 +9,34 @@ #pragma once #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 using namespace Aws::GameLift; @@ -37,10 +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/AWSGameLiftClientSystemComponentTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp index df6fb0d666..096a182819 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp @@ -13,10 +13,9 @@ #include #include +#include #include -#include - using namespace AWSGameLift; class AWSGameLiftClientManagerMock @@ -30,6 +29,17 @@ public: MOCK_METHOD0(DeactivateManager, void()); }; +class AWSGameLiftClientLocalTicketTrackerMock + : public AWSGameLiftClientLocalTicketTracker +{ +public: + AWSGameLiftClientLocalTicketTrackerMock() = default; + ~AWSGameLiftClientLocalTicketTrackerMock() = default; + + MOCK_METHOD0(ActivateTracker, void()); + MOCK_METHOD0(DeactivateTracker, void()); +}; + class TestAWSGameLiftClientSystemComponent : public AWSGameLiftClientSystemComponent { @@ -37,20 +47,29 @@ public: TestAWSGameLiftClientSystemComponent() { m_gameliftClientManagerMockPtr = nullptr; + m_gameliftClientTicketTrackerMockPtr = nullptr; } ~TestAWSGameLiftClientSystemComponent() { m_gameliftClientManagerMockPtr = nullptr; + m_gameliftClientTicketTrackerMockPtr = nullptr; } void SetUpMockManager() { - AZStd::unique_ptr gameliftClientManagerMock = AZStd::make_unique(); + AZStd::unique_ptr gameliftClientManagerMock = + AZStd::make_unique(); m_gameliftClientManagerMockPtr = gameliftClientManagerMock.get(); SetGameLiftClientManager(AZStd::move(gameliftClientManagerMock)); + + AZStd::unique_ptr gameliftClientTicketTrackerMock = + AZStd::make_unique(); + m_gameliftClientTicketTrackerMockPtr = gameliftClientTicketTrackerMock.get(); + SetGameLiftClientTicketTracker(AZStd::move(gameliftClientTicketTrackerMock)); } AWSGameLiftClientManagerMock* m_gameliftClientManagerMockPtr; + AWSGameLiftClientLocalTicketTrackerMock* m_gameliftClientTicketTrackerMockPtr; }; class AWSCoreSystemComponentMock @@ -145,8 +164,10 @@ TEST_F(AWSGameLiftClientSystemComponentTest, ActivateDeactivate_Call_GameLiftCli { m_entity->Init(); EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientManagerMockPtr), ActivateManager()).Times(1); + EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientTicketTrackerMockPtr), ActivateTracker()).Times(1); m_entity->Activate(); EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientManagerMockPtr), DeactivateManager()).Times(1); + EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientTicketTrackerMockPtr), DeactivateTracker()).Times(1); m_entity->Deactivate(); } 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 771bdf0c08..cd54414cc1 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -7,13 +7,21 @@ # set(FILES + ../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h + ../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp + ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h + Include/Request/AWSGameLiftAcceptMatchRequest.h Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h Include/Request/AWSGameLiftCreateSessionRequest.h Include/Request/AWSGameLiftJoinSessionRequest.h Include/Request/AWSGameLiftSearchSessionsRequest.h + Include/Request/AWSGameLiftStartMatchmakingRequest.h + Include/Request/AWSGameLiftStopMatchmakingRequest.h 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 @@ -24,12 +32,23 @@ set(FILES Source/Activity/AWSGameLiftLeaveSessionActivity.h Source/Activity/AWSGameLiftSearchSessionsActivity.cpp 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 Source/AWSGameLiftClientSystemComponent.h + Source/Request/AWSGameLiftAcceptMatchRequest.cpp Source/Request/AWSGameLiftCreateSessionOnQueueRequest.cpp Source/Request/AWSGameLiftCreateSessionRequest.cpp Source/Request/AWSGameLiftJoinSessionRequest.cpp Source/Request/AWSGameLiftSearchSessionsRequest.cpp + Source/Request/AWSGameLiftStartMatchmakingRequest.cpp + Source/Request/AWSGameLiftStopMatchmakingRequest.cpp + Source/Request/IAWSGameLiftInternalRequests.h + Source/Request/IAWSGameLiftMatchmakingInternalRequests.h ) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake index 6614eabb46..3e73dd8a0a 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake @@ -7,11 +7,15 @@ # 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 Tests/AWSGameLiftClientMocks.h Tests/AWSGameLiftClientSystemComponentTest.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/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h index 588bd51380..e29df324d3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h @@ -17,4 +17,5 @@ namespace AWSGameLift static const char* AWSGameLiftSessionStatusReasons[2] = { "NotSet", "Interrupted" }; static constexpr const char AWSGameLiftErrorMessageTemplate[] = "Exception: %s, Message: %s"; + static constexpr const char AWSGameLiftClientMissingErrorMessage[] = "GameLift client is not configured yet."; } // 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/AWSMetrics/Code/Source/IdentityProvider.cpp b/Gems/AWSMetrics/Code/Source/IdentityProvider.cpp index 7e4ec05aba..bc8ff9a42e 100644 --- a/Gems/AWSMetrics/Code/Source/IdentityProvider.cpp +++ b/Gems/AWSMetrics/Code/Source/IdentityProvider.cpp @@ -22,7 +22,7 @@ namespace AWSMetrics AZStd::string IdentityProvider::GetEngineVersion() { - static constexpr const char* EngineConfigFilePath = "@root@/engine.json"; + static constexpr const char* EngineConfigFilePath = "@products@/engine.json"; static constexpr const char* EngineVersionJsonKey = "O3DEVersion"; AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetDirectInstance(); diff --git a/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h b/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h index 6b7c809601..b77f39c93c 100644 --- a/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h +++ b/Gems/AWSMetrics/Code/Tests/AWSMetricsGemMock.h @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include namespace AWSMetrics { @@ -34,15 +34,22 @@ namespace AWSMetrics // Set up the file IO and alias m_localFileIO = aznew AZ::IO::LocalFileIO(); m_priorFileIO = AZ::IO::FileIOBase::GetInstance(); - // we need to set it to nullptr first because otherwise the + // we need to set it to nullptr first because otherwise the // underneath code assumes that we might be leaking the previous instance AZ::IO::FileIOBase::SetInstance(nullptr); AZ::IO::FileIOBase::SetInstance(m_localFileIO); - const AZStd::string engineRoot = AZ::Test::GetEngineRootPath(); - m_localFileIO->SetAlias("@devroot@", engineRoot.c_str()); - m_localFileIO->SetAlias("@root@", engineRoot.c_str()); - m_localFileIO->SetAlias("@user@", GetTestFolderPath().c_str()); + const AZ::IO::Path engineRoot = AZ::Test::GetEngineRootPath(); + const auto productAssetPath = GetTestFolderPath() / "Cache"; + const auto userPath = GetTestFolderPath() / "user"; + m_localFileIO->CreatePath(productAssetPath.c_str()); + m_localFileIO->CreatePath(userPath.c_str()); + m_localFileIO->SetAlias("@engroot@", engineRoot.c_str()); + m_localFileIO->SetAlias("@products@", productAssetPath.c_str()); + m_localFileIO->SetAlias("@user@", userPath.c_str()); + // Copy engine.json to the cache + EXPECT_TRUE(m_localFileIO->Copy((engineRoot / "engine.json").c_str(), "engine.json")); + m_serializeContext = AZStd::make_unique(); m_registrationContext = AZStd::make_unique(); @@ -69,6 +76,13 @@ namespace AWSMetrics m_serializeContext.reset(); m_registrationContext.reset(); + const auto productAssetPath = GetTestFolderPath() / "Cache"; + const auto userPath = GetTestFolderPath() / "user"; + // Clear the product asset cache alias to prevent cache write errors + m_localFileIO->ClearAlias("@products@"); + m_localFileIO->DestroyPath(userPath.c_str()); + m_localFileIO->DestroyPath(productAssetPath.c_str()); + AZ::IO::FileIOBase::SetInstance(nullptr); delete m_localFileIO; AZ::IO::FileIOBase::SetInstance(m_priorFileIO); @@ -97,22 +111,22 @@ namespace AWSMetrics bool CreateFile(const AZStd::string& filePath, const AZStd::string& content) { AZ::IO::HandleType fileHandle; + // Suppress errors about writing to product asset cache + AZ_TEST_START_TRACE_SUPPRESSION; if (!m_localFileIO->Open(filePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText, fileHandle)) { return false; } m_localFileIO->Write(fileHandle, content.c_str(), content.size()); + AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; m_localFileIO->Close(fileHandle); return true; } AZStd::string GetDefaultTestFilePath() { - AZStd::string testFilePath = GetTestFolderPath(); - AzFramework::StringFunc::Path::Join(testFilePath.c_str(), "Test.json", testFilePath); - - return testFilePath; + return (GetTestFolderPath() / "Test.json").Native(); } bool RemoveFile(const AZStd::string& filePath) @@ -133,14 +147,16 @@ namespace AWSMetrics AZ::IO::FileIOBase* m_priorFileIO = nullptr; AZ::IO::FileIOBase* m_localFileIO = nullptr; + AZ::Test::ScopedAutoTempDirectory m_testDirectory; AZStd::unique_ptr m_serializeContext; AZStd::unique_ptr m_registrationContext; AZStd::unique_ptr m_settingsRegistry; private: - AZStd::string GetTestFolderPath() + AZ::IO::Path GetTestFolderPath() { - return AZ_TRAIT_TEST_ROOT_FOLDER; + AZ::IO::Path testPathString{ m_testDirectory.GetDirectory() }; + return testPathString; } }; } diff --git a/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp b/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp index b4bf68bac1..56f28856af 100644 --- a/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp +++ b/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp @@ -142,14 +142,14 @@ namespace AssetValidation system.GetIConsole()->AddCommand("addseedlist", ConsoleCommandAddSeedList); system.GetIConsole()->AddCommand("removeseedlist", ConsoleCommandRemoveSeedList); system.GetIConsole()->AddCommand("printexcluded", ConsoleCommandTogglePrintExcluded); - } + } bool AssetValidationSystemComponent::IsKnownAsset(const char* assetPath) { AZStd::string lowerAsset{ assetPath }; AZStd::replace(lowerAsset.begin(), lowerAsset.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR); - const AZStd::vector prefixes = { "./", "@assets@/" }; + const AZStd::vector prefixes = { "./", "@products@/" }; for (const AZStd::string& prefix : prefixes) { if (lowerAsset.starts_with(prefix)) @@ -392,7 +392,7 @@ namespace AssetValidation AssetValidationRequestBus::Broadcast(&AssetValidationRequestBus::Events::AddSeedList, seedfilepath); } - bool AssetValidationSystemComponent::AddSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId) + bool AssetValidationSystemComponent::AddSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId) { for (const AzFramework::SeedInfo& thisSeed : seedList) { @@ -401,7 +401,7 @@ namespace AssetValidation return true; } - bool AssetValidationSystemComponent::RemoveSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId) + bool AssetValidationSystemComponent::RemoveSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId) { AssetValidationRequests::AssetSourceList removeList; for (const AzFramework::SeedInfo& thisSeed : seedList) 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 3493099d4c..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() @@ -244,7 +211,7 @@ namespace ImageProcessingAtom } AZ::IO::FixedMaxPath projectConfigFolder; - if (auto sourceGameRoot = fileIoBase->ResolvePath("@devassets@"); sourceGameRoot.has_value()) + if (auto sourceGameRoot = fileIoBase->ResolvePath("@projectroot@"); sourceGameRoot.has_value()) { projectConfigFolder = *sourceGameRoot; projectConfigFolder /= s_projectConfigRelativeFolder; @@ -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) - { - 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) + else { - 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/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp index 7c5be89508..27ae90dcdc 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp @@ -139,16 +139,16 @@ namespace AZ //! Validates if a given .shadervariantlist file is located at the correct path for a given .shader full path. //! There are two valid paths: //! 1- Lower Precedence: The same folder where the .shader file is located. - //! 2- Higher Precedence: //ShaderVariants/. + //! 2- Higher Precedence: /ShaderVariants/. //! The "Higher Precedence" path gives the option to game projects to override what variants to generate. If this //! file exists then the "Lower Precedence" path is disregarded. //! A .shader full path is located under an AP scan folder. - //! Example: "/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shader" - //! - In this example the Scan Folder is "/Gems/Atom/Feature/Common/Assets", while the subfolder is "Materials/Types". + //! Example: "/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shader" + //! - In this example the Scan Folder is "/Gems/Atom/Feature/Common/Assets", while the subfolder is "Materials/Types". //! The "Higher Precedence" expected valid location for the .shadervariantlist would be: - //! - //ShaderVariants/Materials/Types/StandardPBR_ForwardPass.shadervariantlist. + //! - //ShaderVariants/Materials/Types/StandardPBR_ForwardPass.shadervariantlist. //! The "Lower Precedence" valid location would be: - //! - /Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shadervariantlist. + //! - /Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shadervariantlist. //! @shouldExitEarlyFromProcessJob [out] Set to true if ProcessJob should do no work but return successfully. //! Set to false if ProcessJob should do work and create assets. //! When @shaderVariantListFileFullPath is provided by a Gem/Feature instead of the Game Project @@ -169,17 +169,13 @@ namespace AZ AZStd::string shaderVariantListFileRelativePath = shaderProductFileRelativePath; AzFramework::StringFunc::Path::ReplaceExtension(shaderVariantListFileRelativePath, RPI::ShaderVariantListSourceData::Extension); - const char * gameProjectPath = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameProjectPath, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath); + AZ::IO::FixedMaxPath gameProjectPath = AZ::Utils::GetProjectPath(); - AZStd::string expectedHigherPrecedenceFileFullPath; - AzFramework::StringFunc::Path::Join(gameProjectPath, RPI::ShaderVariantTreeAsset::CommonSubFolder, expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */); - AzFramework::StringFunc::Path::Join(expectedHigherPrecedenceFileFullPath.c_str(), shaderProductFileRelativePath.c_str(), expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */); - AzFramework::StringFunc::Path::ReplaceExtension(expectedHigherPrecedenceFileFullPath, AZ::RPI::ShaderVariantListSourceData::Extension); - AzFramework::StringFunc::Path::Normalize(expectedHigherPrecedenceFileFullPath); + auto expectedHigherPrecedenceFileFullPath = (gameProjectPath + / RPI::ShaderVariantTreeAsset::CommonSubFolder / shaderProductFileRelativePath).LexicallyNormal(); + expectedHigherPrecedenceFileFullPath.ReplaceExtension(AZ::RPI::ShaderVariantListSourceData::Extension); - AZStd::string normalizedShaderVariantListFileFullPath = shaderVariantListFileFullPath; - AzFramework::StringFunc::Path::Normalize(normalizedShaderVariantListFileFullPath); + auto normalizedShaderVariantListFileFullPath = AZ::IO::FixedMaxPath(shaderVariantListFileFullPath).LexicallyNormal(); if (expectedHigherPrecedenceFileFullPath == normalizedShaderVariantListFileFullPath) { @@ -203,23 +199,15 @@ namespace AZ } // Check the "Lower Precedence" case, .shader path == .shadervariantlist path. - AZStd::string normalizedShaderFileFullPath = shaderFileFullPath; - AzFramework::StringFunc::Path::Normalize(normalizedShaderFileFullPath); - - AZStd::string normalizedShaderFileFullPathWithoutExtension = normalizedShaderFileFullPath; - AzFramework::StringFunc::Path::StripExtension(normalizedShaderFileFullPathWithoutExtension); - - AZStd::string normalizedShaderVariantListFileFullPathWithoutExtension = normalizedShaderVariantListFileFullPath; - AzFramework::StringFunc::Path::StripExtension(normalizedShaderVariantListFileFullPathWithoutExtension); - -#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS - //In certain circumstances, the capitalization of the drive letter may not match - const bool caseSensitive = false; -#else - //On the other platforms there's no drive letter, so it should be a non-issue. - const bool caseSensitive = true; -#endif - if (!StringFunc::Equal(normalizedShaderFileFullPathWithoutExtension.c_str(), normalizedShaderVariantListFileFullPathWithoutExtension.c_str(), caseSensitive)) + AZ::IO::Path normalizedShaderFileFullPath = AZ::IO::Path(shaderFileFullPath).LexicallyNormal(); + + auto normalizedShaderFileFullPathWithoutExtension = normalizedShaderFileFullPath; + normalizedShaderFileFullPathWithoutExtension.ReplaceExtension(""); + + auto normalizedShaderVariantListFileFullPathWithoutExtension = normalizedShaderVariantListFileFullPath; + normalizedShaderVariantListFileFullPathWithoutExtension.ReplaceExtension(""); + + if (normalizedShaderFileFullPathWithoutExtension != normalizedShaderVariantListFileFullPathWithoutExtension) { AZ_Error(ShaderVariantAssetBuilderName, false, "For shader file at path [%s], the shader variant list [%s] is expected to be located at [%s.%s] or [%s]" , normalizedShaderFileFullPath.c_str(), normalizedShaderVariantListFileFullPath.c_str(), 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/EnvironmentCubeMapForwardMSAA.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass index cd52ce946b..877ae489c0 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass @@ -147,22 +147,6 @@ }, "LoadAction": "Clear" } - }, - { - "Name": "ScatterDistanceOutput", - "SlotType": "Output", - "ScopeAttachmentUsage": "RenderTarget", - "LoadStoreAction": { - "ClearValue": { - "Value": [ - 0.0, - 0.0, - 0.0, - 0.0 - ] - }, - "LoadAction": "Clear" - } } ], "ImageAttachments": [ @@ -257,23 +241,6 @@ "AssetRef": { "FilePath": "Textures/BRDFTexture.attimage" } - }, - { - "Name": "ScatterDistanceImage", - "SizeSource": { - "Source": { - "Pass": "Parent", - "Attachment": "Output" - } - }, - "MultisampleSource": { - "Pass": "This", - "Attachment": "DepthStencilInputOutput" - }, - "ImageDescriptor": { - "Format": "R11G11B10_FLOAT", - "SharedQueueMask": "Graphics" - } } ], "Connections": [ @@ -318,13 +285,6 @@ "Pass": "This", "Attachment": "BRDFTexture" } - }, - { - "LocalSlot": "ScatterDistanceOutput", - "AttachmentRef": { - "Pass": "This", - "Attachment": "ScatterDistanceImage" - } } ] } 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/Shadow/DirectionalLightShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli index dd235fcd3a..633ea85387 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli @@ -10,7 +10,6 @@ #include #include -#include "JitterTablePcf.azsli" #include "Shadow.azsli" #include "ShadowmapAtlasLib.azsli" #include "BicubicPcfFilters.azsli" @@ -82,12 +81,6 @@ class DirectionalLightShadow // result.y == true if the given coordinate is in shadow. bool2 IsShadowed(float3 shadowCoord, uint indexOfCascade); - // This checks if the point is shadowed or not for the given center coordinate and jitter. - bool IsShadowedWithJitter( - float3 jitterUnit, - float jitterDepthDiffBase, - uint jitterIndex); - // This outputs visibility ratio (from 0.0 to 1.0) of the given coordinate // from the light origin without filtering. float GetVisibilityFromLightNoFilter(); @@ -189,75 +182,6 @@ bool2 DirectionalLightShadow::IsShadowed(float3 shadowCoord, uint indexOfCascade return bool2(false, false); } -bool DirectionalLightShadow::IsShadowedWithJitter( - float3 jitterUnit, - float jitterDepthDiffBase, - uint jitterIndex) -{ - const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount; - const float4x4 worldToLightViewMatrices[ViewSrg::MaxCascadeCount] = - ViewSrg::m_directionalLightShadows[m_lightIndex].m_worldToLightViewMatrices; - const float4x4 lightViewToShadowmapMatrices[ViewSrg::MaxCascadeCount] = - ViewSrg::m_directionalLightShadows[m_lightIndex].m_lightViewToShadowmapMatrices; - const float boundaryScale = - ViewSrg::m_directionalLightShadows[m_lightIndex].m_boundaryScale; - - const float2 jitterXY = g_jitterTablePcf[jitterIndex]; - - // jitterLightView is the jittering diff vector from the lighted point on the surface - // in the light view space. It is remarked as "v_J" in the comment - // named "Calculate depth adjusting diff for jittered samples" - // just before the function GetJitterUnitVectorDepthDiffBase. - const float4 jitterLightView = float4(jitterXY, 0., 0.) * boundaryScale; - - // It checks the jittered point is lit or shadowed from the detailed cascade - // to the less detailed one. - for (uint indexOfCascade = 0; indexOfCascade < cascadeCount; ++indexOfCascade) - { - // jitterShadowmap is the jittering diff vector in the shadowmap space. - const float4 jitterShadowmap = mul(lightViewToShadowmapMatrices[indexOfCascade], jitterLightView); - - // Calculation of the jittering for Z-coordinate (light direction) is required - // to check lit/shadowed for the jittered point. - // jitterDepthDiff is the Z-coordinate of the jittering diff vector - // in the shadowmap space. - float jitterDepthDiff = 0.; - - // jitterDepthDiffBase is "1/tan(theta)" in the comment. - if (jitterDepthDiffBase != 0.) - { - // jitterUnitLightView is the unit vector in the light view space - // noted as "v_M" in the comment. - const float3 jitterUnitLightView = - normalize(mul(worldToLightViewMatrices[indexOfCascade], float4(jitterUnit, 0.)).xyz); - const float lightViewToShadowmapZScale = -lightViewToShadowmapMatrices[indexOfCascade]._m22; - // jitterDepthDiff is the "d" in the note, and it is calculated by - // d = (v_J . v_M) / tan(theta) - // in the light view space. Furthermore it have to be converted - // to the light clip space, which can be done by lightViewToShadowmapZScale. - jitterDepthDiff = - dot(jitterLightView.xyz, jitterUnitLightView) * jitterDepthDiffBase * - lightViewToShadowmapZScale; - } - // jitteredCoord is the coordinate of the jittered point in the shadowmap space. - const float3 jitteredCoord = - m_shadowCoords[indexOfCascade] + float3(jitterShadowmap.xy, jitterDepthDiff); - // Check for the jittered point is lit or shadowed. - const bool2 checkedShadowed = IsShadowed( - jitteredCoord, - indexOfCascade); - // If check is done, return the lit/shadowed flag. - // Otherwise make it pend to the next cascade. - if (checkedShadowed.x) - { - m_debugInfo.m_cascadeIndex = indexOfCascade; - return checkedShadowed.y; - } - } - m_debugInfo.m_cascadeIndex = cascadeCount; - return false; -} - float DirectionalLightShadow::GetVisibilityFromLightNoFilter() { const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli deleted file mode 100644 index 9082286758..0000000000 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli +++ /dev/null @@ -1,185 +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 - * - */ - - /* - The following is the output of - $ python3 pcf_jitter_table.py 6 g_jitterTablePcf 0 - where pcf_jitter_table.py has the following contents. - -@code -#!/usr/bin/env python3 - -import random -import sys -import math - - -""" Returns if a point in the range -[radius_min, radius_sup)*[angle_min, angle_sup) -is contained in the tuple polar coordinates. -""" -def is_point_include(radius_min, radius_sup, angle_min, angle_sup, polars): - for polar in polars: - if (radius_min <= polar[0] and polar[0] < radius_sup and - angle_min <= polar[1] and polar[1] < angle_sup): - return True - return False - - -""" Insert a randomly generated polar coordianted point in each -range [r0, r1)*[a0, a1) if there has not been such a point -in tuple coords yet, where [0, 1)*[0, 2pi) is divided -into the rad_count*agl_count ranges. -""" -def add_jitter_coords(radius_count, angle_count, polars): - radius_base = 1.0 / math.sqrt(radius_count) - for radius_index in range(radius_count): - # range of radius - radius_min = math.sqrt(radius_index) * radius_base - radius_sup = math.sqrt(radius_index + 1) * radius_base - - # randomize angle order - random_state = random.getstate() - angle_indices = list(range(angle_count)) - random.shuffle(angle_indices) - random.setstate(random_state) - - for angle_index in angle_indices: - # range of angle - angle_min = 2 * math.pi * angle_index / angle_count - angle_sup = 2 * math.pi * (angle_index + 1) / angle_count - - # if no point in the radius/angle range, add a new point - if not is_point_include(radius_min, radius_sup, - angle_min, angle_sup, - polars): - radius = radius_min + (radius_sup - radius_min) * random.random() - angle = angle_min + (angle_sup - angle_min) * random.random() - polars += [[radius, angle]] - - -""" Return a formatted string readable as an array of -orthogonal coordinated points which are in inside of the unit disk. -""" -def conv_array_string(polars): - result = "{\n" - for [radius, angle] in polars: - x = radius * math.cos(angle) - y = radius * math.sin(angle) - result += str.format(" float2({: 1.20e}, {: 1.20e}),\n", x, y) - result = result.rstrip(",\n") + "\n};\n" - return result - - -if __name__ == "__main__": - rad_size = 1 - ang_size = 1 - - if len(sys.argv) > 3: - random_seed = int(sys.argv[3]) - else: - random_seed = 0 - - if len(sys.argv) > 2: - array_name = sys.argv[2] - else: - array_name = False - - if len(sys.argv) > 1: - len_log = int(sys.argv[1]) - else: - print(" usage: {} array_len_log2 [array_file_name] [random_seed]".format(__file__)) - print(" array_len_log2 = 2 -> array length = 4") - print(" array_len_log2 = 6 -> array length = 64") - sys.exit() - - random.seed(random_seed) - coords = [] - add_jitter_coords(rad_size, ang_size, coords) - for index in range(len_log): - if index % 2 == 0: - rad_size *= 2 - else: - ang_size *= 2 - add_jitter_coords(rad_size, ang_size, coords) - - if array_name: - print(str.format("static const float2 {}[{}] =", array_name, len(coords))) - print(conv_array_string(coords)) - - @endcode - */ -#pragma once - -static const float2 g_jitterTablePcf[64] = -{ - float2( 4.21857815578105532772e-02, -8.43367430701083664601e-01), - float2(-1.66526814909220763350e-02, 2.96922406531470617352e-01), - float2(-1.06374665780382349212e-01, -3.45521852905696924552e-01), - float2( 5.42648241814168375008e-01, 7.63475573328278533936e-01), - float2(-1.55045122122251910479e-01, 5.78282315712970729216e-01), - float2( 1.01310018770242576264e-02, -6.88001749851880561870e-01), - float2(-5.41276603451248283783e-01, 5.21888233660957712168e-01), - float2(-6.69885071867917680777e-01, -6.72019666097878665134e-01), - float2( 1.22985029409499718039e-02, 4.54706838949524849713e-01), - float2( 4.00334354168925599105e-01, -6.20112671104014120949e-02), - float2( 2.32326155804074424571e-01, 5.14183027524470093184e-01), - float2(-3.26788693165450228051e-01, -6.03339478694129849323e-01), - float2( 7.72374386126136736053e-01, 1.23204314299169448432e-01), - float2(-4.45379212004159807936e-01, -6.35591042627205338178e-01), - float2( 9.86986293787213919693e-01, -5.18195017297516449806e-02), - float2(-9.09197225477999193544e-01, 1.95281945570711268356e-01), - float2( 8.78123785413316704229e-02, -2.77671865082058690055e-02), - float2( 1.93947312440399088906e-01, 4.27852204081567363825e-03), - float2(-2.06133675819526185347e-01, -1.49183652412411493771e-01), - float2(-4.11351098583102647854e-01, 2.36214692717993696158e-01), - float2( 3.50058750095615767162e-01, -3.57193658067260721989e-01), - float2(-5.54174780014121681759e-01, -2.23361040823672196698e-01), - float2(-6.29913348094886860196e-01, 1.29962593232600148729e-01), - float2( 3.96119563669521335125e-01, 4.90495219155295036906e-01), - float2( 7.26077464944819728210e-01, -3.70531027878536270426e-02), - float2(-5.50726266551596621568e-01, 6.48997654184258587762e-01), - float2(-6.98067624269093189859e-01, -3.83843898992943299842e-01), - float2( 8.72900706885875177221e-02, 8.24287559846993866941e-01), - float2( 6.65413234189638491678e-01, -5.66029707430476647367e-01), - float2(-5.97071574457786802270e-01, -6.93417220711863180327e-01), - float2( 6.09778569514949131403e-01, 6.92279483269558570946e-01), - float2(-8.10051800827623957879e-01, 5.82366304247235455627e-01), - float2(-8.77200948157437071506e-02, -1.88326609190753474499e-01), - float2( 9.79306884403889771340e-02, 1.86693151785678163046e-01), - float2( 4.60071424048798319206e-02, -1.98255149016034859510e-01), - float2(-5.37585860722621794450e-02, 3.99205315590760584366e-02), - float2( 2.18621803321778829243e-01, -3.85632280444686503795e-01), - float2(-2.98409571230789372187e-02, 4.22286693608096730390e-01), - float2( 3.58654757584850270025e-01, 2.95175871390239985548e-01), - float2(-3.85631921979480485341e-01, -3.00322047091407640096e-01), - float2( 4.49800763439369810648e-01, 3.98492182500493397068e-01), - float2(-4.97878650048238891035e-01, 2.57984038389083569776e-01), - float2(-3.12055242602567339816e-01, -4.88013525550807125697e-01), - float2( 5.87078632117718268724e-01, -6.97256834327608099322e-02), - float2( 6.23692403999373534695e-01, 3.11519734097943645779e-01), - float2( 6.64426445690903810792e-01, -2.27661844509491811950e-01), - float2(-3.24662942872471160793e-01, 5.68939932480760024447e-01), - float2(-5.31263995010459511015e-01, -4.66108719959298256619e-01), - float2( 5.10323549430644951563e-01, 5.81027848262460677731e-01), - float2( 2.82695533021593392586e-01, -7.03582425015577883620e-01), - float2(-5.98419541732174709026e-01, -4.68015982003612274198e-01), - float2(-3.95281650646674975746e-01, 6.10614720709622194050e-01), - float2( 7.87454411900813555647e-01, 1.37726315874787758053e-01), - float2(-7.36310249594224086600e-01, 4.25723821775386646049e-01), - float2( 6.48232481978769037312e-01, -5.53108138515975955585e-01), - float2(-1.88558544306507869237e-01, -7.79120748356531223067e-01), - float2(-3.78614630625567993860e-01, 7.82366459873827913007e-01), - float2(-8.48582606942172357201e-01, -3.78504015913022351381e-01), - float2( 1.91472859899175090748e-02, -9.13050020447597532325e-01), - float2( 8.08826910050883585157e-01, 4.17202663034078935489e-01), - float2(-9.27062588380768493046e-01, -2.94160352051227980130e-01), - float2( 6.67882607007592055126e-01, -6.88642020601400450808e-01), - float2(-1.59349274307943010454e-02, 9.37629353656756814317e-01), - float2( 9.86975590293644233775e-01, 1.44401793964158337014e-01) -}; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli index b91d0ce915..daed3a2921 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli @@ -13,7 +13,6 @@ #include #include #include "BicubicPcfFilters.azsli" -#include "JitterTablePcf.azsli" #include "Shadow.azsli" // ProjectedShadow calculates shadowed area projected from a light. @@ -44,11 +43,6 @@ class ProjectedShadow float GetThickness(); bool IsShadowed(float3 shadowPosition); - bool IsShadowedWithJitter( - float3 jitterUnitX, - float3 jitterUnitY, - float jitterDepthDiffBase, - uint jitterIndex); void SetShadowPosition(); float3 GetAtlasPosition(float2 texturePosition); static float UnprojectDepth(uint shadowIndex, float depthBufferValue); @@ -321,35 +315,6 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition) return false; } -bool ProjectedShadow::IsShadowedWithJitter( - float3 jitterUnitX, - float3 jitterUnitY, - float jitterDepthDiffBase, - uint jitterIndex) -{ - ViewSrg::ProjectedShadow shadow = ViewSrg::m_projectedShadows[m_shadowIndex]; - const float4x4 depthBiasMatrix = shadow.m_depthBiasMatrix; - const float boundaryScale = shadow.m_boundaryScale; - - const float2 jitterXY = g_jitterTablePcf[jitterIndex]; - - const float dist = distance(m_worldPosition, m_viewPosition); - const float boundaryRadius = dist * tan(boundaryScale); - // jitterWorldXY is the jittering diff vector from the lighted point on the surface - // in the world space. It is remarked as "v_J" in the comment - // named "Calculate depth adjusting diff for jittered samples" - // just before the function GetJitterUnitVectorDepthDiffBase. - const float3 jitterWorldXY = jitterUnitX * (jitterXY.x * boundaryRadius) + jitterUnitY * (jitterXY.y * boundaryRadius); - // The adjusting diff of depth ("d" in the comment) is calculated by - // jitterXY.y * boundaryRadius * jitterDepthDiffBase. - const float3 jitterWorldZ = m_lightDirection * (jitterXY.y * boundaryRadius * jitterDepthDiffBase); - - const float3 jitteredWorldPosition = m_worldPosition + jitterWorldXY + jitterWorldZ; - const float4 jitteredShadowmapHomogeneous = mul(depthBiasMatrix, float4(jitteredWorldPosition, 1)); - - return IsShadowed(jitteredShadowmapHomogeneous.xyz / jitteredShadowmapHomogeneous.w); -} - void ProjectedShadow::SetShadowPosition() { const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli index e05ff076cb..1fe81016cf 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/Shadow.azsli @@ -23,14 +23,11 @@ struct FilterParameter uint m_isEnabled; uint2 m_shadowmapOriginInSlice; uint m_shadowmapSize; - uint m_parameterOffset; - uint m_parameterCount; float m_lightDistanceOfCameraViewFrustum; float m_n_f_n; // n / (f - n) float m_n_f; // n - f float m_f; // f // where n: nearDepth, f: farDepth. - float2 m_padding; // explicit padding }; class Shadow 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/ShaderResourceGroups/CoreLights/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli index 2065e28703..94d6f20da3 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/CoreLights/ViewSrg.azsli @@ -22,14 +22,11 @@ partial ShaderResourceGroup ViewSrg uint m_isEnabled; uint2 m_shadowmapOriginInSlice; uint m_shadowmapSize; - uint m_parameterOffset; - uint m_parameterCount; float m_lightDistanceOfCameraViewFrustum; float m_n_f_n; // n / (f - n) float m_n_f; // n - f float m_f; // f // where n: nearDepth, f: farDepth. - float2 m_padding; // explicit padding }; // Simple Point Lights 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 - -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; +#include - 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/Reflections/ReflectionComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl index 806337499c..207da9e857 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl @@ -15,7 +15,6 @@ // box-filtered from the MSAA sub-pixels of the reflection texture. #include -#include #include #include @@ -26,8 +25,6 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2DMS m_reflection; } -#include - // Vertex Shader VSOutput MainVS(VSInput input) { 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/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 60614993b5..cf456b2e41 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -286,7 +286,6 @@ set(FILES ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli - ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli ShaderLib/Atom/Features/Shadow/Shadow.azsli ShaderLib/Atom/Features/Shadow/ShadowmapAtlasLib.azsli diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h index 75d266cc52..769c7b95a6 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h @@ -154,12 +154,6 @@ namespace AZ //! @param count Sample Count for filtering (up to 64) virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0; - //! This specifies the width of boundary between shadowed area and lit area. - //! @param handle the light handle. - //! @param width Boundary width. The shadow is gradually changed the degree of shadowed. - //! If width == 0, softening edge is disabled. Units are in meters. - virtual void SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) = 0; - //! Sets whether the directional shadowmap should use receiver plane bias. //! This attempts to reduce shadow acne when using large pcf filters. virtual void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) = 0; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h index 3ab83200ae..5aa2dfb800 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h @@ -90,8 +90,6 @@ namespace AZ virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0; //! Specifies filter method of shadows. virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0; - //! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on the boundary. 0 disables softening. - virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0; //! Sets sample count for filtering of shadow boundary (up to 64) virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0; //! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h index 6752ac4c52..1a5a776cdf 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h @@ -70,9 +70,6 @@ namespace AZ virtual void SetShadowBias(LightHandle handle, float bias) = 0; //! Specifies filter method of shadows. virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0; - //! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on - //! the boundary. 0 disables softening. - virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0; //! Sets sample count for filtering of shadow boundary (up to 64) virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0; //! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h index dbad3af21f..309331dcf5 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/ShadowConstants.h @@ -42,7 +42,6 @@ namespace AZ // [GFX TODO][ATOM-2408] Make the max number of cascade modifiable at runtime. static constexpr uint16_t MaxNumberOfCascades = 4; static constexpr uint16_t MaxPcfSamplingCount = 64; - static constexpr float MaxSofteningBoundaryWidth = 0.1f; } // namespace Shadow } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h index 3d6c0c3015..46560f435d 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h @@ -54,8 +54,6 @@ namespace AZ::Render virtual void SetShadowBias(ShadowId id, float bias) = 0; //! Sets the shadow filter method virtual void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) = 0; - //! Sets the width of boundary between shadowed area and lit area. - virtual void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) = 0; //! Sets the sample count for filtering of the shadow boundary, max 64. virtual void SetFilteringSampleCount(ShadowId id, uint16_t count) = 0; //! Sets all of the shadow properites in one call 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/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 42cca0e57c..0a9f3480ad 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -584,15 +584,6 @@ namespace AZ m_shadowBufferNeedsUpdate = true; } - void DirectionalLightFeatureProcessor::SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) - { - for (auto& it : m_shadowData) - { - it.second.GetData(handle.GetIndex()).m_boundaryScale = boundaryWidth / 2.f; - } - m_shadowBufferNeedsUpdate = true; - } - void DirectionalLightFeatureProcessor::SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) { m_shadowProperties.GetData(handle.GetIndex()).m_isReceiverPlaneBiasEnabled = enable; @@ -1116,50 +1107,13 @@ namespace AZ for (const auto& passIt : m_esmShadowmapsPasses) { const RPI::View* cameraView = passIt.second.front()->GetRenderPipeline()->GetDefaultView().get(); - UpdateStandardDeviations(handle, cameraView); - UpdateFilterOffsetsCounts(handle, cameraView); + UpdateFilterEnabled(handle, cameraView); UpdateShadowmapPositionInAtlas(handle, cameraView); SetFilterParameterToPass(handle, cameraView); } } - void DirectionalLightFeatureProcessor::UpdateStandardDeviations(LightHandle handle, const RPI::View* cameraView) - { - if (handle != m_shadowingLightHandle) - { - return; - } - - const DirectionalLightShadowData& data = m_shadowData.at(cameraView).GetData(handle.GetIndex()); - const ShadowProperty& property = m_shadowProperties.GetData(handle.GetIndex()); - AZStd::fixed_vector standardDeviations; - for (size_t cascadeIndex = 0; cascadeIndex < property.m_segments.at(cameraView).size(); ++cascadeIndex) - { - const Aabb& aabb = property.m_segments.at(cameraView)[cascadeIndex].m_aabb; - const float aabbDiameter = AZStd::GetMax( - aabb.GetMax().GetX() - aabb.GetMin().GetX(), - aabb.GetMax().GetZ() - aabb.GetMin().GetZ()); - float standardDeviation = 0.f; - if (aabbDiameter > 0.f) - { - const float boundaryWidth = data.m_boundaryScale * 2.f; - const float ratioToAabbWidth = boundaryWidth / aabbDiameter; - const float widthInPixels = ratioToAabbWidth * data.m_shadowmapSize; - standardDeviation = widthInPixels / (2 * GaussianMathFilter::ReliableSectionFactor); - } - standardDeviations.push_back(standardDeviation); - } - - for (const RPI::RenderPipelineId& pipelineId : m_renderPipelineIdsForPersistentView.at(cameraView)) - { - for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses.at(pipelineId)) - { - esmPass->SetFilterParameters(standardDeviations); - } - } - } - - void DirectionalLightFeatureProcessor::UpdateFilterOffsetsCounts(LightHandle handle, const RPI::View* cameraView) + void DirectionalLightFeatureProcessor::UpdateFilterEnabled(LightHandle handle, const RPI::View* cameraView) { if (handle != m_shadowingLightHandle) { @@ -1170,29 +1124,11 @@ namespace AZ if (shadowData.m_shadowFilterMethod == aznumeric_cast(ShadowFilterMethod::Esm) || (shadowData.m_shadowFilterMethod == aznumeric_cast(ShadowFilterMethod::EsmPcf))) { - // Get array of filter counts for the camera view. - const RPI::RenderPipelineId& pipelineId = m_renderPipelineIdsForPersistentView.at(cameraView).front(); - AZ_Assert(!m_esmShadowmapsPasses.at(pipelineId).empty(), "Cannot find a EsmShadowmapsPass."); - const AZStd::array_view filterCounts = m_esmShadowmapsPasses.at(pipelineId).front()->GetFilterCounts(); - AZ_Assert(filterCounts.size() == GetCascadeCount(handle), "FilterCounts differs with cascade count."); - - // Create array of filter offsets - AZStd::vector filterOffsets; - filterOffsets.reserve(filterCounts.size()); - uint32_t filterOffset = 0; - for (const uint32_t count : filterCounts) - { - filterOffsets.push_back(filterOffset); - filterOffset += count; - } - // Write filter offsets and filter counts to ESM data for (uint16_t index = 0; index < GetCascadeCount(handle); ++index) { EsmShadowmapsPass::FilterParameter& filterParameter = m_esmParameterData.at(cameraView).GetData(index); filterParameter.m_isEnabled = true; - filterParameter.m_parameterOffset = filterOffsets[index]; - filterParameter.m_parameterCount = filterCounts[index]; } } else @@ -1202,8 +1138,6 @@ namespace AZ { EsmShadowmapsPass::FilterParameter& filterParameter = m_esmParameterData.at(cameraView).GetData(index); filterParameter.m_isEnabled = false; - filterParameter.m_parameterOffset = 0; - filterParameter.m_parameterCount = 0; } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h index 039f51d549..3c1ff8eabd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h @@ -217,7 +217,6 @@ namespace AZ void SetDebugFlags(LightHandle handle, DebugDrawFlags flags) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; - void SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) override; void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) override; const Data::Instance GetLightBuffer() const; @@ -278,10 +277,8 @@ namespace AZ //! This updates the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(LightHandle handle); - //! This updates standard deviations for each cascade. - void UpdateStandardDeviations(LightHandle handle, const RPI::View* cameraView); - //! This updates filter offset and size for each cascade. - void UpdateFilterOffsetsCounts(LightHandle handle, const RPI::View* cameraView); + //! This updates if the filter is enabled. + void UpdateFilterEnabled(LightHandle handle, const RPI::View* cameraView); //! This updates shadowmap position(origin and size) in the atlas for each cascade. void UpdateShadowmapPositionInAtlas(LightHandle handle, const RPI::View* cameraView); //! This set filter parameters to passes which execute filtering. diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp index 26e1757a5a..acf81ede32 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp @@ -322,11 +322,6 @@ namespace AZ { SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method); } - - void DiskLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) - { - SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians); - } void DiskLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h index d65f587718..275712f84f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h @@ -53,7 +53,6 @@ namespace AZ void SetShadowBias(LightHandle handle, float bias) override; void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; void SetEsmExponent(LightHandle handle, float esmExponent) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp index 99eaa8a919..b92d538fb5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp @@ -42,28 +42,6 @@ namespace AZ return m_lightTypeName; } - void EsmShadowmapsPass::SetFilterParameters(const AZStd::array_view& standardDeviations) - { - // Set descriptor for Gaussian filters for given set of standard deviations. - MathFilterDescriptor descriptor; - descriptor.m_kind = MathFilterKind::Gaussian; - descriptor.m_gaussians.reserve(standardDeviations.size()); - for (const float standardDeviation : standardDeviations) - { - descriptor.m_gaussians.emplace_back(GaussianFilterDescriptor{ standardDeviation }); - } - - // Set filter paramter buffer along with element counts for each filter. - MathFilter::BufferWithElementCounts bufferCounts = MathFilter::FindOrCreateFilterBuffer(descriptor); - m_filterTableBuffer = bufferCounts.first; - m_filterCounts = AZStd::move(bufferCounts.second); - } - - AZStd::array_view EsmShadowmapsPass::GetFilterCounts() const - { - return m_filterCounts; - } - void EsmShadowmapsPass::SetShadowmapIndexTableBuffer(const Data::Instance& tableBuffer) { m_shadowmapIndexTableBuffer = tableBuffer; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h index 6e5e1aa311..5a9898d507 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.h @@ -50,14 +50,11 @@ namespace AZ uint32_t m_isEnabled = false; AZStd::array m_shadowmapOriginInSlice = { {0, 0 } }; // shadowmap origin in the slice of the atlas. uint32_t m_shadowmapSize = static_cast(ShadowmapSize::None); // width and height of shadowmap. - uint32_t m_parameterOffset; // offset of the filter parameter. - uint32_t m_parameterCount; // element count of the filter parameter. float m_lightDistanceOfCameraViewFrustum = 0.f; float m_n_f_n = 0.f; // n / (f - n) float m_n_f = 0.f; // n - f float m_f = 0.f; // f // where n: nearDepth, f: farDepth. - AZStd::array m_padding = {{0.f, 0.f}}; // explicit padding }; virtual ~EsmShadowmapsPass() = default; @@ -65,13 +62,6 @@ namespace AZ const Name& GetLightTypeName() const; - //! This sets the standard deviations of the Gaussian filter - //! for each cascade. - void SetFilterParameters(const AZStd::array_view& standardDeviations); - - //! This returns element count of filters. - AZStd::array_view GetFilterCounts() const; - //! This sets the buffer of the table which enable to get shadowmap index //! from the coordinate in the atlas. //! Note that shadowmpa index is shader light index for a spot light diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index d3b5646e0b..dcf412c35d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -292,11 +292,6 @@ namespace AZ SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method); } - void PointLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) - { - SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians); - } - void PointLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count) { SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetFilteringSampleCount, count); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h index b784eb1bb5..54cb0303cc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h @@ -50,7 +50,6 @@ namespace AZ void SetShadowBias(LightHandle handle, float bias) override; void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override; void SetFilteringSampleCount(LightHandle handle, uint16_t count) override; void SetEsmExponent(LightHandle handle, float esmExponent) override; void SetPointData(LightHandle handle, const PointLightData& data) override; 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/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp index 69a4ecdee5..cf1897054a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp @@ -54,27 +54,10 @@ namespace AZ m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass); // retrieve the number of threads per thread group from the shader - const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" }); - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs); + if (!outcome.IsSuccess()) { - const RHI::ShaderStageAttributeArguments& args = *numThreads; - bool validArgs = args.size() == 3; - if (validArgs) - { - validArgs &= args[0].type() == azrtti_typeid(); - validArgs &= args[1].type() == azrtti_typeid(); - validArgs &= args[2].type() == azrtti_typeid(); - } - - if (!validArgs) - { - AZ_Error("PassSystem", false, "[DiffuseProbeGridBlendDistancePass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str()); - return; - } - - m_dispatchArgs.m_threadsPerGroupX = static_cast(AZStd::any_cast(args[0])); - m_dispatchArgs.m_threadsPerGroupY = static_cast(AZStd::any_cast(args[1])); - m_dispatchArgs.m_threadsPerGroupZ = static_cast(AZStd::any_cast(args[2])); + AZ_Error("PassSystem", false, "[DiffuseProbeGridBlendDistancePass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str()); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp index 83ef312bf4..f4733c833a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp @@ -54,27 +54,10 @@ namespace AZ m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass); // retrieve the number of threads per thread group from the shader - const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" }); - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs); + if (!outcome.IsSuccess()) { - const RHI::ShaderStageAttributeArguments& args = *numThreads; - bool validArgs = args.size() == 3; - if (validArgs) - { - validArgs &= args[0].type() == azrtti_typeid(); - validArgs &= args[1].type() == azrtti_typeid(); - validArgs &= args[2].type() == azrtti_typeid(); - } - - if (!validArgs) - { - AZ_Error("PassSystem", false, "[DiffuseProbeBlendIrradiancePass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str()); - return; - } - - m_dispatchArgs.m_threadsPerGroupX = static_cast(AZStd::any_cast(args[0])); - m_dispatchArgs.m_threadsPerGroupY = static_cast(AZStd::any_cast(args[1])); - m_dispatchArgs.m_threadsPerGroupZ = static_cast(AZStd::any_cast(args[2])); + AZ_Error("PassSystem", false, "[DiffuseProbeBlendIrradiancePass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str()); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp index b251526cb4..6c72f904b9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp @@ -67,27 +67,10 @@ namespace AZ srgLayout = shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass); // retrieve the number of threads per thread group from the shader - const auto numThreads = shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" }); - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(shader->GetAsset(), dispatchArgs); + if (!outcome.IsSuccess()) { - const RHI::ShaderStageAttributeArguments& args = *numThreads; - bool validArgs = args.size() == 3; - if (validArgs) - { - validArgs &= args[0].type() == azrtti_typeid(); - validArgs &= args[1].type() == azrtti_typeid(); - validArgs &= args[2].type() == azrtti_typeid(); - } - - if (!validArgs) - { - AZ_Error("PassSystem", false, "[DiffuseProbeGridBorderUpdatePass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str()); - return; - } - - dispatchArgs.m_threadsPerGroupX = static_cast(AZStd::any_cast(args[0])); - dispatchArgs.m_threadsPerGroupY = static_cast(AZStd::any_cast(args[1])); - dispatchArgs.m_threadsPerGroupZ = static_cast(AZStd::any_cast(args[2])); + AZ_Error("PassSystem", false, "[DiffuseProbeGridBorderUpdatePass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str()); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp index 2690f90a7d..61540c3332 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp @@ -58,27 +58,10 @@ namespace AZ m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass); // retrieve the number of threads per thread group from the shader - const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" }); - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs); + if (!outcome.IsSuccess()) { - const RHI::ShaderStageAttributeArguments& args = *numThreads; - bool validArgs = args.size() == 3; - if (validArgs) - { - validArgs &= args[0].type() == azrtti_typeid(); - validArgs &= args[1].type() == azrtti_typeid(); - validArgs &= args[2].type() == azrtti_typeid(); - } - - if (!validArgs) - { - AZ_Error("PassSystem", false, "[DiffuseProbeClassificationPass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str()); - return; - } - - m_dispatchArgs.m_threadsPerGroupX = static_cast(AZStd::any_cast(args[0])); - m_dispatchArgs.m_threadsPerGroupY = static_cast(AZStd::any_cast(args[1])); - m_dispatchArgs.m_threadsPerGroupZ = static_cast(AZStd::any_cast(args[2])); + AZ_Error("PassSystem", false, "[DiffuseProbeClassificationPass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str()); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp index 67fb95a833..a1b236ed4d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp @@ -58,27 +58,10 @@ namespace AZ m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass); // retrieve the number of threads per thread group from the shader - const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" }); - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs); + if (!outcome.IsSuccess()) { - const RHI::ShaderStageAttributeArguments& args = *numThreads; - bool validArgs = args.size() == 3; - if (validArgs) - { - validArgs &= args[0].type() == azrtti_typeid(); - validArgs &= args[1].type() == azrtti_typeid(); - validArgs &= args[2].type() == azrtti_typeid(); - } - - if (!validArgs) - { - AZ_Error("PassSystem", false, "[DiffuseProbeRelocationPass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str()); - return; - } - - m_dispatchArgs.m_threadsPerGroupX = static_cast(AZStd::any_cast(args[0])); - m_dispatchArgs.m_threadsPerGroupY = static_cast(AZStd::any_cast(args[1])); - m_dispatchArgs.m_threadsPerGroupZ = static_cast(AZStd::any_cast(args[2])); + AZ_Error("PassSystem", false, "[DiffuseProbeRelocationPass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str()); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 51e18b8e31..cdcc07b545 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -54,10 +54,14 @@ namespace AZ { AZStd::shared_ptr> buffer = readbackResult.m_dataBuffer; + RHI::Format format = readbackResult.m_imageDescriptor.m_format; + // convert bgra to rgba by swapping channels const int numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format); - if (readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + if (format == RHI::Format::B8G8R8A8_UNORM) { + format = RHI::Format::R8G8B8A8_UNORM; + buffer = AZStd::make_shared>(readbackResult.m_dataBuffer->size()); AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin()); @@ -89,7 +93,7 @@ namespace AZ jobCompletion.StartAndWaitForCompletion(); } - Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, readbackResult.m_imageDescriptor.m_format, *buffer); + Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, format, *buffer); Utils::PngFile::SaveSettings saveSettings; saveSettings.m_compressionLevel = r_pngCompressionLevel; diff --git a/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetDispatchItem.cpp b/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetDispatchItem.cpp index 19f379b17d..fcf1402ae6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetDispatchItem.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetDispatchItem.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -79,15 +80,11 @@ namespace AZ m_dispatchItem.m_pipelineState = m_morphTargetShader->AcquirePipelineState(pipelineStateDescriptor); // Get the threads-per-group values from the compute shader [numthreads(x,y,z)] - const auto& numThreads = m_morphTargetShader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, AZ::Name{ "numthreads" }); auto& arguments = m_dispatchItem.m_arguments.m_direct; - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_morphTargetShader->GetAsset(), arguments); + if (!outcome.IsSuccess()) { - const auto& args = *numThreads; - // Check that the arguments are valid integers, and fall back to 1,1,1 if there is an error - arguments.m_threadsPerGroupX = static_cast(args[0].type() == azrtti_typeid() ? AZStd::any_cast(args[0]) : 1); - arguments.m_threadsPerGroupY = static_cast(args[1].type() == azrtti_typeid() ? AZStd::any_cast(args[1]) : 1); - arguments.m_threadsPerGroupZ = static_cast(args[2].type() == azrtti_typeid() ? AZStd::any_cast(args[2]) : 1); + AZ_Error("MorphTargetDispatchItem", false, outcome.GetError().c_str()); } arguments.m_totalNumberOfThreadsX = m_morphTargetMetaData.m_vertexCount; 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..af53fc3ce1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -507,8 +507,14 @@ 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. + // Look into making 'm_meshBuffers != meshBuffers' faster by possibly building a crc and doing a crc check. + 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 +560,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..da92cc04e7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -186,17 +186,6 @@ namespace AZ::Render m_filterParameterNeedsUpdate = true; } - void ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) - { - AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBoundaryWidthAngle()."); - - ShadowData& shadowData = m_shadowData.GetElement(id.GetIndex()); - shadowData.m_boundaryScale = boundaryWidthRadians / 2.0f; - - m_shadowmapPassNeedsUpdate = true; - m_filterParameterNeedsUpdate = true; - } - void ProjectedShadowFeatureProcessor::SetFilteringSampleCount(ShadowId id, uint16_t count) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFilteringSampleCount()."); @@ -346,7 +335,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")); @@ -368,14 +357,13 @@ namespace AZ::Render { if (m_filterParameterNeedsUpdate) { - UpdateStandardDeviations(); - UpdateFilterOffsetsCounts(); + UpdateEsmPassEnabled(); SetFilterParameterToPass(); m_filterParameterNeedsUpdate = false; } } - void ProjectedShadowFeatureProcessor::UpdateStandardDeviations() + void ProjectedShadowFeatureProcessor::UpdateEsmPassEnabled() { if (m_esmShadowmapsPasses.empty()) { @@ -383,24 +371,7 @@ namespace AZ::Render return; } - AZStd::vector standardDeviations(m_shadowProperties.GetDataCount()); - - for (uint32_t i = 0; i < m_shadowProperties.GetDataCount(); ++i) - { - ShadowProperty& shadowProperty = m_shadowProperties.GetDataVector().at(i); - const ShadowData& shadow = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); - if (!FilterMethodIsEsm(shadow)) - { - continue; - } - const FilterParameter& filter = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); - const float boundaryWidthAngle = shadow.m_boundaryScale * 2.0f; - const float fieldOfView = GetMax(shadowProperty.m_desc.m_fieldOfViewYRadians, MinimumFieldOfView); - const float ratioToEntireWidth = boundaryWidthAngle / fieldOfView; - const float widthInPixels = ratioToEntireWidth * filter.m_shadowmapSize; - standardDeviations.at(i) = widthInPixels / (2.0f * GaussianMathFilter::ReliableSectionFactor); - } - if (standardDeviations.empty()) + if (m_shadowProperties.GetDataCount() == 0) { for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { @@ -411,50 +382,6 @@ namespace AZ::Render for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { esmPass->SetEnabledComputation(true); - esmPass->SetFilterParameters(standardDeviations); - } - } - - void ProjectedShadowFeatureProcessor::UpdateFilterOffsetsCounts() - { - if (m_esmShadowmapsPasses.empty()) - { - AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass."); - return; - } - - // Get array of filter counts for the camera view. - const AZStd::array_view filterCounts = m_esmShadowmapsPasses.front()->GetFilterCounts(); - - // Create array of filter offsets. - AZStd::vector filterOffsets; - filterOffsets.reserve(filterCounts.size()); - uint32_t filterOffset = 0; - for (const uint32_t count : filterCounts) - { - filterOffsets.push_back(filterOffset); - filterOffset += count; - } - - auto& shadowProperties = m_shadowProperties.GetDataVector(); - for (uint32_t i = 0; i < shadowProperties.size(); ++i) - { - ShadowProperty& shadowProperty = shadowProperties.at(i); - const ShadowId shadowId = shadowProperty.m_shadowId; - ShadowData& shadowData = m_shadowData.GetElement(shadowId.GetIndex()); - FilterParameter& filterData = m_shadowData.GetElement(shadowId.GetIndex()); - - if (FilterMethodIsEsm(shadowData)) - { - filterData.m_parameterOffset = filterOffsets[i]; - filterData.m_parameterCount = filterCounts[i]; - } - else - { - // If filter is not required, reset offsets and counts of filter in ESM data. - filterData.m_parameterOffset = 0; - filterData.m_parameterCount = 0; - } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index 0b266b9a40..6269166827 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -49,7 +49,6 @@ namespace AZ::Render void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override; void SetShadowBias(ShadowId id, float bias) override; void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) override; void SetFilteringSampleCount(ShadowId id, uint16_t count) override; void SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor) override; const ProjectedShadowDescriptor& GetShadowProperties(ShadowId id) override; @@ -101,8 +100,7 @@ namespace AZ::Render //! Functions to update the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(); - void UpdateStandardDeviations(); - void UpdateFilterOffsetsCounts(); + void UpdateEsmPassEnabled(); void SetFilterParameterToPass(); bool FilterMethodIsEsm(const ShadowData& shadowData) const; diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshDispatchItem.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshDispatchItem.cpp index bfc533763e..5ec26cebde 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshDispatchItem.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshDispatchItem.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -199,17 +200,14 @@ namespace AZ m_instanceSrg->Compile(); m_dispatchItem.m_uniqueShaderResourceGroup = m_instanceSrg->GetRHIShaderResourceGroup(); m_dispatchItem.m_pipelineState = m_skinningShader->AcquirePipelineState(pipelineStateDescriptor); - - const auto& numThreads = m_skinningShader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, AZ::Name{ "numthreads" }); + auto& arguments = m_dispatchItem.m_arguments.m_direct; - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_skinningShader->GetAsset(), arguments); + if (!outcome.IsSuccess()) { - const auto& args = *numThreads; - arguments.m_threadsPerGroupX = static_cast(args[0].type() == azrtti_typeid() ? AZStd::any_cast(args[0]) : 1); - arguments.m_threadsPerGroupY = static_cast(args[1].type() == azrtti_typeid() ? AZStd::any_cast(args[1]) : 1); - arguments.m_threadsPerGroupZ = static_cast(args[2].type() == azrtti_typeid() ? AZStd::any_cast(args[2]) : 1); + AZ_Error("SkinnedMeshInputBuffers", false, outcome.GetError().c_str()); } - + arguments.m_totalNumberOfThreadsX = xThreads; arguments.m_totalNumberOfThreadsY = yThreads; arguments.m_totalNumberOfThreadsZ = 1; 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/3rdParty/Findrenderdoc.cmake b/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake index f8f17392b3..b95a246afe 100644 --- a/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake +++ b/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake @@ -10,6 +10,5 @@ ly_add_external_target( NAME renderdoc 3RDPARTY_ROOT_DIRECTORY "${LY_RENDERDOC_PATH}" VERSION - INCLUDE_DIRECTORIES . COMPILE_DEFINITIONS USE_RENDERDOC ) diff --git a/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake b/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake index a74d250901..5e88fcec2f 100644 --- a/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake +++ b/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake @@ -6,4 +6,5 @@ # # -set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/librenderdoc.so") +set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/lib/librenderdoc.so") +set(RENDERDOC_INCLUDE_DIRECTORIES "include") diff --git a/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake b/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake index 559863ca07..70c8564a82 100644 --- a/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake +++ b/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake @@ -7,3 +7,4 @@ # set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/renderdoc.dll") +set(RENDERDOC_INCLUDE_DIRECTORIES ".") 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 0ce43159f6..ef4e734c01 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h @@ -166,6 +166,10 @@ namespace AZ AZStd::array_view> GetImageGroup() const; AZStd::array_view> GetBufferGroup() const; AZStd::array_view GetSamplerGroup() const; + + //! Reset image and buffer views setup for this ShaderResourceGroupData + //! So it won't hold references for any RHI resources + void ResetViews(); //! Returns the opaque constant data populated by calls to SetConstant and SetConstantData. //! @@ -179,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; @@ -203,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); } @@ -241,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/FrameGraph.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp index 9f3d21a2f1..e4e1a887b0 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp @@ -109,13 +109,11 @@ namespace AZ { if (attachment->GetFirstScopeAttachment() == nullptr) { + //We allow the rendering to continue even if an attachment is not used. AZ_Error( "FrameGraph", false, "Invalid State: attachment '%s' was added but never used!", attachment->GetId().GetCStr()); - - Clear(); - 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 da7697d8f2..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); } @@ -330,6 +360,14 @@ namespace AZ return m_samplers; } + void ShaderResourceGroupData::ResetViews() + { + m_imageViews.assign(m_imageViews.size(), nullptr); + m_bufferViews.assign(m_bufferViews.size(), nullptr); + m_imageViewsUnboundedArray.assign(m_imageViewsUnboundedArray.size(), nullptr); + m_bufferViewsUnboundedArray.assign(m_bufferViewsUnboundedArray.size(), nullptr); + } + AZStd::array_view ShaderResourceGroupData::GetConstantData() const { return m_constantsData.GetConstantData(); @@ -340,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/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupPool.cpp index 70f08dec1e..eb80b038eb 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -111,13 +111,19 @@ namespace AZ { AZStd::lock_guard lock(m_groupsToCompileMutex); - AZ_Assert(!shaderResourceGroup.IsQueuedForCompile(), "Attempting to compile an SRG that's already been queued for compile. Only compile an SRG once per frame."); + bool isQueuedForCompile = shaderResourceGroup.IsQueuedForCompile(); + AZ_Warning( + "ShaderResourceGroupPool", !isQueuedForCompile, + "Attempting to compile an SRG that's already been queued for compile. Only compile an SRG once per frame."); - CalculateGroupDataDiff(shaderResourceGroup, groupData); + if (!isQueuedForCompile) + { + CalculateGroupDataDiff(shaderResourceGroup, groupData); - shaderResourceGroup.SetData(groupData); + shaderResourceGroup.SetData(groupData); - QueueForCompileNoLock(shaderResourceGroup); + QueueForCompileNoLock(shaderResourceGroup); + } } void ShaderResourceGroupPool::QueueForCompile(ShaderResourceGroup& group) 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/Tests/UtilsTests.cpp b/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp index 8495e9b119..27336c8280 100644 --- a/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp +++ b/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp @@ -28,7 +28,7 @@ namespace UnitTest m_application.reset(); } - static constexpr const char TestDataFolder[] = "@devroot@/Gems/Atom/RHI/Code/Tests/UtilsTestsData/"; + static constexpr const char TestDataFolder[] = "@engroot@/Gems/Atom/RHI/Code/Tests/UtilsTestsData/"; AZStd::unique_ptr m_application; }; 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/BufferMemoryPageAllocator.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemoryPageAllocator.cpp index 07b18266f5..a1025e5501 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemoryPageAllocator.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemoryPageAllocator.cpp @@ -55,7 +55,7 @@ namespace AZ RHI::Ptr bufferMemory; const VkMemoryPropertyFlags flags = ConvertHeapMemoryLevel(m_descriptor.m_heapMemoryLevel) | m_descriptor.m_additionalMemoryPropertyFlags; - RHI::Ptr memory = GetDevice().AllocateMemory(memoryRequirements.size, memoryRequirements.memoryTypeBits, flags); + RHI::Ptr memory = GetDevice().AllocateMemory(memoryRequirements.size, memoryRequirements.memoryTypeBits, flags, m_descriptor.m_bindFlags); if (memory) { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp index d90f1c33d5..2620aa5f7b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp @@ -822,7 +822,7 @@ namespace AZ { RHI::ConstPtr shaderResourceGroup; const auto& srgBitset = pipelineLayout.GetAZSLBindingSlotsOfIndex(index); - AZStd::vector shaderResourceGroupList; + AZStd::fixed_vector shaderResourceGroupList; // Collect all the SRGs that are part of this descriptor set. They could be more than // 1, so we would need to merge their values before committing the descriptor set. for (uint32_t bindingSlot = 0; bindingSlot < srgBitset.size(); ++bindingSlot) 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/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index d5671cff05..3294b77eaa 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -696,8 +696,7 @@ namespace AZ usageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; } if (RHI::CheckBitsAny(bindFlags, BindFlags::Constant)) @@ -742,12 +741,24 @@ namespace AZ if (RHI::CheckBitsAny(bindFlags, BindFlags::RayTracingShaderTable)) { - usageFlags |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + usageFlags |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR; + } + + if (ShouldApplyDeviceAddressBit(bindFlags)) + { + usageFlags |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; } return usageFlags; } + bool ShouldApplyDeviceAddressBit(RHI::BufferBindFlags bindFlags) + { + return RHI::CheckBitsAny( + bindFlags, + RHI::BufferBindFlags::InputAssembly | RHI::BufferBindFlags::DynamicInputAssembly | RHI::BufferBindFlags::RayTracingShaderTable); + } + VkPipelineStageFlags GetSupportedPipelineStages(RHI::PipelineStateType type) { // These stages don't need any special queue to be supported. diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.h index 873b8ec2d1..b82b1a2f7c 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.h @@ -82,5 +82,6 @@ namespace AZ VkImageUsageFlags ImageUsageFlagsOfFormatFeatureFlags(VkFormatFeatureFlags formatFeatureFlags); VkAccessFlags GetSupportedAccessFlags(VkPipelineStageFlags pipelineStageFlags); + bool ShouldApplyDeviceAddressBit(RHI::BufferBindFlags bindFlags); } } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp index 35bc966d1d..14b6c01498 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp @@ -185,6 +185,9 @@ namespace AZ VkPhysicalDeviceShaderFloat16Int8FeaturesKHR float16Int8 = {}; VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR separateDepthStencil = {}; + VkDeviceCreateInfo deviceInfo = {}; + deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + // If we are running Vulkan >= 1.2, then we must use VkPhysicalDeviceVulkan12Features instead // of VkPhysicalDeviceShaderFloat16Int8FeaturesKHR or VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR. if (majorVersion >= 1 && minorVersion >= 2) @@ -194,7 +197,14 @@ namespace AZ vulkan12Features.shaderFloat16 = physicalDevice.GetPhysicalDeviceVulkan12Features().shaderFloat16; vulkan12Features.shaderInt8 = physicalDevice.GetPhysicalDeviceVulkan12Features().shaderInt8; vulkan12Features.separateDepthStencilLayouts = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts; + vulkan12Features.descriptorBindingPartiallyBound = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts; + vulkan12Features.descriptorIndexing = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts; + vulkan12Features.descriptorBindingVariableDescriptorCount = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts; + vulkan12Features.bufferDeviceAddress = physicalDevice.GetPhysicalDeviceVulkan12Features().bufferDeviceAddress; + vulkan12Features.bufferDeviceAddressMultiDevice = physicalDevice.GetPhysicalDeviceVulkan12Features().bufferDeviceAddressMultiDevice; + vulkan12Features.runtimeDescriptorArray = physicalDevice.GetPhysicalDeviceVulkan12Features().runtimeDescriptorArray; robustness2.pNext = &vulkan12Features; + deviceInfo.pNext = &depthClipEnabled; } else { @@ -206,11 +216,11 @@ namespace AZ float16Int8.pNext = &separateDepthStencil; robustness2.pNext = &float16Int8; + + + deviceInfo.pNext = &descriptorIndexingFeatures; } - VkDeviceCreateInfo deviceInfo = {}; - deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceInfo.pNext = &descriptorIndexingFeatures; deviceInfo.flags = 0; deviceInfo.queueCreateInfoCount = static_cast(queueCreationInfo.size()); deviceInfo.pQueueCreateInfos = queueCreationInfo.data(); @@ -547,9 +557,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 @@ -740,7 +750,7 @@ namespace AZ vkGetPhysicalDeviceQueueFamilyProperties(nativePhysicalDevice, &queueFamilyCount, m_queueFamilyProperties.data()); } - RHI::Ptr Device::AllocateMemory(uint64_t sizeInBytes, const uint32_t memoryTypeMask, const VkMemoryPropertyFlags flags) + RHI::Ptr Device::AllocateMemory(uint64_t sizeInBytes, const uint32_t memoryTypeMask, const VkMemoryPropertyFlags flags, const RHI::BufferBindFlags bufferBindFlags) { const auto& physicalDevice = static_cast(GetPhysicalDevice()); const VkPhysicalDeviceMemoryProperties& memProp = physicalDevice.GetMemoryProperties(); @@ -770,6 +780,7 @@ namespace AZ RHI::CheckBitsAll(memoryTypesToUseMask, memoryTypeBit)) { memoryDesc.m_memoryTypeIndex = memoryIndex; + memoryDesc.m_bufferBindFlags = bufferBindFlags; auto result = memory->Init(*this, memoryDesc); if (result == RHI::ResultCode::Success) { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h index 13ccf9367b..44c1e20a06 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h @@ -100,7 +100,11 @@ namespace AZ RHI::Ptr AcquireCommandList(uint32_t familyQueueIndex, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY); RHI::Ptr AcquireCommandList(RHI::HardwareQueueClass queueClass, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY); - RHI::Ptr AllocateMemory(uint64_t sizeInBytes, const uint32_t memoryTypeMask, const VkMemoryPropertyFlags flags); + RHI::Ptr AllocateMemory( + uint64_t sizeInBytes, + const uint32_t memoryTypeMask, + const VkMemoryPropertyFlags flags, + const RHI::BufferBindFlags bufferBindFlags = RHI::BufferBindFlags::None); uint32_t GetCurrentFrameIndex() const; @@ -126,7 +130,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/FrameGraphExecuteGroupMerged.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMerged.cpp index 5314fc2aa1..84b6cafa7d 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMerged.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMerged.cpp @@ -59,7 +59,9 @@ namespace AZ void FrameGraphExecuteGroupMerged::BeginInternal() { + m_commandList = AcquireCommandList(VK_COMMAND_BUFFER_LEVEL_PRIMARY); m_commandList->BeginCommandBuffer(); + m_workRequest.m_commandList = m_commandList; } void FrameGraphExecuteGroupMerged::EndInternal() diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMergedHandler.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMergedHandler.cpp index 90e6d63044..1bcdd17ab6 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMergedHandler.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuteGroupMergedHandler.cpp @@ -41,9 +41,7 @@ namespace AZ RETURN_RESULT_IF_UNSUCCESSFUL(result); } - // Set the command list and renderpass contexts. - m_primaryCommandList = device.AcquireCommandList(m_hardwareQueueClass); - group->SetPrimaryCommandList(*m_primaryCommandList); + // Set the renderpass contexts. group->SetRenderPasscontexts(m_renderPassContexts); return RHI::ResultCode::Success; @@ -54,7 +52,8 @@ namespace AZ AZ_Assert(m_executeGroups.size() == 1, "Too many execute groups when initializing context"); FrameGraphExecuteGroupBase* group = static_cast(m_executeGroups.back()); AddWorkRequest(group->GetWorkRequest()); - m_workRequest.m_commandList = m_primaryCommandList; + //Merged handler will only have one commandlist. + m_workRequest.m_commandList = group->GetCommandLists()[0]; } } } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp index e91b158629..7165b5c305 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.cpp @@ -31,7 +31,12 @@ namespace AZ { return static_cast(Base::GetDevice()); } - + + FrameGraphExecuter::FrameGraphExecuter() + { + SetJobPolicy(RHI::JobPolicy::Parallel); + } + RHI::ResultCode FrameGraphExecuter::InitInternal(const RHI::FrameGraphExecuterDescriptor& descriptor) { const RHI::ConstPtr rhiPlatformLimitsDescriptor = descriptor.m_platformLimitsDescriptor; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.h index 20f9f19a6a..8fc54ca0a6 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphExecuter.h @@ -35,6 +35,8 @@ namespace AZ Device& GetDevice() const; private: + FrameGraphExecuter(); + ////////////////////////////////////////////////////////////////////////// // RHI::FrameGraphExecuter RHI::ResultCode InitInternal(const RHI::FrameGraphExecuterDescriptor& descriptor) 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/Memory.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.cpp index a316a5eacb..bc10ba6dd9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.cpp @@ -7,6 +7,7 @@ */ #include #include +#include #include #include #include @@ -31,6 +32,15 @@ namespace AZ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = descriptor.m_sizeInBytes; allocInfo.memoryTypeIndex = descriptor.m_memoryTypeIndex; + + VkMemoryAllocateFlagsInfo memAllocInfo{}; + if (ShouldApplyDeviceAddressBit(descriptor.m_bufferBindFlags)) + { + memAllocInfo.flags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + } + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + + allocInfo.pNext = &memAllocInfo; VkDeviceMemory deviceMemory; VkResult vkResult = vkAllocateMemory(device.GetNativeDevice(), &allocInfo, nullptr, &deviceMemory); AZ_Error( diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.h index 56726a82f6..bfed64d3a0 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Memory.h @@ -37,6 +37,7 @@ namespace AZ { VkDeviceSize m_sizeInBytes = 0; uint32_t m_memoryTypeIndex = 0; + RHI::BufferBindFlags m_bufferBindFlags = RHI::BufferBindFlags::None; }; ~Memory() = default; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MergedShaderResourceGroupPool.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MergedShaderResourceGroupPool.h index b011ddcab4..7c21f21ce7 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MergedShaderResourceGroupPool.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MergedShaderResourceGroupPool.h @@ -38,7 +38,7 @@ namespace AZ static RHI::Ptr Create(); - using ShaderResourceGroupList = AZStd::vector; + using ShaderResourceGroupList = AZStd::fixed_vector; //! Finds or create a new instance of a MergedShaderResourceGroup. //! @param shaderResourceGroupList The list of ShaderResourceGroups that are being merged. MergedShaderResourceGroup* FindOrCreate(const ShaderResourceGroupList& shaderResourceGroupList); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp index c5ca07eb27..b2772d716e 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -105,6 +105,12 @@ 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(); @@ -116,15 +122,17 @@ namespace AZ 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) + for (uint32_t groupIndex = 0; groupIndex < static_cast(shaderImageList.size()); ++groupIndex) { const RHI::ShaderInputImageIndex index(groupIndex); auto imgViews = groupData.GetImageViewArray(index); - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageView); + 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) { @@ -139,9 +147,9 @@ namespace AZ 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) + for (uint32_t groupIndex = 0; groupIndex < static_cast(shaderImageUnboundeArrayList.size()); ++groupIndex) { const RHI::ShaderInputImageUnboundedArrayIndex index(groupIndex); auto imgViews = groupData.GetImageViewUnboundedArray(index); @@ -154,12 +162,13 @@ namespace AZ 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) { const RHI::ShaderInputSamplerIndex index(groupIndex); auto samplerArray = groupData.GetSamplerArray(index); - uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::Sampler); + uint32_t layoutIndex = + m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::Sampler); descriptorSet.UpdateSamplers(layoutIndex, samplerArray); } 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.Edit/Common/AssetUtils.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/AssetUtils.h index 341824a84d..bd11965ffa 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/AssetUtils.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/AssetUtils.h @@ -44,14 +44,14 @@ namespace AZ AZStd::string GetSourcePathByAssetId(const AZ::Data::AssetId& assetId); //! Tries to resolve a relative file reference, given the path of a referencing file. - //! @param originatingSourceFilePath Path to the parent file that references referencedSourceFilePath. May be absolute or relative to asset-root. + //! @param originatingSourceFilePath Path to the parent file that references referencedSourceFilePath. May be absolute or relative to asset-root. //! @param referencedSourceFilePath Path that the parent file references. May be relative to the parent file location or relative to asset-root. //! @return A full path for referencedSourceFilePath, if a full path was found. If a full path could not be constructed, returns referencedSourceFilePath unmodified. AZStd::string ResolvePathReference(const AZStd::string& originatingSourceFilePath, const AZStd::string& referencedSourceFilePath); - //! Returns the list of paths where a source asset file could possibly appear. + //! Returns the list of paths where a source asset file could possibly appear. //! This is intended for use by AssetBuilders when reporting dependencies, to support relative paths between source files. - //! When a source data file references another file using a relative path, the path might be relative to the originating + //! When a source data file references another file using a relative path, the path might be relative to the originating //! file or it might be a standard source asset path (i.e. relative to the logical asset-root). This function will help reporting //! dependencies on all possible locations where that file may appear at some point in the future. //! For example a file MyGem/Assets/Foo/a.json might reference another file as "Bar/b.json". In this case, calling @@ -94,9 +94,9 @@ namespace AZ template Outcome> LoadAsset(const AZ::Data::AssetId& assetId, [[maybe_unused]] const char* sourcePathForDebug) { - if (nullptr == AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@")) + if (nullptr == AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@")) { - // The absence of "@assets@" is not necessarily the reason LoadAsset() can't be used in CreateJobs(), but it + // The absence of "@products@" is not necessarily the reason LoadAsset() can't be used in CreateJobs(), but it // is a symptom of calling LoadAsset() from CreateJobs() which is not supported. AZ_Assert(false, "It appears AssetUtils::LoadAsset() is being called in CreateJobs(). It can only be used in ProcessJob()."); return AZ::Failure(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index 93e595243e..c35d0750a5 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -268,6 +268,8 @@ namespace AZ AZStd::vector m_cachedStreamBufferViews; AZStd::vector m_cachedIndexBufferViews; AZStd::vector> m_cachedDrawSrg; + + uint32_t m_nextDrawSrgIdx = 0; // structure includes DrawItem and stream and index buffer index using BufferViewIndexType = uint32_t; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h index a2972ff0fd..3e92542429 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -40,6 +41,23 @@ namespace AZ //! Loads a streaming image asset for the given file path Data::Instance LoadStreamingTexture(AZStd::string_view path); + + //! Looks for a three arguments attribute named @attributeName in the given shader asset. + //! Assigns the value to each non-null output variables. + //! @param shaderAsset + //! @param attributeName + //! @param numThreadsX Can be NULL. If not NULL it takes the value of the 1st argument of the attribute. Becomes 1 on error. + //! @param numThreadsY Can be NULL. If not NULL it takes the value of the 2nd argument of the attribute. Becomes 1 on error. + //! @param numThreadsZ Can be NULL. If not NULL it takes the value of the 3rd argument of the attribute. Becomes 1 on error. + //! @returns An Outcome instance with error message in case of error. + AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, const AZ::Name& attributeName, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ); + + //! Same as above, but assumes the name of the attribute to be 'numthreads'. + AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ); + + //! Same as above. Provided as a convenience when all arguments of the 'numthreads' attributes should be assigned to RHI::DispatchDirect::m_threadsPerGroup* variables. + AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, RHI::DispatchDirect& dispatchDirect); + } // namespace RPI } // namespace AZ 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/Include/Atom/RPI.Public/Shader/ShaderResourceGroup.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderResourceGroup.h index b2cea448c1..99f1f5f598 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderResourceGroup.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderResourceGroup.h @@ -133,6 +133,9 @@ namespace AZ /// Returns an array of RPI buffers associated with the buffer shader input index. AZStd::array_view> GetBufferArray(RHI::ShaderInputNameIndex& inputIndex) const; AZStd::array_view> GetBufferArray(RHI::ShaderInputBufferIndex inputIndex) const; + + //! Reset image and buffer views so that it won't hold references for any RHI resources + void ResetViews(); ////////////////////////////////////////////////////////////////////////// // Methods for assignment / access of RHI Image types. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index 4da85823b3..3b3473a2da 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -518,7 +518,6 @@ namespace AZ if (drawSrg) { drawItem.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup(); - m_cachedDrawSrg.push_back(drawSrg); } // Set scissor per draw if scissor is enabled. @@ -608,7 +607,6 @@ namespace AZ if (drawSrg) { drawItem.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup(); - m_cachedDrawSrg.push_back(drawSrg); } // Set scissor per draw if scissor is enabled. @@ -635,7 +633,22 @@ namespace AZ { return nullptr; } - auto drawSrg = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), m_shader->GetSupervariantIndex(), m_drawSrgLayout->GetName()); + + Data::Instance drawSrg; + if (m_nextDrawSrgIdx == m_cachedDrawSrg.size()) + { + drawSrg = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), m_shader->GetSupervariantIndex(), m_drawSrgLayout->GetName()); + m_cachedDrawSrg.push_back(drawSrg); + } + else if (m_nextDrawSrgIdx < m_cachedDrawSrg.size()) + { + drawSrg = m_cachedDrawSrg[m_nextDrawSrgIdx]; + } + else + { + AZ_Assert(false, "Unexpected next draw srg index"); + } + m_nextDrawSrgIdx++; // Set fallback value for shader variant if draw srg contains constant for shader variant fallback if (m_hasShaderVariantKeyFallbackEntry) @@ -727,7 +740,7 @@ namespace AZ } for (auto& drawItemProperties : m_cachedDrawList) - { + { view->AddDrawItem(m_drawListTag, drawItemProperties); } } @@ -743,9 +756,14 @@ namespace AZ m_cachedDrawItems.clear(); m_cachedStreamBufferViews.clear(); m_cachedIndexBufferViews.clear(); - m_cachedDrawSrg.clear(); m_cachedDrawList.clear(); + m_nextDrawSrgIdx = 0; m_drawFinalized = false; + + for (auto srg:m_cachedDrawSrg) + { + srg->ResetViews(); + } } const RHI::PipelineState* DynamicDrawContext::GetCurrentPipelineState() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ComputePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ComputePass.cpp index 2505409c62..8bcf9b1b85 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ComputePass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ComputePass.cpp @@ -107,30 +107,13 @@ namespace AZ dispatchArgs.m_totalNumberOfThreadsY = passData->m_totalNumberOfThreadsY; dispatchArgs.m_totalNumberOfThreadsZ = passData->m_totalNumberOfThreadsZ; - const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" }); - if (numThreads) + const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), dispatchArgs); + if (!outcome.IsSuccess()) { - const RHI::ShaderStageAttributeArguments& args = *numThreads; - bool validArgs = args.size() == 3; - if (validArgs) - { - validArgs &= args[0].type() == azrtti_typeid(); - validArgs &= args[1].type() == azrtti_typeid(); - validArgs &= args[2].type() == azrtti_typeid(); - } - - if (!validArgs) - { - AZ_Error("PassSystem", false, "[ComputePass '%s']: Shader '%s' contains invalid numthreads arguments.", - GetPathName().GetCStr(), - passData->m_shaderReference.m_filePath.data()); - return; - } - - dispatchArgs.m_threadsPerGroupX = aznumeric_cast(AZStd::any_cast(args[0])); - dispatchArgs.m_threadsPerGroupY = aznumeric_cast(AZStd::any_cast(args[1])); - dispatchArgs.m_threadsPerGroupZ = aznumeric_cast(AZStd::any_cast(args[2])); + AZ_Error("PassSystem", false, "[ComputePass '%s']: Shader '%.*s' contains invalid numthreads arguments:\n%s", + GetPathName().GetCStr(), passData->m_shaderReference.m_filePath.size(), passData->m_shaderReference.m_filePath.data(), outcome.GetError().c_str()); } + m_dispatchItem.m_arguments = dispatchArgs; m_isFullscreenPass = passData->m_makeFullscreenPass; 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/RPIUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp index 9173b47fad..e70885daa1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp @@ -143,5 +143,79 @@ namespace AZ return RPI::StreamingImage::FindOrCreate(streamingImageAsset); } + + //! A helper function for GetComputeShaderNumThreads(), to consolidate error messages, etc. + static bool GetAttributeArgumentByIndex(const Data::Asset& shaderAsset, const AZ::Name& attributeName, const RHI::ShaderStageAttributeArguments& args, const size_t argIndex, uint16_t* value, AZStd::string& errorMsg) + { + if (value) + { + const auto numArguments = args.size(); + if (numArguments > argIndex) + { + if (args[argIndex].type() == azrtti_typeid()) + { + *value = aznumeric_caster(AZStd::any_cast(args[argIndex])); + } + else + { + errorMsg = AZStd::string::format("Was expecting argument '%zu' in attribute '%s' to be of type 'int' from shader asset '%s'", argIndex, attributeName.GetCStr(), shaderAsset.GetHint().c_str()); + return false; + } + } + else + { + errorMsg = AZStd::string::format("Was expecting at least '%zu' arguments in attribute '%s' from shader asset '%s'", argIndex + 1, attributeName.GetCStr(), shaderAsset.GetHint().c_str()); + return false; + } + } + return true; + } + + AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, const AZ::Name& attributeName, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ) + { + // Set default 1, 1, 1 now. In case of errors later this is what the caller will get. + if (numThreadsX) + { + *numThreadsX = 1; + } + if (numThreadsY) + { + *numThreadsY = 1; + } + if (numThreadsZ) + { + *numThreadsZ = 1; + } + const auto numThreads = shaderAsset->GetAttribute(RHI::ShaderStage::Compute, attributeName); + if (!numThreads) + { + return AZ::Failure(AZStd::string::format("Couldn't find attribute '%s' in shader asset '%s'", attributeName.GetCStr(), shaderAsset.GetHint().c_str())); + } + const RHI::ShaderStageAttributeArguments& args = *numThreads; + AZStd::string errorMsg; + if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 0, numThreadsX, errorMsg)) + { + return AZ::Failure(errorMsg); + } + if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 1, numThreadsY, errorMsg)) + { + return AZ::Failure(errorMsg); + } + if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 2, numThreadsZ, errorMsg)) + { + return AZ::Failure(errorMsg); + } + return AZ::Success(); + } + + AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ) + { + return GetComputeShaderNumThreads(shaderAsset, Name{ "numthreads" }, numThreadsX, numThreadsY, numThreadsZ); + } + + AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, RHI::DispatchDirect& dispatchDirect) + { + return GetComputeShaderNumThreads(shaderAsset, &dispatchDirect.m_threadsPerGroupX, &dispatchDirect.m_threadsPerGroupY, &dispatchDirect.m_threadsPerGroupZ); + } } } 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 499365a9a9..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 @@ -580,6 +584,11 @@ namespace AZ return {}; } + void ShaderResourceGroup::ResetViews() + { + m_data.ResetViews(); + } + const RHI::SamplerState& ShaderResourceGroup::GetSampler(RHI::ShaderInputNameIndex& inputIndex, uint32_t arrayIndex) const { inputIndex.ValidateOrFindSamplerIndex(GetLayout()); diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp index 63a1524929..a5511edea1 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp @@ -53,16 +53,6 @@ namespace UnitTest return false; } - const char* AssetSystemStub::GetAbsoluteDevGameFolderPath() - { - return nullptr; - } - - const char* AssetSystemStub::GetAbsoluteDevRootFolderPath() - { - return nullptr; - } - bool AssetSystemStub::GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) { return false; diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h index a73570e3c9..2dd810b1e6 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h @@ -56,8 +56,6 @@ namespace UnitTest AZStd::unordered_map m_sourceInfoMap; bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; - const char* GetAbsoluteDevGameFolderPath() override; - const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override; bool GenerateRelativeSourcePath( const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override; 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/RPI/Code/Tests/Common/RPITestFixture.cpp b/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.cpp index aaa574a14a..5343c295d8 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.cpp @@ -67,7 +67,7 @@ namespace UnitTest AZ::IO::Path assetPath = AZStd::string_view{ AZ::Utils::GetProjectPath() }; assetPath /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", assetPath.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetPath.c_str()); m_jsonRegistrationContext = AZStd::make_unique(); m_jsonSystemComponent = AZStd::make_unique(); @@ -90,7 +90,7 @@ namespace UnitTest JobManagerThreadDesc threadDesc; #if AZ_TRAIT_SET_JOB_PROCESSOR_ID threadDesc.m_cpuId = 0; // Don't set processors IDs on windows -#endif +#endif uint32_t numWorkerThreads = AZStd::thread::hardware_concurrency(); @@ -99,7 +99,7 @@ namespace UnitTest desc.m_workerThreads.push_back(threadDesc); #if AZ_TRAIT_SET_JOB_PROCESSOR_ID threadDesc.m_cpuId++; -#endif +#endif } m_jobManager = AZStd::make_unique(desc); 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/CMakeLists.txt b/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt index add499f080..ea5b65be7c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt @@ -36,6 +36,7 @@ ly_add_target( Gem::Atom_RPI.Edit Gem::Atom_RPI.Public Gem::Atom_RHI.Reflect + Gem::Atom_Feature_Common.Static Gem::Atom_Bootstrap.Headers ) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewContent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewContent.h new file mode 100644 index 0000000000..27876622da --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewContent.h @@ -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 + * + */ + +#pragma once + +#include +#include + +namespace AtomToolsFramework +{ + //! Interface for describing scene content that will be rendered using the PreviewRenderer + class PreviewContent + { + public: + AZ_CLASS_ALLOCATOR(PreviewContent, AZ::SystemAllocator, 0); + + PreviewContent() = default; + virtual ~PreviewContent() = default; + + //! Initiate loading of scene content, models, materials, etc + virtual void Load() = 0; + + //! Return true if content is loaded and ready to render + virtual bool IsReady() const = 0; + + //! Return true if content failed to load + virtual bool IsError() const = 0; + + //! Report any issues encountered while loading + virtual void ReportErrors() = 0; + + //! Prepare or pose content before rendering + virtual void Update() = 0; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.h new file mode 100644 index 0000000000..ea8f5fb356 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.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 +#include +#include +#include + +class QPixmap; + +namespace AtomToolsFramework +{ + //! PreviewRendererCaptureRequest describes the size, content, and behavior of a scene to be rendered to an image + struct PreviewRendererCaptureRequest final + { + AZ_CLASS_ALLOCATOR(PreviewRendererCaptureRequest, AZ::SystemAllocator, 0); + + int m_size = 512; + AZStd::shared_ptr m_content; + AZStd::function m_captureFailedCallback; + AZStd::function m_captureCompleteCallback; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.h new file mode 100644 index 0000000000..8fab1cb5c1 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.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 + +namespace AtomToolsFramework +{ + struct PreviewRendererCaptureRequest; + + //! Public interface for PreviewRenderer so that it can be used in other modules + class PreviewRendererInterface + { + public: + AZ_RTTI(PreviewRendererInterface, "{C5B5E3D0-0055-4C08-9B98-FDBBB5F05BED}"); + + virtual ~PreviewRendererInterface() = default; + virtual void AddCaptureRequest(const PreviewRendererCaptureRequest& captureRequest) = 0; + virtual AZ::RPI::ScenePtr GetScene() const = 0; + virtual AZ::RPI::ViewPtr GetView() const = 0; + virtual AZ::Uuid GetEntityContextId() const = 0; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h new file mode 100644 index 0000000000..810eccaa20 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h @@ -0,0 +1,24 @@ +/* + * 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 AtomToolsFramework +{ + //! PreviewRendererSystemRequests provides an interface for PreviewRendererSystemComponent + class PreviewRendererSystemRequests : public AZ::EBusTraits + { + public: + // Only a single handler is allowed + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + }; + using PreviewRendererSystemRequestBus = AZ::EBus; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h new file mode 100644 index 0000000000..fe1980b39b --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h @@ -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 + * + */ +#pragma once + +#include +#include +#include + +namespace AtomToolsFramework +{ + //! PreviewerFeatureProcessorProviderRequests allows registering custom Feature Processors for preview image generation + class PreviewerFeatureProcessorProviderRequests : public AZ::EBusTraits + { + public: + //! Get a list of custom feature processors to register with preview image renderer + virtual void GetRequiredFeatureProcessors(AZStd::unordered_set& featureProcessors) const = 0; + }; + + using PreviewerFeatureProcessorProviderBus = AZ::EBus; +} // namespace AtomToolsFramework 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..69b99093f0 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -25,7 +25,7 @@ namespace AtomToolsFramework { //! The RenderViewportWidget class is a Qt wrapper around an Atom viewport. - //! RenderViewportWidget renders to an internal window using RPI::ViewportContext + //! RenderViewportWidget renders to an internal window using AZ::RPI::ViewportContext //! and delegates input via its internal ViewportControllerList. //! @see AZ::RPI::ViewportContext for Atom's API for setting up class RenderViewportWidget @@ -39,7 +39,7 @@ namespace AtomToolsFramework public: //! Creates a RenderViewportWidget. //! Requires the Atom RPI to be initialized in order - //! to internally construct an RPI::ViewportContext. + //! to internally construct an AZ::RPI::ViewportContext. //! If initializeViewportContext is set to false, nothing will be displayed on-screen until InitiliazeViewportContext is called. explicit RenderViewportWidget(QWidget* parent = nullptr, bool shouldInitializeViewportContext = true); ~RenderViewportWidget(); @@ -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/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 13b9a2ba27..3fae2ee7a3 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -173,7 +173,7 @@ namespace AtomToolsFramework AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); - AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); + AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) { @@ -289,7 +289,7 @@ namespace AtomToolsFramework ExitMainLoop(); } } - + void AtomToolsApplication::SaveSettings() { if (m_activatedLocalUserSettings) @@ -378,7 +378,7 @@ namespace AtomToolsFramework ExitMainLoop(); } } - + bool AtomToolsApplication::LaunchLocalServer() { // Determine if this is the first launch of the tool by attempting to connect to a running server diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp index 21a185b290..865f12d904 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace AtomToolsFramework { @@ -19,6 +20,7 @@ namespace AtomToolsFramework AtomToolsFrameworkSystemComponent::CreateDescriptor(), AtomToolsDocumentSystemComponent::CreateDescriptor(), AtomToolsMainWindowSystemComponent::CreateDescriptor(), + PreviewRendererSystemComponent::CreateDescriptor(), }); } @@ -28,6 +30,7 @@ namespace AtomToolsFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), }; } } 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/PreviewRenderer/PreviewRenderer.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp new file mode 100644 index 0000000000..a0d2034082 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp @@ -0,0 +1,252 @@ +/* + * 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 + +namespace AtomToolsFramework +{ + PreviewRenderer::PreviewRenderer(const AZStd::string& sceneName, const AZStd::string& pipelineName) + { + PreviewerFeatureProcessorProviderBus::Handler::BusConnect(); + + m_entityContext = AZStd::make_unique(); + m_entityContext->InitContext(); + + // Create and register a scene with all required feature processors + AZStd::unordered_set featureProcessors; + PreviewerFeatureProcessorProviderBus::Broadcast( + &PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors); + + AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end()); + m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); + + // Bind m_frameworkScene to the entity context's AzFramework::Scene + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Failed to get scene system implementation."); + + AZ::Outcome, AZStd::string> createSceneOutcome = sceneSystem->CreateScene(sceneName); + AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str()); + + m_frameworkScene = createSceneOutcome.TakeValue(); + m_frameworkScene->SetSubsystem(m_scene); + m_frameworkScene->SetSubsystem(m_entityContext.get()); + + // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene + AZ::RPI::RenderPipelineDescriptor pipelineDesc; + pipelineDesc.m_mainViewTagName = "MainCamera"; + pipelineDesc.m_name = pipelineName; + pipelineDesc.m_rootPassTemplate = "MainPipelineRenderToTexture"; + + // We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue + // [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost + pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4; + m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc); + m_scene->AddRenderPipeline(m_renderPipeline); + m_scene->Activate(); + AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene); + m_passHierarchy.push_back(pipelineName); + m_passHierarchy.push_back("CopyToSwapChain"); + + // Connect camera to pipeline's default view after camera entity activated + AZ::Matrix4x4 viewToClipMatrix; + AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, FieldOfView, AspectRatio, NearDist, FarDist, true); + m_view = AZ::RPI::View::CreateView(AZ::Name("MainCamera"), AZ::RPI::View::UsageCamera); + m_view->SetViewToClipMatrix(viewToClipMatrix); + m_renderPipeline->SetDefaultView(m_view); + + m_state.reset(new PreviewRendererIdleState(this)); + + AZ::Interface::Register(this); + } + + PreviewRenderer::~PreviewRenderer() + { + PreviewerFeatureProcessorProviderBus::Handler::BusDisconnect(); + + m_state.reset(); + + m_currentCaptureRequest = {}; + m_captureRequestQueue = {}; + + m_scene->Deactivate(); + m_scene->RemoveRenderPipeline(m_renderPipeline->GetId()); + AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene); + m_frameworkScene->UnsetSubsystem(m_scene); + m_frameworkScene->UnsetSubsystem(m_entityContext.get()); + + AZ::Interface::Unregister(this); + } + + void PreviewRenderer::AddCaptureRequest(const PreviewRendererCaptureRequest& captureRequest) + { + m_captureRequestQueue.push(captureRequest); + } + + AZ::RPI::ScenePtr PreviewRenderer::GetScene() const + { + return m_scene; + } + + AZ::RPI::ViewPtr PreviewRenderer::GetView() const + { + return m_view; + } + + AZ::Uuid PreviewRenderer::GetEntityContextId() const + { + return m_entityContext->GetContextId(); + } + + void PreviewRenderer::ProcessCaptureRequests() + { + if (!m_captureRequestQueue.empty()) + { + // pop the next request to be rendered from the queue + m_currentCaptureRequest = m_captureRequestQueue.front(); + m_captureRequestQueue.pop(); + + m_state.reset(); + m_state.reset(new PreviewRendererLoadState(this)); + } + } + + void PreviewRenderer::CancelCaptureRequest() + { + if (m_currentCaptureRequest.m_captureFailedCallback) + { + m_currentCaptureRequest.m_captureFailedCallback(); + } + m_state.reset(); + m_state.reset(new PreviewRendererIdleState(this)); + } + + void PreviewRenderer::CompleteCaptureRequest() + { + m_state.reset(); + m_state.reset(new PreviewRendererIdleState(this)); + } + + void PreviewRenderer::LoadContent() + { + m_currentCaptureRequest.m_content->Load(); + } + + void PreviewRenderer::UpdateLoadContent() + { + if (m_currentCaptureRequest.m_content->IsReady()) + { + m_state.reset(); + m_state.reset(new PreviewRendererCaptureState(this)); + return; + } + + if (m_currentCaptureRequest.m_content->IsError()) + { + CancelLoadContent(); + return; + } + } + + void PreviewRenderer::CancelLoadContent() + { + m_currentCaptureRequest.m_content->ReportErrors(); + CancelCaptureRequest(); + } + + void PreviewRenderer::PoseContent() + { + m_currentCaptureRequest.m_content->Update(); + } + + bool PreviewRenderer::StartCapture() + { + auto captureCompleteCallback = m_currentCaptureRequest.m_captureCompleteCallback; + auto captureFailedCallback = m_currentCaptureRequest.m_captureFailedCallback; + auto captureCallback = [captureCompleteCallback, captureFailedCallback](const AZ::RPI::AttachmentReadback::ReadbackResult& result) + { + if (result.m_dataBuffer) + { + if (captureCompleteCallback) + { + captureCompleteCallback(QPixmap::fromImage(QImage( + result.m_dataBuffer.get()->data(), result.m_imageDescriptor.m_size.m_width, + result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888))); + } + } + else + { + if (captureFailedCallback) + { + captureFailedCallback(); + } + } + }; + + if (auto renderToTexturePass = azrtti_cast(m_renderPipeline->GetRootPass().get())) + { + renderToTexturePass->ResizeOutput(m_currentCaptureRequest.m_size, m_currentCaptureRequest.m_size); + } + + m_renderPipeline->AddToRenderTickOnce(); + + bool startedCapture = false; + AZ::Render::FrameCaptureRequestBus::BroadcastResult( + startedCapture, &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, m_passHierarchy, + AZStd::string("Output"), captureCallback, AZ::RPI::PassAttachmentReadbackOption::Output); + return startedCapture; + } + + void PreviewRenderer::EndCapture() + { + m_currentCaptureRequest = {}; + m_renderPipeline->RemoveFromRenderTick(); + } + + void PreviewRenderer::GetRequiredFeatureProcessors(AZStd::unordered_set& featureProcessors) const + { + featureProcessors.insert({ + "AZ::Render::TransformServiceFeatureProcessor", + "AZ::Render::MeshFeatureProcessor", + "AZ::Render::SimplePointLightFeatureProcessor", + "AZ::Render::SimpleSpotLightFeatureProcessor", + "AZ::Render::PointLightFeatureProcessor", + // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow + // flickering [ATOM-13568] + // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. + // Possibly re-enable with [GFX TODO][ATOM-13639] + // "AZ::Render::DirectionalLightFeatureProcessor", + "AZ::Render::DiskLightFeatureProcessor", + "AZ::Render::CapsuleLightFeatureProcessor", + "AZ::Render::QuadLightFeatureProcessor", + "AZ::Render::DecalTextureArrayFeatureProcessor", + "AZ::Render::ImageBasedLightFeatureProcessor", + "AZ::Render::PostProcessFeatureProcessor", + "AZ::Render::SkyBoxFeatureProcessor" }); + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.h new file mode 100644 index 0000000000..6c8e282d80 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.h @@ -0,0 +1,75 @@ +/* + * 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 + +namespace AtomToolsFramework +{ + //! Processes requests for setting up content that gets rendered to a texture and captured to an image + class PreviewRenderer final + : public PreviewRendererInterface + , public PreviewerFeatureProcessorProviderBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(PreviewRenderer, AZ::SystemAllocator, 0); + AZ_RTTI(PreviewRenderer, "{60FCB7AB-2A94-417A-8C5E-5B588D17F5D1}", PreviewRendererInterface); + + PreviewRenderer(const AZStd::string& sceneName, const AZStd::string& pipelineName); + ~PreviewRenderer() override; + + void AddCaptureRequest(const PreviewRendererCaptureRequest& captureRequest) override; + + AZ::RPI::ScenePtr GetScene() const override; + AZ::RPI::ViewPtr GetView() const override; + AZ::Uuid GetEntityContextId() const override; + + void ProcessCaptureRequests(); + void CancelCaptureRequest(); + void CompleteCaptureRequest(); + + void LoadContent(); + void UpdateLoadContent(); + void CancelLoadContent(); + + void PoseContent(); + + bool StartCapture(); + void EndCapture(); + + private: + //! AZ::Render::PreviewerFeatureProcessorProviderBus::Handler interface overrides... + void GetRequiredFeatureProcessors(AZStd::unordered_set& featureProcessors) const override; + + static constexpr float AspectRatio = 1.0f; + static constexpr float NearDist = 0.001f; + static constexpr float FarDist = 100.0f; + static constexpr float FieldOfView = AZ::Constants::HalfPi; + + AZ::RPI::ScenePtr m_scene; + AZStd::shared_ptr m_frameworkScene; + AZ::RPI::RenderPipelinePtr m_renderPipeline; + AZ::RPI::ViewPtr m_view; + AZStd::vector m_passHierarchy; + AZStd::unique_ptr m_entityContext; + + //! Incoming requests are appended to this queue and processed one at a time in OnTick function. + AZStd::queue m_captureRequestQueue; + PreviewRendererCaptureRequest m_currentCaptureRequest; + + AZStd::unique_ptr m_state; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererCaptureState.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererCaptureState.cpp new file mode 100644 index 0000000000..5d1bd9158a --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererCaptureState.cpp @@ -0,0 +1,42 @@ +/* + * 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 AtomToolsFramework +{ + PreviewRendererCaptureState::PreviewRendererCaptureState(PreviewRenderer* renderer) + : PreviewRendererState(renderer) + { + m_renderer->PoseContent(); + AZ::TickBus::Handler::BusConnect(); + } + + PreviewRendererCaptureState::~PreviewRendererCaptureState() + { + AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect(); + AZ::TickBus::Handler::BusDisconnect(); + m_renderer->EndCapture(); + } + + void PreviewRendererCaptureState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if ((m_ticksToCapture-- <= 0) && m_renderer->StartCapture()) + { + AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect(); + AZ::TickBus::Handler::BusDisconnect(); + } + } + + void PreviewRendererCaptureState::OnCaptureFinished( + [[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info) + { + m_renderer->CompleteCaptureRequest(); + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererCaptureState.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererCaptureState.h new file mode 100644 index 0000000000..74195ab396 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererCaptureState.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 +#include + +namespace AtomToolsFramework +{ + //! PreviewRendererCaptureState renders a thumbnail to a pixmap and notifies MaterialOrModelThumbnail once finished + class PreviewRendererCaptureState final + : public PreviewRendererState + , public AZ::TickBus::Handler + , public AZ::Render::FrameCaptureNotificationBus::Handler + { + public: + PreviewRendererCaptureState(PreviewRenderer* renderer); + ~PreviewRendererCaptureState(); + + private: + //! AZ::TickBus::Handler interface overrides... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + //! AZ::Render::FrameCaptureNotificationBus::Handler overrides... + void OnCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override; + + //! This is necessary to suspend capture to allow a frame for Material and Mesh components to assign materials + int m_ticksToCapture = 1; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererIdleState.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererIdleState.cpp new file mode 100644 index 0000000000..b60142dd14 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererIdleState.cpp @@ -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 + * + */ + +#include +#include + +namespace AtomToolsFramework +{ + PreviewRendererIdleState::PreviewRendererIdleState(PreviewRenderer* renderer) + : PreviewRendererState(renderer) + { + AZ::TickBus::Handler::BusConnect(); + } + + PreviewRendererIdleState::~PreviewRendererIdleState() + { + AZ::TickBus::Handler::BusDisconnect(); + } + + void PreviewRendererIdleState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + m_renderer->ProcessCaptureRequests(); + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererIdleState.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererIdleState.h new file mode 100644 index 0000000000..4a0bc9067e --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererIdleState.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 + +namespace AtomToolsFramework +{ + //! PreviewRendererIdleState checks whether there are any new thumbnails that need to be rendered every tick + class PreviewRendererIdleState final + : public PreviewRendererState + , public AZ::TickBus::Handler + { + public: + PreviewRendererIdleState(PreviewRenderer* renderer); + ~PreviewRendererIdleState(); + + private: + //! AZ::TickBus::Handler interface overrides... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererLoadState.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererLoadState.cpp new file mode 100644 index 0000000000..2a1d99a090 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererLoadState.cpp @@ -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 + * + */ + +#include +#include + +namespace AtomToolsFramework +{ + PreviewRendererLoadState::PreviewRendererLoadState(PreviewRenderer* renderer) + : PreviewRendererState(renderer) + { + m_renderer->LoadContent(); + AZ::TickBus::Handler::BusConnect(); + } + + PreviewRendererLoadState::~PreviewRendererLoadState() + { + AZ::TickBus::Handler::BusDisconnect(); + } + + void PreviewRendererLoadState::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if ((m_timeRemainingS += deltaTime) > TimeOutS) + { + m_renderer->CancelLoadContent(); + return; + } + + m_renderer->UpdateLoadContent(); + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererLoadState.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererLoadState.h new file mode 100644 index 0000000000..359329c8da --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererLoadState.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 AtomToolsFramework +{ + //! PreviewRendererLoadState pauses further rendering until all assets used for rendering a thumbnail have been loaded + class PreviewRendererLoadState final + : public PreviewRendererState + , public AZ::TickBus::Handler + { + public: + PreviewRendererLoadState(PreviewRenderer* renderer); + ~PreviewRendererLoadState(); + + private: + //! AZ::TickBus::Handler interface overrides... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + static constexpr float TimeOutS = 5.0f; + float m_timeRemainingS = 0.0f; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererState.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererState.h new file mode 100644 index 0000000000..53c452ef51 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererState.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 + +namespace AtomToolsFramework +{ + class PreviewRenderer; + + //! PreviewRendererState is an interface for defining states that manages the logic flow of the PreviewRenderer + class PreviewRendererState + { + public: + explicit PreviewRendererState(PreviewRenderer* renderer) + : m_renderer(renderer) + { + } + + virtual ~PreviewRendererState() = default; + + protected: + PreviewRenderer* m_renderer = {}; + }; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp new file mode 100644 index 0000000000..f88adfc65d --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp @@ -0,0 +1,75 @@ +/* + * 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 + +namespace AtomToolsFramework +{ + void PreviewRendererSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0); + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("PreviewRendererSystemComponent", "System component that manages a global PreviewRenderer.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void PreviewRendererSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("PreviewRendererSystem")); + } + + void PreviewRendererSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("PreviewRendererSystem")); + } + + void PreviewRendererSystemComponent::Init() + { + } + + void PreviewRendererSystemComponent::Activate() + { + AzFramework::AssetCatalogEventBus::Handler::BusConnect(); + AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); + PreviewRendererSystemRequestBus::Handler::BusConnect(); + } + + void PreviewRendererSystemComponent::Deactivate() + { + PreviewRendererSystemRequestBus::Handler::BusDisconnect(); + AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + m_previewRenderer.reset(); + } + + void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) + { + AZ::TickBus::QueueFunction([this](){ + m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer( + "PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline")); + }); + } + + void PreviewRendererSystemComponent::OnApplicationAboutToStop() + { + m_previewRenderer.reset(); + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h new file mode 100644 index 0000000000..8110d84794 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h @@ -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 + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AtomToolsFramework +{ + //! System component that manages a global PreviewRenderer. + class PreviewRendererSystemComponent final + : public AZ::Component + , public AzFramework::AssetCatalogEventBus::Handler + , public AzFramework::ApplicationLifecycleEvents::Bus::Handler + , public PreviewRendererSystemRequestBus::Handler + { + public: + AZ_COMPONENT(PreviewRendererSystemComponent, "{E9F79FD8-82F2-4C80-966D-95F28484F229}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + protected: + // AZ::Component interface overrides... + void Init() override; + void Activate() override; + void Deactivate() override; + + private: + // AzFramework::AssetCatalogEventBus::Handler overrides ... + void OnCatalogLoaded(const char* catalogFile) override; + + // AzFramework::ApplicationLifecycleEvents overrides... + void OnApplicationAboutToStop() override; + + AZStd::unique_ptr m_previewRenderer; + }; +} // namespace AtomToolsFramework 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/AtomToolsFramework/Code/atomtoolsframework_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake index 3d4bb82eec..a2446cebcc 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake @@ -58,4 +58,20 @@ set(FILES Source/Window/AtomToolsMainWindow.cpp Source/Window/AtomToolsMainWindowSystemComponent.cpp Source/Window/AtomToolsMainWindowSystemComponent.h + Include/AtomToolsFramework/PreviewRenderer/PreviewContent.h + Include/AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.h + Include/AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.h + Include/AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h + Include/AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h + Source/PreviewRenderer/PreviewRenderer.cpp + Source/PreviewRenderer/PreviewRenderer.h + Source/PreviewRenderer/PreviewRendererState.h + Source/PreviewRenderer/PreviewRendererIdleState.cpp + Source/PreviewRenderer/PreviewRendererIdleState.h + Source/PreviewRenderer/PreviewRendererLoadState.cpp + Source/PreviewRenderer/PreviewRendererLoadState.h + Source/PreviewRenderer/PreviewRendererCaptureState.cpp + Source/PreviewRenderer/PreviewRendererCaptureState.h + Source/PreviewRenderer/PreviewRendererSystemComponent.cpp + Source/PreviewRenderer/PreviewRendererSystemComponent.h ) \ No newline at end of file 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/CreateMaterialDialog/CreateMaterialDialog.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp index f27a08b08d..fd20716570 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp @@ -24,7 +24,7 @@ namespace MaterialEditor { CreateMaterialDialog::CreateMaterialDialog(QWidget* parent) - : CreateMaterialDialog(QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials", parent) + : CreateMaterialDialog(QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials", parent) { } 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/MaterialEditorBrowserInteractions.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp index e6ffd2842f..d73737a2dc 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp @@ -106,7 +106,7 @@ namespace MaterialEditor menu->addAction("Create Material...", [entry]() { const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo( - QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) + + QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" + AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." + AZ::RPI::MaterialSourceData::Extension).absoluteFilePath(); @@ -182,7 +182,7 @@ namespace MaterialEditor menu->addAction("Create Child Material...", [entry]() { const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo( - QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) + + QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" + AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." + AZ::RPI::MaterialSourceData::Extension).absoluteFilePath(); 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/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp index a00d3eec05..5721dfcc72 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp @@ -346,7 +346,7 @@ namespace MaterialEditor AZStd::string ViewportSettingsInspector::GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) const { - AZStd::string savePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); + AZStd::string savePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@"); savePath += AZ_CORRECT_FILESYSTEM_SEPARATOR; savePath += "Materials"; savePath += AZ_CORRECT_FILESYSTEM_SEPARATOR; diff --git a/Gems/Atom/Tools/MaterialEditor/Scripts/GenerateAllMaterialScreenshots.py b/Gems/Atom/Tools/MaterialEditor/Scripts/GenerateAllMaterialScreenshots.py index 6708553e20..041e1a80c5 100755 --- a/Gems/Atom/Tools/MaterialEditor/Scripts/GenerateAllMaterialScreenshots.py +++ b/Gems/Atom/Tools/MaterialEditor/Scripts/GenerateAllMaterialScreenshots.py @@ -16,10 +16,10 @@ import sys import os.path import filecmp -g_devroot = azlmbr.paths.devroot -sys.path.append(os.path.join(g_devroot, 'Tests', 'Atom', 'Automated')) +g_engroot = azlmbr.paths.engroot +sys.path.append(os.path.join(g_engroot, 'Tests', 'Atom', 'Automated')) -g_materialTestFolder = os.path.join(g_devroot,'Gems','Atom','TestData','TestData','Materials','StandardPbrTestCases') +g_materialTestFolder = os.path.join(g_engroot,'Gems','Atom','TestData','TestData','Materials','StandardPbrTestCases') # Change this to True to replace the expected screenshot images g_replaceExpectedScreenshots = False diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py b/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py index 7bd65568ed..c31047d347 100755 --- a/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py +++ b/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py @@ -135,7 +135,7 @@ def main(): # clean previously generated shader variant list file so they don't clash. pre, ext = os.path.splitext(shaderAssetInfo.relativePath) - projectShaderVariantListFilePath = os.path.join(azlmbr.paths.devassets, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist') + projectShaderVariantListFilePath = os.path.join(azlmbr.paths.projectroot, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist') pre, ext = os.path.splitext(filename) defaultShaderVariantListFilePath = f'{pre}.shadervariantlist' 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/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index b45fbe05f6..39d8933863 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -1353,8 +1353,9 @@ namespace AZ::AtomBridge // if 2d draw need to project pos to screen first AzFramework::TextDrawParameters params; AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + const auto dpiScaleFactor = viewportContext->GetDpiScalingFactor(); params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works - params.m_position = AZ::Vector3(x, y, 1.0f); + params.m_position = AZ::Vector3(x * dpiScaleFactor, y * dpiScaleFactor, 1.0f); params.m_color = m_rendState.m_color; params.m_scale = AZ::Vector2(size); params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp index 5711e3ff6e..cdb941546d 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp @@ -46,7 +46,7 @@ static void DumfontTexture(IConsoleCmdArgs* cmdArgs) if (fontName && *fontName && *fontName != '0') { - AZStd::string fontFilePath("@devroot@/"); + AZStd::string fontFilePath("@engroot@/"); fontFilePath += fontName; fontFilePath += ".bmp"; 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/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h index 557e6b3dd2..72c4ef97a8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h @@ -120,13 +120,6 @@ namespace AZ //! Sets the filter method of shadows. virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0; - //! Gets the width of softening boundary between shadowed area and lit area in degrees. - virtual float GetSofteningBoundaryWidthAngle() const = 0; - - //! Sets the width of softening boundary between shadowed area and lit area in degrees. - //! 0 disables softening. - virtual void SetSofteningBoundaryWidthAngle(float degrees) = 0; - //! Gets the sample count for filtering of the shadow boundary. virtual uint32_t GetFilteringSampleCount() const = 0; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h index a6d3c6fbed..c76c922385 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h @@ -59,7 +59,6 @@ namespace AZ float m_bias = 0.1f; ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256; ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None; - float m_boundaryWidthInDegrees = 0.25f; uint16_t m_filteringSampleCount = 12; float m_esmExponent = 87.0f; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h index 644856e768..a8088c63ac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightBus.h @@ -153,15 +153,6 @@ namespace AZ //! @param method filter method. virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0; - //! This gets the width of boundary between shadowed area and lit area. - //! @return Boundary width. The shadow is gradually changed the degree of shadowed. - virtual float GetSofteningBoundaryWidth() const = 0; - - //! This specifies the width of boundary between shadowed area and lit area. - //! @param width Boundary width. The shadow is gradually changed the degree of shadowed. - //! If width == 0, softening edge is disabled. Units are in meters. - virtual void SetSofteningBoundaryWidth(float width) = 0; - //! This gets the sample count for filtering of the shadow boundary. //! @return Sample Count for filtering (up to 64) virtual uint32_t GetFilteringSampleCount() const = 0; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h index 0123d06275..a58acc0114 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/DirectionalLightComponentConfig.h @@ -101,10 +101,6 @@ namespace AZ //! Method of shadow's filtering. ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None; - //! Width of the boundary between shadowed area and lit one. - //! If this is 0, edge softening is disabled. Units are in meters. - float m_boundaryWidth = 0.03f; // 3cm - //! Sample Count for filtering (from 4 to 64) //! It is used only when the pixel is predicted as on the boundary. uint16_t m_filteringSampleCount = 32; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h new file mode 100644 index 0000000000..30762a60ba --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h @@ -0,0 +1,34 @@ +/* + * 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 + +class QPixmap; + +namespace AZ +{ + namespace Render + { + //! EditorMaterialSystemComponentNotifications is an interface for handling notifications from EditorMaterialSystemComponent, like + //! being informed that material preview images are available + class EditorMaterialSystemComponentNotifications : public AZ::EBusTraits + { + public: + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + + //! Notify that a material preview image is ready + virtual void OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) = 0; + }; + using EditorMaterialSystemComponentNotificationBus = AZ::EBus; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h index 47fad038b6..191f444586 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h @@ -5,20 +5,22 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include #include #include #include +#include namespace AZ { namespace Render { - //! EditorMaterialSystemComponentRequests provides an interface to communicate with MaterialEditor - class EditorMaterialSystemComponentRequests - : public AZ::EBusTraits + //! EditorMaterialSystemComponentRequests provides an interface for interacting with EditorMaterialSystemComponent, performing + //! different operations like opening the material editor, the material instance inspector, and managing material preview images + class EditorMaterialSystemComponentRequests : public AZ::EBusTraits { public: // Only a single handler is allowed @@ -31,6 +33,14 @@ namespace AZ //! Open material instance editor virtual void OpenMaterialInspector( const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0; + + //! Generate a material preview image + virtual void RenderMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0; + + //! Get recently rendered material preview image + virtual QPixmap GetRenderedMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const = 0; }; using EditorMaterialSystemComponentRequestBus = AZ::EBus; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h deleted file mode 100644 index 8030c3ae05..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h +++ /dev/null @@ -1,33 +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 - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - //! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation - //! Duplicates will be ignored - //! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp - class ThumbnailFeatureProcessorProviderRequests - : public AZ::EBusTraits - { - public: - //! Get a list of custom feature processors to register with thumbnail renderer - virtual const AZStd::vector& GetCustomFeatureProcessors() const = 0; - }; - - using ThumbnailFeatureProcessorProviderBus = AZ::EBus; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ 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/AreaLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp index c7af44a28e..f0418a5024 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp @@ -36,7 +36,6 @@ namespace AZ ->Field("Shadow Bias", &AreaLightComponentConfig::m_bias) ->Field("Shadowmap Max Size", &AreaLightComponentConfig::m_shadowmapMaxSize) ->Field("Shadow Filter Method", &AreaLightComponentConfig::m_shadowFilterMethod) - ->Field("Softening Boundary Width", &AreaLightComponentConfig::m_boundaryWidthInDegrees) ->Field("Filtering Sample Count", &AreaLightComponentConfig::m_filteringSampleCount) ->Field("Esm Exponent", &AreaLightComponentConfig::m_esmExponent) ; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp index c0204ecac5..36cb2a7f5a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp @@ -74,8 +74,6 @@ namespace AZ::Render ->Event("SetShadowmapMaxSize", &AreaLightRequestBus::Events::SetShadowmapMaxSize) ->Event("GetShadowFilterMethod", &AreaLightRequestBus::Events::GetShadowFilterMethod) ->Event("SetShadowFilterMethod", &AreaLightRequestBus::Events::SetShadowFilterMethod) - ->Event("GetSofteningBoundaryWidthAngle", &AreaLightRequestBus::Events::GetSofteningBoundaryWidthAngle) - ->Event("SetSofteningBoundaryWidthAngle", &AreaLightRequestBus::Events::SetSofteningBoundaryWidthAngle) ->Event("GetFilteringSampleCount", &AreaLightRequestBus::Events::GetFilteringSampleCount) ->Event("SetFilteringSampleCount", &AreaLightRequestBus::Events::SetFilteringSampleCount) ->Event("GetEsmExponent", &AreaLightRequestBus::Events::GetEsmExponent) @@ -95,7 +93,6 @@ namespace AZ::Render ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias") ->VirtualProperty("ShadowmapMaxSize", "GetShadowmapMaxSize", "SetShadowmapMaxSize") ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") - ->VirtualProperty("SofteningBoundaryWidthAngle", "GetSofteningBoundaryWidthAngle", "SetSofteningBoundaryWidthAngle") ->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount") ->VirtualProperty("EsmExponent", "GetEsmExponent", "SetEsmExponent"); ; @@ -307,7 +304,6 @@ namespace AZ::Render m_lightShapeDelegate->SetShadowBias(m_configuration.m_bias); m_lightShapeDelegate->SetShadowmapMaxSize(m_configuration.m_shadowmapMaxSize); m_lightShapeDelegate->SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); - m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(m_configuration.m_boundaryWidthInDegrees); m_lightShapeDelegate->SetFilteringSampleCount(m_configuration.m_filteringSampleCount); m_lightShapeDelegate->SetEsmExponent(m_configuration.m_esmExponent); } @@ -506,20 +502,6 @@ namespace AZ::Render } } - float AreaLightComponentController::GetSofteningBoundaryWidthAngle() const - { - return m_configuration.m_boundaryWidthInDegrees; - } - - void AreaLightComponentController::SetSofteningBoundaryWidthAngle(float width) - { - m_configuration.m_boundaryWidthInDegrees = width; - if (m_lightShapeDelegate) - { - m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(width); - } - } - uint32_t AreaLightComponentController::GetFilteringSampleCount() const { return m_configuration.m_filteringSampleCount; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h index 3bec61551f..cc6223e7e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h @@ -82,8 +82,6 @@ namespace AZ void SetShadowmapMaxSize(ShadowmapSize size) override; ShadowFilterMethod GetShadowFilterMethod() const override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - float GetSofteningBoundaryWidthAngle() const override; - void SetSofteningBoundaryWidthAngle(float width) override; uint32_t GetFilteringSampleCount() const override; void SetFilteringSampleCount(uint32_t count) override; float GetEsmExponent() const override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp index 37d94f5ed1..9d384c2e24 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentConfig.cpp @@ -37,7 +37,6 @@ namespace AZ ->Field("IsCascadeCorrectionEnabled", &DirectionalLightComponentConfig::m_isCascadeCorrectionEnabled) ->Field("IsDebugColoringEnabled", &DirectionalLightComponentConfig::m_isDebugColoringEnabled) ->Field("ShadowFilterMethod", &DirectionalLightComponentConfig::m_shadowFilterMethod) - ->Field("SofteningBoundaryWidth", &DirectionalLightComponentConfig::m_boundaryWidth) ->Field("PcfFilteringSampleCount", &DirectionalLightComponentConfig::m_filteringSampleCount) ->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp index fbc1ccc35d..78558cfc85 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.cpp @@ -80,8 +80,6 @@ namespace AZ ->Event("SetDebugColoringEnabled", &DirectionalLightRequestBus::Events::SetDebugColoringEnabled) ->Event("GetShadowFilterMethod", &DirectionalLightRequestBus::Events::GetShadowFilterMethod) ->Event("SetShadowFilterMethod", &DirectionalLightRequestBus::Events::SetShadowFilterMethod) - ->Event("GetSofteningBoundaryWidth", &DirectionalLightRequestBus::Events::GetSofteningBoundaryWidth) - ->Event("SetSofteningBoundaryWidth", &DirectionalLightRequestBus::Events::SetSofteningBoundaryWidth) ->Event("GetFilteringSampleCount", &DirectionalLightRequestBus::Events::GetFilteringSampleCount) ->Event("SetFilteringSampleCount", &DirectionalLightRequestBus::Events::SetFilteringSampleCount) ->Event("GetShadowReceiverPlaneBiasEnabled", &DirectionalLightRequestBus::Events::GetShadowReceiverPlaneBiasEnabled) @@ -99,7 +97,6 @@ namespace AZ ->VirtualProperty("ViewFrustumCorrectionEnabled", "GetViewFrustumCorrectionEnabled", "SetViewFrustumCorrectionEnabled") ->VirtualProperty("DebugColoringEnabled", "GetDebugColoringEnabled", "SetDebugColoringEnabled") ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") - ->VirtualProperty("SofteningBoundaryWidth", "GetSofteningBoundaryWidth", "SetSofteningBoundaryWidth") ->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount") ->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled"); ; @@ -404,21 +401,6 @@ namespace AZ } } - float DirectionalLightComponentController::GetSofteningBoundaryWidth() const - { - return m_configuration.m_boundaryWidth; - } - - void DirectionalLightComponentController::SetSofteningBoundaryWidth(float width) - { - width = GetMin(Shadow::MaxSofteningBoundaryWidth, GetMax(0.f, width)); - m_configuration.m_boundaryWidth = width; - if (m_featureProcessor) - { - m_featureProcessor->SetShadowBoundaryWidth(m_lightHandle, width); - } - } - uint32_t DirectionalLightComponentController::GetFilteringSampleCount() const { return aznumeric_cast(m_configuration.m_filteringSampleCount); @@ -517,7 +499,6 @@ namespace AZ SetViewFrustumCorrectionEnabled(m_configuration.m_isCascadeCorrectionEnabled); SetDebugColoringEnabled(m_configuration.m_isDebugColoringEnabled); SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); - SetSofteningBoundaryWidth(m_configuration.m_boundaryWidth); SetFilteringSampleCount(m_configuration.m_filteringSampleCount); SetShadowReceiverPlaneBiasEnabled(m_configuration.m_receiverPlaneBiasEnabled); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h index b8052bfc36..933f2705e7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DirectionalLightComponentController.h @@ -76,8 +76,6 @@ namespace AZ void SetDebugColoringEnabled(bool enabled) override; ShadowFilterMethod GetShadowFilterMethod() const override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - float GetSofteningBoundaryWidth() const override; - void SetSofteningBoundaryWidth(float width) override; uint32_t GetFilteringSampleCount() const override; void SetFilteringSampleCount(uint32_t count) override; bool GetShadowReceiverPlaneBiasEnabled() const override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index baf0cdced1..ebc79abca5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -147,14 +147,6 @@ namespace AZ::Render } } - void DiskLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees) - { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees)); - } - } - void DiskLightDelegate::SetFilteringSampleCount(uint32_t count) { if (GetShadowsEnabled() && GetLightHandle().IsValid()) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h index e0fd16f6be..2be782c69c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h @@ -44,7 +44,6 @@ namespace AZ void SetShadowBias(float bias) override; void SetShadowmapMaxSize(ShadowmapSize size) override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(float widthInDegrees) override; void SetFilteringSampleCount(uint32_t count) override; void SetEsmExponent(float exponent) override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 659c394cba..1e5b2580f7 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( @@ -154,15 +154,6 @@ namespace AZ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) - ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_boundaryWidthInDegrees, "Softening boundary width", - "Width of the boundary between shadowed area and lit one. " - "Units are in degrees. " - "If this is 0, softening edge is disabled.") - ->Attribute(Edit::Attributes::Min, 0.f) - ->Attribute(Edit::Attributes::Max, 1.f) - ->Attribute(Edit::Attributes::Suffix, " deg") - ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) - ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsEsmDisabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_filteringSampleCount, "Filtering sample count", "This is only used when the pixel is predicted to be on the boundary. Specific to PCF and ESM+PCF.") ->Attribute(Edit::Attributes::Min, 4) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp index ef9c73c0b4..69ba295e9b 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( @@ -133,15 +133,6 @@ namespace AZ ->EnumAttribute(ShadowFilterMethod::Esm, "ESM") ->EnumAttribute(ShadowFilterMethod::EsmPcf, "ESM+PCF") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_boundaryWidth, "Softening boundary width", - "Width of the boundary between shadowed area and lit one. " - "Units are in meters. " - "If this is 0, softening edge is disabled.") - ->Attribute(Edit::Attributes::Min, 0.f) - ->Attribute(Edit::Attributes::Max, 0.1f) - ->Attribute(Edit::Attributes::Suffix, " m") - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsEsmDisabled) ->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_filteringSampleCount, "Filtering sample count", "This is used only when the pixel is predicted as on the boundary. " "Specific to PCF and ESM+PCF.") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h index 2bd25b76a3..336c67f55d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h @@ -56,7 +56,6 @@ namespace AZ void SetShadowBias([[maybe_unused]] float bias) override {}; void SetShadowmapMaxSize([[maybe_unused]] ShadowmapSize size) override {}; void SetShadowFilterMethod([[maybe_unused]] ShadowFilterMethod method) override {}; - void SetSofteningBoundaryWidthAngle([[maybe_unused]] float widthInDegrees) override {}; void SetFilteringSampleCount([[maybe_unused]] uint32_t count) override {}; void SetEsmExponent([[maybe_unused]] float esmExponent) override{}; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h index 6d08971542..9bb8188898 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h @@ -75,8 +75,6 @@ namespace AZ virtual void SetShadowmapMaxSize(ShadowmapSize size) = 0; //! Sets the filter method for the shadow virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0; - //! Sets the width of boundary between shadowed area and lit area in degrees. - virtual void SetSofteningBoundaryWidthAngle(float widthInDegrees) = 0; //! Sets the sample count for filtering of the shadow boundary, max 64. virtual void SetFilteringSampleCount(uint32_t count) = 0; //! Sets the Esm exponent to use. Higher values produce a steeper falloff between light and shadow. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp index 8853db5751..661b0c6b25 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp @@ -92,14 +92,6 @@ namespace AZ::Render } } - void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees) - { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees)); - } - } - void SphereLightDelegate::SetFilteringSampleCount(uint32_t count) { if (GetShadowsEnabled() && GetLightHandle().IsValid()) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h index e2903b2d72..8bdee2442a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h @@ -34,7 +34,6 @@ namespace AZ void SetShadowBias(float bias) override; void SetShadowmapMaxSize(ShadowmapSize size) override; void SetShadowFilterMethod(ShadowFilterMethod method) override; - void SetSofteningBoundaryWidthAngle(float widthInDegrees) override; void SetFilteringSampleCount(uint32_t count) override; void SetEsmExponent(float esmExponent) override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index 7f676e4c10..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) @@ -389,7 +390,7 @@ namespace AZ // create the full paths char projectPath[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN); AZStd::string irradianceTextureFullPath; AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true); @@ -481,7 +482,7 @@ namespace AZ AZStd::string fullPath; char projectPath[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN); if (!relativePath.empty()) { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp index 896e88f4e2..1718dde5d4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp @@ -6,15 +6,17 @@ * */ -#include -#include - -#include #include #include +#include +#include #include #include #include +#include +#include +#include +#include #include @@ -68,7 +70,7 @@ namespace AZ void EditorCommonFeaturesSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { - AZ_UNUSED(required); + required.push_back(AZ_CRC_CE("ThumbnailerService")); } void EditorCommonFeaturesSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) @@ -82,24 +84,23 @@ namespace AZ void EditorCommonFeaturesSystemComponent::Activate() { - m_renderer = AZStd::make_unique(); - m_previewerFactory = AZStd::make_unique (); m_skinnedMeshDebugDisplay = AZStd::make_unique(); AzToolsFramework::EditorLevelNotificationBus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusConnect(); + AzFramework::AssetCatalogEventBus::Handler::BusConnect(); AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); } void EditorCommonFeaturesSystemComponent::Deactivate() { - AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect(); AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusDisconnect(); m_skinnedMeshDebugDisplay.reset(); - m_previewerFactory.reset(); - m_renderer.reset(); + TeardownThumbnails(); } void EditorCommonFeaturesSystemComponent::OnNewLevelCreated() @@ -191,6 +192,13 @@ namespace AZ } } + void EditorCommonFeaturesSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) + { + AZ::TickBus::QueueFunction([this](){ + SetupThumbnails(); + }); + } + const AzToolsFramework::AssetBrowser::PreviewerFactory* EditorCommonFeaturesSystemComponent::GetPreviewerFactory( const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const { @@ -199,7 +207,33 @@ namespace AZ void EditorCommonFeaturesSystemComponent::OnApplicationAboutToStop() { + TeardownThumbnails(); + } + + void EditorCommonFeaturesSystemComponent::SetupThumbnails() + { + using namespace AzToolsFramework::Thumbnailer; + using namespace LyIntegration; + + ThumbnailerRequestsBus::Broadcast( + &ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache), + ThumbnailContext::DefaultContext); + + m_renderer = AZStd::make_unique(); + m_previewerFactory = AZStd::make_unique(); + } + + void EditorCommonFeaturesSystemComponent::TeardownThumbnails() + { + using namespace AzToolsFramework::Thumbnailer; + using namespace LyIntegration; + + ThumbnailerRequestsBus::Broadcast( + &ThumbnailerRequests::UnregisterThumbnailProvider, SharedThumbnailCache::ProviderName, + ThumbnailContext::DefaultContext); + m_renderer.reset(); + m_previewerFactory.reset(); } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h index 2560bf7e11..8021770873 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h @@ -11,10 +11,10 @@ #include #include #include -#include #include -#include -#include +#include +#include +#include namespace AZ { @@ -28,6 +28,7 @@ namespace AZ , public AzToolsFramework::EditorLevelNotificationBus::Handler , public AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler , public AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler + , public AzFramework::AssetCatalogEventBus::Handler , public AzFramework::ApplicationLifecycleEvents::Bus::Handler { public: @@ -53,15 +54,23 @@ namespace AZ void OnNewLevelCreated() override; // SliceEditorEntityOwnershipServiceBus overrides ... - void OnSliceInstantiated(const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override; + void OnSliceInstantiated( + const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override; void OnSliceInstantiationFailed(const AZ::Data::AssetId&, const AzFramework::SliceInstantiationTicket&) override; + // AzFramework::AssetCatalogEventBus::Handler overrides ... + void OnCatalogLoaded(const char* catalogFile) override; + // AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler overrides... - const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override; + const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory( + const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override; // AzFramework::ApplicationLifecycleEvents overrides... void OnApplicationAboutToStop() override; + void SetupThumbnails(); + void TeardownThumbnails(); + private: AZStd::unique_ptr m_skinnedMeshDebugDisplay; @@ -69,8 +78,8 @@ namespace AZ AZStd::string m_atomLevelDefaultAssetPath{ "LevelAssets/default.slice" }; float m_envProbeHeight{ 200.0f }; - AZStd::unique_ptr m_renderer; - AZStd::unique_ptr m_previewerFactory; + AZStd::unique_ptr m_renderer; + AZStd::unique_ptr m_previewerFactory; }; } // namespace Render } // namespace AZ 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..a3ae61b14b 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") @@ -148,11 +148,13 @@ namespace AZ BaseClass::Activate(); MaterialReceiverNotificationBus::Handler::BusConnect(GetEntityId()); MaterialComponentNotificationBus::Handler::BusConnect(GetEntityId()); + EditorMaterialSystemComponentNotificationBus::Handler::BusConnect(); UpdateMaterialSlots(); } void EditorMaterialComponent::Deactivate() { + EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect(); MaterialReceiverNotificationBus::Handler::BusDisconnect(); MaterialComponentNotificationBus::Handler::BusDisconnect(); BaseClass::Deactivate(); @@ -260,6 +262,18 @@ namespace AZ } } + void EditorMaterialComponent::OnRenderMaterialPreviewComplete( + [[maybe_unused]] const AZ::EntityId& entityId, + [[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId, + [[maybe_unused]] const QPixmap& pixmap) + { + if (entityId == GetEntityId()) + { + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues); + } + } + AZ::u32 EditorMaterialComponent::OnConfigurationChanged() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h index f8895d994f..ce21ae82ac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -21,8 +22,9 @@ namespace AZ //! In-editor material component for displaying and editing material assignments. class EditorMaterialComponent final : public EditorRenderComponentAdapter - , private MaterialReceiverNotificationBus::Handler - , private MaterialComponentNotificationBus::Handler + , public MaterialReceiverNotificationBus::Handler + , public MaterialComponentNotificationBus::Handler + , public EditorMaterialSystemComponentNotificationBus::Handler { public: using BaseClass = EditorRenderComponentAdapter; @@ -52,6 +54,10 @@ namespace AZ //! MaterialComponentNotificationBus::Handler overrides... void OnMaterialInstanceCreated(const MaterialAssignment& materialAssignment) override; + //! EditorMaterialSystemComponentNotificationBus::Handler overrides... + void OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) override; + // Regenerates the editor component material slots based on the material and // LOD mapping from the model or other consumer of materials. // If any corresponding material assignments are found in the component diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 095c670e15..b6875e2e9c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -23,10 +23,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -49,29 +45,18 @@ namespace AZ MaterialPropertyInspector::MaterialPropertyInspector(QWidget* parent) : AtomToolsFramework::InspectorWidget(parent) { - // Create the menu button - QToolButton* menuButton = new QToolButton(this); - menuButton->setAutoRaise(true); - menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg")); - menuButton->setVisible(true); - QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); }); - AddHeading(menuButton); - - m_messageLabel = new QLabel(this); - m_messageLabel->setWordWrap(true); - m_messageLabel->setVisible(true); - m_messageLabel->setAlignment(Qt::AlignCenter); - m_messageLabel->setText(tr("Material not available")); - AddHeading(m_messageLabel); - + CreateHeading(); + AZ::TickBus::Handler::BusConnect(); AZ::EntitySystemBus::Handler::BusConnect(); + EditorMaterialSystemComponentNotificationBus::Handler::BusConnect(); } MaterialPropertyInspector::~MaterialPropertyInspector() { AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect(); - AZ::EntitySystemBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); + AZ::EntitySystemBus::Handler::BusDisconnect(); + EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect(); MaterialComponentNotificationBus::Handler::BusDisconnect(); } @@ -140,7 +125,7 @@ namespace AZ } Populate(); - m_messageLabel->setVisible(false); + LoadOverridesFromEntity(); return true; } @@ -152,8 +137,9 @@ namespace AZ m_dirtyPropertyFlags.set(); m_editorFunctors = {}; m_internalEditNotification = {}; - m_messageLabel->setVisible(true); - m_messageLabel->setText(tr("Material not available")); + m_updateUI = {}; + m_updatePreview = {}; + UpdateHeading(); } bool MaterialPropertyInspector::IsLoaded() const @@ -168,49 +154,63 @@ namespace AZ m_dirtyPropertyFlags.set(); m_internalEditNotification = {}; - AZ::TickBus::Handler::BusDisconnect(); AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect(); AtomToolsFramework::InspectorWidget::Reset(); } - void MaterialPropertyInspector::AddDetailsGroup() + void MaterialPropertyInspector::CreateHeading() { - const AZStd::string& groupName = "Details"; - const AZStd::string& groupDisplayName = "Details"; - const AZStd::string& groupDescription = ""; - - auto propertyGroupContainer = new QWidget(this); - propertyGroupContainer->setLayout(new QHBoxLayout()); + // Create the menu button + QToolButton* menuButton = new QToolButton(this); + menuButton->setAutoRaise(true); + menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg")); + menuButton->setVisible(true); + QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); }); + AddHeading(menuButton); - AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey = - MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, m_editData.m_materialAssetId); - auto thumbnailWidget = new AzToolsFramework::Thumbnailer::ThumbnailWidget(this); - thumbnailWidget->setFixedSize(QSize(120, 120)); - thumbnailWidget->setVisible(true); - thumbnailWidget->SetThumbnailKey(thumbnailKey, AzToolsFramework::Thumbnailer::ThumbnailContext::DefaultContext); - propertyGroupContainer->layout()->addWidget(thumbnailWidget); + m_overviewImage = new QLabel(this); + m_overviewImage->setFixedSize(QSize(120, 120)); + m_overviewImage->setScaledContents(true); + m_overviewImage->setVisible(false); - auto materialInfoWidget = new QLabel(this); + m_overviewText = new QLabel(this); QSizePolicy sizePolicy1(QSizePolicy::Ignored, QSizePolicy::Preferred); sizePolicy1.setHorizontalStretch(0); sizePolicy1.setVerticalStretch(0); - sizePolicy1.setHeightForWidth(materialInfoWidget->sizePolicy().hasHeightForWidth()); - materialInfoWidget->setSizePolicy(sizePolicy1); - materialInfoWidget->setMinimumSize(QSize(0, 0)); - materialInfoWidget->setMaximumSize(QSize(16777215, 16777215)); - materialInfoWidget->setTextFormat(Qt::AutoText); - materialInfoWidget->setScaledContents(false); - materialInfoWidget->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); - materialInfoWidget->setWordWrap(true); + sizePolicy1.setHeightForWidth(m_overviewText->sizePolicy().hasHeightForWidth()); + m_overviewText->setSizePolicy(sizePolicy1); + m_overviewText->setMinimumSize(QSize(0, 0)); + m_overviewText->setMaximumSize(QSize(16777215, 16777215)); + m_overviewText->setTextFormat(Qt::AutoText); + m_overviewText->setScaledContents(false); + m_overviewText->setWordWrap(true); + m_overviewText->setVisible(true); + + auto overviewContainer = new QWidget(this); + overviewContainer->setLayout(new QHBoxLayout()); + overviewContainer->layout()->addWidget(m_overviewImage); + overviewContainer->layout()->addWidget(m_overviewText); + AddHeading(overviewContainer); + } + + void MaterialPropertyInspector::UpdateHeading() + { + if (!IsLoaded()) + { + m_overviewText->setText(tr("Material not available")); + m_overviewText->setAlignment(Qt::AlignCenter); + m_overviewImage->setVisible(false); + return; + } QFileInfo materialFileInfo(AZ::RPI::AssetUtils::GetProductPathByAssetId(m_editData.m_materialAsset.GetId()).c_str()); QFileInfo materialSourceFileInfo(m_editData.m_materialSourcePath.c_str()); QFileInfo materialTypeSourceFileInfo(m_editData.m_materialTypeSourcePath.c_str()); - QFileInfo materialParentSourceFileInfo(AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str()); + QFileInfo materialParentSourceFileInfo( + AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str()); AZStd::string entityName; - AZ::ComponentApplicationBus::BroadcastResult( - entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId); + AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId); AZStd::string slotName; MaterialComponentRequestBus::EventResult( @@ -226,7 +226,8 @@ namespace AZ } if (!materialTypeSourceFileInfo.fileName().isEmpty()) { - materialInfo += tr("Material Type %1").arg(materialTypeSourceFileInfo.fileName()); + materialInfo += + tr("Material Type %1").arg(materialTypeSourceFileInfo.fileName()); } if (!materialSourceFileInfo.fileName().isEmpty()) { @@ -234,14 +235,21 @@ namespace AZ } if (!materialParentSourceFileInfo.fileName().isEmpty()) { - materialInfo += tr("Material Parent %1").arg(materialParentSourceFileInfo.fileName()); + materialInfo += + tr("Material Parent %1").arg(materialParentSourceFileInfo.fileName()); } materialInfo += tr(""); - materialInfoWidget->setText(materialInfo); - propertyGroupContainer->layout()->addWidget(materialInfoWidget); + m_overviewText->setText(materialInfo); + m_overviewText->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); - AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupContainer); + QPixmap pixmap; + EditorMaterialSystemComponentRequestBus::BroadcastResult( + pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId, + m_materialAssignmentId); + m_overviewImage->setPixmap(pixmap); + m_overviewImage->setVisible(true); + m_updatePreview |= pixmap.isNull(); } void MaterialPropertyInspector::AddUvNamesGroup() @@ -282,13 +290,8 @@ namespace AZ AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); } - void MaterialPropertyInspector::Populate() + void MaterialPropertyInspector::AddPropertiesGroup() { - AddGroupsBegin(); - - AddDetailsGroup(); - AddUvNamesGroup(); - // Copy all of the properties from the material asset to the source data that will be exported for (const auto& groupDefinition : m_editData.m_materialTypeSourceData.GetGroupDefinitionsInDisplayOrder()) { @@ -327,10 +330,14 @@ namespace AZ [this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0); AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget); } + } + void MaterialPropertyInspector::Populate() + { + AddGroupsBegin(); + AddUvNamesGroup(); + AddPropertiesGroup(); AddGroupsEnd(); - - LoadOverridesFromEntity(); } void MaterialPropertyInspector::LoadOverridesFromEntity() @@ -375,6 +382,7 @@ namespace AZ m_dirtyPropertyFlags.set(); RunEditorMaterialFunctors(); RebuildAll(); + UpdateHeading(); } void MaterialPropertyInspector::SaveOverridesToEntity(bool commitChanges) @@ -398,6 +406,9 @@ namespace AZ MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited); m_internalEditNotification = false; } + + // m_updatePreview should be set to true here for continuous preview updates as slider/color properties change but needs + // throttling } void MaterialPropertyInspector::RunEditorMaterialFunctors() @@ -521,15 +532,15 @@ 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 { const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo( - QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) + + QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" + AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." + AZ::RPI::MaterialSourceData::Extension).absoluteFilePath(); @@ -607,7 +618,8 @@ namespace AZ MaterialComponentRequestBus::Event( m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId, MaterialPropertyOverrideMap()); - QueueUpdateUI(); + m_updateUI = true; + m_updatePreview = true; }); action->setEnabled(IsLoaded()); @@ -702,10 +714,7 @@ namespace AZ void MaterialPropertyInspector::OnEntityActivated(const AZ::EntityId& entityId) { - if (m_entityId == entityId) - { - QueueUpdateUI(); - } + m_updateUI |= (m_entityId == entityId); } void MaterialPropertyInspector::OnEntityDeactivated(const AZ::EntityId& entityId) @@ -719,25 +728,39 @@ namespace AZ void MaterialPropertyInspector::OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name) { AZ_UNUSED(name); - if (m_entityId == entityId) - { - QueueUpdateUI(); - } + m_updateUI |= (m_entityId == entityId); } void MaterialPropertyInspector::OnTick(float deltaTime, ScriptTimePoint time) { AZ_UNUSED(time); AZ_UNUSED(deltaTime); - UpdateUI(); - AZ::TickBus::Handler::BusDisconnect(); + if (m_updateUI) + { + m_updateUI = false; + UpdateUI(); + } + + if (m_updatePreview) + { + m_updatePreview = false; + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_materialAssignmentId); + } } void MaterialPropertyInspector::OnMaterialsEdited() { - if (!m_internalEditNotification) + m_updateUI |= !m_internalEditNotification; + m_updatePreview = true; + } + + void MaterialPropertyInspector::OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) + { + if (m_overviewImage && m_entityId == entityId && m_materialAssignmentId == materialAssignmentId) { - QueueUpdateUI(); + m_overviewImage->setPixmap(pixmap); } } @@ -761,16 +784,6 @@ namespace AZ LoadMaterial(m_entityId, m_materialAssignmentId); } } - - void MaterialPropertyInspector::QueueUpdateUI() - { - if (!AZ::TickBus::Handler::BusIsConnected()) - { - AZ::TickBus::Handler::BusConnect(); - } - } } // namespace EditorMaterialComponentInspector } // namespace Render } // namespace AZ - -//#include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h index 048c1e19cb..82f46ebc90 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h @@ -9,6 +9,7 @@ #pragma once #if !defined(Q_MOC_RUN) +#include #include #include #include @@ -31,14 +32,13 @@ namespace AZ { namespace EditorMaterialComponentInspector { - using PropertyChangedCallback = AZStd::function; - class MaterialPropertyInspector : public AtomToolsFramework::InspectorWidget , public AzToolsFramework::IPropertyEditorNotify , public AZ::EntitySystemBus::Handler , public AZ::TickBus::Handler , public MaterialComponentNotificationBus::Handler + , public EditorMaterialSystemComponentNotificationBus::Handler { Q_OBJECT public: @@ -89,11 +89,19 @@ namespace AZ //! MaterialComponentNotificationBus::Handler overrides... void OnMaterialsEdited() override; + //! EditorMaterialSystemComponentNotificationBus::Handler overrides... + void OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, + const AZ::Render::MaterialAssignmentId& materialAssignmentId, + const QPixmap& pixmap) override; + void UpdateUI(); - void QueueUpdateUI(); - void AddDetailsGroup(); + void CreateHeading(); + void UpdateHeading(); + void AddUvNamesGroup(); + void AddPropertiesGroup(); void LoadOverridesFromEntity(); void SaveOverridesToEntity(bool commitChanges); @@ -115,7 +123,10 @@ namespace AZ AZ::RPI::MaterialPropertyFlags m_dirtyPropertyFlags = {}; AZStd::unordered_map m_groups = {}; bool m_internalEditNotification = {}; - QLabel* m_messageLabel = {}; + bool m_updateUI = {}; + bool m_updatePreview = {}; + QLabel* m_overviewText = {}; + QLabel* m_overviewImage = {}; }; } // namespace EditorMaterialComponentInspector } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index f5d38f48c4..d65fd0f2af 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -6,23 +6,25 @@ * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include +#include +#include #include +#include +#include AZ_POP_DISABLE_WARNING namespace AZ @@ -100,6 +102,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &EditorMaterialComponentSlot::GetLabel) ->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true) ->Attribute("ThumbnailCallback", &EditorMaterialComponentSlot::OpenPopupMenu) + ->Attribute("ThumbnailIcon", &EditorMaterialComponentSlot::GetPreviewPixmapData) ; } } @@ -118,6 +121,33 @@ namespace AZ } }; + AZStd::vector EditorMaterialComponentSlot::GetPreviewPixmapData() const + { + if (!GetActiveAssetId().IsValid()) + { + return {}; + } + + QPixmap pixmap; + EditorMaterialSystemComponentRequestBus::BroadcastResult( + pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId, m_id); + if (pixmap.isNull()) + { + if (m_updatePreview) + { + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id); + m_updatePreview = false; + } + return {}; + } + + QByteArray pixmapBytes; + QDataStream stream(&pixmapBytes, QIODevice::WriteOnly); + stream << pixmap; + return AZStd::vector(pixmapBytes.begin(), pixmapBytes.end()); + } + AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const { return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId(); @@ -169,14 +199,6 @@ namespace AZ ClearOverrides(); } - void EditorMaterialComponentSlot::ClearToDefaultAsset() - { - m_materialAsset = AZ::Data::Asset(GetDefaultAssetId(), AZ::AzTypeInfo::Uuid()); - MaterialComponentRequestBus::Event( - m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); - ClearOverrides(); - } - void EditorMaterialComponentSlot::ClearOverrides() { MaterialComponentRequestBus::Event( @@ -315,6 +337,10 @@ namespace AZ AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, m_entityId); + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id); + m_updatePreview = false; + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited); AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h index 377fe9a18b..78fc7449de 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h @@ -8,37 +8,52 @@ #pragma once -#include -#include - -#include -#include #include +#include +#include +#include +#include +#include namespace AZ { namespace Render { - static const size_t DefaultMaterialSlotIndex = std::numeric_limits::max(); - //! Details for a single editable material assignment struct EditorMaterialComponentSlot final { AZ_RTTI(EditorMaterialComponentSlot, "{344066EB-7C3D-4E92-B53D-3C9EBD546488}"); AZ_CLASS_ALLOCATOR(EditorMaterialComponentSlot, SystemAllocator, 0); - static void Reflect(ReflectContext* context); static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + static void Reflect(ReflectContext* context); + + //! Get cached preview image as a buffer to use as an RPE attribute + //! If a cached image isn't avalible then a request will be made to render one + AZStd::vector GetPreviewPixmapData() const; + //! Returns the overridden asset id if it's valid, otherwise gets the default asseet id AZ::Data::AssetId GetActiveAssetId() const; + + //! Returns the default asseet id of the material provded by the model AZ::Data::AssetId GetDefaultAssetId() const; + + //! Returns the display name of the material slot AZStd::string GetLabel() const; + + //! Returns true if the active material asset has a source material bool HasSourceData() const; + //! Assign a new material override asset void SetAsset(const Data::AssetId& assetId); + + //! Assign a new material override asset void SetAsset(const Data::Asset& asset); + + //! Remove material and prperty overrides void Clear(); - void ClearToDefaultAsset(); + + //! Remove prperty overrides void ClearOverrides(); void OpenMaterialExporter(); @@ -54,6 +69,7 @@ namespace AZ void OpenPopupMenu(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType); void OnMaterialChanged() const; void OnDataChanged() const; + mutable bool m_updatePreview = true; }; // Vector of slots for assignable or overridable material data. @@ -62,8 +78,8 @@ namespace AZ // Table containing all editable material data that is displayed in the edit context and inspector // The vector represents all the LODs that can have material overrides. // The container will be populated with every potential material slot on an associated model, using its default values. - // Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment map - // as overrides that will be applied to material instances + // Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment + // map as overrides that will be applied to material instances using EditorMaterialComponentSlotsByLodContainer = AZStd::vector; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp index a6b595940f..5df17a5478 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp @@ -7,6 +7,11 @@ */ #include +#include +#include +#include +#include +#include #include #include #include @@ -16,11 +21,10 @@ #include #include #include -#include #include #include #include -#include +#include // Disables warning messages triggered by the Qt library // 4251: class needs to have dll-interface to be used by clients of class @@ -30,6 +34,8 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") #include #include #include +#include +#include #include AZ_POP_DISABLE_WARNING @@ -55,7 +61,7 @@ namespace AZ { ec->Class("EditorMaterialSystemComponent", "System component that manages launching and maintaining connections the material editor.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } @@ -64,17 +70,17 @@ namespace AZ void EditorMaterialSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("EditorMaterialSystem", 0x5c93bc4e)); + provided.push_back(AZ_CRC_CE("EditorMaterialSystem")); } void EditorMaterialSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - incompatible.push_back(AZ_CRC("EditorMaterialSystem", 0x5c93bc4e)); + incompatible.push_back(AZ_CRC_CE("EditorMaterialSystem")); } void EditorMaterialSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC("ThumbnailerService", 0x65422b97)); + required.push_back(AZ_CRC_CE("PreviewRendererSystem")); } void EditorMaterialSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) @@ -89,25 +95,23 @@ namespace AZ void EditorMaterialSystemComponent::Activate() { + EditorMaterialSystemComponentNotificationBus::Handler::BusConnect(); EditorMaterialSystemComponentRequestBus::Handler::BusConnect(); - AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect(); AzToolsFramework::EditorMenuNotificationBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); - SetupThumbnails(); m_materialBrowserInteractions.reset(aznew MaterialBrowserInteractions); } void EditorMaterialSystemComponent::Deactivate() { + EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect(); EditorMaterialSystemComponentRequestBus::Handler::BusDisconnect(); - AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorMenuNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); - TeardownThumbnails(); m_materialBrowserInteractions.reset(); if (m_openMaterialEditorAction) @@ -154,11 +158,76 @@ namespace AZ } } - void EditorMaterialSystemComponent::OnApplicationAboutToStop() + void EditorMaterialSystemComponent::RenderMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) { - TeardownThumbnails(); + static constexpr const char* DefaultModelPath = "models/sphere.azmodel"; + static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset"; + + if (auto previewRenderer = AZ::Interface::Get()) + { + AZ::Data::AssetId materialAssetId = {}; + MaterialComponentRequestBus::EventResult( + materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, materialAssignmentId); + if (!materialAssetId.IsValid()) + { + MaterialComponentRequestBus::EventResult( + materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, materialAssignmentId); + if (!materialAssetId.IsValid()) + { + return; + } + } + + AZ::Render::MaterialPropertyOverrideMap propertyOverrides; + AZ::Render::MaterialComponentRequestBus::EventResult( + propertyOverrides, entityId, &AZ::Render::MaterialComponentRequestBus::Events::GetPropertyOverrides, + materialAssignmentId); + + previewRenderer->AddCaptureRequest( + { 128, + AZStd::make_shared( + previewRenderer->GetScene(), previewRenderer->GetView(), previewRenderer->GetEntityContextId(), + AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath), materialAssetId, + AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath), propertyOverrides), + [entityId, materialAssignmentId]() + { + AZ_Warning( + "EditorMaterialSystemComponent", false, "RenderMaterialPreview capture failed for entity %s slot %s.", + entityId.ToString().c_str(), materialAssignmentId.ToString().c_str()); + }, + [entityId, materialAssignmentId](const QPixmap& pixmap) + { + AZ::Render::EditorMaterialSystemComponentNotificationBus::Broadcast( + &AZ::Render::EditorMaterialSystemComponentNotificationBus::Events::OnRenderMaterialPreviewComplete, entityId, + materialAssignmentId, pixmap); + } }); + } } - + + QPixmap EditorMaterialSystemComponent::GetRenderedMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const + { + const auto& itr1 = m_materialPreviews.find(entityId); + if (itr1 != m_materialPreviews.end()) + { + const auto& itr2 = itr1->second.find(materialAssignmentId); + if (itr2 != itr1->second.end()) + { + return itr2->second; + } + } + return QPixmap(); + } + + void EditorMaterialSystemComponent::OnRenderMaterialPreviewComplete( + [[maybe_unused]] const AZ::EntityId& entityId, + [[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId, + [[maybe_unused]] const QPixmap& pixmap) + { + m_materialPreviews[entityId][materialAssignmentId] = pixmap; + } + void EditorMaterialSystemComponent::OnPopulateToolMenuItems() { if (!m_openMaterialEditorAction) @@ -201,26 +270,6 @@ namespace AZ "Material Property Inspector", LyViewPane::CategoryTools, inspectorOptions); } - void EditorMaterialSystemComponent::SetupThumbnails() - { - using namespace AzToolsFramework::Thumbnailer; - using namespace LyIntegration; - - ThumbnailerRequestsBus::Broadcast( - &ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(Thumbnails::MaterialThumbnailCache), - ThumbnailContext::DefaultContext); - } - - void EditorMaterialSystemComponent::TeardownThumbnails() - { - using namespace AzToolsFramework::Thumbnailer; - using namespace LyIntegration; - - ThumbnailerRequestsBus::Broadcast( - &ThumbnailerRequests::UnregisterThumbnailProvider, Thumbnails::MaterialThumbnailCache::ProviderName, - ThumbnailContext::DefaultContext); - } - AzToolsFramework::AssetBrowser::SourceFileDetails EditorMaterialSystemComponent::GetSourceFileDetails( const char* fullSourceFileName) { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h index 7fa43ea309..6fce538ead 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h @@ -5,31 +5,30 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once +#include +#include #include -#include #include #include -#include #include - -#include - #include +#include namespace AZ { namespace Render { //! System component that manages launching and maintaining connections with the material editor. - class EditorMaterialSystemComponent + class EditorMaterialSystemComponent final : public AZ::Component - , private EditorMaterialSystemComponentRequestBus::Handler - , private AzFramework::ApplicationLifecycleEvents::Bus::Handler - , private AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler - , private AzToolsFramework::EditorMenuNotificationBus::Handler - , private AzToolsFramework::EditorEvents::Bus::Handler + , public EditorMaterialSystemComponentNotificationBus::Handler + , public EditorMaterialSystemComponentRequestBus::Handler + , public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler + , public AzToolsFramework::EditorMenuNotificationBus::Handler + , public AzToolsFramework::EditorEvents::Bus::Handler { public: AZ_COMPONENT(EditorMaterialSystemComponent, "{96652157-DA0B-420F-B49C-0207C585144C}"); @@ -51,9 +50,13 @@ namespace AZ //! EditorMaterialSystemComponentRequestBus::Handler overrides... void OpenMaterialEditor(const AZStd::string& sourcePath) override; void OpenMaterialInspector(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override; + void RenderMaterialPreview(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override; + QPixmap GetRenderedMaterialPreview( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const override; - // AzFramework::ApplicationLifecycleEvents overrides... - void OnApplicationAboutToStop() override; + //! EditorMaterialSystemComponentNotificationBus::Handler overrides... + void OnRenderMaterialPreviewComplete( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap)override; //! AssetBrowserInteractionNotificationBus::Handler overrides... AzToolsFramework::AssetBrowser::SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override; @@ -65,12 +68,9 @@ namespace AZ // AztoolsFramework::EditorEvents::Bus::Handler overrides... void NotifyRegisterViews() override; - void SetupThumbnails(); - void TeardownThumbnails(); - QAction* m_openMaterialEditorAction = nullptr; - AZStd::unique_ptr m_materialBrowserInteractions; + AZStd::unordered_map> m_materialPreviews; }; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp deleted file mode 100644 index 0723859ff5..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp +++ /dev/null @@ -1,112 +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 - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - static constexpr const int MaterialThumbnailSize = 512; // 512 is the default size in render to texture pass - - ////////////////////////////////////////////////////////////////////////// - // MaterialThumbnail - ////////////////////////////////////////////////////////////////////////// - MaterialThumbnail::MaterialThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) - : Thumbnail(key) - { - m_assetId = GetAssetId(key, RPI::MaterialAsset::RTTI_Type()); - if (!m_assetId.IsValid()) - { - AZ_Error("MaterialThumbnail", false, "Failed to find matching assetId for the thumbnailKey."); - m_state = State::Failed; - return; - } - - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key); - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); - } - - void MaterialThumbnail::LoadThread() - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent( - RPI::MaterialAsset::RTTI_Type(), - &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, - m_key, - MaterialThumbnailSize); - // wait for response from thumbnail renderer - m_renderWait.acquire(); - } - - MaterialThumbnail::~MaterialThumbnail() - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); - } - - void MaterialThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) - { - m_pixmap = thumbnailImage; - m_renderWait.release(); - } - - void MaterialThumbnail::ThumbnailFailedToRender() - { - m_state = State::Failed; - m_renderWait.release(); - } - - void MaterialThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId) - { - if (m_assetId == assetId && - m_state == State::Ready) - { - m_state = State::Unloaded; - Load(); - } - } - - ////////////////////////////////////////////////////////////////////////// - // MaterialThumbnailCache - ////////////////////////////////////////////////////////////////////////// - MaterialThumbnailCache::MaterialThumbnailCache() - : ThumbnailCache() - { - } - - MaterialThumbnailCache::~MaterialThumbnailCache() = default; - - int MaterialThumbnailCache::GetPriority() const - { - // Material thumbnails override default source thumbnails, so carry higher priority - return 1; - } - - const char* MaterialThumbnailCache::GetProviderName() const - { - return ProviderName; - } - - bool MaterialThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const - { - return - GetAssetId(key, RPI::MaterialAsset::RTTI_Type()).IsValid() && - // in case it's a source scene file, it will contain both material and model products - // model thumbnails are handled by MeshThumbnail - !GetAssetId(key, RPI::ModelAsset::RTTI_Type()).IsValid(); - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - -#include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h deleted file mode 100644 index dba922a1b2..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h +++ /dev/null @@ -1,73 +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 - -#if !defined(Q_MOC_RUN) -#include -#include -#include -#include -#include -#endif - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - /** - * Custom material or model thumbnail that detects when an asset changes and updates the thumbnail - */ - class MaterialThumbnail - : public AzToolsFramework::Thumbnailer::Thumbnail - , public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler - , private AzFramework::AssetCatalogEventBus::Handler - { - Q_OBJECT - public: - MaterialThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key); - ~MaterialThumbnail() override; - - //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; - void ThumbnailFailedToRender() override; - - protected: - void LoadThread() override; - - private: - // AzFramework::AssetCatalogEventBus::Handler interface overrides... - void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override; - - AZStd::binary_semaphore m_renderWait; - Data::AssetId m_assetId; - }; - - /** - * Cache configuration for large material thumbnails - */ - class MaterialThumbnailCache - : public AzToolsFramework::Thumbnailer::ThumbnailCache - { - public: - MaterialThumbnailCache(); - ~MaterialThumbnailCache() override; - - int GetPriority() const override; - const char* GetProviderName() const override; - - static constexpr const char* ProviderName = "Material Thumbnails"; - - protected: - bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ 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/EditorMeshSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.cpp index 8b2f6c8a11..c6a7ff1f50 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.cpp @@ -6,13 +6,10 @@ * */ -#include #include #include -#include -#include -#include -#include +#include +#include namespace AZ { @@ -47,11 +44,6 @@ namespace AZ incompatible.push_back(AZ_CRC_CE("EditorMeshSystem")); } - void EditorMeshSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) - { - required.push_back(AZ_CRC_CE("ThumbnailerService")); - } - void EditorMeshSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { AZ_UNUSED(dependent); @@ -59,39 +51,10 @@ namespace AZ void EditorMeshSystemComponent::Activate() { - AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); - SetupThumbnails(); } void EditorMeshSystemComponent::Deactivate() { - TeardownThumbnails(); - AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); - } - - void EditorMeshSystemComponent::OnApplicationAboutToStop() - { - TeardownThumbnails(); - } - - void EditorMeshSystemComponent::SetupThumbnails() - { - using namespace AzToolsFramework::Thumbnailer; - using namespace LyIntegration; - - ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, - MAKE_TCACHE(Thumbnails::MeshThumbnailCache), - ThumbnailContext::DefaultContext); - } - - void EditorMeshSystemComponent::TeardownThumbnails() - { - using namespace AzToolsFramework::Thumbnailer; - using namespace LyIntegration; - - ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::UnregisterThumbnailProvider, - Thumbnails::MeshThumbnailCache::ProviderName, - ThumbnailContext::DefaultContext); } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.h index 64d72bc33d..a784785830 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshSystemComponent.h @@ -8,7 +8,6 @@ #pragma once #include -#include namespace AZ { @@ -17,7 +16,6 @@ namespace AZ //! System component that sets up necessary logic related to EditorMeshComponent. class EditorMeshSystemComponent : public AZ::Component - , private AzFramework::ApplicationLifecycleEvents::Bus::Handler { public: AZ_COMPONENT(EditorMeshSystemComponent, "{4D332E3D-C4FC-410B-A915-8E234CBDD4EC}"); @@ -26,20 +24,12 @@ namespace AZ static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); protected: // AZ::Component interface overrides... void Activate() override; void Deactivate() override; - - private: - // AzFramework::ApplicationLifecycleEvents overrides... - void OnApplicationAboutToStop() override; - - void SetupThumbnails(); - void TeardownThumbnails(); }; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp deleted file mode 100644 index 658d420a16..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp +++ /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 - * - */ - -#include -#include -#include -#include -#include - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - static constexpr const int MeshThumbnailSize = 512; // 512 is the default size in render to texture pass - - ////////////////////////////////////////////////////////////////////////// - // MeshThumbnail - ////////////////////////////////////////////////////////////////////////// - MeshThumbnail::MeshThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) - : Thumbnail(key) - { - m_assetId = GetAssetId(key, RPI::ModelAsset::RTTI_Type()); - if (!m_assetId.IsValid()) - { - AZ_Error("MeshThumbnail", false, "Failed to find matching assetId for the thumbnailKey."); - m_state = State::Failed; - return; - } - - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key); - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); - } - - void MeshThumbnail::LoadThread() - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent( - RPI::ModelAsset::RTTI_Type(), - &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, - m_key, - MeshThumbnailSize); - // wait for response from thumbnail renderer - m_renderWait.acquire(); - } - - MeshThumbnail::~MeshThumbnail() - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); - } - - void MeshThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) - { - m_pixmap = thumbnailImage; - m_renderWait.release(); - } - - void MeshThumbnail::ThumbnailFailedToRender() - { - m_state = State::Failed; - m_renderWait.release(); - } - - void MeshThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId) - { - if (m_assetId == assetId && - m_state == State::Ready) - { - m_state = State::Unloaded; - Load(); - } - } - - ////////////////////////////////////////////////////////////////////////// - // MeshThumbnailCache - ////////////////////////////////////////////////////////////////////////// - MeshThumbnailCache::MeshThumbnailCache() - : ThumbnailCache() - { - } - - MeshThumbnailCache::~MeshThumbnailCache() = default; - - int MeshThumbnailCache::GetPriority() const - { - // Material thumbnails override default source thumbnails, so carry higher priority - return 1; - } - - const char* MeshThumbnailCache::GetProviderName() const - { - return ProviderName; - } - - bool MeshThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const - { - return GetAssetId(key, RPI::ModelAsset::RTTI_Type()).IsValid(); - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - -#include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h deleted file mode 100644 index 2975b6950c..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h +++ /dev/null @@ -1,72 +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 - -#if !defined(Q_MOC_RUN) -#include -#include -#include -#include -#endif - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - /** - * Custom material or model thumbnail that detects when an asset changes and updates the thumbnail - */ - class MeshThumbnail - : public AzToolsFramework::Thumbnailer::Thumbnail - , public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler - , private AzFramework::AssetCatalogEventBus::Handler - { - Q_OBJECT - public: - MeshThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key); - ~MeshThumbnail() override; - - //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; - void ThumbnailFailedToRender() override; - - protected: - void LoadThread() override; - - private: - // AzFramework::AssetCatalogEventBus::Handler interface overrides... - void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override; - - AZStd::binary_semaphore m_renderWait; - Data::AssetId m_assetId; - }; - - /** - * Cache configuration for large material thumbnails - */ - class MeshThumbnailCache - : public AzToolsFramework::Thumbnailer::ThumbnailCache - { - public: - MeshThumbnailCache(); - ~MeshThumbnailCache() override; - - int GetPriority() const override; - const char* GetProviderName() const override; - - static constexpr const char* ProviderName = "Mesh Thumbnails"; - - protected: - bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ 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 d7fcd124d0..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) @@ -321,7 +322,7 @@ namespace AZ } char projectPath[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN); // retrieve the source cubemap path from the configuration // we need to make sure to use the same source cubemap for each bake 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/SharedPreview/SharedPreviewContent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewContent.cpp new file mode 100644 index 0000000000..91b6d2b02a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewContent.cpp @@ -0,0 +1,173 @@ +/* + * 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 + +namespace AZ +{ + namespace LyIntegration + { + SharedPreviewContent::SharedPreviewContent( + RPI::ScenePtr scene, + RPI::ViewPtr view, + AZ::Uuid entityContextId, + const Data::AssetId& modelAssetId, + const Data::AssetId& materialAssetId, + const Data::AssetId& lightingPresetAssetId, + const Render::MaterialPropertyOverrideMap& materialPropertyOverrides) + : m_scene(scene) + , m_view(view) + , m_entityContextId(entityContextId) + , m_materialPropertyOverrides(materialPropertyOverrides) + { + // Create preview model + AzFramework::EntityContextRequestBus::EventResult( + m_modelEntity, m_entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "SharedPreviewContentModel"); + m_modelEntity->CreateComponent(Render::MeshComponentTypeId); + m_modelEntity->CreateComponent(Render::MaterialComponentTypeId); + m_modelEntity->CreateComponent(azrtti_typeid()); + m_modelEntity->Init(); + m_modelEntity->Activate(); + + m_modelAsset.Create(modelAssetId); + m_materialAsset.Create(materialAssetId); + m_lightingPresetAsset.Create(lightingPresetAssetId); + } + + SharedPreviewContent::~SharedPreviewContent() + { + if (m_modelEntity) + { + m_modelEntity->Deactivate(); + AzFramework::EntityContextRequestBus::Event( + m_entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity); + m_modelEntity = nullptr; + } + } + + void SharedPreviewContent::Load() + { + m_modelAsset.QueueLoad(); + m_materialAsset.QueueLoad(); + m_lightingPresetAsset.QueueLoad(); + } + + bool SharedPreviewContent::IsReady() const + { + return (!m_modelAsset.GetId().IsValid() || m_modelAsset.IsReady()) && + (!m_materialAsset.GetId().IsValid() || m_materialAsset.IsReady()) && + (!m_lightingPresetAsset.GetId().IsValid() || m_lightingPresetAsset.IsReady()); + } + + bool SharedPreviewContent::IsError() const + { + return m_modelAsset.IsError() || m_materialAsset.IsError() || m_lightingPresetAsset.IsError(); + } + + void SharedPreviewContent::ReportErrors() + { + AZ_Warning( + "SharedPreviewContent", !m_modelAsset.GetId().IsValid() || m_modelAsset.IsReady(), "Asset failed to load in time: %s", + m_modelAsset.ToString().c_str()); + AZ_Warning( + "SharedPreviewContent", !m_materialAsset.GetId().IsValid() || m_materialAsset.IsReady(), "Asset failed to load in time: %s", + m_materialAsset.ToString().c_str()); + AZ_Warning( + "SharedPreviewContent", !m_lightingPresetAsset.GetId().IsValid() || m_lightingPresetAsset.IsReady(), + "Asset failed to load in time: %s", m_lightingPresetAsset.ToString().c_str()); + } + + void SharedPreviewContent::Update() + { + UpdateModel(); + UpdateLighting(); + UpdateCamera(); + } + + void SharedPreviewContent::UpdateModel() + { + Render::MeshComponentRequestBus::Event( + m_modelEntity->GetId(), &Render::MeshComponentRequestBus::Events::SetModelAsset, m_modelAsset); + + Render::MaterialComponentRequestBus::Event( + m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetMaterialOverride, + Render::DefaultMaterialAssignmentId, m_materialAsset.GetId()); + + Render::MaterialComponentRequestBus::Event( + m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetPropertyOverrides, + Render::DefaultMaterialAssignmentId, m_materialPropertyOverrides); + } + + void SharedPreviewContent::UpdateLighting() + { + if (m_lightingPresetAsset.IsReady()) + { + auto preset = m_lightingPresetAsset->GetDataAs(); + if (preset) + { + auto iblFeatureProcessor = m_scene->GetFeatureProcessor(); + auto postProcessFeatureProcessor = m_scene->GetFeatureProcessor(); + auto postProcessSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId()); + auto exposureControlSettingInterface = postProcessSettingInterface->GetOrCreateExposureControlSettingsInterface(); + auto directionalLightFeatureProcessor = + m_scene->GetFeatureProcessor(); + auto skyboxFeatureProcessor = m_scene->GetFeatureProcessor(); + skyboxFeatureProcessor->Enable(true); + skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap); + + Camera::Configuration cameraConfig; + cameraConfig.m_fovRadians = FieldOfView; + cameraConfig.m_nearClipDistance = NearDist; + cameraConfig.m_farClipDistance = FarDist; + cameraConfig.m_frustumWidth = 100.0f; + cameraConfig.m_frustumHeight = 100.0f; + + AZStd::vector lightHandles; + + preset->ApplyLightingPreset( + iblFeatureProcessor, skyboxFeatureProcessor, exposureControlSettingInterface, directionalLightFeatureProcessor, + cameraConfig, lightHandles); + } + } + } + + void SharedPreviewContent::UpdateCamera() + { + // Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it + Vector3 center = {}; + float radius = {}; + if (m_modelAsset.IsReady()) + { + m_modelAsset->GetAabb().GetAsSphere(center, radius); + } + + const auto distance = radius + NearDist; + const auto cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), CameraRotationAngle); + const auto cameraPosition = center + cameraRotation.TransformVector(Vector3(0.0f, distance, 0.0f)); + const auto cameraTransform = Transform::CreateLookAt(cameraPosition, center); + m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform)); + } + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewContent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewContent.h new file mode 100644 index 0000000000..7308aa19bc --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewContent.h @@ -0,0 +1,67 @@ +/* + * 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 LyIntegration + { + //! Creates a simple scene used for most previews and thumbnails + class SharedPreviewContent final : public AtomToolsFramework::PreviewContent + { + public: + AZ_CLASS_ALLOCATOR(SharedPreviewContent, AZ::SystemAllocator, 0); + + SharedPreviewContent( + RPI::ScenePtr scene, + RPI::ViewPtr view, + AZ::Uuid entityContextId, + const Data::AssetId& modelAssetId, + const Data::AssetId& materialAssetId, + const Data::AssetId& lightingPresetAssetId, + const Render::MaterialPropertyOverrideMap& materialPropertyOverrides); + + ~SharedPreviewContent() override; + + void Load() override; + bool IsReady() const override; + bool IsError() const override; + void ReportErrors() override; + void Update() override; + + private: + void UpdateModel(); + void UpdateLighting(); + void UpdateCamera(); + + static constexpr float AspectRatio = 1.0f; + static constexpr float NearDist = 0.001f; + static constexpr float FarDist = 100.0f; + static constexpr float FieldOfView = Constants::HalfPi; + static constexpr float CameraRotationAngle = Constants::QuarterPi / 2.0f; + + RPI::ScenePtr m_scene; + RPI::ViewPtr m_view; + AZ::Uuid m_entityContextId; + Entity* m_modelEntity = nullptr; + + Data::Asset m_modelAsset; + Data::Asset m_materialAsset; + Data::Asset m_lightingPresetAsset; + Render::MaterialPropertyOverrideMap m_materialPropertyOverrides; + }; + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/ThumbnailUtils.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewUtils.cpp similarity index 55% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/ThumbnailUtils.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewUtils.cpp index c8f67138ef..c2988cf53d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/ThumbnailUtils.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewUtils.cpp @@ -11,37 +11,42 @@ #include #include #include -#include +#include +#include namespace AZ { namespace LyIntegration { - namespace Thumbnails + namespace SharedPreviewUtils { - Data::AssetId GetAssetId(AzToolsFramework::Thumbnailer::SharedThumbnailKey key, const Data::AssetType& assetType) + Data::AssetId GetAssetId( + AzToolsFramework::Thumbnailer::SharedThumbnailKey key, + const Data::AssetType& assetType, + const Data::AssetId& defaultAssetId) { - static const Data::AssetId invalidAssetId; - // if it's a source thumbnail key, find first product with a matching asset type auto sourceKey = azrtti_cast(key.data()); if (sourceKey) { bool foundIt = false; AZStd::vector productsAssetInfo; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID, sourceKey->GetSourceUuid(), productsAssetInfo); + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID, + sourceKey->GetSourceUuid(), productsAssetInfo); if (!foundIt) { - return invalidAssetId; + return defaultAssetId; } - auto assetInfoIt = AZStd::find_if(productsAssetInfo.begin(), productsAssetInfo.end(), + auto assetInfoIt = AZStd::find_if( + productsAssetInfo.begin(), productsAssetInfo.end(), [&assetType](const Data::AssetInfo& assetInfo) { return assetInfo.m_assetType == assetType; }); if (assetInfoIt == productsAssetInfo.end()) { - return invalidAssetId; + return defaultAssetId; } return assetInfoIt->m_assetId; @@ -53,10 +58,9 @@ namespace AZ { return productKey->GetAssetId(); } - return invalidAssetId; + return defaultAssetId; } - QString WordWrap(const QString& string, int maxLength) { QString result; @@ -81,6 +85,32 @@ namespace AZ } return result; } - } // namespace Thumbnails + + AZStd::unordered_set GetSupportedAssetTypes() + { + return { RPI::AnyAsset::RTTI_Type(), RPI::MaterialAsset::RTTI_Type(), RPI::ModelAsset::RTTI_Type() }; + } + + bool IsSupportedAssetType(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) + { + for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes()) + { + const AZ::Data::AssetId& assetId = SharedPreviewUtils::GetAssetId(key, typeId); + if (assetId.IsValid()) + { + if (typeId == RPI::AnyAsset::RTTI_Type()) + { + AZ::Data::AssetInfo assetInfo; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, assetId); + return AzFramework::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), "lightingpreset.azasset"); + } + return true; + } + } + + return false; + } + } // namespace SharedPreviewUtils } // namespace LyIntegration } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/ThumbnailUtils.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewUtils.h similarity index 53% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/ThumbnailUtils.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewUtils.h index e4efcd6b49..6c5d83d22a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/ThumbnailUtils.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewUtils.h @@ -18,13 +18,23 @@ namespace AZ { namespace LyIntegration { - namespace Thumbnails + namespace SharedPreviewUtils { //! Get assetId by assetType that belongs to either source or product thumbnail key - Data::AssetId GetAssetId(AzToolsFramework::Thumbnailer::SharedThumbnailKey key, const Data::AssetType& assetType); + Data::AssetId GetAssetId( + AzToolsFramework::Thumbnailer::SharedThumbnailKey key, + const Data::AssetType& assetType, + const Data::AssetId& defaultAssetId = {}); - //! Word wrap function for previewer QLabel, since by default it does not break long words such as filenames, so manual word wrap needed + //! Word wrap function for previewer QLabel, since by default it does not break long words such as filenames, so manual word + //! wrap needed QString WordWrap(const QString& string, int maxLength); - } // namespace Thumbnails + + //! Get the set of all asset types supported by the shared preview + AZStd::unordered_set GetSupportedAssetTypes(); + + //! Determine if a thumbnail key has an asset supported by the shared preview + bool IsSupportedAssetType(AzToolsFramework::Thumbnailer::SharedThumbnailKey key); + } // namespace SharedPreviewUtils } // namespace LyIntegration } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp similarity index 69% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp index 9730c7d4a9..942af55721 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp @@ -7,23 +7,21 @@ */ #include - +#include #include #include -#include -#include #include - -#include -#include +#include +#include +#include // Disables warning messages triggered by the Qt library -// 4251: class needs to have dll-interface to be used by clients of class +// 4251: class needs to have dll-interface to be used by clients of class // 4800: forcing value to bool 'true' or 'false' (performance warning) AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") -#include -#include #include +#include +#include AZ_POP_DISABLE_WARNING namespace AZ @@ -32,18 +30,22 @@ namespace AZ { static constexpr int CharWidth = 6; - CommonPreviewer::CommonPreviewer(QWidget* parent) + SharedPreviewer::SharedPreviewer(QWidget* parent) : Previewer(parent) - , m_ui(new Ui::CommonPreviewerClass()) + , m_ui(new Ui::SharedPreviewerClass()) { m_ui->setupUi(this); } - CommonPreviewer::~CommonPreviewer() + SharedPreviewer::~SharedPreviewer() + { + } + + void SharedPreviewer::Clear() const { } - void CommonPreviewer::Display(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) + void SharedPreviewer::Display(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) { using namespace AzToolsFramework::AssetBrowser; using namespace AzToolsFramework::Thumbnailer; @@ -54,23 +56,23 @@ namespace AZ UpdateFileInfo(); } - const QString& CommonPreviewer::GetName() const + const QString& SharedPreviewer::GetName() const { return m_name; } - void CommonPreviewer::resizeEvent([[maybe_unused]] QResizeEvent* event) + void SharedPreviewer::resizeEvent([[maybe_unused]] QResizeEvent* event) { m_ui->m_previewWidget->setMaximumHeight(m_ui->m_previewWidget->width()); UpdateFileInfo(); } - void CommonPreviewer::UpdateFileInfo() const + void SharedPreviewer::UpdateFileInfo() const { - m_ui->m_fileInfoLabel->setText(Thumbnails::WordWrap(m_fileInfo, m_ui->m_fileInfoLabel->width() / CharWidth)); + m_ui->m_fileInfoLabel->setText(SharedPreviewUtils::WordWrap(m_fileInfo, m_ui->m_fileInfoLabel->width() / CharWidth)); } } // namespace LyIntegration } // namespace AZ -#include +#include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.h similarity index 73% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.h index 1dfa8788e0..ab6f793982 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.h @@ -5,22 +5,23 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #if !defined(Q_MOC_RUN) -#include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include #include +#include AZ_POP_DISABLE_WARNING #endif namespace Ui { - class CommonPreviewerClass; + class SharedPreviewerClass; } namespace AzToolsFramework @@ -30,8 +31,8 @@ namespace AzToolsFramework class ProductAssetBrowserEntry; class SourceAssetBrowserEntry; class AssetBrowserEntry; - } -} + } // namespace AssetBrowser +} // namespace AzToolsFramework class QResizeEvent; @@ -39,18 +40,17 @@ namespace AZ { namespace LyIntegration { - class CommonPreviewer final - : public AzToolsFramework::AssetBrowser::Previewer + class SharedPreviewer final : public AzToolsFramework::AssetBrowser::Previewer { Q_OBJECT public: - AZ_CLASS_ALLOCATOR(CommonPreviewer, AZ::SystemAllocator, 0); + AZ_CLASS_ALLOCATOR(SharedPreviewer, AZ::SystemAllocator, 0); - explicit CommonPreviewer(QWidget* parent = nullptr); - ~CommonPreviewer(); + explicit SharedPreviewer(QWidget* parent = nullptr); + ~SharedPreviewer(); // AzToolsFramework::AssetBrowser::Previewer overrides... - void Clear() const override {} + void Clear() const override; void Display(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) override; const QString& GetName() const override; @@ -60,9 +60,9 @@ namespace AZ private: void UpdateFileInfo() const; - QScopedPointer m_ui; + QScopedPointer m_ui; QString m_fileInfo; - QString m_name = "CommonPreviewer"; + QString m_name = "SharedPreviewer"; }; } // namespace LyIntegration } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.ui b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.ui similarity index 97% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.ui rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.ui index f97dde0a1f..139231d607 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewer.ui +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.ui @@ -1,7 +1,7 @@ - CommonPreviewerClass - + SharedPreviewerClass + 0 diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewerFactory.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewerFactory.cpp new file mode 100644 index 0000000000..14d6e5b5f0 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewerFactory.cpp @@ -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 + * + */ + +#include +#include +#include +#include + +namespace AZ +{ + namespace LyIntegration + { + AzToolsFramework::AssetBrowser::Previewer* SharedPreviewerFactory::CreatePreviewer(QWidget* parent) const + { + return new SharedPreviewer(parent); + } + + bool SharedPreviewerFactory::IsEntrySupported(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const + { + return SharedPreviewUtils::IsSupportedAssetType(entry->GetThumbnailKey()); + } + + const QString& SharedPreviewerFactory::GetName() const + { + return m_name; + } + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewerFactory.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewerFactory.h similarity index 76% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewerFactory.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewerFactory.h index 1f9cc9d5df..e3021cb8ab 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewerFactory.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewerFactory.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include @@ -18,14 +19,13 @@ namespace AZ { namespace LyIntegration { - class CommonPreviewerFactory final - : public AzToolsFramework::AssetBrowser::PreviewerFactory + class SharedPreviewerFactory final : public AzToolsFramework::AssetBrowser::PreviewerFactory { public: - AZ_CLASS_ALLOCATOR(CommonPreviewerFactory, AZ::SystemAllocator, 0); + AZ_CLASS_ALLOCATOR(SharedPreviewerFactory, AZ::SystemAllocator, 0); - CommonPreviewerFactory() = default; - ~CommonPreviewerFactory() = default; + SharedPreviewerFactory() = default; + ~SharedPreviewerFactory() = default; // AzToolsFramework::AssetBrowser::PreviewerFactory overrides... AzToolsFramework::AssetBrowser::Previewer* CreatePreviewer(QWidget* parent = nullptr) const override; @@ -33,7 +33,7 @@ namespace AZ const QString& GetName() const override; private: - QString m_name = "CommonPreviewer"; + QString m_name = "SharedPreviewer"; }; } // namespace LyIntegration } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp new file mode 100644 index 0000000000..5d82bac139 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp @@ -0,0 +1,106 @@ +/* + * 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 + +namespace AZ +{ + namespace LyIntegration + { + static constexpr const int SharedThumbnailSize = 256; + + ////////////////////////////////////////////////////////////////////////// + // SharedThumbnail + ////////////////////////////////////////////////////////////////////////// + SharedThumbnail::SharedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) + : Thumbnail(key) + { + for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes()) + { + const AZ::Data::AssetId& assetId = SharedPreviewUtils::GetAssetId(key, typeId); + if (assetId.IsValid()) + { + m_assetId = assetId; + m_typeId = typeId; + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key); + AzFramework::AssetCatalogEventBus::Handler::BusConnect(); + return; + } + } + + AZ_Error("SharedThumbnail", false, "Failed to find matching assetId for the thumbnailKey."); + m_state = State::Failed; + } + + void SharedThumbnail::LoadThread() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent( + m_typeId, &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, m_key, SharedThumbnailSize); + // wait for response from thumbnail renderer + m_renderWait.acquire(); + } + + SharedThumbnail::~SharedThumbnail() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + } + + void SharedThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) + { + m_pixmap = thumbnailImage; + m_renderWait.release(); + } + + void SharedThumbnail::ThumbnailFailedToRender() + { + m_state = State::Failed; + m_renderWait.release(); + } + + void SharedThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId) + { + if (m_assetId == assetId && m_state == State::Ready) + { + m_state = State::Unloaded; + Load(); + } + } + + ////////////////////////////////////////////////////////////////////////// + // SharedThumbnailCache + ////////////////////////////////////////////////////////////////////////// + SharedThumbnailCache::SharedThumbnailCache() + : ThumbnailCache() + { + } + + SharedThumbnailCache::~SharedThumbnailCache() = default; + + int SharedThumbnailCache::GetPriority() const + { + // Custom thumbnails have a higher priority to override default source thumbnails + return 1; + } + + const char* SharedThumbnailCache::GetProviderName() const + { + return ProviderName; + } + + bool SharedThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const + { + return SharedPreviewUtils::IsSupportedAssetType(key); + } + } // namespace LyIntegration +} // namespace AZ + +#include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.h new file mode 100644 index 0000000000..dee433e0bb --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.h @@ -0,0 +1,66 @@ +/* + * 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 +{ + namespace LyIntegration + { + //! Custom thumbnail for most common Atom assets + //! Detects asset changes and updates the thumbnail + class SharedThumbnail final + : public AzToolsFramework::Thumbnailer::Thumbnail + , public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler + , private AzFramework::AssetCatalogEventBus::Handler + { + Q_OBJECT + public: + SharedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key); + ~SharedThumbnail() override; + + //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... + void ThumbnailRendered(const QPixmap& thumbnailImage) override; + void ThumbnailFailedToRender() override; + + protected: + void LoadThread() override; + + private: + // AzFramework::AssetCatalogEventBus::Handler interface overrides... + void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override; + + AZStd::binary_semaphore m_renderWait; + Data::AssetId m_assetId; + AZ::Uuid m_typeId; + }; + + //! Cache configuration for shared thumbnails + class SharedThumbnailCache final : public AzToolsFramework::Thumbnailer::ThumbnailCache + { + public: + SharedThumbnailCache(); + ~SharedThumbnailCache() override; + + int GetPriority() const override; + const char* GetProviderName() const override; + + static constexpr const char* ProviderName = "Common Feature Shared Thumbnail Provider"; + + protected: + bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override; + }; + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnailRenderer.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnailRenderer.cpp new file mode 100644 index 0000000000..c43ba5f1cf --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnailRenderer.cpp @@ -0,0 +1,75 @@ +/* + * 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 AZ +{ + namespace LyIntegration + { + SharedThumbnailRenderer::SharedThumbnailRenderer() + { + m_defaultModelAsset.Create(DefaultModelAssetId, true); + m_defaultMaterialAsset.Create(DefaultMaterialAssetId, true); + m_defaultLightingPresetAsset.Create(DefaultLightingPresetAssetId, true); + + for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes()) + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(typeId); + } + SystemTickBus::Handler::BusConnect(); + } + + SharedThumbnailRenderer::~SharedThumbnailRenderer() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect(); + SystemTickBus::Handler::BusDisconnect(); + } + + void SharedThumbnailRenderer::RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) + { + if (auto previewRenderer = AZ::Interface::Get()) + { + previewRenderer->AddCaptureRequest( + { thumbnailSize, + AZStd::make_shared( + previewRenderer->GetScene(), previewRenderer->GetView(), previewRenderer->GetEntityContextId(), + SharedPreviewUtils::GetAssetId(thumbnailKey, RPI::ModelAsset::RTTI_Type(), DefaultModelAssetId), + SharedPreviewUtils::GetAssetId(thumbnailKey, RPI::MaterialAsset::RTTI_Type(), DefaultMaterialAssetId), + SharedPreviewUtils::GetAssetId(thumbnailKey, RPI::AnyAsset::RTTI_Type(), DefaultLightingPresetAssetId), + Render::MaterialPropertyOverrideMap()), + [thumbnailKey]() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( + thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); + }, + [thumbnailKey](const QPixmap& pixmap) + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( + thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered, pixmap); + } }); + } + } + + bool SharedThumbnailRenderer::Installed() const + { + return true; + } + + void SharedThumbnailRenderer::OnSystemTick() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents(); + } + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnailRenderer.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnailRenderer.h new file mode 100644 index 0000000000..4db7728109 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnailRenderer.h @@ -0,0 +1,57 @@ +/* + * 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 + +namespace AZ +{ + namespace LyIntegration + { + //! Provides custom thumbnail rendering of supported asset types + class SharedThumbnailRenderer final + : public AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler + , public SystemTickBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(SharedThumbnailRenderer, AZ::SystemAllocator, 0); + + SharedThumbnailRenderer(); + ~SharedThumbnailRenderer(); + + private: + //! ThumbnailerRendererRequestsBus::Handler interface overrides... + void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override; + bool Installed() const override; + + //! SystemTickBus::Handler interface overrides... + void OnSystemTick() override; + + // Default assets to be kept loaded and used for rendering if not overridden + static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset"; + const Data::AssetId DefaultLightingPresetAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath); + Data::Asset m_defaultLightingPresetAsset; + + static constexpr const char* DefaultModelPath = "models/sphere.azmodel"; + const Data::AssetId DefaultModelAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath); + Data::Asset m_defaultModelAsset; + + static constexpr const char* DefaultMaterialPath = ""; + const Data::AssetId DefaultMaterialAssetId; + Data::Asset m_defaultMaterialAsset; + }; + } // namespace LyIntegration +} // namespace AZ 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/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewerFactory.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewerFactory.cpp deleted file mode 100644 index 856d38d6cb..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Preview/CommonPreviewerFactory.cpp +++ /dev/null @@ -1,37 +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 - -namespace AZ -{ - namespace LyIntegration - { - AzToolsFramework::AssetBrowser::Previewer* CommonPreviewerFactory::CreatePreviewer(QWidget* parent) const - { - return new CommonPreviewer(parent); - } - - bool CommonPreviewerFactory::IsEntrySupported(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const - { - return - Thumbnails::GetAssetId(entry->GetThumbnailKey(), RPI::MaterialAsset::RTTI_Type()).IsValid() || - Thumbnails::GetAssetId(entry->GetThumbnailKey(), RPI::ModelAsset::RTTI_Type()).IsValid(); - } - - const QString& CommonPreviewerFactory::GetName() const - { - return m_name; - } - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp deleted file mode 100644 index 9c69fdd995..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp +++ /dev/null @@ -1,120 +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 AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - CommonThumbnailRenderer::CommonThumbnailRenderer() - : m_data(new ThumbnailRendererData) - { - // CommonThumbnailRenderer supports both models and materials, but we connect on materialAssetType - // since MaterialOrModelThumbnail dispatches event on materialAssetType address too - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type()); - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type()); - SystemTickBus::Handler::BusConnect(); - ThumbnailFeatureProcessorProviderBus::Handler::BusConnect(); - - m_steps[Step::Initialize] = AZStd::make_shared(this); - m_steps[Step::FindThumbnailToRender] = AZStd::make_shared(this); - m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared(this); - m_steps[Step::Capture] = AZStd::make_shared(this); - m_steps[Step::ReleaseResources] = AZStd::make_shared(this); - - m_minimalFeatureProcessors = - { - "AZ::Render::TransformServiceFeatureProcessor", - "AZ::Render::MeshFeatureProcessor", - "AZ::Render::SimplePointLightFeatureProcessor", - "AZ::Render::SimpleSpotLightFeatureProcessor", - "AZ::Render::PointLightFeatureProcessor", - // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow - // flickering [ATOM-13568] - // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. - // Possibly re-enable with [GFX TODO][ATOM-13639] - // "AZ::Render::DirectionalLightFeatureProcessor", - "AZ::Render::DiskLightFeatureProcessor", - "AZ::Render::CapsuleLightFeatureProcessor", - "AZ::Render::QuadLightFeatureProcessor", - "AZ::Render::DecalTextureArrayFeatureProcessor", - "AZ::Render::ImageBasedLightFeatureProcessor", - "AZ::Render::PostProcessFeatureProcessor", - "AZ::Render::SkyBoxFeatureProcessor" - }; - } - - CommonThumbnailRenderer::~CommonThumbnailRenderer() - { - if (m_currentStep != Step::None) - { - CommonThumbnailRenderer::SetStep(Step::ReleaseResources); - } - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect(); - SystemTickBus::Handler::BusDisconnect(); - ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect(); - } - - void CommonThumbnailRenderer::SetStep(Step step) - { - if (m_currentStep != Step::None) - { - m_steps[m_currentStep]->Stop(); - } - m_currentStep = step; - m_steps[m_currentStep]->Start(); - } - - Step CommonThumbnailRenderer::GetStep() const - { - return m_currentStep; - } - - bool CommonThumbnailRenderer::Installed() const - { - return true; - } - - void CommonThumbnailRenderer::OnSystemTick() - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents(); - } - - const AZStd::vector& CommonThumbnailRenderer::GetCustomFeatureProcessors() const - { - return m_minimalFeatureProcessors; - } - - AZStd::shared_ptr CommonThumbnailRenderer::GetData() const - { - return m_data; - } - - void CommonThumbnailRenderer::RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) - { - m_data->m_thumbnailSize = thumbnailSize; - m_data->m_thumbnailQueue.push(thumbnailKey); - if (m_currentStep == Step::None) - { - SetStep(Step::Initialize); - } - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h deleted file mode 100644 index 7c3b17e104..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h +++ /dev/null @@ -1,69 +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 - -// Disables warning messages triggered by the Qt library -// 4251: class needs to have dll-interface to be used by clients of class -// 4800: forcing value to bool 'true' or 'false' (performance warning) -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - class ThumbnailRendererStep; - - //! Provides custom rendering of material and model thumbnails - class CommonThumbnailRenderer - : public ThumbnailRendererContext - , private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler - , private SystemTickBus::Handler - , private ThumbnailFeatureProcessorProviderBus::Handler - { - public: - AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0) - - CommonThumbnailRenderer(); - ~CommonThumbnailRenderer(); - - //! ThumbnailRendererContext overrides... - void SetStep(Step step) override; - Step GetStep() const override; - AZStd::shared_ptr GetData() const override; - - private: - //! ThumbnailerRendererRequestsBus::Handler interface overrides... - void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override; - bool Installed() const override; - - //! SystemTickBus::Handler interface overrides... - void OnSystemTick() override; - - //! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides... - const AZStd::vector& GetCustomFeatureProcessors() const override; - - AZStd::unordered_map> m_steps; - Step m_currentStep = Step::None; - AZStd::shared_ptr m_data; - AZStd::vector m_minimalFeatureProcessors; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererContext.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererContext.h deleted file mode 100644 index d3bb2be64a..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererContext.h +++ /dev/null @@ -1,42 +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 - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - struct ThumbnailRendererData; - - enum class Step - { - None, - Initialize, - FindThumbnailToRender, - WaitForAssetsToLoad, - Capture, - ReleaseResources - }; - - //! An interface for ThumbnailRendererSteps to communicate with thumbnail renderer - class ThumbnailRendererContext - { - public: - virtual void SetStep(Step step) = 0; - virtual Step GetStep() const = 0; - virtual AZStd::shared_ptr GetData() const = 0; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h deleted file mode 100644 index c471cf90d7..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h +++ /dev/null @@ -1,70 +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 "Atom/RPI.Reflect/Model/ModelAsset.h" - -#include -#include -#include -#include -#include -#include - -namespace AzFramework -{ - class Scene; -} - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - //! ThumbnailRendererData encapsulates all data used by thumbnail renderer and caches assets - struct ThumbnailRendererData final - { - static constexpr const char* LightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset"; - static constexpr const char* DefaultModelPath = "models/sphere.azmodel"; - static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial"; - - RPI::ScenePtr m_scene; - AZStd::string m_sceneName = "Material Thumbnail Scene"; - AZStd::string m_pipelineName = "Material Thumbnail Pipeline"; - AZStd::shared_ptr m_frameworkScene; - RPI::RenderPipelinePtr m_renderPipeline; - AZStd::unique_ptr m_entityContext; - AZStd::vector m_passHierarchy; - - RPI::ViewPtr m_view = nullptr; - Entity* m_modelEntity = nullptr; - - int m_thumbnailSize = 512; - - //! Incoming thumbnail requests are appended to this queue and processed one at a time in OnTick function. - AZStd::queue m_thumbnailQueue; - //! Current thumbnail key being rendered. - AzToolsFramework::Thumbnailer::SharedThumbnailKey m_thumbnailKeyRendered; - - Data::Asset m_lightingPresetAsset; - - Data::Asset m_defaultModelAsset; - //! Model asset about to be rendered - Data::Asset m_modelAsset; - - Data::Asset m_defaultMaterialAsset; - //! Material asset about to be rendered - Data::Asset m_materialAsset; - - AZStd::unordered_set m_assetsToLoad; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp deleted file mode 100644 index f728d56bbc..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp +++ /dev/null @@ -1,130 +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 -#include -#include -#include -#include - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - CaptureStep::CaptureStep(ThumbnailRendererContext* context) - : ThumbnailRendererStep(context) - { - } - - void CaptureStep::Start() - { - if (!m_context->GetData()->m_materialAsset || - !m_context->GetData()->m_modelAsset) - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - m_context->GetData()->m_thumbnailKeyRendered, - &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); - m_context->SetStep(Step::FindThumbnailToRender); - return; - } - Render::MaterialComponentRequestBus::Event( - m_context->GetData()->m_modelEntity->GetId(), - &Render::MaterialComponentRequestBus::Events::SetDefaultMaterialOverride, - m_context->GetData()->m_materialAsset.GetId()); - Render::MeshComponentRequestBus::Event( - m_context->GetData()->m_modelEntity->GetId(), - &Render::MeshComponentRequestBus::Events::SetModelAsset, - m_context->GetData()->m_modelAsset); - RepositionCamera(); - m_readyToCapture = true; - m_ticksToCapture = 1; - TickBus::Handler::BusConnect(); - } - - void CaptureStep::Stop() - { - m_context->GetData()->m_renderPipeline->RemoveFromRenderTick(); - TickBus::Handler::BusDisconnect(); - Render::FrameCaptureNotificationBus::Handler::BusDisconnect(); - } - - void CaptureStep::RepositionCamera() const - { - // Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it - const Aabb& aabb = m_context->GetData()->m_modelAsset->GetAabb(); - Vector3 modelCenter; - float radius; - aabb.GetAsSphere(modelCenter, radius); - - float distance = StartingDistanceMultiplier * - GetMax(GetMax(aabb.GetExtents().GetX(), aabb.GetExtents().GetY()), aabb.GetExtents().GetZ()) + - DepthNear; - const Quaternion cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), StartingRotationAngle); - Vector3 cameraPosition(modelCenter.GetX(), modelCenter.GetY() - distance, modelCenter.GetZ()); - cameraPosition = cameraRotation.TransformVector(cameraPosition); - auto cameraTransform = Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition); - m_context->GetData()->m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform)); - } - - void CaptureStep::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) - { - if (m_readyToCapture && m_ticksToCapture-- <= 0) - { - m_context->GetData()->m_renderPipeline->AddToRenderTickOnce(); - - RPI::AttachmentReadback::CallbackFunction readbackCallback = [&](const RPI::AttachmentReadback::ReadbackResult& result) - { - if (!result.m_dataBuffer) - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - m_context->GetData()->m_thumbnailKeyRendered, - &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); - return; - } - uchar* data = result.m_dataBuffer.get()->data(); - QImage image( - data, result.m_imageDescriptor.m_size.m_width, result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888); - QPixmap pixmap; - pixmap.convertFromImage(image); - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - m_context->GetData()->m_thumbnailKeyRendered, - &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered, - pixmap); - }; - - Render::FrameCaptureNotificationBus::Handler::BusConnect(); - bool startedCapture = false; - Render::FrameCaptureRequestBus::BroadcastResult( - startedCapture, - &Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, - m_context->GetData()->m_passHierarchy, AZStd::string("Output"), readbackCallback, RPI::PassAttachmentReadbackOption::Output); - // Reset the capture flag if the capture request was successful. Otherwise try capture it again next tick. - if (startedCapture) - { - m_readyToCapture = false; - } - } - } - - void CaptureStep::OnCaptureFinished([[maybe_unused]] Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info) - { - m_context->SetStep(Step::FindThumbnailToRender); - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h deleted file mode 100644 index e93828e3b8..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h +++ /dev/null @@ -1,55 +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 AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - //! CaptureStep renders a thumbnail to a pixmap and notifies MaterialOrModelThumbnail once finished - class CaptureStep - : public ThumbnailRendererStep - , private TickBus::Handler - , private Render::FrameCaptureNotificationBus::Handler - { - public: - CaptureStep(ThumbnailRendererContext* context); - - void Start() override; - void Stop() override; - - private: - //! Places the camera so that the entire model is visible - void RepositionCamera() const; - - //! AZ::TickBus::Handler interface overrides... - void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; - - //! Render::FrameCaptureNotificationBus::Handler overrides... - void OnCaptureFinished(Render::FrameCaptureResult result, const AZStd::string& info) override; - - static constexpr float DepthNear = 0.01f; - static constexpr float StartingDistanceMultiplier = 1.75f; - static constexpr float StartingRotationAngle = Constants::QuarterPi / 2.0f; - - //! This flag is needed to wait one frame after each frame capture to reset FrameCaptureSystemComponent - bool m_readyToCapture = true; - //! This is necessary to suspend capture to allow a frame for Material and Mesh components to assign materials - int m_ticksToCapture = 0; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.cpp deleted file mode 100644 index 826ec4ae0d..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.cpp +++ /dev/null @@ -1,79 +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 - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - FindThumbnailToRenderStep::FindThumbnailToRenderStep(ThumbnailRendererContext* context) - : ThumbnailRendererStep(context) - { - } - - void FindThumbnailToRenderStep::Start() - { - TickBus::Handler::BusConnect(); - } - - void FindThumbnailToRenderStep::Stop() - { - TickBus::Handler::BusDisconnect(); - } - - void FindThumbnailToRenderStep::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) - { - PickNextThumbnail(); - } - - void FindThumbnailToRenderStep::PickNextThumbnail() - { - if (!m_context->GetData()->m_thumbnailQueue.empty()) - { - // pop the next thumbnailkey to be rendered from the queue - m_context->GetData()->m_thumbnailKeyRendered = m_context->GetData()->m_thumbnailQueue.front(); - m_context->GetData()->m_thumbnailQueue.pop(); - - // Find whether thumbnailkey contains a material asset or set a default material - m_context->GetData()->m_materialAsset = m_context->GetData()->m_defaultMaterialAsset; - Data::AssetId materialAssetId = GetAssetId(m_context->GetData()->m_thumbnailKeyRendered, RPI::MaterialAsset::RTTI_Type()); - if (materialAssetId.IsValid()) - { - if (m_context->GetData()->m_assetsToLoad.emplace(materialAssetId).second) - { - m_context->GetData()->m_materialAsset.Create(materialAssetId); - m_context->GetData()->m_materialAsset.QueueLoad(); - } - } - - // Find whether thumbnailkey contains a model asset or set a default model - m_context->GetData()->m_modelAsset = m_context->GetData()->m_defaultModelAsset; - Data::AssetId modelAssetId = GetAssetId(m_context->GetData()->m_thumbnailKeyRendered, RPI::ModelAsset::RTTI_Type()); - if (modelAssetId.IsValid()) - { - if (m_context->GetData()->m_assetsToLoad.emplace(modelAssetId).second) - { - m_context->GetData()->m_modelAsset.Create(modelAssetId); - m_context->GetData()->m_modelAsset.QueueLoad(); - } - } - - m_context->SetStep(Step::WaitForAssetsToLoad); - } - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h deleted file mode 100644 index e63bc5dce5..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h +++ /dev/null @@ -1,40 +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 AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - //! FindThumbnailToRenderStep checks whether there are any new thumbnails that need to be rendered every tick - class FindThumbnailToRenderStep - : public ThumbnailRendererStep - , private TickBus::Handler - { - public: - FindThumbnailToRenderStep(ThumbnailRendererContext* context); - - void Start() override; - void Stop() override; - - private: - - //! AZ::TickBus::Handler interface overrides... - void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; - - void PickNextThumbnail(); - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp deleted file mode 100644 index 6f74627bee..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ /dev/null @@ -1,191 +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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - InitializeStep::InitializeStep(ThumbnailRendererContext* context) - : ThumbnailRendererStep(context) - { - } - - void InitializeStep::Start() - { - auto data = m_context->GetData(); - - data->m_entityContext = AZStd::make_unique(); - data->m_entityContext->InitContext(); - - // Create and register a scene with all required feature processors - RPI::SceneDescriptor sceneDesc; - - AZ::EBusAggregateResults> results; - ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors); - - AZStd::set featureProcessorNames; - for (auto& resultCollection : results.values) - { - for (auto& featureProcessorName : resultCollection) - { - if (featureProcessorNames.emplace(featureProcessorName).second) - { - sceneDesc.m_featureProcessorNames.push_back(featureProcessorName); - } - } - } - - data->m_scene = RPI::Scene::CreateScene(sceneDesc); - - // Bind m_defaultScene to the GameEntityContext's AzFramework::Scene - auto* sceneSystem = AzFramework::SceneSystemInterface::Get(); - AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation."); - Outcome, AZStd::string> createSceneOutcome = - sceneSystem->CreateScene(data->m_sceneName); - AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str()); // This should never happen unless scene creation has changed. - data->m_frameworkScene = createSceneOutcome.TakeValue(); - data->m_frameworkScene->SetSubsystem(data->m_scene); - - data->m_frameworkScene->SetSubsystem(data->m_entityContext.get()); - // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene - RPI::RenderPipelineDescriptor pipelineDesc; - pipelineDesc.m_mainViewTagName = "MainCamera"; - pipelineDesc.m_name = data->m_pipelineName; - pipelineDesc.m_rootPassTemplate = "ThumbnailPipelineRenderToTexture"; - // We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue - // [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost - pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4; - data->m_renderPipeline = RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc); - data->m_scene->AddRenderPipeline(data->m_renderPipeline); - data->m_scene->Activate(); - RPI::RPISystemInterface::Get()->RegisterScene(data->m_scene); - data->m_passHierarchy.push_back(data->m_pipelineName); - data->m_passHierarchy.push_back("CopyToSwapChain"); - - // Connect camera to pipeline's default view after camera entity activated - Name viewName = Name("MainCamera"); - data->m_view = RPI::View::CreateView(viewName, RPI::View::UsageCamera); - - Matrix4x4 viewToClipMatrix; - MakePerspectiveFovMatrixRH(viewToClipMatrix, - Constants::QuarterPi, - AspectRatio, - NearDist, - FarDist, true); - data->m_view->SetViewToClipMatrix(viewToClipMatrix); - - data->m_renderPipeline->SetDefaultView(data->m_view); - - // Create lighting preset - data->m_lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath(ThumbnailRendererData::LightingPresetPath); - if (data->m_lightingPresetAsset.IsReady()) - { - auto preset = data->m_lightingPresetAsset->GetDataAs(); - if (preset) - { - auto iblFeatureProcessor = data->m_scene->GetFeatureProcessor(); - auto postProcessFeatureProcessor = data->m_scene->GetFeatureProcessor(); - auto exposureControlSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId())->GetOrCreateExposureControlSettingsInterface(); - auto directionalLightFeatureProcessor = data->m_scene->GetFeatureProcessor(); - auto skyboxFeatureProcessor = data->m_scene->GetFeatureProcessor(); - skyboxFeatureProcessor->Enable(true); - skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap); - - Camera::Configuration cameraConfig; - cameraConfig.m_fovRadians = Constants::HalfPi; - cameraConfig.m_nearClipDistance = NearDist; - cameraConfig.m_farClipDistance = FarDist; - cameraConfig.m_frustumWidth = 100.0f; - cameraConfig.m_frustumHeight = 100.0f; - - AZStd::vector lightHandles; - - preset->ApplyLightingPreset( - iblFeatureProcessor, - skyboxFeatureProcessor, - exposureControlSettingInterface, - directionalLightFeatureProcessor, - cameraConfig, - lightHandles); - } - } - - // Create preview model - AzFramework::EntityContextRequestBus::EventResult(data->m_modelEntity, data->m_entityContext->GetContextId(), - &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ThumbnailPreviewModel"); - data->m_modelEntity->CreateComponent(Render::MeshComponentTypeId); - data->m_modelEntity->CreateComponent(Render::MaterialComponentTypeId); - data->m_modelEntity->CreateComponent(azrtti_typeid()); - data->m_modelEntity->Init(); - data->m_modelEntity->Activate(); - - // preload default model - Data::AssetId defaultModelAssetId; - Data::AssetCatalogRequestBus::BroadcastResult( - defaultModelAssetId, - &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, - m_context->GetData()->DefaultModelPath, - RPI::ModelAsset::RTTI_Type(), - false); - AZ_Error("ThumbnailRenderer", defaultModelAssetId.IsValid(), "Default model asset is invalid. Verify the asset %s exists.", m_context->GetData()->DefaultModelPath); - if (m_context->GetData()->m_assetsToLoad.emplace(defaultModelAssetId).second) - { - data->m_defaultModelAsset.Create(defaultModelAssetId); - data->m_defaultModelAsset.QueueLoad(); - } - - // preload default material - Data::AssetId defaultMaterialAssetId; - Data::AssetCatalogRequestBus::BroadcastResult( - defaultMaterialAssetId, - &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, - m_context->GetData()->DefaultMaterialPath, - RPI::MaterialAsset::RTTI_Type(), - false); - AZ_Error("ThumbnailRenderer", defaultMaterialAssetId.IsValid(), "Default material asset is invalid. Verify the asset %s exists.", m_context->GetData()->DefaultMaterialPath); - if (m_context->GetData()->m_assetsToLoad.emplace(defaultMaterialAssetId).second) - { - data->m_defaultMaterialAsset.Create(defaultMaterialAssetId); - data->m_defaultMaterialAsset.QueueLoad(); - } - - m_context->SetStep(Step::FindThumbnailToRender); - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h deleted file mode 100644 index cdac492e80..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h +++ /dev/null @@ -1,37 +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 AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - //! InitializeStep sets up RPI system and scene and prepares it for rendering thumbnail entities - //! This step is only called once when CommonThumbnailRenderer begins rendering its first thumbnail - class InitializeStep - : public ThumbnailRendererStep - { - public: - InitializeStep(ThumbnailRendererContext* context); - - void Start() override; - - private: - static constexpr float AspectRatio = 1.0f; - static constexpr float NearDist = 0.1f; - static constexpr float FarDist = 100.0f; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp deleted file mode 100644 index a14f48e408..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp +++ /dev/null @@ -1,57 +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 AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - ReleaseResourcesStep::ReleaseResourcesStep(ThumbnailRendererContext* context) - : ThumbnailRendererStep(context) - { - } - - void ReleaseResourcesStep::Start() - { - auto data = m_context->GetData(); - - data->m_defaultMaterialAsset.Release(); - data->m_defaultModelAsset.Release(); - data->m_materialAsset.Release(); - data->m_modelAsset.Release(); - data->m_lightingPresetAsset.Release(); - - if (data->m_modelEntity) - { - AzFramework::EntityContextRequestBus::Event(data->m_entityContext->GetContextId(), - &AzFramework::EntityContextRequestBus::Events::DestroyEntity, data->m_modelEntity); - data->m_modelEntity = nullptr; - } - - data->m_scene->Deactivate(); - data->m_scene->RemoveRenderPipeline(data->m_renderPipeline->GetId()); - RPI::RPISystemInterface::Get()->UnregisterScene(data->m_scene); - data->m_frameworkScene->UnsetSubsystem(data->m_scene); - data->m_frameworkScene->UnsetSubsystem(data->m_entityContext.get()); - data->m_scene = nullptr; - data->m_frameworkScene = nullptr; - data->m_renderPipeline = nullptr; - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h deleted file mode 100644 index 4858b90b96..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.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 AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - class ReleaseResourcesStep - : public ThumbnailRendererStep - { - public: - ReleaseResourcesStep(ThumbnailRendererContext* context); - - void Start() override; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h deleted file mode 100644 index cc10f91525..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h +++ /dev/null @@ -1,37 +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 - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - class ThumbnailRendererContext; - - //! ThumbnailRendererStep decouples CommonThumbnailRenderer logic into easy-to-understand and debug pieces - class ThumbnailRendererStep - { - public: - explicit ThumbnailRendererStep(ThumbnailRendererContext* context) : m_context(context) {} - virtual ~ThumbnailRendererStep() = default; - - //! Start is called when step begins execution - virtual void Start() {} - //! Stop is called when step ends execution - virtual void Stop() {} - - protected: - ThumbnailRendererContext* m_context; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.cpp deleted file mode 100644 index 4f32bc4b68..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.cpp +++ /dev/null @@ -1,101 +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 "Thumbnails/ThumbnailerBus.h" - -#include -#include -#include -#include - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - WaitForAssetsToLoadStep::WaitForAssetsToLoadStep(ThumbnailRendererContext* context) - : ThumbnailRendererStep(context) - { - } - - void WaitForAssetsToLoadStep::Start() - { - LoadNextAsset(); - } - - void WaitForAssetsToLoadStep::Stop() - { - Data::AssetBus::Handler::BusDisconnect(); - TickBus::Handler::BusDisconnect(); - m_context->GetData()->m_assetsToLoad.clear(); - } - - void WaitForAssetsToLoadStep::LoadNextAsset() - { - if (m_context->GetData()->m_assetsToLoad.empty()) - { - // When all assets are loaded, render the thumbnail itself - m_context->SetStep(Step::Capture); - } - else - { - // Pick the the next asset and wait until its ready - const auto assetIdIt = m_context->GetData()->m_assetsToLoad.begin(); - m_context->GetData()->m_assetsToLoad.erase(assetIdIt); - m_assetId = *assetIdIt; - Data::AssetBus::Handler::BusConnect(m_assetId); - // If asset is already loaded, then AssetEvents will call OnAssetReady instantly and we don't need to wait this time - if (Data::AssetBus::Handler::BusIsConnected()) - { - TickBus::Handler::BusConnect(); - m_timeRemainingS = TimeOutS; - } - } - } - - void WaitForAssetsToLoadStep::OnAssetReady([[maybe_unused]] Data::Asset asset) - { - Data::AssetBus::Handler::BusDisconnect(); - LoadNextAsset(); - } - - void WaitForAssetsToLoadStep::OnAssetError([[maybe_unused]] Data::Asset asset) - { - Data::AssetBus::Handler::BusDisconnect(); - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - m_context->GetData()->m_thumbnailKeyRendered, - &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); - m_context->SetStep(Step::FindThumbnailToRender); - } - - void WaitForAssetsToLoadStep::OnAssetCanceled([[maybe_unused]] Data::AssetId assetId) - { - Data::AssetBus::Handler::BusDisconnect(); - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - m_context->GetData()->m_thumbnailKeyRendered, - &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); - m_context->SetStep(Step::FindThumbnailToRender); - } - - void WaitForAssetsToLoadStep::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) - { - m_timeRemainingS -= deltaTime; - if (m_timeRemainingS < 0) - { - auto assetIdStr = m_assetId.ToString(); - AZ_Warning("CommonThumbnailRenderer", false, "Timed out waiting for asset %s to load.", assetIdStr.c_str()); - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event( - m_context->GetData()->m_thumbnailKeyRendered, - &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender); - m_context->SetStep(Step::FindThumbnailToRender); - } - } - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h deleted file mode 100644 index 921d1511ca..0000000000 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h +++ /dev/null @@ -1,50 +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 - -namespace AZ -{ - namespace LyIntegration - { - namespace Thumbnails - { - //! WaitForAssetsToLoadStep pauses further rendering until all assets used for rendering a thumbnail have been loaded - class WaitForAssetsToLoadStep - : public ThumbnailRendererStep - , private Data::AssetBus::Handler - , private TickBus::Handler - { - public: - WaitForAssetsToLoadStep(ThumbnailRendererContext* context); - - void Start() override; - void Stop() override; - - private: - void LoadNextAsset(); - - // AZ::Data::AssetBus::Handler - void OnAssetReady(Data::Asset asset) override; - void OnAssetError(Data::Asset asset) override; - void OnAssetCanceled(Data::AssetId assetId) override; - - //! AZ::TickBus::Handler interface overrides... - void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; - - static constexpr float TimeOutS = 3.0f; - Data::AssetId m_assetId; - float m_timeRemainingS = 0; - }; - } // namespace Thumbnails - } // namespace LyIntegration -} // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 174eb30b31..2714b65a56 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -7,9 +7,9 @@ # set(FILES + Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h - Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h Source/Module.cpp Source/Animation/EditorAttachmentComponent.h Source/Animation/EditorAttachmentComponent.cpp @@ -45,8 +45,6 @@ set(FILES Source/Material/EditorMaterialSystemComponent.h Source/Material/MaterialBrowserInteractions.h Source/Material/MaterialBrowserInteractions.cpp - Source/Material/MaterialThumbnail.cpp - Source/Material/MaterialThumbnail.h Source/Mesh/EditorMeshComponent.h Source/Mesh/EditorMeshComponent.cpp Source/Mesh/EditorMeshStats.h @@ -55,8 +53,6 @@ set(FILES Source/Mesh/EditorMeshSystemComponent.h Source/Mesh/EditorMeshStatsSerializer.cpp Source/Mesh/EditorMeshStatsSerializer.h - Source/Mesh/MeshThumbnail.h - Source/Mesh/MeshThumbnail.cpp Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.cpp @@ -95,28 +91,19 @@ set(FILES Source/SkyBox/EditorHDRiSkyboxComponent.h Source/SkyBox/EditorPhysicalSkyComponent.cpp Source/SkyBox/EditorPhysicalSkyComponent.h - Source/Thumbnails/ThumbnailUtils.h - Source/Thumbnails/ThumbnailUtils.cpp - Source/Thumbnails/Preview/CommonPreviewer.cpp - Source/Thumbnails/Preview/CommonPreviewer.h - Source/Thumbnails/Preview/CommonPreviewer.ui - Source/Thumbnails/Preview/CommonPreviewerFactory.cpp - Source/Thumbnails/Preview/CommonPreviewerFactory.h - Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp - Source/Thumbnails/Rendering/CommonThumbnailRenderer.h - Source/Thumbnails/Rendering/ThumbnailRendererData.h - Source/Thumbnails/Rendering/ThumbnailRendererContext.h - Source/Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h - Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp - Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h - Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.cpp - Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h - Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.cpp - Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h - Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp - Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h - Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp - Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h + Source/SharedPreview/SharedPreviewer.cpp + Source/SharedPreview/SharedPreviewer.h + Source/SharedPreview/SharedPreviewer.ui + Source/SharedPreview/SharedPreviewerFactory.cpp + Source/SharedPreview/SharedPreviewerFactory.h + Source/SharedPreview/SharedPreviewContent.cpp + Source/SharedPreview/SharedPreviewContent.h + Source/SharedPreview/SharedPreviewUtils.cpp + Source/SharedPreview/SharedPreviewUtils.h + Source/SharedPreview/SharedThumbnail.cpp + Source/SharedPreview/SharedThumbnail.h + Source/SharedPreview/SharedThumbnailRenderer.cpp + Source/SharedPreview/SharedThumbnailRenderer.h Source/Scripting/EditorEntityReferenceComponent.cpp Source/Scripting/EditorEntityReferenceComponent.h Source/SurfaceData/EditorSurfaceDataMeshComponent.cpp 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/HairSimulationCompute.azsl b/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl index f8d178206a..496aa5c7da 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairSimulationCompute.azsl @@ -31,7 +31,7 @@ // THE SOFTWARE. // //-------------------------------------------------------------------------------------- -#include +#include #include //-------------------------------------------------------------------------------------- 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 --- - 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) + : HairGeometryRasterPass(descriptor) { + SetShaderPath("Shaders/hairrenderingfillppll.azshader"); } - 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() + RPI::Ptr HairPPLLRasterPass::Create(const RPI::PassDescriptor& descriptor) { - if (GetScene()) - { - RasterPass::InitializeInternal(); - } + RPI::Ptr pass = aznew HairPPLLRasterPass(descriptor); + return pass; } + //! 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/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index e4f1b02a88..7c8ce74f8e 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -226,27 +226,34 @@ namespace AZ // Prepare materials array for the per pass srg std::vector hairObjectsRenderMaterials; - uint32_t obj = 0; - for (auto objIter = m_hairRenderObjects.begin(); objIter != m_hairRenderObjects.end(); ++objIter, ++obj) + uint32_t objectIndex = 0; + for (auto& renderObject : m_hairRenderObjects) { - HairRenderObject* renderObject = objIter->get(); if (!renderObject->IsEnabled()) { continue; } - renderObject->Update(); + + renderObject->SetRenderIndex(objectIndex); // [To Do] Hair - update the following parameters for dynamic LOD control // should change or when parameters are being changed on the editor side. // float Distance = sqrtf( m_activeScene.scene->GetCameraPos().x * m_activeScene.scene->GetCameraPos().x + // m_activeScene.scene->GetCameraPos().y * m_activeScene.scene->GetCameraPos().y + // m_activeScene.scene->GetCameraPos().z * m_activeScene.scene->GetCameraPos().z); -// objIter->get()->UpdateRenderingParameters( -// renderingSettings, m_nScreenWidth * m_nScreenHeight * AVE_FRAGS_PER_PIXEL, m_deltaTime, Distance); + const float distanceFromCamera = 1.0f; // fixed distance until LOD mechanism is worked on + const float updateShadows = false; // same here - currently cheap self shadow approx + renderObject->UpdateRenderingParameters( nullptr, RESERVED_PIXELS_FOR_OIT, distanceFromCamera, updateShadows); - // this will be used for the constant buffer + // this will be used in the constant buffer to set the material array used by the resolve pass hairObjectsRenderMaterials.push_back(renderObject->GetHairRenderParams()); + + // The data update for the GPU bind - this should be the very last thing done after the + // data has been read and / or altered on the CPU side. + renderObject->Update(); + ++objectIndex; } + FillHairMaterialsArray(hairObjectsRenderMaterials); } @@ -532,4 +539,3 @@ namespace AZ } // namespace Hair } // namespace Render } // namespace AZ - diff --git a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp index a89a3eb1b8..baf986daf6 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.cpp @@ -878,6 +878,11 @@ namespace AZ const AMD::TressFXRenderingSettings* parameters, const int nodePoolSize, float distance, bool shadowUpdate /*= false*/) { + if (!parameters) + { + parameters = m_renderSettings; + } + // Update Render Parameters // If you alter FiberRadius make sure to change it also in the material properties // passed by the Feature Processor for the shading. @@ -888,7 +893,7 @@ namespace AZ // original TressFX lighting parameters - two specular lobes approximating // the Marschner R and and TRT lobes + diffuse component. - m_renderCB->MatKValue = {{{0.f, parameters->m_HairKDiffuse, parameters->m_HairKSpec1, parameters->m_HairSpecExp1}}}; + m_renderCB->MatKValue = { {{0.f, parameters->m_HairKDiffuse, parameters->m_HairKSpec1, parameters->m_HairSpecExp1}} }; m_renderCB->HairKs2 = parameters->m_HairKSpec2; m_renderCB->HairEx2 = parameters->m_HairSpecExp2; @@ -903,11 +908,13 @@ namespace AZ m_strandCB->TipPercentage = parameters->m_TipPercentage; m_strandCB->StrandUVTilingFactor = parameters->m_StrandUVTilingFactor; m_strandCB->FiberRatio = parameters->m_FiberRatio; + m_strandCB->EnableThinTip = parameters->m_EnableThinTip; + m_strandCB->EnableStrandUV = parameters->m_EnableStrandUV; // Reset LOD hair density for the frame m_LODHairDensity = 1.f; + float fiberRadius = parameters->m_FiberRadius; - float FiberRadius = parameters->m_FiberRadius; if (parameters->m_EnableHairLOD) { float MinLODDist = shadowUpdate ? @@ -922,23 +929,18 @@ namespace AZ float DistanceRatio = AZStd::min((distance - MinLODDist) / AZStd::max(MaxLODDist - MinLODDist, 0.00001f), 1.f); // Lerp: x + s(y-x) - float MaxLODFiberRadius = FiberRadius * (shadowUpdate ? parameters->m_ShadowLODWidthMultiplier : parameters->m_LODWidthMultiplier); - FiberRadius = FiberRadius + (DistanceRatio * (MaxLODFiberRadius - FiberRadius)); + float MaxLODFiberRadius = fiberRadius * (shadowUpdate ? parameters->m_ShadowLODWidthMultiplier : parameters->m_LODWidthMultiplier); + fiberRadius = fiberRadius + (DistanceRatio * (MaxLODFiberRadius - fiberRadius)); // Lerp: x + s(y-x) m_LODHairDensity = 1.f + (DistanceRatio * ((shadowUpdate ? parameters->m_ShadowLODPercent : parameters->m_LODPercent) - 1.f)); } } - m_strandCB->FiberRadius = FiberRadius; - - m_strandCB->NumVerticesPerStrand = m_NumVerticesPerStrand; // Always constant - m_strandCB->EnableThinTip = parameters->m_EnableThinTip; + m_strandCB->FiberRadius = fiberRadius; + m_strandCB->NumVerticesPerStrand = m_NumVerticesPerStrand; // Constant through the run per object m_strandCB->NodePoolSize = nodePoolSize; - m_strandCB->RenderParamsIndex = m_RenderIndex; // Always constant - - m_strandCB->EnableStrandUV = parameters->m_EnableStrandUV; - m_strandCB->EnableStrandTangent = parameters->m_EnableStrandTangent; + m_strandCB->RenderParamsIndex = m_RenderIndex; // Per Objects specific according to its index in the FP } //!===================================================================================== @@ -977,8 +979,10 @@ namespace AZ UpdateSimulationParameters(simSettings, SIMULATION_TIME_STEP); // [To Do] Hair - change to be dynamically calculated - const float distanceFromCamera = 1.0; + const float distanceFromCamera = 1.0f; const float updateShadows = false; + m_renderSettings = renderSettings; + m_simSettings = simSettings; UpdateRenderingParameters(renderSettings, RESERVED_PIXELS_FOR_OIT, distanceFromCamera, updateShadows); if (!GetShaders()) diff --git a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h index c81d2b9079..fa4095eed0 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h +++ b/Gems/AtomTressFX/Code/Rendering/HairRenderObject.h @@ -269,6 +269,7 @@ namespace AZ void UpdateSimulationParameters(const AMD::TressFXSimulationSettings* settings, float timeStep); void SetWind(const Vector3& windDir, float windMag, int frame); + void SetRenderIndex(uint32_t renderIndex) { m_RenderIndex = renderIndex; } void ResetPositions() { m_simCB->g_ResetPositions = 1.0f; } void IncreaseSimulationFrame() @@ -315,6 +316,10 @@ namespace AZ float m_frameDeltaTime = 0.02; + //! The following are the configuration settings that might be required during the update. + AMD::TressFXSimulationSettings* m_simSettings = nullptr; + AMD::TressFXRenderingSettings* m_renderSettings = nullptr; + //! Hair asset information uint32_t m_TotalIndices = 0; uint32_t m_NumTotalVertices = 0; @@ -331,7 +336,7 @@ namespace AZ //! Controls reset / copy base hair state uint32_t m_SimulationFrame = 0; - // [To Do] - verify if still required + //! The index used as a look up into the material array during the resolve pass uint32_t m_RenderIndex = 0; //!----------------------------------------------------------------- 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/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp b/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp index a31885cc69..2fe5d749cf 100644 --- a/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp @@ -105,7 +105,7 @@ namespace AudioControlBuilder Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request")); } - // For each preload request in the control file, determine which config group is used for this platform and register each + // For each preload request in the control file, determine which config group is used for this platform and register each // bank listed in that preload request as a dependency. while (preloadRequestNode) { @@ -163,7 +163,7 @@ namespace AudioControlBuilder const AZ::rapidxml::xml_node* configGroupNode = configGroupMap[configGroupName]; if (!configGroupNode) { - // The config group this platform uses isn't defined in the control file. This might be intentional, so just + // The config group this platform uses isn't defined in the control file. This might be intentional, so just // generate a warning and keep going to the next preload node. AZ_TracePrintf("Audio Control Builder", "%s node for config group %s is not defined, so no banks are referenced.", Audio::ATLXmlTags::ATLConfigGroupTag, configGroupName.c_str()); @@ -188,7 +188,7 @@ namespace AudioControlBuilder } // Prepend the bank name with the relative path to the wwise sounds folder to get relative path to the bank from - // the @assets@ alias and push that into the list of banks referenced. + // the @products@ alias and push that into the list of banks referenced. AZStd::string soundsPrefix = Audio::Wwise::DefaultBanksPath; banksReferenced.emplace_back(soundsPrefix + bankNameAttribute->value()); @@ -496,7 +496,7 @@ namespace AudioControlBuilder pathDependencies.emplace(relativeBankPath, AssetBuilderSDK::ProductPathDependencyType::ProductFile); } - // For each bank figure out what events are included in the bank, then run through every event referenced in the file and + // For each bank figure out what events are included in the bank, then run through every event referenced in the file and // make sure it is in the list gathered from the banks. const auto triggersNode = node->first_node(Audio::ATLXmlTags::TriggersNodeTag); if (!triggersNode) @@ -520,7 +520,7 @@ namespace AudioControlBuilder AZStd::set wwiseEventsInReferencedBanks; - // Load all bankdeps files for all banks referenced and aggregate the list of events in those files. + // Load all bankdeps files for all banks referenced and aggregate the list of events in those files. for (const AZStd::string& relativeBankPath : banksReferenced) { // Create the full path to the bankdeps file from the bank file. diff --git a/Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.cpp b/Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.cpp index 3a1916edc3..22fa4cab7d 100644 --- a/Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.cpp @@ -52,7 +52,7 @@ namespace WwiseBuilder { fileNames.push_back(dependenciesArray[dependencyIndex].GetString()); } - + // The dependency array is empty, which likely means it was modified by hand. However, every bank is dependent // on init.bnk (other than itself), so just force add it as a dependency here. and emit a warning. if (fileNames.size() == 0) @@ -93,7 +93,7 @@ namespace WwiseBuilder void WwiseBuilderWorker::Initialize() { - AZ::IO::Path configFile("@devassets@"); + AZ::IO::Path configFile("@projectroot@"); configFile /= Audio::Wwise::DefaultBanksPath; configFile /= Audio::Wwise::ConfigFile; @@ -180,7 +180,7 @@ namespace WwiseBuilder { AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Starting Job.\n"); AZ::IO::PathView fullPath(request.m_fullPath); - + if (m_isShuttingDown) { AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str()); @@ -204,7 +204,7 @@ namespace WwiseBuilder AZ::Outcome gatherProductDependenciesResponse = GatherProductDependencies(request.m_fullPath, request.m_sourceFile, dependencyPaths); if (!gatherProductDependenciesResponse.IsSuccess()) { - AZ_Error(WwiseBuilderWindowName, false, "Dependency gathering for %s failed. %s", + AZ_Error(WwiseBuilderWindowName, false, "Dependency gathering for %s failed. %s", request.m_fullPath.c_str(), gatherProductDependenciesResponse.GetError().c_str()); } else diff --git a/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseBuilderTest.cpp b/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseBuilderTest.cpp index 2387588f31..e65dbcce9f 100644 --- a/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseBuilderTest.cpp +++ b/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseBuilderTest.cpp @@ -28,7 +28,7 @@ protected: { m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); @@ -39,7 +39,7 @@ protected: assetRoot /= "Cache"; AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", assetRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str()); } void TearDown() override @@ -158,7 +158,7 @@ TEST_F(WwiseBuilderTests, WwiseBuilder_InitBank_NoMetadata_NoDependencies) TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_NoMetadata_NoDependencies) { - // Should generate a warning after trying to find metadata for the given bank, when the bank is not the init bank. + // Should generate a warning after trying to find metadata for the given bank, when the bank is not the init bank. // Warning should be about not being able to generate full dependency information without the metadata file. TestSuccessCaseNoDependencies("test_doesNotExist.bnk", true); } @@ -180,15 +180,15 @@ TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_MultipleDependencies) TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_DependencyArrayNonexistent_NoDependencies) { - // Should generate a warning when trying to get dependency info from metadata file, but the dependency field does - // not an empty array. Warning should be describing that a dependency on the init bank was added by default, but + // Should generate a warning when trying to get dependency info from metadata file, but the dependency field does + // not an empty array. Warning should be describing that a dependency on the init bank was added by default, but // the full dependency list could not be generated. TestSuccessCaseNoDependencies("test_bank7.bnk", true); } TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_NoElementsInDependencyArray_NoDependencies) { - // Should generate a warning when trying to get dependency info from metadata file, but the dependency field is + // Should generate a warning when trying to get dependency info from metadata file, but the dependency field is // an empty array. Warning should be describing that a dependency on the init bank was added by default, but the // full dependency list could not be generated. TestSuccessCaseNoDependencies("test_bank8.bnk", true); @@ -196,8 +196,8 @@ TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_NoElementsInDependencyArray_N TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_MissingInitBankDependency_MultipleDependencies) { - // Should generate a warning when trying to get dependency info from metadata file, but the dependency info in the - // metadata doesn't include the init bank. Warning should be describing that a dependency on the init bank was + // Should generate a warning when trying to get dependency info from metadata file, but the dependency info in the + // metadata doesn't include the init bank. Warning should be describing that a dependency on the init bank was // added by default. AZStd::vector expectedPaths = { "Sounds/wwise/init.bnk", diff --git a/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseTest.cpp b/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseTest.cpp index 3724069045..4ee816b048 100644 --- a/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseTest.cpp +++ b/Gems/AudioEngineWwise/Code/Tests/AudioEngineWwiseTest.cpp @@ -231,7 +231,7 @@ namespace UnitTest m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); @@ -253,10 +253,10 @@ namespace UnitTest AZ_TEST_ASSERT(serializeContext != nullptr); Audio::Wwise::ConfigurationSettings::Reflect(serializeContext); - // Set the @assets@ alias to the path where *this* cpp file lives. + // Set the @products@ alias to the path where *this* cpp file lives. AZStd::string rootFolder(AZ::Test::GetCurrentExecutablePath()); AZ::StringFunc::Path::Join(rootFolder.c_str(), "Test.Assets/Gems/AudioEngineWwise", rootFolder); - m_fileIO->SetAlias("@assets@", rootFolder.c_str()); + m_fileIO->SetAlias("@products@", rootFolder.c_str()); // So we don't have to compute it in each test... AZStd::string defaultBanksPath(Audio::Wwise::DefaultBanksPath); @@ -303,26 +303,12 @@ namespace UnitTest m_mapEntry.m_bankSubPath = "soundbanks"; config.m_platformMappings.push_back(m_mapEntry); - // Unfortunately we are writing to the config file that is simulated to be in the @assets@ subfolder - // This will cause an issue during save. Since 'm_configFilePath' is an absolute path, we need to - // reset the @assets@ alias to a meaningful assets path that does not contain any root of the m_configFilePath - // in order to write to the config file and proceed - //AZStd::string rootFolder(AZ::Test::GetCurrentExecutablePath()); - //AZ::IO::Path tempAssetPath(rootFolder); - //tempAssetPath /= "Cache"; - - //AZStd::string previousAlias = m_fileIO->GetAlias("@assets@"); - //m_fileIO->SetAlias("@assets@", tempAssetPath.c_str()); - config.Save(m_configFilePath); - //m_fileIO->SetAlias("@assets@", previousAlias.c_str()); m_wwiseImpl.SetBankPaths(); - //m_fileIO->SetAlias("@assets@", tempAssetPath.c_str()); m_fileIO->Remove(m_configFilePath.c_str()); - //m_fileIO->SetAlias("@assets@", previousAlias.c_str()); EXPECT_STREQ(m_wwiseImpl.m_soundbankFolder.c_str(), "sounds/wwise/soundbanks/"); } 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/AudioSystem/Code/Source/Engine/ATLComponents.cpp b/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp index 996a2a80b6..aa93a2bc12 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp @@ -977,7 +977,7 @@ namespace Audio , m_rPreloadRequests(rPreloadRequests) , m_nTriggerImplIDCounter(AUDIO_TRIGGER_IMPL_ID_NUM_RESERVED) , m_rFileCacheMgr(rFileCacheMgr) - , m_rootPath("@assets@") + , m_rootPath("@products@") #if !defined(AUDIO_RELEASE) , m_pDebugNameStore(nullptr) #endif // !AUDIO_RELEASE diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index 0c697f12b6..b7ee805b3c 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -286,13 +286,16 @@ namespace Blast DefaultConfigurationPath, globalConfiguration); AZ_Warning("Blast", loaded, "Failed to load Blast configuration, initializing with default configs."); - SetGlobalConfiguration(globalConfiguration); - SaveConfiguration(); + ApplyGlobalConfiguration(globalConfiguration); + if (!loaded) + { + SaveConfiguration(); + } } void BlastSystemComponent::SaveConfiguration() { - auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); + auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@"); if (!assetRoot) { @@ -309,7 +312,7 @@ namespace Blast void BlastSystemComponent::CheckoutConfiguration() { - const auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); + const auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@"); AZStd::string fullPath; AzFramework::StringFunc::Path::Join(assetRoot, DefaultConfigurationPath, fullPath); @@ -394,8 +397,13 @@ namespace Blast void BlastSystemComponent::SetGlobalConfiguration(const BlastGlobalConfiguration& globalConfiguration) { - m_configuration = globalConfiguration; + ApplyGlobalConfiguration(globalConfiguration); SaveConfiguration(); + } + + void BlastSystemComponent::ApplyGlobalConfiguration(const BlastGlobalConfiguration& globalConfiguration) + { + m_configuration = globalConfiguration; { AZ::Data::Asset& materialLibrary = m_configuration.m_materialLibrary; diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.h b/Gems/Blast/Code/Source/Components/BlastSystemComponent.h index a43bc17aca..8f6c200870 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.h +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.h @@ -93,6 +93,7 @@ namespace Blast void InitPhysics(); void DeactivatePhysics(); + void ApplyGlobalConfiguration(const BlastGlobalConfiguration& materialLibrary); void RegisterCommands(); // Internal helper functions & classes diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index dddd1e275a..1fe6e35d75 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -344,9 +344,9 @@ namespace Blast void UpdateMassProperties( [[maybe_unused]] AzPhysics::MassComputeFlags flags, - [[maybe_unused]] const AZ::Vector3* centerOfMassOffsetOverride, - [[maybe_unused]] const AZ::Matrix3x3* inertiaTensorOverride, - [[maybe_unused]] const float* massOverride) override + [[maybe_unused]] const AZ::Vector3& centerOfMassOffsetOverride, + [[maybe_unused]] const AZ::Matrix3x3& inertiaTensorOverride, + [[maybe_unused]] const float massOverride) override { } diff --git a/Gems/Blast/Editor/Scripts/blast_asset_builder.py b/Gems/Blast/Editor/Scripts/blast_asset_builder.py index 06dc15f5c1..a570489ed1 100644 --- a/Gems/Blast/Editor/Scripts/blast_asset_builder.py +++ b/Gems/Blast/Editor/Scripts/blast_asset_builder.py @@ -95,7 +95,9 @@ def generate_assetinfo_product(request): outputFilename = os.path.join(request.tempDirPath, assetinfoFilename) # the only rule in it is to run this file again as a scene processor - currentScript = pathlib.Path(__file__).resolve() + currentScript = str(pathlib.Path(__file__).resolve()) + currentScript = currentScript.replace('\\', '/').lower() + currentScript = currentScript.replace('blast_asset_builder.py', 'blast_chunk_processor.py') aDict = {"values": [{"$type": "ScriptProcessorRule", "scriptFilename": f"{currentScript}"}]} jsonString = json.dumps(aDict) jsonFile = open(outputFilename, "w") @@ -167,124 +169,3 @@ try: pythonAssetBuilderHandler = register_asset_builder() except: pythonAssetBuilderHandler = None - -# -# SceneAPI Processor -# -blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0) - -def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList): - import azlmbr.scene - import azlmbr.object - import azlmbr.paths - import json, os - - jsonFilename = os.path.basename(scene.sourceFilename) - jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks') - - # prepare output folder - basePath, _ = os.path.split(jsonFilename) - outputPath = os.path.join(outputDirectory, basePath) - if not os.path.exists(outputPath): - os.makedirs(outputPath, False) - - # write out a JSON file with the chunk file info - with open(jsonFilename, "w") as jsonFile: - jsonFile.write(scene.manifest.ExportToJson()) - - exportProduct = azlmbr.scene.ExportProduct() - exportProduct.filename = jsonFilename - exportProduct.sourceId = scene.sourceGuid - exportProduct.assetType = blastChunksAssetType - exportProduct.subId = 101 - - exportProductList = azlmbr.scene.ExportProductList() - exportProductList.AddProduct(exportProduct) - return exportProductList - -def on_prepare_for_export(args): - try: - scene = args[0] # azlmbr.scene.Scene - outputDirectory = args[1] # string - platformIdentifier = args[2] # string - productList = args[3] # azlmbr.scene.ExportProductList - return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList) - except: - log_exception_traceback() - -def get_mesh_node_names(sceneGraph): - import azlmbr.scene as sceneApi - import azlmbr.scene.graph - from scene_api import scene_data as sceneData - - meshDataList = [] - node = sceneGraph.get_root() - children = [] - - while node.IsValid(): - # store children to process after siblings - if sceneGraph.has_node_child(node): - children.append(sceneGraph.get_node_child(node)) - - # store any node that has mesh data content - nodeContent = sceneGraph.get_node_content(node) - if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'): - if sceneGraph.is_node_end_point(node) is False: - nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node)) - nodePath = nodeName.get_path() - if (len(nodeName.get_path())): - meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node))) - - # advance to next node - if sceneGraph.has_node_sibling(node): - node = sceneGraph.get_node_sibling(node) - elif children: - node = children.pop() - else: - node = azlmbr.scene.graph.NodeIndex() - - return meshDataList - -def update_manifest(scene): - import uuid, os - import azlmbr.scene as sceneApi - import azlmbr.scene.graph - from scene_api import scene_data as sceneData - - graph = sceneData.SceneGraph(scene.graph) - meshNameList = get_mesh_node_names(graph) - sceneManifest = sceneData.SceneManifest() - sourceFilenameOnly = os.path.basename(scene.sourceFilename) - sourceFilenameOnly = sourceFilenameOnly.replace('.','_') - - for activeMeshIndex in range(len(meshNameList)): - chunkName = meshNameList[activeMeshIndex] - chunkPath = chunkName.get_path() - meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name()) - meshGroup = sceneManifest.add_mesh_group(meshGroupName) - meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}' - sceneManifest.mesh_group_select_node(meshGroup, chunkPath) - - return sceneManifest.export() - -sceneJobHandler = None - -def on_update_manifest(args): - try: - scene = args[0] - return update_manifest(scene) - except: - global sceneJobHandler - sceneJobHandler = None - log_exception_traceback() - -# try to create SceneAPI handler for processing -try: - import azlmbr.scene as sceneApi - if (sceneJobHandler == None): - sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler() - sceneJobHandler.connect() - sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest) - sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export) -except: - sceneJobHandler = None diff --git a/Gems/Blast/Editor/Scripts/blast_chunk_processor.py b/Gems/Blast/Editor/Scripts/blast_chunk_processor.py new file mode 100644 index 0000000000..d112f465e9 --- /dev/null +++ b/Gems/Blast/Editor/Scripts/blast_chunk_processor.py @@ -0,0 +1,141 @@ +""" +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 a Python Asset Builder script examines each .blast file to see if an +associated .fbx file needs to be processed by exporting all of its chunks +into a scene manifest + +This is also a SceneAPI script that executes from a foo.fbx.assetinfo scene +manifest that writes out asset chunk data for .blast files +""" +import os, traceback, binascii, sys, json, pathlib +import azlmbr.math +import azlmbr.asset +import azlmbr.asset.entity +import azlmbr.asset.builder +import azlmbr.bus + +# +# SceneAPI Processor +# +blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0) + +def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList): + import azlmbr.scene + import azlmbr.object + import azlmbr.paths + import json, os + + jsonFilename = os.path.basename(scene.sourceFilename) + jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks') + + # prepare output folder + basePath, _ = os.path.split(jsonFilename) + outputPath = os.path.join(outputDirectory, basePath) + if not os.path.exists(outputPath): + os.makedirs(outputPath, False) + + # write out a JSON file with the chunk file info + with open(jsonFilename, "w") as jsonFile: + jsonFile.write(scene.manifest.ExportToJson()) + + exportProduct = azlmbr.scene.ExportProduct() + exportProduct.filename = jsonFilename + exportProduct.sourceId = scene.sourceGuid + exportProduct.assetType = blastChunksAssetType + exportProduct.subId = 101 + + exportProductList = azlmbr.scene.ExportProductList() + exportProductList.AddProduct(exportProduct) + return exportProductList + +def on_prepare_for_export(args): + try: + scene = args[0] # azlmbr.scene.Scene + outputDirectory = args[1] # string + platformIdentifier = args[2] # string + productList = args[3] # azlmbr.scene.ExportProductList + return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList) + except: + log_exception_traceback() + +def get_mesh_node_names(sceneGraph): + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + meshDataList = [] + node = sceneGraph.get_root() + children = [] + + while node.IsValid(): + # store children to process after siblings + if sceneGraph.has_node_child(node): + children.append(sceneGraph.get_node_child(node)) + + # store any node that has mesh data content + nodeContent = sceneGraph.get_node_content(node) + if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'): + if sceneGraph.is_node_end_point(node) is False: + nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node)) + nodePath = nodeName.get_path() + if (len(nodeName.get_path())): + meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node))) + + # advance to next node + if sceneGraph.has_node_sibling(node): + node = sceneGraph.get_node_sibling(node) + elif children: + node = children.pop() + else: + node = azlmbr.scene.graph.NodeIndex() + + return meshDataList + +def update_manifest(scene): + import uuid, os + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + graph = sceneData.SceneGraph(scene.graph) + meshNameList = get_mesh_node_names(graph) + sceneManifest = sceneData.SceneManifest() + sourceFilenameOnly = os.path.basename(scene.sourceFilename) + sourceFilenameOnly = sourceFilenameOnly.replace('.','_') + + for activeMeshIndex in range(len(meshNameList)): + chunkName = meshNameList[activeMeshIndex] + chunkPath = chunkName.get_path() + meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name()) + meshGroup = sceneManifest.add_mesh_group(meshGroupName) + meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}' + sceneManifest.mesh_group_select_node(meshGroup, chunkPath) + + return sceneManifest.export() + +sceneJobHandler = None + +def on_update_manifest(args): + try: + scene = args[0] + return update_manifest(scene) + except: + global sceneJobHandler + sceneJobHandler = None + log_exception_traceback() + +# try to create SceneAPI handler for processing +try: + import azlmbr.scene as sceneApi + if (sceneJobHandler == None): + sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler() + sceneJobHandler.connect() + sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest) + sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export) +except: + sceneJobHandler = None 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/CertificateManager/Code/Source/DataSource/FileDataSource.cpp b/Gems/CertificateManager/Code/Source/DataSource/FileDataSource.cpp index 21b0b28c47..85c9c696a2 100644 --- a/Gems/CertificateManager/Code/Source/DataSource/FileDataSource.cpp +++ b/Gems/CertificateManager/Code/Source/DataSource/FileDataSource.cpp @@ -16,7 +16,7 @@ namespace CertificateManager { static bool ReadFileIntoString(const char* filename, AZStd::vector& outBuffer) { - AZStd::string certificatePath = "@assets@/certificates/"; + AZStd::string certificatePath = "@products@/certificates/"; certificatePath.append(filename); AZ::IO::FileIOBase* fileBase = AZ::IO::FileIOBase::GetInstance(); @@ -58,7 +58,7 @@ namespace CertificateManager return true; } - FileDataSource::FileDataSource() + FileDataSource::FileDataSource() : m_privateKeyPEM(nullptr) , m_certificatePEM(nullptr) , m_certificateAuthorityCertPEM(nullptr) @@ -73,13 +73,13 @@ namespace CertificateManager azfree(m_privateKeyPEM); azfree(m_certificatePEM); azfree(m_certificateAuthorityCertPEM); - } + } void FileDataSource::ConfigureDataSource(const char* keyPath, const char* certPath, const char* caPath) { ConfigurePrivateKey(keyPath); ConfigureCertificate(certPath); - ConfigureCertificateAuthority(caPath); + ConfigureCertificateAuthority(caPath); } void FileDataSource::ConfigurePrivateKey(const char* path) @@ -107,7 +107,7 @@ namespace CertificateManager if (path != nullptr) { LoadGenericFile(path,m_certificatePEM); - } + } } void FileDataSource::ConfigureCertificateAuthority(const char* path) @@ -133,7 +133,7 @@ namespace CertificateManager { return m_certificateAuthorityCertPEM; } - + bool FileDataSource::HasPublicKey() const { return m_certificatePEM != nullptr; @@ -143,7 +143,7 @@ namespace CertificateManager { return m_certificatePEM; } - + bool FileDataSource::HasPrivateKey() const { return m_privateKeyPEM != nullptr; diff --git a/Gems/EMotionFX/Code/CMakeLists.txt b/Gems/EMotionFX/Code/CMakeLists.txt index ed5447ba25..67d0ba6d85 100644 --- a/Gems/EMotionFX/Code/CMakeLists.txt +++ b/Gems/EMotionFX/Code/CMakeLists.txt @@ -36,6 +36,7 @@ ly_add_target( PUBLIC AZ::AtomCore Gem::Atom_RPI.Public + Gem::AtomLyIntegration_CommonFeatures.Static Gem::LmbrCentral COMPILE_DEFINITIONS PUBLIC @@ -108,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; + outResult = AZStd::string::format("Cannot import actor. Cannot find asset at path %s.", filename.c_str()); + return false; } - // 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) + // check if we have already loaded the actor + const size_t actorIndex = EMotionFX::GetActorManager().FindActorIndex(actorAssetId); + if (actorIndex != InvalidIndex) { - 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()); - return false; + return true; } - // 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); + // 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/Source/EMotionFXManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/EMotionFXManager.cpp index 300543c896..d5fa3867e0 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/EMotionFXManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/EMotionFXManager.cpp @@ -50,7 +50,7 @@ namespace EMotionFX // Create EMotion FX allocators Allocators::Create(); - + // create the new object gEMFX = AZ::Environment::CreateVariable(kEMotionFXInstanceVarName); gEMFX.Set(EMotionFXManager::Create()); @@ -166,11 +166,11 @@ namespace EMotionFX delete m_debugDraw; m_debugDraw = nullptr; - + m_eventManager->Destroy(); m_eventManager = nullptr; - + // delete the thread datas for (uint32 i = 0; i < m_threadDatas.size(); ++i) { @@ -341,7 +341,7 @@ namespace EMotionFX void EMotionFXManager::InitAssetFolderPaths() { // Initialize the asset source folder path. - const char* assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); + const char* assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@"); if (assetSourcePath) { m_assetSourceFolder = assetSourcePath; @@ -361,12 +361,12 @@ namespace EMotionFX } else { - AZ_Warning("EMotionFX", false, "Failed to set asset source path for alias '@devassets@'."); + AZ_Warning("EMotionFX", false, "Failed to set asset source path for alias '@projectroot@'."); } // Initialize the asset cache folder path. - const char* assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@"); + const char* assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@"); if (assetCachePath) { m_assetCacheFolder = assetCachePath; @@ -386,7 +386,7 @@ namespace EMotionFX } else { - AZ_Warning("EMotionFX", false, "Failed to set asset cache path for alias '@assets@'."); + AZ_Warning("EMotionFX", false, "Failed to set asset cache path for alias '@products@'."); } } 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; - } + return EMStudioManager::GetInstance()->GetApp(); + } + EMStudioManager* GetManager() + { + return EMStudioManager::GetInstance(); + } + + bool HasMainWindow() + { + return EMStudioManager::GetInstance()->HasMainWindow(); + } + + MainWindow* GetMainWindow() + { + return EMStudioManager::GetInstance()->GetMainWindow(); + } - // create the new EMStudio object - gEMStudioMgr = new EMStudioManager(app, argc, argv); + PluginManager* GetPluginManager() + { + return EMStudioManager::GetInstance()->GetPluginManager(); + } + + LayoutManager* GetLayoutManager() + { + return EMStudioManager::GetInstance()->GetLayoutManager(); + } - // return success - return true; + NotificationWindowManager* GetNotificationWindowManager() + { + return EMStudioManager::GetInstance()->GetNotificationWindowManager(); } + MotionEventPresetManager* GetEventPresetManager() + { + return EMStudioManager::GetInstance()->GetEventPresetManger(); + } - // the shutdown function - void Initializer::Shutdown() + CommandSystem::CommandManager* GetCommandManager() { - delete gEMStudioMgr; - gEMStudioMgr = nullptr; + 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 129d005f80..17438dd59a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/FileManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/FileManager.cpp @@ -72,13 +72,13 @@ namespace EMStudio { AZStd::string filename; - AZStd::string assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@"); + AZStd::string assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@"); AzFramework::StringFunc::AssetDatabasePath::Normalize(assetCachePath); AZStd::string relativePath; EBUS_EVENT_RESULT(relativePath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, assetId); AzFramework::StringFunc::AssetDatabasePath::Join(assetCachePath.c_str(), relativePath.c_str(), filename); - + return filename; } @@ -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())) { @@ -243,7 +239,7 @@ namespace EMStudio void FileManager::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::TypeId sourceTypeId) { AZStd::string filename; - AZStd::string assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); + AZStd::string assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@"); AzFramework::StringFunc::AssetDatabasePath::Normalize(assetSourcePath); AzFramework::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), relativePath.c_str(), filename); @@ -373,7 +369,7 @@ namespace EMStudio const ProductAssetBrowserEntry* product = azrtti_cast(assetBrowserEntry); filename.clear(); - AZStd::string cachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@"); + AZStd::string cachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@"); AzFramework::StringFunc::AssetDatabasePath::Normalize(cachePath); AzFramework::StringFunc::AssetDatabasePath::Join(cachePath.c_str(), product->GetRelativePath().c_str(), filename); @@ -395,7 +391,7 @@ namespace EMStudio { return AZStd::string(); } - + return filenames[0]; } @@ -435,12 +431,12 @@ namespace EMStudio AZStd::string result; if (EMStudio::GetCommandManager()->ExecuteCommand(command, result)) { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, "Actor successfully saved"); } else { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, AZStd::string::format("Actor failed to save

%s", result.c_str()).c_str()); } } @@ -574,12 +570,12 @@ namespace EMStudio AZStd::string result; if (GetCommandManager()->ExecuteCommand(command, result) == false) { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, AZStd::string::format("MotionSet failed to save

%s", result.c_str()).c_str()); } else { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, "MotionSet successfully saved"); } } @@ -608,7 +604,7 @@ namespace EMStudio } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - + AZStd::string FileManager::LoadAnimGraphFileDialog([[maybe_unused]] QWidget* parent) { GetManager()->SetAvoidRendering(true); @@ -618,7 +614,7 @@ namespace EMStudio { return AZStd::string(); } - + return filenames[0]; } 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 06088790d9..b75fb71060 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp @@ -378,7 +378,7 @@ namespace EMStudio // reset action m_resetAction = menu->addAction(tr("&Reset"), this, &MainWindow::OnReset, QKeySequence::New); m_resetAction->setObjectName("EMFX.MainWindow.ResetAction"); - + // save all m_saveAllAction = menu->addAction(tr("Save All..."), this, &MainWindow::OnSaveAll, QKeySequence::Save); m_saveAllAction->setObjectName("EMFX.MainWindow.SaveAllAction"); @@ -467,7 +467,7 @@ namespace EMStudio menu->addAction("Documentation", this, [] { QDesktopServices::openUrl(QUrl("https://o3de.org/docs/")); - }); + }); menu->addAction("Forums", this, [] { @@ -491,7 +491,7 @@ namespace EMStudio // load preferences PluginOptionsNotificationsBus::Router::BusRouterConnect(); - LoadPreferences(); + LoadPreferences(); m_autosaveTimer->setInterval(m_options.GetAutoSaveInterval() * 60 * 1000); // Create the dirty file manager and register the workspace callback. @@ -1072,7 +1072,7 @@ namespace EMStudio // get only the version number of EMotion FX AZStd::string emfxVersionString = EMotionFX::GetEMotionFX().GetVersionString(); AzFramework::StringFunc::Replace(emfxVersionString, "EMotion FX ", "", true /* case sensitive */); - + // set the window title // only set the EMotion FX version if the filename is empty AZStd::string windowTitle; @@ -1359,7 +1359,7 @@ namespace EMStudio void MainWindow::LoadCharacter(const AZ::Data::AssetId& actorAssetId, const AZ::Data::AssetId& animgraphId, const AZ::Data::AssetId& motionSetId) { m_characterFiles.clear(); - AZStd::string cachePath = gEnv->pFileIO->GetAlias("@assets@"); + AZStd::string cachePath = gEnv->pFileIO->GetAlias("@products@"); AZStd::string filename; AzFramework::StringFunc::AssetDatabasePath::Normalize(cachePath); @@ -1543,12 +1543,12 @@ namespace EMStudio AZStd::string result; if (EMStudio::GetCommandManager()->ExecuteCommand(command, result)) { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, "Workspace successfully saved"); } else { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, AZStd::string::format("Workspace failed to save

%s", result.c_str()).c_str()); } } @@ -1575,12 +1575,12 @@ namespace EMStudio AZStd::string result; if (EMStudio::GetCommandManager()->ExecuteCommand(command, result)) { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, "Workspace successfully saved"); } else { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, AZStd::string::format("Workspace failed to save

%s", result.c_str()).c_str()); } } @@ -1644,7 +1644,7 @@ namespace EMStudio Workspace* workspace = GetManager()->GetWorkspace(); workspace->SetDirtyFlag(true); - } + } void MainWindow::OnReset() { @@ -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); @@ -2312,7 +2307,7 @@ namespace EMStudio void MainWindow::Activate(const AZ::Data::AssetId& actorAssetId, const EMotionFX::AnimGraph* animGraph, const EMotionFX::MotionSet* motionSet) { - AZStd::string cachePath = gEnv->pFileIO->GetAlias("@assets@"); + AZStd::string cachePath = gEnv->pFileIO->GetAlias("@products@"); AZStd::string filename; AzFramework::StringFunc::AssetDatabasePath::Normalize(cachePath); @@ -2776,17 +2771,17 @@ namespace EMStudio AZStd::string result; if (GetCommandManager()->ExecuteCommandGroup(commandGroup, result, false)) { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS, "Autosave completed"); } else { - GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, + GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR, AZStd::string::format("Autosave failed

%s", result.c_str()).c_str()); } } - + void MainWindow::moveEvent(QMoveEvent* event) { MCORE_UNUSED(event); 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 4cdf645b16..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], "@assets@/", 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/Assets/AnimGraphAsset.cpp b/Gems/EMotionFX/Code/Source/Integration/Assets/AnimGraphAsset.cpp index 3e459d31d6..3593f58ff2 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Assets/AnimGraphAsset.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Assets/AnimGraphAsset.cpp @@ -6,6 +6,8 @@ * */ +#include + #include #include #include @@ -66,14 +68,10 @@ namespace EMotionFX // through this method. Once EMotionFX is integrated to the asset system this can go away. AZStd::string assetFilename; EBUS_EVENT_RESULT(assetFilename, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, asset.GetId()); - const char* devAssetsPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); - if (devAssetsPath) + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); + if (!projectPath.empty()) { - AZStd::string assetSourcePath = devAssetsPath; - - AzFramework::StringFunc::AssetDatabasePath::Normalize(assetSourcePath); - AZStd::string filename; - AzFramework::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), assetFilename.c_str(), filename); + AZ::IO::FixedMaxPathString filename{ (projectPath / assetFilename).LexicallyNormal().FixedMaxPathStringAsPosix() }; assetData->m_emfxAnimGraph->SetFileName(filename.c_str()); } @@ -81,7 +79,7 @@ namespace EMotionFX { if (GetEMotionFX().GetIsInEditorMode()) { - AZ_Warning("EMotionFX", false, "Failed to retrieve asset source path with alias '@devassets@'. Cannot set absolute filename for '%s'", assetFilename.c_str()); + AZ_Warning("EMotionFX", false, "Failed to retrieve project root path . Cannot set absolute filename for '%s'", assetFilename.c_str()); } assetData->m_emfxAnimGraph->SetFileName(assetFilename.c_str()); } diff --git a/Gems/EMotionFX/Code/Source/Integration/Assets/MotionSetAsset.cpp b/Gems/EMotionFX/Code/Source/Integration/Assets/MotionSetAsset.cpp index 5ca9a34e72..280cedd06a 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Assets/MotionSetAsset.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Assets/MotionSetAsset.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ namespace EMotionFX const char* motionFile = entry->GetFilename(); AZ::Data::AssetId motionAssetId; EBUS_EVENT_RESULT(motionAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, motionFile, azrtti_typeid(), false); - + // if it failed to find it, it might be still compiling - try forcing an immediate compile: if (!motionAssetId.IsValid()) { @@ -149,14 +150,11 @@ namespace EMotionFX // through this method. Once EMotionFX is integrated to the asset system this can go away. AZStd::string assetFilename; EBUS_EVENT_RESULT(assetFilename, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, asset.GetId()); - const char* devAssetsPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); - if (devAssetsPath) - { - AZStd::string assetSourcePath = devAssetsPath; - AZ::StringFunc::AssetDatabasePath::Normalize(assetSourcePath); - AZStd::string filename; - AZ::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), assetFilename.c_str(), filename); + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); + if (!projectPath.empty()) + { + AZ::IO::FixedMaxPathString filename{ (projectPath / assetFilename).LexicallyNormal().FixedMaxPathStringAsPosix() }; assetData->m_emfxMotionSet->SetFilename(filename.c_str()); } @@ -164,11 +162,11 @@ namespace EMotionFX { if (GetEMotionFX().GetIsInEditorMode()) { - AZ_Warning("EMotionFX", false, "Failed to retrieve asset source path with alias '@devassets@'. Cannot set absolute filename for '%s'", assetFilename.c_str()); + AZ_Warning("EMotionFX", false, "Failed to retrieve project root path . Cannot set absolute filename for '%s'", assetFilename.c_str()); } assetData->m_emfxMotionSet->SetFilename(assetFilename.c_str()); } - + // now load them in: const EMotionFX::MotionSet::MotionEntries& motionEntries = assetData->m_emfxMotionSet->GetMotionEntries(); // Get the motions in the motion set. Escalate them to the top of the build queue first so that they can be done in parallel. @@ -179,7 +177,7 @@ namespace EMotionFX const char* motionFilename = motionEntry->GetFilename(); AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, motionFilename); } - + // now that they're all escalated, the asset processor will be processing them across all threads, and we can request them one by one: for (const auto& item : motionEntries) { 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/Components/SimpleLODComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp index 5db0fae842..aa17058adf 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp @@ -142,12 +142,31 @@ namespace EMotionFX { ActorComponentNotificationBus::Handler::BusConnect(GetEntityId()); AZ::TickBus::Handler::BusConnect(); + + // Remember the lod type and level so that we can set it back to the previous one on deactivation of the component. + AZ::Render::MeshComponentRequestBus::EventResult(m_previousLodType, + GetEntityId(), + &AZ::Render::MeshComponentRequestBus::Events::GetLodType); + + if (m_actorInstance) + { + m_previousLodLevel = m_actorInstance->GetLODLevel(); + } } void SimpleLODComponent::Deactivate() { AZ::TickBus::Handler::BusDisconnect(); ActorComponentNotificationBus::Handler::BusDisconnect(); + + AZ::Render::MeshComponentRequestBus::Event(GetEntityId(), + &AZ::Render::MeshComponentRequestBus::Events::SetLodType, + m_previousLodType); + + if (m_actorInstance) + { + m_actorInstance->SetLODLevel(m_previousLodLevel); + } } void SimpleLODComponent::OnActorInstanceCreated(EMotionFX::ActorInstance* actorInstance) @@ -183,7 +202,7 @@ namespace EMotionFX return max - 1; } - void SimpleLODComponent::UpdateLodLevelByDistance(EMotionFX::ActorInstance * actorInstance, const Configuration& configuration, AZ::EntityId entityId) + void SimpleLODComponent::UpdateLodLevelByDistance(EMotionFX::ActorInstance* actorInstance, const Configuration& configuration, AZ::EntityId entityId) { if (actorInstance) { @@ -201,15 +220,30 @@ namespace EMotionFX AZ::RPI::ViewportContextPtr defaultViewportContext = viewportContextManager->GetViewportContextByName(viewportContextManager->GetDefaultViewportContextName()); const float distance = worldPos.GetDistance(defaultViewportContext->GetCameraTransform().GetTranslation()); - const size_t lodByDistance = GetLodByDistance(configuration.m_lodDistances, distance); - actorInstance->SetLODLevel(lodByDistance); + const size_t requestedLod = GetLodByDistance(configuration.m_lodDistances, distance); + actorInstance->SetLODLevel(requestedLod); if (configuration.m_enableLodSampling) { - const float animGraphSampleRate = configuration.m_lodSampleRates[lodByDistance]; + const float animGraphSampleRate = configuration.m_lodSampleRates[requestedLod]; const float updateRateInSeconds = animGraphSampleRate > 0.0f ? 1.0f / animGraphSampleRate : 0.0f; actorInstance->SetMotionSamplingRate(updateRateInSeconds); } + + // Disable the automatic mesh LOD level adjustment based on screen space in case a simple LOD component is present. + // The simple LOD component overrides the mesh LOD level and syncs the skeleton with the mesh LOD level. + AZ::Render::MeshComponentRequestBus::Event(entityId, + &AZ::Render::MeshComponentRequestBus::Events::SetLodType, + AZ::RPI::Cullable::LodType::SpecificLod); + + // When setting the actor instance LOD level, a change is just requested and with the next update it will get applied. + // This means that the current LOD level might differ from the requested one. We need to sync the Atom LOD level with the + // current LOD level of the actor instance to avoid skinning artifacts. The requested LOD level will be present and applied + // the following frame. + const size_t currentLod = actorInstance->GetLODLevel(); + AZ::Render::MeshComponentRequestBus::Event(entityId, + &AZ::Render::MeshComponentRequestBus::Events::SetLodOverride, + static_cast(currentLod)); } } } // namespace integration diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h index 96bebdc660..a00918e7c0 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h @@ -17,7 +17,7 @@ #include #include - +#include namespace EMotionFX { @@ -93,6 +93,9 @@ namespace EMotionFX Configuration m_configuration; // Component configuration. EMotionFX::ActorInstance* m_actorInstance; // Associated actor instance (retrieved from Actor Component). + + AZ::RPI::Cullable::LodType m_previousLodType = AZ::RPI::Cullable::LodType::Default; + size_t m_previousLodLevel = 0; }; } // namespace Integration 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 0f87c488b9..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 @@ -487,9 +488,9 @@ namespace EMotionFX return; } - SetMediaRoot("@assets@"); - // \todo Right now we're pointing at the @devassets@ location (source) and working from there, because .actor and .motion (motion) aren't yet processed through - // the scene pipeline. Once they are, we'll need to update various segments of the Tool to always read from the @assets@ cache, but write to the @devassets@ data/metadata. + SetMediaRoot("@products@"); + // \todo Right now we're pointing at the @projectroot@ location (source) and working from there, because .actor and .motion (motion) aren't yet processed through + // the scene pipeline. Once they are, we'll need to update various segments of the Tool to always read from the @products@ cache, but write to the @projectroot@ data/metadata. EMotionFX::GetEMotionFX().InitAssetFolderPaths(); // Register EMotionFX event handler @@ -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/Bugs/CanDeleteMotionSetWhenSameMotionInTwoMotionSets.cpp b/Gems/EMotionFX/Code/Tests/Bugs/CanDeleteMotionSetWhenSameMotionInTwoMotionSets.cpp index 25cd840cda..44453ed70f 100644 --- a/Gems/EMotionFX/Code/Tests/Bugs/CanDeleteMotionSetWhenSameMotionInTwoMotionSets.cpp +++ b/Gems/EMotionFX/Code/Tests/Bugs/CanDeleteMotionSetWhenSameMotionInTwoMotionSets.cpp @@ -25,11 +25,11 @@ namespace EMotionFX ExecuteCommands({ R"str(CreateMotionSet -name MotionSet0)str", R"str(CreateMotionSet -name MotionSet1)str", - R"str(MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str", - R"str(MotionSetAddMotion -motionSetID 1 -motionFilenamesAndIds @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str", + R"str(MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str", + R"str(MotionSetAddMotion -motionSetID 1 -motionFilenamesAndIds @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str", R"str(MotionSetRemoveMotion -motionSetID 0 -motionIds rin_idle)str", R"str(RemoveMotionSet -motionSetID 0)str", - R"str(RemoveMotion -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion)str", + R"str(RemoveMotion -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion)str", }); EMStudio::MotionSetsWindowPlugin* motionSetsWindowPlugin = static_cast(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::MotionSetsWindowPlugin::CLASS_ID)); 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/CommandRemoveMotionTests.cpp b/Gems/EMotionFX/Code/Tests/CommandRemoveMotionTests.cpp index ce0b586bfe..e4e58fa01d 100644 --- a/Gems/EMotionFX/Code/Tests/CommandRemoveMotionTests.cpp +++ b/Gems/EMotionFX/Code/Tests/CommandRemoveMotionTests.cpp @@ -33,7 +33,7 @@ namespace EMotionFX ASSERT_TRUE(motionSet) << "Motion set with id 0 does not exist"; motionSetsWindowPlugin->SetSelectedSet(motionSet); - const std::string filename = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"; + const std::string filename = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"; ExecuteCommands({ "ImportMotion -filename " + filename, "MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds " + filename + ";rin_idle", diff --git a/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp b/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp index 0155b38da2..7b9f23f094 100644 --- a/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp +++ b/Gems/EMotionFX/Code/Tests/EMotionFXBuilderTests.cpp @@ -55,7 +55,7 @@ namespace EMotionFX // By using this mock catalog, we can pretend to load the specific referenced assets without actually loading anything. UnitTest::MockLoadAssetCatalogAndHandler testAssetCatalog({ referencedAnimGraph, referencedMotionSet }); - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExample.animgraph"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExample.animgraph"; AZStd::vector productDependencies; EMotionFXBuilder::AnimGraphBuilderWorker builderWorker; @@ -68,7 +68,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestAnimGraphAsset_NoDependency_OutputNoProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExampleNoDependency.animgraph"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExampleNoDependency.animgraph"; AZStd::vector productDependencies; EMotionFXBuilder::AnimGraphBuilderWorker builderWorker; @@ -78,7 +78,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestAnimGraphAsset_InvalidFilePath_OutputNoProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.animgraph"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.animgraph"; AZStd::vector productDependencies; EMotionFXBuilder::AnimGraphBuilderWorker builderWorker; @@ -88,7 +88,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestAnimGraphAsset_EmptyFile_OutputNoProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyAnimGraphExample.animgraph"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyAnimGraphExample.animgraph"; AZStd::vector productDependencies; EMotionFXBuilder::AnimGraphBuilderWorker builderWorker; @@ -100,7 +100,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_HasReferenceNode_OutputProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset"; ProductPathDependencySet productDependencies; EMotionFXBuilder::MotionSetBuilderWorker builderWorker; @@ -112,7 +112,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_NoDependency_OutputNoProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExampleNoDependency.motionset"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExampleNoDependency.motionset"; ProductPathDependencySet productDependencies; EMotionFXBuilder::MotionSetBuilderWorker builderWorker; @@ -122,7 +122,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_InvalidFilePath_OutputNoProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.motionset"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.motionset"; ProductPathDependencySet productDependencies; EMotionFXBuilder::MotionSetBuilderWorker builderWorker; @@ -132,7 +132,7 @@ namespace EMotionFX TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_EmptyFile_OutputNoProductDependencies) { - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyMotionSetExample.motionset"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyMotionSetExample.motionset"; ProductPathDependencySet productDependencies; EMotionFXBuilder::MotionSetBuilderWorker builderWorker; diff --git a/Gems/EMotionFX/Code/Tests/Editor/MotionSetLoadEscalation.cpp b/Gems/EMotionFX/Code/Tests/Editor/MotionSetLoadEscalation.cpp index b1650ce0c6..905c4a825b 100644 --- a/Gems/EMotionFX/Code/Tests/Editor/MotionSetLoadEscalation.cpp +++ b/Gems/EMotionFX/Code/Tests/Editor/MotionSetLoadEscalation.cpp @@ -124,7 +124,7 @@ namespace EMotionFX { using testing::_; - const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset"; + const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset"; MockAssetSystemRequests assetSystem; EXPECT_CALL(assetSystem, CompileAssetSync(_)) diff --git a/Gems/EMotionFX/Code/Tests/Game/SamplePerformanceTests.cpp b/Gems/EMotionFX/Code/Tests/Game/SamplePerformanceTests.cpp index ff7ead3800..36abf2b1d2 100644 --- a/Gems/EMotionFX/Code/Tests/Game/SamplePerformanceTests.cpp +++ b/Gems/EMotionFX/Code/Tests/Game/SamplePerformanceTests.cpp @@ -302,7 +302,7 @@ namespace EMotionFX GetEMotionFX().SetMediaRootFolder(assetFolder.c_str()); GetEMotionFX().InitAssetFolderPaths(); - const char* actorFilename = "@assets@\\animationsamples\\advanced_rinlocomotion\\actor\\rinactor.actor"; + const char* actorFilename = "@products@\\animationsamples\\advanced_rinlocomotion\\actor\\rinactor.actor"; Importer* importer = GetEMotionFX().GetImporter(); importer->SetLoggingEnabled(false); @@ -402,9 +402,9 @@ namespace EMotionFX // This path points to assets in the advance rin demo. // To test different assets, change the path here. - const char* actorFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor"; - const char* motionSetFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset"; - const char* animGraphFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph"; + const char* actorFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor"; + const char* motionSetFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset"; + const char* animGraphFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph"; Importer* importer = GetEMotionFX().GetImporter(); importer->SetLoggingEnabled(false); @@ -714,9 +714,9 @@ namespace EMotionFX // This path points to assets in the advance rin demo. // To test different assets, change the path here. - const char* actorFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor"; - const char* motionSetFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset"; - const char* animGraphFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph"; + const char* actorFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor"; + const char* motionSetFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset"; + const char* animGraphFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph"; Importer* importer = GetEMotionFX().GetImporter(); importer->SetLoggingEnabled(false); @@ -772,13 +772,13 @@ namespace EMotionFX TEST_F(PerformanceTestFixture, DISABLED_MotionSamplingPerformanceNonUniform) { // Make sure that the motion is set to use NonUniform sampling! Change this in the scene settings! Otherwise you get wrong results. - TestMotionSamplingPerformance("@assets@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_idle.motion"); + TestMotionSamplingPerformance("@products@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_idle.motion"); } TEST_F(PerformanceTestFixture, DISABLED_MotionSamplingPerformanceUniform) { // Make sure that the motion is set to use Uniform sampling! Change this in the scene settings! Otherwise you get wrong results. - TestMotionSamplingPerformance("@assets@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_walk_kick_01.motion"); + TestMotionSamplingPerformance("@products@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_walk_kick_01.motion"); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp b/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp index 9b18ca5862..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 @devroot@/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/Integration/PoseComparisonFixture.h b/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonFixture.h index d354153f9b..0006b79628 100644 --- a/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonFixture.h +++ b/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonFixture.h @@ -27,7 +27,7 @@ namespace EMotionFX {} }; - class INTEG_PoseComparisonFixture + class PoseComparisonFixture : public SystemComponentFixture , public ::testing::WithParamInterface { @@ -47,8 +47,8 @@ namespace EMotionFX // This fixture exists to separate the tests that test the pose comparsion // functionality from the tests that use the pose comparison functionality // (even though it doesn't use the recording) - class INTEG_TestPoseComparisonFixture - : public INTEG_PoseComparisonFixture + class TestPoseComparisonFixture + : public PoseComparisonFixture { }; }; // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonTests.cpp b/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonTests.cpp index cccf744cfc..c704a5b114 100644 --- a/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonTests.cpp +++ b/Gems/EMotionFX/Code/Tests/Integration/PoseComparisonTests.cpp @@ -154,14 +154,14 @@ namespace EMotionFX return MakeMatcher(new KeyTrackMatcher(expected, nodeName)); } - void INTEG_PoseComparisonFixture::SetUp() + void PoseComparisonFixture::SetUp() { SystemComponentFixture::SetUp(); LoadAssets(); } - void INTEG_PoseComparisonFixture::TearDown() + void PoseComparisonFixture::TearDown() { m_actorInstance->Destroy(); @@ -176,7 +176,7 @@ namespace EMotionFX SystemComponentFixture::TearDown(); } - void INTEG_PoseComparisonFixture::LoadAssets() + void PoseComparisonFixture::LoadAssets() { const AZStd::string actorPath = ResolvePath(GetParam().m_actorFile); m_actor = EMotionFX::GetImporter().LoadActor(actorPath); @@ -195,7 +195,7 @@ namespace EMotionFX m_actorInstance->SetAnimGraphInstance(AnimGraphInstance::Create(m_animGraph, m_actorInstance, m_motionSet)); } - TEST_P(INTEG_PoseComparisonFixture, Integ_TestPoses) + TEST_P(PoseComparisonFixture, TestPoses) { const AZStd::string recordingPath = ResolvePath(GetParam().m_recordingFile); Recorder* recording = EMotionFX::Recorder::LoadFromFile(recordingPath.c_str()); @@ -231,7 +231,7 @@ namespace EMotionFX recording->Destroy(); } - TEST_P(INTEG_TestPoseComparisonFixture, Integ_TestRecording) + TEST_P(TestPoseComparisonFixture, TestRecording) { // Make one recording, 10 seconds at 60 fps Recorder::RecordSettings settings; @@ -294,30 +294,30 @@ namespace EMotionFX recording->Destroy(); } - INSTANTIATE_TEST_CASE_P(Integ_TestPoses, INTEG_PoseComparisonFixture, + INSTANTIATE_TEST_CASE_P(DISABLED_TestPoses, PoseComparisonFixture, ::testing::Values( PoseComparisonFixtureParams ( - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording" + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording" ), PoseComparisonFixtureParams ( - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.actor", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.animgraph", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.motionset", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.emfxrecording" + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.actor", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.animgraph", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.motionset", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.emfxrecording" ) ) ); - INSTANTIATE_TEST_CASE_P(Integ_TestPoseComparison, INTEG_TestPoseComparisonFixture, + INSTANTIATE_TEST_CASE_P(DISABLED_TestPoseComparison, TestPoseComparisonFixture, ::testing::Values( PoseComparisonFixtureParams ( - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset", - "@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording" + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset", + "@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording" ) ) ); 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/AnimGraph/PreviewMotionFixture.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/PreviewMotionFixture.cpp index 287c34723f..16b223914e 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/PreviewMotionFixture.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/AnimGraph/PreviewMotionFixture.cpp @@ -30,12 +30,12 @@ namespace EMotionFX motionSetsWindowPlugin->SetSelectedSet(motionSet); ExecuteCommands({ - "ImportMotion -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion", - "MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle" + "ImportMotion -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion", + "MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle" }); char resolvedPath[AZ::IO::MaxPathLength]; - EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion", resolvedPath, AZ_ARRAY_SIZE(resolvedPath))); + EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->ResolvePath("@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion", resolvedPath, AZ_ARRAY_SIZE(resolvedPath))); m_motionFileName = resolvedPath; AzFramework::ApplicationRequests::Bus::Broadcast([](AzFramework::ApplicationRequests* requests, AZStd::string& path) { requests->NormalizePathKeepCase(path); }, m_motionFileName); m_motionName = "rin_idle"; diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Menus/FileMenu/CanReset.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Menus/FileMenu/CanReset.cpp index 1f9a78a413..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 { @@ -44,7 +43,7 @@ namespace EMotionFX RecordProperty("test_case_id", "C16302179"); - const AZStd::string motionAsset("@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"); + const AZStd::string motionAsset("@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"); const AZStd::string createAnimGraphCmd("CreateAnimGraph"); const AZStd::string motionSetName("TestMotionSet"); const AZStd::string createMotionSetCmd("CreateMotionSet -motionSetID 42 -name " + motionSetName); @@ -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/Motions/CanAddMotions.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/CanAddMotions.cpp index 82bfeba557..bb8368b17e 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/CanAddMotions.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/CanAddMotions.cpp @@ -36,7 +36,7 @@ namespace EMotionFX RecordProperty("test_case_id", "C1559124"); const QString assetName = "rin_idle"; // Asset name to appear in table - const AZStd::string motionCmd = AZStd::string::format("ImportMotion -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"); + const AZStd::string motionCmd = AZStd::string::format("ImportMotion -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"); auto motionWindowPlugin = static_cast(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::MotionWindowPlugin::CLASS_ID)); ASSERT_TRUE(motionWindowPlugin) << "Could not find the Motion Window Plugin"; diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/MotionPlaybacksTests.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/MotionPlaybacksTests.cpp index 6804d351cf..14d27ed60f 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/MotionPlaybacksTests.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Motions/MotionPlaybacksTests.cpp @@ -38,7 +38,7 @@ namespace EMotionFX EXPECT_EQ(table->rowCount(), 1) << "Expected the table to have no rows yet"; // Create actor and actor instance. - const char* actorFilename = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor"; + const char* actorFilename = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor"; AZStd::unique_ptr m_actor = EMotionFX::GetImporter().LoadActor(actorFilename); EXPECT_TRUE(m_actor.get() != nullptr) << "Actor not loaded."; EMotionFX::ActorInstance* m_actorInstance = ActorInstance::Create(m_actor.get()); 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/CanAutoSaveFile.cpp b/Gems/EMotionFX/Code/Tests/UI/CanAutoSaveFile.cpp index 4eb44a54ab..bdbc50fe9c 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanAutoSaveFile.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanAutoSaveFile.cpp @@ -26,7 +26,7 @@ namespace EMotionFX EMStudio::GetMainWindow()->ApplicationModeChanged("AnimGraph"); // Load Rin anim graph. - const char* rinGraph = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph"; + const char* rinGraph = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph"; const AZStd::string rinGraphPath = ResolvePath(rinGraph); AZStd::string command = AZStd::string::format("LoadAnimGraph -filename \"%s\"", rinGraphPath.c_str()); AZStd::string result; 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 b24971bfde..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. @@ -551,7 +555,7 @@ namespace EMotionFX QString GetTestMotionFileName() const { - AZStd::string resolvedAssetPath = this->ResolvePath("@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"); + AZStd::string resolvedAssetPath = this->ResolvePath("@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion"); return QString::fromUtf8(resolvedAssetPath.data(), aznumeric_cast(resolvedAssetPath.size())); } @@ -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/EditorPythonBindings/Assets/release_notes.md b/Gems/EditorPythonBindings/Assets/release_notes.md index 4a4b5bc71f..59192e1fdc 100644 --- a/Gems/EditorPythonBindings/Assets/release_notes.md +++ b/Gems/EditorPythonBindings/Assets/release_notes.md @@ -59,7 +59,7 @@ The API: - ExecuteByString(string) – runs a string buffer in the Python VM; it returns no value - ExecuteByFilename(string) – loads a file off of the disk to execute in the Python VM; the call returns no value. The filename can contain an alias such as - ‘\@devroot\@’ to execute a project relative file inside the Editor + ‘\@projectroot\@’ to execute a project relative file inside the Editor #### New Console Commands diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp index 706ca48156..df246d230d 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp @@ -17,10 +17,16 @@ #include #include +#include +#include #include +#include #include #include +#include +#include +#include namespace EditorPythonBindings { @@ -571,6 +577,37 @@ namespace EditorPythonBindings return false; } + pybind11::object PythonProxyObject::ToJson() + { + rapidjson::Document document; + AZ::JsonSerializerSettings settings; + settings.m_keepDefaults = true; + + auto resultCode = + AZ::JsonSerialization::Store(document, document.GetAllocator(), m_wrappedObject.m_address, nullptr, m_wrappedObject.m_typeId, settings); + + if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Error("PythonProxyObject", false, "Failed to serialize to json"); + return pybind11::cast(Py_None); + } + + AZStd::string jsonString; + AZ::Outcome outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString); + + if (!outcome.IsSuccess()) + { + AZ_Error("PythonProxyObject", false, "Failed to write json string: %s", outcome.GetError().c_str()); + return pybind11::cast(Py_None); + } + + jsonString.erase(AZStd::remove(jsonString.begin(), jsonString.end(), '\n'), jsonString.end()); + auto pythonCode = AZStd::string::format( + R"PYTHON(exec("import json") or json.loads("""%s"""))PYTHON", jsonString.c_str()); + + return pybind11::eval(pythonCode.c_str()); + } + bool PythonProxyObject::DoComparisonEvaluation(pybind11::object pythonOther, Comparison comparison) { bool invertLogic = false; @@ -912,6 +949,7 @@ namespace EditorPythonBindings .def("set_property", &PythonProxyObject::SetPropertyValue) .def("get_property", &PythonProxyObject::GetPropertyValue) .def("invoke", &PythonProxyObject::Invoke) + .def("to_json", &PythonProxyObject::ToJson) .def(Operator::s_isEqual, [](PythonProxyObject& self, pybind11::object rhs) { return self.DoEqualityEvaluation(rhs); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h index 5b611c32c0..a9d2fad5a0 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h @@ -58,6 +58,8 @@ namespace EditorPythonBindings //! Performs an equality operation to compare this object with another object bool DoEqualityEvaluation(pybind11::object pythonOther); + pybind11::object ToJson(); + //! Perform a comparison of a Python operator enum class Comparison { diff --git a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp index 4ca40a5bab..81476bdd7f 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp @@ -38,7 +38,7 @@ namespace EditorPythonBindings static constexpr const char* s_default = "default"; static constexpr const char* s_globals = "globals"; - // a structure for pybind11 to bind to hold constants, properties, and enums from the Behavior Context + // a structure for pybind11 to bind to hold constants, properties, and enums from the Behavior Context struct StaticPropertyHolder final { AZ_CLASS_ALLOCATOR(StaticPropertyHolder, AZ::SystemAllocator, 0); @@ -54,7 +54,7 @@ namespace EditorPythonBindings if (m_behaviorContext == nullptr) { return false; - } + } m_fullName = PyModule_GetName(scope.ptr()); @@ -199,12 +199,10 @@ namespace EditorPythonBindings } }); - RegisterAliasIfExists(pathsModule, "@devroot@", "devroot"); RegisterAliasIfExists(pathsModule, "@engroot@", "engroot"); - RegisterAliasIfExists(pathsModule, "@assets@", "assets"); - RegisterAliasIfExists(pathsModule, "@devassets@", "devassets"); + RegisterAliasIfExists(pathsModule, "@products@", "products"); + RegisterAliasIfExists(pathsModule, "@projectroot@", "projectroot"); RegisterAliasIfExists(pathsModule, "@log@", "log"); - RegisterAliasIfExists(pathsModule, "@root@", "root"); const char* executableFolder = nullptr; AZ::ComponentApplicationBus::BroadcastResult(executableFolder, &AZ::ComponentApplicationBus::Events::GetExecutableFolder); @@ -363,7 +361,7 @@ namespace EditorPythonBindings m_staticPropertyHolderMap.reset(); EditorPythonBindings::EditorPythonBindingsNotificationBus::Handler::BusDisconnect(); } - + void PythonReflectionComponent::OnImportModule(PyObject* module) { pybind11::module parentModule = pybind11::cast(module); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp index cecbf15c5a..876ef19803 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp @@ -524,7 +524,7 @@ namespace EditorPythonBindings { AZ::IO::Path newSourcePath = jsonSourcePathPointer; // Resolve any file aliases first - Do not use ResolvePath() as that assumes - // any relative path is underneath the @assets@ alias + // any relative path is underneath the @products@ alias if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr) { AZ::IO::FixedMaxPath replacedAliasPath; @@ -803,7 +803,7 @@ namespace EditorPythonBindings return Result::Error_InvalidFilename; } - // support the alias version of a script such as @devroot@/Editor/Scripts/select_story_anim_objects.py + // support the alias version of a script such as @engroot@/Editor/Scripts/select_story_anim_objects.py AZStd::string theFilename(filename); { char resolvedPath[AZ_MAX_PATH_LEN] = { 0 }; diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp index 12d7d696d3..5aa4921287 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp @@ -268,7 +268,7 @@ print ('entityId invalid is ' + str(entityId.id)) { Skip = 0, ImportModule, - TestCallHit, + TestCallHit, TestTypeDoCall1 }; @@ -302,7 +302,7 @@ print ('entityId invalid is ' + str(entityId.id)) pybind11::exec(R"( import sys, os import azlmbr.paths - sys.path.append(os.path.join(azlmbr.paths.devroot,'Gems','EditorPythonBindings','Code','Tests')) + sys.path.append(os.path.join(azlmbr.paths.engroot,'Gems','EditorPythonBindings','Code','Tests')) from test_package import import_test as itest print('ImportModule') itest.test_call() @@ -401,8 +401,8 @@ print ('entityId invalid is ' + str(entityId.id)) pybind11::exec(R"( import sys, os import azlmbr.paths - sys.path.append(os.path.join(azlmbr.paths.devroot,'Gems','EditorPythonBindings','Code','Tests')) - sys.path.append(os.path.join(azlmbr.paths.devroot,'Gems','EditorPythonBindings','Code','Tests','test_package')) + sys.path.append(os.path.join(azlmbr.paths.engroot,'Gems','EditorPythonBindings','Code','Tests')) + sys.path.append(os.path.join(azlmbr.paths.engroot,'Gems','EditorPythonBindings','Code','Tests','test_package')) from test_package import import_many import_many.test_many_entity_id() diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp index f68bef1c93..96619997bc 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp @@ -161,7 +161,7 @@ namespace UnitTest ->Method("accept_vector_of_floats", &PythonReflectionContainerSimpleTypes::AcceptVectorOfFloats, nullptr, "") ->Method("return_vector_of_doubles", &PythonReflectionContainerSimpleTypes::ReturnVectorOfDoubles, nullptr, "") ->Method("accept_vector_of_doubles", &PythonReflectionContainerSimpleTypes::AcceptVectorOfDoubles, nullptr, "") - ->Property("vector_of_s8", + ->Property("vector_of_s8", [](PythonReflectionContainerSimpleTypes* self) { return self->m_s8ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_s8ValueValues.AcceptValues(values); }) ->Property("vector_of_u8", @@ -792,7 +792,7 @@ namespace UnitTest theAsset = reflectAny.access_any_ref() if( reflectAny.compare_asset_ids(theAsset,testObject.theAsset) ): print ('MutateAssetId') - + )"); } catch ([[maybe_unused]] const std::exception& e) @@ -1429,7 +1429,6 @@ namespace UnitTest { Skip = 0, EngrootIs, - DevrootIs, pathResolvedTo, }; @@ -1442,10 +1441,6 @@ namespace UnitTest { return static_cast(LogTypes::EngrootIs); } - else if (AzFramework::StringFunc::StartsWith(message, "devroot is ")) - { - return static_cast(LogTypes::DevrootIs); - } else if (AzFramework::StringFunc::StartsWith(message, "path resolved to ")) { return static_cast(LogTypes::pathResolvedTo); @@ -1470,9 +1465,6 @@ namespace UnitTest if (len(azlmbr.paths.engroot) != 0): print ('engroot is {}'.format(azlmbr.paths.engroot)) - if (len(azlmbr.paths.devroot) != 0): - print ('devroot is {}'.format(azlmbr.paths.devroot)) - path = azlmbr.paths.resolve_path('@engroot@/engineassets/texturemsg/defaultsolids.mtl') if (path.find('@engroot@') == -1): print ('path resolved to {}'.format(path)) @@ -1487,7 +1479,6 @@ namespace UnitTest e.Deactivate(); EXPECT_EQ(m_testSink.m_evaluationMap[(int)LogTypes::EngrootIs], 1); - EXPECT_EQ(m_testSink.m_evaluationMap[(int)LogTypes::DevrootIs], 1); EXPECT_EQ(m_testSink.m_evaluationMap[(int)LogTypes::pathResolvedTo], 1); } } diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h b/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h index a0109f8029..143863b4c8 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h +++ b/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h @@ -81,7 +81,6 @@ namespace UnitTest } m_fileIOHelper = AZStd::make_unique(); - m_fileIOHelper->m_fileIO.SetAlias("@devroot@", m_engineRoot.c_str()); m_fileIOHelper->m_fileIO.SetAlias("@engroot@", m_engineRoot.c_str()); AzFramework::Application::Descriptor appDesc; diff --git a/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl b/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl index be9ddb4f20..800257e157 100644 --- a/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl +++ b/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl @@ -28,6 +28,19 @@ namespace AZ private: using VariableDescriptor = ExpressionEvaluation::ExpressionTree::VariableDescriptor; + static constexpr AZStd::string_view EmptyAnyIdentifier = "Empty AZStd::any"; + + static bool IsEmptyAny(const rapidjson::Value& typeId) + { + if (typeId.IsString()) + { + AZStd::string_view typeName(typeId.GetString(), typeId.GetStringLength()); + return typeName == EmptyAnyIdentifier; + } + + return false; + } + JsonSerializationResult::Result Load ( void* outputValue , [[maybe_unused]] const Uuid& outputValueTypeId @@ -62,22 +75,25 @@ namespace AZ , JsonSerialization::TypeIdFieldIdentifier)); } - result.Combine(LoadTypeId(typeId, typeIdMember->value, context)); - if (typeId.IsNull()) - { - return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic - , "ExpressionTreeVariableDescriptorSerializer::Load failed to load the AZ TypeId of the value"); - } - - AZStd::any storage = context.GetSerializeContext()->CreateAny(typeId); - if (storage.empty() || storage.type() != typeId) + if (!IsEmptyAny(typeIdMember->value)) { - return context.Report(result, "ExpressionTreeVariableDescriptorSerializer::Load failed to load a value matched the " - "reported AZ TypeId. The C++ declaration may have been deleted or changed."); + result.Combine(LoadTypeId(typeId, typeIdMember->value, context)); + if (typeId.IsNull()) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic + , "ExpressionTreeVariableDescriptorSerializer::Load failed to load the AZ TypeId of the value"); + } + + AZStd::any storage = context.GetSerializeContext()->CreateAny(typeId); + if (storage.empty() || storage.type() != typeId) + { + return context.Report(result, "ExpressionTreeVariableDescriptorSerializer::Load failed to load a value matched the " + "reported AZ TypeId. The C++ declaration may have been deleted or changed."); + } + + result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast(&storage), typeId, inputValue, "Value", context)); + outputDatum->m_value = storage; } - - result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast(&storage), typeId, inputValue, "Value", context)); - outputDatum->m_value = storage; // any storage end return context.Report(result, result.GetProcessing() != JSR::Processing::Halted @@ -123,20 +139,32 @@ namespace AZ , azrtti_typeidm_supportedTypes)>() , context)); - rapidjson::Value typeValue; - result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->m_value.type(), context)); - outputValue.AddMember - ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) - , AZStd::move(typeValue) - , context.GetJsonAllocator()); - - result.Combine(ContinueStoringToJsonObjectField - ( outputValue - , "Value" - , AZStd::any_cast(const_cast(&inputScriptDataPtr->m_value)) - , defaultScriptDataPtr ? AZStd::any_cast(const_cast(&defaultScriptDataPtr->m_value)) : nullptr - , inputScriptDataPtr->m_value.type() - , context)); + if (!inputScriptDataPtr->m_value.empty()) + { + rapidjson::Value typeValue; + result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->m_value.type(), context)); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(typeValue) + , context.GetJsonAllocator()); + + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "Value" + , AZStd::any_cast(const_cast(&inputScriptDataPtr->m_value)) + , defaultScriptDataPtr ? AZStd::any_cast(const_cast(&defaultScriptDataPtr->m_value)) : nullptr + , inputScriptDataPtr->m_value.type() + , context)); + } + else + { + rapidjson::Value emptyAny; + emptyAny.SetString(EmptyAnyIdentifier.data(), aznumeric_caster(EmptyAnyIdentifier.size()), context.GetJsonAllocator()); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(emptyAny) + , context.GetJsonAllocator()); + } return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ? "VariableDescriptor Store finished saving VariableDescriptor" diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelLoading.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelLoading.inl index 38747553a9..7dbe516aa5 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelLoading.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelLoading.inl @@ -27,7 +27,7 @@ namespace GameStateSamples IConsole* iConsole = iSystem ? iSystem->GetIConsole() : nullptr; if (iConsole) { - iConsole->GetCVar("level_load_screen_uicanvas_path")->Set("@assets@/ui/canvases/defaultlevelloadingscreen.uicanvas"); + iConsole->GetCVar("level_load_screen_uicanvas_path")->Set("@products@/ui/canvases/defaultlevelloadingscreen.uicanvas"); iConsole->GetCVar("level_load_screen_sequence_to_auto_play")->Set("DefaultLevelLoadingAnimatedSequence"); } } diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelPaused.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelPaused.inl index 63fea35224..4227e45fe9 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelPaused.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelPaused.inl @@ -182,7 +182,7 @@ namespace GameStateSamples //////////////////////////////////////////////////////////////////////////////////////////////// inline const char* GameStateLevelPaused::GetPauseMenuCanvasAssetPath() { - return "@assets@/ui/canvases/defaultpausemenuscreen.uicanvas"; + return "@products@/ui/canvases/defaultpausemenuscreen.uicanvas"; } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelRunning.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelRunning.inl index f9ec19581c..a4af32feb8 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelRunning.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLevelRunning.inl @@ -206,6 +206,6 @@ namespace GameStateSamples //////////////////////////////////////////////////////////////////////////////////////////////// inline const char* GameStateLevelRunning::GetPauseButtonCanvasAssetPath() { - return "@assets@/ui/canvases/defaultpausebuttonfortouchscreens.uicanvas"; + return "@products@/ui/canvases/defaultpausebuttonfortouchscreens.uicanvas"; } } diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLocalUserLobby.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLocalUserLobby.inl index dc658611b5..4a4fbac93a 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLocalUserLobby.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLocalUserLobby.inl @@ -223,7 +223,7 @@ namespace GameStateSamples //////////////////////////////////////////////////////////////////////////////////////////////// inline const char* GameStateLocalUserLobby::GetSignedInUserOverlayCanvasAssetPath() { - return "@assets@/ui/canvases/defaultsignedinusersoverlay.uicanvas"; + return "@products@/ui/canvases/defaultsignedinusersoverlay.uicanvas"; } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -282,7 +282,7 @@ namespace GameStateSamples } // ...sort them by index and then go through to check whether they have been - // assigned a local user id. If so, auto-assign their local user id into the + // assigned a local user id. If so, auto-assign their local user id into the // first available local player slot (unless they've already been assigned). AZStd::sort(gamepadInputDevices.begin(), gamepadInputDevices.end(), [](const AzFramework::InputDevice* lhs, const AzFramework::InputDevice* rhs) diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateMainMenu.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateMainMenu.inl index 41ee034844..98147756f4 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateMainMenu.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateMainMenu.inl @@ -257,7 +257,7 @@ namespace GameStateSamples //////////////////////////////////////////////////////////////////////////////////////////////// inline const char* GameStateMainMenu::GetMainMenuCanvasAssetPath() { - return "@assets@/ui/canvases/defaultmainmenuscreen.uicanvas"; + return "@products@/ui/canvases/defaultmainmenuscreen.uicanvas"; } diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateOptionsMenu.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateOptionsMenu.inl index 64269d223f..4389f5f0b7 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateOptionsMenu.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateOptionsMenu.inl @@ -215,7 +215,7 @@ namespace GameStateSamples //////////////////////////////////////////////////////////////////////////////////////////////// inline const char* GameStateOptionsMenu::GetOptionsMenuCanvasAssetPath() { - return "@assets@/ui/canvases/defaultoptionsmenuscreen.uicanvas"; + return "@products@/ui/canvases/defaultoptionsmenuscreen.uicanvas"; } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStatePrimaryUserSelection.inl b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStatePrimaryUserSelection.inl index bf50913ef9..42ad2290e1 100644 --- a/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStatePrimaryUserSelection.inl +++ b/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStatePrimaryUserSelection.inl @@ -168,6 +168,6 @@ namespace GameStateSamples //////////////////////////////////////////////////////////////////////////////////////////////// inline const char* GameStatePrimaryUserSelection::GetPrimaryUserSelectionCanvasAssetPath() { - return "@assets@/ui/canvases/defaultprimaryuserselectionscreen.uicanvas"; + return "@products@/ui/canvases/defaultprimaryuserselectionscreen.uicanvas"; } } 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; - - RegisterStyleSheetBus(m_editorId); - } - } - - // 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; + void SetEditorId(const EditorId& editorId); + void SetScene(const AZ::EntityId& sceneId); - EditorId editorId; - SceneRequestBus::EventResult(editorId, m_scene, &SceneRequests::GetEditorId); - SetEditorId(editorId); - } + void SetStyle(const AZStd::string& style); + void SetStyle(const AZ::EntityId& styledEntity); + void SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement); - void SetStyle(const AZ::EntityId& styledEntity) - { - ReleaseStyle(); + bool HasAttribute(Styling::Attribute attribute) const; + void RemoveAttributeOverride(Styling::Attribute attribute); - m_styledEntity = styledEntity; + QColor GetColor(Styling::Attribute color, QColor defaultValue = QColor()) const; + QFont GetFont() const; - 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(); - -#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); + //! 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; - SetScene(sceneId); + 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; - PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateVirtualChild, parentStyledEntity, virtualChildElement); + bool HasTextAlignment() const; + Qt::Alignment GetTextAlignment(Qt::Alignment defaultAlignment) const; - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } + void AddSelector(const AZStd::string_view& selector); + void RemoveSelector(const AZStd::string_view& selector); - 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/StaticLib/GraphCanvas/Styling/StyleManager.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleManager.cpp index 56f6e84332..0b4691ecf5 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleManager.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleManager.cpp @@ -74,10 +74,10 @@ namespace m_paletteSwatches.push_back(QColor(0, 0, 0)); m_sourcePixmap = new QPixmap(16, 16); m_sourcePixmap->fill(Qt::transparent); - + QPainter painter(m_sourcePixmap); painter.setRenderHint(QPainter::RenderHint::Antialiasing); - + QPen pen; pen.setWidth(4); pen.setColor(QColor(0, 0, 0)); @@ -257,7 +257,7 @@ namespace GraphCanvas QBitmap mask = m_sourcePixmap->createMaskFromColor(m_paletteSwatches[i], Qt::MaskOutColor); painter.setClipRegion(QRegion(mask)); - QtDrawingUtils::FillArea(painter, drawRect, (*palettes[i%palettes.size()])); + QtDrawingUtils::FillArea(painter, drawRect, (*palettes[i%palettes.size()])); } return pixmap; @@ -317,7 +317,7 @@ namespace GraphCanvas void StyleManager::LoadStyleSheet() { - AZStd::string file = AZStd::string::format("@assets@/%s", m_assetPath.c_str()); + AZStd::string file = AZStd::string::format("@products@/%s", m_assetPath.c_str()); AZ::IO::FileIOBase* fileBase = AZ::IO::FileIOBase::GetInstance(); @@ -448,7 +448,7 @@ namespace GraphCanvas auto mapIter = m_dataPaletteMapping.find(dataType); if (mapIter == m_dataPaletteMapping.end()) - { + { return "ObjectDataColorPalette"; } else @@ -852,7 +852,7 @@ namespace GraphCanvas return icon; } - + void StyleManager::ClearStyles() { StyleManagerNotificationBus::Event(m_editorId, &StyleManagerNotifications::OnStylesUnloaded); 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/HttpRequestor/Code/Tests/HttpRequestorTest.cpp b/Gems/HttpRequestor/Code/Tests/HttpRequestorTest.cpp index c7540d091f..48826fcf10 100644 --- a/Gems/HttpRequestor/Code/Tests/HttpRequestorTest.cpp +++ b/Gems/HttpRequestor/Code/Tests/HttpRequestorTest.cpp @@ -7,52 +7,50 @@ */ #include +#include #include #include #include #include "HttpRequestManager.h" -class Integ_HttpTest - : public ::testing::Test +class HttpTest + : public UnitTest::ScopedAllocatorSetupFixture { -public: - HttpRequestor::ManagerPtr m_httpRequestManager; +}; + +TEST_F(HttpTest, DISABLED_HttpRequesterTest) +{ + HttpRequestor::Manager httpRequestManager; // to wait for test to complete - AZStd::mutex m_requestMutex; - AZStd::condition_variable m_requestConditionVar; + AZStd::mutex requestMutex; + AZStd::condition_variable requestConditionVar; - AZStd::string resultData; - AZStd::atomic resultCode; + AZStd::string resultData = {}; + AZStd::atomic resultCode = Aws::Http::HttpResponseCode::REQUEST_NOT_MADE; - Integ_HttpTest() { - m_httpRequestManager = AZStd::make_shared(); - resultCode = Aws::Http::HttpResponseCode::REQUEST_NOT_MADE; - resultData = "{}"; - - AZStd::unique_lock lock(m_requestMutex); - m_requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(10)); + AZStd::unique_lock lock(requestMutex); + requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(10)); } - virtual ~Integ_HttpTest() - { - m_httpRequestManager.reset(); - } -}; + httpRequestManager.AddTextRequest( + HttpRequestor::TextParameters("https://httpbin.org/ip", + Aws::Http::HttpMethod::HTTP_GET, + [&resultData, &resultCode, &requestConditionVar](const AZStd::string& data, Aws::Http::HttpResponseCode code) + { + resultData = data; + resultCode = code; + requestConditionVar.notify_all(); + } + ) + ); -TEST_F(Integ_HttpTest, HttpRequesterTest) -{ - m_httpRequestManager->AddTextRequest(HttpRequestor::TextParameters("https://httpbin.org/ip", Aws::Http::HttpMethod::HTTP_GET, [this](const AZStd::string & data, Aws::Http::HttpResponseCode code) { - resultData = data; - resultCode = code; - m_requestConditionVar.notify_all(); - })); - - AZStd::unique_lock lock(m_requestMutex); - m_requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(5000)); + AZStd::unique_lock lock(requestMutex); + requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(5000)); + } EXPECT_NE(Aws::Http::HttpResponseCode::REQUEST_NOT_MADE, resultCode); } 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) + { + m_values.push_front(value); + } + else + { + m_values.push_back(value); + } + + switch (m_scaleMode) { - if (val < m_minScale) + case AutoExpand: + { + if (value < m_minScale) + { + m_minScale = value; + } + else if (value > m_maxScale) + { + m_maxScale = value; + } + break; + } + case AutoScale: { - m_minScale = 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; } - else if (val > m_maxScale) + default: { - m_maxScale = val; + 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/InAppPurchases/Code/Source/Platform/Android/InAppPurchasesAndroid.cpp b/Gems/InAppPurchases/Code/Source/Platform/Android/InAppPurchasesAndroid.cpp index bd13e91dc6..d0d7160a73 100644 --- a/Gems/InAppPurchases/Code/Source/Platform/Android/InAppPurchasesAndroid.cpp +++ b/Gems/InAppPurchases/Code/Source/Platform/Android/InAppPurchasesAndroid.cpp @@ -58,7 +58,7 @@ namespace InAppPurchases } PurchasedProductDetailsAndroid* purchasedProductDetails = new PurchasedProductDetailsAndroid(); - + purchasedProductDetails->SetProductId(AZ::Android::JNI::ConvertJstringToString(static_cast(env->GetObjectField(jpurchasedProduct, fid[0])))); purchasedProductDetails->SetOrderId(AZ::Android::JNI::ConvertJstringToString(static_cast(env->GetObjectField(jpurchasedProduct, fid[1])))); purchasedProductDetails->SetPackageName(AZ::Android::JNI::ConvertJstringToString(static_cast(env->GetObjectField(jpurchasedProduct, fid[2])))); @@ -75,7 +75,7 @@ namespace InAppPurchases int numProducts = env->GetArrayLength(jproductDetails); InAppPurchasesInterface::GetInstance()->GetCache()->ClearCachedProductDetails(); - + const int NUM_FIELDS_PRODUCTS = 7; jfieldID fid[NUM_FIELDS_PRODUCTS]; jclass cls; @@ -224,7 +224,7 @@ namespace InAppPurchases AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; AZ::u64 fileSize = 0; - if (!fileReader->Open("@assets@/product_ids.json", AZ::IO::OpenMode::ModeRead, fileHandle)) + if (!fileReader->Open("@products@/product_ids.json", AZ::IO::OpenMode::ModeRead, fileHandle)) { AZ_TracePrintf("LumberyardInAppBilling", "Unable to open file product_ids.json\n"); return; @@ -319,10 +319,10 @@ namespace InAppPurchases env->DeleteLocalRef(billingClass); } - + void InAppPurchasesAndroid::RestorePurchasedProducts() const { - + } void InAppPurchasesAndroid::ConsumePurchase(const AZStd::string& purchaseToken) const 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/Builders/CopyDependencyBuilder/EmfxWorkspaceBuilderWorker/EmfxWorkspaceBuilderWorker.cpp b/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/EmfxWorkspaceBuilderWorker/EmfxWorkspaceBuilderWorker.cpp index 82c07f9f73..3dd7d5501e 100644 --- a/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/EmfxWorkspaceBuilderWorker/EmfxWorkspaceBuilderWorker.cpp +++ b/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/EmfxWorkspaceBuilderWorker/EmfxWorkspaceBuilderWorker.cpp @@ -63,13 +63,13 @@ namespace CopyDependencyBuilder charBuffer.back() = 0; /* File Contents of EMFX Workspace file looks like - startScript="ImportActor -filename \"@assets@/animationsamples/advanced_rinlocomotion/actor/rinactor.actor\"\nCreateActorInstance + startScript="ImportActor -filename \"@products@/animationsamples/advanced_rinlocomotion/actor/rinactor.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.020660 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000, - 0.00000000,0.00000000,0.99997193\n LoadMotionSet -filename \"@assets@/AnimationSamples/Advanced_RinLocomotion/AnimationEditorFiles/Advanced_RinLocomotion.motionset\" - \nLoadAnimGraph -filename \"@assets@/AnimationSamples/Advanced_RinLocomotion/AnimationEditorFiles/Advanced_RinLocomotion.animgraph\" + 0.00000000,0.00000000,0.99997193\n LoadMotionSet -filename \"@products@/AnimationSamples/Advanced_RinLocomotion/AnimationEditorFiles/Advanced_RinLocomotion.motionset\" + \nLoadAnimGraph -filename \"@products@/AnimationSamples/Advanced_RinLocomotion/AnimationEditorFiles/Advanced_RinLocomotion.animgraph\" \nActivateAnimGraph -actorInstanceID %LASTRESULT3% -animGraphID %LASTRESULT1% -motionSetID %LASTRESULT2% -visualizeScale 1.000000\n" */ - AZStd::regex pathRegex(R"(\s+-filename\s+\\\"(?:@assets@\/)?(\S+)\\\")"); + AZStd::regex pathRegex(R"(\s+-filename\s+\\\"(?:@products@\/)?(\S+)\\\")"); AZStd::smatch matches; AZStd::string::const_iterator searchStart = charBuffer.begin(); while (AZStd::regex_search(searchStart, matches, pathRegex)) diff --git a/Gems/LmbrCentral/Code/Source/Builders/LevelBuilder/LevelBuilderWorker.cpp b/Gems/LmbrCentral/Code/Source/Builders/LevelBuilder/LevelBuilderWorker.cpp index 631bc41358..00e9eaf0fe 100644 --- a/Gems/LmbrCentral/Code/Source/Builders/LevelBuilder/LevelBuilderWorker.cpp +++ b/Gems/LmbrCentral/Code/Source/Builders/LevelBuilder/LevelBuilderWorker.cpp @@ -30,7 +30,7 @@ namespace LevelBuilder { const char s_materialExtension[] = ".mtl"; - const char s_audioControlFilesLevelPath[] = "@devassets@/libs/gameaudio/wwise/levels/%s"; + const char s_audioControlFilesLevelPath[] = "@projectroot@/libs/gameaudio/wwise/levels/%s"; const char s_audioControlFilter[] = "*.xml"; AZ::u64 readXmlDataLength(AZ::IO::GenericStream* stream, int& charSize) diff --git a/Gems/LmbrCentral/Code/Source/Builders/MaterialBuilder/MaterialBuilderComponent.cpp b/Gems/LmbrCentral/Code/Source/Builders/MaterialBuilder/MaterialBuilderComponent.cpp index c06b21337b..48973b2779 100644 --- a/Gems/LmbrCentral/Code/Source/Builders/MaterialBuilder/MaterialBuilderComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Builders/MaterialBuilder/MaterialBuilderComponent.cpp @@ -28,7 +28,7 @@ namespace MaterialBuilder const char g_nodeNameTexture[] = "Texture"; const char g_nodeNameTextures[] = "Textures"; const char g_attributeFileName[] = "File"; - + const int g_numSourceImageFormats = 9; const char* g_sourceImageFormats[g_numSourceImageFormats] = { ".tif", ".tiff", ".bmp", ".gif", ".jpg", ".jpeg", ".tga", ".png", ".dds" }; bool IsSupportedImageExtension(const AZStd::string& extension) @@ -66,7 +66,7 @@ namespace MaterialBuilder return actualFileName; } - // Parses the material XML for all texture paths + // Parses the material XML for all texture paths AZ::Outcome GetTexturePathsFromMaterial(AZ::rapidxml::xml_node* materialNode, AZStd::vector& paths) { AZ::Outcome resultOutcome = AZ::Failure(AZStd::string("")); @@ -94,7 +94,7 @@ namespace MaterialBuilder // do an initial clean-up of the path taken from the file, similar to MaterialHelpers::SetTexturesFromXml AZStd::string texturePath = CleanLegacyPathingFromTexturePath(rawTexturePath); paths.emplace_back(AZStd::move(texturePath)); - } + } textureNode = textureNode->next_sibling(g_nodeNameTexture); } while (textureNode); @@ -112,7 +112,7 @@ namespace MaterialBuilder return AZ::Failure(AZStd::string("SubMaterials node exists but does not have any child Material nodes.")); } - do + do { // grab the texture paths from the submaterial, or error out if necessary AZ::Outcome subMaterialTexturePathsResult = GetTexturePathsFromMaterial(subMaterialNode, paths); @@ -142,7 +142,7 @@ namespace MaterialBuilder } // find a sequence of digits with a string starting from lastDigitIndex, and try to parse that sequence to and int - // and store it in outAnimIndex. + // and store it in outAnimIndex. bool ParseFilePathForCompleteNumber(const AZStd::string& filePath, int& lastDigitIndex, int& outAnimIndex) { int firstAnimIndexDigit = lastDigitIndex; @@ -162,7 +162,7 @@ namespace MaterialBuilder AZ::Outcome GetAllTexturesInTextureSequence(const AZStd::string& path, AZStd::vector& texturesInSequence) { // Taken from CShaderMan::mfReadTexSequence - // All comments next to variable declarations in this function are the original variable names in + // All comments next to variable declarations in this function are the original variable names in // CShaderMan::mfReadTexSequence, to help keep track of how these variables relate to the original function AZStd::string prefix; AZStd::string postfix; @@ -172,7 +172,7 @@ namespace MaterialBuilder AzFramework::StringFunc::Path::GetExtension(filePath.c_str(), extension); AzFramework::StringFunc::Path::StripExtension(filePath); - // unsure if it is actually possible to enter here or the original version with '$' as the indicator + // unsure if it is actually possible to enter here or the original version with '$' as the indicator // for texture sequences, but they check for both just in case, so this will match the behavior. char separator = '#'; // chSep int firstSeparatorIndex = static_cast(filePath.find(separator)); @@ -186,7 +186,7 @@ namespace MaterialBuilder separator = '$'; } - // we don't actually care about getting the speed of the animation, so just remove everything from the + // we don't actually care about getting the speed of the animation, so just remove everything from the // end of the string starting with the last open parenthesis size_t speedStartIndex = filePath.find_last_of('('); if (speedStartIndex != AZStd::string::npos) @@ -195,7 +195,7 @@ namespace MaterialBuilder AzFramework::StringFunc::Append(filePath, '\0'); } - // try to find where the digits start after the separator (there can be any number of separators + // try to find where the digits start after the separator (there can be any number of separators // between the texture name prefix and where the digit range starts) int firstAnimIndexDigit = -1; // m int numSeparators = 0; // j @@ -219,7 +219,7 @@ namespace MaterialBuilder { return AZ::Failure(AZStd::string("Failed to find separator '#' or '$' in texture path.")); } - + // store off everything before the separator prefix = AZStd::move(filePath.substr(0, firstSeparatorIndex)); @@ -242,7 +242,7 @@ namespace MaterialBuilder // reset to the start of the next index ++lastDigitIndex; - + // find the length of the end index, then parse that to an int if (!ParseFilePathForCompleteNumber(filePath, lastDigitIndex, endAnimIndex)) { @@ -262,8 +262,8 @@ namespace MaterialBuilder return AZ::Success(); } - - // Determine which product path to use based on the path stored in the texture, and make it relative to + + // Determine which product path to use based on the path stored in the texture, and make it relative to // the cache. bool ResolveMaterialTexturePath(const AZStd::string& path, AZStd::string& outPath) { @@ -272,7 +272,7 @@ namespace MaterialBuilder //if its a source image format try to load the dds AZStd::string extension; bool hasExtension = AzFramework::StringFunc::Path::GetExtension(path.c_str(), extension); - + // Replace all supported extensions with DDS if it has an extension. If the extension exists but is not supported, fail out. if (hasExtension && IsSupportedImageExtension(extension)) { @@ -286,17 +286,17 @@ namespace MaterialBuilder AZStd::to_lower(aliasedPath.begin(), aliasedPath.end()); AzFramework::StringFunc::Path::Normalize(aliasedPath); - + AZStd::string currentFolderSpecifier = AZStd::string::format(".%c", AZ_CORRECT_FILESYSTEM_SEPARATOR); if (AzFramework::StringFunc::StartsWith(aliasedPath, currentFolderSpecifier)) { AzFramework::StringFunc::Strip(aliasedPath, currentFolderSpecifier.c_str(), false, true); } - + AZStd::string resolvedPath; char fullPathBuffer[AZ_MAX_PATH_LEN] = {}; - // if there is an alias already at the front of the path, resolve it, and try to make it relative to the - // cache (@assets@). If it can't, then error out. + // if there is an alias already at the front of the path, resolve it, and try to make it relative to the + // cache (@products@). If it can't, then error out. // This case handles the possibility of aliases existing in texture paths in materials that is still supported // by the legacy loading code, however it is not currently used, so the else path is always taken. if (aliasedPath[0] == '@') @@ -308,7 +308,7 @@ namespace MaterialBuilder } resolvedPath = fullPathBuffer; AzFramework::StringFunc::Path::Normalize(resolvedPath); - if (!AzFramework::StringFunc::Replace(resolvedPath, AZ::IO::FileIOBase::GetDirectInstance()->GetAlias("@assets@"), "")) + if (!AzFramework::StringFunc::Replace(resolvedPath, AZ::IO::FileIOBase::GetDirectInstance()->GetAlias("@products@"), "")) { AZ_Warning(s_materialBuilder, false, "Failed to resolve aliased texture path %s to be relative to the asset cache. Please make sure this alias resolves to a path within the asset cache.", aliasedPath.c_str()); return false; @@ -318,7 +318,7 @@ namespace MaterialBuilder { resolvedPath = AZStd::move(aliasedPath); } - + // AP deferred path resolution requires UNIX separators and no leading separators, so clean up and convert here if (AzFramework::StringFunc::StartsWith(resolvedPath, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING)) { @@ -357,7 +357,7 @@ namespace MaterialBuilder // (optimization) this builder does not emit source dependencies: builderDescriptor.m_flags |= AssetBuilderSDK::AssetBuilderDesc::BF_EmitsNoDependencies; - + m_materialBuilder.BusConnect(builderDescriptor.m_busId); EBUS_EVENT(AssetBuilderSDK::AssetBuilderBus, RegisterBuilderInformation, builderDescriptor); @@ -548,10 +548,10 @@ namespace MaterialBuilder } } - // for each texture in the file + // for each texture in the file for (const AZStd::string& texPath : texturePaths) { - // if the texture path starts with a '$' then it is a special runtime defined texture, so it it doesn't have + // if the texture path starts with a '$' then it is a special runtime defined texture, so it it doesn't have // an actual asset on disk to depend on. If the texture path doesn't have an extension, then it is a texture // that is determined at runtime (such as 'nearest_cubemap'), so also ignore those, as other things pull in // those dependencies. @@ -567,7 +567,7 @@ namespace MaterialBuilder AZ_Warning(s_materialBuilder, false, "Failed to resolve texture path %s to a product path when gathering dependencies for %s. Registering dependencies on this texture path will be skipped.", texPath.c_str(), path.c_str()); continue; } - + resolvedPaths.emplace_back(AZStd::move(resolvedPath)); } diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp index 20386e0628..c9fc656488 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp @@ -22,7 +22,7 @@ namespace LmbrCentral { - const char bundleRoot[] = "@assets@"; + const char bundleRoot[] = "@products@"; void BundlingSystemComponent::Activate() { @@ -178,7 +178,7 @@ namespace LmbrCentral // Not already opened, new entry m_openedBundles[bundleName] = AZStd::make_unique(); - AZStd::shared_ptr nextCatalog; // Not all bundles will have catalogs - some are legacy. + AZStd::shared_ptr nextCatalog; // Not all bundles will have catalogs - some are legacy. if (nextBundle == nullptr) { // Added to the end 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/LmbrCentral.cpp b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp index 31cf68e9a0..a5884ccce9 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp @@ -484,7 +484,7 @@ namespace LmbrCentral } // load the catalog from disk (supported over VFS). - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, LoadCatalog, AZStd::string::format("@assets@/%s", s_assetCatalogFilename).c_str()); + EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, LoadCatalog, AZStd::string::format("@products@/%s", s_assetCatalogFilename).c_str()); } void LmbrCentralSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system) 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/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp index 65847a7fef..8b697f35f2 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp @@ -69,7 +69,7 @@ namespace UnitTest AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath()); assetRoot /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str()); SerializeContext* serializeContext; ComponentApplicationBus::BroadcastResult(serializeContext, &ComponentApplicationRequests::GetSerializeContext); @@ -114,7 +114,7 @@ namespace UnitTest { AssetBuilderSDK::ProductPathDependencySet resolvedPaths; AZStd::vector productDependencies; - + AssetBuilderSDK::ProcessJobRequest request; request.m_fullPath = GetFullPath(fileName); request.m_sourceFile = fileName; @@ -211,8 +211,6 @@ namespace UnitTest ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::AssetSystem::AssetSystemRequestBus::Handler overrides - const char* GetAbsoluteDevGameFolderPath() override { return ""; } - const char* GetAbsoluteDevRootFolderPath() override { return ""; } bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return true; } bool GenerateRelativeSourcePath( [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, @@ -428,7 +426,7 @@ namespace UnitTest "Fonts/fontexample-bolditalic.font" }; - AZStd::string fileName = "Fonts/FontFamilyExample.fontfamily"; + AZStd::string fileName = "Fonts/FontFamilyExample.fontfamily"; FontBuilderWorker builderWorker; @@ -445,7 +443,7 @@ namespace UnitTest AZStd::string fileName = "Fonts/FontExample.font"; FontBuilderWorker builderWorker; - + TestSuccessCase(&builderWorker, fileName, "Fonts/FontExample.ttf"); } @@ -667,7 +665,7 @@ namespace UnitTest builderWorker.AddSchemaFileDirectory(GetFullPath("Xmls/Schema/WithoutVersionConstraints/OptionalAttribute")); AZStd::vector expectedProductDependencies; - + TestSuccessCase(&builderWorker, fileName, expectedPaths, expectedProductDependencies); } @@ -896,7 +894,7 @@ namespace UnitTest AssetBuilderSDK::CreateJobsResponse response; request.m_sourceFile = "Tests/Xmls/XmlExampleWithoutVersion.xml"; - request.m_watchFolder = "@root@/../Gems/LmbrCentral/Code/"; + request.m_watchFolder = "@engroot@/Gems/LmbrCentral/Code/"; XmlBuilderWorker builderWorker; builderWorker.AddSchemaFileDirectory(GetFullPath("Xmls/Schema/WithoutVersionConstraints/FullFeatured")); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp index 1fd17e25ce..e50fbbe56c 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp @@ -104,7 +104,7 @@ namespace UnitTest m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); AZ::Debug::TraceMessageBus::Handler::BusConnect(); @@ -114,7 +114,7 @@ namespace UnitTest AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath()); assetRoot /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str()); auto* serializeContext = m_app.GetSerializeContext(); @@ -142,7 +142,7 @@ namespace UnitTest AZStd::string GetTestFileAliasedPath(AZStd::string_view fileName) { - constexpr char testFileFolder[] = "@devroot@/Gems/LmbrCentral/Code/Tests/Levels/"; + constexpr char testFileFolder[] = "@engroot@/Gems/LmbrCentral/Code/Tests/Levels/"; return AZStd::string::format("%s%.*s", testFileFolder, aznumeric_cast(fileName.size()), fileName.data()); } diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp index 9e077c113c..a9b36d4624 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp @@ -36,7 +36,7 @@ namespace UnitTest m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); @@ -45,7 +45,7 @@ namespace UnitTest AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath()); assetRoot /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str()); } void TearDown() override diff --git a/Gems/LmbrCentral/Code/Tests/Builders/MaterialBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/MaterialBuilderTests.cpp index 225168ecaa..3786ef7565 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/MaterialBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/MaterialBuilderTests.cpp @@ -35,7 +35,7 @@ namespace UnitTest m_app.reset(aznew AzToolsFramework::ToolsApplication); m_app->Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); AZ::Debug::TraceMessageBus::Handler::BusConnect(); @@ -45,8 +45,7 @@ namespace UnitTest AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath()); assetRoot /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", assetRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str()); } void TearDown() override @@ -128,8 +127,8 @@ namespace UnitTest TEST_F(MaterialBuilderTests, MaterialBuilder_MalformedMaterial_NoChildren_ExpectFailure) { - // Should fail in MaterialBuilderWorker::GetResolvedTexturePathsFromMaterial after calling - // Internal::GetTexturePathsFromMaterial, which should return an AZ::Failure when both a Textures node and a + // Should fail in MaterialBuilderWorker::GetResolvedTexturePathsFromMaterial after calling + // Internal::GetTexturePathsFromMaterial, which should return an AZ::Failure when both a Textures node and a // SubMaterials node are not found. No other AZ_Errors should be generated. TestFailureCase("test_mat2.mtl", 1); } @@ -141,7 +140,7 @@ namespace UnitTest TEST_F(MaterialBuilderTests, MaterialBuilder_MalformedMaterial_EmptySubMaterialNode_ExpectFailure) { - // Should fail in MaterialBuilderWorker::GetResolvedTexturePathsFromMaterial after calling + // Should fail in MaterialBuilderWorker::GetResolvedTexturePathsFromMaterial after calling // Internal::GetTexturePathsFromMaterial, which should return an AZ::Failure when a SubMaterials node is present, // but has no children Material node. No other AZ_Errors should be generated. TestFailureCase("test_mat4.mtl", 1); @@ -154,9 +153,9 @@ namespace UnitTest TEST_F(MaterialBuilderTests, MaterialBuilder_MalformedMaterial_EmptyMaterialInSubMaterial_ExpectFailure) { - // Should fail in MaterialBuilderWorker::GetResolvedTexturePathsFromMaterial after calling + // Should fail in MaterialBuilderWorker::GetResolvedTexturePathsFromMaterial after calling // Internal::GetTexturePathsFromMaterial, which should return an AZ::Failure when a SubMaterials node is present, - // but a child Material node has no child Textures node and no child SubMaterials node. No other AZ_Errors should + // but a child Material node has no child Textures node and no child SubMaterials node. No other AZ_Errors should // be generated. TestFailureCase("test_mat6.mtl", 1); } @@ -235,7 +234,7 @@ namespace UnitTest AZStd::vector expectedPaths = { "engineassets/textures/hex.dds", // resolved from "/engineassets/textures/hex.dds" "engineassets/textures/hex_ddn.dds", // resolved from "./engineassets/textures/hex_ddn.dds" - "engineassets/textures/hex_spec.dds" // resolved from "@assets@/engineassets/textures/hex_spec.dds" + "engineassets/textures/hex_spec.dds" // resolved from "@products@/engineassets/textures/hex_spec.dds" }; TestSuccessCase("test_mat17.mtl", expectedPaths); } diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp index 0d70e34701..f679a3502d 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp @@ -24,16 +24,16 @@ class SeedBuilderTests AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; registry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); - + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); const char* dir = m_app.GetExecutableFolder(); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", dir); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", dir); AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); } @@ -49,7 +49,7 @@ TEST_F(SeedBuilderTests, SeedBuilder_SourceDependency_Valid) { DependencyBuilder::SeedBuilderWorker seedBuilderWorker; AssetBuilderSDK::CreateJobsRequest request; - constexpr char testSeedFolder[] = "@root@/../Gems/LmbrCentral/Code/Tests/Seed"; + constexpr char testSeedFolder[] = "@engroot@/Gems/LmbrCentral/Code/Tests/Seed"; char resolvedPath[AZ_MAX_PATH_LEN]; AZ::IO::FileIOBase::GetInstance()->ResolvePath(testSeedFolder, resolvedPath, AZ_MAX_PATH_LEN); request.m_watchFolder = resolvedPath; @@ -78,7 +78,7 @@ TEST_F(SeedBuilderTests, SeedBuilder_EmptySourceDependency_Valid) { DependencyBuilder::SeedBuilderWorker seedBuilderWorker; AssetBuilderSDK::CreateJobsRequest request; - constexpr char testSeedFolder[] = "@root@/../Gems/LmbrCentral/Code/Tests/Seed"; + constexpr char testSeedFolder[] = "@engroot@/Gems/LmbrCentral/Code/Tests/Seed"; char resolvedPath[AZ_MAX_PATH_LEN]; AZ::IO::FileIOBase::GetInstance()->ResolvePath(testSeedFolder, resolvedPath, AZ_MAX_PATH_LEN); request.m_watchFolder = resolvedPath; diff --git a/Gems/LmbrCentral/Code/Tests/BundlingSystemComponentTests.cpp b/Gems/LmbrCentral/Code/Tests/BundlingSystemComponentTests.cpp index 08251eb716..2d3f67c112 100644 --- a/Gems/LmbrCentral/Code/Tests/BundlingSystemComponentTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/BundlingSystemComponentTests.cpp @@ -26,12 +26,12 @@ namespace UnitTest { - class Integ_BundlingSystemComponentFixture : + class BundlingSystemComponentFixture : public ::testing::Test { public: - Integ_BundlingSystemComponentFixture() = default; + BundlingSystemComponentFixture() = default; bool TestAsset(const char* assetPath) { @@ -52,16 +52,16 @@ namespace UnitTest { return false; } - + AZ::Data::AssetInfo assetInfo; AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, testAssetId); return assetInfo.m_relativePath == assetPath; } }; - TEST_F(Integ_BundlingSystemComponentFixture, HasBundle_LoadBundles_Success) + TEST_F(BundlingSystemComponentFixture, DISABLED_HasBundle_LoadBundles_Success) { - // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our + // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our // cache as test/bundle/staticdata.pak and should be loaded below const char testAssetPath[] = "staticdata/csv/bundlingsystemtestgameproperties.csv"; @@ -72,9 +72,9 @@ namespace UnitTest EXPECT_FALSE(TestAsset(testAssetPath)); } - TEST_F(Integ_BundlingSystemComponentFixture, HasBundle_LoadBundlesCatalogChecks_Success) + TEST_F(BundlingSystemComponentFixture, DISABLED_HasBundle_LoadBundlesCatalogChecks_Success) { - // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our + // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our // cache as test/bundle/staticdata.pak and should be loaded below // The Pak has a catalog describing the contents which should automatically update our central asset catalog const char testAssetPath[] = "staticdata/csv/bundlingsystemtestgameproperties.csv"; @@ -92,36 +92,36 @@ namespace UnitTest EXPECT_FALSE(TestAsset(noCatalogAsset)); } - TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SingleUnloadCheckCatalog_Success) + TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SingleUnloadCheckCatalog_Success) { - // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our + // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our // cache as test/bundle/staticdata.pak and should be loaded below // The Pak has a catalog describing the contents which should automatically update our central asset catalog const char testCSVAsset[] = "staticdata/csv/bundlingsystemtestgameproperties.csv"; const char testCSVAssetPak[] = "test/bundle/staticdata.pak"; - // This asset lives only within LmbrCentral/Assets/Test/Bundle/ping.pak + // This asset lives only within LmbrCentral/Assets/Test/Bundle/ping.pak const char testDDSAsset[] = "textures/test/ping.dds"; const char testDDSAssetPak[] = "test/bundle/ping.pak"; EXPECT_FALSE(TestAssetId(testCSVAsset)); EXPECT_FALSE(TestAssetId(testDDSAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); EXPECT_FALSE(TestAssetId(testCSVAsset)); EXPECT_TRUE(TestAssetId(testDDSAsset)); EXPECT_TRUE(gEnv->pCryPak->ClosePack(testDDSAssetPak)); EXPECT_FALSE(TestAssetId(testCSVAsset)); EXPECT_FALSE(TestAssetId(testDDSAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testCSVAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testCSVAssetPak)); EXPECT_TRUE(TestAssetId(testCSVAsset)); EXPECT_FALSE(TestAssetId(testDDSAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); EXPECT_TRUE(TestAssetId(testCSVAsset)); EXPECT_TRUE(TestAssetId(testDDSAsset)); EXPECT_TRUE(gEnv->pCryPak->ClosePack(testDDSAssetPak)); EXPECT_TRUE(TestAssetId(testCSVAsset)); EXPECT_FALSE(TestAssetId(testDDSAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); EXPECT_TRUE(TestAssetId(testCSVAsset)); EXPECT_TRUE(TestAssetId(testDDSAsset)); EXPECT_TRUE(gEnv->pCryPak->ClosePack(testCSVAssetPak)); @@ -132,9 +132,9 @@ namespace UnitTest EXPECT_FALSE(TestAssetId(testDDSAsset)); } - TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SingleLoadAndBundleMode_Success) + TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SingleLoadAndBundleMode_Success) { - // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our + // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our // cache as test/bundle/staticdata.pak and should be loaded below // The Pak has a catalog describing the contents which should automatically update our central asset catalog const char testCSVAsset[] = "staticdata/csv/bundlingsystemtestgameproperties.csv"; @@ -143,7 +143,7 @@ namespace UnitTest const char testMTLAssetPak[] = "test/TestMaterials.pak"; EXPECT_FALSE(TestAssetId(testCSVAsset)); EXPECT_FALSE(TestAssetId(testMTLAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testMTLAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testMTLAssetPak)); EXPECT_FALSE(TestAssetId(testCSVAsset)); EXPECT_TRUE(TestAssetId(testMTLAsset)); LmbrCentral::BundlingSystemRequestBus::Broadcast(&LmbrCentral::BundlingSystemRequestBus::Events::LoadBundles, "test/bundle", ".pak"); @@ -157,37 +157,37 @@ namespace UnitTest EXPECT_FALSE(TestAssetId(testMTLAsset)); } - TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_OpenClosePackCount_Match) + TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_OpenClosePackCount_Match) { - // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our + // This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our // cache as test/bundle/staticdata.pak and should be loaded below // The Pak has a catalog describing the contents which should automatically update our central asset catalog const char testCSVAsset[] = "staticdata/csv/bundlingsystemtestgameproperties.csv"; const char testCSVAssetPak[] = "test/bundle/staticdata.pak"; - // This asset lives only within LmbrCentral/Assets/Test/Bundle/ping.pak + // This asset lives only within LmbrCentral/Assets/Test/Bundle/ping.pak const char testDDSAssetPak[] = "test/bundle/ping.pak"; size_t bundleCount{ 0 }; LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount,&LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 0); EXPECT_FALSE(TestAssetId(testCSVAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 1); EXPECT_TRUE(gEnv->pCryPak->ClosePack(testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 0); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testCSVAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testCSVAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 1); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 2); EXPECT_TRUE(gEnv->pCryPak->ClosePack(testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 1); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 2); EXPECT_TRUE(gEnv->pCryPak->ClosePack(testCSVAssetPak)); @@ -198,7 +198,7 @@ namespace UnitTest EXPECT_EQ(bundleCount, 0); } - TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SplitPakTestWithAsset_Success) + TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SplitPakTestWithAsset_Success) { // This asset lives only within LmbrCentral/Assets/Test/SplitBundleTest/splitbundle__1.pak which is a dependent bundle of splitbundle.pak const char testDDSAsset_split[] = "textures/milestone2/am_floor_tile_ddna_test.dds.7"; @@ -208,7 +208,7 @@ namespace UnitTest LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 0); EXPECT_FALSE(TestAssetId(testDDSAsset_split)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 2); EXPECT_TRUE(TestAssetId(testDDSAsset_split)); @@ -217,7 +217,7 @@ namespace UnitTest EXPECT_EQ(bundleCount, 0); EXPECT_FALSE(TestAssetId(testDDSAsset_split)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testDDSAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testDDSAssetPak)); LmbrCentral::BundlingSystemRequestBus::BroadcastResult(bundleCount, &LmbrCentral::BundlingSystemRequestBus::Events::GetOpenedBundleCount); EXPECT_EQ(bundleCount, 2); EXPECT_TRUE(TestAssetId(testDDSAsset_split)); @@ -228,7 +228,7 @@ namespace UnitTest } // Verify that our bundles using catalogs of the same name work properly - TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SharedCatalogName_Success) + TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SharedCatalogName_Success) { // This bundle was built for PC but is generic and the test should work fine on other platforms // gamepropertioessmall_pc.pak has a smaller version of the gameproperties csv @@ -240,7 +240,7 @@ namespace UnitTest EXPECT_FALSE(TestAssetId(testGamePropertiesAsset)); EXPECT_FALSE(TestAssetId(testUserRequestAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testGamePropertiesAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testGamePropertiesAssetPak)); EXPECT_TRUE(TestAssetId(testGamePropertiesAsset)); EXPECT_FALSE(TestAssetId(testUserRequestAsset)); AZ::Data::AssetId testAssetId; @@ -253,7 +253,7 @@ namespace UnitTest EXPECT_NE(assetInfo.m_sizeBytes, 0); AZ::u64 assetSize1 = assetInfo.m_sizeBytes; - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testUserRequestAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testUserRequestAssetPak)); EXPECT_TRUE(TestAssetId(testGamePropertiesAsset)); EXPECT_TRUE(TestAssetId(testUserRequestAsset)); @@ -271,13 +271,13 @@ namespace UnitTest EXPECT_FALSE(TestAssetId(testGamePropertiesAsset)); EXPECT_FALSE(TestAssetId(testUserRequestAsset)); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testUserRequestAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testUserRequestAssetPak)); AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, testAssetId); AZ::u64 assetSize3 = assetInfo.m_sizeBytes; EXPECT_EQ(assetSize3, assetSize2); - EXPECT_TRUE(gEnv->pCryPak->OpenPack("@assets@", testGamePropertiesAssetPak)); + EXPECT_TRUE(gEnv->pCryPak->OpenPack("@products@", testGamePropertiesAssetPak)); AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, testAssetId); AZ::u64 assetSize4 = assetInfo.m_sizeBytes; EXPECT_EQ(assetSize4, assetSize1); diff --git a/Gems/LmbrCentral/Code/Tests/EmfxWorkSpace/productdependencies.emfxworkspace b/Gems/LmbrCentral/Code/Tests/EmfxWorkSpace/productdependencies.emfxworkspace index 6c7f7d3d87..0e92f9e83c 100644 --- a/Gems/LmbrCentral/Code/Tests/EmfxWorkSpace/productdependencies.emfxworkspace +++ b/Gems/LmbrCentral/Code/Tests/EmfxWorkSpace/productdependencies.emfxworkspace @@ -1,3 +1,3 @@ [General] version=1 -startScript="ImportActor -filename \"@assets@/foo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00015891\nLoadMotionSet -filename \"@assets@/foo.motionset\"\nLoadAnimGraph -filename \"@assets@/foo.animgraph\"\nActivateAnimGraph -actorInstanceID %LASTRESULT3% -animGraphID %LASTRESULT1% -motionSetID %LASTRESULT2% -visualizeScale 1.000000\n" +startScript="ImportActor -filename \"@products@/foo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00015891\nLoadMotionSet -filename \"@products@/foo.motionset\"\nLoadAnimGraph -filename \"@products@/foo.animgraph\"\nActivateAnimGraph -actorInstanceID %LASTRESULT3% -animGraphID %LASTRESULT1% -motionSetID %LASTRESULT2% -visualizeScale 1.000000\n" diff --git a/Gems/LmbrCentral/Code/Tests/Materials/test_mat17.mtl b/Gems/LmbrCentral/Code/Tests/Materials/test_mat17.mtl index 099e006f4c..c3d0ddcf61 100644 --- a/Gems/LmbrCentral/Code/Tests/Materials/test_mat17.mtl +++ b/Gems/LmbrCentral/Code/Tests/Materials/test_mat17.mtl @@ -2,7 +2,7 @@ - +
diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index 36de4c4ed3..9fca421538 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -15,6 +15,6 @@ ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/o3de-core/", "dependencies": [] } diff --git a/Gems/LyShine/Code/Editor/EditorMenu.cpp b/Gems/LyShine/Code/Editor/EditorMenu.cpp index 8a6c93de6b..0eeec9a06e 100644 --- a/Gems/LyShine/Code/Editor/EditorMenu.cpp +++ b/Gems/LyShine/Code/Editor/EditorMenu.cpp @@ -606,7 +606,7 @@ void EditorWindow::AddMenu_View() [this]([[maybe_unused]] bool checked) { // Clear guides - AZStd::string canvasUndoXml = CanvasHelpers::BeginUndoableCanvasChange(GetCanvas()); + AZStd::string canvasUndoXml = CanvasHelpers::BeginUndoableCanvasChange(GetCanvas()); EBUS_EVENT_ID(GetCanvas(), UiEditorCanvasBus, RemoveAllGuides); CanvasHelpers::EndUndoableCanvasChange(this, "clear guides", canvasUndoXml); }); @@ -724,7 +724,7 @@ void EditorWindow::AddMenu_View_LanguageSetting(QMenu* viewMenu) // Iterate through the subdirectories of the localization folder. Each // directory corresponds to a different language containing localization // translations for that language. - AZStd::string fullLocPath(AZStd::string(gEnv->pFileIO->GetAlias("@assets@")) + "/" + AZStd::string(m_startupLocFolderName.toUtf8().constData())); + AZStd::string fullLocPath(AZStd::string(gEnv->pFileIO->GetAlias("@products@")) + "/" + AZStd::string(m_startupLocFolderName.toUtf8().constData())); QDir locDir(fullLocPath.c_str()); locDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); locDir.setSorting(QDir::Name); @@ -733,7 +733,7 @@ void EditorWindow::AddMenu_View_LanguageSetting(QMenu* viewMenu) { QString directoryName(subDirectory.fileName().toLower()); - // The loc system expects XML assets stored in a language-specific + // The loc system expects XML assets stored in a language-specific // folder with an "_xml" suffix in the name. Truncate the displayed // name so the user just sees the language name (this isn't required // though). @@ -754,7 +754,7 @@ void EditorWindow::AddMenu_View_LanguageSetting(QMenu* viewMenu) { // First try to locate the directory by name, without the "_xml" // suffix (in case it actually exists by this name). - QString fullLocPath(QString(gEnv->pFileIO->GetAlias("@assets@")) + "/" + m_startupLocFolderName + "/" + directoryName); + QString fullLocPath(QString(gEnv->pFileIO->GetAlias("@products@")) + "/" + m_startupLocFolderName + "/" + directoryName); QDir locDir(fullLocPath); // Try the directory with the expected suffix @@ -780,7 +780,7 @@ void EditorWindow::AddMenu_View_LanguageSetting(QMenu* viewMenu) "This used to be set to CSystem::OnLocalizationFolderCVarChanged but is now missing. " "UI Editor language-switching features are no longer working."); } - + // Update the language setting; this will allow font families to // load language-specific font assets ICVar* languageCvar = gEnv->pConsole->GetCVar("g_language"); diff --git a/Gems/LyShine/Code/Editor/UiSliceManager.cpp b/Gems/LyShine/Code/Editor/UiSliceManager.cpp index 97e9a51e11..a8fdad5343 100644 --- a/Gems/LyShine/Code/Editor/UiSliceManager.cpp +++ b/Gems/LyShine/Code/Editor/UiSliceManager.cpp @@ -138,7 +138,7 @@ void UiSliceManager::MakeSliceFromEntities(AzToolsFramework::EntityIdList& entit // expand the list of entities to include all child entities AzToolsFramework::EntityIdSet entitiesAndDescendants = GatherEntitiesAndAllDescendents(entities); - const AZStd::string slicesAssetsPath = "@devassets@/UI/Slices"; + const AZStd::string slicesAssetsPath = "@projectroot@/UI/Slices"; if (!gEnv->pFileIO->Exists(slicesAssetsPath.c_str())) { @@ -991,13 +991,13 @@ AZStd::string UiSliceManager::MakeTemporaryFilePathForSave(const char* targetFil AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); AZ_Assert(fileIO, "File IO is not initialized."); - AZStd::string devAssetPath = fileIO->GetAlias("@devassets@"); + AZStd::string devAssetPath = fileIO->GetAlias("@projectroot@"); AZStd::string userPath = fileIO->GetAlias("@user@"); AZStd::string tempPath = targetFilename; EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, devAssetPath); EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, userPath); EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, tempPath); - AzFramework::StringFunc::Replace(tempPath, "@devassets@", devAssetPath.c_str()); + AzFramework::StringFunc::Replace(tempPath, "@projectroot@", devAssetPath.c_str()); AzFramework::StringFunc::Replace(tempPath, devAssetPath.c_str(), userPath.c_str()); tempPath.append(".slicetemp"); 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 ////////////////////////////////////////////////////////////////////////// +namespace +{ + using UiAnimParamSystemString = AZStd::basic_string, AZStd::stateless_allocator>; + + 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.find(eUiAnimNodeType_ ## name) == g_animNodeEnumToStringMap.end()); \ +#define REGISTER_NODE_TYPE(name) assert(!g_animNodeEnumToStringMap.contains(eUiAnimNodeType_ ## name)); \ g_animNodeEnumToStringMap[eUiAnimNodeType_ ## name] = AZ_STRINGIZE(name); \ - g_animNodeStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; + g_animNodeStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.find(eUiAnimParamType_ ## name) == g_animParamEnumToStringMap.end()); \ +#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/LyShine/Code/Source/UiCanvasManager.cpp b/Gems/LyShine/Code/Source/UiCanvasManager.cpp index a71c46d45e..f2397248b6 100644 --- a/Gems/LyShine/Code/Source/UiCanvasManager.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasManager.cpp @@ -67,7 +67,7 @@ namespace // Check if extension needs to be fixed up const AZStd::string canvasExtension("uicanvas"); - bool validExtension = AzFramework::StringFunc::Path::IsExtension(assetPath.c_str(), canvasExtension.c_str(), true); + bool validExtension = AzFramework::StringFunc::Path::IsExtension(assetPath.c_str(), canvasExtension.c_str(), true); if (!validExtension) { // Fix extension @@ -79,7 +79,7 @@ namespace // Normalize path EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePathKeepCase, assetPath); - // Check for any leading slashes as the specified path should be a relative path to the @assets@ alias. + // Check for any leading slashes as the specified path should be a relative path to the @products@ alias. // This eliminates inconsistencies between lower level file opens on different platforms int numCharsToErase = 0; for (; numCharsToErase < assetPath.length(); ++numCharsToErase) @@ -581,7 +581,7 @@ void UiCanvasManager::UpdateLoadedCanvases(float deltaTime) // Update all the canvases loaded in game. // It is unlikely this will call out to client code that could remove a canvas but we have no - // control over what custom components do so we increment the count that will defer all canvas deletion + // control over what custom components do so we increment the count that will defer all canvas deletion m_recursionGuardCount++; if (m_generateMousePositionInputEvent) { @@ -1087,7 +1087,7 @@ void UiCanvasManager::DebugDisplayCanvasData(int setting) const } const char* enabledString = isCanvasEnabled ? "Y" : "N"; totalEnabled += isCanvasEnabled ? 1 : 0; - + bool posEnabled = canvas->GetIsPositionalInputSupported(); const char* posEnabledString = posEnabled ? "Y" : "N"; totalPositionalInputs += posEnabled ? 1 : 0; @@ -1178,7 +1178,7 @@ void UiCanvasManager::DebugDisplayDrawCallData() const const AZ::Vector3 blue(0.3f,0.3f,1); const AZ::Vector3 green(0.3f,1,0.3f); const AZ::Vector3 yellow(0.7f,0.7f,0.2f); - + // local function to write a line of text (with a background rect) and increment Y offset AZStd::function WriteLine = [&](const char* buffer, const AZ::Vector3& color) { @@ -1191,7 +1191,7 @@ void UiCanvasManager::DebugDisplayDrawCallData() const draw2d->DrawText(buffer, AZ::Vector2(xOffset, yOffset), 16, textOpacity, &textOptions); yOffset += lineSpacing; }; - + char buffer[200]; sprintf_s(buffer, "NN: %20s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s", diff --git a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp index ff4e054ac7..60074ac0a0 100644 --- a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp +++ b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp @@ -60,7 +60,7 @@ AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); using namespace AZ; using ::testing::NiceMock; -class LyShineSystemTestComponent +class LyShineSystemTestComponent : public LyShine::LyShineSystemComponent { friend class LyShineEditorTest; @@ -90,18 +90,18 @@ protected: m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); const AZStd::string engineRoot = AZ::Test::GetEngineRootPath(); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engineRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engineRoot.c_str()); AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath()); assetRoot /= "Cache"; - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str()); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str()); AZ::SerializeContext* context = nullptr; ComponentApplicationBus::BroadcastResult(context, &ComponentApplicationBus::Events::GetSerializeContext); 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 ////////////////////////////////////////////////////////////////////////// +namespace +{ + using AnimParamSystemString = AZStd::basic_string, AZStd::stateless_allocator>; + + 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.find(AnimNodeType::name) == g_animNodeEnumToStringMap.end()); \ - g_animNodeEnumToStringMap[AnimNodeType::name] = AZ_STRINGIZE(name); \ - g_animNodeStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = AnimNodeType::name; +#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.find(AnimParamType::name) == g_animParamEnumToStringMap.end()); \ - g_animParamEnumToStringMap[AnimParamType::name] = AZ_STRINGIZE(name); \ - g_animParamStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = AnimParamType::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/MessagePopup/Code/Source/LyShineMessagePopup.cpp b/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp index 104118e4bf..40fed3448b 100644 --- a/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp +++ b/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp @@ -127,21 +127,21 @@ namespace MessagePopup switch (_buttons) { case EPopupButtons::EPopupButtons_NoButtons: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@assets@/ui/canvases/defaultmessagepopup.uicanvas"); + canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup.uicanvas"); break; case EPopupButtons::EPopupButtons_Confirm: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@assets@/ui/canvases/defaultmessagepopup_confirm.uicanvas"); + canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_confirm.uicanvas"); isNavigationSupported = true; break; case EPopupButtons::EPopupButtons_YesNo: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@assets@/ui/canvases/defaultmessagepopup_yesno.uicanvas"); + canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_yesno.uicanvas"); isNavigationSupported = true; break; } } else if (_kind == EPopupKind_Toaster) { - canvasEntityId = gEnv->pLyShine->LoadCanvas("@assets@/ui/canvases/toaster.uicanvas"); + canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/toaster.uicanvas"); } if (canvasEntityId.IsValid()) 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/Editor/DebugDraw.cpp b/Gems/PhysX/Code/Editor/DebugDraw.cpp index 440b198675..5936312ecf 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.cpp +++ b/Gems/PhysX/Code/Editor/DebugDraw.cpp @@ -148,16 +148,16 @@ namespace PhysX using VisibilityFunc = bool(*)(); editContext->Class( - "PhysX Collider Debug Draw", "Manages global and per-collider debug draw settings and logic") + "PhysX Collider Debug Draw", "Global and per-collider debug draw preferences.") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Collider::m_locallyEnabled, "Draw collider", - "Shows the geometry for the collider in the viewport") + "Display collider geometry in the viewport.") ->Attribute(AZ::Edit::Attributes::CheckboxTooltip, "If set, the geometry of this collider is visible in the viewport. 'Draw Helpers' needs to be enabled to use.") ->Attribute(AZ::Edit::Attributes::Visibility, VisibilityFunc{ []() { return IsGlobalColliderDebugCheck(GlobalCollisionDebugState::Manual); } }) ->Attribute(AZ::Edit::Attributes::ReadOnly, &IsDrawColliderReadOnly) ->DataElement(AZ::Edit::UIHandlers::Button, &Collider::m_globalButtonState, "Draw collider", - "Shows the geometry for the collider in the viewport") + "Display collider geometry in the viewport.") ->Attribute(AZ::Edit::Attributes::ButtonText, "Global override") ->Attribute(AZ::Edit::Attributes::ButtonTooltip, "A global setting is overriding this property (to disable the override, " diff --git a/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp b/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp index b9ed827ce0..02e684de63 100644 --- a/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp +++ b/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp @@ -51,23 +51,27 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Editor Joint Limit Config Base", "Base joint limit parameters") + "Editor Joint Limit Config Base", "Base joint limit parameters.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isLimited, "Limit", "True if the motion about the unconstrained axes of this joint are limited") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isLimited, "Limit", + "When active, the joint's degrees of freedom are limited.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointLimitConfig::IsInComponentMode) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isSoftLimit, "Soft limit", "True if the joint is allowed to rotate beyond limits and spring back") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isSoftLimit, "Soft limit", + "When active, motion beyond the joint limit with a spring-like return is allowed.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::m_isLimited) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointLimitConfig::IsInComponentMode) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_damping, "Damping", "The damping strength of the drive, the force proportional to the velocity error") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_damping, "Damping", + "Dissipation of energy and reduction in spring oscillations when outside the joint limit.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::IsSoftLimited) ->Attribute(AZ::Edit::Attributes::Max, s_springMax) ->Attribute(AZ::Edit::Attributes::Min, s_springMin) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_stiffness, "Stiffness", "The spring strength of the drive, the force proportional to the position error") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_stiffness, "Stiffness", + "The spring's drive relative to the position of the follower when outside the joint limit.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::IsSoftLimited) ->Attribute(AZ::Edit::Attributes::Max, s_springMax) ->Attribute(AZ::Edit::Attributes::Min, s_springMin) @@ -115,18 +119,20 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Angular Limit", "Rotation limitation") + "Angular Limit", "Rotation limitation.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_standardLimitConfig , "Standard limit configuration" - , "Common limit parameters to all joint types") - ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitPositive, "Positive angular limit", "Positive rotation angle") + , "Common limit parameters to all joint types.") + ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitPositive, "Positive angular limit", + "Positive rotation angle.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitPairConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMax) ->Attribute(AZ::Edit::Attributes::Min, s_angleMin) - ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitNegative, "Negative angular limit", "Negative rotation angle") + ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitNegative, "Negative angular limit", + "Negative rotation angle.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitPairConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMin) ->Attribute(AZ::Edit::Attributes::Min, -s_angleMax) @@ -164,18 +170,20 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Angular Limit", "Rotation limitation") + "Angular Limit", "Rotation limitation.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_standardLimitConfig , "Standard limit configuration" - , "Common limit parameters to all joint types") - ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitY, "Y axis angular limit", "Limit for swing angle about Y axis") + , "Common limit parameters to all joint types.") + ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitY, "Y axis angular limit", + "Limit for swing angle about Y axis.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConeConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMax) ->Attribute(AZ::Edit::Attributes::Min, s_angleMin) - ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitZ, "Z axis angular limit", "Limit for swing angle about Z axis") + ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitZ, "Z axis angular limit", + "Limit for swing angle about Z axis.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConeConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMax) ->Attribute(AZ::Edit::Attributes::Min, s_angleMin) @@ -226,33 +234,33 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &PhysX::EditorJointConfig::m_localPosition, "Local Position" - , "Local Position of joint, relative to its entity") + , "Local Position of joint, relative to its entity.") ->DataElement(0, &PhysX::EditorJointConfig::m_localRotation, "Local Rotation" - , "Local Rotation of joint, relative to its entity") + , "Local Rotation of joint, relative to its entity.") ->Attribute(AZ::Edit::Attributes::Min, LocalRotationMin) ->Attribute(AZ::Edit::Attributes::Max, LocalRotationMax) ->DataElement(0, &PhysX::EditorJointConfig::m_leadEntity, "Lead Entity" - , "Parent entity associated with joint") + , "Parent entity associated with joint.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorJointConfig::ValidateLeadEntityId) ->DataElement(0, &PhysX::EditorJointConfig::m_selfCollide, "Lead-Follower Collide" - , "Lead and follower pair will collide with each other") + , "When active, the lead and follower pair will collide with each other.") ->DataElement(0, &PhysX::EditorJointConfig::m_displayJointSetup, "Display Setup in Viewport" - , "Display joint setup in the viewport") + , "Display joint setup in the viewport.") ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointConfig::IsInComponentMode) ->DataElement(0, &PhysX::EditorJointConfig::m_selectLeadOnSnap, "Select Lead on Snap" - , "Select lead entity on snap to position in component mode") + , "Select lead entity on snap to position in component mode.") ->DataElement(0, &PhysX::EditorJointConfig::m_breakable , "Breakable" - , "Joint is breakable when force or torque exceeds limit") + , "Joint is breakable when force or torque exceeds limit.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointConfig::IsInComponentMode) ->DataElement(0, &PhysX::EditorJointConfig::m_forceMax, - "Maximum Force", "Amount of force joint can withstand before breakage") + "Maximum Force", "Amount of force joint can withstand before breakage.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointConfig::m_breakable) ->Attribute(AZ::Edit::Attributes::Max, s_breakageMax) ->Attribute(AZ::Edit::Attributes::Min, s_breakageMin) ->DataElement(0, &PhysX::EditorJointConfig::m_torqueMax, - "Maximum Torque", "Amount of torque joint can withstand before breakage") + "Maximum Torque", "Amount of torque joint can withstand before breakage.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointConfig::m_breakable) ->Attribute(AZ::Edit::Attributes::Max, s_breakageMax) ->Attribute(AZ::Edit::Attributes::Min, s_breakageMin) diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp index f65768cec9..a5b6430591 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp @@ -8,7 +8,9 @@ #include "EditorSystemComponent.h" #include +#include #include +#include #include #include #include @@ -23,7 +25,7 @@ namespace PhysX { - constexpr const char* DefaultAssetFilePath = "Physics/SurfaceTypeMaterialLibrary"; + constexpr const char* DefaultAssetFilePath = "Assets/Physics/SurfaceTypeMaterialLibrary"; constexpr const char* TemplateAssetFilename = "PhysX/TemplateMaterialLibrary"; static AZStd::optional> GetMaterialLibraryTemplate() @@ -67,7 +69,7 @@ namespace PhysX assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, relativePath.c_str(), assetType, true /*autoRegisterIfNotFound*/); AZ::Data::Asset newAsset = - AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default); + AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default); if (auto* newMaterialLibraryData = azrtti_cast(newAsset.GetData())) { @@ -138,6 +140,14 @@ namespace PhysX if (auto retrievedMaterialLibrary = RetrieveDefaultMaterialLibrary()) { physxSystem->UpdateMaterialLibrary(retrievedMaterialLibrary.value()); + + // After setting the default material library, save the physx configuration. + auto saveCallback = []([[maybe_unused]] const PhysXSystemConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the PhysX configuration after setting default material library."); + }; + physxSystem->GetSettingsRegistryManager().SaveSystemConfiguration(physxSystem->GetPhysXConfiguration(), saveCallback); } } } @@ -236,11 +246,15 @@ namespace PhysX if (!resultAssetId.IsValid()) { // No file for the default material library, create it - const char* assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectsourceassets@"); - AZStd::string fullPath; - AzFramework::StringFunc::Path::ConstructFull(assetRoot, DefaultAssetFilePath, assetExtension.c_str(), fullPath); + AZ::IO::Path fullPath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(fullPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); + } + fullPath /= DefaultAssetFilePath; + fullPath.ReplaceExtension(AZ::IO::PathView(assetExtension)); - if (auto materialLibraryOpt = CreateMaterialLibrary(fullPath, relativePath)) + if (auto materialLibraryOpt = CreateMaterialLibrary(fullPath.Native(), relativePath)) { return materialLibraryOpt; } diff --git a/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp b/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp index 7a441a1c55..02f0bc038a 100644 --- a/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp @@ -54,17 +54,17 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("Wind Configuration", "Wind settings for PhysX") + editContext->Class("Wind Configuration", "Wind force entity tags.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &WindConfiguration::m_globalWindTag, "Global wind tag", - "Tag value that will be used to mark entities that provide global wind value.\n" - "Global wind has no bounds and affects objects across entire level.") + "Global wind provider tags.\n" + "Global winds apply to entire world.") ->DataElement(AZ::Edit::UIHandlers::Default, &WindConfiguration::m_localWindTag, "Local wind tag", - "Tag value that will be used to mark entities that provide local wind value.\n" - "Local wind is only applied within bounds defined by PhysX collider.") + "Local wind provider tags.\n" + "Local winds are constrained to a PhysX collider's boundaries.") ; } } diff --git a/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp b/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp index e14ce62537..c199159a43 100644 --- a/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp @@ -31,38 +31,39 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("PhysX PVD Settings", "PhysX PVD Settings") + editContext->Class("PhysX PVD Settings", + "Connection configuration settings for the PhysX Visual Debugger (PVD). Requires PhysX Debug Gem.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &PvdConfiguration::m_transportType, - "PVD Transport Type", "PVD supports writing to a TCP/IP network socket or to a file.") + "PVD Transport Type", "Output PhysX Visual Debugger data to a TCP/IP network socket or to a file.") ->EnumAttribute(Debug::PvdTransportType::Network, "Network") ->EnumAttribute(Debug::PvdTransportType::File, "File") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_host, - "PVD Host", "Host IP address of the PhysX Visual Debugger application") + "PVD Host", "Host IP address of the PhysX Visual Debugger server.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_port, - "PVD Port", "Port of the PhysX Visual Debugger application") + "PVD Port", "Port of the PhysX Visual Debugger server.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug) + ->Attribute(AZ::Edit::Attributes::Min, AZStd::numeric_limits::min()) + ->Attribute(AZ::Edit::Attributes::Max, AZStd::numeric_limits::max()) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_timeoutInMilliseconds, - "PVD Timeout", "Timeout (in milliseconds) used when connecting to the PhysX Visual Debugger application") + "PVD Timeout", "Timeout (in milliseconds) when connecting to the PhysX Visual Debugger server.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_fileName, - "PVD FileName", "Filename to output PhysX Visual Debugger data.") + "PVD FileName", "Output filename for PhysX Visual Debugger data.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsFileDebug) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &PvdConfiguration::m_autoConnectMode, - "PVD Auto Connect", "Automatically connect to the PhysX Visual Debugger " - "(Requires PhysX Debug gem for Editor and Game modes).") + "PVD Auto Connect", "Automatically connect to the PhysX Visual Debugger.") ->EnumAttribute(Debug::PvdAutoConnectMode::Disabled, "Disabled") ->EnumAttribute(Debug::PvdAutoConnectMode::Editor, "Editor") ->EnumAttribute(Debug::PvdAutoConnectMode::Game, "Game") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &PvdConfiguration::m_reconnect, - "PVD Reconnect", "Reconnect (Disconnect and Connect) when switching between game and edit mode " - "(Requires PhysX Debug gem).") + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &PvdConfiguration::m_reconnect, "PVD Reconnect", + "Reconnect (disconnect and connect) to the PhysX Visual Debugger server when switching between game and edit mode.") ; } } @@ -131,7 +132,7 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("Editor Configuration", "Editor settings for PhysX") + editContext->Class("Editor Configuration", "Editor settings for PhysX.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Slider, &DebugDisplayData::m_centerOfMassDebugSize, diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp index 92952bcdaa..fa71fa0061 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp @@ -33,14 +33,14 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Ball Joint", "The ball joint supports a cone limiting the maximum rotation around the y and z axes.") + "PhysX Ball Joint", "A dynamic joint constraint with swing rotation limits around the Y and Z axes of the joint.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ball-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "Limitations for the swing (Y and Z axis) about joint") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode") + ->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "The rotation angle limit around the joint's Y and Z axes.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 7c07ca207c..262e40cb99 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 @@ -52,13 +53,15 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { - editContext->Class("EditorProxyShapeConfig", "PhysX Base shape collider") + editContext->Class("EditorProxyShapeConfig", "PhysX Base collider.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh", "PhysX mesh collider asset") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh", + "Specifies the PhysX mesh collider asset for this PhysX collider component.") ->Attribute(AZ_CRC_CE("EditButton"), "") ->Attribute(AZ_CRC_CE("EditDescription"), "Open in Scene Settings") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration", "Configuration of asset shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration", + "PhysX mesh asset collider configuration.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly); } } @@ -85,7 +88,7 @@ namespace PhysX { editContext->Class( "EditorProxyShapeConfig", "PhysX Base shape collider") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorProxyShapeConfig::m_shapeType, "Shape", "The shape of the collider") + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorProxyShapeConfig::m_shapeType, "Shape", "The shape of the collider.") ->EnumAttribute(Physics::ShapeType::Sphere, "Sphere") ->EnumAttribute(Physics::ShapeType::Box, "Box") ->EnumAttribute(Physics::ShapeType::Capsule, "Capsule") @@ -95,20 +98,20 @@ namespace PhysX // potentially be different ComponentModes for different shape types) ->Attribute(AZ::Edit::Attributes::ReadOnly, &AzToolsFramework::ComponentModeFramework::InComponentMode) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_sphere, "Sphere", "Configuration of sphere shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_sphere, "Sphere", "Configuration of sphere shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsSphereConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_box, "Box", "Configuration of box shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_box, "Box", "Configuration of box shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsBoxConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_capsule, "Capsule", "Configuration of capsule shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_capsule, "Capsule", "Configuration of capsule shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsCapsuleConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_physicsAsset, "Asset", "Configuration of asset shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_physicsAsset, "Asset", "Configuration of asset shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsAssetConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_subdivisionLevel, "Subdivision level", - "The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling") + "The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.") ->Attribute(AZ::Edit::Attributes::Min, Utils::MinCapsuleSubdivisionLevel) ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxCapsuleSubdivisionLevel) ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::ShowingSubdivisionLevel) @@ -199,7 +202,7 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Collider", "PhysX shape collider") + "PhysX Collider", "Creates geometry in the PhysX simulation, using either a primitive shape or geometry from an asset.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") @@ -207,17 +210,17 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged) ->Attribute(AZ::Edit::Attributes::RemoveNotify, &EditorColliderComponent::ValidateRigidBodyMeshGeometryType) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_colliderDebugDraw, - "Debug draw settings", "Debug draw settings") + "Debug draw settings", "Debug draw settings.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } @@ -791,6 +794,7 @@ namespace PhysX if (shapes.empty()) { m_componentWarnings.clear(); + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); return; @@ -824,9 +828,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 +850,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/EditorFixedJointComponent.cpp b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp index 94f57690ba..692671174b 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp @@ -30,13 +30,14 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Fixed Joint", "The fixed joint constraints the position and orientation of a body to another.") + "PhysX Fixed Joint", + "A dynamic joint constraint that constrains a rigid body to the joint with no free translation or rotation on any axis.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/fixed-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp index 610a0a9b2e..dff1ff86bb 100644 --- a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp @@ -164,7 +164,7 @@ namespace PhysX { // EditorForceRegionComponent editContext->Class( - "PhysX Force Region", "The force region component is used to apply a physical force on objects within the region") + "PhysX Force Region", "The force region component is used to apply a physical force on objects within the region.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/ForceVolume.svg") @@ -173,9 +173,10 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/force-region/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("PhysXTriggerService", 0x3a117d7b)) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_debugForces, "Debug Forces", "Draws debug arrows when an entity enters a force region. This occurs in gameplay mode to show the force direction on an entity.") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_forces, "Forces", "Forces in force region") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_debugForces, "Debug Forces", + "Draws debug arrows when an entity enters a force region. This occurs in gameplay mode to show the force direction on an entity.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_forces, "Forces", "Forces in force region.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorForceRegionComponent::OnForcesChanged) ; diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp index 5537502327..1b575074e2 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp @@ -33,14 +33,14 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Hinge Joint", "The entity constrains two actors in PhysX, keeping the origins and x-axes together, and allows free rotation around this common axis") + "PhysX Hinge Joint", "A dynamic joint that constrains a rigid body with rotation limits around a single axis.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/hinge-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "Limitations for the rotation about hinge axis") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode") + ->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "The rotation angle limit around the joint's axis.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorJointComponent.cpp b/Gems/PhysX/Code/Source/EditorJointComponent.cpp index 88e192026d..9a24392fd0 100644 --- a/Gems/PhysX/Code/Source/EditorJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorJointComponent.cpp @@ -37,11 +37,11 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Joint", "The joint constrains the position and orientation of a body to another.") + "PhysX Joint", "A dynamic joint that constrains the position and orientation of one rigid body to another.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &EditorJointComponent::m_config, "Standard Joint Parameters", "Joint parameters shared by all joint types") + ->DataElement(0, &EditorJointComponent::m_config, "Standard Joint Parameters", "Joint parameters shared by all joint types.") ; } } diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index 447c2755c6..e384f222e8 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); @@ -123,38 +122,38 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialLinearVelocity, - "Initial linear velocity", "Initial linear velocity") + "Initial linear velocity", "Linear velocity applied when the rigid body is activated.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSpeedUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialAngularVelocity, - "Initial angular velocity", "Initial angular velocity (limited by maximum angular velocity)") + "Initial angular velocity", "Angular velocity applied when the rigid body is activated (limited by maximum angular velocity).") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_linearDamping, - "Linear damping", "Linear damping (must be non-negative)") + "Linear damping", "The rate of decay over time for linear velocity even if no forces are acting on the rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility) ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_angularDamping, - "Angular damping", "Angular damping (must be non-negative)") + "Angular damping", "The rate of decay over time for angular velocity even if no forces are acting on the rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility) ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_sleepMinEnergy, - "Sleep threshold", "Kinetic energy per unit mass below which body can go to sleep (must be non-negative)") + "Sleep threshold", "The rigid body can go to sleep (settle) when kinetic energy per unit mass is persistently below this value.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility) ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSleepThresholdUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_startAsleep, - "Start asleep", "The rigid body will be asleep when spawned") + "Start asleep", "When active, the rigid body will be asleep when spawned, and wake when the body is disturbed.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_interpolateMotion, - "Interpolate motion", "Makes object motion look smoother") + "Interpolate motion", "When active, simulation results are interpolated resulting in smoother motion.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInterpolationVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_gravityEnabled, - "Gravity enabled", "Rigid body will be affected by gravity") + "Gravity enabled", "When active, global gravity affects this rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetGravityVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_kinematic, - "Kinematic", "Rigid body is kinematic") + "Kinematic", "When active, the rigid body is not affected by gravity or other forces and is moved by script.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetKinematicVisibility) // Linear axis locking properties @@ -162,85 +161,90 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearX, "Lock X", - "Lock motion along X direction") + "When active, forces won't create translation on the X axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearY, "Lock Y", - "Lock motion along Y direction") + "When active, forces won't create translation on the Y axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearZ, "Lock Z", - "Lock motion along Z direction") + "When active, forces won't create translation on the Z axis of the rigid body.") // Angular axis locking properties ->ClassElement(AZ::Edit::ClassElements::Group, "Angular Axis Locking") ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularX, "Lock X", - "Lock rotation around X direction") + "When active, forces won't create rotation on the X axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularY, "Lock Y", - "Lock rotation around Y direction") + "When active, forces won't create rotation on the Y axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularZ, "Lock Z", - "Lock rotation around Z direction") + "When active, forces won't create rotation on the Z axis of the rigid body.") ->ClassElement(AZ::Edit::ClassElements::Group, "Continuous Collision Detection") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdEnabled, - "CCD enabled", "Whether continuous collision detection is enabled for this body") + "CCD enabled", "When active, the rigid body has continuous collision detection (CCD). Use this to ensure accurate " + "collision detection, particularly for fast moving rigid bodies. CCD must be activated in the global PhysX preferences.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdMinAdvanceCoefficient, - "Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness") + "Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness.") ->Attribute(AZ::Edit::Attributes::Min, 0.01f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ->Attribute(AZ::Edit::Attributes::Max, 0.99f) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCCDEnabled) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdFrictionEnabled, - "CCD friction", "Whether friction is applied when CCD collisions are resolved") + "CCD friction", "When active, friction is applied when continuous collision detection (CCD) collisions are resolved.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCCDEnabled) ->ClassElement(AZ::Edit::ClassElements::Group, "") // end previous group by starting new unnamed expanded group ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_maxAngularVelocity, - "Maximum angular velocity", "The PhysX solver will clamp angular velocities with magnitude exceeding this value") + "Maximum angular velocity", "Clamp angular velocities to this maximum value. " + "This prevents rigid bodies from rotating at unrealistic velocities after collisions.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMaxVelocitiesVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit()) // Mass properties ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeCenterOfMass, - "Compute COM", "Whether to automatically compute the center of mass") + "Compute COM", "Compute the center of mass (COM) for this rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_centerOfMassOffset, - "COM offset", "Center of mass offset in local frame") + "COM offset", "Local space offset for the center of mass (COM).") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCoMVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetLengthUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeMass, - "Compute Mass", "Whether to automatically compute the mass") + "Compute Mass", "When active, the mass of the rigid body is computed based on the volume and density values of its colliders.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_mass, - "Mass", "The mass of the object (must be non-negative, with a value of zero treated as infinite)") + "Mass", "The mass of the rigid body in kilograms. A value of 0 is treated as infinite. " + "The trajectory of infinite mass bodies cannot be affected by any collisions or forces other than gravity.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetMassUnit()) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMassVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeInertiaTensor, - "Compute inertia", "Whether to automatically compute the inertia values based on the mass and shape of the rigid body") + "Compute inertia", "When active, inertia is computed based on the mass and shape of the rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(Editor::InertiaHandler, &AzPhysics::RigidBodyConfiguration::m_inertiaTensor, - "Inertia diagonal", "Diagonal elements of the inertia tensor") + "Inertia diagonal", "Inertia diagonal elements that specify an inertia tensor; determines the " + "torque required to rotate the rigid body on each axis.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetInertiaUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_includeAllShapesInMassCalculation, - "Include non-simulated shapes in Mass", "If set, non-simulated shapes will also be included in the center of mass, inertia and mass calculations.") + "Include non-simulated shapes in Mass", + "When active, non-simulated shapes are included in the center of mass, inertia, and mass calculations.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ; @@ -251,7 +255,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorRigidBodyConfiguration::m_centerOfMassDebugDraw, - "Debug draw COM", "Whether to debug draw the center of mass for this body") + "Debug draw COM", "Display the rigid body's center of mass (COM) in the viewport.") ; } } @@ -310,6 +314,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 +349,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/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 2bbc04faae..4ee51d7787 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -79,7 +79,7 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Shape Collider", "Creates geometry in the PhysX simulation based on an attached shape component") + "PhysX Shape Collider", "Create a PhysX collider using a shape provided by a Shape component.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") @@ -88,13 +88,14 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/shape-collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderConfig, - "Collider configuration", "Configuration of the collider") + "Collider configuration", "Configuration of the collider.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnConfigurationChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderDebugDraw, - "Debug draw settings", "Debug draw settings") + "Debug draw settings", "Debug draw settings.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_subdivisionCount, "Subdivision count", "Number of angular subdivisions in the PhysX cylinder") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_subdivisionCount, "Subdivision count", + "Number of angular subdivisions in the PhysX cylinder.") ->Attribute(AZ::Edit::Attributes::Min, Utils::MinFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSubdivisionCountChange) diff --git a/Gems/PhysX/Code/Source/ForceRegion.cpp b/Gems/PhysX/Code/Source/ForceRegion.cpp index 63426d304e..5314f97f9a 100644 --- a/Gems/PhysX/Code/Source/ForceRegion.cpp +++ b/Gems/PhysX/Code/Source/ForceRegion.cpp @@ -62,10 +62,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Force Region", "Applies forces on entities within a region") + "Force Region", "Applies forces on entities within a region.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceRegion::m_forces, "Forces", "Forces acting in the region") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceRegion::m_forces, "Forces", "Forces acting in the region.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } diff --git a/Gems/PhysX/Code/Source/ForceRegionForces.cpp b/Gems/PhysX/Code/Source/ForceRegionForces.cpp index 202a073640..6ed4c3e93a 100644 --- a/Gems/PhysX/Code/Source/ForceRegionForces.cpp +++ b/Gems/PhysX/Code/Source/ForceRegionForces.cpp @@ -37,13 +37,13 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "World Space Force", "Applies a force in world space") + "World Space Force", "Applies a force in world space.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceWorldSpace::m_direction, "Direction", "Direction of the force in world space") + ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceWorldSpace::m_direction, "Direction", "Direction of the force in world space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceWorldSpace::m_magnitude, "Magnitude", "Magnitude of the force in world space") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceWorldSpace::m_magnitude, "Magnitude", "Magnitude of the force in world space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -109,13 +109,13 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Local Space Force", "Applies a force in the volume's local space") + "Local Space Force", "Applies a force in the volume's local space.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceLocalSpace::m_direction, "Direction", "Direction of the force in local space") + ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceLocalSpace::m_direction, "Direction", "Direction of the force in local space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLocalSpace::m_magnitude, "Magnitude", "Magnitude of the force in local space") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLocalSpace::m_magnitude, "Magnitude", "Magnitude of the force in local space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -179,10 +179,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Point Force", "Applies a force relative to the center of the volume") + "Point Force", "Applies a force directed towards or away from the center of the volume.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForcePoint::m_magnitude, "Magnitude", "Magnitude of the point force") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForcePoint::m_magnitude, "Magnitude", "Magnitude of the point force.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -242,19 +242,24 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Spline Follow Force", "Applies a force to make objects follow a spline at a given speed") + "Spline Follow Force", "Applies a force to make objects follow a spline at a given speed.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_dampingRatio, "Damping Ratio", "Amount of damping applied to an entity that is moving towards a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_dampingRatio, "Damping Ratio", + "Values below 1 cause the entity to approach the spline faster but lead to overshooting and oscillation, " + "while higher values will cause it to approach more slowly but more smoothly.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDampingRatio) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_frequency, "Frequency", "Frequency at which an entity moves towards a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_frequency, "Frequency", + "Affects how quickly the entity approaches the spline.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinFrequency) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxFrequency) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_targetSpeed, "Target Speed", "Speed at which entities in the force region move along a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_targetSpeed, "Target Speed", + "Speed at which entities in the force region move along a spline.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_lookAhead, "Lookahead", "Distance at which entities look ahead in their path to reach a point on a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_lookAhead, "Lookahead", + "Distance at which entities look ahead in their path to reach a point on a spline.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -393,10 +398,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Simple Drag Force", "Simulates a drag force on entities") + "Simple Drag Force", "Simulates a drag force on entities.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSimpleDrag::m_volumeDensity, "Region Density", "Density of the region") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSimpleDrag::m_volumeDensity, "Region Density", "Density of the region.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDensity) ; @@ -463,10 +468,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Linear Damping Force", "Applies an opposite force to the entity's velocity") + "Linear Damping Force", "Applies an opposite force to the entity's velocity.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLinearDamping::m_damping, "Damping", "Amount of damping applied to an opposite force") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLinearDamping::m_damping, "Damping", "Amount of damping applied to an opposite force.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDamping) ; diff --git a/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp b/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp index 530bd03732..ae74859528 100644 --- a/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp @@ -59,22 +59,22 @@ namespace PhysX ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitY, "Swing limit Y", - "Maximum angle from the Y axis of the joint frame") + "The rotation angle limit around the joint's Y axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitZ, "Swing limit Z", - "Maximum angle from the Z axis of the joint frame") + "The rotation angle limit around the joint's Z axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitLower, "Twist lower limit", - "Lower limit for rotation about the X axis of the joint frame") + "The lower rotation angle limit around the joint's X axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, -180.0f) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitUpper, "Twist upper limit", - "Upper limit for rotation about the X axis of the joint frame") + "The upper rotation angle limit around the joint's X axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, -180.0f) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp index ed9030ac4f..6af08cfaeb 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp @@ -42,16 +42,16 @@ namespace PhysX "PhysX Character Controller Configuration", "PhysX Character Controller Configuration") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &CharacterControllerConfiguration::m_slopeBehaviour, - "Slope Behaviour", "Behaviour of the controller on surfaces above the maximum slope") + "Slope Behavior", "Behavior of the controller on surfaces that exceed the Maximum Slope Angle.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->EnumAttribute(SlopeBehaviour::PreventClimbing, "Prevent Climbing") ->EnumAttribute(SlopeBehaviour::ForceSliding, "Force Sliding") ->DataElement(AZ::Edit::UIHandlers::Default, &CharacterControllerConfiguration::m_contactOffset, - "Contact Offset", "Extra distance outside the controller used for smoother contact resolution") + "Contact Offset", "Distance from the controller boundary where contact with surfaces can be resolved.") ->Attribute(AZ::Edit::Attributes::Min, 0.01f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ->DataElement(AZ::Edit::UIHandlers::Default, &CharacterControllerConfiguration::m_scaleCoefficient, - "Scale", "Scalar coefficient used to scale the controller, usually slightly smaller than 1") + "Scale", "Scales the controller. Usually less than 1.0 to ensure visual contact between the character and surface.") ->Attribute(AZ::Edit::Attributes::Min, 0.01f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp index 4a4f12558d..e86b81359c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp @@ -33,7 +33,7 @@ namespace PhysX "PhysX Character Gameplay Configuration", "PhysX Character Gameplay Configuration") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->DataElement(AZ::Edit::UIHandlers::Default, &CharacterGameplayConfiguration::m_gravityMultiplier, - "Gravity Multiplier", "Multiplier to be combined with the world gravity value for applying character gravity") + "Gravity Multiplier", "Multiplier for global gravity value that applies only to this character entity.") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ; } diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp index 4e24067dbf..114f9bc2f7 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp @@ -36,18 +36,18 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape") + "EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape.") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorCharacterControllerProxyShapeConfig::m_shapeType, "Shape", - "The shape associated with the character controller") + "The shape of the character controller.") ->EnumAttribute(Physics::ShapeType::Capsule, "Capsule") ->EnumAttribute(Physics::ShapeType::Box, "Box") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_box, "Box", - "Configuration of box shape") + "Configuration of box shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsBoxConfig) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_capsule, "Capsule", - "Configuration of capsule shape") + "Configuration of capsule shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsCapsuleConfig) ; } @@ -93,7 +93,8 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Character Controller", "PhysX Character Controller") + "PhysX Character Controller", + "Provides basic character interactions with the physical world, such as preventing movement through other PhysX bodies.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg") @@ -101,12 +102,12 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-controller/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_configuration, - "Configuration", "Configuration for the character controller") + "Configuration", "Configuration for the character controller.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCharacterControllerComponent::OnControllerConfigChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_proxyShapeConfiguration, - "Shape Configuration", "The configuration for the shape associated with the character controller") + "Shape Configuration", "The configuration for the shape associated with the character controller.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCharacterControllerComponent::OnShapeConfigChanged) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp index 81d158d81c..de4deb442f 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp @@ -43,7 +43,7 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Character Gameplay", "PhysX Character Gameplay") + "PhysX Character Gameplay", "An example implementation of character physics behavior such as gravity.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg") @@ -51,7 +51,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-gameplay/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterGameplayComponent::m_gameplayConfig, - "Gameplay Configuration", "Gameplay Configuration") + "Gameplay Configuration", "Gameplay Configuration.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 6fa3bdbffd..57972fea3e 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -82,7 +82,7 @@ namespace PhysX if (editContext) { editContext->Class( - "PhysX Ragdoll", "Provides simulation of characters in PhysX.") + "PhysX Ragdoll", "Creates a PhysX ragdoll simulation for an animation actor.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXRagdoll.svg") @@ -91,26 +91,28 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ragdoll/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_positionIterations, "Position Iteration Count", - "A higher iteration count generally improves fidelity at the cost of performance, but note that very high " - "values may lead to severe instability if ragdoll colliders interfere with satisfying joint constraints") + "The frequency at which ragdoll collider positions are resolved. Higher values can increase fidelity but decrease " + "performance. Very high values might introduce instability.") ->Attribute(AZ::Edit::Attributes::Min, 1) ->Attribute(AZ::Edit::Attributes::Max, 255) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_velocityIterations, "Velocity Iteration Count", - "A higher iteration count generally improves fidelity at the cost of performance, but note that very high " - "values may lead to severe instability if ragdoll colliders interfere with satisfying joint constraints") + "The frequency at which ragdoll collider velocities are resolved. Higher values can increase fidelity but decrease " + "performance. Very high values might introduce instability.") ->Attribute(AZ::Edit::Attributes::Min, 1) ->Attribute(AZ::Edit::Attributes::Max, 255) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_enableJointProjection, - "Enable Joint Projection", "Whether to use joint projection to preserve joint constraints " - "in demanding situations at the expense of potentially reducing physical correctness") + "Enable Joint Projection", "When active, preserves joint constraints in volatile simulations. " + "Might not be physically correct in all simulations.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_jointProjectionLinearTolerance, - "Joint Projection Linear Tolerance", "Linear joint error above which projection will be applied") + "Joint Projection Linear Tolerance", + "Maximum linear joint error. Projection is applied to linear joint errors above this value.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Step, 1e-3f) ->Attribute(AZ::Edit::Attributes::Visibility, &RagdollComponent::IsJointProjectionVisible) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_jointProjectionAngularToleranceDegrees, - "Joint Projection Angular Tolerance", "Angular joint error (in degrees) above which projection will be applied") + "Joint Projection Angular Tolerance", + "Maximum angular joint error. Projection is applied to angular joint errors above this value.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") diff --git a/Gems/PhysX/Code/Source/RigidBody.cpp b/Gems/PhysX/Code/Source/RigidBody.cpp index f8b22affe0..5b36376da5 100644 --- a/Gems/PhysX/Code/Source/RigidBody.cpp +++ b/Gems/PhysX/Code/Source/RigidBody.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,28 @@ namespace PhysX { + namespace + { + const AZ::Vector3 DefaultCenterOfMass = AZ::Vector3::CreateZero(); + const float DefaultMass = 1.0f; + const AZ::Matrix3x3 DefaultInertiaTensor = AZ::Matrix3x3::CreateIdentity(); + + bool IsSimulationShape(const physx::PxShape& pxShape) + { + return (pxShape.getFlags() & physx::PxShapeFlag::eSIMULATION_SHAPE); + } + + bool CanShapeComputeMassProperties(const physx::PxShape& pxShape) + { + // Note: List based on computeMassAndInertia function in ExtRigidBodyExt.cpp file in PhysX. + const physx::PxGeometryType::Enum geometryType = pxShape.getGeometryType(); + return geometryType == physx::PxGeometryType::eSPHERE + || geometryType == physx::PxGeometryType::eBOX + || geometryType == physx::PxGeometryType::eCAPSULE + || geometryType == physx::PxGeometryType::eCONVEXMESH; + } + } + void RigidBody::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); @@ -152,104 +175,120 @@ namespace PhysX m_shapes.erase(found); } - void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3* centerOfMassOffsetOverride, const AZ::Matrix3x3* inertiaTensorOverride, const float* massOverride) + void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3& centerOfMassOffsetOverride, const AZ::Matrix3x3& inertiaTensorOverride, const float massOverride) { - // Input validation - bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM); - AZ_Assert(computeCenterOfMass || centerOfMassOffsetOverride, - "UpdateMassProperties: MassComputeFlags::COMPUTE_COM is not set but COM offset is not specified"); - computeCenterOfMass = computeCenterOfMass || !centerOfMassOffsetOverride; - - bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA); - AZ_Assert(computeInertiaTensor || inertiaTensorOverride, - "UpdateMassProperties: MassComputeFlags::COMPUTE_INERTIA is not set but inertia tensor is not specified"); - computeInertiaTensor = computeInertiaTensor || !inertiaTensorOverride; - - bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS); - AZ_Assert(computeMass || massOverride, - "UpdateMassProperties: MassComputeFlags::COMPUTE_MASS is not set but mass is not specified"); - computeMass = computeMass || !massOverride; + const bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM); + const bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA); + const bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS); + const bool needsCompute = computeCenterOfMass || computeInertiaTensor || computeMass; + const bool includeAllShapesInMassCalculation = AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES); - AZ::u32 shapesCount = GetShapeCount(); - - // Basic cases when we don't need to compute anything - if (shapesCount == 0 || flags == AzPhysics::MassComputeFlags::NONE) + // Basic case where all properties are set directly. + if (!needsCompute) { - if (massOverride) - { - SetMass(*massOverride); - } - - if (inertiaTensorOverride) - { - SetInertia(*inertiaTensorOverride); - } - - if (centerOfMassOffsetOverride) - { - SetCenterOfMassOffset(*centerOfMassOffsetOverride); - } + SetCenterOfMassOffset(centerOfMassOffsetOverride); + SetMass(massOverride); + SetInertia(inertiaTensorOverride); return; } - // Setup center of mass offset pointer for PxRigidBodyExt::updateMassAndInertia function - AZStd::optional optionalComOverride; - if (!computeCenterOfMass && centerOfMassOffsetOverride) + // If there are no shapes then set the properties directly without computing anything. + if (m_shapes.empty()) { - optionalComOverride = PxMathConvert(*centerOfMassOffsetOverride); + SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride); + SetMass(computeMass ? DefaultMass : massOverride); + SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride); + return; } - const physx::PxVec3* massLocalPose = optionalComOverride.has_value() ? &optionalComOverride.value() : nullptr; + auto cannotComputeMassProperties = [this, includeAllShapesInMassCalculation] + { + PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); + return AZStd::any_of(m_shapes.cbegin(), m_shapes.cend(), + [includeAllShapesInMassCalculation](const AZStd::shared_ptr& shape) + { + const physx::PxShape& pxShape = *shape->GetPxShape(); + const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape); + + return includeShape && !CanShapeComputeMassProperties(pxShape); + }); + }; + + // If contains shapes that cannot compute mass properties (triangle mesh, + // plane or heightfield) then default values will be used. + if (cannotComputeMassProperties()) + { + AZ_Warning("RigidBody", !computeCenterOfMass, + "Rigid body '%s' cannot compute COM because it contains triangle mesh, plane or heightfield shapes, it will default to %s.", + GetName().c_str(), AZ::ToString(DefaultCenterOfMass).c_str()); + AZ_Warning("RigidBody", !computeMass, + "Rigid body '%s' cannot compute Mass because it contains triangle mesh, plane or heightfield shapes, it will default to %0.1f.", + GetName().c_str(), DefaultMass); + AZ_Warning("RigidBody", !computeInertiaTensor, + "Rigid body '%s' cannot compute Inertia because it contains triangle mesh, plane or heightfield shapes, it will default to %s.", + GetName().c_str(), AZ::ToString(DefaultInertiaTensor.RetrieveScale()).c_str()); + + SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride); + SetMass(computeMass ? DefaultMass : massOverride); + SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride); + return; + } - bool includeAllShapesInMassCalculation = - AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES); + // Center of mass needs to be considered first since + // it's needed when computing mass and inertia. + if (computeCenterOfMass) + { + // Compute Center of Mass + UpdateCenterOfMass(includeAllShapesInMassCalculation); + } + else + { + SetCenterOfMassOffset(centerOfMassOffsetOverride); + } + const physx::PxVec3 pxCenterOfMass = PxMathConvert(GetCenterOfMassLocal()); - // Handle the case when we don't compute mass - if (!computeMass) + if (computeMass) { + // Gather material densities from all shapes, + // mass computation is based on them. + AZStd::vector densities; + densities.reserve(m_shapes.size()); + for (const auto& shape : m_shapes) + { + densities.emplace_back(shape->GetMaterial()->GetDensity()); + } + + // Compute Mass + Inertia { PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, *massOverride, massLocalPose, - includeAllShapesInMassCalculation); + physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, + densities.data(), static_cast(densities.size()), + &pxCenterOfMass, includeAllShapesInMassCalculation); } + // There is no physx function to only compute the mass without + // computing the inertia. So now that both have been computed + // we can override the inertia if it's suppose to use a + // specific value set by the user. if (!computeInertiaTensor) { - SetInertia(*inertiaTensorOverride); + SetInertia(inertiaTensorOverride); } - - return; - } - - // Handle the cases when mass should be computed from density - if (shapesCount == 1) - { - AZStd::shared_ptr shape = GetShape(0); - float density = shape->GetMaterial()->GetDensity(); - - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, density, massLocalPose, - includeAllShapesInMassCalculation); } else { - AZStd::vector densities(shapesCount); - for (AZ::u32 i = 0; i < shapesCount; ++i) + if (computeInertiaTensor) { - densities[i] = GetShape(i)->GetMaterial()->GetDensity(); + // Set Mass + Compute Inertia + PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); + physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, massOverride, + &pxCenterOfMass, includeAllShapesInMassCalculation); + } + else + { + SetMass(massOverride); + SetInertia(inertiaTensorOverride); } - - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, densities.data(), - shapesCount, massLocalPose, includeAllShapesInMassCalculation); - } - - // Set the overrides if provided. - // Note: We don't set the center of mass here because it was already provided - // to PxRigidBodyExt::updateMassAndInertia above - if (!computeInertiaTensor) - { - SetInertia(*inertiaTensorOverride); } } @@ -344,70 +383,57 @@ namespace PhysX } } - void RigidBody::UpdateComputedCenterOfMass() + void RigidBody::UpdateCenterOfMass(bool includeAllShapesInMassCalculation) { - if (m_pxRigidActor) + if (m_shapes.empty()) { - physx::PxU32 shapeCount = 0; - { - PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); - shapeCount = m_pxRigidActor->getNbShapes(); - } - if (shapeCount > 0) - { - AZStd::vector shapes; - shapes.resize(shapeCount); - - { - PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->getShapes(&shapes[0], shapeCount); - } - - shapes.erase(AZStd::remove_if(shapes.begin() - , shapes.end() - , [](const physx::PxShape* shape) - { - return shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE; - }) - , shapes.end()); - shapeCount = static_cast(shapes.size()); + SetCenterOfMassOffset(DefaultCenterOfMass); + return; + } - if (shapeCount == 0) - { - SetZeroCenterOfMass(); - return; - } + AZStd::vector pxShapes; + pxShapes.reserve(m_shapes.size()); + { + // Filter shapes in the same way that updateMassAndInertia function does. + PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); + for (const auto& shape : m_shapes) + { + const physx::PxShape& pxShape = *shape->GetPxShape(); + const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape); - const auto properties = physx::PxRigidBodyExt::computeMassPropertiesFromShapes(&shapes[0], shapeCount); - const physx::PxTransform computedCenterOfMass(properties.centerOfMass); + if (includeShape && CanShapeComputeMassProperties(pxShape)) { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setCMassLocalPose(computedCenterOfMass); + pxShapes.emplace_back(&pxShape); } } - else - { - SetZeroCenterOfMass(); - } } - } - void RigidBody::SetInertia(const AZ::Matrix3x3& inertia) - { - if (m_pxRigidActor) + if (pxShapes.empty()) { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setMassSpaceInertiaTensor(PxMathConvert(inertia.RetrieveScale())); + SetCenterOfMassOffset(DefaultCenterOfMass); + return; } + + const physx::PxMassProperties pxMassProperties = [this, &pxShapes] + { + // Note: PhysX computeMassPropertiesFromShapes function does not use densities + // to compute the shape's masses, which are needed to calculate the center of mass. + // This differs from updateMassAndInertia function, which uses material density values. + // So the masses used during center of mass calculation do not match the masses + // used during mass/inertia calculation. This is an inconsistency in PhysX. + PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); + return physx::PxRigidBodyExt::computeMassPropertiesFromShapes(pxShapes.data(), static_cast(pxShapes.size())); + }(); + + SetCenterOfMassOffset(PxMathConvert(pxMassProperties.centerOfMass)); } - void RigidBody::ComputeInertia() + void RigidBody::SetInertia(const AZ::Matrix3x3& inertia) { if (m_pxRigidActor) { PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - auto localPose = m_pxRigidActor->getCMassLocalPose().p; - physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, m_pxRigidActor->getMass(), &localPose); + m_pxRigidActor->setMassSpaceInertiaTensor(PxMathConvert(inertia.RetrieveScale())); } } @@ -783,13 +809,4 @@ namespace PhysX { return m_name; } - - void RigidBody::SetZeroCenterOfMass() - { - if (m_pxRigidActor) - { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setCMassLocalPose(physx::PxTransform(PxMathConvert(AZ::Vector3::CreateZero()))); - } - } } diff --git a/Gems/PhysX/Code/Source/RigidBody.h b/Gems/PhysX/Code/Source/RigidBody.h index 10f2ebb556..a20f79b161 100644 --- a/Gems/PhysX/Code/Source/RigidBody.h +++ b/Gems/PhysX/Code/Source/RigidBody.h @@ -109,17 +109,15 @@ namespace PhysX void RemoveShape(AZStd::shared_ptr shape) override; void UpdateMassProperties(AzPhysics::MassComputeFlags flags = AzPhysics::MassComputeFlags::DEFAULT, - const AZ::Vector3* centerOfMassOffsetOverride = nullptr, - const AZ::Matrix3x3* inertiaTensorOverride = nullptr, - const float* massOverride = nullptr) override; + const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(), + const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(), + const float massOverride = 1.0f) override; private: void CreatePhysXActor(const AzPhysics::RigidBodyConfiguration& configuration); - void UpdateComputedCenterOfMass(); - void ComputeInertia(); + void UpdateCenterOfMass(bool includeAllShapesInMassCalculation); void SetInertia(const AZ::Matrix3x3& inertia); - void SetZeroCenterOfMass(); AZStd::shared_ptr m_pxRigidActor; AZStd::vector> m_shapes; diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 3b5c98ba2d..86e3ceb98f 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -198,8 +198,8 @@ namespace PhysX AZ_Warning("PhysXScene", shapeAdded, "No Collider or Shape information found when creating Rigid body [%s]", configuration->m_debugName.c_str()); } const AzPhysics::MassComputeFlags& flags = configuration->GetMassComputeFlags(); - newBody->UpdateMassProperties(flags, &configuration->m_centerOfMassOffset, - &configuration->m_inertiaTensor, &configuration->m_mass); + newBody->UpdateMassProperties(flags, configuration->m_centerOfMassOffset, + configuration->m_inertiaTensor, configuration->m_mass); crc = AZ::Crc32(newBody, sizeof(*newBody)); return newBody; diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index ebdfc5d417..cc21285f8c 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -511,10 +511,12 @@ namespace PhysX materialLibrary.BlockUntilLoadComplete(); - AZ_Warning("PhysX", (materialLibrary.GetData() != nullptr), + const bool loadedSuccessfully = materialLibrary.GetData() != nullptr && !materialLibrary.IsError(); + + AZ_Warning("PhysX", loadedSuccessfully, "LoadDefaultMaterialLibrary: Default Material Library asset data is invalid."); - return materialLibrary.GetData() != nullptr && !materialLibrary.IsError(); + return loadedSuccessfully; } //TEMP -- until these are fully moved over here diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index 42c78d2c1d..2f4d082ccb 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -101,7 +101,7 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("PhysX", "Global PhysX physics configuration") + editContext->Class("PhysX", "Global PhysX physics configuration.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) @@ -458,7 +458,13 @@ namespace PhysX { const PhysXSystemConfiguration defaultConfig = PhysXSystemConfiguration::CreateDefault(); m_physXSystem->Initialize(&defaultConfig); - registryManager.SaveSystemConfiguration(defaultConfig, {}); + + auto saveCallback = []([[maybe_unused]] const PhysXSystemConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the default PhysX configuration."); + }; + registryManager.SaveSystemConfiguration(defaultConfig, saveCallback); } //Load the DefaultSceneConfig @@ -471,7 +477,13 @@ namespace PhysX { const AzPhysics::SceneConfiguration defaultConfig = AzPhysics::SceneConfiguration::CreateDefault(); m_physXSystem->UpdateDefaultSceneConfiguration(defaultConfig); - registryManager.SaveDefaultSceneConfiguration(defaultConfig, {}); + + auto saveCallback = []([[maybe_unused]] const AzPhysics::SceneConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the default Scene configuration."); + }; + registryManager.SaveDefaultSceneConfiguration(defaultConfig, saveCallback); } //load the debug configuration and initialize the PhysX debug interface @@ -486,7 +498,13 @@ namespace PhysX { const Debug::DebugConfiguration defaultConfig = Debug::DebugConfiguration::CreateDefault(); debug->Initialize(defaultConfig); - registryManager.SaveDebugConfiguration(defaultConfig, {}); + + auto saveCallback = []([[maybe_unused]] const Debug::DebugConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the default PhysX Debug configuration."); + }; + registryManager.SaveDebugConfiguration(defaultConfig, saveCallback); } } } diff --git a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp index 0c0eee3eb0..9e3a738370 100644 --- a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp +++ b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -1283,11 +1285,13 @@ namespace PhysX EXPECT_TRUE(AZ::IsClose(expectedMass, mass, 0.001f)); } + // Valid material density values: [0.01f, 1e5f] INSTANTIATE_TEST_CASE_P(PhysX, MultiShapesDensityTestFixture, ::testing::Values( - AZStd::make_pair(std::numeric_limits::min(), std::numeric_limits::max()), - AZStd::make_pair(-std::numeric_limits::max(), 0.0f), - AZStd::make_pair(1.0f, 1e9f) + AZStd::make_pair(0.01f, 0.01f), + AZStd::make_pair(1e5f, 1e5f), + AZStd::make_pair(0.01f, 1e5f), + AZStd::make_pair(2364.0f, 10.0f) )); // Fixture for testing extreme density values @@ -1311,6 +1315,7 @@ namespace PhysX && resultingDensity <= Physics::MaterialConfiguration::MaxDensityLimit); } + // Valid material density values: [0.01f, 1e5f] INSTANTIATE_TEST_CASE_P(PhysX, DensityBoundariesTestFixture, ::testing::Values( std::numeric_limits::min(), @@ -1318,7 +1323,9 @@ namespace PhysX -std::numeric_limits::max(), 0.0f, 1.0f, - 1e9f + 1e9f, + 0.01f, + 1e5f )); enum class SimulatedShapesMode @@ -1329,7 +1336,7 @@ namespace PhysX }; class MassComputeFixture - : public ::testing::TestWithParam<::testing::tuple> + : public ::testing::TestWithParam<::testing::tuple> { public: void SetUp() override final @@ -1349,6 +1356,8 @@ namespace PhysX AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &m_rigidBodyConfig); m_rigidBody = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, simBodyHandle)); } + + ASSERT_TRUE(m_rigidBody != nullptr); } void TearDown() override final @@ -1363,130 +1372,242 @@ namespace PhysX m_rigidBody = nullptr; } - SimulatedShapesMode GetShapesMode() const + Physics::ShapeType GetShapeType() const { return ::testing::get<0>(GetParam()); } - AzPhysics::MassComputeFlags GetMassComputeFlags() const + SimulatedShapesMode GetShapesMode() const { return ::testing::get<1>(GetParam()); } + AzPhysics::MassComputeFlags GetMassComputeFlags() const + { + const AzPhysics::MassComputeFlags massComputeFlags = ::testing::get<2>(GetParam()); + if (IncludeAllShapes()) + { + return massComputeFlags | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES; + } + else + { + return massComputeFlags; + } + } + + bool IncludeAllShapes() const + { + return ::testing::get<3>(GetParam()); + } + bool IsMultiShapeTest() const { - return ::testing::get<2>(GetParam()); + return ::testing::get<4>(GetParam()); } bool IsMassExpectedToChange() const { return m_rigidBodyConfig.m_computeMass && - (!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); + (GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); } bool IsComExpectedToChange() const { return m_rigidBodyConfig.m_computeCenterOfMass && - (!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); + (GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); } bool IsInertiaExpectedToChange() const { return m_rigidBodyConfig.m_computeInertiaTensor && - (!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); + (GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); } + AZStd::shared_ptr CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, Physics::ShapeType shapeType) + { + AZStd::shared_ptr shape; + Physics::System* physics = AZ::Interface::Get(); + switch (shapeType) + { + case Physics::ShapeType::Sphere: + shape = physics->CreateShape(colliderConfiguration, Physics::SphereShapeConfiguration()); + break; + case Physics::ShapeType::Box: + shape = physics->CreateShape(colliderConfiguration, Physics::BoxShapeConfiguration()); + break; + case Physics::ShapeType::Capsule: + shape = physics->CreateShape(colliderConfiguration, Physics::CapsuleShapeConfiguration()); + break; + } + return shape; + }; + AzPhysics::RigidBodyConfiguration m_rigidBodyConfig; - AzPhysics::RigidBody* m_rigidBody; + AzPhysics::RigidBody* m_rigidBody = nullptr; AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle; }; TEST_P(MassComputeFixture, RigidBody_ComputeMassFlagsCombinationsTwoShapes_MassPropertiesCalculatedAccordingly) { - SimulatedShapesMode shapeMode = GetShapesMode(); - AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags(); - bool multiShapeTest = IsMultiShapeTest(); - Physics::System* physics = AZ::Interface::Get(); + const Physics::ShapeType shapeType = GetShapeType(); + const SimulatedShapesMode shapeMode = GetShapesMode(); + const AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags(); + const bool multiShapeTest = IsMultiShapeTest(); // Save initial values - AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld(); - AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld(); - float massBefore = m_rigidBody->GetMass(); + const AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld(); + const AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld(); + const float massBefore = m_rigidBody->GetMass(); - // Box shape will be simulated for ALL and MIXED shape modes - Physics::ColliderConfiguration boxColliderConfig; - boxColliderConfig.m_isSimulated = + // Shape will be simulated for ALL and MIXED shape modes + Physics::ColliderConfiguration colliderConfig; + colliderConfig.m_isSimulated = (shapeMode == SimulatedShapesMode::ALL || shapeMode == SimulatedShapesMode::MIXED); - boxColliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f); + colliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f); - AZStd::shared_ptr boxShape = - physics->CreateShape(boxColliderConfig, Physics::BoxShapeConfiguration()); - m_rigidBody->AddShape(boxShape); + AZStd::shared_ptr shape = CreateShape(colliderConfig, shapeType); + m_rigidBody->AddShape(shape); if (multiShapeTest) { // Sphere shape will be simulated only for the ALL shape mode Physics::ColliderConfiguration sphereColliderConfig; sphereColliderConfig.m_isSimulated = (shapeMode == SimulatedShapesMode::ALL); - sphereColliderConfig.m_position = AZ::Vector3(-1.0f, 0.0f, 0.0f); - AZStd::shared_ptr sphereShape = - physics->CreateShape(sphereColliderConfig, Physics::SphereShapeConfiguration()); + sphereColliderConfig.m_position = AZ::Vector3(-2.0f, 0.0f, 0.0f); + AZStd::shared_ptr sphereShape = CreateShape(sphereColliderConfig, Physics::ShapeType::Sphere); m_rigidBody->AddShape(sphereShape); } // Verify swapping materials results in changes in the mass. - m_rigidBody->UpdateMassProperties(massComputeFlags, &m_rigidBodyConfig.m_centerOfMassOffset, - &m_rigidBodyConfig.m_inertiaTensor, &m_rigidBodyConfig.m_mass); + m_rigidBody->UpdateMassProperties(massComputeFlags, m_rigidBodyConfig.m_centerOfMassOffset, + m_rigidBodyConfig.m_inertiaTensor, m_rigidBodyConfig.m_mass); - float massAfter = m_rigidBody->GetMass(); - AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld(); - AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld(); + const float massAfter = m_rigidBody->GetMass(); + const AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld(); + const AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld(); + using ::testing::Not; + using ::testing::FloatNear; + using ::UnitTest::IsClose; if (IsMassExpectedToChange()) { - EXPECT_FALSE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON)); + EXPECT_THAT(massBefore, Not(FloatNear(massAfter, FLT_EPSILON))); } else { - EXPECT_TRUE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON)); + EXPECT_THAT(massBefore, FloatNear(massAfter, FLT_EPSILON)); } if (IsComExpectedToChange()) { - EXPECT_FALSE(comBefore.IsClose(comAfter)); + EXPECT_THAT(comBefore, Not(IsClose(comAfter))); } else { - EXPECT_TRUE(comBefore.IsClose(comAfter)); + EXPECT_THAT(comBefore, IsClose(comAfter)); } if (IsInertiaExpectedToChange()) { - EXPECT_FALSE(inertiaBefore.IsClose(inertiaAfter)); + EXPECT_THAT(inertiaBefore, Not(IsClose(inertiaAfter))); } else { - EXPECT_TRUE(inertiaBefore.IsClose(inertiaAfter)); + EXPECT_THAT(inertiaBefore, IsClose(inertiaAfter)); } } - AzPhysics::MassComputeFlags possibleMassComputeFlags[] = { - AzPhysics::MassComputeFlags::NONE, AzPhysics::MassComputeFlags::DEFAULT, AzPhysics::MassComputeFlags::COMPUTE_MASS, - AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA, - AzPhysics::MassComputeFlags::DEFAULT | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, - AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA, AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, + static const AzPhysics::MassComputeFlags PossibleMassComputeFlags[] = + { + // No compute + AzPhysics::MassComputeFlags::NONE, + + // Compute Mass only + AzPhysics::MassComputeFlags::COMPUTE_MASS, + + // Compute Inertia only + AzPhysics::MassComputeFlags::COMPUTE_INERTIA, + + // Compute COM only + AzPhysics::MassComputeFlags::COMPUTE_COM, + + // Compute combinations of 2 AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM, - AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA, - AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA, - AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES + + // Compute all + AzPhysics::MassComputeFlags::DEFAULT, // COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS }; INSTANTIATE_TEST_CASE_P(PhysX, MassComputeFixture, ::testing::Combine( - ::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }), - ::testing::ValuesIn(possibleMassComputeFlags), - ::testing::Bool())); + ::testing::ValuesIn({ Physics::ShapeType::Sphere, Physics::ShapeType::Box, Physics::ShapeType::Capsule }), // Values for GetShapeType() + ::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }), // Values for GetShapesMode() + ::testing::ValuesIn(PossibleMassComputeFlags), // Values for GetMassComputeFlags() + ::testing::Bool(), // Values for IncludeAllShapes() + ::testing::Bool())); // Values for IsMultiShapeTest() + + class MassPropertiesWithTriangleMesh + : public ::testing::TestWithParam + { + public: + void SetUp() override + { + if (auto* physicsSystem = AZ::Interface::Get()) + { + AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration(); + sceneConfiguration.m_sceneName = AzPhysics::DefaultPhysicsSceneName; + m_testSceneHandle = physicsSystem->AddScene(sceneConfiguration); + } + } + + void TearDown() override + { + // Clean up the Test scene + if (auto* physicsSystem = AZ::Interface::Get()) + { + physicsSystem->RemoveScene(m_testSceneHandle); + } + m_testSceneHandle = AzPhysics::InvalidSceneHandle; + } + + AzPhysics::MassComputeFlags GetMassComputeFlags() const + { + return GetParam(); + } + + AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle; + }; + + TEST_P(MassPropertiesWithTriangleMesh, KinematicRigidBody_ComputeMassProperties_TriggersWarnings) + { + const AzPhysics::MassComputeFlags flags = GetMassComputeFlags(); + + const bool doesComputeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM); + const bool doesComputeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS); + const bool doesComputeInertia = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA); + + UnitTest::ErrorHandler computeCenterOfMassWarningHandler( + "cannot compute COM"); + UnitTest::ErrorHandler computeMassWarningHandler( + "cannot compute Mass"); + UnitTest::ErrorHandler computeIneriaWarningHandler( + "cannot compute Inertia"); + + AzPhysics::SimulatedBodyHandle rigidBodyhandle = TestUtils::AddKinematicTriangleMeshCubeToScene(m_testSceneHandle, 3.0f, flags); + + EXPECT_TRUE(rigidBodyhandle != AzPhysics::InvalidSimulatedBodyHandle); + EXPECT_EQ(computeCenterOfMassWarningHandler.GetExpectedWarningCount(), doesComputeCenterOfMass ? 1 : 0); + EXPECT_EQ(computeMassWarningHandler.GetExpectedWarningCount(), doesComputeMass ? 1 : 0); + EXPECT_EQ(computeIneriaWarningHandler.GetExpectedWarningCount(), doesComputeInertia ? 1 : 0); + + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->RemoveSimulatedBody(m_testSceneHandle, rigidBodyhandle); + } + } + INSTANTIATE_TEST_CASE_P(PhysX, MassPropertiesWithTriangleMesh, + ::testing::ValuesIn(PossibleMassComputeFlags)); // Values for GetMassComputeFlags() } // namespace PhysX diff --git a/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp b/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp index 6186db6f1e..67fe67bc23 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp +++ b/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp @@ -253,6 +253,36 @@ namespace PhysX return AzPhysics::InvalidSimulatedBodyHandle; } + AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags) + { + // Generate input data + VertexIndexData cubeMeshData = GenerateCubeMeshData(halfExtent); + AZStd::vector cookedData; + bool cookingResult = false; + Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory, + cubeMeshData.first.data(), static_cast(cubeMeshData.first.size()), + cubeMeshData.second.data(), static_cast(cubeMeshData.second.size()), + cookedData); + AZ_Assert(cookingResult, "Failed to cook the cube mesh."); + + // Setup shape & collider configurations + auto shapeConfig = AZStd::make_shared(); + shapeConfig->SetCookedMeshData(cookedData.data(), cookedData.size(), + Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh); + + AzPhysics::RigidBodyConfiguration rigidBodyConfiguration; + rigidBodyConfiguration.m_kinematic = true; + rigidBodyConfiguration.SetMassComputeFlags(massComputeFlags); + rigidBodyConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair( + AZStd::make_shared(), shapeConfig); + + if (auto* sceneInterface = AZ::Interface::Get()) + { + return sceneInterface->AddSimulatedBody(scene, &rigidBodyConfiguration); + } + return AzPhysics::InvalidSimulatedBodyHandle; + } + void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag) { Physics::CollisionFilteringRequestBus::Event(entity->GetId(), &Physics::CollisionFilteringRequests::SetCollisionLayer, layerName, AZ::Crc32(colliderTag.c_str())); diff --git a/Gems/PhysX/Code/Tests/PhysXTestCommon.h b/Gems/PhysX/Code/Tests/PhysXTestCommon.h index ef12ca8350..95859a3f86 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestCommon.h +++ b/Gems/PhysX/Code/Tests/PhysXTestCommon.h @@ -89,6 +89,7 @@ namespace PhysX const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default); AzPhysics::SimulatedBodyHandle AddStaticTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent); + AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags); // Collision Filtering void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag = ""); 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/PhysXSamples/Assets/Characters/Cowboy/AnimationEditorFiles/Cowboy.emfxworkspace b/Gems/PhysXSamples/Assets/Characters/Cowboy/AnimationEditorFiles/Cowboy.emfxworkspace index 1727656092..8bc507082c 100644 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/AnimationEditorFiles/Cowboy.emfxworkspace +++ b/Gems/PhysXSamples/Assets/Characters/Cowboy/AnimationEditorFiles/Cowboy.emfxworkspace @@ -1,3 +1,3 @@ [General] version=1 -startScript="ImportActor -filename \"@assets@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@assets@/Characters/Cowboy/AnimationEditorFiles/Cowboy.motionset\"\nLoadAnimGraph -filename \"@assets@/Characters/Cowboy/AnimationEditorFiles/Cowboy.animgraph\"\nActivateAnimGraph -actorInstanceID %LASTRESULT3% -animGraphID %LASTRESULT1% -motionSetID %LASTRESULT2% -visualizeScale 1.000000\n" +startScript="ImportActor -filename \"@products@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@products@/Characters/Cowboy/AnimationEditorFiles/Cowboy.motionset\"\nLoadAnimGraph -filename \"@products@/Characters/Cowboy/AnimationEditorFiles/Cowboy.animgraph\"\nActivateAnimGraph -actorInstanceID %LASTRESULT3% -animGraphID %LASTRESULT1% -motionSetID %LASTRESULT2% -visualizeScale 1.000000\n" diff --git a/Gems/Prefab/PrefabBuilder/CMakeLists.txt b/Gems/Prefab/PrefabBuilder/CMakeLists.txt index 450b8d0408..9aedbd3d99 100644 --- a/Gems/Prefab/PrefabBuilder/CMakeLists.txt +++ b/Gems/Prefab/PrefabBuilder/CMakeLists.txt @@ -13,6 +13,9 @@ endif() ly_add_target( NAME PrefabBuilder.Static STATIC NAMESPACE Gem + INCLUDE_DIRECTORIES + PRIVATE + . FILES_CMAKE prefabbuilder_files.cmake BUILD_DEPENDENCIES @@ -20,6 +23,9 @@ ly_add_target( AZ::AzCore AZ::AzToolsFramework AZ::AssetBuilderSDK + AZ::SceneCore + AZ::SceneData + 3rdParty::RapidJSON ) ly_add_target( @@ -35,7 +41,8 @@ ly_add_target( Gem::PrefabBuilder.Static ) -# the prefab builder only needs to be active in builders +# create an alias for the tool version +ly_create_alias(NAME PrefabBuilder.Tools NAMESPACE Gem TARGETS Gem::PrefabBuilder.Builders) # we automatically add this gem, if it is present, to all our known set of builder applications: ly_enable_gems(GEMS PrefabBuilder) diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp index 762f459d9d..2bf7026549 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace AZ::Prefab { @@ -22,7 +23,8 @@ namespace AZ::Prefab : Module() { m_descriptors.insert(m_descriptors.end(), { - PrefabBuilderComponent::CreateDescriptor() + PrefabBuilderComponent::CreateDescriptor(), + AZ::SceneAPI::Behaviors::PrefabGroupBehavior::CreateDescriptor() }); } }; diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h index 3aea173ba4..2aec317e69 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace UnitTest { diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h new file mode 100644 index 0000000000..c2f8313b95 --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h @@ -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 + * + */ +#pragma once + +#include +#include +#include + +namespace AZ::SceneAPI::DataTypes +{ + class IPrefabGroup + : public ISceneNodeGroup + { + public: + AZ_RTTI(IPrefabGroup, "{7E50FAEF-3379-4521-99C5-B428FDEE3B7B}", ISceneNodeGroup); + + ~IPrefabGroup() override = default; + virtual AzToolsFramework::Prefab::PrefabDomConstReference GetPrefabDomRef() const = 0; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp new file mode 100644 index 0000000000..856aba979b --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp @@ -0,0 +1,161 @@ +/* + * 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 UnitTest +{ + class PrefabBehaviorTests + : public PrefabBuilderTests + { + public: + static void SetUpTestCase() + { + // Allocator needed by SceneCore + if (!AZ::AllocatorInstance().IsReady()) + { + AZ::AllocatorInstance().Create(); + } + AZ::SceneAPI::SceneCoreStandaloneAllocator::Initialize(AZ::Environment::GetInstance()); + } + + static void TearDownTestCase() + { + AZ::SceneAPI::SceneCoreStandaloneAllocator::TearDown(); + AZ::AllocatorInstance().Destroy(); + } + + void SetUp() override + { + PrefabBuilderTests::SetUp(); + m_prefabGroupBehavior = AZStd::make_unique(); + m_prefabGroupBehavior->Activate(); + + // Mocking the asset system replacing the AssetSystem::AssetSystemComponent + AZ::Entity* systemEntity = m_app.FindEntity(AZ::SystemEntityId); + systemEntity->FindComponent()->Deactivate(); + using namespace testing; + ON_CALL(m_assetSystemRequestMock, GetSourceInfoBySourcePath(_, _, _)).WillByDefault([](auto* path, auto& info, auto&) + { + return PrefabBehaviorTests::OnGetSourceInfoBySourcePath(path, info); + }); + m_assetSystemRequestMock.BusConnect(); + } + + void TearDown() override + { + m_assetSystemRequestMock.BusDisconnect(); + + m_prefabGroupBehavior->Deactivate(); + m_prefabGroupBehavior.reset(); + + PrefabBuilderTests::TearDown(); + } + + static bool OnGetSourceInfoBySourcePath(AZStd::string_view sourcePath, AZ::Data::AssetInfo& assetInfo) + { + if (sourcePath == AZStd::string_view("mock")) + { + assetInfo.m_assetId = AZ::Uuid::CreateRandom(); + assetInfo.m_assetType = azrtti_typeid(); + assetInfo.m_relativePath = "mock/path"; + assetInfo.m_sizeBytes = 0; + } + return true; + } + + struct TestPreExportEventContext + { + TestPreExportEventContext() + : m_scene("test_context") + { + using namespace AZ::SceneAPI::Events; + m_preExportEventContext = AZStd::make_unique(m_productList, m_outputDirectory, m_scene, "mock"); + } + + void SetOutputDirectory(AZStd::string outputDirectory) + { + using namespace AZ::SceneAPI::Events; + m_outputDirectory = AZStd::move(outputDirectory); + m_preExportEventContext = AZStd::make_unique(m_productList, m_outputDirectory, m_scene, "mock"); + } + + AZStd::unique_ptr m_preExportEventContext; + AZ::SceneAPI::Events::ExportProductList m_productList; + AZStd::string m_outputDirectory; + AZ::SceneAPI::Containers::Scene m_scene; + }; + + AZStd::unique_ptr m_prefabGroupBehavior; + testing::NiceMock m_assetSystemRequestMock; + }; + + TEST_F(PrefabBehaviorTests, PrefabBehavior_EmptyContextIgnored_Works) + { + auto context = TestPreExportEventContext{}; + + auto result = AZ::SceneAPI::Events::ProcessingResult::Failure; + AZ::SceneAPI::Events::CallProcessorBus::BroadcastResult( + result, + &AZ::SceneAPI::Events::CallProcessorBus::Events::Process, + context.m_preExportEventContext.get()); + + EXPECT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Ignored); + } + + TEST_F(PrefabBehaviorTests, PrefabBehavior_SimplePrefab_Works) + { + auto context = TestPreExportEventContext{}; + + // check for the file at /mock/fake_prefab.procprefab + AZ::Test::ScopedAutoTempDirectory tempDir; + context.SetOutputDirectory(tempDir.GetDirectory()); + + auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(Data::jsonPrefab); + ASSERT_TRUE(jsonOutcome); + + auto prefabGroup = AZStd::make_shared(); + prefabGroup.get()->SetId(AZ::Uuid::CreateRandom()); + prefabGroup.get()->SetName("fake_prefab"); + prefabGroup.get()->SetPrefabDom(AZStd::move(jsonOutcome.GetValue())); + context.m_scene.GetManifest().AddEntry(prefabGroup); + context.m_scene.SetSource("mock", AZ::Uuid::CreateRandom()); + + auto result = AZ::SceneAPI::Events::ProcessingResult::Failure; + AZ::SceneAPI::Events::CallProcessorBus::BroadcastResult( + result, + &AZ::SceneAPI::Events::CallProcessorBus::Events::Process, + context.m_preExportEventContext.get()); + + EXPECT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Success); + + AZStd::string pathStr; + AzFramework::StringFunc::Path::ConstructFull(tempDir.GetDirectory(), "mock/fake_prefab.procprefab", pathStr, true); + if (!AZ::IO::SystemFile::Exists(pathStr.c_str())) + { + AZ_Warning("testing", false, "The product asset (%s) is missing", pathStr.c_str()); + } + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl new file mode 100644 index 0000000000..b39f782a1e --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl @@ -0,0 +1,104 @@ +/* + * 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 UnitTest +{ + namespace Data + { + const char* jsonPrefab = R"JSON( + { + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "test_template_1", + "Components": { + "Component_[12122553907433030840]": { + "$type": "EditorVisibilityComponent", + "Id": 12122553907433030840 + }, + "Component_[5666150279650800686]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 5666150279650800686, + "Parent Entity": "" + }, + "Component_[8790726658974076423]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8790726658974076423 + } + } + }, + "Entities": { + "Entity_[1588652751483]": { + "Id": "Entity_[1588652751483]", + "Name": "root", + "Components": { + "Component_[11872748096995986607]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 11872748096995986607, + "Parent Entity": "ContainerEntity", + "Transform Data": { + "Rotate": [ + 0.0, + 0.10000000149011612, + 180.0 + ] + } + }, + "Component_[12138841758570858610]": { + "$type": "EditorVisibilityComponent", + "Id": 12138841758570858610 + }, + "Component_[15735658354806796004]": { + "$type": "EditorOnlyEntityComponent", + "Id": 15735658354806796004 + } + } + }, + "Entity_[1592947718779]": { + "Id": "Entity_[1592947718779]", + "Name": "cube", + "Components": { + "Component_[2505301170249328189]": { + "$type": "EditorOnlyEntityComponent", + "Id": 2505301170249328189 + }, + "Component_[3716170894544198343]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 3716170894544198343, + "Parent Entity": "Entity_[1588652751483]" + }, + "Component_[5862175558847453681]": { + "$type": "EditorVisibilityComponent", + "Id": 5862175558847453681 + } + } + }, + "Entity_[1597242686075]": { + "Id": "Entity_[1597242686075]", + "Name": "cubeKid", + "Components": { + "Component_[10128771992421174485]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 10128771992421174485, + "Parent Entity": "Entity_[1592947718779]" + }, + "Component_[14936165953779771344]": { + "$type": "EditorVisibilityComponent", + "Id": 14936165953779771344 + }, + "Component_[403416213715997356]": { + "$type": "EditorOnlyEntityComponent", + "Id": 403416213715997356 + } + } + } + } + } + )JSON"; + + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp new file mode 100644 index 0000000000..f4dd37b86c --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::SceneAPI::SceneData +{ + // PrefabGroup + + PrefabGroup::PrefabGroup() + : m_id(Uuid::CreateNull()) + , m_name() + { + } + + const AZStd::string& PrefabGroup::GetName() const + { + return m_name; + } + + void PrefabGroup::SetName(AZStd::string name) + { + m_name = AZStd::move(name); + } + + const Uuid& PrefabGroup::GetId() const + { + return m_id; + } + + void PrefabGroup::SetId(Uuid id) + { + m_id = AZStd::move(id); + } + + Containers::RuleContainer& PrefabGroup::GetRuleContainer() + { + return m_rules; + } + + const Containers::RuleContainer& PrefabGroup::GetRuleContainerConst() const + { + return m_rules; + } + + DataTypes::ISceneNodeSelectionList& PrefabGroup::GetSceneNodeSelectionList() + { + return m_nodeSelectionList; + } + + const DataTypes::ISceneNodeSelectionList& PrefabGroup::GetSceneNodeSelectionList() const + { + return m_nodeSelectionList; + } + + void PrefabGroup::SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom) + { + m_prefabDomData = AZStd::make_shared(); + m_prefabDomData->CopyValue(prefabDom); + } + + AzToolsFramework::Prefab::PrefabDomConstReference PrefabGroup::GetPrefabDomRef() const + { + if (m_prefabDomData) + { + return m_prefabDomData->GetValue(); + } + return {}; + } + + void PrefabGroup::Reflect(ReflectContext* context) + { + SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + serializeContext->Class() + ->Version(1) + ->Field("name", &PrefabGroup::m_name) + ->Field("nodeSelectionList", &PrefabGroup::m_nodeSelectionList) + ->Field("rules", &PrefabGroup::m_rules) + ->Field("id", &PrefabGroup::m_id) + ->Field("prefabDomData", &PrefabGroup::m_prefabDomData); + } + + BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + auto setPrefabDomData = [](PrefabGroup& self, const AZStd::string& json) + { + auto jsonOutcome = JsonSerializationUtils::ReadJsonString(json); + if (jsonOutcome.IsSuccess()) + { + self.SetPrefabDom(AZStd::move(jsonOutcome.GetValue())); + return true; + } + AZ_Error("prefab", false, "Set PrefabDom failed (%s)", jsonOutcome.GetError().c_str()); + return false; + }; + + auto getPrefabDomData = [](const PrefabGroup& self) -> AZStd::string + { + if (self.GetPrefabDomRef().has_value() == false) + { + return {}; + } + AZStd::string buffer; + JsonSerializationUtils::WriteJsonString(self.GetPrefabDomRef().value(), buffer); + return buffer; + }; + + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(Script::Attributes::Scope, Script::Attributes::ScopeFlags::Common) + ->Attribute(Script::Attributes::Module, "prefab") + ->Property("name", BehaviorValueProperty(&PrefabGroup::m_name)) + ->Property("id", BehaviorValueProperty(&PrefabGroup::m_id)) + ->Property("prefabDomData", getPrefabDomData, setPrefabDomData); + } + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h new file mode 100644 index 0000000000..e5c47186eb --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h @@ -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. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class ReflectContext; +} + +namespace AZ::SceneAPI::Containers +{ + class Scene; +} + +namespace AZ::SceneAPI::SceneData +{ + class PrefabGroup final + : public DataTypes::IPrefabGroup + { + public: + AZ_RTTI(PrefabGroup, "{99FE3C6F-5B55-4D8B-8013-2708010EC715}", DataTypes::IPrefabGroup); + AZ_CLASS_ALLOCATOR(PrefabGroup, SystemAllocator, 0); + + static void Reflect(AZ::ReflectContext* context); + + PrefabGroup(); + ~PrefabGroup() override = default; + + // DataTypes::IPrefabGroup + AzToolsFramework::Prefab::PrefabDomConstReference GetPrefabDomRef() const override; + const AZStd::string& GetName() const override; + const Uuid& GetId() const override; + Containers::RuleContainer& GetRuleContainer() override; + const Containers::RuleContainer& GetRuleContainerConst() const override; + DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() override; + const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() const override; + + // Concrete API + void SetId(Uuid id); + void SetName(AZStd::string name); + void SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom); + + private: + SceneNodeSelectionList m_nodeSelectionList; + Containers::RuleContainer m_rules; + AZStd::string m_name; + Uuid m_id; + AZStd::shared_ptr m_prefabDomData; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp new file mode 100644 index 0000000000..44a95c1b6b --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp @@ -0,0 +1,237 @@ +/* + * 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 + +namespace AZ::SceneAPI::Behaviors +{ + // + // ExportEventHandler + // + + struct PrefabGroupBehavior::ExportEventHandler final + : public AZ::SceneAPI::SceneCore::ExportingComponent + { + using PreExportEventContextFunction = AZStd::function; + PreExportEventContextFunction m_preExportEventContextFunction; + AZ::Prefab::PrefabGroupAssetHandler m_prefabGroupAssetHandler; + + ExportEventHandler() = delete; + + ExportEventHandler(PreExportEventContextFunction function) + : m_preExportEventContextFunction(AZStd::move(function)) + { + BindToCall(&ExportEventHandler::PrepareForExport); + AZ::SceneAPI::SceneCore::ExportingComponent::Activate(); + } + + ~ExportEventHandler() + { + AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate(); + } + + Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context) + { + return m_preExportEventContextFunction(context); + } + }; + + // + // PrefabGroupBehavior + // + + void PrefabGroupBehavior::Activate() + { + m_exportEventHandler = AZStd::make_shared([this](auto& context) + { + return this->OnPrepareForExport(context); + }); + } + + void PrefabGroupBehavior::Deactivate() + { + m_exportEventHandler.reset(); + } + + AZStd::unique_ptr PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const + { + using namespace AzToolsFramework::Prefab; + + auto* prefabLoaderInterface = AZ::Interface::Get(); + if (!prefabLoaderInterface) + { + AZ_Error("prefab", false, "Could not get PrefabLoaderInterface"); + return {}; + } + + // write to a UTF-8 string buffer + auto prefabDomRef = prefabGroup->GetPrefabDomRef(); + if (!prefabDomRef) + { + AZ_Error("prefab", false, "PrefabGroup(%s) missing PrefabDom", prefabGroup->GetName().c_str()); + return {}; + } + + const AzToolsFramework::Prefab::PrefabDom& prefabDom = prefabDomRef.value(); + rapidjson::StringBuffer sb; + rapidjson::Writer> writer(sb); + if (prefabDom.Accept(writer) == false) + { + AZ_Error("prefab", false, "Could not write PrefabGroup(%s) to JSON", prefabGroup->GetName().c_str()); + return {}; + } + + // validate the PrefabDom will make a valid Prefab template instance + auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), prefabGroup->GetName().c_str()); + if (templateId == InvalidTemplateId) + { + AZ_Error("prefab", false, "PrefabGroup(%s) Could not write load template", prefabGroup->GetName().c_str()); + return {}; + } + + auto* prefabSystemComponentInterface = AZ::Interface::Get(); + if (!prefabSystemComponentInterface) + { + AZ_Error("prefab", false, "Could not get PrefabSystemComponentInterface"); + return {}; + } + + const rapidjson::Document& generatedInstanceDom = prefabSystemComponentInterface->FindTemplateDom(templateId); + auto proceduralPrefab = AZStd::make_unique(rapidjson::kObjectType); + proceduralPrefab->CopyFrom(generatedInstanceDom, proceduralPrefab->GetAllocator(), true); + + return proceduralPrefab; + } + + bool PrefabGroupBehavior::WriteOutProductAsset( + Events::PreExportEventContext& context, + const SceneData::PrefabGroup* prefabGroup, + const rapidjson::Document& doc) const + { + // Retrieve source asset info so we can get a string with the relative path to the asset + bool assetInfoResult; + Data::AssetInfo info; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + assetInfoResult, + &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, + context.GetScene().GetSourceFilename().c_str(), + info, + watchFolder); + + AZ::IO::FixedMaxPath assetPath(info.m_relativePath); + assetPath.ReplaceFilename(prefabGroup->GetName().c_str()); + + AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName( + assetPath.c_str(), + context.GetOutputDirectory(), + AZ::Prefab::PrefabGroupAssetHandler::s_Extension); + + AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite); + if (fileStream.IsOpen() == false) + { + AZ_Error("prefab", false, "File path(%s) could not open for write", filePath.c_str()); + return false; + } + + // write to a UTF-8 string buffer + rapidjson::StringBuffer sb; + rapidjson::Writer> writer(sb); + if (doc.Accept(writer) == false) + { + AZ_Error("prefab", false, "PrefabGroup(%s) Could not buffer JSON", prefabGroup->GetName().c_str()); + return false; + } + + const auto bytesWritten = fileStream.Write(sb.GetSize(), sb.GetString()); + if (bytesWritten > 1) + { + AZ::u32 subId = AZ::Crc32(filePath.c_str()); + context.GetProductList().AddProduct( + filePath, + context.GetScene().GetSourceGuid(), + azrtti_typeid(), + {}, + AZStd::make_optional(subId)); + + return true; + } + return false; + } + + Events::ProcessingResult PrefabGroupBehavior::OnPrepareForExport(Events::PreExportEventContext& context) const + { + AZStd::vector prefabGroupCollection; + const Containers::SceneManifest& manifest = context.GetScene().GetManifest(); + + for (size_t i = 0; i < manifest.GetEntryCount(); ++i) + { + const auto* group = azrtti_cast(manifest.GetValue(i).get()); + if (group) + { + prefabGroupCollection.push_back(group); + } + } + + if (prefabGroupCollection.empty()) + { + return AZ::SceneAPI::Events::ProcessingResult::Ignored; + } + + for (const auto* prefabGroup : prefabGroupCollection) + { + auto result = CreateProductAssetData(prefabGroup); + if (!result) + { + return Events::ProcessingResult::Failure; + } + + if (WriteOutProductAsset(context, prefabGroup, *result.get()) == false) + { + return Events::ProcessingResult::Failure; + } + } + + return Events::ProcessingResult::Success; + } + + void PrefabGroupBehavior::Reflect(ReflectContext* context) + { + AZ::SceneAPI::SceneData::PrefabGroup::Reflect(context); + Prefab::ProceduralPrefabAsset::Reflect(context); + + SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class()->Version(1); + } + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h new file mode 100644 index 0000000000..b57d30695a --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h @@ -0,0 +1,55 @@ +/* + * 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 + +namespace AZ +{ + class ReflectContext; +} + +namespace AZ::SceneAPI::Events +{ + class PreExportEventContext; +} + +namespace AZ::SceneAPI::Behaviors +{ + class PrefabGroupBehavior + : public SceneCore::BehaviorComponent + { + public: + AZ_COMPONENT(PrefabGroupBehavior, "{13DC2819-CAC2-4977-91D7-C870087072AB}", SceneCore::BehaviorComponent); + + ~PrefabGroupBehavior() override = default; + + void Activate() override; + void Deactivate() override; + static void Reflect(ReflectContext* context); + + private: + Events::ProcessingResult OnPrepareForExport(Events::PreExportEventContext& context) const; + AZStd::unique_ptr CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const; + + bool WriteOutProductAsset( + Events::PreExportEventContext& context, + const SceneData::PrefabGroup* prefabGroup, + const rapidjson::Document& doc) const; + + struct ExportEventHandler; + AZStd::shared_ptr m_exportEventHandler; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp new file mode 100644 index 0000000000..9fb935ce9f --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp @@ -0,0 +1,161 @@ +/* + * 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 UnitTest +{ + TEST_F(PrefabBuilderTests, PrefabGroup_FindsRequiredReflection_True) + { + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + + auto findElementWithName = [](const AZ::SerializeContext::ClassData* classData, const char* name) + { + auto it = AZStd::find_if(classData->m_elements.begin(), classData->m_elements.end(), [name](const auto& element) + { + return strcmp(element.m_name, name) == 0; + }); + return it != classData->m_elements.end(); + }; + + auto* prefabGroupClassData = serializeContext->FindClassData(azrtti_typeid()); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "name")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "nodeSelectionList")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "rules")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "id")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "prefabDomData")); + + m_app.GetJsonRegistrationContext()->EnableRemoveReflection(); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_JsonWithPrefabArbitraryPrefab_Works) + { + namespace JSR = AZ::JsonSerializationResult; + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + AZ::Prefab::ProceduralPrefabAsset::Reflect(serializeContext); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + AZ::Prefab::ProceduralPrefabAsset::Reflect(m_app.GetJsonRegistrationContext()); + + // fill out a PrefabGroup using JSON + AZStd::string_view input = R"JSON( + { + "name" : "tester", + "id" : "{49698DBC-B447-49EF-9B56-25BB29342AFB}", + "prefabDomData" : {"foo": "bar"} + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + ASSERT_FALSE(document.HasParseError()); + + SceneData::PrefabGroup instancePrefabGroup; + EXPECT_EQ(AZ::JsonSerialization::Load(instancePrefabGroup, document).GetOutcome(), JSR::Outcomes::PartialDefaults); + + ASSERT_TRUE(instancePrefabGroup.GetPrefabDomRef().has_value()); + const AzToolsFramework::Prefab::PrefabDom& dom = instancePrefabGroup.GetPrefabDomRef().value(); + EXPECT_TRUE(dom.IsObject()); + EXPECT_TRUE(dom.GetObject().HasMember("foo")); + EXPECT_STREQ(dom.GetObject().FindMember("foo")->value.GetString(), "bar"); + EXPECT_STREQ(instancePrefabGroup.GetName().c_str(), "tester"); + EXPECT_STREQ(instancePrefabGroup.GetId().ToString().c_str(), "{49698DBC-B447-49EF-9B56-25BB29342AFB}"); + + m_app.GetJsonRegistrationContext()->EnableRemoveReflection(); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + AZ::Prefab::ProceduralPrefabAsset::Reflect(m_app.GetJsonRegistrationContext()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJson_Detected) + { + using namespace AZ::SceneAPI; + + AZStd::string_view input = R"JSON( + { + bad json that will not parse + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + + SceneData::PrefabGroup prefabGroup; + prefabGroup.SetId(AZ::Uuid::CreateRandom()); + prefabGroup.SetName("tester"); + prefabGroup.SetPrefabDom(AZStd::move(document)); + + const AzToolsFramework::Prefab::PrefabDom& dom = prefabGroup.GetPrefabDomRef().value(); + EXPECT_TRUE(dom.IsNull()); + EXPECT_STREQ("tester", prefabGroup.GetName().c_str()); + } + + struct PrefabBuilderBehaviorTests + : public PrefabBuilderTests + { + void SetUp() override + { + using namespace AZ::SceneAPI; + + PrefabBuilderTests::SetUp(); + SceneData::PrefabGroup::Reflect(m_app.GetSerializeContext()); + SceneData::PrefabGroup::Reflect(m_app.GetBehaviorContext()); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + m_scriptContext = AZStd::make_unique(); + m_scriptContext->BindTo(m_app.GetBehaviorContext()); + } + + void TearDown() override + { + using namespace AZ::SceneAPI; + m_app.GetJsonRegistrationContext()->EnableRemoveReflection(); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + + m_scriptContext.reset(); + PrefabBuilderTests::TearDown(); + } + + void ExpectExecute(AZStd::string_view script) + { + EXPECT_TRUE(m_scriptContext->Execute(script.data())); + } + + AZStd::unique_ptr m_scriptContext; + }; + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupClass_Exists) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("assert(group)"); + ExpectExecute("assert(group.name)"); + ExpectExecute("assert(group.id)"); + ExpectExecute("assert(group.prefabDomData)"); + } + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupAssignment_Works) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("group.name = 'tester'"); + ExpectExecute("group.id = Uuid.CreateString('{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}', 0)"); + ExpectExecute("group.prefabDomData = '{\"foo\": \"bar\"}'"); + ExpectExecute("assert(group.name == 'tester')"); + ExpectExecute("assert(tostring(group.id) == '{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}')"); + ExpectExecute("assert(group.prefabDomData == '{\\n \"foo\": \"bar\"\\n}')"); + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp new file mode 100644 index 0000000000..17ed4d9c0c --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp @@ -0,0 +1,185 @@ +/* + * 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 AZ::Prefab +{ + // AssetTypeInfoHandler + + class PrefabGroupAssetHandler::AssetTypeInfoHandler final + : public AZ::AssetTypeInfoBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(AssetTypeInfoHandler, AZ::SystemAllocator, 0); + AssetTypeInfoHandler(); + ~AssetTypeInfoHandler() override; + AZ::Data::AssetType GetAssetType() const override; + const char* GetAssetTypeDisplayName() const override; + const char* GetGroup() const override; + const char* GetBrowserIcon() const override; + void GetAssetTypeExtensions(AZStd::vector& extensions) override; + }; + + PrefabGroupAssetHandler::AssetTypeInfoHandler::AssetTypeInfoHandler() + { + AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid()); + } + + PrefabGroupAssetHandler::AssetTypeInfoHandler::~AssetTypeInfoHandler() + { + AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid()); + } + + AZ::Data::AssetType PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetType() const + { + return azrtti_typeid(); + } + + const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetTypeDisplayName() const + { + return "Procedural Prefab"; + } + + const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetGroup() const + { + return "Prefab"; + } + + const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetBrowserIcon() const + { + return "Icons/Components/Box.png"; + } + + void PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetTypeExtensions(AZStd::vector& extensions) + { + extensions.push_back(PrefabGroupAssetHandler::s_Extension); + } + + // PrefabGroupAssetHandler + + AZStd::string_view PrefabGroupAssetHandler::s_Extension{ "procprefab" }; + + PrefabGroupAssetHandler::PrefabGroupAssetHandler() + { + auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); + if (assetCatalog) + { + assetCatalog->EnableCatalogForAsset(azrtti_typeid()); + assetCatalog->AddExtension(s_Extension.data()); + } + if (AZ::Data::AssetManager::IsReady()) + { + AZ::Data::AssetManager::Instance().RegisterHandler(this, azrtti_typeid()); + } + m_assetTypeInfoHandler = AZStd::make_shared(); + } + + PrefabGroupAssetHandler::~PrefabGroupAssetHandler() + { + m_assetTypeInfoHandler.reset(); + if (AZ::Data::AssetManager::IsReady()) + { + AZ::Data::AssetManager::Instance().UnregisterHandler(this); + } + } + + AZ::Data::AssetData* PrefabGroupAssetHandler::CreateAsset([[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) + { + if (type != azrtti_typeid()) + { + AZ_Error("prefab", false, "Invalid asset type! Only handle 'ProceduralPrefabAsset'"); + return nullptr; + } + return aznew ProceduralPrefabAsset{}; + } + + void PrefabGroupAssetHandler::DestroyAsset(AZ::Data::AssetData* ptr) + { + // Note: the PrefabLoaderInterface will handle the lifetime of the Prefab Template + delete ptr; + } + + void PrefabGroupAssetHandler::GetHandledAssetTypes(AZStd::vector& assetTypes) + { + assetTypes.push_back(azrtti_typeid()); + } + + AZ::Data::AssetHandler::LoadResult PrefabGroupAssetHandler::LoadAssetData( + const AZ::Data::Asset& asset, + AZStd::shared_ptr stream, + [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) + { + using namespace AzToolsFramework::Prefab; + + auto* proceduralPrefabAsset = asset.GetAs(); + if (!proceduralPrefabAsset) + { + AZ_Error("prefab", false, "This should be a ProceduralPrefabAsset type, as this is the only type we process!"); + return LoadResult::Error; + } + + AZStd::string buffer; + buffer.resize(stream->GetLoadedSize()); + stream->Read(stream->GetLoadedSize(), buffer.data()); + + auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(buffer); + if (jsonOutcome.IsSuccess() == false) + { + AZ_Error("prefab", false, "Asset JSON failed to compile %s", jsonOutcome.GetError().c_str()); + return LoadResult::Error; + } + const auto& jsonDoc = jsonOutcome.GetValue(); + + if (jsonDoc.IsObject() == false) + { + return LoadResult::Error; + } + + if (jsonDoc.FindMember("Source") == jsonDoc.MemberEnd()) + { + return LoadResult::Error; + } + const auto& templateName = jsonDoc["Source"]; + + AZStd::string stringJson; + auto stringOutcome = AZ::JsonSerializationUtils::WriteJsonString(jsonDoc, stringJson); + if (stringOutcome.IsSuccess() == false) + { + AZ_Error("prefab", false, "Could not write to JSON string %s", stringOutcome.GetError().c_str()); + return LoadResult::Error; + } + + // prepare the template + auto* prefabLoaderInterface = AZ::Interface::Get(); + if (!prefabLoaderInterface) + { + return LoadResult::Error; + } + + auto templateId = prefabLoaderInterface->LoadTemplateFromString(stringJson.data(), templateName.GetString()); + if (templateId == InvalidTemplateId) + { + return LoadResult::Error; + } + + proceduralPrefabAsset->SetTemplateId(templateId); + proceduralPrefabAsset->SetTemplateName(templateName.GetString()); + return LoadResult::LoadComplete; + } + + AZStd::unique_ptr s_PrefabGroupAssetHandler; +} + diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h new file mode 100644 index 0000000000..301f8a467a --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.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 +#include +#include + +namespace AZ::Prefab +{ + class PrefabGroupAssetHandler final + : public AZ::Data::AssetHandler + { + public: + AZ_CLASS_ALLOCATOR(PrefabGroupAssetHandler, AZ::SystemAllocator, 0); + PrefabGroupAssetHandler(); + ~PrefabGroupAssetHandler() override; + + static AZStd::string_view s_Extension; + + protected: + AZ::Data::AssetData* CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override; + void DestroyAsset(AZ::Data::AssetData* ptr) override; + void GetHandledAssetTypes(AZStd::vector& assetTypes) override; + AZ::Data::AssetHandler::LoadResult LoadAssetData( + const AZ::Data::Asset& asset, + AZStd::shared_ptr stream, + const AZ::Data::AssetFilterCB& assetLoadFilterCB) override; + + class AssetTypeInfoHandler; + AZStd::shared_ptr m_assetTypeInfoHandler; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp new file mode 100644 index 0000000000..a63c0caa69 --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp @@ -0,0 +1,162 @@ +/* + * 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 + +namespace UnitTest +{ + TEST_F(PrefabBuilderTests, PrefabGroup_FindsRequiredReflection_True) + { + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + + auto findElementWithName = [](const AZ::SerializeContext::ClassData* classData, const char* name) + { + auto it = AZStd::find_if(classData->m_elements.begin(), classData->m_elements.end(), [name](const auto& element) + { + return strcmp(element.m_name, name) == 0; + }); + return it != classData->m_elements.end(); + }; + + auto* prefabGroupClassData = serializeContext->FindClassData(azrtti_typeid()); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "name")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "nodeSelectionList")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "rules")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "id")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "prefabDomBuffer")); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_JsonWithPrefabArbitraryPrefab_Works) + { + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + + // fill out a PrefabGroup using JSON + AZStd::string_view input = R"JSON( + { + "name" : "tester", + "id" : "{49698DBC-B447-49EF-9B56-25BB29342AFB}", + "prefabDomBuffer" : "{\"foo\":\"bar\"}" + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + ASSERT_FALSE(document.HasParseError()); + + SceneData::PrefabGroup instancePrefabGroup; + AZ::JsonSerialization::Load(instancePrefabGroup, document); + + const auto& dom = instancePrefabGroup.GetPrefabDom(); + EXPECT_TRUE(dom.GetObject().HasMember("foo")); + EXPECT_STREQ(dom.GetObject().FindMember("foo")->value.GetString(), "bar"); + EXPECT_STREQ(instancePrefabGroup.GetName().c_str(), "tester"); + EXPECT_STREQ( + instancePrefabGroup.GetId().ToString().c_str(), + "{49698DBC-B447-49EF-9B56-25BB29342AFB}"); + EXPECT_TRUE(instancePrefabGroup.GetPrefabDom().IsObject()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJson_Detected) + { + using namespace AZ::SceneAPI; + + AZStd::string_view input = R"JSON( + { + bad json that will not parse + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + + SceneData::PrefabGroup prefabGroup; + prefabGroup.SetId(AZStd::move(AZ::Uuid::CreateRandom())); + prefabGroup.SetName(AZStd::move("tester")); + prefabGroup.SetPrefabDom(AZStd::move(document)); + + const auto& dom = prefabGroup.GetPrefabDom(); + EXPECT_TRUE(dom.IsNull()); + EXPECT_STREQ("tester", prefabGroup.GetName().c_str()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJsonBuffer_Detected) + { + using namespace AZ::SceneAPI; + + AZStd::string_view inputJson = R"JSON( + { + bad json that will not parse + })JSON"; + + SceneData::PrefabGroup prefabGroup; + prefabGroup.SetId(AZStd::move(AZ::Uuid::CreateRandom())); + prefabGroup.SetName(AZStd::move("tester")); + prefabGroup.SetPrefabDomBuffer(std::move(inputJson)); + + const auto& dom = prefabGroup.GetPrefabDom(); + EXPECT_TRUE(dom.IsNull()); + EXPECT_STREQ("tester", prefabGroup.GetName().c_str()); + } + + struct PrefabBuilderBehaviorTests + : public PrefabBuilderTests + { + void SetUp() override + { + using namespace AZ::SceneAPI; + + PrefabBuilderTests::SetUp(); + SceneData::PrefabGroup::Reflect(m_app.GetSerializeContext()); + SceneData::PrefabGroup::Reflect(m_app.GetBehaviorContext()); + m_scriptContext = AZStd::make_unique(); + m_scriptContext->BindTo(m_app.GetBehaviorContext()); + } + + void TearDown() override + { + m_scriptContext.reset(); + PrefabBuilderTests::TearDown(); + } + + void ExpectExecute(AZStd::string_view script) + { + EXPECT_TRUE(m_scriptContext->Execute(script.data())); + } + + AZStd::unique_ptr m_scriptContext; + }; + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupClass_Exists) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("assert(group)"); + ExpectExecute("assert(group.name)"); + ExpectExecute("assert(group.id)"); + ExpectExecute("assert(group.prefabDomBuffer)"); + } + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupAssignment_Works) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("group.name = 'tester'"); + ExpectExecute("group.id = Uuid.CreateString('{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}', 0)"); + ExpectExecute("group.prefabDomBuffer = '{}'"); + ExpectExecute("assert(group.name == 'tester')"); + ExpectExecute("assert(tostring(group.id) == '{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}')"); + ExpectExecute("assert(group.prefabDomBuffer == '{}')"); + } +} diff --git a/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake b/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake index 9cdf90d951..4f3bf860be 100644 --- a/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake +++ b/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake @@ -9,4 +9,11 @@ set(FILES PrefabBuilderComponent.h PrefabBuilderComponent.cpp + PrefabGroup/IPrefabGroup.h + PrefabGroup/PrefabGroup.cpp + PrefabGroup/PrefabGroup.h + PrefabGroup/PrefabGroupBehavior.cpp + PrefabGroup/PrefabGroupBehavior.h + PrefabGroup/ProceduralAssetHandler.cpp + PrefabGroup/ProceduralAssetHandler.h ) diff --git a/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake b/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake index 3f1ae0388b..faf2f88b5e 100644 --- a/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake +++ b/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake @@ -9,4 +9,7 @@ set(FILES PrefabBuilderTests.h PrefabBuilderTests.cpp + PrefabGroup/PrefabGroupTests.cpp + PrefabGroup/PrefabBehaviorTests.cpp + PrefabGroup/PrefabBehaviorTests.inl ) diff --git a/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py b/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py index 2c63e5f90c..2578efeae2 100755 --- a/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py +++ b/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py @@ -104,6 +104,15 @@ class SceneManifest(): self.manifest['values'].append(meshGroup) return meshGroup + def add_prefab_group(self, name, id, json) -> dict: + prefabGroup = {} + prefabGroup['$type'] = '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup' + prefabGroup['name'] = name + prefabGroup['id'] = id + prefabGroup['prefabDomData'] = json + self.manifest['values'].append(prefabGroup) + return prefabGroup + def mesh_group_select_node(self, meshGroup, nodeName): meshGroup['nodeSelectionList']['selectedNodes'].append(nodeName) diff --git a/Gems/QtForPython/Code/Source/QtForPythonSystemComponent.cpp b/Gems/QtForPython/Code/Source/QtForPythonSystemComponent.cpp index a7bd193fd7..83a158dd18 100644 --- a/Gems/QtForPython/Code/Source/QtForPythonSystemComponent.cpp +++ b/Gems/QtForPython/Code/Source/QtForPythonSystemComponent.cpp @@ -202,9 +202,6 @@ namespace QtForPython QtBootstrapParameters QtForPythonSystemComponent::GetQtBootstrapParameters() const { QtBootstrapParameters params; - - char devroot[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devroot@", devroot, AZ_MAX_PATH_LEN); #if !defined(Q_OS_WIN) #error Unsupported OS platform for this QtForPython gem diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp index c1a1252c6b..1d3cd60e49 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,9 @@ #include #include +#include +#include +#include #include #include @@ -81,6 +85,93 @@ namespace SceneBuilder return m_cachedFingerprint.c_str(); } + void SceneBuilderWorker::PopulateSourceDependencies(const AZStd::string& manifestJson, AZStd::vector& sourceFileDependencies) + { + auto readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(manifestJson); + AZStd::string errorMsg; + if (!readJsonOutcome.IsSuccess()) + { + // This may be an old format xml file. We don't have any dependencies in the old format so there's no point trying to parse an xml + return; + } + + rapidjson::Document document = readJsonOutcome.TakeValue(); + + auto manifestObject = document.GetObject(); + auto valuesIterator = manifestObject.FindMember("values"); + auto valuesArray = valuesIterator->value.GetArray(); + + AZStd::vector paths; + AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast( + &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestDependencyPaths, paths); + + for (const auto& value : valuesArray) + { + auto object = value.GetObject(); + + for (const auto& path : paths) + { + rapidjson::Pointer pointer(path.c_str()); + + auto dependencyValue = pointer.Get(object); + + if (dependencyValue && dependencyValue->IsString()) + { + AZStd::string dependency = dependencyValue->GetString(); + + sourceFileDependencies.emplace_back(AssetBuilderSDK::SourceFileDependency( + dependency, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute)); + } + } + } + } + + bool SceneBuilderWorker::ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) + { + AZStd::string manifestExtension; + AZStd::string generatedManifestExtension; + + AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast( + &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestExtension, manifestExtension); + AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast( + &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetGeneratedManifestExtension, generatedManifestExtension); + + if (manifestExtension.empty() || generatedManifestExtension.empty()) + { + AZ_Error("SceneBuilderWorker", false, "Failed to get scene manifest extension"); + return false; + } + + AZ::SettingsRegistryInterface::FixedValueString assetCacheRoot; + AZ::SettingsRegistry::Get()->Get(assetCacheRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder); + + auto manifestPath = (AZ::IO::Path(request.m_watchFolder) / (request.m_sourceFile + manifestExtension)); + auto generatedManifestPath = (AZ::IO::Path(assetCacheRoot) / (request.m_sourceFile + generatedManifestExtension)); + + auto populateDependenciesFunc = [&response](const AZStd::string& path) + { + auto readFileOutcome = AZ::Utils::ReadFile(path, AZ::SceneAPI::Containers::SceneManifest::MaxSceneManifestFileSizeInBytes); + if (!readFileOutcome.IsSuccess()) + { + AZ_Error("SceneBuilderWorker", false, "%s", readFileOutcome.GetError().c_str()); + return; + } + + PopulateSourceDependencies(readFileOutcome.TakeValue(), response.m_sourceFileDependencyList); + }; + + if (AZ::IO::FileIOBase::GetInstance()->Exists(manifestPath.Native().c_str())) + { + populateDependenciesFunc(manifestPath.Native()); + } + else if (AZ::IO::FileIOBase::GetInstance()->Exists(generatedManifestPath.Native().c_str())) + { + populateDependenciesFunc(generatedManifestPath.Native()); + } + + return true; + } + void SceneBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) { // Check for shutdown @@ -118,6 +209,11 @@ namespace SceneBuilder sourceFileDependencyInfo.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards; response.m_sourceFileDependencyList.push_back(sourceFileDependencyInfo); + if (!ManifestDependencyCheck(request, response)) + { + return; + } + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h index 3ecc657a3a..32456f9b2d 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace AssetBuilderSDK { @@ -45,11 +46,15 @@ namespace SceneBuilder public: ~SceneBuilderWorker() override = default; + void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response); void ShutDown() override; const char* GetFingerprint() const; + static void PopulateSourceDependencies( + const AZStd::string& manifestJson, AZStd::vector& sourceFileDependencies); + static bool ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); static AZ::Uuid GetUUID(); void PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const; diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp index a4da836aeb..ba1a186e5a 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp @@ -11,11 +11,14 @@ #include #include #include +#include #include #include #include +#include #include #include +#include using namespace AZ; using namespace SceneBuilder; @@ -36,13 +39,12 @@ protected: m_app.Start(AZ::ComponentApplication::Descriptor()); AZ::Debug::TraceMessageBus::Handler::BusConnect(); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); m_workingDirectory = m_app.GetExecutableFolder(); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", m_workingDirectory); - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", m_workingDirectory); + AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", m_workingDirectory); } void TearDown() override @@ -78,7 +80,7 @@ protected: } void TestSuccessCase(const SceneAPI::Events::ExportProduct& exportProduct, - const AssetBuilderSDK::ProductPathDependency* expectedPathDependency = nullptr, + const AssetBuilderSDK::ProductPathDependency* expectedPathDependency = nullptr, const AZ::Uuid* expectedProductDependency = nullptr) { AssetBuilderSDK::ProductPathDependencySet expectedPathDependencies; @@ -86,13 +88,13 @@ protected: { expectedPathDependencies.emplace(*expectedPathDependency); } - + AZStd::vector expectedProductDependencies; if (expectedProductDependency) { expectedProductDependencies.push_back(*expectedProductDependency); } - + TestSuccessCase(exportProduct, expectedPathDependencies, expectedProductDependencies); } @@ -121,7 +123,7 @@ TEST_F(SceneBuilderTests, SceneBuilderWorker_ExportProductDependencies_PathDepen const char* absolutePathToFile = "/some/test/file.mtl"; #endif // AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS AssetBuilderSDK::ProductPathDependency expectedPathDependency(absolutePathToFile, AssetBuilderSDK::ProductPathDependencyType::SourceFile); - + SceneAPI::Events::ExportProduct product("testExportFile", AZ::Uuid::CreateRandom(), AZ::Data::AssetType::CreateNull(), u8(0), AZStd::nullopt); product.m_legacyPathDependencies.push_back(absolutePathToFile); TestSuccessCase(product, &expectedPathDependency); @@ -133,7 +135,7 @@ TEST_F(SceneBuilderTests, SceneBuilderWorker_ExportProductDependencies_PathDepen const char* relativeDependencyPathToFile = "some/test/file.mtl"; AssetBuilderSDK::ProductPathDependency expectedPathDependency(relativeDependencyPathToFile, AssetBuilderSDK::ProductPathDependencyType::ProductFile); - + SceneAPI::Events::ExportProduct product("testExportFile", AZ::Uuid::CreateRandom(), AZ::Data::AssetType::CreateNull(), u8(0), AZStd::nullopt); product.m_legacyPathDependencies.push_back(relativeDependencyPathToFile); @@ -194,3 +196,200 @@ TEST_F(SceneBuilderTests, SceneBuilderWorker_ExportProductDependencies_ProductAn TestSuccessCase(exportProduct, expectedPathDependencies, { dependencyId }); } + +struct ImportHandler + : SceneAPI::Events::AssetImportRequestBus::Handler +{ + ImportHandler() + { + BusConnect(); + } + + ~ImportHandler() override + { + BusDisconnect(); + } + + void GetManifestDependencyPaths(AZStd::vector& paths) override + { + paths.emplace_back("/scriptFilename"); + paths.emplace_back("/layer1/layer2/0/target"); + } + + void GetManifestExtension(AZStd::string& result) override + { + result = ".test"; + } + + void GetGeneratedManifestExtension(AZStd::string& result) override + { + result = ".test.gen"; + } +}; + +using SourceDependencyTests = UnitTest::ScopedAllocatorSetupFixture; + +namespace SourceDependencyJson +{ + constexpr const char* TestJson = R"JSON( +{ + "values": [ + { + "$type": "Test1", + "scriptFilename": "a/test/path.png" + }, + { + "$type": "Test2", + "layer1" : { + "layer2" : [ + { + "target": "value.png", + "otherData": "value2.png" + }, + { + "target" : "wrong.png" + } + ] + } + } + ] +} + )JSON"; +} + +TEST_F(SourceDependencyTests, SourceDependencyTest) +{ + ImportHandler handler; + AZStd::vector dependencies; + + SceneBuilderWorker::PopulateSourceDependencies(SourceDependencyJson::TestJson, dependencies); + + ASSERT_EQ(dependencies.size(), 2); + ASSERT_STREQ(dependencies[0].m_sourceFileDependencyPath.c_str(), "a/test/path.png"); + ASSERT_STREQ(dependencies[1].m_sourceFileDependencyPath.c_str(), "value.png"); +} + +struct SettingsRegistryMock : AZ::Interface::Registrar +{ + bool Get(FixedValueString& result, AZStd::string_view) const override + { + result = "cache"; + return true; + } + + void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& /*applyPatchSettings*/) override{} + void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& /*applyPatchSettings*/) override{} + + MOCK_CONST_METHOD1(GetType, Type (AZStd::string_view)); + MOCK_CONST_METHOD2(Visit, bool (Visitor&, AZStd::string_view)); + MOCK_CONST_METHOD2(Visit, bool (const VisitorCallback&, AZStd::string_view)); + MOCK_METHOD1(RegisterNotifier, NotifyEventHandler (const NotifyCallback&)); + MOCK_METHOD1(RegisterNotifier, NotifyEventHandler (NotifyCallback&&)); + MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler (const PreMergeEventCallback&)); + MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler (PreMergeEventCallback&&)); + MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler (const PostMergeEventCallback&)); + MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler (PostMergeEventCallback&&)); + MOCK_CONST_METHOD2(Get, bool (bool&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (s64&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (u64&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (double&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (AZStd::string&, AZStd::string_view)); + MOCK_CONST_METHOD3(GetObject, bool (void*, Uuid, AZStd::string_view)); + MOCK_METHOD2(Set, bool (AZStd::string_view, bool)); + MOCK_METHOD2(Set, bool (AZStd::string_view, s64)); + MOCK_METHOD2(Set, bool (AZStd::string_view, u64)); + MOCK_METHOD2(Set, bool (AZStd::string_view, double)); + MOCK_METHOD2(Set, bool (AZStd::string_view, AZStd::string_view)); + MOCK_METHOD2(Set, bool (AZStd::string_view, const char*)); + MOCK_METHOD3(SetObject, bool (AZStd::string_view, const void*, Uuid)); + MOCK_METHOD1(Remove, bool (AZStd::string_view)); + MOCK_METHOD3(MergeCommandLineArgument, bool (AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&)); + MOCK_METHOD2(MergeSettings, bool (AZStd::string_view, Format)); + MOCK_METHOD4(MergeSettingsFile, bool (AZStd::string_view, Format, AZStd::string_view, AZStd::vector*)); + MOCK_METHOD5(MergeSettingsFolder, bool (AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector*)); + MOCK_METHOD1(SetUseFileIO, void (bool)); +}; + +struct SourceDependencyMockedIOTests : UnitTest::ScopedAllocatorSetupFixture + , UnitTest::SetRestoreFileIOBaseRAII +{ + SourceDependencyMockedIOTests() + : UnitTest::SetRestoreFileIOBaseRAII(m_ioMock) + { + + } + + void SetUp() override + { + using namespace ::testing; + + ON_CALL(m_ioMock, Open(_, _, _)) + .WillByDefault(Invoke( + [](auto, auto, IO::HandleType& handle) + { + handle = 1234; + return AZ::IO::Result(AZ::IO::ResultCode::Success); + })); + + ON_CALL(m_ioMock, Size(An(), _)).WillByDefault(Invoke([](auto, AZ::u64& size) + { + size = strlen(SourceDependencyJson::TestJson); + return AZ::IO::ResultCode::Success; + })); + + EXPECT_CALL(m_ioMock, Read(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](auto, void* buffer, auto, auto, AZ::u64* bytesRead) + { + memcpy(buffer, SourceDependencyJson::TestJson, strlen(SourceDependencyJson::TestJson)); + *bytesRead = strlen(SourceDependencyJson::TestJson); + return AZ::IO::ResultCode::Success; + })); + + EXPECT_CALL(m_ioMock, Close(_)).WillRepeatedly(Return(AZ::IO::ResultCode::Success)); + } + + IO::NiceFileIOBaseMock m_ioMock; +}; + +TEST_F(SourceDependencyMockedIOTests, RegularManifestHasPriority) +{ + ImportHandler handler; + SettingsRegistryMock settingsRegistry; + + AssetBuilderSDK::CreateJobsRequest request; + AssetBuilderSDK::CreateJobsResponse response; + + request.m_sourceFile = "file.fbx"; + + using namespace ::testing; + + AZStd::string genPath = AZStd::string("cache").append(1, AZ_TRAIT_OS_PATH_SEPARATOR).append("file.fbx.test.gen"); + + EXPECT_CALL(m_ioMock, Exists(StrEq("file.fbx.test"))).WillRepeatedly(Return(true)); + EXPECT_CALL(m_ioMock, Exists(StrEq(genPath.c_str()))).Times(Exactly(0)); + + ASSERT_TRUE(SceneBuilderWorker::ManifestDependencyCheck(request, response)); + ASSERT_EQ(response.m_sourceFileDependencyList.size(), 2); +} + +TEST_F(SourceDependencyMockedIOTests, GeneratedManifestTest) +{ + ImportHandler handler; + SettingsRegistryMock settingsRegistry; + + AssetBuilderSDK::CreateJobsRequest request; + AssetBuilderSDK::CreateJobsResponse response; + + request.m_sourceFile = "file.fbx"; + + using namespace ::testing; + + AZStd::string genPath = AZStd::string("cache").append(1, AZ_TRAIT_OS_PATH_SEPARATOR).append("file.fbx.test.gen"); + + EXPECT_CALL(m_ioMock, Exists(StrEq("file.fbx.test"))).WillRepeatedly(Return(false)); + EXPECT_CALL(m_ioMock, Exists(StrEq(genPath.c_str()))).WillRepeatedly(Return(true)); + + ASSERT_TRUE(SceneBuilderWorker::ManifestDependencyCheck(request, response)); + ASSERT_EQ(response.m_sourceFileDependencyList.size(), 2); +} diff --git a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp index 0853789b19..7d4cfec909 100644 --- a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp +++ b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp @@ -110,13 +110,12 @@ namespace ScriptCanvasBuilder const ScriptCanvas::Translation::Result translationResult = TranslateToLua(request); auto isSuccessOutcome = translationResult.IsSuccess(ScriptCanvas::Translation::TargetFlags::Lua); - if (!isSuccessOutcome.IsSuccess()) { return AZ::Failure(isSuccessOutcome.TakeError()); } - auto& translation = translationResult.m_translations.find(ScriptCanvas::Translation::TargetFlags::Lua)->second; + auto& translation = translationResult.m_translations.find(ScriptCanvas::Translation::TargetFlags::Lua)->second; AZ::Data::Asset asset; scriptAssetId.m_subId = AZ::ScriptAsset::CompiledAssetSubId; asset.Create(scriptAssetId); @@ -481,6 +480,7 @@ namespace ScriptCanvasBuilder buildEntity->Activate(); } + AZ_Assert(buildEntity->GetState() == AZ::Entity::State::Active, "build entity not active"); return sourceGraph; } 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/Components/GraphUpgrade.cpp b/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp index e87c82e186..0e5f11fa40 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp @@ -519,7 +519,7 @@ namespace ScriptCanvasEditor if (validationResults.HasErrors()) { - AZ::Interface::Get()->GraphNeedsManualUpgrade(sm->m_asset.GetId()); + sm->MarkError("Failed to Parse"); for (auto& err : validationResults.GetEvents()) { @@ -686,7 +686,7 @@ namespace ScriptCanvasEditor void EditorGraphUpgradeMachine::OnComplete(IState::ExitStatus exitStatus) { - UpgradeNotifications::Bus::Broadcast(&UpgradeNotifications::OnGraphUpgradeComplete, m_asset, exitStatus == IState::ExitStatus::Skipped); + UpgradeNotificationsBus::Broadcast(&UpgradeNotifications::OnGraphUpgradeComplete, m_asset, exitStatus == IState::ExitStatus::Skipped); m_asset = {}; } @@ -735,7 +735,7 @@ namespace ScriptCanvasEditor { AZ::SystemTickBus::Handler::BusDisconnect(); - OnComplete(exitStatus); + OnComplete(m_error.empty() ? exitStatus : IState::ExitStatus::Skipped); } } diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h index fdc8cbc1d8..0fb0f567e8 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h @@ -41,7 +41,7 @@ namespace ScriptCanvasEditor azrtti_typeid(), "Script Canvas", "Script Canvas Graph Asset", - "@devassets@/scriptcanvas", + "@projectroot@/scriptcanvas", ".scriptcanvas", "Script Canvas", "Untitled-%i", diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h index 4497968ffc..7e7b600081 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h @@ -215,21 +215,6 @@ namespace ScriptCanvasEditor using EditorLoggingComponentNotificationBus = AZ::EBus; - class IUpgradeRequests - { - public: - AZ_TYPE_INFO(IUpgradeRequests, "{D25318F2-4DDA-4E76-98CB-6D561BB6234D}"); - - using AssetList = AZStd::list; - virtual AssetList& GetAssetsToUpgrade() = 0; - - virtual void ClearGraphsThatNeedUpgrade() = 0; - virtual void GraphNeedsManualUpgrade(const AZ::Data::AssetId&) = 0; - virtual const AZStd::vector& GetGraphsThatNeedManualUpgrade() const = 0; - virtual bool IsUpgrading() = 0; - virtual void SetIsUpgrading(bool isUpgrading) = 0; - }; - class UpgradeNotifications : public AZ::EBusTraits { @@ -237,15 +222,11 @@ namespace ScriptCanvasEditor static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - using Bus = AZ::EBus; - virtual void OnUpgradeStart() {} - virtual void OnUpgradeComplete() {} virtual void OnUpgradeCancelled() {} virtual void OnGraphUpgradeComplete(AZ::Data::Asset&, bool skipped = false) { (void)skipped; } }; - - + using UpgradeNotificationsBus = AZ::EBus; } diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h index fb6e109156..817b883695 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h @@ -133,18 +133,23 @@ namespace ScriptCanvasEditor bool GetVerbose() const; + const AZStd::string GetError() const { return m_error; } + void SetVerbose(bool isVerbose); const AZStd::string& GetDebugPrefix() const; void SetDebugPrefix(AZStd::string_view); + void MarkError(AZStd::string_view error) { m_error = error; } + AZStd::shared_ptr m_currentState = nullptr; AZStd::vector> m_states; private: bool m_isVerbose = true; AZStd::string m_debugPrefix; + AZStd::string m_error; }; //! This state machine will collect and share a variety of data from the EditorGraph diff --git a/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp b/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp index 4b60fd357c..f901915fdb 100644 --- a/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp +++ b/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp @@ -6,46 +6,35 @@ * */ - -#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 +#include +#include #include #include #include #include -#include -#include - -#include - -#include -#include - #include -#include -#include - -#include -#include namespace ScriptCanvasEditor { @@ -54,6 +43,7 @@ namespace ScriptCanvasEditor SystemComponent::SystemComponent() { AzToolsFramework::AssetSeedManagerRequests::Bus::Handler::BusConnect(); + m_versionExplorer = AZStd::make_unique(); } SystemComponent::~SystemComponent() @@ -61,7 +51,6 @@ namespace ScriptCanvasEditor AzToolsFramework::UnregisterViewPane(LyViewPane::ScriptCanvas); AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); AzToolsFramework::AssetSeedManagerRequests::Bus::Handler::BusDisconnect(); } @@ -141,17 +130,12 @@ namespace ScriptCanvasEditor { if (userSettings->m_showUpgradeDialog) { - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); } else { m_upgradeDisabled = true; } } - else - { - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); - } } void SystemComponent::NotifyRegisterViews() @@ -172,7 +156,6 @@ namespace ScriptCanvasEditor AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); ScriptCanvasExecutionBus::Handler::BusDisconnect(); SystemRequestBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); m_jobContext.reset(); m_jobManager.reset(); @@ -397,64 +380,6 @@ namespace ScriptCanvasEditor return reporter; } - void SystemComponent::OnCatalogLoaded(const char* /*catalogFile*/) - { - // Enumerate all ScriptCanvas assets - AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, - nullptr, - [this](const AZ::Data::AssetId, const AZ::Data::AssetInfo& assetInfo) { - - if (assetInfo.m_assetType == azrtti_typeid()) - { - AddAssetToUpgrade(assetInfo); - } - }, - nullptr - ); - } - - void SystemComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) - { - if (IsUpgrading()) - { - return; - } - - auto assetInfo = ScriptCanvasEditor::AssetHelpers::GetAssetInfo(assetId); - AddAssetToUpgrade(assetInfo); - } - - void SystemComponent::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& /*assetInfo*/) - { - if (IsUpgrading()) - { - return; - } - - AZStd::erase_if(m_assetsToConvert, [assetId](const AZ::Data::AssetInfo& assetToConvert) - { - return assetToConvert.m_assetId == assetId; - } - ); - } - - void SystemComponent::AddAssetToUpgrade(const AZ::Data::AssetInfo& assetInfo) - { - auto query = AZStd::find_if(m_assetsToConvert.begin(), m_assetsToConvert.end(), [assetInfo](const AZ::Data::AssetInfo& assetToConvert) - { - return assetToConvert.m_assetId == assetInfo.m_assetId; - } - ); - - if (query == m_assetsToConvert.end()) - { - if (assetInfo.m_assetType == azrtti_typeid()) - { - m_assetsToConvert.push_back(assetInfo); - } - } - } - AzToolsFramework::AssetSeedManagerRequests::AssetTypePairs SystemComponent::GetAssetTypeMapping() { return { diff --git a/Gems/ScriptCanvas/Code/Editor/SystemComponent.h b/Gems/ScriptCanvas/Code/Editor/SystemComponent.h index 3e18262c98..6f85b3c5a6 100644 --- a/Gems/ScriptCanvas/Code/Editor/SystemComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/SystemComponent.h @@ -9,23 +9,20 @@ #pragma once -#include -#include -#include - #include -#include +#include +#include #include - +#include #include +#include #include #include +#include #include - -#include - -#include -#include +#include +#include +#include namespace ScriptCanvasEditor { @@ -36,9 +33,7 @@ namespace ScriptCanvasEditor , private AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler , private ScriptCanvasExecutionBus::Handler , private AZ::UserSettingsNotificationBus::Handler - , private AzFramework::AssetCatalogEventBus::Handler , private AZ::Data::AssetBus::MultiHandler - , private AZ::Interface::Registrar , private AzToolsFramework::AssetSeedManagerRequests::Bus::Handler , private AzToolsFramework::EditorContextMenuBus::Handler { @@ -96,74 +91,30 @@ namespace ScriptCanvasEditor void OnUserSettingsActivated() override; //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - // AzFramework::AssetCatalogEventBus::Handler... - void OnCatalogLoaded(const char* /*catalogFile*/) override; - void OnCatalogAssetAdded(const AZ::Data::AssetId& /*assetId*/) override; - void OnCatalogAssetRemoved(const AZ::Data::AssetId& /*assetId*/, const AZ::Data::AssetInfo& /*assetInfo*/) override; - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// // AssetSeedManagerRequests::Bus::Handler... AzToolsFramework::AssetSeedManagerRequests::AssetTypePairs GetAssetTypeMapping() override; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// - // IUpgradeRequests... - void ClearGraphsThatNeedUpgrade() override - { - m_assetsThatNeedManualUpgrade.clear(); - } - - IUpgradeRequests::AssetList& GetAssetsToUpgrade() override - { - return m_assetsToConvert; - } - - void GraphNeedsManualUpgrade(const AZ::Data::AssetId& assetId) override - { - if (AZStd::find(m_assetsThatNeedManualUpgrade.begin(), m_assetsThatNeedManualUpgrade.end(), assetId) == m_assetsThatNeedManualUpgrade.end()) - { - m_assetsThatNeedManualUpgrade.push_back(assetId); - } - } - - const AZStd::vector& GetGraphsThatNeedManualUpgrade() const override - { - return m_assetsThatNeedManualUpgrade; - } - - bool IsUpgrading() override - { - return m_isUpgrading; - } - - void SetIsUpgrading(bool isUpgrading) override - { - m_isUpgrading = isUpgrading; - } - - //////////////////////////////////////////////////////////////////////// - + private: SystemComponent(const SystemComponent&) = delete; void FilterForScriptCanvasEnabledEntities(AzToolsFramework::EntityIdList& sourceList, AzToolsFramework::EntityIdList& targetList); void PopulateEditorCreatableTypes(); - void AddAssetToUpgrade(const AZ::Data::AssetInfo& assetInfo); - + AZStd::unique_ptr m_jobManager; AZStd::unique_ptr m_jobContext; + AZStd::unique_ptr m_versionExplorer; AZStd::unordered_set m_creatableTypes; AssetTracker m_assetTracker; - IUpgradeRequests::AssetList m_assetsToConvert; AZStd::vector m_assetsThatNeedManualUpgrade; bool m_isUpgrading = false; bool m_upgradeDisabled = false; - }; } diff --git a/Gems/ScriptCanvas/Code/Editor/Utilities/CommonSettingsConfigurations.cpp b/Gems/ScriptCanvas/Code/Editor/Utilities/CommonSettingsConfigurations.cpp index 15cbd12d7e..a7c39fbf0f 100644 --- a/Gems/ScriptCanvas/Code/Editor/Utilities/CommonSettingsConfigurations.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Utilities/CommonSettingsConfigurations.cpp @@ -9,27 +9,20 @@ #include "CommonSettingsConfigurations.h" #include +#include +#include #include namespace ScriptCanvasEditor { AZStd::string GetEditingGameDataFolder() { - const char* resultValue = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(resultValue, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath); - if (!resultValue) + AZ::IO::Path projectPath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - if (AZ::IO::FileIOBase::GetInstance()) - { - resultValue = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); - } + settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); } - if (!resultValue) - { - resultValue = "."; - } - - return resultValue; + return projectPath.Native(); } } 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/Widgets/NodePalette/NodePaletteModel.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp index b29992f9b1..047a07cd0b 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp @@ -829,12 +829,12 @@ namespace ScriptCanvasEditor NodePaletteModel::NodePaletteModel() : m_paletteId(AZ::Entity::MakeId()) { - UpgradeNotifications::Bus::Handler::BusConnect(); + UpgradeNotificationsBus::Handler::BusConnect(); } NodePaletteModel::~NodePaletteModel() { - UpgradeNotifications::Bus::Handler::BusDisconnect(); + UpgradeNotificationsBus::Handler::BusDisconnect(); DisconnectLambdas(); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h index b27bfd97f1..28d627af01 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h @@ -61,7 +61,7 @@ namespace ScriptCanvasEditor class NodePaletteModel : public GraphCanvas::CategorizerInterface - , UpgradeNotifications::Bus::Handler + , UpgradeNotificationsBus::Handler { public: typedef AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, NodePaletteModelInformation* > NodePaletteRegistry; @@ -104,10 +104,6 @@ namespace ScriptCanvasEditor { DisconnectLambdas(); } - void OnUpgradeComplete() override - { - ConnectLambdas(); - } // Asset Node Support void OnRowsInserted(const QModelIndex& parentIndex, int first, int last); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp index 96b0ac353f..9e51c1896f 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp @@ -148,7 +148,7 @@ namespace ScriptCanvasEditor , m_assetModel(assetModel) , m_categorizer(nodePaletteModel) { - UpgradeNotifications::Bus::Handler::BusConnect(); + UpgradeNotificationsBus::Handler::BusConnect(); if (m_assetModel) { @@ -166,7 +166,7 @@ namespace ScriptCanvasEditor AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); AZ::Data::AssetBus::MultiHandler::BusDisconnect(); - UpgradeNotifications::Bus::Handler::BusDisconnect(); + UpgradeNotificationsBus::Handler::BusDisconnect(); } @@ -386,15 +386,6 @@ namespace ScriptCanvasEditor AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } - void ScriptCanvasRootPaletteTreeItem::OnUpgradeComplete() - { - ConnectLambdas(); - - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); - - TraverseTree(); - } - void ScriptCanvasRootPaletteTreeItem::OnUpgradeCancelled() { if (!AzFramework::AssetCatalogEventBus::Handler::BusIsConnected()) diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h index 7b2e9293bd..45645ceb33 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h @@ -57,7 +57,7 @@ namespace ScriptCanvasEditor : public GraphCanvas::NodePaletteTreeItem , AzFramework::AssetCatalogEventBus::Handler , AZ::Data::AssetBus::MultiHandler - , UpgradeNotifications::Bus::Handler + , UpgradeNotificationsBus::Handler , AZ::SystemTickBus::Handler { public: @@ -92,7 +92,6 @@ namespace ScriptCanvasEditor // UpgradeNotifications::Bus void OnUpgradeStart() override; - void OnUpgradeComplete() override; void OnUpgradeCancelled() override; //// diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.cpp index 91f85ae36a..5b78c62bac 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.cpp @@ -411,10 +411,7 @@ namespace ScriptCanvasEditor case Qt::TextAlignmentRole: { - if (index.column() == ColumnIndex::Type) - { - return QVariant(Qt::AlignLeft | Qt::AlignVCenter); - } + return QVariant(Qt::AlignLeft | Qt::AlignVCenter); } break; @@ -880,6 +877,11 @@ namespace ScriptCanvasEditor return tr(m_columnNames[section]); } + if (role == Qt::TextAlignmentRole) + { + return QVariant(Qt::AlignLeft | Qt::AlignVCenter); + } + return QAbstractItemModel::headerData(section, orientation, role); } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp index fbe03ffc0a..1ad8b58317 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -134,7 +135,6 @@ #include #include #include -#include #include @@ -224,7 +224,7 @@ namespace ScriptCanvasEditor } } } // anonymous namespace. - + void Workspace::Save() { auto workspace = AZ::UserSettings::CreateFind(AZ_CRC("ScriptCanvasEditorWindowState", 0x10c47d36), AZ::UserSettings::CT_LOCAL); @@ -423,7 +423,7 @@ namespace ScriptCanvasEditor , m_queueCloseRequest(false) , m_hasQueuedClose(false) , m_isInAutomation(false) - , m_allowAutoSave(true) + , m_allowAutoSave(true) , m_systemTickActions(0) , m_closeCurrentGraphAfterSave(false) , m_styleManager(ScriptCanvasEditor::AssetEditorId, "ScriptCanvas/StyleSheet/graphcanvas_style.json") @@ -432,7 +432,7 @@ namespace ScriptCanvasEditor GraphCanvas::AssetEditorAutomationRequestBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId); AZStd::array unresolvedPath; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@assets@/translation/scriptcanvas_en_us.qm", unresolvedPath.data(), unresolvedPath.size()); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/translation/scriptcanvas_en_us.qm", unresolvedPath.data(), unresolvedPath.size()); QString translationFilePath(unresolvedPath.data()); if ( m_translator.load(QLocale::Language::English, translationFilePath) ) @@ -541,7 +541,7 @@ namespace ScriptCanvasEditor m_editorToolbar->AddCustomAction(m_createFunctionOutput); connect(m_createFunctionOutput, &QToolButton::clicked, this, &MainWindow::CreateFunctionOutput); - + { m_validateGraphToolButton = new QToolButton(); @@ -553,7 +553,7 @@ namespace ScriptCanvasEditor m_editorToolbar->AddCustomAction(m_validateGraphToolButton); connect(m_validateGraphToolButton, &QToolButton::clicked, this, &MainWindow::OnValidateCurrentGraph); - + m_layout->addWidget(m_editorToolbar); // Tab bar @@ -578,7 +578,7 @@ namespace ScriptCanvasEditor m_commandLine = new Widget::CommandLine(this); m_commandLine->setBaseSize(QSize(size().width(), m_commandLine->size().height())); - m_commandLine->setObjectName("CommandLine"); + m_commandLine->setObjectName("CommandLine"); m_layout->addWidget(m_commandLine); m_layout->addWidget(m_emptyCanvas); @@ -602,7 +602,7 @@ namespace ScriptCanvasEditor // in sync with the order we display under tools for consistency. { const bool isInContextMenu = false; - Widget::ScriptCanvasNodePaletteConfig nodePaletteConfig(m_nodePaletteModel, m_scriptEventsAssetModel, isInContextMenu); + Widget::ScriptCanvasNodePaletteConfig nodePaletteConfig(m_nodePaletteModel, m_scriptEventsAssetModel, isInContextMenu); m_nodePalette = aznew Widget::NodePaletteDockWidget(tr("Node Palette"), this, nodePaletteConfig); m_nodePalette->setObjectName("NodePalette"); @@ -704,78 +704,7 @@ namespace ScriptCanvasEditor m_autoSaveTimer.setSingleShot(true); connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWindow::OnAutoSave); - UpdateMenuState(false); - - PromptForUpgrade(); - } - - void MainWindow::PromptForUpgrade() - { - static bool displayUpgradePrompt = false; // Set to true if you need the upgrade dialog to show up on ScriptCanvas editor open - if (displayUpgradePrompt && m_showUpgradeTool) - { - QTimer::singleShot(1.f, this, [this]() - { - UpgradeTool* upgradeTool = aznew UpgradeTool(this); - - QPoint centerPoint = frameGeometry().center(); - - upgradeTool->adjustSize(); - upgradeTool->move(centerPoint.x() - upgradeTool->width() / 2, centerPoint.y() - upgradeTool->height() / 2); - - if (upgradeTool->exec() == QDialog::Accepted) - { - // Manual correction - size_t assetsThatNeedManualInspection = AZ::Interface::Get()->GetGraphsThatNeedManualUpgrade().size(); - QString message = QObject::tr("%1 Graph(s) upgraded
%2 graph(s) did not require upgrade").arg(upgradeTool->UpgradedGraphCount()).arg(upgradeTool->SkippedGraphCount()); - if (assetsThatNeedManualInspection > 0) - { - message.append(QObject::tr("
%1 graph(s) that need manual corrections. You will be prompted to review them after you close this dialog.
").arg(assetsThatNeedManualInspection)); - } - - // Report - char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 }; - AZStd::string outputFileName = AZStd::string::format("@devroot@/ScriptCanvasUpgradeReport.html"); - AZ::IO::FileIOBase::GetInstance()->ResolvePath(outputFileName.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN); - AZStd::string urlToReport = AZStd::string::format("For more information see the Upgrade Report.", resolvedBuffer); - message.append(QObject::tr("
%1").arg(urlToReport.c_str())); - - // Backup - if (upgradeTool->HasBackup()) - { - AZStd::string outputFileName2 = AZStd::string::format("@devroot@/ScriptCanvas_BACKUP"); - - AZ::IO::FileIOBase::GetInstance()->ResolvePath(outputFileName2.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN); - - AZStd::string backupPath = AZStd::string::format("
Open the Backup Folder.", resolvedBuffer); - message.append(QObject::tr("%1").arg(backupPath.c_str())); - } - - // Done upgrading, show details - QMessageBox mb(QMessageBox::Information, - QObject::tr("Upgrade Complete"), - message, - QMessageBox::Ok, this); - mb.setTextFormat(Qt::RichText); - - centerPoint = frameGeometry().center(); - - mb.adjustSize(); - mb.move(centerPoint.x() - width() / 2, centerPoint.y() - height() / 2); - - mb.exec(); - - // If there are graphs that need manual correction, show the helper - if (assetsThatNeedManualInspection > 0) - { - UpgradeHelper* upgradeHelper = new UpgradeHelper(this); - upgradeHelper->show(); - } - } - - }); - } } MainWindow::~MainWindow() @@ -882,14 +811,14 @@ namespace ScriptCanvasEditor connect(ui->action_ViewVariableManager, &QAction::triggered, this, &MainWindow::OnVariableManager); connect(m_variableDockWidget, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged); - + connect(ui->action_ViewLogWindow, &QAction::triggered, this, &MainWindow::OnViewLogWindow); connect(m_loggingWindow, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged); connect(ui->action_ViewDebugger, &QAction::triggered, this, &MainWindow::OnViewDebugger); connect(ui->action_ViewCommandLine, &QAction::triggered, this, &MainWindow::OnViewCommandLine); connect(ui->action_ViewLog, &QAction::triggered, this, &MainWindow::OnViewLog); - + connect(ui->action_GraphValidation, &QAction::triggered, this, &MainWindow::OnViewGraphValidation); connect(ui->action_Debugging, &QAction::triggered, this, &MainWindow::OnViewDebuggingWindow); @@ -897,7 +826,7 @@ namespace ScriptCanvasEditor connect(ui->action_NodeStatistics, &QAction::triggered, this, &MainWindow::OnViewStatisticsPanel); connect(ui->action_PresetsEditor, &QAction::triggered, this, &MainWindow::OnViewPresetsEditor); - connect(ui->action_ViewRestoreDefaultLayout, &QAction::triggered, this, &MainWindow::OnRestoreDefaultLayout); + connect(ui->action_ViewRestoreDefaultLayout, &QAction::triggered, this, &MainWindow::OnRestoreDefaultLayout); } void MainWindow::SignalActiveSceneChanged(AZ::Data::AssetId assetId) @@ -920,7 +849,7 @@ namespace ScriptCanvasEditor // The paste action refreshes based on the scene's mimetype RefreshPasteAction(); - + bool enabled = false; if (graphId.IsValid()) @@ -1016,7 +945,7 @@ namespace ScriptCanvasEditor QString tabName = m_tabBar->tabText(tabCounter); UnsavedChangesOptions shouldSaveResults = ShowSaveDialog(tabName); - + if (shouldSaveResults == UnsavedChangesOptions::SAVE) { Callbacks::OnSave saveCB = [this](bool isSuccessful, AZ::Data::AssetPtr, AZ::Data::AssetId) @@ -1041,7 +970,7 @@ namespace ScriptCanvasEditor { m_processedClosedAssetIds.clear(); event->ignore(); - + return; } else if (shouldSaveResults == UnsavedChangesOptions::CONTINUE_WITHOUT_SAVING && @@ -1066,7 +995,7 @@ namespace ScriptCanvasEditor } m_processedClosedAssetIds.clear(); - + event->accept(); } @@ -1232,7 +1161,7 @@ namespace ScriptCanvasEditor const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(memoryAssetId); if (fileState != Tracker::ScriptCanvasFileState::NEW) { - AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::UpdateFileState, memoryAssetId, Tracker::ScriptCanvasFileState::MODIFIED); + AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::UpdateFileState, memoryAssetId, Tracker::ScriptCanvasFileState::MODIFIED); } } } @@ -1734,12 +1663,12 @@ namespace ScriptCanvasEditor { currentTarget.SetInvalid(); } - } + } } return retVal; } - + void MainWindow::OnFileNew() { MakeNewFile(); @@ -1756,7 +1685,7 @@ namespace ScriptCanvasEditor m_tabBar->InsertGraphTab(tabIndex, assetId); if (!IsTabOpen(assetId, outTabIndex)) - { + { AZ_Assert(false, AZStd::string::format("Unable to open new Script Canvas Asset with id %s in the Script Canvas Editor", AssetHelpers::AssetIdToString(assetId).c_str()).c_str()); return -1; } @@ -1854,7 +1783,7 @@ namespace ScriptCanvasEditor } PrepareAssetForSave(inMemoryAssetId); - + AZStd::string suggestedFilename; AZStd::string suggestedFileFilter; GetSuggestedFullFilenameToSaveAs(inMemoryAssetId, suggestedFilename, suggestedFileFilter); @@ -1881,7 +1810,7 @@ namespace ScriptCanvasEditor AZStd::string assetRoot; AZStd::array assetRootChar; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devroot@", assetRootChar.data(), assetRootChar.size()); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@engroot@", assetRootChar.data(), assetRootChar.size()); assetRoot = assetRootChar.data(); /* if (!AZ::StringFunc::StartsWith(filePath, assetRoot)) @@ -1906,7 +1835,7 @@ namespace ScriptCanvasEditor if (isValidFileName) { - AZStd::string internalStringFile = selectedFile.toUtf8().data(); + AZStd::string internalStringFile = selectedFile.toUtf8().data(); if (!AssetHelpers::IsValidSourceFile(internalStringFile, GetActiveScriptCanvasId())) { @@ -1923,7 +1852,7 @@ namespace ScriptCanvasEditor return true; } - + return false; } @@ -1965,7 +1894,7 @@ namespace ScriptCanvasEditor saveTabIndex = m_tabBar->FindTab(previousFileAssetId); } } - + AzFramework::StringFunc::Path::GetFileName(memoryAsset->GetAbsolutePath().c_str(), tabName); // Update the tab's assetId to the file asset Id (necessary when saving a new asset) @@ -2047,7 +1976,7 @@ namespace ScriptCanvasEditor } m_closeCurrentGraphAfterSave = false; - + EnableAssetView(memoryAsset); UnblockCloseRequests(); @@ -2071,7 +2000,7 @@ namespace ScriptCanvasEditor AZStd::invoke(onSave, saveSuccess, asset, previousAssetId); } }; - + AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Save, assetId, onSaveCallback); UpdateSaveState(); @@ -2109,7 +2038,7 @@ namespace ScriptCanvasEditor // Disable the current view if we are saving. if (memoryAsset) { - DisableAssetView(memoryAsset); + DisableAssetView(memoryAsset); } BlockCloseRequests(); @@ -2126,7 +2055,7 @@ namespace ScriptCanvasEditor AZStd::string assetRoot; { AZStd::array assetRootChar; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", assetRootChar.data(), assetRootChar.size()); + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", assetRootChar.data(), assetRootChar.size()); assetRoot = assetRootChar.data(); } @@ -2147,7 +2076,7 @@ namespace ScriptCanvasEditor AssetRegistryRequestBus::BroadcastResult(fileFilters, &AssetRegistryRequests::GetAssetHandlerFileFilters); QString filter; - + AZStd::set filterSet; auto aggregateFilters = fileFilters.values; for (auto aggregateFilters2 : fileFilters.values) @@ -2190,7 +2119,7 @@ namespace ScriptCanvasEditor dialog.setFileMode(QFileDialog::ExistingFiles); dialog.setNameFilters(nameFilters); - if (dialog.exec() == QDialog::Accepted) + if (dialog.exec() == QDialog::Accepted) { m_filesToOpen = dialog.selectedFiles(); @@ -2206,7 +2135,7 @@ namespace ScriptCanvasEditor ui->action_Paste->setShortcut(QKeySequence(QKeySequence::Paste)); ui->action_Delete->setShortcut(QKeySequence(QKeySequence::Delete)); - connect(ui->menuEdit, &QMenu::aboutToShow, this, &MainWindow::OnEditMenuShow); + connect(ui->menuEdit, &QMenu::aboutToShow, this, &MainWindow::OnEditMenuShow); // Edit Menu connect(ui->action_Undo, &QAction::triggered, this, &MainWindow::TriggerUndo); @@ -2215,8 +2144,8 @@ namespace ScriptCanvasEditor connect(ui->action_Copy, &QAction::triggered, this, &MainWindow::OnEditCopy); connect(ui->action_Paste, &QAction::triggered, this, &MainWindow::OnEditPaste); connect(ui->action_Duplicate, &QAction::triggered, this, &MainWindow::OnEditDuplicate); - connect(ui->action_Delete, &QAction::triggered, this, &MainWindow::OnEditDelete); - connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::RefreshPasteAction); + connect(ui->action_Delete, &QAction::triggered, this, &MainWindow::OnEditDelete); + connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::RefreshPasteAction); connect(ui->action_RemoveUnusedNodes, &QAction::triggered, this, &MainWindow::OnRemoveUnusedNodes); connect(ui->action_RemoveUnusedVariables, &QAction::triggered, this, &MainWindow::OnRemoveUnusedVariables); connect(ui->action_RemoveUnusedElements, &QAction::triggered, this, &MainWindow::OnRemoveUnusedElements); @@ -2246,7 +2175,7 @@ namespace ScriptCanvasEditor connect(ui->action_GotoEndOfChain, &QAction::triggered, this, &MainWindow::OnGotoEndOfChain); - connect(ui->action_GlobalPreferences, &QAction::triggered, [this]() + connect(ui->action_GlobalPreferences, &QAction::triggered, [this]() { ScriptCanvasEditor::SettingsDialog(ui->action_GlobalPreferences->text(), ScriptCanvas::ScriptCanvasId(), this).exec(); @@ -2372,7 +2301,7 @@ namespace ScriptCanvasEditor { AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId(); - GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SelectAllRelative, GraphCanvas::ConnectionType::CT_Input); + GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SelectAllRelative, GraphCanvas::ConnectionType::CT_Input); } void MainWindow::OnSelectOutputs() @@ -2635,8 +2564,8 @@ namespace ScriptCanvasEditor { ScriptCanvas::ScriptCanvasId scriptCanvasId; AssetTrackerRequestBus::BroadcastResult(scriptCanvasId, &AssetTrackerRequests::GetScriptCanvasId, assetId); - return scriptCanvasId; - } + return scriptCanvasId; + } ScriptCanvas::ScriptCanvasId MainWindow::GetScriptCanvasId(const GraphCanvas::GraphId& graphCanvasGraphId) const { @@ -2849,7 +2778,7 @@ namespace ScriptCanvasEditor AssetTrackerRequests::AssetList assets; AssetTrackerRequestBus::BroadcastResult(assets, &AssetTrackerRequests::GetAssets); - + for (auto asset : assets) { RemoveScriptCanvasAsset(asset->GetAsset().GetId()); @@ -2927,14 +2856,14 @@ namespace ScriptCanvasEditor } void MainWindow::SaveTab(int index) - { + { QVariant tabdata = m_tabBar->tabData(index); if (tabdata.isValid()) { auto assetId = tabdata.value(); SaveAssetImpl(assetId, nullptr); } - + } void MainWindow::CloseAllTabs() @@ -2955,7 +2884,7 @@ namespace ScriptCanvasEditor m_isClosingTabs = true; m_skipTabOnClose = assetId; CloseNextTab(); - } + } } void MainWindow::CopyPathToClipboard(int index) @@ -3051,7 +2980,7 @@ namespace ScriptCanvasEditor // The last tab has been removed. SetActiveAsset({}); } - + // Handling various close all events because the save is async need to deal with this in a bunch of different ways // Always want to trigger this, even if we don't have any active tabs to avoid doubling the clean-up // information @@ -3069,7 +2998,7 @@ namespace ScriptCanvasEditor for (const auto& slotId : outputDataSlotIds) { - + if (!IsInUndoRedo(graphCanvasGraphId) && !isPaste && CreateAzEventHandlerSlotMenuAction::FindBehaviorMethodWithAzEventReturn(graphCanvasGraphId, slotId)) { CreateAzEventHandlerSlotMenuAction eventHandlerAction(this); @@ -3195,7 +3124,7 @@ namespace ScriptCanvasEditor m_minimap->hide(); } */ - + resizeDocks( { m_nodePalette, m_propertyGrid }, { static_cast(size().width() * 0.15f), static_cast(size().width() * 0.2f) }, @@ -3204,7 +3133,7 @@ namespace ScriptCanvasEditor resizeDocks({ m_nodePalette, m_minimap }, { static_cast(size().height() * 0.70f), static_cast(size().height() * 0.30f) }, Qt::Vertical); - + resizeDocks({ m_propertyGrid, m_variableDockWidget }, { static_cast(size().height() * 0.70f), static_cast(size().height() * 0.30f) }, @@ -3229,7 +3158,7 @@ namespace ScriptCanvasEditor AZ::EntityId graphCanvasGraphId; EditorGraphRequestBus::EventResult(graphCanvasGraphId, scriptCanvasId, &EditorGraphRequests::GetGraphCanvasGraphId); - + bool hasCopiableSelection = false; bool hasSelection = false; @@ -3268,7 +3197,7 @@ namespace ScriptCanvasEditor ui->action_Duplicate->setEnabled(hasCopiableSelection); // Delete will work for anything that is selectable - ui->action_Delete->setEnabled(hasSelection); + ui->action_Delete->setEnabled(hasSelection); } void MainWindow::OnViewNodePalette() @@ -3470,7 +3399,7 @@ namespace ScriptCanvasEditor if (ui->action_Debugging->isChecked() != m_loggingWindow->isVisible()) { ui->action_Debugging->setChecked(m_loggingWindow->isVisible()); - } + } // Want these two elements to be mutually exclusive. if (m_statusWidget->isVisible() == m_validationDockWidget->isVisible()) @@ -3507,18 +3436,20 @@ namespace ScriptCanvasEditor void MainWindow::RunUpgradeTool() { - VersionExplorer* versionExplorer = aznew VersionExplorer(this); + using namespace VersionExplorer; + auto versionExplorer = aznew VersionExplorer::Controller(this); versionExplorer->exec(); - // Manual correction - size_t assetsThatNeedManualInspection = AZ::Interface::Get()->GetGraphsThatNeedManualUpgrade().size(); - - // If there are graphs that need manual correction, show the helper - if (assetsThatNeedManualInspection > 0) + const ModificationResults* result = nullptr; + ModelRequestsBus::BroadcastResult(result, &ModelRequestsTraits::GetResults); + if (result && !result->m_failures.empty()) { + // If there are graphs that need manual correction, show the helper UpgradeHelper* upgradeHelper = new UpgradeHelper(this); upgradeHelper->show(); } + + delete versionExplorer; } void MainWindow::OnShowValidationErrors() @@ -3624,7 +3555,7 @@ namespace ScriptCanvasEditor if (m_isRestoringWorkspace) { m_isRestoringWorkspace = false; - + if (m_queuedFocusOverride.IsValid()) { SetActiveAsset(m_queuedFocusOverride); @@ -3701,7 +3632,7 @@ namespace ScriptCanvasEditor hasModifications = ( fileState == Tracker::ScriptCanvasFileState::MODIFIED || fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED); - + AssetTrackerRequestBus::BroadcastResult(isSaving, &AssetTrackerRequests::IsSaving, m_activeAssetId); } @@ -4084,7 +4015,7 @@ namespace ScriptCanvasEditor if (action == nullptr) { GraphCanvas::GraphCanvasMimeEvent* mimeEvent = m_sceneContextMenu->GetNodePalette()->GetContextMenuEvent(); - + NodeIdPair finalNode = ProcessCreateNodeMimeEvent(mimeEvent, graphCanvasGraphId, AZ::Vector2(aznumeric_cast(scenePoint.x()), aznumeric_cast(scenePoint.y()))); GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection); @@ -4436,7 +4367,7 @@ namespace ScriptCanvasEditor targetLayer = (*selectedEntityIdIter); - selectedEntityIdIter = selectedEntityIds.erase(selectedEntityIdIter); + selectedEntityIdIter = selectedEntityIds.erase(selectedEntityIdIter); } else { @@ -4456,7 +4387,7 @@ namespace ScriptCanvasEditor AZ::TransformBus::Event(createdId, &AZ::TransformBus::Events::SetParent, targetLayer); } } - + for (const AZ::EntityId& entityId : selectedEntityIds) { AssignGraphToEntityImpl(entityId); @@ -4515,7 +4446,7 @@ namespace ScriptCanvasEditor { usableRequestBus = firstRequestBus; } - + if (usableRequestBus == nullptr) { AzToolsFramework::EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::AddComponentsToEntities, AzToolsFramework::EntityIdList{ entityId } @@ -4681,7 +4612,7 @@ namespace ScriptCanvasEditor } bool MainWindow::IsNodeNudgingEnabled() const - { + { if (m_userSettings) { return m_userSettings->m_allowNodeNudging; @@ -4720,7 +4651,7 @@ namespace ScriptCanvasEditor return 0.03f; } - float MainWindow::GetShakeDeadZonePercent() const + float MainWindow::GetShakeDeadZonePercent() const { if (m_userSettings) { @@ -4730,7 +4661,7 @@ namespace ScriptCanvasEditor return 0.01f; } - float MainWindow::GetShakeStraightnessPercent() const + float MainWindow::GetShakeStraightnessPercent() const { if (m_userSettings) { diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h index ba5ff7f97f..2c84412e28 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h @@ -51,8 +51,7 @@ #include -#include -#include +#include #if SCRIPTCANVAS_EDITOR #include @@ -99,30 +98,25 @@ namespace ScriptCanvasEditor class ScriptCanvasAssetBrowserModel : public AzToolsFramework::AssetBrowser::AssetBrowserFilterModel - , private UpgradeNotifications::Bus::Handler + , private UpgradeNotificationsBus::Handler { public: explicit ScriptCanvasAssetBrowserModel(QObject* parent = nullptr) : AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent) { - UpgradeNotifications::Bus::Handler::BusConnect(); + UpgradeNotificationsBus::Handler::BusConnect(); } ~ScriptCanvasAssetBrowserModel() override { - UpgradeNotifications::Bus::Handler::BusDisconnect(); + UpgradeNotificationsBus::Handler::BusDisconnect(); } void OnUpgradeStart() override { AzToolsFramework::AssetBrowser::AssetBrowserComponentNotificationBus::Handler::BusDisconnect(); } - - void OnUpgradeComplete() override - { - AzToolsFramework::AssetBrowser::AssetBrowserComponentNotificationBus::Handler::BusConnect(); - } }; class OnSaveToast @@ -673,7 +667,7 @@ namespace ScriptCanvasEditor AZStd::array assetRootArray; if (!AZ::IO::FileIOBase::GetInstance()->ResolvePath(ScriptCanvas::AssetDescription::GetSuggestedSavePath(), assetRootArray.data(), assetRootArray.size())) { - AZ_ErrorOnce("Script Canvas", false, "Unable to resolve @devassets@ path"); + AZ_ErrorOnce("Script Canvas", false, "Unable to resolve @projectroot@ path"); } AZStd::string assetPath; @@ -806,7 +800,5 @@ namespace ScriptCanvasEditor Workspace* m_workspace; void OnSaveCallback(bool saveSuccess, AZ::Data::AssetPtr, AZ::Data::AssetId previousFileAssetId); - - void PromptForUpgrade(); }; } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp new file mode 100644 index 0000000000..dbcd176ee5 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp @@ -0,0 +1,629 @@ +/* + * 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 + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + Controller::Controller(QWidget* parent) + : AzQtComponents::StyledDialog(parent) + , m_view(new Ui::View()) + { + m_view->setupUi(this); + m_view->tableWidget->horizontalHeader()->setVisible(false); + m_view->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + m_view->tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); + m_view->tableWidget->setColumnWidth(3, 22); + m_view->textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + m_view->textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + connect(m_view->scanButton, &QPushButton::pressed, this, &Controller::OnButtonPressScan); + connect(m_view->closeButton, &QPushButton::pressed, this, &Controller::OnButtonPressClose); + m_view->upgradeAllButton->setVisible(false); + connect(m_view->upgradeAllButton, &QPushButton::pressed, this, &Controller::OnButtonPressUpgrade); + m_view->progressBar->setValue(0); + m_view->progressBar->setVisible(false); + + UpgradeNotificationsBus::Handler::BusConnect(); + ModelNotificationsBus::Handler::BusConnect(); + } + + Controller::~Controller() + { + delete m_view; + } + + void Controller::AddLogEntries() + { + const AZStd::vector* logs = nullptr; + LogBus::BroadcastResult(logs, &LogTraits::GetEntries); + if (!logs || logs->empty()) + { + return; + } + + const QTextCursor oldCursor = m_view->textEdit->textCursor(); + QScrollBar* scrollBar = m_view->textEdit->verticalScrollBar(); + + m_view->textEdit->moveCursor(QTextCursor::End); + QTextCursor textCursor = m_view->textEdit->textCursor(); + + for (auto& entry : *logs) + { + textCursor.insertText("\n"); + textCursor.insertText(entry.c_str()); + } + + scrollBar->setValue(scrollBar->maximum()); + m_view->textEdit->moveCursor(QTextCursor::StartOfLine); + LogBus::Broadcast(&LogTraits::Clear); + } + + void Controller::EnableAllUpgradeButtons() + { + for (int row = 0; row < m_view->tableWidget->rowCount(); ++row) + { + if (QPushButton* button = qobject_cast(m_view->tableWidget->cellWidget(row, ColumnAction))) + { + button->setEnabled(true); + } + } + } + + QList Controller::FindTableItems(const AZ::Data::AssetInfo& info) + { + return m_view->tableWidget->findItems(info.m_relativePath.c_str(), Qt::MatchFlag::MatchExactly); + } + + void Controller::OnButtonPressClose() + { + reject(); + } + + void Controller::OnButtonPressScan() + { + // \todo move to another file + auto isUpToDate = [this](AZ::Data::Asset asset) + { + AZ::Entity* scriptCanvasEntity = nullptr; + + if (asset.GetType() == azrtti_typeid()) + { + ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs(); + if (!scriptCanvasAsset) + { + AZ_Warning + (ScriptCanvas::k_VersionExplorerWindow.data() + , false + , "InspectAsset: %s, AsestData failed to return ScriptCanvasAsset" + , asset.GetHint().c_str()); + return true; + } + + scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity(); + AZ_Assert(scriptCanvasEntity, "The Script Canvas asset must have a valid entity"); + } + + auto graphComponent = scriptCanvasEntity->FindComponent(); + AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component"); + return !m_view->forceUpgrade->isChecked() && graphComponent->GetVersion().IsLatest(); + }; + + ScanConfiguration config; + config.reportFilteredGraphs = !m_view->onlyShowOutdated->isChecked(); + config.filter = isUpToDate; + + SetLoggingPreferences(); + ModelRequestsBus::Broadcast(&ModelRequestsTraits::Scan, config); + } + + void Controller::OnButtonPressUpgrade() + { + OnButtonPressUpgradeImplementation({}); + } + + void Controller::OnButtonPressUpgradeImplementation(const AZ::Data::AssetInfo& assetInfo) + { + auto simpleUpdate = [this](AZ::Data::Asset asset) + { + if (asset.GetType() == azrtti_typeid()) + { + ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs(); + AZ_Assert(scriptCanvasAsset, "Unable to get the asset of ScriptCanvasAsset, but received type: %s" + , azrtti_typeid().template ToString().c_str()); + if (!scriptCanvasAsset) + { + return; + } + + AZ::Entity* scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity(); + AZ_Assert(scriptCanvasEntity, "View::UpgradeGraph The Script Canvas asset must have a valid entity"); + if (!scriptCanvasEntity) + { + return; + } + + AZ::Entity* queryEntity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(queryEntity, &AZ::ComponentApplicationRequests::FindEntity, scriptCanvasEntity->GetId()); + if (queryEntity) + { + if (queryEntity->GetState() == AZ::Entity::State::Active) + { + queryEntity->Deactivate(); + } + + scriptCanvasEntity = queryEntity; + } + + if (scriptCanvasEntity->GetState() == AZ::Entity::State::Constructed) + { + scriptCanvasEntity->Init(); + } + + if (scriptCanvasEntity->GetState() == AZ::Entity::State::Init) + { + scriptCanvasEntity->Activate(); + } + + AZ_Assert(scriptCanvasEntity->GetState() == AZ::Entity::State::Active, "Graph entity is not active"); + auto graphComponent = scriptCanvasEntity->FindComponent(); + AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component"); + if (graphComponent) + { + graphComponent->UpgradeGraph + (asset + , m_view->forceUpgrade->isChecked() ? Graph::UpgradeRequest::Forced : Graph::UpgradeRequest::IfOutOfDate + , m_view->verbose->isChecked()); + } + } + }; + + auto onReadyOnlyFile = [this]()->bool + { + int result = QMessageBox::No; + QMessageBox mb + (QMessageBox::Warning + , QObject::tr("Failed to Save Upgraded File") + , QObject::tr("The upgraded file could not be saved because the file is read only.\n" + "Do you want to make it writeable and overwrite it?") + , QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::No + , this); + result = mb.exec(); + return result == QMessageBox::YesToAll; + }; + + SetLoggingPreferences(); + ModifyConfiguration config; + config.modifySingleAsset = assetInfo; + config.modification = simpleUpdate; + config.onReadOnlyFile = onReadyOnlyFile; + config.backupGraphBeforeModification = m_view->makeBackupCheckbox->isChecked(); + ModelRequestsBus::Broadcast(&ModelRequestsTraits::Modify, config); + } + + void Controller::OnButtonPressUpgradeSingle(const AZ::Data::AssetInfo& assetInfo) + { + OnButtonPressUpgradeImplementation(assetInfo); + } + + void Controller::OnUpgradeModificationBegin([[maybe_unused]] const ModifyConfiguration& config, const AZ::Data::AssetInfo& info) + { + for (auto* item : FindTableItems(info)) + { + int row = item->row(); + SetRowBusy(row); + m_view->tableWidget->setCellWidget(row, ColumnAction, nullptr); + } + } + + void Controller::OnUpgradeModificationEnd + ( [[maybe_unused]] const ModifyConfiguration& config + , const AZ::Data::AssetInfo& info + , ModificationResult result) + { + if (result.errorMessage.empty()) + { + VE_LOG("Successfully modified %s", result.assetInfo.m_relativePath.c_str()); + } + else + { + VE_LOG("Failed to modify %s: %s", result.assetInfo.m_relativePath.c_str(), result.errorMessage.data()); + } + + for (auto* item : FindTableItems(info)) + { + int row = item->row(); + + if (result.errorMessage.empty()) + { + SetRowSucceeded(row); + } + else + { + SetRowFailed(row, ""); + + if (QPushButton* button = qobject_cast(m_view->tableWidget->cellWidget(row, ColumnAction))) + { + button->setEnabled(false); + } + } + } + + m_view->progressBar->setVisible(true); + ++m_handledAssetCount; + m_view->progressBar->setValue(m_handledAssetCount); + AddLogEntries(); + } + + void Controller::OnGraphUpgradeComplete(AZ::Data::Asset& asset, bool skipped) + { + ModificationResult result; + result.asset = asset; + AZ::Data::AssetCatalogRequestBus::BroadcastResult + ( result.assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, asset.GetId()); + + if (skipped) + { + result.errorMessage = "Failed in editor upgrade state machine - check logs"; + } + + ModificationNotificationsBus::Broadcast(&ModificationNotificationsTraits::ModificationComplete, result); + } + + void Controller::OnScanBegin(size_t assetCount) + { + m_handledAssetCount = 0; + m_view->tableWidget->setRowCount(0); + m_view->progressBar->setVisible(true); + m_view->progressBar->setRange(0, aznumeric_cast(assetCount)); + m_view->progressBar->setValue(0); + m_view->scanButton->setEnabled(false); + m_view->upgradeAllButton->setEnabled(false); + m_view->onlyShowOutdated->setEnabled(false); + + QString spinnerText = QStringLiteral("Scan in progress - gathering graphs that can be updated"); + m_view->spinner->SetText(spinnerText); + SetSpinnerIsBusy(true); + } + + void Controller::OnScanComplete(const ScanResult& result) + { + m_view->onlyShowOutdated->setEnabled(true); + + QString spinnerText = QStringLiteral("Scan Complete"); + spinnerText.append(QString::asprintf(" - Discovered: %zu, Failed: %zu, Upgradeable: %zu, Up-to-date: %zu" + , result.m_catalogAssets.size() + , result.m_loadErrors.size() + , result.m_unfiltered.size() + , result.m_filteredAssets.size())); + + m_view->spinner->SetText(spinnerText); + SetSpinnerIsBusy(false); + m_view->progressBar->setVisible(false); + EnableAllUpgradeButtons(); + + if (!result.m_unfiltered.empty()) + { + m_view->upgradeAllButton->setEnabled(true); + } + } + + void Controller::OnScanFilteredGraph(const AZ::Data::AssetInfo& info) + { + OnScannedGraph(info, Filtered::Yes); + } + + void Controller::OnScannedGraph(const AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] Filtered filtered) + { + const int rowIndex = m_view->tableWidget->rowCount(); + + if (filtered == Filtered::No || !m_view->onlyShowOutdated->isChecked()) + { + m_view->tableWidget->insertRow(rowIndex); + QTableWidgetItem* rowName = new QTableWidgetItem(tr(assetInfo.m_relativePath.c_str())); + m_view->tableWidget->setItem(rowIndex, static_cast(ColumnAsset), rowName); + SetRowSucceeded(rowIndex); + + if (filtered == Filtered::No) + { + QPushButton* upgradeButton = new QPushButton(this); + upgradeButton->setText("Upgrade"); + upgradeButton->setEnabled(false); + SetRowBusy(rowIndex); + + connect + ( upgradeButton + , &QPushButton::pressed + , this + , [this, assetInfo]() + { + this->OnButtonPressUpgradeSingle(assetInfo); + }); + + m_view->tableWidget->setCellWidget(rowIndex, static_cast(ColumnAction), upgradeButton); + } + + char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 }; + AZStd::string path = AZStd::string::format("@devroot@/%s", assetInfo.m_relativePath.c_str()); + AZ::IO::FileIOBase::GetInstance()->ResolvePath(path.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN); + AZ::StringFunc::Path::GetFullPath(resolvedBuffer, path); + AZ::StringFunc::Path::Normalize(path); + + bool result = false; + AZ::Data::AssetInfo info; + AZStd::string watchFolder; + QByteArray assetNameUtf8 = assetInfo.m_relativePath.c_str(); + AzToolsFramework::AssetSystemRequestBus::BroadcastResult + ( result + , &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath + , assetNameUtf8 + , info + , watchFolder); + AZ_Error + ( ScriptCanvas::k_VersionExplorerWindow.data() + , result + , "Failed to locate asset info for '%s'.", assetNameUtf8.constData()); + + QToolButton* browseButton = new QToolButton(this); + browseButton->setToolTip(AzQtComponents::fileBrowserActionName()); + browseButton->setIcon(QIcon(":/stylesheet/img/UI20/browse-edit.svg")); + + QString absolutePath = QDir(watchFolder.c_str()).absoluteFilePath(info.m_relativePath.c_str()); + connect(browseButton, &QPushButton::clicked, [absolutePath] { + AzQtComponents::ShowFileOnDesktop(absolutePath); + }); + + m_view->tableWidget->setCellWidget(rowIndex, static_cast(ColumnBrowse), browseButton); + } + + OnScannedGraphResult(assetInfo); + } + + void Controller::OnScannedGraphResult([[maybe_unused]] const AZ::Data::AssetInfo& info) + { + m_view->progressBar->setValue(aznumeric_cast(m_handledAssetCount)); + ++m_handledAssetCount; + AddLogEntries(); + } + + void Controller::OnScanLoadFailure(const AZ::Data::AssetInfo& info) + { + const int rowIndex = m_view->tableWidget->rowCount(); + m_view->tableWidget->insertRow(rowIndex); + QTableWidgetItem* rowName = new QTableWidgetItem + ( tr(AZStd::string::format("Load Error: %s", info.m_relativePath.c_str()).c_str())); + m_view->tableWidget->setItem(rowIndex, static_cast(ColumnAsset), rowName); + SetRowFailed(rowIndex, "Load failed"); + OnScannedGraphResult(info); + } + + void Controller::OnScanUnFilteredGraph(const AZ::Data::AssetInfo& info) + { + OnScannedGraph(info, Filtered::No); + } + + void Controller::OnUpgradeBegin + ( const ModifyConfiguration& config + , [[maybe_unused]] const WorkingAssets& assets) + { + QString spinnerText = QStringLiteral("Upgrade in progress - "); + if (config.modifySingleAsset.m_assetId.IsValid()) + { + spinnerText.append(" single graph"); + + if (assets.size() == 1) + { + for (auto* item : FindTableItems(assets.front().info)) + { + int row = item->row(); + SetRowBusy(row); + } + } + } + else + { + for (int row = 0; row < m_view->tableWidget->rowCount(); ++row) + { + if (QPushButton* button = qobject_cast(m_view->tableWidget->cellWidget(row, ColumnAction))) + { + button->setEnabled(false); + } + + SetRowBusy(row); + } + + spinnerText.append(" all scanned graphs"); + } + + m_view->spinner->SetText(spinnerText); + SetSpinnerIsBusy(true); + } + + void Controller::SetLoggingPreferences() + { + LogBus::Broadcast(&LogTraits::SetVerbose, m_view->verbose->isChecked()); + LogBus::Broadcast(&LogTraits::SetVersionExporerExclusivity, m_view->updateReportingOnly->isChecked()); + } + + void Controller::SetSpinnerIsBusy(bool isBusy) + { + m_view->spinner->SetIsBusy(isBusy); + m_view->spinner->SetBusyIconSize(16); + } + + void Controller::OnUpgradeComplete(const ModificationResults& result) + { + QString spinnerText = QStringLiteral("Upgrade Complete - "); + spinnerText.append(QString::asprintf(" - Upgraded: %zu, Failed: %zu" + , result.m_successes.size() + , result.m_failures.size())); + m_view->spinner->SetText(spinnerText); + SetSpinnerIsBusy(false); + AddLogEntries(); + EnableAllUpgradeButtons(); + m_view->scanButton->setEnabled(true); + } + + void Controller::OnUpgradeDependenciesGathered(const AZ::Data::AssetInfo& info, Result result) + { + for (auto* item : FindTableItems(info)) + { + int row = item->row(); + + if (result == Result::Success) + { + SetRowSucceeded(row); + } + else + { + SetRowFailed(row, ""); + } + + if (QPushButton* button = qobject_cast(m_view->tableWidget->cellWidget(row, ColumnAction))) + { + button->setEnabled(true); + } + } + + m_view->progressBar->setVisible(true); + ++m_handledAssetCount; + m_view->progressBar->setValue(m_handledAssetCount); + AddLogEntries(); + } + + void Controller::OnUpgradeDependencySortBegin + ( [[maybe_unused]] const ModifyConfiguration& config + , const WorkingAssets& assets) + { + m_handledAssetCount = 0; + m_view->progressBar->setVisible(true); + m_view->progressBar->setRange(0, aznumeric_caster(assets.size())); + m_view->progressBar->setValue(0); + m_view->scanButton->setEnabled(false); + m_view->upgradeAllButton->setEnabled(false); + m_view->onlyShowOutdated->setEnabled(false); + + for (int row = 0; row != m_view->tableWidget->rowCount(); ++row) + { + if (QPushButton* button = qobject_cast(m_view->tableWidget->cellWidget(row, ColumnAction))) + { + button->setEnabled(false); + SetRowBusy(row); + } + } + + QString spinnerText = QStringLiteral("Upgrade in progress - gathering dependencies for the scanned graphs"); + m_view->spinner->SetText(spinnerText); + SetSpinnerIsBusy(true); + } + + void Controller::OnUpgradeDependencySortEnd + ( [[maybe_unused]] const ModifyConfiguration& config + , const WorkingAssets& assets + , [[maybe_unused]] const AZStd::vector& sortedOrder) + { + m_handledAssetCount = 0; + m_view->progressBar->setRange(0, aznumeric_caster(assets.size())); + m_view->progressBar->setValue(0); + m_view->progressBar->setVisible(true); + + for (int row = 0; row != m_view->tableWidget->rowCount(); ++row) + { + if (QPushButton* button = qobject_cast(m_view->tableWidget->cellWidget(row, ColumnAction))) + { + button->setEnabled(false); + SetRowPending(row); + } + } + + QString spinnerText = QStringLiteral("Upgrade in progress - gathering dependencies is complete"); + m_view->spinner->SetText(spinnerText); + SetSpinnerIsBusy(false); + AddLogEntries(); + } + + void Controller::SetRowBusy(int index) + { + if (index >= m_view->tableWidget->rowCount()) + { + return; + } + + AzQtComponents::StyledBusyLabel* busy = new AzQtComponents::StyledBusyLabel(this); + busy->SetBusyIconSize(16); + m_view->tableWidget->setCellWidget(index, ColumnStatus, busy); + } + + void Controller::SetRowFailed(int index, AZStd::string_view message) + { + if (index >= m_view->tableWidget->rowCount()) + { + return; + } + + QToolButton* doneButton = new QToolButton(this); + doneButton->setIcon(QIcon(":/stylesheet/img/UI20/titlebar-close.svg")); + doneButton->setToolTip(message.data()); + m_view->tableWidget->setCellWidget(index, ColumnStatus, doneButton); + } + + void Controller::SetRowPending(int index) + { + m_view->tableWidget->removeCellWidget(index, ColumnStatus); + } + + void Controller::SetRowsBusy() + { + for (int i = 0; i != m_view->tableWidget->rowCount(); ++i) + { + SetRowBusy(i); + } + } + + void Controller::SetRowSucceeded(int index) + { + if (index >= m_view->tableWidget->rowCount()) + { + return; + } + + QToolButton* doneButton = new QToolButton(this); + doneButton->setIcon(QIcon(":/stylesheet/img/UI20/checkmark-menu.svg")); + m_view->tableWidget->setCellWidget(index, ColumnStatus, doneButton); + } + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.h new file mode 100644 index 0000000000..9842ea3da4 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.h @@ -0,0 +1,108 @@ +/* + * 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 +#include +#include +#include +#include +AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") +#include +AZ_POP_DISABLE_WARNING +#endif + +class QPushButton; +class QTableWidgetItem; + +namespace Ui +{ + class View; +} + +namespace AzQtComponents +{ + class StyledBusyLabel; +} + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + //! A tool that collects and upgrades all Script Canvas graphs in the asset catalog + //! Handles display change notifications, handles state change notifications, sends control requests + class Controller + : public AzQtComponents::StyledDialog + , private UpgradeNotificationsBus::Handler + , private ModelNotificationsBus::Handler + { + Q_OBJECT + + public: + AZ_CLASS_ALLOCATOR(Controller, AZ::SystemAllocator, 0); + + explicit Controller(QWidget* parent = nullptr); + ~Controller(); + + private: + static constexpr int ColumnAsset = 0; + static constexpr int ColumnAction = 1; + static constexpr int ColumnBrowse = 2; + static constexpr int ColumnStatus = 3; + + Ui::View* m_view = nullptr; + int m_handledAssetCount = 0; + + void AddLogEntries(); + void EnableAllUpgradeButtons(); + QList FindTableItems(const AZ::Data::AssetInfo& assetInfo); + + void OnButtonPressClose(); + void OnButtonPressScan(); + void OnButtonPressUpgrade(); + void OnButtonPressUpgradeImplementation(const AZ::Data::AssetInfo& assetInfo); + void OnButtonPressUpgradeSingle(const AZ::Data::AssetInfo& assetInfo); + + void OnGraphUpgradeComplete(AZ::Data::Asset&, bool skipped) override; + + void OnScanBegin(size_t assetCount) override; + void OnScanComplete(const ScanResult& result) override; + void OnScanFilteredGraph(const AZ::Data::AssetInfo& info) override; + void OnScanLoadFailure(const AZ::Data::AssetInfo& info) override; + void OnScanUnFilteredGraph(const AZ::Data::AssetInfo& info) override; + enum class Filtered { No, Yes }; + void OnScannedGraph(const AZ::Data::AssetInfo& info, Filtered filtered); + void OnScannedGraphResult(const AZ::Data::AssetInfo& info); + + // for single operation UI updates, just check the assets size, or note it on the request + void OnUpgradeBegin(const ModifyConfiguration& config, const WorkingAssets& assets) override; + void OnUpgradeComplete(const ModificationResults& results) override; + void OnUpgradeDependenciesGathered(const AZ::Data::AssetInfo& info, Result result) override; + void OnUpgradeDependencySortBegin(const ModifyConfiguration& config, const WorkingAssets& assets) override; + void OnUpgradeDependencySortEnd + ( const ModifyConfiguration& config + , const WorkingAssets& assets + , const AZStd::vector& sortedOrder) override; + void OnUpgradeModificationBegin(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info) override; + void OnUpgradeModificationEnd(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info, ModificationResult result) override; + + void SetLoggingPreferences(); + void SetSpinnerIsBusy(bool isBusy); + void SetRowBusy(int index); + void SetRowFailed(int index, AZStd::string_view message); + void SetRowPending(int index); + void SetRowsBusy(); + void SetRowSucceeded(int index); + }; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp new file mode 100644 index 0000000000..f067feeca4 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp @@ -0,0 +1,233 @@ +/* + * 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 FileSaverCpp +{ + class FileEventHandler + : public AZ::IO::FileIOEventBus::Handler + { + public: + int m_errorCode = 0; + AZStd::string m_fileName; + + FileEventHandler() + { + BusConnect(); + } + + ~FileEventHandler() + { + BusDisconnect(); + } + + void OnError(const AZ::IO::SystemFile* /*file*/, const char* fileName, int errorCode) override + { + m_errorCode = errorCode; + + if (fileName) + { + m_fileName = fileName; + } + } + }; +} + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + FileSaver::FileSaver + ( AZStd::function onReadOnlyFile + , AZStd::function onComplete) + : m_onReadOnlyFile(onReadOnlyFile) + , m_onComplete(onComplete) + {} + + void FileSaver::PerformMove + ( AZStd::string tmpFileName + , AZStd::string target + , size_t remainingAttempts) + { + FileSaverCpp::FileEventHandler fileEventHandler; + + if (remainingAttempts == 0) + { + AZ::SystemTickBus::QueueFunction([this, tmpFileName]() + { + FileSaveResult result; + result.fileSaveError = "Failed to move updated file from temporary location to tmpFileName destination"; + result.tempFileRemovalError = RemoveTempFile(tmpFileName); + m_onComplete(result); + }); + } + else if (remainingAttempts == 2) + { + auto streamer = AZ::Interface::Get(); + // before the last attempt, flush all the caches + AZ::IO::FileRequestPtr flushRequest = streamer->FlushCaches(); + streamer->SetRequestCompleteCallback(flushRequest + , [this, remainingAttempts, tmpFileName, target]([[maybe_unused]] AZ::IO::FileRequestHandle request) + { + // One last try + AZ::SystemTickBus::QueueFunction( + [this, remainingAttempts, tmpFileName, target]() { PerformMove(tmpFileName, target, remainingAttempts - 1); }); + }); + streamer->QueueRequest(flushRequest); + } + else + { + // the actual move attempt + auto moveResult = AZ::IO::SmartMove(tmpFileName.c_str(), target.c_str()); + if (moveResult.GetResultCode() == AZ::IO::ResultCode::Success) + { + auto streamer = AZ::Interface::Get(); + AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); + // Bump the slice asset up in the asset processor's queue. + AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str()); + AZ::SystemTickBus::QueueFunction([this, tmpFileName]() + { + FileSaveResult result; + result.tempFileRemovalError = RemoveTempFile(tmpFileName); + m_onComplete(result); + }); + } + else + { + AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "moving converted file to tmpFileName destination failed: %s, trying again", target.c_str()); + auto streamer = AZ::Interface::Get(); + AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); + streamer->SetRequestCompleteCallback(flushRequest, [this, tmpFileName, target, remainingAttempts]([[maybe_unused]] AZ::IO::FileRequestHandle request) + { + // Continue saving. + AZ::SystemTickBus::QueueFunction([this, tmpFileName, target, remainingAttempts]() { PerformMove(tmpFileName, target, remainingAttempts - 1); }); + }); + streamer->QueueRequest(flushRequest); + } + } + } + + void FileSaver::OnSourceFileReleased(AZ::Data::Asset asset) + { + AZStd::string relativePath, fullPath; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); + bool fullPathFound = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, fullPath); + AZStd::string tmpFileName; + // here we are saving the graph to a temp file instead of the original file and then copying the temp file to the original file. + // This ensures that AP will not a get a file change notification on an incomplete graph file causing it to fail processing. Temp files are ignored by AP. + if (!AZ::IO::CreateTempFileName(fullPath.c_str(), tmpFileName)) + { + FileSaveResult result; + result.fileSaveError = "Failure to create temporary file name"; + m_onComplete(result); + return; + } + + bool tempSavedSucceeded = false; + AZ::IO::FileIOStream fileStream(tmpFileName.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText); + if (fileStream.IsOpen()) + { + if (asset.GetType() == azrtti_typeid()) + { + ScriptCanvasEditor::ScriptCanvasAssetHandler handler; + tempSavedSucceeded = handler.SaveAssetData(asset, &fileStream); + } + + fileStream.Close(); + } + + if (!tempSavedSucceeded) + { + FileSaveResult result; + result.fileSaveError = "Save asset data to temporary file failed"; + m_onComplete(result); + return; + } + + AzToolsFramework::SourceControlCommandBus::Broadcast + ( &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit + , fullPath.c_str() + , true + , [this, fullPath, tmpFileName]([[maybe_unused]] bool success, const AzToolsFramework::SourceControlFileInfo& info) + { + constexpr const size_t k_maxAttemps = 10; + + if (!info.IsReadOnly()) + { + PerformMove(tmpFileName, fullPath, k_maxAttemps); + } + else if (m_onReadOnlyFile && m_onReadOnlyFile()) + { + AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true); + PerformMove(tmpFileName, fullPath, k_maxAttemps); + } + else + { + FileSaveResult result; + result.fileSaveError = "Source file was and remained read-only"; + result.tempFileRemovalError = RemoveTempFile(tmpFileName); + m_onComplete(result); + } + }); + } + + AZStd::string FileSaver::RemoveTempFile(AZStd::string_view tempFile) + { + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + if (!fileIO) + { + return "GraphUpgradeComplete: No FileIO instance"; + } + + if (fileIO->Exists(tempFile.data()) && !fileIO->Remove(tempFile.data())) + { + return AZStd::string::format("Failed to remove temporary file: %s", tempFile.data()); + } + + return ""; + } + + void FileSaver::Save(AZ::Data::Asset asset) + { + AZStd::string relativePath, fullPath; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); + bool fullPathFound = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult + (fullPathFound + , &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath + , relativePath, fullPath); + + if (!fullPathFound) + { + FileSaveResult result; + result.fileSaveError = "Full source path not found"; + m_onComplete(result); + } + else + { + auto streamer = AZ::Interface::Get(); + AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(fullPath); + streamer->SetRequestCompleteCallback(flushRequest, [this, asset]([[maybe_unused]] AZ::IO::FileRequestHandle request) + { + this->OnSourceFileReleased(asset); + }); + streamer->QueueRequest(flushRequest); + } + } + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.h new file mode 100644 index 0000000000..dd333927ed --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.h @@ -0,0 +1,48 @@ +/* + * 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 ScriptCanvasEditor +{ + namespace VersionExplorer + { + struct FileSaveResult + { + AZStd::string fileSaveError; + AZStd::string tempFileRemovalError; + }; + + class FileSaver + { + public: + AZ_CLASS_ALLOCATOR(FileSaver, AZ::SystemAllocator, 0); + + FileSaver + ( AZStd::function onReadOnlyFile + , AZStd::function onComplete); + + void Save(AZ::Data::Asset asset); + + private: + AZStd::function m_onComplete; + AZStd::function m_onReadOnlyFile; + + void OnSourceFileReleased(AZ::Data::Asset asset); + + void PerformMove + ( AZStd::string source + , AZStd::string target + , size_t remainingAttempts); + + AZStd::string RemoveTempFile(AZStd::string_view tempFile); + }; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/LogTraits.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/LogTraits.h new file mode 100644 index 0000000000..47cebe7324 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/LogTraits.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 + +#define VE_LOG(...) LogBus::Broadcast(&LogTraits::Entry, __VA_ARGS__); + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + class LogTraits + : public AZ::EBusTraits + { + public: + virtual void Activate() = 0; + virtual void Clear() = 0; + virtual void Deactivate() = 0; + virtual void Entry(const char* format, ...) = 0; + virtual const AZStd::vector* GetEntries() const = 0; + virtual void SetVersionExporerExclusivity(bool enabled) = 0; + virtual void SetVerbose(bool verbosity) = 0; + }; + using LogBus = AZ::EBus; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Model.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Model.cpp new file mode 100644 index 0000000000..d8d242adbb --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Model.cpp @@ -0,0 +1,175 @@ +/* + * 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 ModifierCpp +{ + +} + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + EditorKeepAlive::EditorKeepAlive() + { + ISystem* system = nullptr; + CrySystemRequestBus::BroadcastResult(system, &CrySystemRequestBus::Events::GetCrySystem); + + if (system) + { + m_edKeepEditorActive = system->GetIConsole()->GetCVar("ed_KeepEditorActive"); + + if (m_edKeepEditorActive) + { + m_keepEditorActive = m_edKeepEditorActive->GetIVal(); + m_edKeepEditorActive->Set(1); + } + } + } + + EditorKeepAlive::~EditorKeepAlive() + { + if (m_edKeepEditorActive) + { + m_edKeepEditorActive->Set(m_keepEditorActive); + } + } + + Model::Model() + { + ModelRequestsBus::Handler::BusConnect(); + } + + void Model::CacheSettings() + { + m_settingsCache = AZStd::make_unique(); + ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false; + ScriptCanvas::Grammar::g_printAbstractCodeModel = false; + ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false; + } + + const ModificationResults* Model::GetResults() + { + return !IsWorking() ? &m_modResults : nullptr; + } + + void Model::Idle() + { + m_state = State::Idle; + m_keepEditorAlive.reset(); + m_log.Deactivate(); + } + + bool Model::IsReadyToModify() const + { + if (IsWorking()) + { + return false; + } + + if (!m_scanner) + { + return false; + } + + return !m_scanner->GetResult().m_unfiltered.empty(); + } + + bool Model::IsWorking() const + { + return m_state != State::Idle; + } + + void Model::Modify(const ModifyConfiguration& modification) + { + if (!IsReadyToModify()) + { + AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Explorer is not ready to modify graphs."); + return; + } + + if (modification.modifySingleAsset.m_assetId.IsValid()) + { + const auto& results = m_scanner->GetResult(); + auto iter = AZStd::find_if + ( results.m_unfiltered.begin() + , results.m_unfiltered.end() + , [&modification](const auto& candidate) + { + return candidate.info.m_assetId == modification.modifySingleAsset.m_assetId; + }); + + if (iter == results.m_unfiltered.end()) + { + AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Requested upgrade graph not found in scanned list."); + return; + } + + + m_state = State::ModifySingle; + m_modifier = AZStd::make_unique(modification, WorkingAssets{ *iter }, [this]() { OnModificationComplete(); }); + } + else + { + auto results = m_scanner->TakeResult(); + m_state = State::ModifyAll; + m_modifier = AZStd::make_unique(modification, AZStd::move(results.m_unfiltered), [this]() { OnModificationComplete(); }); + } + + m_modResults = {}; + m_log.Activate(); + m_keepEditorAlive = AZStd::make_unique(); + } + + void Model::OnModificationComplete() + { + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeComplete, m_modifier->GetResult()); + m_modifier.reset(); + + if (m_state == State::ModifyAll) + { + m_scanner.reset(); + } + + Idle(); + } + + void Model::OnScanComplete() + { + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnScanComplete, m_scanner->GetResult()); + Idle(); + } + + void Model::Scan(const ScanConfiguration& config) + { + if (IsWorking()) + { + AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Explorer is already working"); + return; + } + + m_state = State::Scanning; + m_log.Activate(); + m_keepEditorAlive = AZStd::make_unique(); + m_scanner = AZStd::make_unique(config, [this](){ OnScanComplete(); }); + } + + void Model::RestoreSettings() + { + m_settingsCache.reset(); + } + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Model.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Model.h new file mode 100644 index 0000000000..ab0f710870 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Model.h @@ -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 +#include +#include +#include +#include +#include + +struct ICVar; + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + //! Scoped utility to set and restore the "ed_KeepEditorActive" CVar in order to allow + //! the upgrade tool to work even if the editor is not in the foreground + class EditorKeepAlive + { + public: + EditorKeepAlive(); + ~EditorKeepAlive(); + + private: + int m_keepEditorActive; + ICVar* m_edKeepEditorActive; + }; + + //! Handles model change requests, state queries; sends state change notifications + class Model + : public ModelRequestsBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(Model, AZ::SystemAllocator, 0); + + Model(); + + const ModificationResults* GetResults() override; + + void Modify(const ModifyConfiguration& modification) override; + + void Scan(const ScanConfiguration& config) override; + + private: + enum class State + { + Idle, + Scanning, + ModifyAll, + ModifySingle + }; + + State m_state = State::Idle; + Log m_log; + + // these two are managed by the same class because the modifer will only operate on the results of the scanner + AZStd::unique_ptr m_modifier; + AZStd::unique_ptr m_scanner; + AZStd::unique_ptr m_settingsCache; + AZStd::unique_ptr m_keepEditorAlive; + + ModificationResults m_modResults; + + void CacheSettings(); + void Idle(); + bool IsReadyToModify() const; + bool IsWorking() const; + void OnModificationComplete(); + void OnScanComplete(); + void RestoreSettings(); + }; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h new file mode 100644 index 0000000000..01eb200542 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h @@ -0,0 +1,108 @@ +/* + * 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 ScriptCanvasEditor +{ + namespace VersionExplorer + { + struct WorkingAsset + { + AZ::Data::Asset asset; + AZ::Data::AssetInfo info; + }; + + using WorkingAssets = AZStd::vector; + + struct ModifyConfiguration + { + AZStd::function)> modification; + AZStd::function onReadOnlyFile; + AZ::Data::AssetInfo modifySingleAsset; + bool backupGraphBeforeModification = false; + bool successfulDependencyUpgradeRequired = true; + }; + + struct ModificationResult + { + AZ::Data::Asset asset; + AZ::Data::AssetInfo assetInfo; + AZStd::string errorMessage; + }; + + struct ModificationResults + { + AZStd::vector m_successes; + AZStd::vector m_failures; + }; + + struct ScanConfiguration + { + AZStd::function)> filter; + bool reportFilteredGraphs = false; + }; + + struct ScanResult + { + AZStd::vector m_catalogAssets; + WorkingAssets m_unfiltered; + AZStd::vector m_filteredAssets; + AZStd::vector m_loadErrors; + }; + + enum Result + { + Failure, + Success + }; + + class ModificationNotificationsTraits + : public AZ::EBusTraits + { + public: + virtual void ModificationComplete(const ModificationResult& result) = 0; + }; + using ModificationNotificationsBus = AZ::EBus; + + class ModelRequestsTraits + : public AZ::EBusTraits + { + public: + virtual const ModificationResults* GetResults() = 0; + virtual void Modify(const ModifyConfiguration& modification) = 0; + virtual void Scan(const ScanConfiguration& filter) = 0; + }; + using ModelRequestsBus = AZ::EBus; + + class ModelNotificationsTraits + : public AZ::EBusTraits + { + public: + virtual void OnScanBegin(size_t assetCount) = 0; + virtual void OnScanComplete(const ScanResult& result) = 0; + virtual void OnScanFilteredGraph(const AZ::Data::AssetInfo& info) = 0; + virtual void OnScanLoadFailure(const AZ::Data::AssetInfo& info) = 0; + virtual void OnScanUnFilteredGraph(const AZ::Data::AssetInfo& info) = 0; + + virtual void OnUpgradeBegin(const ModifyConfiguration& config, const WorkingAssets& assets) = 0; + virtual void OnUpgradeComplete(const ModificationResults& results) = 0; + virtual void OnUpgradeDependenciesGathered(const AZ::Data::AssetInfo& info, Result result) = 0; + virtual void OnUpgradeDependencySortBegin(const ModifyConfiguration& config, const WorkingAssets& assets) = 0; + virtual void OnUpgradeDependencySortEnd + ( const ModifyConfiguration& config + , const WorkingAssets& assets + , const AZStd::vector& sortedOrder) = 0; + virtual void OnUpgradeModificationBegin(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info) = 0; + virtual void OnUpgradeModificationEnd(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info, ModificationResult result) = 0; + }; + using ModelNotificationsBus = AZ::EBus; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Modifier.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Modifier.cpp new file mode 100644 index 0000000000..f3da1ba309 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Modifier.cpp @@ -0,0 +1,405 @@ +/* + * 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 ModifierCpp +{ + +} + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + Modifier::Modifier + ( const ModifyConfiguration& modification + , WorkingAssets&& assets + , AZStd::function onComplete) + : m_state(State::GatheringDependencies) + , m_config(modification) + , m_assets(assets) + , m_onComplete(onComplete) + { + AZ_Assert(m_config.modification, "No modification function provided"); + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeBegin, modification, m_assets); + AZ::SystemTickBus::Handler::BusConnect(); + } + + const AZ::Data::AssetInfo& Modifier::GetCurrentAsset() const + { + return m_state == State::GatheringDependencies + ? m_assets[m_assetIndex].info + : m_assets[m_dependencyOrderedAssetIndicies[m_assetIndex]].info; + } + + AZStd::unordered_set& Modifier::GetOrCreateDependencyIndexSet() + { + auto iter = m_dependencies.find(m_assetIndex); + if (iter == m_dependencies.end()) + { + iter = m_dependencies.insert_or_assign(m_assetIndex, AZStd::unordered_set()).first; + } + + return iter->second; + } + + const ModificationResults& Modifier::GetResult() const + { + return m_results; + } + + void Modifier::GatherDependencies() + { + AZ::SerializeContext* serializeContext{}; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + AZ_Assert(serializeContext, "SerializeContext is required to enumerate dependent assets in the ScriptCanvas file"); + + bool anyFailures = false; + auto asset = LoadAsset(); + + if (asset + && asset.GetAs() + && asset.GetAs()->GetScriptCanvasGraph() + && asset.GetAs()->GetScriptCanvasGraph()->GetGraphData()) + { + auto graphData = asset.GetAs()->GetScriptCanvasGraph()->GetGraphData(); + + auto dependencyGrabber = [this] + ( void* instancePointer + , const AZ::SerializeContext::ClassData* classData + , [[maybe_unused]] const AZ::SerializeContext::ClassElement* classElement) + { + if (auto azTypeId = classData->m_azRtti->GetTypeId(); + azTypeId == azrtti_typeid>()) + { + const auto* subgraphAsset = + reinterpret_cast*>(instancePointer); + if (subgraphAsset->GetId().IsValid()) + { + if (auto iter = m_assetInfoIndexById.find(subgraphAsset->GetId().m_guid); iter != m_assetInfoIndexById.end()) + { + // insert the index of the dependency into the set that belongs to this asset + GetOrCreateDependencyIndexSet().insert(iter->second); + } + } + } + // always continue, make note of the script canvas dependencies + return true; + }; + + if (!serializeContext->EnumerateInstanceConst + ( graphData + , azrtti_typeid() + , dependencyGrabber + , {} + , AZ::SerializeContext::ENUM_ACCESS_FOR_READ + , nullptr + , nullptr)) + { + anyFailures = true; + VE_LOG("Modifier: ERROR - Failed to gather dependencies from graph data: %s" + , GetCurrentAsset().m_relativePath.c_str()) + } + } + else + { + anyFailures = true; + VE_LOG("Modifier: ERROR - Failed to load asset %s for modification, even though it scanned properly" + , GetCurrentAsset().m_relativePath.c_str()); + } + + ModelNotificationsBus::Broadcast + ( &ModelNotificationsTraits::OnUpgradeDependenciesGathered + , GetCurrentAsset() + , anyFailures ? Result::Failure : Result::Success); + + // Flush asset database events to ensure no asset references are held by closures queued on Ebuses. + AZ::Data::AssetManager::Instance().DispatchEvents(); + } + + AZ::Data::Asset Modifier::LoadAsset() + { + AZ::Data::Asset asset = AZ::Data::AssetManager::Instance().GetAsset + ( GetCurrentAsset().m_assetId + , azrtti_typeid() + , AZ::Data::AssetLoadBehavior::PreLoad); + + asset.BlockUntilLoadComplete(); + + if (asset.IsReady()) + { + return asset; + } + else + { + return {}; + } + } + + void Modifier::ModificationComplete(const ModificationResult& result) + { + m_result = result; + + if (result.errorMessage.empty()) + { + SaveModifiedGraph(result); + } + else + { + ReportModificationError(result.errorMessage); + } + } + + void Modifier::ModifyCurrentAsset() + { + m_result = {}; + m_result.assetInfo = GetCurrentAsset(); + + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeModificationBegin, m_config, GetCurrentAsset()); + + if (auto asset = LoadAsset()) + { + ModificationNotificationsBus::Handler::BusConnect(); + m_modifyState = ModifyState::InProgress; + m_config.modification(asset); + } + else + { + ReportModificationError("Failed to load during modification"); + } + } + + void Modifier::ModifyNextAsset() + { + ModelNotificationsBus::Broadcast + ( &ModelNotificationsTraits::OnUpgradeModificationEnd, m_config, GetCurrentAsset(), m_result); + ModificationNotificationsBus::Handler::BusDisconnect(); + m_modifyState = ModifyState::Idle; + ++m_assetIndex; + m_result = {}; + } + + void Modifier::ReportModificationError(AZStd::string_view report) + { + m_result.asset = {}; + m_result.errorMessage = report; + m_results.m_failures.push_back(m_result); + ModifyNextAsset(); + } + + void Modifier::ReportModificationSuccess() + { + m_results.m_successes.push_back(m_result.assetInfo); + ModifyNextAsset(); + } + + void Modifier::ReportSaveResult() + { + AZStd::lock_guard lock(m_mutex); + m_fileSaver.reset(); + + if (m_fileSaveResult.fileSaveError.empty()) + { + ReportModificationSuccess(); + } + else + { + ReportModificationError(m_fileSaveResult.fileSaveError); + } + + m_fileSaveResult = {}; + m_modifyState = ModifyState::Idle; + } + + void Modifier::OnFileSaveComplete(const FileSaveResult& result) + { + if (!result.tempFileRemovalError.empty()) + { + VE_LOG + ( "Temporary file not removed for %s: %s" + , m_result.assetInfo.m_relativePath.c_str() + , result.tempFileRemovalError.c_str()); + } + + AZStd::lock_guard lock(m_mutex); + m_modifyState = ModifyState::ReportResult; + m_fileSaver.reset(); + m_fileSaveResult = result; + } + + void Modifier::OnSystemTick() + { + switch (m_state) + { + case State::GatheringDependencies: + TickGatherDependencies(); + break; + + case State::ModifyingGraphs: + TickUpdateGraph(); + break; + } + + AZ::Data::AssetManager::Instance().DispatchEvents(); + AZ::SystemTickBus::ExecuteQueuedEvents(); + } + + void Modifier::SaveModifiedGraph(const ModificationResult& result) + { + m_modifyState = ModifyState::Saving; + m_fileSaver = AZStd::make_unique + ( m_config.onReadOnlyFile + , [this](const FileSaveResult& result) { OnFileSaveComplete(result); }); + m_fileSaver->Save(result.asset); + } + + void Modifier::SortGraphsByDependencies() + { + m_dependencyOrderedAssetIndicies.reserve(m_assets.size()); + Sorter sorter; + sorter.modifier = this; + sorter.Sort(); + } + + ModificationResults&& Modifier::TakeResult() + { + return AZStd::move(m_results); + } + + void Modifier::TickGatherDependencies() + { + if (m_assetIndex == 0) + { + if (m_config.successfulDependencyUpgradeRequired) + { + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeDependencySortBegin, m_config, m_assets); + m_assetInfoIndexById.reserve(m_assets.size()); + + for (size_t index = 0; index != m_assets.size(); ++index) + { + m_assetInfoIndexById.insert({ m_assets[index].info.m_assetId.m_guid, index }); + } + } + else + { + m_dependencyOrderedAssetIndicies.reserve(m_assets.size()); + + for (size_t index = 0; index != m_assets.size(); ++index) + { + m_dependencyOrderedAssetIndicies.push_back(index); + } + + // go straight into ModifyinGraphs + m_assetIndex = m_assets.size(); + } + } + + if (m_assetIndex == m_assets.size()) + { + if (m_config.successfulDependencyUpgradeRequired) + { + SortGraphsByDependencies(); + ModelNotificationsBus::Broadcast + ( &ModelNotificationsTraits::OnUpgradeDependencySortEnd + , m_config + , m_assets + , m_dependencyOrderedAssetIndicies); + } + + m_assetIndex = 0; + m_state = State::ModifyingGraphs; + } + else + { + GatherDependencies(); + ++m_assetIndex; + } + } + + void Modifier::TickUpdateGraph() + { + if (m_assetIndex == m_assets.size()) + { + VE_LOG("Modifier: Complete."); + AZ::SystemTickBus::Handler::BusDisconnect(); + + if (m_onComplete) + { + m_onComplete(); + } + } + else + { + AZStd::lock_guard lock(m_mutex); + + switch (m_modifyState) + { + case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::Idle: + ModifyCurrentAsset(); + break; + case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::ReportResult: + ReportSaveResult(); + break; + default: + break; + } + } + } + + const AZStd::unordered_set* Modifier::Sorter::GetDependencies(size_t index) const + { + auto iter = modifier->m_dependencies.find(index); + return iter != modifier->m_dependencies.end() ? &iter->second : nullptr; + } + + void Modifier::Sorter::Sort() + { + for (size_t index = 0; index != modifier->m_assets.size(); ++index) + { + Visit(index); + } + } + + void Modifier::Sorter::Visit(size_t index) + { + if (markedPermanent.contains(index)) + { + return; + } + + if (markedTemporary.contains(index)) + { + AZ_Error + (ScriptCanvas::k_VersionExplorerWindow.data() + , false + , "Modifier: Dependency sort has failed during, circular dependency detected for Asset: %s" + , modifier->GetCurrentAsset().m_relativePath.c_str()); + return; + } + + markedTemporary.insert(index); + + if (auto dependencies = GetDependencies(index)) + { + for (auto& dependency : *dependencies) + { + Visit(dependency); + } + } + + markedTemporary.erase(index); + markedPermanent.insert(index); + modifier->m_dependencyOrderedAssetIndicies.push_back(index); + } + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Modifier.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Modifier.h new file mode 100644 index 0000000000..981a1eb746 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Modifier.h @@ -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 + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + class Modifier + : private AZ::SystemTickBus::Handler + , private ModificationNotificationsBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(Modifier, AZ::SystemAllocator, 0); + + Modifier + ( const ModifyConfiguration& modification + , WorkingAssets&& assets + , AZStd::function onComplete); + + const ModificationResults& GetResult() const; + ModificationResults&& TakeResult(); + + private: + friend class Sorter; + + struct Sorter + { + Modifier* modifier; + AZStd::unordered_set markedPermanent; + AZStd::unordered_set markedTemporary; + void Sort(); + + private: + void Visit(size_t index); + const AZStd::unordered_set* GetDependencies(size_t index) const; + }; + + enum class State + { + GatheringDependencies, + ModifyingGraphs + }; + + enum class ModifyState + { + Idle, + InProgress, + Saving, + ReportResult + }; + + AZStd::recursive_mutex m_mutex; + + // the two states reside in this class because the modification is only complete if the new source file saves out + State m_state = State::GatheringDependencies; + ModifyState m_modifyState = ModifyState::Idle; + size_t m_assetIndex = 0; + AZStd::function m_onComplete; + // asset infos in scanned order + WorkingAssets m_assets; + // dependency sorted order indices into the asset vector + AZStd::vector m_dependencyOrderedAssetIndicies; + // dependency indices by asset info index (only exist if graphs have them) + AZStd::unordered_map> m_dependencies; + AZStd::unordered_map m_assetInfoIndexById; + AZStd::vector m_failures; + ModifyConfiguration m_config; + ModificationResult m_result; + ModificationResults m_results; + AZStd::unique_ptr m_fileSaver; + FileSaveResult m_fileSaveResult; + + void GatherDependencies(); + const AZ::Data::AssetInfo& GetCurrentAsset() const; + AZStd::unordered_set& GetOrCreateDependencyIndexSet(); + AZ::Data::Asset LoadAsset(); + void ModifyCurrentAsset(); + void ModifyNextAsset(); + void ModificationComplete(const ModificationResult& result) override; + void ReportModificationError(AZStd::string_view report); + void ReportModificationSuccess(); + void ReportSaveResult(); + void SaveModifiedGraph(const ModificationResult& result); + void SortGraphsByDependencies(); + void OnFileSaveComplete(const FileSaveResult& result); + void OnSystemTick() override; + void TickGatherDependencies(); + void TickUpdateGraph(); + }; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Scanner.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Scanner.cpp new file mode 100644 index 0000000000..bc69f6e634 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Scanner.cpp @@ -0,0 +1,118 @@ +/* + * 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 ScriptCanvasEditor +{ + namespace VersionExplorer + { + Scanner::Scanner(const ScanConfiguration& config, AZStd::function onComplete) + : m_config(config) + , m_onComplete(onComplete) + { + AZ::Data::AssetCatalogRequestBus::Broadcast + ( &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets + , nullptr + , [this](const AZ::Data::AssetId, const AZ::Data::AssetInfo& assetInfo) + { + if (assetInfo.m_assetType == azrtti_typeid()) + { + m_result.m_catalogAssets.push_back(assetInfo); + } + } + , nullptr); + + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnScanBegin, m_result.m_catalogAssets.size()); + AZ::SystemTickBus::Handler::BusConnect(); + } + + void Scanner::FilterAsset(AZ::Data::Asset asset) + { + if (m_config.filter && m_config.filter(asset)) + { + VE_LOG("Scanner: Excluded: %s ", GetCurrentAsset().m_relativePath.c_str()); + m_result.m_filteredAssets.push_back(GetCurrentAsset()); + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnScanFilteredGraph, GetCurrentAsset()); + } + else + { + VE_LOG("Scanner: Included: %s ", GetCurrentAsset().m_relativePath.c_str()); + m_result.m_unfiltered.push_back({ asset, GetCurrentAsset() }); + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnScanUnFilteredGraph, GetCurrentAsset()); + } + } + + const AZ::Data::AssetInfo& Scanner::GetCurrentAsset() const + { + return m_result.m_catalogAssets[m_catalogAssetIndex]; + } + + const ScanResult& Scanner::GetResult() const + { + return m_result; + } + + AZ::Data::Asset Scanner::LoadAsset() + { + AZ::Data::Asset asset = AZ::Data::AssetManager::Instance().GetAsset + ( GetCurrentAsset().m_assetId + , azrtti_typeid() + , AZ::Data::AssetLoadBehavior::PreLoad); + + asset.BlockUntilLoadComplete(); + + if (asset.IsReady()) + { + return asset; + } + else + { + return {}; + } + } + + void Scanner::OnSystemTick() + { + if (m_catalogAssetIndex == m_result.m_catalogAssets.size()) + { + VE_LOG("Scanner: Complete."); + AZ::SystemTickBus::Handler::BusDisconnect(); + + if (m_onComplete) + { + m_onComplete(); + } + } + else + { + if (auto asset = LoadAsset()) + { + VE_LOG("Scanner: Loaded: %s ", GetCurrentAsset().m_relativePath.c_str()); + FilterAsset(asset); + } + else + { + VE_LOG("Scanner: Failed to load: %s ", GetCurrentAsset().m_relativePath.c_str()); + m_result.m_loadErrors.push_back(GetCurrentAsset()); + ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnScanLoadFailure, GetCurrentAsset()); + } + + VE_LOG("Scanner: scan of %s complete", GetCurrentAsset().m_relativePath.c_str()); + ++m_catalogAssetIndex; + } + } + + ScanResult&& Scanner::TakeResult() + { + return AZStd::move(m_result); + } + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Scanner.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Scanner.h new file mode 100644 index 0000000000..fb030845c7 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Scanner.h @@ -0,0 +1,42 @@ +/* + * 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 ScriptCanvasEditor +{ + namespace VersionExplorer + { + class Scanner + : private AZ::SystemTickBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(Scanner, AZ::SystemAllocator, 0); + + Scanner(const ScanConfiguration& config, AZStd::function onComplete); + + const ScanResult& GetResult() const; + ScanResult&& TakeResult(); + + private: + size_t m_catalogAssetIndex = 0; + AZStd::function m_onComplete; + ScanConfiguration m_config; + ScanResult m_result; + + void FilterAsset(AZ::Data::Asset); + const AZ::Data::AssetInfo& GetCurrentAsset() const; + AZ::Data::Asset LoadAsset(); + void OnSystemTick() override; + }; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.cpp index 9670bb2f1f..14bc1c4dc8 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.cpp @@ -6,37 +6,32 @@ * */ +#include +#include #include #include -#include #include -#include #include -#include "UpgradeHelper.h" - #include #include #include #include - #include #include - +#include +#include #include #include #include - -#include - +#include #include +#include +#include #include #include - #include #include -#include -#include namespace ScriptCanvasEditor { @@ -52,40 +47,47 @@ namespace ScriptCanvasEditor m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); int rows = 0; - auto& graphsToUpgrade = AZ::Interface::Get()->GetGraphsThatNeedManualUpgrade(); - for (auto& assetId : graphsToUpgrade) + const VersionExplorer::ModificationResults* result = nullptr; + VersionExplorer::ModelRequestsBus::BroadcastResult(result, &VersionExplorer::ModelRequestsTraits::GetResults); + + if (result && !result->m_failures.empty()) { - auto assetInfo = ScriptCanvasEditor::AssetHelpers::GetAssetInfo(assetId); - m_ui->tableWidget->insertRow(rows); + for (auto& failedUpdate : result->m_failures) + { + auto& assetInfo = failedUpdate.assetInfo; + auto assetId = assetInfo.m_assetId; - connect(m_ui->closeButton, &QPushButton::pressed, this, &QDialog::accept); - connect(m_ui->tableWidget, &QTableWidget::itemDoubleClicked, this, [this, rows, assetId](QTableWidgetItem* item) - { - if (item && item->data(Qt::UserRole).toInt() == rows) + m_ui->tableWidget->insertRow(rows); + + connect(m_ui->closeButton, &QPushButton::pressed, this, &QDialog::accept); + connect(m_ui->tableWidget, &QTableWidget::itemDoubleClicked, this, [this, rows, assetId](QTableWidgetItem* item) { - OpenGraph(assetId); + if (item && item->data(Qt::UserRole).toInt() == rows) + { + OpenGraph(assetId); + } } - } - ); + ); + + auto openGraph = [this, assetId] { + OpenGraph(assetId); + }; - auto openGraph = [this, assetId] { - OpenGraph(assetId); - }; + QTableWidgetItem* rowName = new QTableWidgetItem(tr(assetInfo.m_relativePath.c_str())); + rowName->setData(Qt::UserRole, rows); + m_ui->tableWidget->setItem(rows, 0, rowName); - QTableWidgetItem* rowName = new QTableWidgetItem(tr(assetInfo.m_relativePath.c_str())); - rowName->setData(Qt::UserRole, rows); - m_ui->tableWidget->setItem(rows, 0, rowName); + QToolButton* rowGoToButton = new QToolButton(this); + rowGoToButton->setIcon(QIcon(":/stylesheet/img/UI20/open-in-internal-app.svg")); + rowGoToButton->setToolTip("Open Graph"); - QToolButton* rowGoToButton = new QToolButton(this); - rowGoToButton->setIcon(QIcon(":/stylesheet/img/UI20/open-in-internal-app.svg")); - rowGoToButton->setToolTip("Open Graph"); - - connect(rowGoToButton, &QToolButton::clicked, openGraph); + connect(rowGoToButton, &QToolButton::clicked, openGraph); - m_ui->tableWidget->setCellWidget(rows, 1, rowGoToButton); + m_ui->tableWidget->setCellWidget(rows, 1, rowGoToButton); - ++rows; + ++rows; + } } } 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/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.cpp deleted file mode 100644 index 5e6858c993..0000000000 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.cpp +++ /dev/null @@ -1,697 +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 "UpgradeTool.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#include "UpgradeHelper.h" - -namespace ScriptCanvasEditor -{ - UpgradeTool::UpgradeTool(QWidget* parent /*= nullptr*/) - : AzQtComponents::StyledDialog(parent) - , m_ui(new Ui::UpgradeTool()) - { - m_ui->setupUi(this); - - AzQtComponents::CheckBox::applyToggleSwitchStyle(m_ui->doNotAskCheckbox); - AzQtComponents::CheckBox::applyToggleSwitchStyle(m_ui->makeBackupCheckbox); - - m_ui->progressFrame->setVisible(false); - - connect(m_ui->upgradeButton, &QPushButton::pressed, this, &UpgradeTool::OnUpgrade); - connect(m_ui->notNowButton, &QPushButton::pressed, this, &UpgradeTool::OnNoThanks); - - UpgradeNotifications::Bus::Handler::BusConnect(); - AZ::Debug::TraceMessageBus::Handler::BusConnect(); - - resize(700, 100); - } - - void UpgradeTool::OnNoThanks() - { - DisconnectBuses(); - - UpdateSettings(); - - reject(); - } - - void UpgradeTool::UpdateSettings() - { - auto userSettings = AZ::UserSettings::CreateFind(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL); - if (userSettings) - { - userSettings->m_showUpgradeDialog = !m_ui->doNotAskCheckbox->isChecked(); - - AZ::UserSettingsOwnerRequestBus::Event(AZ::UserSettings::CT_LOCAL, &AZ::UserSettingsOwnerRequests::SaveSettings); - } - } - - UpgradeTool::~UpgradeTool() - { - DisconnectBuses(); - } - - void UpgradeTool::DisconnectBuses() - { - UpgradeNotifications::Bus::Handler::BusDisconnect(); - - AZ::SystemTickBus::Handler::BusDisconnect(); - AZ::Data::AssetBus::MultiHandler::BusDisconnect(); - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); - } - - void UpgradeTool::closeEvent(QCloseEvent* event) - { - // m_keepEditorAlive.reset(); - - DisconnectBuses(); - - UpgradeNotifications::Bus::Broadcast(&UpgradeNotifications::OnUpgradeCancelled); - - AzQtComponents::StyledDialog::closeEvent(event); - } - - bool UpgradeTool::HasBackup() const - { - return m_ui->makeBackupCheckbox->isChecked(); - } - - void UpgradeTool::OnUpgrade() - { - setWindowFlag(Qt::WindowCloseButtonHint, false); - - // m_keepEditorAlive = AZStd::make_unique(); - - UpdateSettings(); - - UpgradeNotifications::Bus::Broadcast(&UpgradeNotifications::OnUpgradeStart); - - m_assetsToUpgrade.clear(); - - IUpgradeRequests* upgradeRequests = AZ::Interface::Get(); - m_assetsToUpgrade = upgradeRequests->GetAssetsToUpgrade(); - - AZ::SystemTickBus::Handler::BusConnect(); - - if (m_ui->makeBackupCheckbox->isChecked()) - { - if (!DoBackup()) - { - // There was a problem, ask if the user wants to keep going or abort - QMessageBox mb(QMessageBox::Warning, - QObject::tr("Backup Failed"), - QObject::tr("Failed to backup your Script Canvas graphs, do you want to proceed with upgrade?"), - QMessageBox::Yes | QMessageBox::No, this); - - if (mb.exec() == QMessageBox::Yes) - { - DoUpgrade(); - } - } - } - else - { - DoUpgrade(); - } - } - - bool UpgradeTool::DoBackup() - { - if (!m_assetsToUpgrade.empty()) - { - m_state = UpgradeState::Backup; - - m_inProgressAsset = m_assetsToUpgrade.begin(); - - m_ui->progressFrame->setVisible(true); - m_ui->progressBar->setRange(0, aznumeric_cast(m_assetsToUpgrade.size())); - - m_ui->spinner->SetIsBusy(true); - m_ui->spinner->SetBusyIconSize(32); - - m_ui->upgradeButton->setEnabled(false); - m_ui->notNowButton->setEnabled(false); - m_ui->doNotAskCheckbox->setEnabled(false); - m_ui->makeBackupCheckbox->setEnabled(false); - - // Make the folder for the backup - - QDateTime theTime = QDateTime::currentDateTime(); - QString subFolder = theTime.toString("yyyy-MM-dd [HH.mm.ss]"); - - m_backupPath = AZStd::string::format("@devroot@/ScriptCanvas_BACKUP/%s", subFolder.toUtf8().data()); - char backupPathCStr[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(m_backupPath.c_str(), backupPathCStr, AZ_MAX_PATH_LEN); - m_backupPath = backupPathCStr; - - if (!AZ::IO::FileIOBase::GetInstance()->Exists(m_backupPath.c_str())) - { - if (AZ::IO::FileIOBase::GetInstance()->CreatePath(m_backupPath.c_str()) != AZ::IO::ResultCode::Success) - { - AZ_Error("Script Canvas", false, "Failed to create backup folder %s", m_backupPath.c_str()); - - return false; - } - } - } - - return true; - } - - void UpgradeTool::BackupComplete() - { - m_currentAssetIndex = 0; - m_ui->progressBar->setValue(0); - - DoUpgrade(); - } - - void UpgradeTool::DoUpgrade() - { - m_state = UpgradeState::Upgrade; - - if (!m_assetsToUpgrade.empty()) - { - m_ui->progressFrame->setVisible(true); - m_ui->progressBar->setRange(0, aznumeric_cast(m_assetsToUpgrade.size())); - - m_ui->spinner->SetIsBusy(true); - m_ui->spinner->SetBusyIconSize(32); - - m_ui->upgradeButton->setEnabled(false); - m_ui->notNowButton->setEnabled(false); - m_ui->doNotAskCheckbox->setEnabled(false); - m_ui->makeBackupCheckbox->setEnabled(false); - - m_inProgressAsset = m_assetsToUpgrade.begin(); - } - } - - void UpgradeTool::OnAssetReady(AZ::Data::Asset asset) - { - // Start asset upgrade job when current asset is unassigned only - // If current asset is present, there is ongoing progress handling this asset already - if (IsOnReadyAssetForCurrentProcess(asset.GetId())) - { - m_inProgress = true; - m_currentAsset = asset; - m_scriptCanvasEntity = AssetUpgradeJob(asset); - if (!m_scriptCanvasEntity) - { - ResetUpgradeCurrentAsset(); - } - } - } - - void UpgradeTool::OnAssetError(AZ::Data::Asset asset) - { - // Reset upgrade target when script canvas entity is unassigned only. - // If script canvas entity is present, we should let the conversion progress finish itself. - if (IsCurrentProcessFreeToAbort(asset.GetId())) - { - AZ_TracePrintf("Script Canvas", "Asset fails to get load: %s\n", asset.GetHint().c_str()); - ResetUpgradeCurrentAsset(); - } - } - - void UpgradeTool::OnAssetUnloaded(const AZ::Data::AssetId assetId, const AZ::Data::AssetType) - { - // Reset upgrade target when script canvas entity is unassigned only. - // If script canvas entity is present, we should let the conversion progress finish itself. - if (IsCurrentProcessFreeToAbort(assetId)) - { - AZ_TracePrintf("Script Canvas", "Asset gets unloaded: %s\n", m_inProgressAsset->m_relativePath.c_str()); - ResetUpgradeCurrentAsset(); - } - } - - - void UpgradeTool::OnGraphUpgradeComplete(AZ::Data::Asset& asset, bool skipped /*=false*/) - { - if (!skipped) - { - AZStd::string relativePath, fullPath; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); - - bool fullPathFound = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, fullPath); - - AZStd::string tmpFileName; - bool tmpFilesaved = false; - - // here we are saving the graph to a temp file instead of the original file and then copying the temp file to the original file. - // This ensures that AP will not a get a file change notification on an incomplete graph file causing it to fail processing. Temp files are ignored by AP. - if (AZ::IO::CreateTempFileName(fullPath.c_str(), tmpFileName)) - { - AZ::IO::FileIOStream fileStream(tmpFileName.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText); - - if (fileStream.IsOpen()) - { - if (asset.GetType() == azrtti_typeid()) - { - tmpFilesaved = AZ::Utils::SaveObjectToStream(fileStream, AZ::DataStream::ST_XML, &asset.GetAs()->GetScriptCanvasData()); - } - - fileStream.Close(); - } - - using SCCommandBus = AzToolsFramework::SourceControlCommandBus; - SCCommandBus::Broadcast(&SCCommandBus::Events::RequestEdit, fullPath.c_str(), true, - [this, &asset, fullPath, tmpFileName, tmpFilesaved](bool /*success*/, const AzToolsFramework::SourceControlFileInfo& info) - { - if (!info.IsReadOnly()) - { - if (tmpFilesaved) - { - PerformMove(asset, tmpFileName, fullPath); - } - } - else - { - if (m_overwriteAll) - { - MakeWriteable(info); - - if (tmpFilesaved) - { - PerformMove(asset, tmpFileName, fullPath); - } - } - else - { - int result = QMessageBox::No; - if (!m_overwriteAll) - { - QMessageBox mb(QMessageBox::Warning, - QObject::tr("Failed to Save Upgraded File"), - QObject::tr("The upgraded file could not be saved because the file is read only.\nDo you want to make it writeable and overwrite it?"), - QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::No, this); - - result = mb.exec(); - if (result == QMessageBox::YesToAll) - { - m_overwriteAll = true; - } - } - - if (result == QMessageBox::Yes || m_overwriteAll) - { - MakeWriteable(info); - - if (tmpFilesaved) - { - PerformMove(asset, tmpFileName, fullPath); - } - } - - } - } - }); - } - } - else - { - // We skipped the upgrade (it's up to date), just mark it complete - AZ::SystemTickBus::QueueFunction([this, asset]() { UpgradeComplete(asset, true); }); - - } - - } - - void UpgradeTool::MakeWriteable(const AzToolsFramework::SourceControlFileInfo& info) - { - AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true); - } - - void UpgradeTool::PerformMove(AZ::Data::Asset& asset, const AZStd::string& source, const AZStd::string& target) - { - auto moveResult = AZ::IO::SmartMove(source.c_str(), target.c_str()); - if (moveResult.GetResultCode() == AZ::IO::ResultCode::Success) - { - // Bump the slice asset up in the asset processor's queue. - AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str()); - - AZ::SystemTickBus::QueueFunction([this, &asset]() { UpgradeComplete(asset); }); - } - else - { - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); - streamer->SetRequestCompleteCallback(flushRequest, [this, &asset, source, target]([[maybe_unused]] AZ::IO::FileRequestHandle request) - { - // Continue saving. - AZ::SystemTickBus::QueueFunction([this, &asset, source, target]() { RetryMove(asset, source, target); }); - }); - streamer->QueueRequest(flushRequest); - } - } - - bool UpgradeTool::IsOnReadyAssetForCurrentProcess(const AZ::Data::AssetId& assetId) - { - return !m_currentAsset && m_inProgressAsset != m_assetsToUpgrade.end() && m_inProgressAsset->m_assetId == assetId; - } - - bool UpgradeTool::IsCurrentProcessFreeToAbort(const AZ::Data::AssetId& assetId) - { - return !m_scriptCanvasEntity && m_inProgressAsset != m_assetsToUpgrade.end() && m_inProgressAsset->m_assetId == assetId; - } - - bool UpgradeTool::IsUpgradeCompleteForAllAssets() - { - return !m_inProgress && !m_currentAsset && !m_scriptCanvasEntity && m_inProgressAsset == m_assetsToUpgrade.end(); - } - - bool UpgradeTool::IsUpgradeCompleteForCurrentAsset() - { - return !m_inProgress && !m_currentAsset && !m_scriptCanvasEntity && m_inProgressAsset != m_assetsToUpgrade.end(); - } - - void UpgradeTool::ResetUpgradeCurrentAsset() - { - AZ::Data::AssetBus::MultiHandler::BusDisconnect(m_currentAsset.GetId()); - - if (m_scriptCanvasEntity) - { - m_scriptCanvasEntity->Deactivate(); - m_scriptCanvasEntity = nullptr; - } - - if (m_inProgressAsset != m_assetsToUpgrade.end()) - { - m_inProgressAsset = m_assetsToUpgrade.erase(m_inProgressAsset); - } - - m_currentAsset.Release(); - m_currentAsset = {}; - m_inProgress = false; - } - - void UpgradeTool::OnSystemTick() - { - switch (m_state) - { - case UpgradeTool::UpgradeState::Upgrade: - - if (IsUpgradeCompleteForCurrentAsset()) - { - m_inProgress = true; - AZ::Data::AssetInfo& assetToUpgrade = *m_inProgressAsset; - - if (!AZ::Data::AssetBus::MultiHandler::BusIsConnectedId(assetToUpgrade.m_assetId)) - { - AZ::Data::AssetBus::MultiHandler::BusConnect(assetToUpgrade.m_assetId); - } - - auto asset = AZ::Data::AssetManager::Instance().GetAsset(assetToUpgrade.m_assetId, assetToUpgrade.m_assetType, AZ::Data::AssetLoadBehavior::Default); - asset.BlockUntilLoadComplete(); - - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(assetToUpgrade.m_relativePath); - streamer->SetRequestCompleteCallback(flushRequest, []([[maybe_unused]] AZ::IO::FileRequestHandle request) - { - }); - streamer->QueueRequest(flushRequest); - - if (asset.IsReady() || asset.GetStatus() == AZ::Data::AssetData::AssetStatus::ReadyPreNotify) - { - m_currentAsset = asset; - m_scriptCanvasEntity = AssetUpgradeJob(asset); - if (!m_scriptCanvasEntity) - { - ResetUpgradeCurrentAsset(); - } - } - - m_ui->spinner->SetText(QObject::tr("%1").arg(assetToUpgrade.m_relativePath.c_str())); - } - else if (IsUpgradeCompleteForAllAssets()) - { - FinalizeUpgrade(); - } - break; - - case UpgradeTool::UpgradeState::Backup: - - if (m_inProgressAsset != m_assetsToUpgrade.end()) - { - AZ::Data::AssetInfo& assetToBackup = *m_inProgressAsset; - - m_ui->spinner->SetText(QObject::tr("%1").arg(assetToBackup.m_relativePath.c_str())); - - BackupAsset(assetToBackup); - - m_ui->progressBar->setValue(aznumeric_cast(++m_currentAssetIndex)); - } - else - { - BackupComplete(); - } - break; - - default: - break; - } - - AZ::Data::AssetManager::Instance().DispatchEvents(); - AZ::SystemTickBus::ExecuteQueuedEvents(); - - } - - void UpgradeTool::BackupAsset(const AZ::Data::AssetInfo& assetInfo) - { - - AZStd::string devRoot = "@devroot@"; - AZStd::string devAssets = "@devassets@"; - - char devRootCStr[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(devRoot.c_str(), devRootCStr, AZ_MAX_PATH_LEN); - - char devAssetsCStr[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(devAssets.c_str(), devAssetsCStr, AZ_MAX_PATH_LEN); - - AZStd::string relativePath = devAssetsCStr; - AzFramework::StringFunc::Replace(relativePath, devRootCStr, ""); - if (relativePath.starts_with("/")) - { - relativePath = relativePath.substr(1, relativePath.size() - 1); - } - - AZStd::string sourceFilePath; - - // Using this to get the watch folder - AZStd::string watchFolder; - AZ::Data::AssetInfo assetInfo2; - bool sourceInfoFound{}; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, assetInfo.m_relativePath.c_str(), assetInfo2, watchFolder); - if (sourceInfoFound) - { - AZStd::string assetPath; - AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), assetPath); - - sourceFilePath = assetPath; - } - - devRoot = devRootCStr; - AzFramework::StringFunc::Path::Normalize(devRoot); - - relativePath = sourceFilePath; - AzFramework::StringFunc::Replace(relativePath, devRoot.c_str(), ""); - if (relativePath.starts_with("/")) - { - relativePath = relativePath.substr(1, relativePath.size() - 1); - } - - AZStd::string targetFilePath; - AzFramework::StringFunc::Path::Join(m_backupPath.c_str(), relativePath.c_str(), targetFilePath); - - if (AZ::IO::FileIOBase::GetInstance()->Copy(sourceFilePath.c_str(), targetFilePath.c_str()) != AZ::IO::ResultCode::Error) - { - AZStd::string filename; - AzFramework::StringFunc::Path::GetFileName(sourceFilePath.c_str(), filename); - AZ_TracePrintf("Script Canvas", "Backup: %s -> %s\n", filename.c_str(), targetFilePath.c_str()); - } - else - { - AZ_TracePrintf("Script Canvas", "(Error) Failed to create backup: %s -> %s\n", sourceFilePath.c_str(), targetFilePath.c_str()); - } - - ++m_inProgressAsset; - } - - void UpgradeTool::UpgradeComplete(const AZ::Data::Asset& asset, bool skipped /*= false*/) - { - m_ui->progressBar->setValue(aznumeric_cast(++m_currentAssetIndex)); - - ResetUpgradeCurrentAsset(); - - if (!skipped) - { - AZStd::string filename; - AzFramework::StringFunc::Path::GetFileName(asset.GetHint().c_str(), filename); - AZ_TracePrintf("Script Canvas", "%s -> Upgraded and Saved!\n", filename.c_str()); - } - } - - void UpgradeTool::FinalizeUpgrade() - { - setWindowFlag(Qt::WindowCloseButtonHint, true); - - AZ::SystemTickBus::Handler::BusDisconnect(); - - m_currentAsset = {}; - - SaveLog(); - - UpgradeNotifications::Bus::Broadcast(&UpgradeNotifications::OnUpgradeComplete); - - AZ_TracePrintf("Script Canvas", "\nUpgrade Complete!\n"); - - DisconnectBuses(); - - accept(); - } - - void UpgradeTool::SaveLog() - { - AZStd::string outputFileName = AZStd::string::format("@devroot@/ScriptCanvasUpgradeReport.html"); - - char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(outputFileName.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN); - - AZStd::string endPath = resolvedBuffer; - AZ::StringFunc::Path::Normalize(endPath); - - AZ::IO::SystemFile outputFile; - if (!outputFile.Open(endPath.c_str(), - AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY)) - { - AZ_Error("Script Canvas", false, "Failed to open file for writing: %s", endPath.c_str()); - return; - } - - QDateTime theTime = QDateTime::currentDateTime(); - AZStd::string timeStamp = theTime.toString("yyyy-MM-dd [HH.mm.ss]").toUtf8().data(); - - AZStd::string header = "\n\n\n\n\n"; - header.append(AZStd::string::format("Log captured: %s
\n", timeStamp.c_str()).c_str()); - - outputFile.Write(header.c_str(), header.size()); - for (auto& log : m_logs) - { - AZStd::string logText = AZStd::string::format("%s
", log.c_str()); - AzFramework::StringFunc::Replace(logText, "\n", "
\n"); - outputFile.Write(logText.data(), logText.size()); - } - AZStd::string footer = "\n\n"; - outputFile.Write(footer.c_str(), footer.size()); - - - - outputFile.Close(); - } - - AZ::Entity* UpgradeTool::AssetUpgradeJob(AZ::Data::Asset&) - { - return nullptr; - } - - void UpgradeTool::RetryMove(AZ::Data::Asset& asset, const AZStd::string& source, const AZStd::string& target) - { - auto moveResult = AZ::IO::SmartMove(source.c_str(), target.c_str()); - if (moveResult.GetResultCode() == AZ::IO::ResultCode::Success) - { - // Bump the slice asset up in the asset processor's queue. - AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str()); - - AZ::SystemTickBus::QueueFunction([this, &asset]() { UpgradeComplete(asset); }); - } - else - { - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); - streamer->SetRequestCompleteCallback(flushRequest, [this, &asset, source, target]([[maybe_unused]] AZ::IO::FileRequestHandle request) - { - // Continue saving. - AZ::SystemTickBus::QueueFunction([this, &asset, source, target]() { RetryMove(asset, source, target); }); - }); - streamer->QueueRequest(flushRequest); - - - //AZ::SystemTickBus::QueueFunction([this, &asset, source, target]() { RetryMove(asset, source, target); }); - } - } - - void UpgradeTool::CaptureLogFromTraceBus(const char* /*window*/, const char* message) - { - AZStd::string msg = message; - if (msg.ends_with("\n")) - { - msg = msg.substr(0, msg.size() - 1); - } - - m_logs.push_back(msg); - } - - bool UpgradeTool::OnPreError(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) - { - AZStd::string msg = AZStd::string::format("(Error): %s
", message); - CaptureLogFromTraceBus(window, msg.c_str()); - - return false; - } - - bool UpgradeTool::OnPreWarning(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) - { - AZStd::string msg = AZStd::string::format("(Warning): %s
", message); - CaptureLogFromTraceBus(window, msg.c_str()); - - return false; - } - - bool UpgradeTool::OnException(const char* message) - { - AZStd::string msg = AZStd::string::format("(Exception): %s
", message); - CaptureLogFromTraceBus("Script Canvas", msg.c_str()); - - return false; - } - - bool UpgradeTool::OnPrintf(const char* window, const char* message) - { - CaptureLogFromTraceBus(window, message); - return false; - } - -#include - -} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.h deleted file mode 100644 index be00198457..0000000000 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.h +++ /dev/null @@ -1,149 +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 - -#if !defined(Q_MOC_RUN) -#include - -AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - -#include -#include - -#include - -#include - -#include - -#include -#include -#include -#endif - -class QPushButton; - -namespace Ui -{ - class UpgradeTool; -} - -namespace ScriptCanvasEditor -{ - class KeepEditorAlive; - - //! A tool that collects and upgrades all Script Canvas graphs in the asset catalog - class UpgradeTool - : public AzQtComponents::StyledDialog - , private AZ::SystemTickBus::Handler - , private AZ::Data::AssetBus::MultiHandler - , private UpgradeNotifications::Bus::Handler - , private AZ::Debug::TraceMessageBus::Handler - { - Q_OBJECT - - public: - AZ_CLASS_ALLOCATOR(UpgradeTool, AZ::SystemAllocator, 0); - - UpgradeTool(QWidget* parent = nullptr); - ~UpgradeTool(); - - size_t& UpgradedGraphCount() { return m_upgradedAssets; } - size_t& SkippedGraphCount() { return m_skippedAssets; } - - bool HasBackup() const; - - private: - - bool IsOnReadyAssetForCurrentProcess(const AZ::Data::AssetId& assetId); - bool IsCurrentProcessFreeToAbort(const AZ::Data::AssetId& assetId); - bool IsUpgradeCompleteForAllAssets(); - bool IsUpgradeCompleteForCurrentAsset(); - void ResetUpgradeCurrentAsset(); - - void OnUpgrade(); - void OnNoThanks(); - void UpdateSettings(); - - enum class UpgradeState - { - Inactive, - Backup, - Upgrade - }; - UpgradeState m_state = UpgradeState::Inactive; - - bool DoBackup(); - void BackupAsset(const AZ::Data::AssetInfo& assetInfo); - void BackupComplete(); - - void DoUpgrade(); - void UpgradeComplete(const AZ::Data::Asset&, bool skipped = false); - - AZ::Entity* AssetUpgradeJob(AZ::Data::Asset& asset); - - // SystemTickBus::Handler - void OnSystemTick() override; - // - - // AssetBus::Handler - void OnAssetReady(AZ::Data::Asset asset) override; - void OnAssetError(AZ::Data::Asset asset) override; - void OnAssetUnloaded(const AZ::Data::AssetId assetId, const AZ::Data::AssetType assetType) override; - // - - // AZ::Debug::TranceMessageBus::Handler - bool OnException(const char* /*message*/) override; - bool OnPrintf(const char* /*window*/, const char* /*message*/) override; - bool OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override; - bool OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override; - // - - void CaptureLogFromTraceBus(const char* window, const char* message); - - void OnGraphUpgradeComplete(AZ::Data::Asset&, bool skipped = false) override; - - void RetryMove(AZ::Data::Asset& asset, const AZStd::string& source, const AZStd::string& target); - - void SaveLog(); - - bool m_inProgress = false; - size_t m_currentAssetIndex = 0; - - size_t m_upgradedAssets = 0; - size_t m_skippedAssets = 0; - - IUpgradeRequests::AssetList m_assetsToUpgrade; - IUpgradeRequests::AssetList::iterator m_inProgressAsset; - - AZ::Data::Asset m_currentAsset; - - AZStd::unique_ptr m_ui; - AZStd::recursive_mutex m_mutex; - - // AZStd::unique_ptr m_keepEditorAlive; - - AZStd::vector m_logs; - - AZ::Entity* m_scriptCanvasEntity = nullptr; - - AZStd::string m_backupPath; - - void FinalizeUpgrade(); - void DisconnectBuses(); - - void closeEvent(QCloseEvent* event) override; - - bool m_overwriteAll = false; - void MakeWriteable(const AzToolsFramework::SourceControlFileInfo& info); - void PerformMove(AZ::Data::Asset& asset, const AZStd::string& source, const AZStd::string& target); - }; -} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.ui b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.ui deleted file mode 100644 index 78a63ce727..0000000000 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.ui +++ /dev/null @@ -1,260 +0,0 @@ - - - UpgradeTool - - - Qt::WindowModal - - - - 0 - 0 - 801 - 328 - - - - - 0 - 0 - - - - Script Canvas Upgrade - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Script Canvas graphs are now compiled into Lua by the Asset Processor! In order to complete this upgrade, all graphs in your current project must be updated.</p><p>This upgrading is making changes to the underlying Script Canvas graph format. Depending on the number of scripts you've created this process could take a few minutes to complete. </p><p>This upgrade is required for any projects saved on 1.26 or earlier.</p><p>If you choose &quot;Not now&quot;, your project may not work correctly.</p></body></html> - - - false - - - true - - - - - - - - 0 - 80 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - false - - - background-color: rgb(47, 47, 47); - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - - 0 - 0 - - - - - 32 - 32 - - - - false - - - - - - - - 0 - 0 - - - - - - - 24 - - - true - - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 0 - - - - - - - - - - - - Backup my source graphs - - - true - - - - - - - Do not ask me again - - - - - - - - 0 - 0 - - - - Qt::LeftToRight - - - Upgrade - - - - - - - - 0 - 0 - - - - Qt::LeftToRight - - - Not now - - - false - - - - - - - - - - - - - - - - AzQtComponents::StyledBusyLabel - QWidget -
AzQtComponents/Components/StyledBusyLabel.h
- 1 -
-
- - -
diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.cpp deleted file mode 100644 index eb182ed291..0000000000 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.cpp +++ /dev/null @@ -1,1023 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace VersionExplorerCpp -{ - class FileEventHandler - : public AZ::IO::FileIOEventBus::Handler - { - public: - int m_errorCode = 0; - AZStd::string m_fileName; - - FileEventHandler() - { - BusConnect(); - } - - ~FileEventHandler() - { - BusDisconnect(); - } - - void OnError(const AZ::IO::SystemFile* /*file*/, const char* fileName, int errorCode) override - { - m_errorCode = errorCode; - - if (fileName) - { - m_fileName = fileName; - } - } - }; -} - -namespace ScriptCanvasEditor -{ - EditorKeepAlive::EditorKeepAlive() - { - ISystem* system = nullptr; - CrySystemRequestBus::BroadcastResult(system, &CrySystemRequestBus::Events::GetCrySystem); - - m_edKeepEditorActive = system->GetIConsole()->GetCVar("ed_KeepEditorActive"); - - if (m_edKeepEditorActive) - { - m_keepEditorActive = m_edKeepEditorActive->GetIVal(); - m_edKeepEditorActive->Set(1); - } - } - - EditorKeepAlive::~EditorKeepAlive() - { - if (m_edKeepEditorActive) - { - m_edKeepEditorActive->Set(m_keepEditorActive); - } - } - - VersionExplorer::VersionExplorer(QWidget* parent /*= nullptr*/) - : AzQtComponents::StyledDialog(parent) - , m_ui(new Ui::VersionExplorer()) - { - m_ui->setupUi(this); - - m_ui->tableWidget->horizontalHeader()->setVisible(false); - m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); - m_ui->tableWidget->setColumnWidth(3, 22); - - m_ui->textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); - m_ui->textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); - - connect(m_ui->scanButton, &QPushButton::pressed, this, &VersionExplorer::OnScan); - connect(m_ui->closeButton, &QPushButton::pressed, this, &VersionExplorer::OnClose); - connect(m_ui->upgradeAllButton, &QPushButton::pressed, this, &VersionExplorer::OnUpgradeAll); - - m_ui->progressBar->setValue(0); - m_ui->progressBar->setVisible(false); - - m_keepEditorAlive = AZStd::make_unique(); - m_inspectingAsset = m_assetsToInspect.end(); - - } - - VersionExplorer::~VersionExplorer() - { - AZ::SystemTickBus::Handler::BusDisconnect(); - - UpgradeNotifications::Bus::Handler::BusDisconnect(); - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); - - } - - void VersionExplorer::Log(const char* format, ...) - { - if (m_ui->verbose->isChecked()) - { - char sBuffer[2048]; - va_list ArgList; - va_start(ArgList, format); - azvsnprintf(sBuffer, sizeof(sBuffer), format, ArgList); - sBuffer[sizeof(sBuffer) - 1] = '\0'; - va_end(ArgList); - - AZ_TracePrintf(ScriptCanvas::k_VersionExplorerWindow.data(), "%s\n", sBuffer); - } - } - - void VersionExplorer::OnClose() - { - reject(); - } - - bool VersionExplorer::IsUpgrading() const - { - return m_inProgressAsset != m_assetsToUpgrade.end() && m_inProgress; - } - - void VersionExplorer::OnSystemTick() - { - switch (m_state) - { - case ProcessState::Scan: - - if (!m_inProgress && m_inspectingAsset != m_assetsToInspect.end()) - { - m_inProgress = true; - AZ::Data::AssetInfo& assetToUpgrade = *m_inspectingAsset; - m_currentAsset = AZ::Data::AssetManager::Instance().GetAsset(assetToUpgrade.m_assetId, assetToUpgrade.m_assetType, AZ::Data::AssetLoadBehavior::PreLoad); - Log("SystemTick::ProcessState::Scan: %s pre-blocking load hint", m_currentAsset.GetHint().c_str()); - m_currentAsset.BlockUntilLoadComplete(); - if (m_currentAsset.IsReady()) - { - // The asset is ready, grab its info - m_inProgress = true; - InspectAsset(m_currentAsset, assetToUpgrade); - } - else - { - m_ui->tableWidget->insertRow(static_cast(m_currentAssetRowIndex)); - QTableWidgetItem* rowName = new QTableWidgetItem - ( tr(AZStd::string::format("Error: %s", assetToUpgrade.m_relativePath.c_str()).c_str())); - m_ui->tableWidget->setItem(static_cast(m_currentAssetRowIndex), static_cast(ColumnAsset), rowName); - ++m_currentAssetRowIndex; - - Log("SystemTick::ProcessState::Scan: %s post-blocking load, problem loading asset", assetToUpgrade.m_relativePath.c_str()); - ++m_failedAssets; - ScanComplete(m_currentAsset); - } - } - break; - - case ProcessState::Upgrade: - { - AZStd::lock_guard lock(m_mutex); - if (m_upgradeComplete) - { - ++m_upgradeAssetIndex; - m_inProgress = false; - m_ui->progressBar->setVisible(true); - m_ui->progressBar->setValue(m_upgradeAssetIndex); - - if (m_scriptCanvasEntity) - { - m_scriptCanvasEntity->Deactivate(); - m_scriptCanvasEntity = nullptr; - } - - GraphUpgradeCompleteUIUpdate(m_upgradeAsset, m_upgradeResult, m_upgradeMessage); - - if (!m_isUpgradingSingleGraph) - { - if (m_inProgressAsset != m_assetsToUpgrade.end()) - { - m_inProgressAsset = m_assetsToUpgrade.erase(m_inProgressAsset); - } - - if (m_inProgressAsset == m_assetsToUpgrade.end()) - { - FinalizeUpgrade(); - } - } - else - { - m_inProgressAsset = m_assetsToUpgrade.erase(m_inProgressAsset); - m_inProgress = false; - m_state = ProcessState::Inactive; - m_settingsCache.reset(); - AZ::SystemTickBus::Handler::BusDisconnect(); - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); - } - - m_isUpgradingSingleGraph = false; - - if (m_assetsToUpgrade.empty()) - { - m_ui->upgradeAllButton->setEnabled(false); - } - - m_upgradeComplete = false; - } - - if (!IsUpgrading() && m_state == ProcessState::Upgrade) - { - AZStd::string errorMessage = BackupGraph(*m_inProgressAsset); - // Make the backup - if (errorMessage.empty()) - { - Log("SystemTick::ProcessState::Upgrade: Backup Success %s ", m_inProgressAsset->GetHint().c_str()); - QList items = m_ui->tableWidget->findItems(m_inProgressAsset->GetHint().c_str(), Qt::MatchFlag::MatchExactly); - if (!items.isEmpty()) - { - for (auto* item : items) - { - int row = item->row(); - AzQtComponents::StyledBusyLabel* spinner = qobject_cast(m_ui->tableWidget->cellWidget(row, ColumnStatus)); - spinner->SetIsBusy(true); - } - } - - // Upgrade the graph - UpgradeGraph(*m_inProgressAsset); - } - else - { - Log("SystemTick::ProcessState::Upgrade: Backup Failed %s ", m_inProgressAsset->GetHint().c_str()); - GraphUpgradeComplete(*m_inProgressAsset, OperationResult::Failure, errorMessage); - } - - } - break; - } - default: - break; - } - - FlushLogs(); - - AZ::Data::AssetManager::Instance().DispatchEvents(); - AZ::SystemTickBus::ExecuteQueuedEvents(); - } - - // Backup - - void VersionExplorer::OnUpgradeAll() - { - m_state = ProcessState::Upgrade; - m_settingsCache = AZStd::make_unique(); - ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false; - ScriptCanvas::Grammar::g_printAbstractCodeModel = false; - ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false; - AZ::Interface::Get()->SetIsUpgrading(true); - AZ::Interface::Get()->ClearGraphsThatNeedUpgrade(); - m_inProgressAsset = m_assetsToUpgrade.begin(); - AZ::Debug::TraceMessageBus::Handler::BusConnect(); - AZ::SystemTickBus::Handler::BusConnect(); - m_ui->progressBar->setVisible(true); - m_ui->progressBar->setRange(0, aznumeric_cast(m_assetsToUpgrade.size())); - m_ui->progressBar->setValue(m_upgradeAssetIndex); - m_keepEditorAlive = AZStd::make_unique(); - } - - AZStd::string VersionExplorer::BackupGraph(const AZ::Data::Asset& asset) - { - if (!m_ui->makeBackupCheckbox->isChecked()) - { - // considered a success - return ""; - } - - QDateTime theTime = QDateTime::currentDateTime(); - QString subFolder = theTime.toString("yyyy-MM-dd [HH.mm.ss]"); - - AZStd::string backupPath = AZStd::string::format("@devroot@/ScriptCanvas_BACKUP/%s", subFolder.toUtf8().data()); - char backupPathCStr[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(backupPath.c_str(), backupPathCStr, AZ_MAX_PATH_LEN); - backupPath = backupPathCStr; - - if (!AZ::IO::FileIOBase::GetInstance()->Exists(backupPath.c_str())) - { - if (AZ::IO::FileIOBase::GetInstance()->CreatePath(backupPath.c_str()) != AZ::IO::ResultCode::Success) - { - AZ_Error(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Failed to create backup folder %s", backupPath.c_str()); - return "Failed to create backup folder"; - } - } - - AZStd::string devRoot = "@devroot@"; - AZStd::string devAssets = "@devassets@"; - - char devRootCStr[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(devRoot.c_str(), devRootCStr, AZ_MAX_PATH_LEN); - - char devAssetsCStr[AZ_MAX_PATH_LEN] = { 0 }; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(devAssets.c_str(), devAssetsCStr, AZ_MAX_PATH_LEN); - - AZStd::string relativePath = devAssetsCStr; - AzFramework::StringFunc::Replace(relativePath, devRootCStr, ""); - if (relativePath.starts_with("/")) - { - relativePath = relativePath.substr(1, relativePath.size() - 1); - } - - AZStd::string sourceFilePath; - - AZStd::string watchFolder; - AZ::Data::AssetInfo assetInfo; - bool sourceInfoFound{}; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, asset.GetHint().c_str(), assetInfo, watchFolder); - if (sourceInfoFound) - { - AZStd::string assetPath; - AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), assetPath); - - sourceFilePath = assetPath; - } - else - { - AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "VersionExplorer::BackupGraph: Failed to find file: %s", asset.GetHint().c_str()); - return "Failed to find source file"; - } - - devRoot = devRootCStr; - AzFramework::StringFunc::Path::Normalize(devRoot); - - relativePath = sourceFilePath; - AzFramework::StringFunc::Replace(relativePath, devRoot.c_str(), ""); - if (relativePath.starts_with("/")) - { - relativePath = relativePath.substr(1, relativePath.size() - 1); - } - - AzFramework::StringFunc::Path::Normalize(relativePath); - AzFramework::StringFunc::Path::Normalize(backupPath); - - AZStd::string targetFilePath = backupPath; - targetFilePath += relativePath; - - if (AZ::IO::FileIOBase::GetInstance()->Copy(sourceFilePath.c_str(), targetFilePath.c_str()) != AZ::IO::ResultCode::Success) - { - AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "VersionExplorer::BackupGraph: Error creating backup: %s ---> %s\n", sourceFilePath.c_str(), targetFilePath.c_str()); - return "Failed to copy source file to backup location"; - } - - Log("VersionExplorer::BackupGraph: Backed up: %s ---> %s\n", sourceFilePath.c_str(), targetFilePath.c_str()); - return ""; - } - - void VersionExplorer::UpgradeGraph(const AZ::Data::Asset& asset) - { - m_inProgress = true; - m_upgradeComplete = false; - Log("UpgradeGraph %s ", m_inProgressAsset->GetHint().c_str()); - m_ui->spinner->SetText(QObject::tr("Upgrading: %1").arg(asset.GetHint().c_str())); - m_scriptCanvasEntity = nullptr; - - UpgradeNotifications::Bus::Handler::BusConnect(); - - if (asset.GetType() == azrtti_typeid()) - { - ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs(); - AZ_Assert(scriptCanvasAsset, "Unable to get the asset of ScriptCanvasAsset, but received type: %s" - , azrtti_typeid().template ToString().c_str()); - - if (!scriptCanvasAsset) - { - return; - } - - AZ::Entity* scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity(); - AZ_Assert(scriptCanvasEntity, "VersionExplorer::UpgradeGraph The Script Canvas asset must have a valid entity"); - if (!scriptCanvasEntity) - { - return; - } - - AZ::Entity* queryEntity = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(queryEntity, &AZ::ComponentApplicationRequests::FindEntity, scriptCanvasEntity->GetId()); - if (queryEntity) - { - if (queryEntity->GetState() == AZ::Entity::State::Active) - { - queryEntity->Deactivate(); - } - - scriptCanvasEntity = queryEntity; - } - - if (scriptCanvasEntity->GetState() == AZ::Entity::State::Constructed) - { - scriptCanvasEntity->Init(); - } - - if (scriptCanvasEntity->GetState() == AZ::Entity::State::Init) - { - scriptCanvasEntity->Activate(); - } - - AZ_Assert(scriptCanvasEntity->GetState() == AZ::Entity::State::Active, "Graph entity is not active"); - auto graphComponent = scriptCanvasEntity->FindComponent(); - AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component"); - - if (graphComponent) - { - m_scriptCanvasEntity = scriptCanvasEntity; - - graphComponent->UpgradeGraph - ( asset - , m_ui->forceUpgrade->isChecked() ? Graph::UpgradeRequest::Forced : Graph::UpgradeRequest::IfOutOfDate - , m_ui->verbose->isChecked()); - } - } - - AZ_Assert(m_scriptCanvasEntity, "The ScriptCanvas asset should have an entity"); - } - - void VersionExplorer::OnGraphUpgradeComplete(AZ::Data::Asset& asset, bool /*skipped*/ /*= false*/) - { - AZStd::string relativePath, fullPath; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); - bool fullPathFound = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, fullPath); - if (!fullPathFound) - { - AZ_Error(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Full source path not found for %s", relativePath.c_str()); - } - - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(fullPath); - streamer->SetRequestCompleteCallback(flushRequest, [this, asset]([[maybe_unused]] AZ::IO::FileRequestHandle request) - { - this->OnSourceFileReleased(asset); - }); - streamer->QueueRequest(flushRequest); - } - - void VersionExplorer::OnSourceFileReleased(AZ::Data::Asset asset) - { - AZStd::string relativePath, fullPath; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); - bool fullPathFound = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, fullPath); - m_tmpFileName.clear(); - AZStd::string tmpFileName; - // here we are saving the graph to a temp file instead of the original file and then copying the temp file to the original file. - // This ensures that AP will not a get a file change notification on an incomplete graph file causing it to fail processing. Temp files are ignored by AP. - if (!AZ::IO::CreateTempFileName(fullPath.c_str(), tmpFileName)) - { - GraphUpgradeComplete(asset, OperationResult::Failure, "Failure to create temporary file name"); - return; - } - - bool tempSavedSucceeded = false; - AZ::IO::FileIOStream fileStream(tmpFileName.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText); - if (fileStream.IsOpen()) - { - if (asset.GetType() == azrtti_typeid()) - { - ScriptCanvasEditor::ScriptCanvasAssetHandler handler; - tempSavedSucceeded = handler.SaveAssetData(asset, &fileStream); - } - - fileStream.Close(); - } - - // attempt to remove temporary file no matter what - m_tmpFileName = tmpFileName; - if (!tempSavedSucceeded) - { - GraphUpgradeComplete(asset, OperationResult::Failure, "Save asset data to temporary file failed"); - return; - } - - using SCCommandBus = AzToolsFramework::SourceControlCommandBus; - SCCommandBus::Broadcast(&SCCommandBus::Events::RequestEdit, fullPath.c_str(), true, - [this, asset, fullPath, tmpFileName]([[maybe_unused]] bool success, const AzToolsFramework::SourceControlFileInfo& info) - { - constexpr const size_t k_maxAttemps = 10; - - if (!info.IsReadOnly()) - { - PerformMove(asset, tmpFileName, fullPath, k_maxAttemps); - } - else - { - if (m_overwriteAll) - { - AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true); - PerformMove(asset, tmpFileName, fullPath, k_maxAttemps); - } - else - { - int result = QMessageBox::No; - if (!m_overwriteAll) - { - QMessageBox mb(QMessageBox::Warning, - QObject::tr("Failed to Save Upgraded File"), - QObject::tr("The upgraded file could not be saved because the file is read only.\nDo you want to make it writeable and overwrite it?"), - QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::No, this); - - result = mb.exec(); - if (result == QMessageBox::YesToAll) - { - m_overwriteAll = true; - } - } - - if (result == QMessageBox::Yes || m_overwriteAll) - { - AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true); - PerformMove(asset, tmpFileName, fullPath, k_maxAttemps); - } - } - } - }); - } - - void VersionExplorer::PerformMove(AZ::Data::Asset asset, AZStd::string source, AZStd::string target - , size_t remainingAttempts) - { - VersionExplorerCpp::FileEventHandler fileEventHandler; - - if (remainingAttempts == 0) - { - // all attempts failed, give up - AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "moving converted file to source destination failed: %s. giving up", target.c_str()); - GraphUpgradeComplete(asset, OperationResult::Failure, "Failed to move updated file from backup to source destination"); - } - else if (remainingAttempts == 2) - { - // before the final attempt, flush all caches - AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "moving converted file to source destination failed: %s, trying again", target.c_str()); - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCaches(); - streamer->SetRequestCompleteCallback(flushRequest - , [this, asset, remainingAttempts, source, target]([[maybe_unused]] AZ::IO::FileRequestHandle request) - { - // Continue saving. - AZ::SystemTickBus::QueueFunction( - [this, asset, remainingAttempts, source, target](){ PerformMove(asset, source, target, remainingAttempts - 1); }); - }); - streamer->QueueRequest(flushRequest); - } - else - { - // the actual move attempt - auto moveResult = AZ::IO::SmartMove(source.c_str(), target.c_str()); - if (moveResult.GetResultCode() == AZ::IO::ResultCode::Success) - { - m_tmpFileName.clear(); - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); - // Bump the slice asset up in the asset processor's queue. - AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str()); - AZ::SystemTickBus::QueueFunction([this, asset]() - { - GraphUpgradeComplete(asset, OperationResult::Success, ""); - }); - } - else - { - AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "moving converted file to source destination failed: %s, trying again", target.c_str()); - auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); - streamer->SetRequestCompleteCallback(flushRequest, [this, asset, source, target, remainingAttempts]([[maybe_unused]] AZ::IO::FileRequestHandle request) - { - // Continue saving. - AZ::SystemTickBus::QueueFunction([this, asset, source, target, remainingAttempts]() { PerformMove(asset, source, target, remainingAttempts - 1); }); - }); - streamer->QueueRequest(flushRequest); - } - } - } - - void VersionExplorer::GraphUpgradeComplete - ( const AZ::Data::Asset asset, OperationResult result, AZStd::string_view message) - { - AZStd::lock_guard lock(m_mutex); - m_upgradeComplete = true; - m_upgradeResult = result; - m_upgradeMessage = message; - m_upgradeAsset = asset; - - if (!m_tmpFileName.empty()) - { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - AZ_Assert(fileIO, "GraphUpgradeComplete: No FileIO instance"); - - if (fileIO->Exists(m_tmpFileName.c_str()) && !fileIO->Remove(m_tmpFileName.c_str())) - { - AZ_TracePrintf(ScriptCanvas::k_VersionExplorerWindow.data(), "Failed to remove temporary file: %s", m_tmpFileName.c_str()); - } - } - - if (m_upgradeResult == OperationResult::Failure) - { - AZ::Interface::Get()->GraphNeedsManualUpgrade(asset.GetId()); - } - - m_tmpFileName.clear(); - } - - void VersionExplorer::GraphUpgradeCompleteUIUpdate - ( const AZ::Data::Asset asset, OperationResult result, AZStd::string_view message) - { - QString text = asset.GetHint().c_str(); - QList items = m_ui->tableWidget->findItems(text, Qt::MatchFlag::MatchExactly); - - if (!items.isEmpty()) - { - for (auto* item : items) - { - int row = item->row(); - QTableWidgetItem* label = m_ui->tableWidget->item(row, ColumnAsset); - QString assetName = asset.GetHint().c_str(); - - if (label->text().compare(assetName) == 0) - { - m_ui->tableWidget->removeCellWidget(row, ColumnAction); - m_ui->tableWidget->removeCellWidget(row, ColumnStatus); - - QToolButton* doneButton = new QToolButton(this); - doneButton->setToolTip("Upgrade complete"); - if (result == OperationResult::Success) - { - doneButton->setIcon(QIcon(":/stylesheet/img/UI20/checkmark-menu.svg")); - } - else - { - doneButton->setIcon(QIcon(":/stylesheet/img/UI20/titlebar-close.svg")); - doneButton->setToolTip(message.data()); - } - - m_ui->tableWidget->setCellWidget(row, ColumnStatus, doneButton); - } - } - } - } - - void VersionExplorer::FinalizeUpgrade() - { - Log("FinalizeUpgrade!"); - m_inProgress = false; - m_assetsToUpgrade.clear(); - m_ui->upgradeAllButton->setEnabled(false); - m_ui->onlyShowOutdated->setEnabled(true); - m_keepEditorAlive.reset(); - m_ui->progressBar->setVisible(false); - - // Manual correction - size_t assetsThatNeedManualInspection = AZ::Interface::Get()->GetGraphsThatNeedManualUpgrade().size(); - if (assetsThatNeedManualInspection > 0) - { - m_ui->spinner->SetText("Some graphs will require manual corrections, you will be prompted to review them upon closing this dialog"); - } - else - { - m_ui->spinner->SetText("Upgrade complete."); - } - - AZ::SystemTickBus::Handler::BusDisconnect(); - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); - UpgradeNotifications::Bus::Handler::BusDisconnect(); - AZ::Interface::Get()->SetIsUpgrading(false); - m_settingsCache.reset(); - } - - // Scanning - - void VersionExplorer::OnScan() - { - m_assetsToUpgrade.clear(); - m_assetsToInspect.clear(); - m_ui->tableWidget->setRowCount(0); - m_inspectedAssets = 0; - m_currentAssetRowIndex = 0; - IUpgradeRequests* upgradeRequests = AZ::Interface::Get(); - m_assetsToInspect = upgradeRequests->GetAssetsToUpgrade(); - DoScan(); - } - - void VersionExplorer::DoScan() - { - m_state = ProcessState::Scan; - m_settingsCache = AZStd::make_unique(); - ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false; - ScriptCanvas::Grammar::g_printAbstractCodeModel = false; - ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false; - - AZ::SystemTickBus::Handler::BusConnect(); - AZ::Debug::TraceMessageBus::Handler::BusConnect(); - - if (!m_assetsToInspect.empty()) - { - m_discoveredAssets = m_assetsToInspect.size(); - m_failedAssets = 0; - m_inspectedAssets = 0; - m_currentAssetRowIndex = 0; - m_ui->progressFrame->setVisible(true); - m_ui->progressBar->setVisible(true); - m_ui->progressBar->setRange(0, aznumeric_cast(m_assetsToInspect.size())); - m_ui->progressBar->setValue(0); - - m_ui->spinner->SetIsBusy(true); - m_ui->spinner->SetBusyIconSize(32); - - m_ui->scanButton->setEnabled(false); - m_ui->upgradeAllButton->setEnabled(false); - m_ui->onlyShowOutdated->setEnabled(false); - - m_inspectingAsset = m_assetsToInspect.begin(); - m_keepEditorAlive = AZStd::make_unique(); - } - } - - void VersionExplorer::BackupComplete() - { - m_currentAssetRowIndex = 0; - m_ui->progressBar->setValue(0); - DoScan(); - } - - void VersionExplorer::InspectAsset(AZ::Data::Asset& asset, AZ::Data::AssetInfo& assetInfo) - { - Log("InspectAsset: %s", asset.GetHint().c_str()); - AZ::Entity* scriptCanvasEntity = nullptr; - if (asset.GetType() == azrtti_typeid()) - { - ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs(); - if (!scriptCanvasAsset) - { - Log("InspectAsset: %s, AsestData failed to return ScriptCanvasAsset", asset.GetHint().c_str()); - return; - } - - scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity(); - AZ_Assert(scriptCanvasEntity, "The Script Canvas asset must have a valid entity"); - } - - auto graphComponent = scriptCanvasEntity->FindComponent(); - AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component"); - - bool onlyShowOutdatedGraphs = m_ui->onlyShowOutdated->isChecked(); - bool forceUpgrade = m_ui->forceUpgrade->isChecked(); - ScriptCanvas::VersionData graphVersion = graphComponent->GetVersion(); - - - if (!forceUpgrade && onlyShowOutdatedGraphs && graphVersion.IsLatest()) - { - ScanComplete(asset); - Log("InspectAsset: %s, is at latest", asset.GetHint().c_str()); - return; - } - - m_ui->tableWidget->insertRow(static_cast(m_currentAssetRowIndex)); - QTableWidgetItem* rowName = new QTableWidgetItem(tr(asset.GetHint().c_str())); - m_ui->tableWidget->setItem(static_cast(m_currentAssetRowIndex), static_cast(ColumnAsset), rowName); - - if (forceUpgrade || !graphComponent->GetVersion().IsLatest()) - { - m_assetsToUpgrade.push_back(asset); - - AzQtComponents::StyledBusyLabel* spinner = new AzQtComponents::StyledBusyLabel(this); - spinner->SetBusyIconSize(16); - - QPushButton* rowGoToButton = new QPushButton(this); - rowGoToButton->setText("Upgrade"); - rowGoToButton->setEnabled(false); - - connect(rowGoToButton, &QPushButton::clicked, [this, spinner, rowGoToButton, assetInfo] { - - AZ::SystemTickBus::QueueFunction([this, rowGoToButton, spinner, assetInfo]() { - // Queue the process state change because we can't connect to the SystemTick bus in a Qt lambda - UpgradeSingle(rowGoToButton, spinner, assetInfo); - }); - - AZ::SystemTickBus::ExecuteQueuedEvents(); - - }); - - m_ui->tableWidget->setCellWidget(static_cast(m_currentAssetRowIndex), static_cast(ColumnAction), rowGoToButton); - m_ui->tableWidget->setCellWidget(static_cast(m_currentAssetRowIndex), static_cast(ColumnStatus), spinner); - } - - char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 }; - AZStd::string path = AZStd::string::format("@devroot@/%s", asset.GetHint().c_str()); - AZ::IO::FileIOBase::GetInstance()->ResolvePath(path.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN); - AZ::StringFunc::Path::GetFullPath(resolvedBuffer, path); - AZ::StringFunc::Path::Normalize(path); - - bool result = false; - AZ::Data::AssetInfo info; - AZStd::string watchFolder; - QByteArray assetNameUtf8 = asset.GetHint().c_str(); - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, assetNameUtf8, info, watchFolder); - - AZ_Error(ScriptCanvas::k_VersionExplorerWindow.data(), result, "Failed to locate asset info for '%s'.", assetNameUtf8.constData()); - - QToolButton* browseButton = new QToolButton(this); - browseButton->setToolTip(AzQtComponents::fileBrowserActionName()); - browseButton->setIcon(QIcon(":/stylesheet/img/UI20/browse-edit.svg")); - - QString absolutePath = QDir(watchFolder.c_str()).absoluteFilePath(info.m_relativePath.c_str()); - connect(browseButton, &QPushButton::clicked, [absolutePath] { - AzQtComponents::ShowFileOnDesktop(absolutePath); - }); - - m_ui->tableWidget->setCellWidget(static_cast(m_currentAssetRowIndex), static_cast(ColumnBrowse), browseButton); - ScanComplete(asset); - ++m_inspectedAssets; - ++m_currentAssetRowIndex; - } - - void VersionExplorer::UpgradeSingle - ( QPushButton* rowGoToButton - , AzQtComponents::StyledBusyLabel* spinner - , AZ::Data::AssetInfo assetInfo) - { - AZ::Data::Asset asset = AZ::Data::AssetManager::Instance().GetAsset - ( assetInfo.m_assetId, assetInfo.m_assetType, AZ::Data::AssetLoadBehavior::PreLoad); - - if (asset) - { - asset.BlockUntilLoadComplete(); - - if (asset.IsReady()) - { - AZ::Interface::Get()->SetIsUpgrading(true); - m_isUpgradingSingleGraph = true; - m_logs.clear(); - m_ui->textEdit->clear(); - spinner->SetIsBusy(true); - rowGoToButton->setEnabled(false); - - m_inProgressAsset = AZStd::find_if(m_assetsToUpgrade.begin(), m_assetsToUpgrade.end() - , [asset](const UpgradeAssets::value_type& assetToUpgrade) - { - return assetToUpgrade.GetId() == asset.GetId(); - }); - - m_state = ProcessState::Upgrade; - AZ::SystemTickBus::Handler::BusConnect(); - } - } - } - - void VersionExplorer::ScanComplete(const AZ::Data::Asset& asset) - { - Log("ScanComplete: %s", asset.GetHint().c_str()); - m_inProgress = false; - m_ui->progressBar->setValue(aznumeric_cast(m_currentAssetRowIndex)); - m_ui->scanButton->setEnabled(true); - - m_inspectingAsset = m_assetsToInspect.erase(m_inspectingAsset); - FlushLogs(); - - if (m_inspectingAsset == m_assetsToInspect.end()) - { - AZ::SystemTickBus::QueueFunction([this]() { FinalizeScan(); }); - - if (!m_assetsToUpgrade.empty()) - { - m_ui->upgradeAllButton->setEnabled(true); - } - } - } - - void VersionExplorer::FinalizeScan() - { - Log("FinalizeScan()"); - - m_ui->spinner->SetIsBusy(false); - m_ui->onlyShowOutdated->setEnabled(true); - - // Enable all the Upgrade buttons - for (int row = 0; row < m_ui->tableWidget->rowCount(); ++row) - { - QPushButton* button = qobject_cast(m_ui->tableWidget->cellWidget(row, ColumnAction)); - if (button) - { - button->setEnabled(true); - } - } - - QString spinnerText = QStringLiteral("Scan Complete"); - if (m_assetsToUpgrade.empty()) - { - spinnerText.append(" - No graphs require upgrade!"); - } - else - { - spinnerText.append(QString::asprintf(" - Discovered: %zu, Inspected: %zu, Failed: %zu, Upgradeable: %zu" - , m_discoveredAssets, m_inspectedAssets, m_failedAssets, m_assetsToUpgrade.size())); - } - - - m_ui->spinner->SetText(spinnerText); - m_ui->progressBar->setVisible(false); - - if (!m_assetsToUpgrade.empty()) - { - m_ui->upgradeAllButton->setEnabled(true); - } - - AZ::SystemTickBus::Handler::BusDisconnect(); - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); - UpgradeNotifications::Bus::Handler::BusDisconnect(); - - m_keepEditorAlive.reset(); - m_settingsCache.reset(); - m_state = ProcessState::Inactive; - } - - void VersionExplorer::FlushLogs() - { - if (m_logs.empty()) - { - return; - } - - const QTextCursor oldCursor = m_ui->textEdit->textCursor(); - QScrollBar* scrollBar = m_ui->textEdit->verticalScrollBar(); - - m_ui->textEdit->moveCursor(QTextCursor::End); - QTextCursor textCursor = m_ui->textEdit->textCursor(); - - while (!m_logs.empty()) - { - auto line = "\n" + m_logs.front(); - - m_logs.pop_front(); - - textCursor.insertText(line.c_str()); - } - - scrollBar->setValue(scrollBar->maximum()); - m_ui->textEdit->moveCursor(QTextCursor::StartOfLine); - - } - - bool VersionExplorer::CaptureLogFromTraceBus(const char* window, const char* message) - { - if (m_ui->updateReportingOnly->isChecked() && window != ScriptCanvas::k_VersionExplorerWindow) - { - return true; - } - - AZStd::string msg = message; - if (msg.ends_with("\n")) - { - msg = msg.substr(0, msg.size() - 1); - } - - m_logs.push_back(msg); - return m_ui->updateReportingOnly->isChecked(); - } - - bool VersionExplorer::OnPreError(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) - { - AZStd::string msg = AZStd::string::format("(Error): %s", message); - return CaptureLogFromTraceBus(window, msg.c_str()); - } - - bool VersionExplorer::OnPreWarning(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) - { - AZStd::string msg = AZStd::string::format("(Warning): %s", message); - return CaptureLogFromTraceBus(window, msg.c_str()); - } - - bool VersionExplorer::OnException(const char* message) - { - AZStd::string msg = AZStd::string::format("(Exception): %s", message); - return CaptureLogFromTraceBus("Script Canvas", msg.c_str()); - } - - bool VersionExplorer::OnPrintf(const char* window, const char* message) - { - return CaptureLogFromTraceBus(window, message); - } - - void VersionExplorer::closeEvent(QCloseEvent* event) - { - m_keepEditorAlive.reset(); - - AzQtComponents::StyledDialog::closeEvent(event); - } - -#include - -} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.h deleted file mode 100644 index acb28a7dba..0000000000 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.h +++ /dev/null @@ -1,180 +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 - -#if !defined(Q_MOC_RUN) -#include - -AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - -#include -#include -#include - -#include -#include - -#include -#include -#include -#endif - -class QPushButton; - -namespace Ui -{ - class VersionExplorer; -} - -namespace AzQtComponents -{ - class StyledBusyLabel; -} - -namespace ScriptCanvasEditor -{ - //! Scoped utility to set and restore the "ed_KeepEditorActive" CVar in order to allow - //! the upgrade tool to work even if the editor is not in the foreground - class EditorKeepAlive - { - public: - EditorKeepAlive(); - ~EditorKeepAlive(); - - private: - int m_keepEditorActive; - ICVar* m_edKeepEditorActive; - }; - - //! A tool that collects and upgrades all Script Canvas graphs in the asset catalog - class VersionExplorer - : public AzQtComponents::StyledDialog - , private AZ::SystemTickBus::Handler - , private UpgradeNotifications::Bus::Handler - , private AZ::Debug::TraceMessageBus::Handler - { - Q_OBJECT - - public: - AZ_CLASS_ALLOCATOR(VersionExplorer, AZ::SystemAllocator, 0); - - explicit VersionExplorer(QWidget* parent = nullptr); - ~VersionExplorer(); - - private: - - static constexpr int ColumnAsset = 0; - static constexpr int ColumnAction = 1; - static constexpr int ColumnBrowse = 2; - static constexpr int ColumnStatus = 3; - - void OnScan(); - void OnClose(); - - enum class ProcessState - { - Inactive, - Backup, - Scan, - Upgrade, - }; - ProcessState m_state = ProcessState::Inactive; - - void DoScan(); - void ScanComplete(const AZ::Data::Asset&); - - void InspectAsset(AZ::Data::Asset& asset, AZ::Data::AssetInfo& assetInfo); - - void OnUpgradeAll(); - - // SystemTickBus::Handler - void OnSystemTick() override; - // - - // AZ::Debug::TranceMessageBus::Handler - bool OnException(const char* /*message*/) override; - bool OnPrintf(const char* /*window*/, const char* /*message*/) override; - bool OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override; - bool OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override; - // - - bool CaptureLogFromTraceBus(const char* window, const char* message); - - enum class OperationResult - { - Success, - Failure, - }; - - void GraphUpgradeComplete(const AZ::Data::Asset, OperationResult result, AZStd::string_view message); - - bool IsUpgrading() const; - - bool m_inProgress = false; - // scan fields - size_t m_currentAssetRowIndex = 0; - size_t m_inspectedAssets = 0; - size_t m_failedAssets = 0; - size_t m_discoveredAssets = 0; - - IUpgradeRequests::AssetList m_assetsToInspect; - IUpgradeRequests::AssetList::iterator m_inspectingAsset; - using UpgradeAssets = AZStd::vector>; - UpgradeAssets m_assetsToUpgrade; - UpgradeAssets::iterator m_inProgressAsset; - - AZ::Data::Asset m_currentAsset; - - AZStd::unique_ptr m_ui; - - AZStd::unique_ptr m_settingsCache; - - // upgrade fields - AZStd::recursive_mutex m_mutex; - bool m_upgradeComplete = false; - AZ::Data::Asset m_upgradeAsset; - int m_upgradeAssetIndex = 0; - OperationResult m_upgradeResult; - AZStd::string m_upgradeMessage; - AZStd::string m_tmpFileName; - - AZStd::unique_ptr m_keepEditorAlive; - - AZStd::deque m_logs; - - AZ::Entity* m_scriptCanvasEntity = nullptr; - - bool m_isUpgradingSingleGraph = false; - - void UpgradeSingle(QPushButton* item, AzQtComponents::StyledBusyLabel* spinner, AZ::Data::AssetInfo assetInfo); - - void FlushLogs(); - - void FinalizeUpgrade(); - void FinalizeScan(); - - void BackupComplete(); - AZStd::string BackupGraph(const AZ::Data::Asset&); - void UpgradeGraph(const AZ::Data::Asset&); - - void GraphUpgradeCompleteUIUpdate(const AZ::Data::Asset asset, OperationResult result, AZStd::string_view message); - void OnGraphUpgradeComplete(AZ::Data::Asset&, bool skipped = false) override; - - void OnSourceFileReleased(AZ::Data::Asset asset); - - void closeEvent(QCloseEvent* event) override; - - bool m_overwriteAll = false; - void PerformMove(AZ::Data::Asset asset, AZStd::string source, AZStd::string target, size_t remainingAttempts); - - void Log(const char* format, ...); - }; -} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.cpp new file mode 100644 index 0000000000..c1faedc3a3 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.cpp @@ -0,0 +1,100 @@ +/* + * 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 ScriptCanvasEditor +{ + namespace VersionExplorer + { + void Log::Activate() + { + AZ::Debug::TraceMessageBus::Handler::BusConnect(); + LogBus::Handler::BusConnect(); + } + + void Log::Clear() + { + m_logs.clear(); + } + + bool Log::CaptureFromTraceBus(const char* window, const char* message) + { + if (m_isExclusiveReportingEnabled && window != ScriptCanvas::k_VersionExplorerWindow) + { + return true; + } + + AZStd::string msg = message; + if (msg.ends_with("\n")) + { + msg = msg.substr(0, msg.size() - 1); + } + + m_logs.push_back(msg); + return m_isExclusiveReportingEnabled; + } + + void Log::Deactivate() + { + AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); + } + + void Log::Entry(const char* format, ...) + { + if (m_isVerbose) + { + char sBuffer[2048]; + va_list ArgList; + va_start(ArgList, format); + azvsnprintf(sBuffer, sizeof(sBuffer), format, ArgList); + sBuffer[sizeof(sBuffer) - 1] = '\0'; + va_end(ArgList); + AZ_TracePrintf(ScriptCanvas::k_VersionExplorerWindow.data(), "%s\n", sBuffer); + } + } + + const AZStd::vector* Log::GetEntries() const + { + return &m_logs; + } + + void Log::SetVersionExporerExclusivity(bool enabled) + { + m_isExclusiveReportingEnabled = enabled; + } + + void Log::SetVerbose(bool verbosity) + { + m_isVerbose = verbosity; + } + + bool Log::OnPreError(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) + { + AZStd::string msg = AZStd::string::format("(Error): %s", message); + return CaptureFromTraceBus(window, msg.c_str()); + } + + bool Log::OnPreWarning(const char* window, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) + { + AZStd::string msg = AZStd::string::format("(Warning): %s", message); + return CaptureFromTraceBus(window, msg.c_str()); + } + + bool Log::OnException(const char* message) + { + AZStd::string msg = AZStd::string::format("(Exception): %s", message); + return CaptureFromTraceBus("Script Canvas", msg.c_str()); + } + + bool Log::OnPrintf(const char* window, const char* message) + { + return CaptureFromTraceBus(window, message); + } + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.h new file mode 100644 index 0000000000..7399b085da --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.h @@ -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 + * + */ + +#pragma once + +#include +#include +#include + +namespace ScriptCanvasEditor +{ + namespace VersionExplorer + { + class Log + : private AZ::Debug::TraceMessageBus::Handler + , private LogBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(Log, AZ::SystemAllocator, 0); + + void Activate() override; + void Clear() override; + void Deactivate(); + void Entry(const char* format, ...) override; + const AZStd::vector* GetEntries() const override; + void SetVersionExporerExclusivity(bool enabled) override; + void SetVerbose(bool verbosity) override; + + private: + bool m_isExclusiveReportingEnabled = false; + bool m_isVerbose = false; + AZStd::vector m_logs; + + bool CaptureFromTraceBus(const char* window, const char* message); + bool OnException(const char* /*message*/) override; + bool OnPrintf(const char* /*window*/, const char* /*message*/) override; + bool OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override; + bool OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override; + }; + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.ui b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/View.ui similarity index 99% rename from Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.ui rename to Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/View.ui index 0cd67bcf22..f549c821d6 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.ui +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/View.ui @@ -1,7 +1,7 @@ - VersionExplorer - + View + Qt::WindowModal diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/ExecutionLogAsset.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/ExecutionLogAsset.cpp index 51cb034f35..e1fe71d4d9 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/ExecutionLogAsset.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/ExecutionLogAsset.cpp @@ -85,7 +85,7 @@ namespace ScriptCanvas const char* ExecutionLogAsset::GetDefaultDirectoryRoot() { - return AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@"); + return AZ::IO::FileIOBase::GetInstance()->GetAlias("@engroot@"); } ExecutionLogAsset::ExecutionLogAsset(const AZ::Data::AssetId& assetId, AZ::Data::AssetData::AssetStatus status) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.h index 037985d980..24c83b20a1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Asset/RuntimeAsset.h @@ -36,7 +36,7 @@ namespace ScriptCanvas azrtti_typeid(), "Script Canvas Runtime", "Script Canvas Runtime Graph", - "@devassets@/scriptcanvas", + "@projectroot@/scriptcanvas", ".scriptcanvas_compiled", "Script Canvas Runtime", "Untitled-%i", @@ -177,7 +177,7 @@ namespace ScriptCanvas azrtti_typeid(), "Script Canvas Function Interface", "Script Canvas Function Interface", - "@devassets@/scriptcanvas", + "@projectroot@/scriptcanvas", ".scriptcanvas_fn_compiled", "Script Canvas Function Interface", "Untitled-Function-%i", diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index 4509e7e907..7d76dbc27d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -4320,6 +4320,12 @@ namespace ScriptCanvas { AZ_Assert(execution->GetSymbol() != Symbol::FunctionDefinition, "Function definition input is not handled in AbstractCodeModel::ParseInputDatum"); + if (!input.GetDataType().IsValid()) + { + AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::InvalidDataTypeInInput)); + return; + } + auto nodes = execution->GetId().m_node->GetConnectedNodes(input); if (nodes.empty()) { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.cpp index 5a2eb80187..c548176af5 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.cpp @@ -67,7 +67,7 @@ namespace ScriptCanvas void ReceiveScriptEvent::PopulateAsset(AZ::Data::Asset asset, SlotIdMapping& populationMapping) { - if (CreateHandler(asset)) + if (InitializeDefinition(asset)) { if (!CreateEbus()) { @@ -140,7 +140,6 @@ namespace ScriptCanvas } } - void ReceiveScriptEvent::InitializeEvent(AZ::Data::Asset asset, int eventIndex, SlotIdMapping& populationMapping) { if (!m_handler) @@ -353,6 +352,13 @@ namespace ScriptCanvas AZStd::optional ReceiveScriptEvent::GetEventIndex(AZStd::string eventName) const { + if (!m_handler) + { + const_cast(this)->InitializeDefinition(m_asset); + const_cast(this)->CreateEbus(); + } + + AZ_Error("ScriptCanvas", m_handler != nullptr, "GetEventIndex called and handler was not created"); return m_handler ? AZStd::optional(m_handler->GetFunctionIndex(eventName.c_str())) : AZStd::nullopt; } @@ -490,15 +496,10 @@ namespace ScriptCanvas return m_definition.IsAddressRequired() || (slot && slot->GetDataType().IsValid()); } - bool ReceiveScriptEvent::CreateHandler(AZ::Data::Asset asset) + bool ReceiveScriptEvent::InitializeDefinition(AZ::Data::Asset asset) { AZStd::lock_guard lock(m_mutex); - if (m_handler) - { - return true; - } - if (!asset) { return false; @@ -518,12 +519,11 @@ namespace ScriptCanvas } return true; - } void ReceiveScriptEvent::OnScriptEventReady(const AZ::Data::Asset& asset) { - if (CreateHandler(asset)) + if (InitializeDefinition(asset)) { CompleteInitialize(asset); } @@ -531,7 +531,7 @@ namespace ScriptCanvas bool ReceiveScriptEvent::CreateEbus() { - if (!m_ebus) + if (!m_ebus || !m_handler) { AZ::BehaviorContext* behaviorContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext); @@ -547,13 +547,11 @@ namespace ScriptCanvas AZ_Assert(m_ebus, "Behavior Context EBus does not exist: %s", m_definition.GetName().c_str()); AZ_Assert(m_ebus->m_createHandler, "The ebus %s has no create handler!", m_definition.GetName().c_str()); AZ_Assert(m_ebus->m_destroyHandler, "The ebus %s has no destroy handler!", m_definition.GetName().c_str()); - AZ_Verify(m_ebus->m_createHandler->InvokeResult(m_handler, &m_definition), "Behavior Context EBus handler creation failed %s", m_definition.GetName().c_str()); - AZ_Assert(m_handler, "Ebus create handler failed %s", m_definition.GetName().c_str()); } - return true; + return m_ebus != nullptr && m_handler != nullptr; } bool ReceiveScriptEvent::IsOutOfDate(const VersionData& graphVersion) const @@ -597,7 +595,6 @@ namespace ScriptCanvas } } - AZStd::string ReceiveScriptEvent::GetUpdateString() const { if (m_ebus) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.h index 0f8d673629..a41d0ba3f9 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ReceiveScriptEvent.h @@ -101,7 +101,7 @@ namespace ScriptCanvas Internal::ScriptEventEntry ConfigureEbusEntry(const ScriptEvents::Method& methodDefinition, const AZ::BehaviorEBusHandler::BusForwarderEvent& event, SlotIdMapping& populationMapping); - bool CreateHandler(AZ::Data::Asset asset); + bool InitializeDefinition(AZ::Data::Asset asset); void CompleteInitialize(AZ::Data::Asset asset); void PopulateAsset(AZ::Data::Asset asset, SlotIdMapping& populationMapping); bool m_eventInitComplete = false; @@ -120,7 +120,6 @@ namespace ScriptCanvas bool m_autoConnectToGraphOwner = true; bool m_connected; - }; } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ScriptEventBase.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ScriptEventBase.cpp index 3b01a8fa7c..e4cbfc94f7 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ScriptEventBase.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ScriptEventBase.cpp @@ -122,11 +122,8 @@ namespace ScriptCanvas void ScriptEventBase::OnActivate() { - if (!m_asset || m_asset.GetStatus() == AZ::Data::AssetData::AssetStatus::NotLoaded) - { - m_asset = AZ::Data::AssetManager::Instance().GetAsset(m_scriptEventAssetId, AZ::Data::AssetLoadBehavior::PreLoad); - m_asset.BlockUntilLoadComplete(); - } + m_asset = AZ::Data::AssetManager::Instance().GetAsset(m_scriptEventAssetId, AZ::Data::AssetLoadBehavior::PreLoad); + m_asset.BlockUntilLoadComplete(); } void ScriptEventBase::OnAssetReady(AZ::Data::Asset asset) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h index da9a1ebc1f..a803ea862e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h @@ -41,6 +41,7 @@ namespace ScriptCanvas constexpr const char* InactiveGraph = "This graph defines no functions, it is never activated, and will never execute. Add a Start node or connect an event handler or define functions."; constexpr const char* InfiniteLoopWritingToVariable = "infinite loop when writing to variable"; constexpr const char* InfiniteSelfActivationLoop = "infinite loop when activating the entity that owns this graph"; + constexpr const char* InvalidDataTypeInInput = "invalid data type in input slot"; constexpr const char* MetaDataNeedsTupleTypeIdForEventCall = "Meta data needs an AzTypeId for Tuple if an EBus call returns multiple types to ScriptCanvas"; constexpr const char* MetaDataRequiredForEventCall = "Meta data is required for ACM node that is an EBus call"; constexpr const char* MissingFalseExecutionSlotOnIf = "A 'False' Execution output slot is required in an If branch node"; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp index 9a1ef73126..ee7eb704bb 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp @@ -246,8 +246,7 @@ namespace ScriptCanvas } else { - ErrorList errors = { AZStd::string("provide translation failure details") }; - return AZ::Failure(errors); + return AZ::Failure(translation.MoveErrors()); } } @@ -874,7 +873,13 @@ namespace ScriptCanvas AZStd::optional eventIndex = ebusHandling->m_node->GetEventIndex(nameAndEventThread.first); if (!eventIndex) { - AddError(nullptr, aznew Internal::ParseError(ebusHandling->m_node->GetEntityId(), AZStd::string::format("EBus handler did not return a valid index for event %s", nameAndEventThread.first.c_str()))); + AddError(nullptr, + aznew Internal::ParseError( + ebusHandling->m_node->GetEntityId() + , AZStd::string::format + ( "EBus Handler %s did not return a valid index for event %s" + , ebusHandling->m_ebusName.c_str() + , nameAndEventThread.first.c_str()))); return; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.cpp index 09315ec4b0..d21fb354b1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.cpp @@ -107,27 +107,9 @@ namespace ScriptCanvas m_translationDuration = AZStd::chrono::microseconds(AZStd::chrono::system_clock::now() - m_translationStartTime).count(); } - AZStd::string GraphToX::ResolveScope(const AZStd::vector& namespaces) - { - AZStd::string resolution; - - if (!namespaces.empty()) - { - resolution = Grammar::ToIdentifier(namespaces[0]); - - for (size_t index = 1; index < namespaces.size(); ++index) - { - resolution += m_configuration.m_lexicalScopeDelimiter; - resolution += Grammar::ToIdentifier(namespaces[index]); - } - } - - return resolution; - } - - void GraphToX::SingleLineComment(Writer& writer) + AZStd::vector&& GraphToX::MoveErrors() { - writer.Write(m_configuration.m_singleLineComment); + return AZStd::move(m_errors); } void GraphToX::OpenBlockComment(Writer& writer) @@ -160,6 +142,29 @@ namespace ScriptCanvas writer.Indent(); } + AZStd::string GraphToX::ResolveScope(const AZStd::vector& namespaces) + { + AZStd::string resolution; + + if (!namespaces.empty()) + { + resolution = Grammar::ToIdentifier(namespaces[0]); + + for (size_t index = 1; index < namespaces.size(); ++index) + { + resolution += m_configuration.m_lexicalScopeDelimiter; + resolution += Grammar::ToIdentifier(namespaces[index]); + } + } + + return resolution; + } + + void GraphToX::SingleLineComment(Writer& writer) + { + writer.Write(m_configuration.m_singleLineComment); + } + void GraphToX::WriteCopyright(Writer& writer) { OpenBlockComment(writer); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.h index 062a58efe1..c4e189679a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToX.h @@ -51,12 +51,13 @@ namespace ScriptCanvas AZStd::string_view GetGraphName() const; AZStd::string_view GetFullPath() const; AZStd::sys_time_t GetTranslationDuration() const; - AZStd::string ResolveScope(const AZStd::vector& namespaces); - void SingleLineComment(Writer& writer); + AZStd::vector&& MoveErrors(); void OpenBlockComment(Writer& writer); void OpenFunctionBlock(Writer& writer); void OpenNamespace(Writer& writer, AZStd::string_view ns); void OpenScope(Writer& writer); + AZStd::string ResolveScope(const AZStd::vector& namespaces); + void SingleLineComment(Writer& writer); void WriteCopyright(Writer& writer); void WriteDoNotModify(Writer& writer); void WriteLastWritten(Writer& writer); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.cpp index dd8b42730d..c39cac13a3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.cpp @@ -72,6 +72,15 @@ namespace ScriptCanvas resultString += entry->GetDescription(); } + for (const auto& errors : m_errors) + { + for (auto& entry : errors.second) + { + resultString += "* "; + resultString += entry->GetDescription(); + } + } + return resultString; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.h index 0cde56d5dd..bd616fbfbb 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/TranslationResult.h @@ -12,10 +12,11 @@ #include #include #include -#include #include -#include +#include +#include #include +#include namespace AZ { @@ -85,7 +86,7 @@ namespace ScriptCanvas AZStd::sys_time_t m_duration; }; - using ErrorList = AZStd::vector; + using ErrorList = AZStd::vector; using Errors = AZStd::unordered_map; using Translations = AZStd::unordered_map; @@ -102,7 +103,6 @@ namespace ScriptCanvas const AZStd::sys_time_t m_translationDuration; Result(AZStd::string invalidSourceInfo); - Result(Result&& source); Result(Grammar::AbstractCodeModelConstPtr model); Result(Grammar::AbstractCodeModelConstPtr model, Translations&& translations, Errors&& errors); diff --git a/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake b/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake index 52b1cc4772..609f7cd454 100644 --- a/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake +++ b/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake @@ -259,15 +259,24 @@ set(FILES Editor/View/Windows/ScriptCanvasContextMenus.cpp Editor/View/Windows/ScriptCanvasContextMenus.h Editor/View/Windows/ScriptCanvasEditorResources.qrc - Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.h + Editor/View/Windows/Tools/UpgradeTool/Controller.cpp + Editor/View/Windows/Tools/UpgradeTool/Controller.h + Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp + Editor/View/Windows/Tools/UpgradeTool/FileSaver.h + Editor/View/Windows/Tools/UpgradeTool/LogTraits.h + Editor/View/Windows/Tools/UpgradeTool/Model.cpp + Editor/View/Windows/Tools/UpgradeTool/Model.h + Editor/View/Windows/Tools/UpgradeTool/Modifier.cpp + Editor/View/Windows/Tools/UpgradeTool/Modifier.h + Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h + Editor/View/Windows/Tools/UpgradeTool/Scanner.cpp + Editor/View/Windows/Tools/UpgradeTool/Scanner.h Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.cpp + Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.h Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui - Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.cpp - Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.h - Editor/View/Windows/Tools/UpgradeTool/UpgradeTool.ui - Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.h - Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.cpp - Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.ui + Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.cpp + Editor/View/Windows/Tools/UpgradeTool/VersionExplorerLog.h + Editor/View/Windows/Tools/UpgradeTool/View.ui Editor/Framework/ScriptCanvasGraphUtilities.inl Editor/Framework/ScriptCanvasGraphUtilities.h Editor/Framework/ScriptCanvasTraceUtilities.h diff --git a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_MultipleOut.scriptcanvas b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_MultipleOut.scriptcanvas index 1d316c4dc0..1770d0e6c2 100644 --- a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_MultipleOut.scriptcanvas +++ b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_MultipleOut.scriptcanvas @@ -1,5227 +1,3238 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "ScriptCanvasData", + "ClassData": { + "m_scriptCanvas": { + "Id": { + "id": 20005528169920 + }, + "Name": "LY_SC_UnitTest_MultipleOut", + "Components": { + "Component_[14476721853276197761]": { + "$type": "EditorGraphVariableManagerComponent", + "Id": 14476721853276197761, + "m_variableData": { + "m_nameVariableMap": [ + { + "Key": { + "m_id": "{0D12DC58-0CED-4E69-9234-7B03F80A0008}" + }, + "Value": { + "Datum": { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Boolean" + }, + "VariableId": { + "m_id": "{0D12DC58-0CED-4E69-9234-7B03F80A0008}" + }, + "VariableName": "Exec 1" + } + }, + { + "Key": { + "m_id": "{2ACB3F3A-6D87-4EBC-AD66-F8702F8F62DD}" + }, + "Value": { + "Datum": { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 3.0, + "label": "Three" + }, + "VariableId": { + "m_id": "{2ACB3F3A-6D87-4EBC-AD66-F8702F8F62DD}" + }, + "VariableName": "Three" + } + }, + { + "Key": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + }, + "Value": { + "Datum": { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + "VariableId": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + }, + "VariableName": "counter" + } + }, + { + "Key": { + "m_id": "{84E8E7E7-7A51-4DB2-8E03-ECEB4E448C2B}" + }, + "Value": { + "Datum": { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Boolean" + }, + "VariableId": { + "m_id": "{84E8E7E7-7A51-4DB2-8E03-ECEB4E448C2B}" + }, + "VariableName": "Exec 3" + } + }, + { + "Key": { + "m_id": "{DB5D6C5D-FD69-474C-BBBE-4984C5715FA8}" + }, + "Value": { + "Datum": { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Boolean" + }, + "VariableId": { + "m_id": "{DB5D6C5D-FD69-474C-BBBE-4984C5715FA8}" + }, + "VariableName": "Exec 2" + } + } + ] + } + }, + "Component_[16621147190561896838]": { + "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph", + "Id": 16621147190561896838, + "m_graphData": { + "m_nodes": [ + { + "Id": { + "id": 20044182875584 + }, + "Name": "SC-Node(Start)", + "Components": { + "Component_[10424525019465666117]": { + "$type": "Start", + "Id": 10424525019465666117, + "Slots": [ + { + "id": { + "m_id": "{25061884-0633-4061-B583-0796CDC9B215}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled when the entity that owns this graph is fully activated.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + }, + { + "Id": { + "id": 20014118104512 + }, + "Name": "SC-Node(OperatorAdd)", + "Components": { + "Component_[12328748346053958726]": { + "$type": "OperatorAdd", + "Id": 12328748346053958726, + "Slots": [ + { + "id": { + "m_id": "{AC7B582F-C1DE-4C3C-ACC9-D729BE959BF5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E73CCFA-3E3B-44B5-9E9E-FD9250FCE078}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B20F1E7F-B2BA-43E4-AD32-D58B8CFA5B57}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + }, + { + "id": { + "m_id": "{265B8942-C187-4D83-A017-20BEE053B96D}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{5B25184E-0BC4-44A8-9539-772AC75432B4}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 1.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 20022708039104 + }, + "Name": "SC-Node(OperatorAdd)", + "Components": { + "Component_[12328748346053958726]": { + "$type": "OperatorAdd", + "Id": 12328748346053958726, + "Slots": [ + { + "id": { + "m_id": "{AC7B582F-C1DE-4C3C-ACC9-D729BE959BF5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E73CCFA-3E3B-44B5-9E9E-FD9250FCE078}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B20F1E7F-B2BA-43E4-AD32-D58B8CFA5B57}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + }, + { + "id": { + "m_id": "{265B8942-C187-4D83-A017-20BEE053B96D}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{5B25184E-0BC4-44A8-9539-772AC75432B4}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 1.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 20061362744768 + }, + "Name": "SC-Node(OperatorAdd)", + "Components": { + "Component_[12328748346053958726]": { + "$type": "OperatorAdd", + "Id": 12328748346053958726, + "Slots": [ + { + "id": { + "m_id": "{AC7B582F-C1DE-4C3C-ACC9-D729BE959BF5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E73CCFA-3E3B-44B5-9E9E-FD9250FCE078}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B20F1E7F-B2BA-43E4-AD32-D58B8CFA5B57}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + }, + { + "id": { + "m_id": "{265B8942-C187-4D83-A017-20BEE053B96D}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{5B25184E-0BC4-44A8-9539-772AC75432B4}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 1.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 20009823137216 + }, + "Name": "SC-Node(EqualTo)", + "Components": { + "Component_[13545476611799837370]": { + "$type": "EqualTo", + "Id": 13545476611799837370, + "Slots": [ + { + "id": { + "m_id": "{3A36FFEC-1706-4E84-AF24-9CA117ED9A08}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{71837FF2-8438-4225-AA73-B017D5097FE7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signal to perform the evaluation when desired.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{85BEF751-FEBE-457B-B73A-F9334E248174}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the result of the operation is true.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F17CFAD4-F1FB-4E17-B466-DDEAE1D0C4D7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the result of the operation is false.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F89D9F4D-8840-4420-BB13-A62BE5E0248C}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value A", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3545012108 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + }, + { + "id": { + "m_id": "{11C3CEF3-4EED-405E-88CB-B25B55C28D5D}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value B", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3545012108 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{2ACB3F3A-6D87-4EBC-AD66-F8702F8F62DD}" + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Value A" + }, + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Value B" + } + ] + } + } + }, + { + "Id": { + "id": 20065657712064 + }, + "Name": "SC-Node(EqualTo)", + "Components": { + "Component_[13545476611799837370]": { + "$type": "EqualTo", + "Id": 13545476611799837370, + "Slots": [ + { + "id": { + "m_id": "{3A36FFEC-1706-4E84-AF24-9CA117ED9A08}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{71837FF2-8438-4225-AA73-B017D5097FE7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signal to perform the evaluation when desired.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{85BEF751-FEBE-457B-B73A-F9334E248174}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the result of the operation is true.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F17CFAD4-F1FB-4E17-B466-DDEAE1D0C4D7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the result of the operation is false.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F89D9F4D-8840-4420-BB13-A62BE5E0248C}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value A", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3545012108 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + }, + { + "id": { + "m_id": "{11C3CEF3-4EED-405E-88CB-B25B55C28D5D}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value B", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3545012108 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{2ACB3F3A-6D87-4EBC-AD66-F8702F8F62DD}" + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Value A" + }, + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Value B" + } + ] + } + } + }, + { + "Id": { + "id": 20069952679360 + }, + "Name": "SC-Node(EqualTo)", + "Components": { + "Component_[13545476611799837370]": { + "$type": "EqualTo", + "Id": 13545476611799837370, + "Slots": [ + { + "id": { + "m_id": "{3A36FFEC-1706-4E84-AF24-9CA117ED9A08}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{71837FF2-8438-4225-AA73-B017D5097FE7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signal to perform the evaluation when desired.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{85BEF751-FEBE-457B-B73A-F9334E248174}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the result of the operation is true.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F17CFAD4-F1FB-4E17-B466-DDEAE1D0C4D7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the result of the operation is false.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F89D9F4D-8840-4420-BB13-A62BE5E0248C}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value A", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3545012108 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{69CFFC85-A02B-4538-8704-D5EB9D768BFA}" + } + }, + { + "id": { + "m_id": "{11C3CEF3-4EED-405E-88CB-B25B55C28D5D}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value B", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3545012108 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{2ACB3F3A-6D87-4EBC-AD66-F8702F8F62DD}" + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Value A" + }, + { + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Value B" + } + ] + } + } + }, + { + "Id": { + "id": 20048477842880 + }, + "Name": "SC Node(SetVariable)", + "Components": { + "Component_[14507547243373092664]": { + "$type": "SetVariableNode", + "Id": 14507547243373092664, + "Slots": [ + { + "id": { + "m_id": "{A23CADFC-70B4-45EB-8178-D9570EE659ED}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "When signaled sends the variable referenced by this node to a Data Output slot", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{69C4E25F-AD39-4CE9-9E19-87DC8C6B43AB}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled after the referenced variable has been pushed to the Data Output slot", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{87AF01EE-69E7-441F-BE3D-8DBD95475AF9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Boolean", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{D0C36DFD-8ED8-4E64-8765-556123BAF3C1}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": true, + "label": "Boolean" + } + ], + "m_variableId": { + "m_id": "{DB5D6C5D-FD69-474C-BBBE-4984C5715FA8}" + }, + "m_variableDataInSlotId": { + "m_id": "{87AF01EE-69E7-441F-BE3D-8DBD95475AF9}" + }, + "m_variableDataOutSlotId": { + "m_id": "{D0C36DFD-8ED8-4E64-8765-556123BAF3C1}" + } + } + } + }, + { + "Id": { + "id": 20057067777472 + }, + "Name": "SC-Node(Print)", + "Components": { + "Component_[17364567436915815279]": { + "$type": "Print", + "Id": 17364567436915815279, + "Slots": [ + { + "id": { + "m_id": "{DE4E1FFE-8F36-4272-81AD-9973CC9DC197}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Input signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{32561DF2-D512-49DD-8DCC-5FB951122DF5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "m_format": "Hello", + "m_unresolvedString": [ + "Hello" + ] + } + } + }, + { + "Id": { + "id": 20052772810176 + }, + "Name": "SC Node(SetVariable)", + "Components": { + "Component_[18169977638437689378]": { + "$type": "SetVariableNode", + "Id": 18169977638437689378, + "Slots": [ + { + "id": { + "m_id": "{F65A6861-482D-4014-8532-31F31E9F64AC}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "When signaled sends the variable referenced by this node to a Data Output slot", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{D4DD8502-2960-4E11-81F5-3CCAC7FCFBCE}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled after the referenced variable has been pushed to the Data Output slot", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0E4216CC-2501-4ED7-8B85-343813337526}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Boolean", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{B150FE31-45A9-4E45-929A-AE9799BA8E31}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": true, + "label": "Boolean" + } + ], + "m_variableId": { + "m_id": "{0D12DC58-0CED-4E69-9234-7B03F80A0008}" + }, + "m_variableDataInSlotId": { + "m_id": "{0E4216CC-2501-4ED7-8B85-343813337526}" + }, + "m_variableDataOutSlotId": { + "m_id": "{B150FE31-45A9-4E45-929A-AE9799BA8E31}" + } + } + } + }, + { + "Id": { + "id": 20018413071808 + }, + "Name": "SC Node(SetVariable)", + "Components": { + "Component_[2433126846967649500]": { + "$type": "SetVariableNode", + "Id": 2433126846967649500, + "Slots": [ + { + "id": { + "m_id": "{EA436CD0-DBC5-4DE6-B658-BA88DB2DD8FB}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "When signaled sends the variable referenced by this node to a Data Output slot", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{EF2E6C1F-3960-45D1-AC20-B49B92785FF9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled after the referenced variable has been pushed to the Data Output slot", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B2EB9354-2D69-4A25-990E-E321FDF0EDDA}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Boolean", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{FD662FEC-08F9-410B-BAA9-1AF2D8229766}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": true, + "label": "Boolean" + } + ], + "m_variableId": { + "m_id": "{84E8E7E7-7A51-4DB2-8E03-ECEB4E448C2B}" + }, + "m_variableDataInSlotId": { + "m_id": "{B2EB9354-2D69-4A25-990E-E321FDF0EDDA}" + }, + "m_variableDataOutSlotId": { + "m_id": "{FD662FEC-08F9-410B-BAA9-1AF2D8229766}" + } + } + } + }, + { + "Id": { + "id": 20027003006400 + }, + "Name": "SC-Node(Expect True)", + "Components": { + "Component_[4200368161720158321]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 4200368161720158321, + "Slots": [ + { + "isVisibile": false, + "id": { + "m_id": "{982114AA-80C2-479A-BB0E-040B8C119422}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "EntityID: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{58E5597B-3073-45DD-86B7-E14097E3EC0B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Candidate", + "toolTip": "a value that must be true", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0D12DC58-0CED-4E69-9234-7B03F80A0008}" + } + }, + { + "id": { + "m_id": "{232AB412-02EB-4201-BA96-BEC573A758EF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Report", + "toolTip": "additional notes for the test report", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CAFD7A46-861D-4156-A15B-38E798044A36}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 4276206253 + } + }, + { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Candidate" + }, + { + "scriptCanvasType": { + "m_type": 5 + }, + "isNullPointer": false, + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "value": "", + "label": "Report" + } + ], + "methodType": 2, + "methodName": "Expect True", + "className": "Unit Testing", + "resultSlotIDs": [ + {} + ], + "prettyClassName": "Unit Testing" + } + } + }, + { + "Id": { + "id": 20035592940992 + }, + "Name": "SC-Node(Expect True)", + "Components": { + "Component_[4200368161720158321]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 4200368161720158321, + "Slots": [ + { + "isVisibile": false, + "id": { + "m_id": "{982114AA-80C2-479A-BB0E-040B8C119422}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "EntityID: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{58E5597B-3073-45DD-86B7-E14097E3EC0B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Candidate", + "toolTip": "a value that must be true", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0D12DC58-0CED-4E69-9234-7B03F80A0008}" + } + }, + { + "id": { + "m_id": "{232AB412-02EB-4201-BA96-BEC573A758EF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Report", + "toolTip": "additional notes for the test report", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CAFD7A46-861D-4156-A15B-38E798044A36}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 4276206253 + } + }, + { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Candidate" + }, + { + "scriptCanvasType": { + "m_type": 5 + }, + "isNullPointer": false, + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "value": "", + "label": "Report" + } + ], + "methodType": 2, + "methodName": "Expect True", + "className": "Unit Testing", + "resultSlotIDs": [ + {} + ], + "prettyClassName": "Unit Testing" + } + } + }, + { + "Id": { + "id": 20039887908288 + }, + "Name": "SC-Node(Expect True)", + "Components": { + "Component_[4200368161720158321]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 4200368161720158321, + "Slots": [ + { + "isVisibile": false, + "id": { + "m_id": "{982114AA-80C2-479A-BB0E-040B8C119422}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "EntityID: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{58E5597B-3073-45DD-86B7-E14097E3EC0B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Candidate", + "toolTip": "a value that must be true", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{84E8E7E7-7A51-4DB2-8E03-ECEB4E448C2B}" + } + }, + { + "id": { + "m_id": "{232AB412-02EB-4201-BA96-BEC573A758EF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Report", + "toolTip": "additional notes for the test report", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CAFD7A46-861D-4156-A15B-38E798044A36}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 4276206253 + } + }, + { + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Candidate" + }, + { + "scriptCanvasType": { + "m_type": 5 + }, + "isNullPointer": false, + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "value": "", + "label": "Report" + } + ], + "methodType": 2, + "methodName": "Expect True", + "className": "Unit Testing", + "resultSlotIDs": [ + {} + ], + "prettyClassName": "Unit Testing" + } + } + }, + { + "Id": { + "id": 20031297973696 + }, + "Name": "SC-Node(Mark Complete)", + "Components": { + "Component_[4383017650724706609]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 4383017650724706609, + "Slots": [ + { + "isVisibile": false, + "id": { + "m_id": "{716C2DBF-F48F-48DC-A1B0-2A0D05ED2DC3}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "EntityID: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{39BDE16A-0CBB-46D1-9C26-24F85A7183FC}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Report", + "toolTip": "additional notes for the test report", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{496BD222-4D88-4AD0-992E-3873EB3E51F9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AD0780F8-7FB5-480D-B7C0-9CC8208223C4}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 4276206253 + } + }, + { + "scriptCanvasType": { + "m_type": 5 + }, + "isNullPointer": false, + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "value": "", + "label": "Report" + } + ], + "methodType": 2, + "methodName": "Mark Complete", + "className": "Unit Testing", + "resultSlotIDs": [ + {} + ], + "prettyClassName": "Unit Testing" + } + } + } + ], + "m_connections": [ + { + "Id": { + "id": 20074247646656 + }, + "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(Print: In)", + "Components": { + "Component_[9544956642945817670]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 9544956642945817670, + "sourceEndpoint": { + "nodeId": { + "id": 20044182875584 + }, + "slotId": { + "m_id": "{25061884-0633-4061-B583-0796CDC9B215}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20057067777472 + }, + "slotId": { + "m_id": "{DE4E1FFE-8F36-4272-81AD-9973CC9DC197}" + } + } + } + } + }, + { + "Id": { + "id": 20078542613952 + }, + "Name": "srcEndpoint=(Print: Out), destEndpoint=(Set Variable: In)", + "Components": { + "Component_[5846765668694970625]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 5846765668694970625, + "sourceEndpoint": { + "nodeId": { + "id": 20057067777472 + }, + "slotId": { + "m_id": "{32561DF2-D512-49DD-8DCC-5FB951122DF5}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20052772810176 + }, + "slotId": { + "m_id": "{F65A6861-482D-4014-8532-31F31E9F64AC}" + } + } + } + } + }, + { + "Id": { + "id": 20082837581248 + }, + "Name": "srcEndpoint=(Print: Out), destEndpoint=(Set Variable: In)", + "Components": { + "Component_[12462935459010319786]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 12462935459010319786, + "sourceEndpoint": { + "nodeId": { + "id": 20057067777472 + }, + "slotId": { + "m_id": "{32561DF2-D512-49DD-8DCC-5FB951122DF5}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20048477842880 + }, + "slotId": { + "m_id": "{A23CADFC-70B4-45EB-8178-D9570EE659ED}" + } + } + } + } + }, + { + "Id": { + "id": 20087132548544 + }, + "Name": "srcEndpoint=(Print: Out), destEndpoint=(Set Variable: In)", + "Components": { + "Component_[15367103920447716135]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 15367103920447716135, + "sourceEndpoint": { + "nodeId": { + "id": 20057067777472 + }, + "slotId": { + "m_id": "{32561DF2-D512-49DD-8DCC-5FB951122DF5}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20018413071808 + }, + "slotId": { + "m_id": "{EA436CD0-DBC5-4DE6-B658-BA88DB2DD8FB}" + } + } + } + } + }, + { + "Id": { + "id": 20091427515840 + }, + "Name": "srcEndpoint=(Add (+): Out), destEndpoint=(Equal To (==): In)", + "Components": { + "Component_[1770958354285950060]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 1770958354285950060, + "sourceEndpoint": { + "nodeId": { + "id": 20061362744768 + }, + "slotId": { + "m_id": "{5E73CCFA-3E3B-44B5-9E9E-FD9250FCE078}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20009823137216 + }, + "slotId": { + "m_id": "{71837FF2-8438-4225-AA73-B017D5097FE7}" + } + } + } + } + }, + { + "Id": { + "id": 20095722483136 + }, + "Name": "srcEndpoint=(Add (+): Out), destEndpoint=(Equal To (==): In)", + "Components": { + "Component_[15742434548170508999]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 15742434548170508999, + "sourceEndpoint": { + "nodeId": { + "id": 20014118104512 + }, + "slotId": { + "m_id": "{5E73CCFA-3E3B-44B5-9E9E-FD9250FCE078}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20065657712064 + }, + "slotId": { + "m_id": "{71837FF2-8438-4225-AA73-B017D5097FE7}" + } + } + } + } + }, + { + "Id": { + "id": 20100017450432 + }, + "Name": "srcEndpoint=(Add (+): Out), destEndpoint=(Equal To (==): In)", + "Components": { + "Component_[5888192785906240625]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 5888192785906240625, + "sourceEndpoint": { + "nodeId": { + "id": 20022708039104 + }, + "slotId": { + "m_id": "{5E73CCFA-3E3B-44B5-9E9E-FD9250FCE078}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20069952679360 + }, + "slotId": { + "m_id": "{71837FF2-8438-4225-AA73-B017D5097FE7}" + } + } + } + } + }, + { + "Id": { + "id": 20104312417728 + }, + "Name": "srcEndpoint=(Set Variable: Out), destEndpoint=(Add (+): In)", + "Components": { + "Component_[14484850856034595321]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 14484850856034595321, + "sourceEndpoint": { + "nodeId": { + "id": 20048477842880 + }, + "slotId": { + "m_id": "{69C4E25F-AD39-4CE9-9E19-87DC8C6B43AB}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20014118104512 + }, + "slotId": { + "m_id": "{AC7B582F-C1DE-4C3C-ACC9-D729BE959BF5}" + } + } + } + } + }, + { + "Id": { + "id": 20108607385024 + }, + "Name": "srcEndpoint=(Set Variable: Out), destEndpoint=(Add (+): In)", + "Components": { + "Component_[13948374526923268574]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 13948374526923268574, + "sourceEndpoint": { + "nodeId": { + "id": 20052772810176 + }, + "slotId": { + "m_id": "{D4DD8502-2960-4E11-81F5-3CCAC7FCFBCE}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20061362744768 + }, + "slotId": { + "m_id": "{AC7B582F-C1DE-4C3C-ACC9-D729BE959BF5}" + } + } + } + } + }, + { + "Id": { + "id": 20112902352320 + }, + "Name": "srcEndpoint=(Set Variable: Out), destEndpoint=(Add (+): In)", + "Components": { + "Component_[17650800042816564839]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17650800042816564839, + "sourceEndpoint": { + "nodeId": { + "id": 20018413071808 + }, + "slotId": { + "m_id": "{EF2E6C1F-3960-45D1-AC20-B49B92785FF9}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20022708039104 + }, + "slotId": { + "m_id": "{AC7B582F-C1DE-4C3C-ACC9-D729BE959BF5}" + } + } + } + } + }, + { + "Id": { + "id": 20117197319616 + }, + "Name": "srcEndpoint=(Expect True: Out), destEndpoint=(Expect True: In)", + "Components": { + "Component_[9927163777207365219]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 9927163777207365219, + "sourceEndpoint": { + "nodeId": { + "id": 20035592940992 + }, + "slotId": { + "m_id": "{CAFD7A46-861D-4156-A15B-38E798044A36}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20039887908288 + }, + "slotId": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + } + } + } + } + }, + { + "Id": { + "id": 20121492286912 + }, + "Name": "srcEndpoint=(Expect True: Out), destEndpoint=(Expect True: In)", + "Components": { + "Component_[6569354793892923961]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 6569354793892923961, + "sourceEndpoint": { + "nodeId": { + "id": 20039887908288 + }, + "slotId": { + "m_id": "{CAFD7A46-861D-4156-A15B-38E798044A36}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20027003006400 + }, + "slotId": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + } + } + } + } + }, + { + "Id": { + "id": 20125787254208 + }, + "Name": "srcEndpoint=(Equal To (==): True), destEndpoint=(Expect True: In)", + "Components": { + "Component_[8734972779133126833]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 8734972779133126833, + "sourceEndpoint": { + "nodeId": { + "id": 20009823137216 + }, + "slotId": { + "m_id": "{85BEF751-FEBE-457B-B73A-F9334E248174}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20035592940992 + }, + "slotId": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + } + } + } + } + }, + { + "Id": { + "id": 20130082221504 + }, + "Name": "srcEndpoint=(Equal To (==): True), destEndpoint=(Expect True: In)", + "Components": { + "Component_[13233443675773627954]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 13233443675773627954, + "sourceEndpoint": { + "nodeId": { + "id": 20065657712064 + }, + "slotId": { + "m_id": "{85BEF751-FEBE-457B-B73A-F9334E248174}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20035592940992 + }, + "slotId": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + } + } + } + } + }, + { + "Id": { + "id": 20134377188800 + }, + "Name": "srcEndpoint=(Equal To (==): True), destEndpoint=(Expect True: In)", + "Components": { + "Component_[8564488729834246542]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 8564488729834246542, + "sourceEndpoint": { + "nodeId": { + "id": 20069952679360 + }, + "slotId": { + "m_id": "{85BEF751-FEBE-457B-B73A-F9334E248174}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20035592940992 + }, + "slotId": { + "m_id": "{9E9BE7E3-A17F-4603-8F71-59130F2D0E7E}" + } + } + } + } + }, + { + "Id": { + "id": 20138672156096 + }, + "Name": "srcEndpoint=(Expect True: Out), destEndpoint=(Mark Complete: In)", + "Components": { + "Component_[17252410557193957278]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17252410557193957278, + "sourceEndpoint": { + "nodeId": { + "id": 20027003006400 + }, + "slotId": { + "m_id": "{CAFD7A46-861D-4156-A15B-38E798044A36}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 20031297973696 + }, + "slotId": { + "m_id": "{496BD222-4D88-4AD0-992E-3873EB3E51F9}" + } + } + } + } + } + ] + }, + "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}", + "versionData": { + "_grammarVersion": 1, + "_runtimeVersion": 1, + "_fileVersion": 1 + }, + "m_variableCounter": 3, + "GraphCanvasData": [ + { + "Key": { + "id": 20005528169920 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "Scale": 0.7164653965189688, + "AnchorX": -554.109130859375, + "AnchorY": -334.977783203125 + } + } + } + } + }, + { + "Key": { + "id": 20009823137216 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 1520.0, + 80.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{4B494FF9-7CD7-496F-9778-8E01DAAE393B}" + } + } + } + }, + { + "Key": { + "id": 20014118104512 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 880.0, + 280.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{DF978315-8A60-40C7-8889-A1C2EF190012}" + } + } + } + }, + { + "Key": { + "id": 20018413071808 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "SetVariableNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 560.0, + 420.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".setVariable" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{C1E23F0C-0829-4CBC-9176-29CF13B83640}" + } + } + } + }, + { + "Key": { + "id": 20022708039104 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 920.0, + 540.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{16FFB986-E605-47C3-8BAC-C5449F871465}" + } + } + } + }, + { + "Key": { + "id": 20027003006400 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MethodNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2600.0, + 280.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{F5ACF848-9D4E-45DA-AA58-42DE9695AAB0}" + } + } + } + }, + { + "Key": { + "id": 20031297973696 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MethodNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2900.0, + 280.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{456E2F70-A915-450F-B570-57C61FD8713C}" + } + } + } + }, + { + "Key": { + "id": 20035592940992 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MethodNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 1980.0, + 280.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{ED35AAD0-F976-4FA9-B6C7-08ED30452D33}" + } + } + } + }, + { + "Key": { + "id": 20039887908288 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MethodNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2280.0, + 280.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{D2CF914D-DFC1-40B6-884F-85EBD5DF95B6}" + } + } + } + }, + { + "Key": { + "id": 20044182875584 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "TimeNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 0.0, + 260.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{8133F38D-4D68-4244-8999-BE5E019A24E0}" + } + } + } + }, + { + "Key": { + "id": 20048477842880 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "SetVariableNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 560.0, + 260.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".setVariable" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{377A822F-6220-45A6-91CB-A3E5F8E9ACFB}" + } + } + } + }, + { + "Key": { + "id": 20052772810176 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "SetVariableNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 560.0, + 60.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".setVariable" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{EF322638-4ADC-43B3-9398-105F49D40D96}" + } + } + } + }, + { + "Key": { + "id": 20057067777472 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "StringNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 200.0, + 260.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{6A6126C8-7C7E-48C1-A8A5-D26B9EA59578}" + } + } + } + }, + { + "Key": { + "id": 20061362744768 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 880.0, + 60.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{01CF2659-C0C0-4D6B-9C48-B47989D98148}" + } + } + } + }, + { + "Key": { + "id": 20065657712064 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 1500.0, + 300.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{F8D64912-B290-443F-90A1-891A893D9F3D}" + } + } + } + }, + { + "Key": { + "id": 20069952679360 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 1540.0, + 560.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{A380E8D9-CEA5-44CF-A9DC-69A3C0115256}" + } + } + } + } + ], + "StatisticsHelper": { + "InstanceCounter": [ + { + "Key": 1244476766431948410, + "Value": 3 + }, + { + "Key": 2150378447039925262, + "Value": 1 + }, + { + "Key": 3117476785392655547, + "Value": 3 + }, + { + "Key": 4199610336680704683, + "Value": 1 + }, + { + "Key": 8132426562032687196, + "Value": 1 + }, + { + "Key": 10204019744198319120, + "Value": 1 + }, + { + "Key": 10684225535275896474, + "Value": 1 + }, + { + "Key": 12033332465728181077, + "Value": 3 + }, + { + "Key": 12096037962474910421, + "Value": 1 + } + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp index 00705bfbaa..9d75d3ed9d 100644 --- a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp +++ b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp @@ -779,7 +779,7 @@ TEST_F(ScriptCanvasTestFixture, InterpretedPrintConnectedInput) TEST_F(ScriptCanvasTestFixture, InterpretedPrintFormatEmptyValue) { - RunUnitTestGraph("LY_SC_UnitTest_PrintFormatEmptyValue", ExecutionMode::Interpreted); + ExpectParseError("LY_SC_UnitTest_PrintFormatEmptyValue"); } TEST_F(ScriptCanvasTestFixture, InterpretedProperties) @@ -819,7 +819,7 @@ TEST_F(ScriptCanvasTestFixture, InterpretedStringFormat) TEST_F(ScriptCanvasTestFixture, InterpretedStringFormatEmptyValue) { - RunUnitTestGraph("LY_SC_UnitTest_StringFormatEmptyValue", ExecutionMode::Interpreted); + ExpectParseError("LY_SC_UnitTest_StringFormatEmptyValue"); } TEST_F(ScriptCanvasTestFixture, InterpretedStringFormatWithRepeatedValueName) 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/DefaultTerrainMacroMaterial.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material new file mode 100644 index 0000000000..afaf7e1947 --- /dev/null +++ b/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material @@ -0,0 +1,8 @@ +{ + "description": "", + "materialType": "TerrainMacroMaterial.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 1, + "properties": { + } +} 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 new file mode 100644 index 0000000000..3cdab8da10 --- /dev/null +++ b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype @@ -0,0 +1,63 @@ +{ + "description": "A material for providing terrain with low-fidelity color and normals. This material will get blended with surface detail materials.", + "propertyLayout": { + "version": 1, + "groups": [ + { + "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": { + "baseColor": [ + { + "name": "textureMap", + "displayName": "Texture", + "description": "Base color of the macro material", + "type": "Image" + } + ], + "normal": [ + { + "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": 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": [ + ], + "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) + + // ------- Macro Color / Normal ------- + float3 macroColor = TerrainMaterialSrg::m_baseColor.rgb; + [unroll] for (uint i = 0; i < 4; ++i) { - macroNormal = GetNormalInputTS(TerrainMaterialSrg::m_macroNormalMap, TerrainMaterialSrg::m_sampler, - origUv, TerrainMaterialSrg::m_flipMacroNormalX, TerrainMaterialSrg::m_flipMacroNormalY, CreateIdentity3x3(), true, 1.0); + 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/Components/TerrainSurfaceDataSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp index 04c94b24d6..0346ff7281 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp @@ -156,9 +156,19 @@ namespace Terrain point.m_entityId = GetEntityId(); point.m_position = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), terrainHeight); point.m_normal = terrain->GetNormal(inPosition); + + // Always add a "terrain" or "terrainHole" tag. const AZ::Crc32 terrainTag = isHole ? SurfaceData::Constants::s_terrainHoleTagCrc : SurfaceData::Constants::s_terrainTagCrc; SurfaceData::AddMaxValueForMasks(point.m_masks, terrainTag, 1.0f); + + // Add all of the surface tags that the terrain has at this point. + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet surfaceWeights; + terrain->GetSurfaceWeights(point.m_position, surfaceWeights); + for (auto& tag : surfaceWeights) + { + SurfaceData::AddMaxValueForMasks(point.m_masks, tag.m_surfaceType, tag.m_weight); + } surfacePointList.push_back(point); } // Only one handler should exist. diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp index 2fbd6a4814..958e175cf3 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp @@ -12,6 +12,7 @@ #include #include +#include namespace Terrain { @@ -33,7 +34,8 @@ namespace Terrain ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( - AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_gradientEntityId, "Gradient Entity", "ID of Entity providing a gradient.") + AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_gradientEntityId, + "Gradient Entity", "ID of Entity providing a gradient.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->UIElement("GradientPreviewer", "Previewer") ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") @@ -68,7 +70,8 @@ namespace Terrain ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( - AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings, "Gradient to Surface Mappings", "Maps Gradient Entities to Surfaces.") + AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings, + "Gradient to Surface Mappings", "Maps Gradient Entities to Surfaces.") ; } } @@ -109,12 +112,36 @@ namespace Terrain void TerrainSurfaceGradientListComponent::Activate() { + LmbrCentral::DependencyNotificationBus::Handler::BusConnect(GetEntityId()); Terrain::TerrainAreaSurfaceRequestBus::Handler::BusConnect(GetEntityId()); + + // Make sure we get update notifications whenever this entity or any dependent gradient entity changes in any way. + // We'll use that to notify the terrain system that the surface information needs to be refreshed. + m_dependencyMonitor.Reset(); + m_dependencyMonitor.ConnectOwner(GetEntityId()); + m_dependencyMonitor.ConnectDependency(GetEntityId()); + + for (auto& surfaceMapping : m_configuration.m_gradientSurfaceMappings) + { + if (surfaceMapping.m_gradientEntityId != GetEntityId()) + { + m_dependencyMonitor.ConnectDependency(surfaceMapping.m_gradientEntityId); + } + } + + // Notify that the area has changed. + OnCompositionChanged(); } void TerrainSurfaceGradientListComponent::Deactivate() { + m_dependencyMonitor.Reset(); + Terrain::TerrainAreaSurfaceRequestBus::Handler::BusDisconnect(); + LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect(); + + // Since this surface data will no longer exist, notify the terrain system to refresh the area. + OnCompositionChanged(); } bool TerrainSurfaceGradientListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) @@ -137,7 +164,9 @@ namespace Terrain return false; } - void TerrainSurfaceGradientListComponent::GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const + void TerrainSurfaceGradientListComponent::GetSurfaceWeights( + const AZ::Vector3& inPosition, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const { outSurfaceWeights.clear(); @@ -146,7 +175,8 @@ namespace Terrain for (const auto& mapping : m_configuration.m_gradientSurfaceMappings) { float weight = 0.0f; - GradientSignal::GradientRequestBus::EventResult(weight, mapping.m_gradientEntityId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + GradientSignal::GradientRequestBus::EventResult(weight, + mapping.m_gradientEntityId, &GradientSignal::GradientRequestBus::Events::GetValue, params); AzFramework::SurfaceData::SurfaceTagWeight tagWeight; tagWeight.m_surfaceType = mapping.m_surfaceTag; @@ -154,4 +184,10 @@ namespace Terrain outSurfaceWeights.emplace(tagWeight); } } + + void TerrainSurfaceGradientListComponent::OnCompositionChanged() + { + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + } + } // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h index 799038354c..bb30029f35 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -47,6 +49,7 @@ namespace Terrain class TerrainSurfaceGradientListComponent : public AZ::Component , public Terrain::TerrainAreaSurfaceRequestBus::Handler + , private LmbrCentral::DependencyNotificationBus::Handler { public: template @@ -72,6 +75,11 @@ namespace Terrain void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const override; private: + ////////////////////////////////////////////////////////////////////////// + // LmbrCentral::DependencyNotificationBus + void OnCompositionChanged() override; + TerrainSurfaceGradientListConfig m_configuration; + LmbrCentral::DependencyMonitor m_dependencyMonitor; }; } // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp index f3eed41717..9cd9ce9fb2 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp @@ -13,8 +13,6 @@ #include #include -#include - #include #include #include @@ -135,17 +133,13 @@ namespace Terrain { m_terrainFeatureProcessor = scene->EnableFeatureProcessor(); } - - AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); m_terrainRendererActive = true; } void TerrainWorldRendererComponent::Deactivate() { // On component deactivation, unregister the feature processor and remove it from the default scene. - m_terrainRendererActive = false; - AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); if (AZ::RPI::Scene* scene = GetScene(); scene) { @@ -178,69 +172,4 @@ namespace Terrain } return false; } - - void TerrainWorldRendererComponent::OnTerrainDataDestroyBegin() - { - // If the terrain is being destroyed, remove all existing terrain data from the feature processor. - - if (m_terrainFeatureProcessor) - { - m_terrainFeatureProcessor->RemoveTerrainData(); - } - } - - void TerrainWorldRendererComponent::OnTerrainDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion, [[maybe_unused]] TerrainDataChangedMask dataChangedMask) - { - // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). - // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions - // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. - // (One case where this was previously able to occur was in rapid updating of the Preview widget on the - // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) - auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); - typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); - - AZ::Vector2 queryResolution = AZ::Vector2(1.0f); - AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); - - AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); - AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); - - - AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); - - uint32_t width = aznumeric_cast( - (float)worldBounds.GetXExtent() / queryResolution.GetX()); - uint32_t height = aznumeric_cast( - (float)worldBounds.GetYExtent() / queryResolution.GetY()); - AZStd::vector pixels; - pixels.resize_no_construct(width * height); - const uint32_t pixelDataSize = width * height * sizeof(float); - memset(pixels.data(), 0, pixelDataSize); - - for (uint32_t y = 0; y < height; y++) - { - for (uint32_t x = 0; x < width; x++) - { - bool terrainExists = true; - float terrainHeight = 0.0f; - AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, - (x * queryResolution.GetX()) + worldBounds.GetMin().GetX(), - (y * queryResolution.GetY()) + worldBounds.GetMin().GetY(), - AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, - &terrainExists); - - pixels[(y * width) + x] = - (terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(); - } - } - - if (m_terrainFeatureProcessor) - { - m_terrainFeatureProcessor->UpdateTerrainData(transform, worldBounds, queryResolution.GetX(), width, height, pixels); - } - } - } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h index cd2a4ea5aa..354b2fde34 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h @@ -9,8 +9,6 @@ #pragma once #include -#include -#include namespace LmbrCentral { @@ -54,7 +52,6 @@ namespace Terrain class TerrainWorldRendererComponent : public AZ::Component - , public AzFramework::Terrain::TerrainDataNotificationBus::Handler { public: template @@ -77,8 +74,6 @@ namespace Terrain bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; protected: - void OnTerrainDataDestroyBegin() override; - void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; AZ::RPI::Scene* GetScene() const; diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 9107ea8541..94c6f5c3c6 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Terrain { @@ -24,6 +25,7 @@ namespace Terrain { Terrain::EditorTerrainHeightGradientListComponent::CreateDescriptor(), Terrain::EditorTerrainLayerSpawnerComponent::CreateDescriptor(), + Terrain::EditorTerrainMacroMaterialComponent::CreateDescriptor(), Terrain::EditorTerrainSurfaceGradientListComponent::CreateDescriptor(), Terrain::EditorTerrainSystemComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index 6aeddc3cd5..878c6b44c8 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace Terrain { @@ -31,6 +32,7 @@ namespace Terrain TerrainWorldRendererComponent::CreateDescriptor(), TerrainHeightGradientListComponent::CreateDescriptor(), TerrainLayerSpawnerComponent::CreateDescriptor(), + TerrainMacroMaterialComponent::CreateDescriptor(), TerrainSurfaceGradientListComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), }); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp new file mode 100644 index 0000000000..55653c64fa --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp @@ -0,0 +1,287 @@ +/* + * 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 Terrain +{ + AZ::Data::AssetId TerrainMacroMaterialConfig::s_macroMaterialTypeAssetId{}; + + void TerrainMacroMaterialConfig::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("MacroMaterial", &TerrainMacroMaterialConfig::m_materialAsset) + ; + + // The edit context for this appears in EditorTerrainMacroMaterialComponent.cpp. + } + } + + AZ::Data::AssetId TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId() + { + // Get the Asset ID for the TerrainMacroMaterial material type and store it in a class static so that we don't have to look it + // up again. + if (!s_macroMaterialTypeAssetId.IsValid()) + { + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + s_macroMaterialTypeAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, TerrainMacroMaterialTypeAsset, + azrtti_typeid(), false); + AZ_Assert(s_macroMaterialTypeAssetId.IsValid(), "The asset '%s' couldn't be found.", TerrainMacroMaterialTypeAsset); + } + + return s_macroMaterialTypeAssetId; + } + + bool TerrainMacroMaterialConfig::IsMaterialTypeCorrect(const AZ::Data::AssetId& assetId) + { + // We'll verify that whatever material we try to load has this material type as a dependency, as a way to implicitly detect + // that we're only trying to use terrain macro materials even before we load the asset. + auto macroMaterialTypeAssetId = GetTerrainMacroMaterialTypeAssetId(); + + // Get the dependencies for the requested asset. + AZ::Outcome, AZStd::string> result; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + result, &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, assetId); + + // If any of the dependencies match the TerrainMacroMaterial materialtype asset, then this should be the correct type of material. + if (result) + { + for (auto& dependency : result.GetValue()) + { + if (dependency.m_assetId == macroMaterialTypeAssetId) + { + return true; + } + } + } + + // Didn't have the expected dependency, so it must not be the right material type. + return false; + } + + AZ::Outcome TerrainMacroMaterialConfig::ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType) + { + if (azrtti_typeid>() != valueType) + { + AZ_Assert(false, "Unexpected value type"); + return AZ::Failure(AZStd::string("Unexpectedly received something other than a material asset for the MacroMaterial!")); + } + + auto newMaterialAsset = *static_cast*>(newValue); + + if (!IsMaterialTypeCorrect(newMaterialAsset.GetId())) + { + return AZ::Failure(AZStd::string::format( + "The selected MacroMaterial ('%s') needs to use the TerrainMacroMaterial material type.", + newMaterialAsset.GetHint().c_str())); + } + + return AZ::Success(); + } + + + void TerrainMacroMaterialComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainMacroMaterialProviderService")); + } + + void TerrainMacroMaterialComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainMacroMaterialProviderService")); + } + + void TerrainMacroMaterialComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + + void TerrainMacroMaterialComponent::Reflect(AZ::ReflectContext* context) + { + TerrainMacroMaterialConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainMacroMaterialComponent::m_configuration) + ; + } + } + + TerrainMacroMaterialComponent::TerrainMacroMaterialComponent(const TerrainMacroMaterialConfig& configuration) + : m_configuration(configuration) + { + } + + void TerrainMacroMaterialComponent::Activate() + { + // Clear out our shape bounds and make sure the material is queued to load. + m_cachedShapeBounds = AZ::Aabb::CreateNull(); + m_configuration.m_materialAsset.QueueLoad(); + + // Don't mark our material as active until it's finished loading and is valid. + m_macroMaterialActive = false; + + // Listen for the material asset to complete loading. + AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_materialAsset.GetId()); + } + + void TerrainMacroMaterialComponent::Deactivate() + { + TerrainMacroMaterialRequestBus::Handler::BusDisconnect(); + + AZ::Data::AssetBus::Handler::BusDisconnect(); + m_configuration.m_materialAsset.Release(); + + m_macroMaterialInstance.reset(); + + // Send out any notifications as appropriate based on the macro material destruction. + HandleMaterialStateChange(); + } + + bool TerrainMacroMaterialComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainMacroMaterialComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainMacroMaterialComponent::OnShapeChanged([[maybe_unused]] ShapeComponentNotifications::ShapeChangeReasons reasons) + { + // This should only get called while the macro material is active. If it gets called while the macro material isn't active, + // we've got a bug where we haven't managed the bus connections properly. + AZ_Assert(m_macroMaterialActive, "The ShapeComponentNotificationBus connection is out of sync with the material load."); + + AZ::Aabb oldShapeBounds = m_cachedShapeBounds; + + LmbrCentral::ShapeComponentRequestsBus::EventResult( + m_cachedShapeBounds, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + TerrainMacroMaterialNotificationBus::Broadcast( + &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialRegionChanged, + GetEntityId(), oldShapeBounds, m_cachedShapeBounds); + } + + void TerrainMacroMaterialComponent::HandleMaterialStateChange() + { + // We only want our component to appear active during the time that the macro material is loaded and valid. The logic below + // will handle all transition possibilities to notify if we've become active, inactive, or just changed. We'll also only + // keep a valid up-to-date copy of the shape bounds while the material is valid, since we don't need it any other time. + + bool wasPreviouslyActive = m_macroMaterialActive; + bool isNowActive = (m_macroMaterialInstance != nullptr); + + // Set our state to active or inactive, based on whether or not the macro material instance is now valid. + m_macroMaterialActive = isNowActive; + + // Handle the different inactive/active transition possibilities. + + if (!wasPreviouslyActive && !isNowActive) + { + // Do nothing, we haven't yet successfully loaded a valid material. + } + else if (!wasPreviouslyActive && isNowActive) + { + // We've transitioned from inactive to active, so send out a message saying that we've been created and start tracking the + // overall shape bounds. + + // Get the current shape bounds. + LmbrCentral::ShapeComponentRequestsBus::EventResult( + m_cachedShapeBounds, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + // Start listening for terrain macro material requests. + TerrainMacroMaterialRequestBus::Handler::BusConnect(GetEntityId()); + + // Start listening for shape changes. + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); + + TerrainMacroMaterialNotificationBus::Broadcast( + &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), m_macroMaterialInstance, + m_cachedShapeBounds); + } + else if (wasPreviouslyActive && !isNowActive) + { + // Stop listening to macro material requests or shape changes, and send out a notification that we no longer have a valid + // macro material. + + TerrainMacroMaterialRequestBus::Handler::BusDisconnect(); + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); + + m_cachedShapeBounds = AZ::Aabb::CreateNull(); + + TerrainMacroMaterialNotificationBus::Broadcast( + &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialDestroyed, GetEntityId()); + } + else + { + // We were active both before and after, so just send out a material changed event. + + TerrainMacroMaterialNotificationBus::Broadcast( + &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), m_macroMaterialInstance); + } + } + + void TerrainMacroMaterialComponent::OnAssetReady(AZ::Data::Asset asset) + { + m_configuration.m_materialAsset = asset; + + if (m_configuration.m_materialAsset.Get()->GetMaterialTypeAsset().GetId() == + TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId()) + { + m_macroMaterialInstance = AZ::RPI::Material::FindOrCreate(m_configuration.m_materialAsset); + } + else + { + AZ_Error("Terrain", false, "Material '%s' has the wrong material type.", m_configuration.m_materialAsset.GetHint().c_str()); + m_macroMaterialInstance.reset(); + } + + // Clear the material asset reference to make sure we don't prevent hot-reloading. + m_configuration.m_materialAsset.Release(); + + HandleMaterialStateChange(); + } + + void TerrainMacroMaterialComponent::OnAssetReloaded(AZ::Data::Asset asset) + { + OnAssetReady(asset); + } + + void TerrainMacroMaterialComponent::GetTerrainMacroMaterialData( + AZ::Data::Instance& macroMaterial, AZ::Aabb& macroMaterialRegion) + { + macroMaterial = m_macroMaterialInstance; + macroMaterialRegion = m_cachedShapeBounds; + } +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h new file mode 100644 index 0000000000..b60f6b2a94 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h @@ -0,0 +1,92 @@ +/* + * 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 LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + class TerrainMacroMaterialConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainMacroMaterialConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainMacroMaterialConfig, "{9DBAFFF0-FD20-4594-8884-E3266D8CCAC8}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + AZ::Data::Asset m_materialAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; + + static AZ::Data::AssetId GetTerrainMacroMaterialTypeAssetId(); + static bool IsMaterialTypeCorrect(const AZ::Data::AssetId&); + AZ::Outcome ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType); + + private: + static inline constexpr const char* TerrainMacroMaterialTypeAsset = "materials/terrain/terrainmacromaterial.azmaterialtype"; + static AZ::Data::AssetId s_macroMaterialTypeAssetId; + + }; + + class TerrainMacroMaterialComponent + : public AZ::Component + , public TerrainMacroMaterialRequestBus::Handler + , private LmbrCentral::ShapeComponentNotificationsBus::Handler + , private AZ::Data::AssetBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainMacroMaterialComponent, "{F82379FB-E2AE-4F75-A6F4-1AE5F5DA42E8}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainMacroMaterialComponent(const TerrainMacroMaterialConfig& configuration); + TerrainMacroMaterialComponent() = default; + ~TerrainMacroMaterialComponent() = default; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + void GetTerrainMacroMaterialData(AZ::Data::Instance& macroMaterial, AZ::Aabb& macroMaterialRegion) override; + + private: + //////////////////////////////////////////////////////////////////////// + // ShapeComponentNotificationsBus + void OnShapeChanged(ShapeComponentNotifications::ShapeChangeReasons reasons) override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Data::AssetBus::Handler + void OnAssetReady(AZ::Data::Asset asset) override; + void OnAssetReloaded(AZ::Data::Asset asset) override; + + void HandleMaterialStateChange(); + + TerrainMacroMaterialConfig m_configuration; + AZ::Aabb m_cachedShapeBounds; + AZ::Data::Instance m_macroMaterialInstance; + bool m_macroMaterialActive{ false }; + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp new file mode 100644 index 0000000000..07472d1b85 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.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 + +namespace Terrain +{ + void EditorTerrainMacroMaterialComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::ReflectSubClass( + context, 1, + &LmbrCentral::EditorWrappedComponentBaseVersionConverter + ); + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + + if (serializeContext) + { + AZ::EditContext* editContext = serializeContext->GetEditContext(); + + // The edit context for TerrainMacroMaterialConfig is specified here to make it easier to add custom filtering to the + // asset picker for the material asset so that we can eventually only display materials that inherit from the proper + // material type. + if (editContext) + { + editContext + ->Class( + "Terrain Macro Material Component", "Provide a terrain macro material for a region of the world") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_materialAsset, "Macro Material", + "Terrain macro material for use by any terrain inside the bounding box on this entity.") + // This is disabled until ChangeValidate can support the Asset type. :( + //->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainMacroMaterialConfig::ValidateMaterialAsset) + ; + } + } + + } +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.h new file mode 100644 index 0000000000..a2fddf5768 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.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 +#include + +namespace Terrain +{ + class EditorTerrainMacroMaterialComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainMacroMaterialComponent, "{24D87D5F-6845-4F1F-81DC-05B4CEBA3EF4}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + static constexpr const char* const s_categoryName = "Terrain"; + static constexpr const char* const s_componentName = "Terrain Macro Material"; + static constexpr const char* const s_componentDescription = "Provides a macro material for a region to the terrain renderer"; + static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainLayerRenderer.svg"; + static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainLayerRenderer.svg"; + static constexpr const char* const s_helpUrl = ""; + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 59984a1b92..8c85e21490 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -10,51 +10,64 @@ #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 -#include -#include -#include + #include +#include + namespace Terrain { namespace { [[maybe_unused]] const char* TerrainFPName = "TerrainFeatureProcessor"; + const char* TerrainHeightmapChars = "TerrainHeightmap"; } 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"); } @@ -70,8 +83,8 @@ namespace Terrain void TerrainFeatureProcessor::Activate() { - m_areaData = {}; Initialize(); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); } void TerrainFeatureProcessor::Initialize() @@ -91,6 +104,10 @@ namespace Terrain { AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material."); } + else + { + PrepareMaterialData(); + } } } ); @@ -99,13 +116,22 @@ namespace Terrain AZ_Error(TerrainFPName, false, "Failed to create Terrain render buffers!"); return; } + OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::HeightData); } void TerrainFeatureProcessor::Deactivate() { + TerrainMacroMaterialNotificationBus::Handler::BusDisconnect(); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect(); + m_patchModel = {}; m_areaData = {}; - AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect(); + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_sectorData.clear(); + m_macroMaterials.Clear(); + m_materialAssetLoader = {}; + m_materialInstance = {}; } void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) @@ -113,163 +139,465 @@ namespace Terrain ProcessSurfaces(packet); } - void TerrainFeatureProcessor::UpdateTerrainData( - const AZ::Transform& transform, - const AZ::Aabb& worldBounds, - float sampleSpacing, - uint32_t width, uint32_t height, const AZStd::vector& heightData) + void TerrainFeatureProcessor::OnTerrainDataDestroyBegin() { - if (!worldBounds.IsValid()) + m_areaData = {}; + } + + void TerrainFeatureProcessor::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) + { + if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) == 0) { return; } + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + const AZ::Aabb& regionToUpdate = dirtyRegion.IsValid() ? dirtyRegion : worldBounds; + + m_dirtyRegion.AddAabb(regionToUpdate); + m_dirtyRegion.Clamp(worldBounds); + + 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_heightmapImageHeight = height; - m_areaData.m_heightmapImageWidth = width; - m_areaData.m_sampleSpacing = sampleSpacing; + m_areaData.m_heightmapImageWidth = aznumeric_cast(worldBounds.GetXExtent() / queryResolution.GetX()); + m_areaData.m_heightmapImageHeight = aznumeric_cast(worldBounds.GetYExtent() / queryResolution.GetY()); + m_areaData.m_updateWidth = aznumeric_cast(m_dirtyRegion.GetXExtent() / queryResolution.GetX()); + 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_heightmapUpdated = true; + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) + { + MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + materialData.m_bounds = region; - // Create heightmap image data - { - m_areaData.m_propertiesDirty = true; + UpdateMacroMaterialData(materialData, material); - AZ::RHI::Size imageSize; - imageSize.m_width = width; - imageSize.m_height = height; + // 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)); + } + } + ); + } - AZStd::vector uint16Heights; - uint16Heights.reserve(heightData.size()); - for (float sampleHeight : heightData) + 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) { - float clampedSample = AZ::GetClamp(sampleHeight, 0.0f, 1.0f); - constexpr uint16_t MaxUint16 = 0xFFFF; - uint16Heights.push_back(aznumeric_cast(clampedSample * MaxUint16)); + // 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; + } - AZ::Data::Instance streamingImagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); - m_areaData.m_heightmapImage = AZ::RPI::StreamingImage::CreateFromCpuData(*streamingImagePool, - AZ::RHI::ImageDimension::Image2D, - imageSize, - AZ::RHI::Format::R16_UNORM, - (uint8_t*)uint16Heights.data(), - heightData.size() * sizeof(uint16_t)); + 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() + { + static const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars); + + uint32_t width = m_areaData.m_updateWidth; + uint32_t height = m_areaData.m_updateHeight; + const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; + const float queryResolution = m_areaData.m_sampleSpacing; + + 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) + { + // World size changed, so the whole world needs updating. + width = worldSize.m_width; + height = worldSize.m_height; + m_dirtyRegion = worldBounds; + + 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 + ); + m_areaData.m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr); AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image!"); } + AZStd::vector pixels; + pixels.reserve(width * height); + + { + // Block other threads from accessing the surface data bus while we are in GetHeightFromFloats (which may call into the SurfaceData bus). + // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions + // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. + // (One case where this was previously able to occur was in rapid updating of the Preview widget on the + // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) + + auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); + typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); + + for (uint32_t y = 0; y < height; y++) + { + for (uint32_t x = 0; x < width; x++) + { + bool terrainExists = true; + float terrainHeight = 0.0f; + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, + (x * queryResolution) + m_dirtyRegion.GetMin().GetX(), + (y * queryResolution) + m_dirtyRegion.GetMin().GetY(), + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, + &terrainExists); + + 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); + } + } + } + + if (m_areaData.m_heightmapImage) + { + const float left = (m_dirtyRegion.GetMin().GetX() - worldBounds.GetMin().GetX()) / queryResolution; + const float top = (m_dirtyRegion.GetMin().GetY() - worldBounds.GetMin().GetY()) / queryResolution; + AZ::RHI::ImageUpdateRequest imageUpdateRequest; + imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast(left); + imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast(top); + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(uint16_t); + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(uint16_t); + imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1; + imageUpdateRequest.m_sourceData = pixels.data(); + imageUpdateRequest.m_image = m_areaData.m_heightmapImage->GetRHIImage(); + + m_areaData.m_heightmapImage->UpdateImageContents(imageUpdateRequest); + } + + 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) + + if (m_materialInstance && m_materialInstance->CanCompile()) { - m_areaData.m_propertiesDirty = false; - m_sectorData.clear(); + if (m_areaData.m_rebuildSectors) + { + // Something about the whole world changed, so the sectors need to be rebuilt - 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(); + m_areaData.m_rebuildSectors = false; - const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); + 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(); - m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); - AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); + for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) + { + for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) + { + auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); + if (!objectSrg) + { + AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); + continue; + } + + m_sectorData.push_back(); + SectorData& sectorData = m_sectorData.back(); - m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); - AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); + 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); + } - 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); + 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; + } + } - for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) - { - for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) + if (m_areaData.m_macroMaterialsUpdated) { - const auto& materialAsset = m_materialInstance->GetAsset(); - auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); - auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); - if (!objectSrg) + // sectors were rebuilt, so any cached macro material data needs to be regenerated + for (SectorData& sectorData : m_sectorData) { - AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); - continue; + 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; + } + } + } } + } + } - { // 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()); + if (m_areaData.m_heightmapUpdated) + { + UpdateTerrainData(); - AZStd::array uvStep = - { - 1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight, - }; + const AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; + m_materialInstance->SetPropertyValue(m_heightmapPropertyIndex, heightmapImage); + m_materialInstance->Compile(); + } - AZ::Transform transform = m_areaData.m_transform; - transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); + 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. - AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); + m_areaData.m_heightmapUpdated = false; + m_areaData.m_macroMaterialsUpdated = false; - objectSrg->SetConstant(m_modelToWorldIndex, matrix3x4); + 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, + }; - 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); + AZ::Transform transform = m_areaData.m_transform; + transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); - objectSrg->Compile(); - } + terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; + terrainDataForSrg.m_heightScale = terrainBounds.GetZExtent(); - m_sectorData.push_back(); - SectorData& sectorData = m_sectorData.back(); + sectorData.m_srg->SetConstant(m_terrainDataIndex, terrainDataForSrg); - for (auto& lod : m_patchModel->GetLods()) + AZStd::array macroMaterialData; + for (uint32_t i = 0; i < sectorData.m_macroMaterials.size(); ++i) { - AZ::RPI::ModelLod& modelLod = *lod.get(); - sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); - AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); + const MacroMaterialData& materialData = m_macroMaterials.GetData(sectorData.m_macroMaterials.at(i)); + ShaderMacroMaterialData& shaderData = macroMaterialData.at(i); + const AZ::Aabb& materialBounds = materialData.m_bounds; + + // Use reverse coordinates (1 - y) for the y direction so that the lower left corner of the macro material images + // map to the lower left corner in world space. This will match up with the height uv coordinate mapping. + shaderData.m_uvMin = { + (xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), + 1.0f - ((yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) + }; + shaderData.m_uvMax = { + ((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), + 1.0f - (((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; - // 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"); - } - uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; - drawPacket.SetStencilRef(stencilRef); - drawPacket.Update(*GetParentScene(), true); + 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(); } } } @@ -283,12 +611,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)); } } @@ -299,7 +635,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()); } } @@ -312,9 +648,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); @@ -334,10 +669,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); @@ -386,14 +721,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()) { @@ -431,7 +766,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) { @@ -447,4 +782,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 32f88565c2..d9f15f2d47 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -9,25 +9,15 @@ #pragma once #include -#include -#include -#include -#include -#include -#include +#include +#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include namespace AZ::RPI { @@ -35,7 +25,9 @@ namespace AZ::RPI { class AsyncAssetLoader; } + class Material; class Model; + class StreamingImage; } namespace Terrain @@ -43,6 +35,8 @@ namespace Terrain class TerrainFeatureProcessor final : 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); @@ -54,27 +48,23 @@ namespace Terrain TerrainFeatureProcessor() = default; ~TerrainFeatureProcessor() = default; - // AZ::Component overrides... + // AZ::RPI::FeatureProcessor overrides... void Activate() override; void Deactivate() override; - - // AZ::RPI::FeatureProcessor overrides... void Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) override; - // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... - void OnMaterialReinitialized(const AZ::Data::Instance& material) override; - void SetWorldSize(AZ::Vector2 sizeInMeters); - void UpdateTerrainData(const AZ::Transform& transform, const AZ::Aabb& worldBounds, float sampleSpacing, - uint32_t width, uint32_t height, const AZStd::vector& heightData); + private: + + using MaterialInstance = AZ::Data::Instance; + static constexpr uint32_t MaxMaterialsPerSector = 4; - void RemoveTerrainData() + enum MacroMaterialFlags { - m_areaData = {}; - } - - private: + ColorImageUsed = 0b01, + NormalImageUsed = 0b10, + }; struct ShaderTerrainData // Must align with struct in Object Srg { @@ -84,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; @@ -104,12 +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 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); @@ -119,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; @@ -131,23 +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; + AZ::Data::Instance m_heightmapImage; uint32_t m_heightmapImageWidth{ 0 }; uint32_t m_heightmapImageHeight{ 0 }; - bool m_propertiesDirty{ true }; + uint32_t m_updateWidth{ 0 }; + uint32_t m_updateHeight{ 0 }; float m_sampleSpacing{ 0.0f }; + bool m_heightmapUpdated{ true }; + bool m_macroMaterialsUpdated{ true }; + bool m_rebuildSectors{ true }; }; TerrainAreaData m_areaData; - - 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; - }; + AZ::Aabb m_dirtyRegion{ AZ::Aabb::CreateNull() }; AZStd::vector m_sectorData; + + AZ::Render::IndexedDataVector m_macroMaterials; }; } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h new file mode 100644 index 0000000000..c0618a7f66 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h @@ -0,0 +1,78 @@ +/* + * 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 Terrain +{ + /** + * Request terrain macro material data. + */ + class TerrainMacroMaterialRequests + : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainMacroMaterialRequests() = default; + + // Get the terrain macro material and the region that it covers. + virtual void GetTerrainMacroMaterialData(AZ::Data::Instance& macroMaterial, AZ::Aabb& macroMaterialRegion) = 0; + }; + + using TerrainMacroMaterialRequestBus = AZ::EBus; + + /** + * Notifications for when the terrain macro material data changes. + */ + class TerrainMacroMaterialNotifications : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + virtual void OnTerrainMacroMaterialCreated( + [[maybe_unused]] AZ::EntityId macroMaterialEntity, + [[maybe_unused]] AZ::Data::Instance macroMaterial, + [[maybe_unused]] const AZ::Aabb& macroMaterialRegion) + { + } + + virtual void OnTerrainMacroMaterialChanged( + [[maybe_unused]] AZ::EntityId macroMaterialEntity, + [[maybe_unused]] AZ::Data::Instance macroMaterial) + { + } + + virtual void OnTerrainMacroMaterialRegionChanged( + [[maybe_unused]] AZ::EntityId macroMaterialEntity, + [[maybe_unused]] const AZ::Aabb& oldRegion, + [[maybe_unused]] const AZ::Aabb& newRegion) + { + } + + virtual void OnTerrainMacroMaterialDestroyed([[maybe_unused]] AZ::EntityId macroMaterialEntity) + { + } + }; + using TerrainMacroMaterialNotificationBus = AZ::EBus; + +} diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index efb68eca31..2db46dc264 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -25,4 +25,6 @@ set(FILES Source/EditorTerrainModule.h Source/TerrainModule.cpp Source/TerrainModule.h + Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp + Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.h ) diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 6d190b7d5e..5739ed6079 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -24,8 +24,11 @@ set(FILES Source/Components/TerrainWorldDebuggerComponent.h Source/Components/TerrainWorldRendererComponent.cpp Source/Components/TerrainWorldRendererComponent.h + Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp + Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h + Source/TerrainRenderer/TerrainMacroMaterialBus.h Source/TerrainSystem/TerrainSystem.cpp Source/TerrainSystem/TerrainSystem.h Source/TerrainSystem/TerrainSystemBus.h 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/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp b/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp index a7e6b6c707..18bddfa26f 100644 --- a/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp +++ b/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp @@ -91,6 +91,11 @@ namespace WhiteBox bodyConfiguration.m_position = worldTransform.GetTranslation(); bodyConfiguration.m_kinematic = true; // note: this field is ignored in the WhiteBoxBodyType::Static case bodyConfiguration.m_colliderAndShapeData = shape; + // Since the shape used is a triangle mesh the COM, Mass and Inertia + // cannot be computed. Disable them to use default values. + bodyConfiguration.m_computeCenterOfMass = false; + bodyConfiguration.m_computeMass = false; + bodyConfiguration.m_computeInertiaTensor = false; m_simulatedBodyHandle = sceneInterface->AddSimulatedBody(defaultScene, &bodyConfiguration); } break; diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp index 849e2272a2..ee0052d5b9 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,7 @@ namespace WhiteBox AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast( &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, - "@devroot@/Gems/WhiteBox/Editor/Scripts/default_shapes.py", scriptArgs); + "@engroot@/Gems/WhiteBox/Editor/Scripts/default_shapes.py", scriptArgs); EditorWhiteBoxComponentNotificationBus::Event( AZ::EntityComponentIdPair(GetEntityId(), GetId()), @@ -499,13 +500,13 @@ namespace WhiteBox static AZStd::string WhiteBoxPathAtProjectRoot(const AZStd::string_view name, const AZStd::string_view extension) { - const char* projectFolder = nullptr; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - projectFolder, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath); - - return AZStd::string::format( - "%s\\%.*s_whitebox.%.*s", projectFolder, aznumeric_cast(name.size()), name.data(), - aznumeric_cast(extension.size()), extension.data()); + AZ::IO::Path whiteBoxPath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(whiteBoxPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); + } + whiteBoxPath /= AZ::IO::FixedMaxPathString::format("%.*s.%.*s", AZ_STRING_ARG(name), AZ_STRING_ARG(extension)); + return whiteBoxPath.Native(); } void EditorWhiteBoxComponent::ExportToFile() diff --git a/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxManipulatorBounds.cpp b/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxManipulatorBounds.cpp index 878ede5e68..a11f2c9905 100644 --- a/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxManipulatorBounds.cpp +++ b/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxManipulatorBounds.cpp @@ -48,10 +48,10 @@ namespace WhiteBox float time; AZ::Vector3 normal; const float rayLength = 1000.0f; - const int intersected = AZ::Intersect::IntersectSegmentTriangleCCW( + const bool intersected = AZ::Intersect::IntersectSegmentTriangleCCW( rayOrigin, rayOrigin + rayDirection * rayLength, p0, p1, p2, normal, time); - if (intersected != 0) + if (intersected) { rayIntersectionDistance = time * rayLength; intersectedTriangleIndex = triangleIndex / 3; 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/fileio.setreg b/Registry/fileio.setreg new file mode 100644 index 0000000000..d89e9f3f89 --- /dev/null +++ b/Registry/fileio.setreg @@ -0,0 +1,26 @@ +{ + "O3DE": { + "AzCore": { + "FileIO": { + "DeprecatedAliases": [ + { + "OldAlias": "@assets@", + "NewAlias": "@products@" + }, + { + "OldAlias": "@root@", + "NewAlias": "@products@" + }, + { + "OldAlias": "@devassets@", + "NewAlias": "@projectroot@" + }, + { + "OldAlias": "@devroot@", + "NewAlias": "@engroot@" + } + ] + } + } + } +} 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/CustomTool/Template/CMakeLists.txt b/Templates/CustomTool/Template/CMakeLists.txt new file mode 100644 index 0000000000..b19ea2edce --- /dev/null +++ b/Templates/CustomTool/Template/CMakeLists.txt @@ -0,0 +1,22 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${o3de_gem_restricted_path}" ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# project cmake for this platform. +include(${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_gem.cmake) + +ly_add_external_target_path(${CMAKE_CURRENT_LIST_DIR}/3rdParty) + +add_subdirectory(Code) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..d73efffa2e --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorSystemComponent.cpp + Source/${Name}EditorSystemComponent.h + Source/${Name}Widget.cpp + Source/${Name}Widget.h + Source/${Name}.qrc +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorModule.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}EditorTest.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_files.cmake new file mode 100644 index 0000000000..b7d6d37bdf --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_files.cmake @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Include/${Name}/${Name}Bus.h + Source/${Name}ModuleInterface.h + Source/${Name}SystemComponent.cpp + Source/${Name}SystemComponent.h +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake new file mode 100644 index 0000000000..b85916191c --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}Module.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake new file mode 100644 index 0000000000..adcfe2645f --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}Test.cpp +) diff --git a/Templates/CustomTool/Template/Code/CMakeLists.txt b/Templates/CustomTool/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..f5cda477fc --- /dev/null +++ b/Templates/CustomTool/Template/Code/CMakeLists.txt @@ -0,0 +1,168 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/${Name}/Code/Platform/ or +# //Gems/${Name}/Code +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +# Add the ${Name}.Static target +# Note: We include the common files and the platform specific files which are set in ${NameLower}_common_files.cmake +# and in ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake +ly_add_target( + NAME ${Name}.Static STATIC + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_files.cmake + ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework +) + +# Here add ${Name} target, it depends on the ${Name}.Static +ly_add_target( + NAME ${Name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_shared_files.cmake + ${pal_dir}/${NameLower}_shared_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::${Name}.Static +) + +# By default, we will specify that the above target ${Name} would be used by +# Client and Server type targets when this gem is enabled. If you don't want it +# active in Clients or Servers by default, delete one of both of the following lines: +ly_create_alias(NAME ${Name}.Clients NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Servers NAMESPACE Gem TARGETS Gem::${Name}) + +# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which +# will also depend on ${Name}.Static +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME ${Name}.Editor.Static STATIC + NAMESPACE Gem + AUTOMOC + AUTORCC + FILES_CMAKE + ${NameLower}_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + Gem::${Name}.Static + ) + + ly_add_target( + NAME ${Name}.Editor GEM_MODULE + NAMESPACE Gem + AUTOMOC + FILES_CMAKE + ${NameLower}_editor_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + Gem::${Name}.Editor.Static + ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + +endif() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for ${Name}.Static + if(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED) + # We support ${Name}.Tests on this platform, add ${Name}.Tests target which depends on ${Name}.Static + ly_add_target( + NAME ${Name}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_files.cmake + ${NameLower}_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzFramework + Gem::${Name}.Static + ) + + # Add ${Name}.Tests to googletest + ly_add_googletest( + NAME Gem::${Name}.Tests + ) + endif() + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + # We are a host platform, see if Editor tests are supported on this platform + if(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED) + # We support ${Name}.Editor.Tests on this platform, add ${Name}.Editor.Tests target which depends on ${Name}.Editor + ly_add_target( + NAME ${Name}.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_editor_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + Gem::${Name}.Editor + ) + + # Add ${Name}.Editor.Tests to googletest + ly_add_googletest( + NAME Gem::${Name}.Editor.Tests + ) + endif() + endif() +endif() diff --git a/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h @@ -0,0 +1,40 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Requests + { + public: + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; + // Put your public methods here + }; + + class ${SanitizedCppName}BusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake new file mode 100644 index 0000000000..5b6da14a20 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Android +# i.e. ../Source/Android/${Name}Android.cpp +# ../Source/Android/${Name}Android.h +# ../Include/Android/${Name}Android.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake new file mode 100644 index 0000000000..5b6da14a20 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Android +# i.e. ../Source/Android/${Name}Android.cpp +# ../Source/Android/${Name}Android.h +# ../Include/Android/${Name}Android.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..90d1caccf4 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED FALSE) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake new file mode 100644 index 0000000000..61efde11c2 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for iOS +# i.e. ../Source/iOS/${Name}iOS.cpp +# ../Source/iOS/${Name}iOS.h +# ../Include/iOS/${Name}iOS.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake new file mode 100644 index 0000000000..61efde11c2 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for iOS +# i.e. ../Source/iOS/${Name}iOS.cpp +# ../Source/iOS/${Name}iOS.h +# ../Include/iOS/${Name}iOS.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..332f4469b6 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED FALSE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Source/${Name}.qrc b/Templates/CustomTool/Template/Code/Source/${Name}.qrc new file mode 100644 index 0000000000..90d7695b88 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}.qrc @@ -0,0 +1,5 @@ + + + toolbar_icon.svg + + diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp b/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..0027af011a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,55 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +void Init${SanitizedCppName}Resources() +{ + // We must register our Qt resources (.qrc file) since this is being loaded from a separate module (gem) + Q_INIT_RESOURCE(${SanitizedCppName}); +} + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + Init${SanitizedCppName}Resources(); + + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..f12fa04929 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,78 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +#include + +#include <${Name}Widget.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, ${SanitizedCppName}SystemComponent>() + ->Version(0); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() = default; + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() = default; + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + BaseSystemComponent::GetProvidedServices(provided); + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + BaseSystemComponent::GetIncompatibleServices(incompatible); + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + BaseSystemComponent::GetRequiredServices(required); + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + BaseSystemComponent::GetDependentServices(dependent); + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}SystemComponent::Activate(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}SystemComponent::Deactivate(); + } + + void ${SanitizedCppName}EditorSystemComponent::NotifyRegisterViews() + { + AzToolsFramework::ViewPaneOptions options; + options.paneRect = QRect(100, 100, 500, 400); + options.showOnToolsToolbar = true; + options.toolbarIcon = ":/${Name}/toolbar_icon.svg"; + + // Register our custom widget as a dockable tool with the Editor + AzToolsFramework::RegisterViewPane<${SanitizedCppName}Widget>("${Name}", "Tools", options); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..bbeac97da3 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,45 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include <${Name}SystemComponent.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}SystemComponent + , private AzToolsFramework::EditorEvents::Bus::Handler + { + using BaseSystemComponent = ${SanitizedCppName}SystemComponent; + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}", BaseSystemComponent); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate() override; + void Deactivate() override; + + // AzToolsFramework::EditorEventsBus overrides ... + void NotifyRegisterViews() override; + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp b/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp new file mode 100644 index 0000000000..0a6e8bde3c --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp @@ -0,0 +1,25 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}SystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Module + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}Module, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}Module, AZ::SystemAllocator, 0); + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}Module) diff --git a/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h b/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..925632491a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,45 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include +#include +#include <${Name}SystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}SystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid<${SanitizedCppName}SystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp new file mode 100644 index 0000000000..cb4d58418e --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp @@ -0,0 +1,92 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}SystemComponent.h> + +#include +#include +#include + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}SystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class<${SanitizedCppName}SystemComponent, AZ::Component>() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class<${SanitizedCppName}SystemComponent>("${SanitizedCppName}", "[Description of functionality provided by this System Component]") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void ${SanitizedCppName}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}Service")); + } + + void ${SanitizedCppName}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}Service")); + } + + void ${SanitizedCppName}SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}SystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + ${SanitizedCppName}SystemComponent::${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}SystemComponent::~${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}SystemComponent::Init() + { + } + + void ${SanitizedCppName}SystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AZ::TickBus::Handler::BusConnect(); + } + + void ${SanitizedCppName}SystemComponent::Deactivate() + { + AZ::TickBus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + + void ${SanitizedCppName}SystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h new file mode 100644 index 0000000000..5495d18e48 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h @@ -0,0 +1,56 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include +#include <${Name}/${Name}Bus.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}SystemComponent + : public AZ::Component + , protected ${SanitizedCppName}RequestBus::Handler + , public AZ::TickBus::Handler + { + public: + AZ_COMPONENT(${SanitizedCppName}SystemComponent, "${SysCompClassId}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + ${SanitizedCppName}SystemComponent(); + ~${SanitizedCppName}SystemComponent(); + + protected: + //////////////////////////////////////////////////////////////////////// + // ${SanitizedCppName}RequestBus interface implementation + + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + void Deactivate() override; + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZTickBus interface implementation + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + //////////////////////////////////////////////////////////////////////// + }; + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp b/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp new file mode 100644 index 0000000000..bd6dd6c86a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp @@ -0,0 +1,44 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +#include +#include + +#include <${Name}Widget.h> + +namespace ${SanitizedCppName} +{ + ${SanitizedCppName}Widget::${SanitizedCppName}Widget(QWidget* parent) + : QWidget(parent) + { + setWindowTitle(QObject::tr("${Name}")); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + + QLabel* introLabel = new QLabel(QObject::tr("Put your cool stuff here!"), this); + mainLayout->addWidget(introLabel, 0, Qt::AlignCenter); + + QString helpText = QString( + "For help getting started, visit the UI Development documentation
or come ask a question in the sig-ui-ux channel on Discord"); + + QLabel* helpLabel = new QLabel(this); + helpLabel->setTextFormat(Qt::RichText); + helpLabel->setText(helpText); + helpLabel->setOpenExternalLinks(true); + + mainLayout->addWidget(helpLabel, 0, Qt::AlignCenter); + + setLayout(mainLayout); + } +} + +#include diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.h b/Templates/CustomTool/Template/Code/Source/${Name}Widget.h new file mode 100644 index 0000000000..4d0c86d043 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Widget.h @@ -0,0 +1,28 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#if !defined(Q_MOC_RUN) +#include + +#include +#endif + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Widget + : public QWidget + { + Q_OBJECT + public: + explicit ${SanitizedCppName}Widget(QWidget* parent = nullptr); + }; +} diff --git a/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg b/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg new file mode 100644 index 0000000000..59de66961c --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp b/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.cmake b/Templates/CustomTool/Template/Platform/Android/android_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Android/android_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.json b/Templates/CustomTool/Template/Platform/Android/android_gem.json new file mode 100644 index 0000000000..23bbb28e66 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Android/android_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Android"], +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake b/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.json b/Templates/CustomTool/Template/Platform/Linux/linux_gem.json new file mode 100644 index 0000000000..d08fbf53ba --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Linux/linux_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Linux"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake b/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.json b/Templates/CustomTool/Template/Platform/Mac/mac_gem.json new file mode 100644 index 0000000000..d42b6f8186 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Mac/mac_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Mac"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake b/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.json b/Templates/CustomTool/Template/Platform/Windows/windows_gem.json new file mode 100644 index 0000000000..a052f1e05a --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Windows/windows_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Windows"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake b/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.json b/Templates/CustomTool/Template/Platform/iOS/ios_gem.json new file mode 100644 index 0000000000..b2dab56d05 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/iOS/ios_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["iOS"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/gem.json b/Templates/CustomTool/Template/gem.json new file mode 100644 index 0000000000..518d831e0f --- /dev/null +++ b/Templates/CustomTool/Template/gem.json @@ -0,0 +1,17 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "", + "restricted_name": "gems" +} diff --git a/Templates/CustomTool/Template/preview.png b/Templates/CustomTool/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/CustomTool/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/CustomTool/template.json b/Templates/CustomTool/template.json new file mode 100644 index 0000000000..e3221db106 --- /dev/null +++ b/Templates/CustomTool/template.json @@ -0,0 +1,382 @@ +{ + "template_name": "CustomTool", + "origin": "The primary repo for CustomTool goes here: i.e. http://www.mydomain.com", + "license": "What license CustomTool uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "CustomTool", + "summary": "A gem template for a custom tool in C++ that gets registered with the Editor.", + "canonical_tags": [], + "user_tags": [ + "CustomTool" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_files.cmake", + "origin": "Code/${NameLower}_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_shared_files.cmake", + "origin": "Code/${NameLower}_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_tests_files.cmake", + "origin": "Code/${NameLower}_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/${NameLower}_android_files.cmake", + "origin": "Code/Platform/Android/${NameLower}_android_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/${NameLower}_shared_android_files.cmake", + "origin": "Code/Platform/Android/${NameLower}_shared_android_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/PAL_android.cmake", + "origin": "Code/Platform/Android/PAL_android.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/${NameLower}_ios_files.cmake", + "origin": "Code/Platform/iOS/${NameLower}_ios_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/${NameLower}_shared_ios_files.cmake", + "origin": "Code/Platform/iOS/${NameLower}_shared_ios_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/PAL_ios.cmake", + "origin": "Code/Platform/iOS/PAL_ios.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}.qrc", + "origin": "Code/Source/${Name}.qrc", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Module.cpp", + "origin": "Code/Source/${Name}Module.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}SystemComponent.cpp", + "origin": "Code/Source/${Name}SystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}SystemComponent.h", + "origin": "Code/Source/${Name}SystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Widget.cpp", + "origin": "Code/Source/${Name}Widget.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Widget.h", + "origin": "Code/Source/${Name}Widget.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/toolbar_icon.svg", + "origin": "Code/Source/toolbar_icon.svg", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}Test.cpp", + "origin": "Code/Tests/${Name}Test.cpp", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Android/android_gem.cmake", + "origin": "Platform/Android/android_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Android/android_gem.json", + "origin": "Platform/Android/android_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Linux/linux_gem.cmake", + "origin": "Platform/Linux/linux_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Linux/linux_gem.json", + "origin": "Platform/Linux/linux_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Mac/mac_gem.cmake", + "origin": "Platform/Mac/mac_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Mac/mac_gem.json", + "origin": "Platform/Mac/mac_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Windows/windows_gem.cmake", + "origin": "Platform/Windows/windows_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Windows/windows_gem.json", + "origin": "Platform/Windows/windows_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/iOS/ios_gem.cmake", + "origin": "Platform/iOS/ios_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/iOS/ios_gem.json", + "origin": "Platform/iOS/ios_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Android", + "origin": "Code/Platform/Android" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Platform/iOS", + "origin": "Code/Platform/iOS" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + }, + { + "dir": "Platform", + "origin": "Platform" + }, + { + "dir": "Platform/Android", + "origin": "Platform/Android" + }, + { + "dir": "Platform/Linux", + "origin": "Platform/Linux" + }, + { + "dir": "Platform/Mac", + "origin": "Platform/Mac" + }, + { + "dir": "Platform/Windows", + "origin": "Platform/Windows" + }, + { + "dir": "Platform/iOS", + "origin": "Platform/iOS" + } + ] +} 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 ignore_extensions: - proc_name = _remove_extension(proc_name) + if proc_name in name_set: + logger.debug(f"Found process with name {proc_name}.") + 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) + if ignore_extensions: + extensionless_name = _remove_extension(proc_name) + if extensionless_name in name_set: + process_set_to_kill.add(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 os +import psutil import shutil +import socket import stat +import subprocess +import tempfile +import time + from typing import List, Tuple -import psutil 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) - - if accept_input and not quitonidle: - self.connect_control() - - 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 + 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: + self.connect_control() + + if connect_to_ap: + self.connect_listen() + + 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_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_process', mock.MagicMock) + @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/BuiltInPackages.cmake b/cmake/3rdParty/BuiltInPackages.cmake index c583774e3a..743e2e983e 100644 --- a/cmake/3rdParty/BuiltInPackages.cmake +++ b/cmake/3rdParty/BuiltInPackages.cmake @@ -18,4 +18,13 @@ set(LY_PAL_PACKAGE_FILE_NAME ${CMAKE_CURRENT_LIST_DIR}/${pal_dir}/BuiltInPackage include(${LY_PAL_PACKAGE_FILE_NAME}) # add the above file to the ALLFILES list, so that they show up in IDEs -set(ALLFILES ${ALLFILES} ${LY_PAL_PACKAGE_FILE_NAME}) \ No newline at end of file +set(ALLFILES ${ALLFILES} ${LY_PAL_PACKAGE_FILE_NAME}) + +# temporary compatibility: +# Some 3p libraries may still refer to zlib as "3rdParty::zlib" instead of +# the correct "3rdParty::ZLIB" (Case difference). Until those libraries are updated +# we alias the casing here. This also provides backward compatibility for Gems that use 3rdParty::zlib +# that are not part of the core O3DE repo. +ly_download_associated_package(ZLIB) +find_package(ZLIB) +add_library(3rdParty::zlib ALIAS 3rdParty::ZLIB) diff --git a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake index 5de2ad0253..9dbdbbd8aa 100644 --- a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake +++ b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake @@ -16,9 +16,9 @@ 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.7.167-rev6-android TARGETS AWSNativeSDK PACKAGE_HASH 1624ba9aaf03d001ed0ffc57d2f945ff82590e75a7ea868de35043cf673e82fb) +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) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-android TARGETS PhysX PACKAGE_HASH b8cb6aa46b2a21671f6cb1f6a78713a3ba88824d0447560ff5ce6c01014b9f43) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-android TARGETS mikkelsen PACKAGE_HASH 075e8e4940884971063b5a9963014e2e517246fa269c07c7dc55b8cf2cd99705) @@ -27,6 +27,6 @@ ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-android TARGETS Goo ly_associate_package(PACKAGE_NAME libpng-1.6.37-rev1-android TARGETS libpng PACKAGE_HASH 51d3ec1559c5595196c11e11674cf5745989d3073bf33dabc6697e3eee77a1cc) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-android TARGETS libsamplerate PACKAGE_HASH bf13662afe65d02bcfa16258a4caa9b875534978227d6f9f36c9cfa92b3fb12b) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-android TARGETS OpenSSL PACKAGE_HASH 4036d4019d722f0e1b7a1621bf60b5a17ca6a65c9c78fd8701cee1131eec8480) -ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-android TARGETS zlib PACKAGE_HASH 85b730b97176772538cfcacd6b6aaf4655fc2d368d134d6dd55e02f28f183826) +ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-android TARGETS ZLIB PACKAGE_HASH 73c9e88892c237a3fc6eafc04268ccd9d479e6d55f9df2ed58b236c8f9cf2cae) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-android TARGETS lz4 PACKAGE_HASH f5b22642d218dbbb442cae61e469e5b241c4740acd258c3e8678e60dec61ea93) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index e634cb19c5..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) @@ -42,7 +40,7 @@ ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-linux TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 88c4a359325d749bc34090b9ac466424847f3b71ba0de15045cf355c17c07099) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-linux TARGETS SPIRVCross PACKAGE_HASH 7889ee5460a688e9b910c0168b31445c0079d363affa07b25d4c8aeb608a0b80) ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev2-linux TARGETS azslc PACKAGE_HASH 1ba84d8321a566d35a1e9aa7400211ba8e6d1c11c08e4be3c93e6e74b8f7aef1) -ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-linux TARGETS zlib PACKAGE_HASH 16f3b9e11cda525efb62144f354c1cfc30a5def9eff020dbe49cb00ee7d8234f) +ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-linux TARGETS ZLIB PACKAGE_HASH 9be5ea85722fc27a8645a9c8a812669d107c68e6baa2ca0740872eaeb6a8b0fc) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-linux TARGETS squish-ccr PACKAGE_HASH 85fecafbddc6a41a27c5f59ed4a5dfb123a94cb4666782cf26e63c0a4724c530) ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-linux TARGETS astc-encoder PACKAGE_HASH 2ba97a06474d609945f0ab4419af1f6bbffdd294ca6b869f5fcebec75c573c0f) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-linux TARGETS ISPCTexComp PACKAGE_HASH 065fd12abe4247dde247330313763cf816c3375c221da030bdec35024947f259) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index fd46191015..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) @@ -40,7 +38,7 @@ ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-mac ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-mac TARGETS Qt PACKAGE_HASH 9d25918351898b308ded3e9e571fff6f26311b2071aeafd00dd5b249fdf53f7e) ly_associate_package(PACKAGE_NAME libpng-1.6.37-mac TARGETS libpng PACKAGE_HASH 1ad76cd038ccc1f288f83c5fe2859a0f35c5154e1fe7658e1230cc428d318a8b) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) -ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-mac TARGETS zlib PACKAGE_HASH 21714e8a6de4f2523ee92a7f52d51fbee29c5f37ced334e00dc3c029115b472e) +ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-mac TARGETS ZLIB PACKAGE_HASH b6fea9c79b8bf106d4703b67fecaa133f832ad28696c2ceef45fb5f20013c096) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac TARGETS squish-ccr PACKAGE_HASH 155bfbfa17c19a9cd2ef025de14c5db598f4290045d5b0d83ab58cb345089a77) ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-mac TARGETS astc-encoder PACKAGE_HASH 96f6ea8c3e45ec7fe525230c7c53ca665c8300d8e28456cc19bb3159ce6f8dcc) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 333c952051..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) @@ -47,7 +45,7 @@ ly_associate_package(PACKAGE_NAME OpenMesh-8.1-rev1-windows ly_associate_package(PACKAGE_NAME civetweb-1.8-rev1-windows TARGETS civetweb PACKAGE_HASH 36d0e58a59bcdb4dd70493fb1b177aa0354c945b06c30416348fd326cf323dd4) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows TARGETS OpenSSL PACKAGE_HASH 9af1c50343f89146b4053101a7aeb20513319a3fe2f007e356d7ce25f9241040) ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) -ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-windows TARGETS zlib PACKAGE_HASH 9afab1d67641ed8bef2fb38fc53942da47f2ab339d9e77d3d20704a48af2da0b) +ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-windows TARGETS ZLIB PACKAGE_HASH 8847112429744eb11d92c44026fc5fc53caa4a06709382b5f13978f3c26c4cbd) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-windows TARGETS squish-ccr PACKAGE_HASH 5c3d9fa491e488ccaf802304ad23b932268a2b2846e383f088779962af2bfa84) ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-windows TARGETS astc-encoder PACKAGE_HASH 3addc6fc1a7eb0d6b7f3d530e962af967e6d92b3825ef485da243346357cf78e) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-windows TARGETS ISPCTexComp PACKAGE_HASH b6fa6ea28a2808a9a5524c72c37789c525925e435770f2d94eb2d387360fa2d0) diff --git a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake index 25df8101b4..8042c888c7 100644 --- a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake +++ b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake @@ -17,9 +17,9 @@ 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-rev3-ios TARGETS AWSNativeSDK PACKAGE_HASH 1246219a213ccfff76b526011febf521586d44dbc1753e474f8fb5fd861654a4) +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) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-ios TARGETS PhysX PACKAGE_HASH b1bbc1fc068d2c6e1eb18eecd4e8b776adc516833e8da3dcb1970cef2a8f0cbd) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-ios TARGETS mikkelsen PACKAGE_HASH 976aaa3ccd8582346132a10af253822ccc5d5bcc9ea5ba44d27848f65ee88a8a) @@ -28,5 +28,5 @@ ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-ios TARGETS GoogleB ly_associate_package(PACKAGE_NAME libpng-1.6.37-ios TARGETS libpng PACKAGE_HASH 18a8217721083c4dc46514105be43ca764fa9c994a74aa0b57766ea6f8187e7b) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-ios TARGETS libsamplerate PACKAGE_HASH 7656b961697f490d4f9c35d2e61559f6fc38c32102e542a33c212cd618fc2119) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-ios TARGETS OpenSSL PACKAGE_HASH cd0dfce3086a7172777c63dadbaf0ac3695b676119ecb6d0614b5fb1da03462f) -ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-ios TARGETS zlib PACKAGE_HASH a59fc0f83a02c616b679799310e9d86fde84514c6d2acefa12c6def0ae4a880c) +ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-ios TARGETS ZLIB PACKAGE_HASH c7f10b4d0fe63192054d926f53b08e852cdf472bc2b18e2f7be5aecac1869f7f) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-ios TARGETS lz4 PACKAGE_HASH 588ea05739caa9231a9a17a1e8cf64c5b9a265e16528bc05420af7e2534e86c1) diff --git a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs index 079c20e212..f3af199bc2 100644 --- a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs +++ b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs @@ -20,7 +20,7 @@ + Value="[InstallFolder]\bin\Windows\profile\Default\o3de.exe"/> diff --git a/cmake/Platform/Windows/Packaging/Shortcuts.wxs b/cmake/Platform/Windows/Packaging/Shortcuts.wxs index fefd15294a..fbfa174d06 100644 --- a/cmake/Platform/Windows/Packaging/Shortcuts.wxs +++ b/cmake/Platform/Windows/Packaging/Shortcuts.wxs @@ -18,7 +18,9 @@ - + + + @@ -52,18 +54,18 @@ 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..3d522ce175 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", @@ -93,6 +92,7 @@ "templates": [ "Templates/AssetGem", "Templates/DefaultGem", + "Templates/CustomTool", "Templates/DefaultProject", "Templates/MinimalProject" ] diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 5fe19ddf46..ffca69a14f 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -793,27 +793,32 @@ catch(Exception e) { } finally { try { - if(env.SNS_TOPIC) { - snsPublish( - topicArn: env.SNS_TOPIC, - subject:'Build Result', - message:"${currentBuild.currentResult}:${BUILD_URL}:${env.RECREATE_VOLUME}:${env.CLEAN_OUTPUT_DIRECTORY}:${env.CLEAN_ASSETS}" - ) - } node('controller') { if("${currentBuild.currentResult}" == "SUCCESS") { + buildFailure = "" emailBody = "${BUILD_URL}\nSuccess!" } else { buildFailure = tm('${BUILD_FAILURE_ANALYZER}') emailBody = "${BUILD_URL}\n${buildFailure}!" - if(env.SNS_TOPIC_BUILD_FAILURE) { - message_json = ["build_url":env.BUILD_URL, "repository_name":env.REPOSITORY_NAME, "branch_name":env.BRANCH_NAME, "build_failure":buildFailure] - snsPublish( - topicArn: env.SNS_TOPIC_BUILD_FAILURE, - subject:'Build Failure', - message:JsonOutput.toJson(message_json) - ) - } + + } + if(env.POST_AR_BUILD_SNS_TOPIC) { + message_json = [ + "build_url": env.BUILD_URL, + "build_number": env.BUILD_NUMBER, + "repository_name": env.REPOSITORY_NAME, + "branch_name": env.BRANCH_NAME, + "build_result": "${currentBuild.currentResult}", + "build_failure": buildFailure, + "recreate_volume": env.RECREATE_VOLUME, + "clean_output_directory": env.CLEAN_OUTPUT_DIRECTORY, + "clean_assets": env.CLEAN_ASSETS + ] + snsPublish( + topicArn: env.POST_AR_BUILD_SNS_TOPIC, + subject:'Build Result', + message:JsonOutput.toJson(message_json) + ) } emailext ( body: "${emailBody}", diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index c607c3d821..7c39661273 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -31,7 +31,6 @@ ], "steps": [ "profile_vs2019", - "test_impact_analysis_profile_vs2019", "asset_profile_vs2019", "test_cpu_profile_vs2019" ] 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 - - 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')) - - f.close() + # 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() + + 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 + + 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 _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) -> (bool, str): + 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,39 +421,60 @@ 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_engine_json_data(engine_name: str = None, - engine_path: str or pathlib.Path = None) -> dict or None: - if not engine_name and not engine_path: - logger.error('Must specify either a Engine name or Engine Path.') +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 - if engine_name and not engine_path: - engine_path = get_registered(engine_name=engine_name) + object_path = pathlib.Path(object_path).resolve() + return object_path / f'{object_typename}.json' + - if not engine_path: - logger.error(f'Engine Path {engine_path} has not been registered.') +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 - 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.') + 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 validation.valid_o3de_engine_json(engine_json): - logger.error(f'Engine json {engine_json} is not valid.') + + 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 engine_json.open('r') as f: + with object_json.open('r') as f: try: - engine_json_data = json.load(f) + object_json_data = json.load(f) except json.JSONDecodeError as e: - logger.warn(f'{engine_json} failed to load: {str(e)}') + logger.warn(f'{object_json} failed to load: {e}') else: - return engine_json_data + 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: + if not engine_name and not engine_path: + logger.error('Must specify either a Engine name or Engine Path.') + return None + + if engine_name and not engine_path: + engine_path = get_registered(engine_name=engine_name) + + return get_json_data('engine', engine_path, validation.valid_o3de_engine_json) + def get_project_json_data(project_name: str = None, project_path: str or pathlib.Path = None) -> dict or 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 None + return get_json_data('restricted', restricted_path, validation.valid_o3de_restricted_json) - 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.') +def get_repo_json_data(repo_uri: str) -> dict or None: + if not repo_uri: + logger.error('Must specify a Repo Uri.') return None - 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 + repo_json = get_repo_path(repo_uri=repo_uri) - return None + return get_json_data_file(repo_json, "Repo", validation.valid_o3de_repo_json) + +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/register.py b/scripts/o3de/o3de/register.py index 7db09368ec..c3d201f23c 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -487,14 +487,14 @@ def register_repo(json_data: dict, if remove: logger.warn(f'Removing repo uri {repo_uri}.') return 0 - repo_sha256 = hashlib.sha256(url.encode()) cache_file = manifest.get_o3de_cache_folder() / str(repo_sha256.hexdigest() + '.json') - result = utils.download_file(url, cache_file) + result = utils.download_file(parsed_uri, cache_file) if result == 0: - json_data['repos'].insert(0, repo_uri.as_posix()) + json_data['repos'].insert(0, repo_uri) + repo_set = set() result = repo.process_add_o3de_repo(cache_file, repo_set) return result @@ -621,6 +621,7 @@ def register(engine_path: pathlib.Path = None, return 1 result = result or register_gem_path(json_data, gem_path, remove, external_subdir_engine_path, external_subdir_project_path) + if isinstance(external_subdir_path, pathlib.PurePath): if not external_subdir_path: logger.error(f'External Subdirectory path is None.') diff --git a/scripts/o3de/o3de/repo.py b/scripts/o3de/o3de/repo.py index 8492f391e5..52abfc3386 100644 --- a/scripts/o3de/o3de/repo.py +++ b/scripts/o3de/o3de/repo.py @@ -12,6 +12,7 @@ import pathlib import shutil import urllib.parse import urllib.request +import hashlib from o3de import manifest, utils, validation @@ -24,7 +25,6 @@ def process_add_o3de_repo(file_name: str or pathlib.Path, file_name = pathlib.Path(file_name).resolve() if not validation.valid_o3de_repo_json(file_name): return 1 - cache_folder = manifest.get_o3de_cache_folder() with file_name.open('r') as f: @@ -34,11 +34,30 @@ def process_add_o3de_repo(file_name: str or pathlib.Path, logger.error(f'{file_name} failed to load: {str(e)}') return 1 - for o3de_object_uris, manifest_json in [(repo_data['engines'], 'engine.json'), - (repo_data['projects'], 'project.json'), - (repo_data['gems'], 'gem.json'), - (repo_data['template'], 'template.json'), - (repo_data['restricted'], 'restricted.json')]: + # A repo may not contain all types of object. + manifest_download_list = [] + try: + manifest_download_list.append((repo_data['engines'], 'engine.json')) + except KeyError: + pass + try: + manifest_download_list.append((repo_data['projects'], 'project.json')) + except KeyError: + pass + try: + manifest_download_list.append((repo_data['gems'], 'gem.json')) + except KeyError: + pass + try: + manifest_download_list.append((repo_data['templates'], 'template.json')) + except KeyError: + pass + try: + manifest_download_list.append((repo_data['restricted'], 'restricted.json')) + except KeyError: + pass + + for o3de_object_uris, manifest_json in manifest_download_list: for o3de_object_uri in o3de_object_uris: manifest_json_uri = f'{o3de_object_uri}/{manifest_json}' manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode()) @@ -49,7 +68,27 @@ def process_add_o3de_repo(file_name: str or pathlib.Path, if download_file_result != 0: return download_file_result - repo_set |= repo_data['repos'] + # Having a repo is also optional + repo_list = [] + try: + repo_list.add(repo_data['repos']) + except KeyError: + pass + + for repo in repo_list: + if repo not in repo_set: + repo_set.add(repo) + for o3de_object_uri in o3de_object_uris: + parsed_uri = urllib.parse.urlparse(f'{repo}/repo.json') + manifest_json_sha256 = hashlib.sha256(parsed_uri.geturl().encode()) + cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') + if cache_file.is_file(): + cache_file.unlink() + download_file_result = utils.download_file(parsed_uri, cache_file) + if download_file_result != 0: + return download_file_result + + return process_add_o3de_repo(parsed_uri.geturl(), repo_set) return 0 @@ -70,11 +109,10 @@ def refresh_repos() -> int: if repo_uri not in repo_set: repo_set.add(repo_uri) - repo_uri = f'{repo_uri}/repo.json' - repo_sha256 = hashlib.sha256(repo_uri.encode()) + parsed_uri = urllib.parse.urlparse(f'{repo_uri}/repo.json') + repo_sha256 = hashlib.sha256(parsed_uri.geturl().encode()) cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') if not cache_file.is_file(): - parsed_uri = urllib.parse.urlparse(repo_uri) download_file_result = utils.download_file(parsed_uri, cache_file) if download_file_result != 0: return download_file_result diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py index 18f80584cc..9f9c747390 100755 --- a/scripts/o3de/o3de/utils.py +++ b/scripts/o3de/o3de/utils.py @@ -13,6 +13,10 @@ import uuid import pathlib import shutil import urllib.request +import logging + +logger = logging.getLogger() +logging.basicConfig() def validate_identifier(identifier: str) -> bool: """ @@ -97,11 +101,11 @@ def download_file(parsed_uri, download_path: pathlib.Path) -> int: if download_path.is_file(): logger.warn(f'File already downloaded to {download_path}.') elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: - with urllib.request.urlopen(url) as s: + with urllib.request.urlopen(parsed_uri.geturl()) as s: with download_path.open('wb') as f: shutil.copyfileobj(s, f) else: - origin_file = pathlib.Path(url).resolve() + origin_file = pathlib.Path(parsed_uri.geturl()).resolve() if not origin_file.is_file(): return 1 shutil.copy(origin_file, download_path) 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