diff --git a/AutomatedTesting/Assets/Destruction/cinder_wall_complex.blast b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.blast new file mode 100644 index 0000000000..fcf67594c7 Binary files /dev/null and b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.blast differ diff --git a/AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx new file mode 100644 index 0000000000..62c305c35e --- /dev/null +++ b/AutomatedTesting/Assets/Destruction/cinder_wall_complex.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f94f2634eacb4d7bee20dacc45edef96e4d268f1adb7960b8aa8f3b6e2906ed +size 6867609 diff --git a/AutomatedTesting/Assets/Prefabs/Bush.prefab b/AutomatedTesting/Assets/Prefabs/Bush.prefab new file mode 100644 index 0000000000..ef497bdcd0 --- /dev/null +++ b/AutomatedTesting/Assets/Prefabs/Bush.prefab @@ -0,0 +1,129 @@ +{ + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "Bush", + "Components": { + "Component_[1140272189295067758]": { + "$type": "EditorInspectorComponent", + "Id": 1140272189295067758 + }, + "Component_[13437832196484687256]": { + "$type": "EditorOnlyEntityComponent", + "Id": 13437832196484687256 + }, + "Component_[1553903646452669645]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 1553903646452669645 + }, + "Component_[15914009348632444632]": { + "$type": "EditorEntitySortComponent", + "Id": 15914009348632444632, + "Child Entity Order": [ + "Entity_[7511491868318]" + ] + }, + "Component_[18046340308818780248]": { + "$type": "EditorPrefabComponent", + "Id": 18046340308818780248 + }, + "Component_[1948833233489872938]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 1948833233489872938, + "Parent Entity": "" + }, + "Component_[2903632350157981339]": { + "$type": "SelectionComponent", + "Id": 2903632350157981339 + }, + "Component_[48827510535192710]": { + "$type": "EditorPendingCompositionComponent", + "Id": 48827510535192710 + }, + "Component_[5609536793322429681]": { + "$type": "EditorLockComponent", + "Id": 5609536793322429681 + }, + "Component_[5859168386298620990]": { + "$type": "EditorEntityIconComponent", + "Id": 5859168386298620990 + }, + "Component_[6604616929271524505]": { + "$type": "EditorVisibilityComponent", + "Id": 6604616929271524505 + } + } + }, + "Entities": { + "Entity_[7511491868318]": { + "Id": "Entity_[7511491868318]", + "Name": "Bush", + "Components": { + "Component_[10227459330338484901]": { + "$type": "EditorInspectorComponent", + "Id": 10227459330338484901, + "ComponentOrderEntryArray": [ + { + "ComponentId": 4998941225335869157 + }, + { + "ComponentId": 9922994635792843826, + "SortIndex": 1 + } + ] + }, + "Component_[10972351222359420947]": { + "$type": "EditorOnlyEntityComponent", + "Id": 10972351222359420947 + }, + "Component_[12101122374155214392]": { + "$type": "EditorPendingCompositionComponent", + "Id": 12101122374155214392 + }, + "Component_[1535264614652988260]": { + "$type": "SelectionComponent", + "Id": 1535264614652988260 + }, + "Component_[16367811417907891218]": { + "$type": "EditorVisibilityComponent", + "Id": 16367811417907891218 + }, + "Component_[17044216787716682880]": { + "$type": "EditorEntitySortComponent", + "Id": 17044216787716682880 + }, + "Component_[2129822594969629430]": { + "$type": "EditorEntityIconComponent", + "Id": 2129822594969629430 + }, + "Component_[2838015156782745450]": { + "$type": "EditorLockComponent", + "Id": 2838015156782745450 + }, + "Component_[4998941225335869157]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 4998941225335869157, + "Parent Entity": "ContainerEntity" + }, + "Component_[8773358049076362578]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 8773358049076362578 + }, + "Component_[9922994635792843826]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 9922994635792843826, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{1201406D-FB20-5B5F-B9B5-6A6E8DE00A14}", + "subId": 276506120 + }, + "assetHint": "assets/objects/foliage/bush_privet_01.azmodel" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab b/AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab new file mode 100644 index 0000000000..b938701b6a --- /dev/null +++ b/AutomatedTesting/Assets/Prefabs/BushFlowerBlender.prefab @@ -0,0 +1,2249 @@ +{ + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "BushFlowerBlender", + "Components": { + "Component_[10351434005293588180]": { + "$type": "EditorLockComponent", + "Id": 10351434005293588180 + }, + "Component_[10967554916960846519]": { + "$type": "EditorOnlyEntityComponent", + "Id": 10967554916960846519 + }, + "Component_[14400678265986305456]": { + "$type": "EditorEntityIconComponent", + "Id": 14400678265986305456 + }, + "Component_[15365520792759708998]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15365520792759708998 + }, + "Component_[16281192066639198814]": { + "$type": "EditorEntitySortComponent", + "Id": 16281192066639198814, + "Child Entity Order": [ + "Entity_[263598916892318]" + ] + }, + "Component_[16356049875968660722]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 16356049875968660722, + "Parent Entity": "" + }, + "Component_[16391695178646515457]": { + "$type": "SelectionComponent", + "Id": 16391695178646515457 + }, + "Component_[16407238476365087382]": { + "$type": "EditorVisibilityComponent", + "Id": 16407238476365087382 + }, + "Component_[3542701736829700396]": { + "$type": "EditorPendingCompositionComponent", + "Id": 3542701736829700396 + }, + "Component_[5160094530340900971]": { + "$type": "EditorInspectorComponent", + "Id": 5160094530340900971 + }, + "Component_[6432428663776737136]": { + "$type": "EditorPrefabComponent", + "Id": 6432428663776737136 + } + } + }, + "Entities": { + "Entity_[263573147088542]": { + "Id": "Entity_[263573147088542]", + "Name": "BushSpawner", + "Components": { + "Component_[11886309121583108836]": { + "$type": "EditorPositionModifierComponent", + "Id": 11886309121583108836, + "Configuration": { + "GradientX": { + "GradientId": "Entity_[263590326957726]" + }, + "GradientY": { + "GradientId": "Entity_[263590326957726]" + }, + "GradientZ": { + "GradientId": "" + } + } + }, + "Component_[1240444912193942742]": { + "$type": "{DD96FD51-A86B-48BC-A6AB-89183B538269} EditorSpawnerComponent", + "Id": 1240444912193942742, + "PreviewEntity": "Entity_[263573147088542]" + }, + "Component_[12813702285187817973]": { + "$type": "EditorInspectorComponent", + "Id": 12813702285187817973, + "ComponentOrderEntryArray": [ + { + "ComponentId": 18133156195370267728 + }, + { + "ComponentId": 13450250419263773680, + "SortIndex": 1 + }, + { + "ComponentId": 8841963751527151666, + "SortIndex": 2 + }, + { + "ComponentId": 1240444912193942742, + "SortIndex": 3 + }, + { + "ComponentId": 17991239182321715249, + "SortIndex": 4 + }, + { + "ComponentId": 14175761523159824993, + "SortIndex": 5 + }, + { + "ComponentId": 2884829698060689565, + "SortIndex": 6 + }, + { + "ComponentId": 11886309121583108836, + "SortIndex": 7 + } + ] + }, + "Component_[13179606361687972691]": { + "$type": "EditorEntityIconComponent", + "Id": 13179606361687972691 + }, + "Component_[13450250419263773680]": { + "$type": "EditorReferenceShapeComponent", + "Id": 13450250419263773680, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[13688014277574607552]": { + "$type": "EditorOnlyEntityComponent", + "Id": 13688014277574607552 + }, + "Component_[14175761523159824993]": { + "$type": "EditorDistributionFilterComponent", + "Id": 14175761523159824993, + "Configuration": { + "ThresholdMin": 0.289000004529953, + "ThresholdMax": 0.5690000057220459, + "Gradient": { + "GradientId": "Entity_[263603211859614]" + } + } + }, + "Component_[14232774271081092639]": { + "$type": "EditorLockComponent", + "Id": 14232774271081092639 + }, + "Component_[14811533745267383811]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 14811533745267383811 + }, + "Component_[17540078415915694273]": { + "$type": "EditorVisibilityComponent", + "Id": 17540078415915694273 + }, + "Component_[17991239182321715249]": { + "$type": "EditorRotationModifierComponent", + "Id": 17991239182321715249, + "Configuration": { + "GradientX": { + "GradientId": "" + }, + "GradientY": { + "GradientId": "" + }, + "GradientZ": { + "GradientId": "Entity_[263590326957726]" + } + } + }, + "Component_[18133156195370267728]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 18133156195370267728, + "Parent Entity": "Entity_[263607506826910]" + }, + "Component_[2884829698060689565]": { + "$type": "EditorScaleModifierComponent", + "Id": 2884829698060689565, + "Configuration": { + "RangeMin": 0.75, + "RangeMax": 1.25, + "Gradient": { + "GradientId": "Entity_[263590326957726]" + } + } + }, + "Component_[50628676751372416]": { + "$type": "EditorEntitySortComponent", + "Id": 50628676751372416, + "Child Entity Order": [ + "Entity_[263603211859614]", + "Entity_[263590326957726]" + ] + }, + "Component_[5689270312836823309]": { + "$type": "SelectionComponent", + "Id": 5689270312836823309 + }, + "Component_[7105803915999711758]": { + "$type": "EditorPendingCompositionComponent", + "Id": 7105803915999711758 + }, + "Component_[8841963751527151666]": { + "$type": "EditorDescriptorListComponent", + "Id": 8841963751527151666, + "Configuration": { + "Descriptors": [ + { + "SpawnerType": "{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}", + "InstanceSpawner": { + "$type": "PrefabInstanceSpawner", + "SpawnableAsset": { + "assetId": { + "guid": "{EE51E73C-D753-54AC-A1C0-4F29844FB6C3}", + "subId": 2740536329 + }, + "assetHint": "assets/prefabs/bush.spawnable" + } + } + } + ] + } + } + } + }, + "Entity_[263577442055838]": { + "Id": "Entity_[263577442055838]", + "Name": "RandomNoise", + "Components": { + "Component_[13412023990029767074]": { + "$type": "EditorRandomGradientComponent", + "Id": 13412023990029767074, + "PreviewEntity": "Entity_[263577442055838]" + }, + "Component_[14048059127502350032]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 14048059127502350032, + "Parent Entity": "Entity_[263581737023134]" + }, + "Component_[15585968636846328006]": { + "$type": "EditorEntitySortComponent", + "Id": 15585968636846328006 + }, + "Component_[17356242264256499333]": { + "$type": "EditorOnlyEntityComponent", + "Id": 17356242264256499333 + }, + "Component_[2157320692130316849]": { + "$type": "EditorLockComponent", + "Id": 2157320692130316849 + }, + "Component_[2167582872687341084]": { + "$type": "EditorReferenceShapeComponent", + "Id": 2167582872687341084, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[2794166072748175035]": { + "$type": "SelectionComponent", + "Id": 2794166072748175035 + }, + "Component_[4784966656524567789]": { + "$type": "EditorVisibilityComponent", + "Id": 4784966656524567789 + }, + "Component_[6185400249317227261]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 6185400249317227261 + }, + "Component_[639949486729001685]": { + "$type": "EditorGradientTransformComponent", + "Id": 639949486729001685, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[7347819195697153472]": { + "$type": "EditorEntityIconComponent", + "Id": 7347819195697153472 + }, + "Component_[9225409585951117678]": { + "$type": "EditorInspectorComponent", + "Id": 9225409585951117678, + "ComponentOrderEntryArray": [ + { + "ComponentId": 14048059127502350032 + }, + { + "ComponentId": 2167582872687341084, + "SortIndex": 1 + }, + { + "ComponentId": 639949486729001685, + "SortIndex": 2 + }, + { + "ComponentId": 13412023990029767074, + "SortIndex": 3 + } + ] + }, + "Component_[9667056664436803603]": { + "$type": "EditorPendingCompositionComponent", + "Id": 9667056664436803603 + } + } + }, + "Entity_[263581737023134]": { + "Id": "Entity_[263581737023134]", + "Name": "FlowerSpawner", + "Components": { + "Component_[10064436441176637741]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 10064436441176637741 + }, + "Component_[10418076448308581948]": { + "$type": "EditorRotationModifierComponent", + "Id": 10418076448308581948, + "Configuration": { + "GradientX": { + "GradientId": "" + }, + "GradientY": { + "GradientId": "" + }, + "GradientZ": { + "GradientId": "Entity_[263577442055838]" + } + } + }, + "Component_[12484622175138429315]": { + "$type": "EditorOnlyEntityComponent", + "Id": 12484622175138429315 + }, + "Component_[12590590072231203407]": { + "$type": "EditorLockComponent", + "Id": 12590590072231203407 + }, + "Component_[13079591667184987458]": { + "$type": "EditorScaleModifierComponent", + "Id": 13079591667184987458, + "Configuration": { + "RangeMin": 0.75, + "RangeMax": 1.25, + "Gradient": { + "GradientId": "Entity_[263577442055838]" + } + } + }, + "Component_[13174773394274524631]": { + "$type": "EditorInspectorComponent", + "Id": 13174773394274524631, + "ComponentOrderEntryArray": [ + { + "ComponentId": 1915862229814997128 + }, + { + "ComponentId": 582800388831726963, + "SortIndex": 1 + }, + { + "ComponentId": 18006385801184712266, + "SortIndex": 2 + }, + { + "ComponentId": 6902499453501456890, + "SortIndex": 3 + }, + { + "ComponentId": 10418076448308581948, + "SortIndex": 4 + }, + { + "ComponentId": 13983302912005698250, + "SortIndex": 5 + }, + { + "ComponentId": 13079591667184987458, + "SortIndex": 6 + }, + { + "ComponentId": 7912787520673119502, + "SortIndex": 7 + } + ] + }, + "Component_[13200009872392504010]": { + "$type": "EditorEntitySortComponent", + "Id": 13200009872392504010, + "Child Entity Order": [ + "Entity_[263594621925022]", + "Entity_[263577442055838]" + ] + }, + "Component_[13983302912005698250]": { + "$type": "EditorDistributionFilterComponent", + "Id": 13983302912005698250, + "Configuration": { + "ThresholdMin": 0.2879999876022339, + "ThresholdMax": 0.5680000185966492, + "Gradient": { + "GradientId": "Entity_[263586031990430]" + } + } + }, + "Component_[14726174657215669218]": { + "$type": "EditorPendingCompositionComponent", + "Id": 14726174657215669218 + }, + "Component_[18006385801184712266]": { + "$type": "EditorDescriptorListComponent", + "Id": 18006385801184712266, + "Configuration": { + "Descriptors": [ + { + "SpawnerType": "{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}", + "InstanceSpawner": { + "$type": "PrefabInstanceSpawner", + "SpawnableAsset": { + "assetId": { + "guid": "{80C0CF4E-9A5E-544B-B89E-BC980175A259}", + "subId": 3875079122 + }, + "assetHint": "assets/prefabs/pinkflower.spawnable" + } + } + }, + { + "SpawnerType": "{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}", + "InstanceSpawner": { + "$type": "PrefabInstanceSpawner", + "SpawnableAsset": { + "assetId": { + "guid": "{20DD1202-A434-5482-9FB9-AED4C78F6CBF}", + "subId": 2669863854 + }, + "assetHint": "assets/prefabs/purpleflower.spawnable" + } + } + }, + { + "SpawnerType": "{23C40FD4-A55F-4BD3-BE5B-DC5423F217C2}", + "InstanceSpawner": { + "$type": "EmptyInstanceSpawner" + } + } + ] + } + }, + "Component_[1915862229814997128]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 1915862229814997128, + "Parent Entity": "Entity_[263607506826910]" + }, + "Component_[2123416208548010747]": { + "$type": "SelectionComponent", + "Id": 2123416208548010747 + }, + "Component_[4172073657954183661]": { + "$type": "EditorVisibilityComponent", + "Id": 4172073657954183661 + }, + "Component_[4297556504463195307]": { + "$type": "EditorEntityIconComponent", + "Id": 4297556504463195307 + }, + "Component_[582800388831726963]": { + "$type": "EditorReferenceShapeComponent", + "Id": 582800388831726963, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[6902499453501456890]": { + "$type": "{DD96FD51-A86B-48BC-A6AB-89183B538269} EditorSpawnerComponent", + "Id": 6902499453501456890, + "PreviewEntity": "Entity_[263581737023134]" + }, + "Component_[7912787520673119502]": { + "$type": "EditorPositionModifierComponent", + "Id": 7912787520673119502, + "Configuration": { + "GradientX": { + "GradientId": "Entity_[263577442055838]" + }, + "GradientY": { + "GradientId": "Entity_[263577442055838]" + }, + "GradientZ": { + "GradientId": "" + } + } + } + } + }, + "Entity_[263586031990430]": { + "Id": "Entity_[263586031990430]", + "Name": "Invert", + "Components": { + "Component_[11123278080744920525]": { + "$type": "EditorEntitySortComponent", + "Id": 11123278080744920525 + }, + "Component_[1199715693160400161]": { + "$type": "EditorInspectorComponent", + "Id": 1199715693160400161, + "ComponentOrderEntryArray": [ + { + "ComponentId": 7438979211266329827 + }, + { + "ComponentId": 6027557066164356793, + "SortIndex": 1 + }, + { + "ComponentId": 8903771095804438921, + "SortIndex": 2 + } + ] + }, + "Component_[12162816428693886169]": { + "$type": "EditorEntityIconComponent", + "Id": 12162816428693886169 + }, + "Component_[14493180089799969074]": { + "$type": "EditorVisibilityComponent", + "Id": 14493180089799969074 + }, + "Component_[16206759293595766479]": { + "$type": "EditorPendingCompositionComponent", + "Id": 16206759293595766479 + }, + "Component_[17566144032052847628]": { + "$type": "SelectionComponent", + "Id": 17566144032052847628 + }, + "Component_[17784057508127919200]": { + "$type": "EditorLockComponent", + "Id": 17784057508127919200 + }, + "Component_[18377773171420051348]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 18377773171420051348 + }, + "Component_[6027557066164356793]": { + "$type": "EditorReferenceShapeComponent", + "Id": 6027557066164356793, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[7438979211266329827]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 7438979211266329827, + "Parent Entity": "Entity_[263594621925022]" + }, + "Component_[8421650081106982664]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8421650081106982664 + }, + "Component_[8903771095804438921]": { + "$type": "EditorInvertGradientComponent", + "Id": 8903771095804438921, + "Configuration": { + "Gradient": { + "GradientId": "Entity_[263594621925022]" + } + }, + "PreviewEntity": "Entity_[263586031990430]" + } + } + }, + "Entity_[263590326957726]": { + "Id": "Entity_[263590326957726]", + "Name": "RandomNoise", + "Components": { + "Component_[10420548607186596407]": { + "$type": "SelectionComponent", + "Id": 10420548607186596407 + }, + "Component_[11351507264577024072]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 11351507264577024072, + "Parent Entity": "Entity_[263573147088542]" + }, + "Component_[11576941520191560445]": { + "$type": "EditorEntityIconComponent", + "Id": 11576941520191560445 + }, + "Component_[11787535428542045683]": { + "$type": "EditorPendingCompositionComponent", + "Id": 11787535428542045683 + }, + "Component_[11946132649485953311]": { + "$type": "EditorEntitySortComponent", + "Id": 11946132649485953311 + }, + "Component_[11970417753234451570]": { + "$type": "EditorOnlyEntityComponent", + "Id": 11970417753234451570 + }, + "Component_[13743879269876214143]": { + "$type": "EditorVisibilityComponent", + "Id": 13743879269876214143 + }, + "Component_[15614946697365576378]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15614946697365576378 + }, + "Component_[17045174842161759045]": { + "$type": "EditorLockComponent", + "Id": 17045174842161759045 + }, + "Component_[3107716031616536910]": { + "$type": "EditorInspectorComponent", + "Id": 3107716031616536910, + "ComponentOrderEntryArray": [ + { + "ComponentId": 11351507264577024072 + }, + { + "ComponentId": 6316453818295975755, + "SortIndex": 1 + }, + { + "ComponentId": 4846745402056100407, + "SortIndex": 2 + }, + { + "ComponentId": 8967153379180885152, + "SortIndex": 3 + } + ] + }, + "Component_[4846745402056100407]": { + "$type": "EditorGradientTransformComponent", + "Id": 4846745402056100407, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[6316453818295975755]": { + "$type": "EditorReferenceShapeComponent", + "Id": 6316453818295975755, + "Configuration": { + "ShapeEntityId": "Entity_[263573147088542]" + } + }, + "Component_[8967153379180885152]": { + "$type": "EditorRandomGradientComponent", + "Id": 8967153379180885152, + "PreviewEntity": "Entity_[263590326957726]" + } + } + }, + "Entity_[263594621925022]": { + "Id": "Entity_[263594621925022]", + "Name": "PerlinNoise", + "Components": { + "Component_[15798567753940523121]": { + "$type": "EditorPerlinGradientComponent", + "Id": 15798567753940523121, + "Configuration": { + "randomSeed": 33, + "frequency": 0.25 + }, + "PreviewEntity": "Entity_[263594621925022]" + }, + "Component_[16035848776632936419]": { + "$type": "EditorPendingCompositionComponent", + "Id": 16035848776632936419 + }, + "Component_[16434108759160050253]": { + "$type": "EditorVisibilityComponent", + "Id": 16434108759160050253 + }, + "Component_[16805003046507210760]": { + "$type": "EditorReferenceShapeComponent", + "Id": 16805003046507210760, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[17679463569742913027]": { + "$type": "EditorOnlyEntityComponent", + "Id": 17679463569742913027 + }, + "Component_[18285870860955961523]": { + "$type": "SelectionComponent", + "Id": 18285870860955961523 + }, + "Component_[2330427084045350198]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 2330427084045350198 + }, + "Component_[5921906930080711994]": { + "$type": "EditorEntitySortComponent", + "Id": 5921906930080711994, + "Child Entity Order": [ + "Entity_[263586031990430]" + ] + }, + "Component_[764117453152842781]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 764117453152842781, + "Parent Entity": "Entity_[263581737023134]" + }, + "Component_[7701931249281891300]": { + "$type": "EditorEntityIconComponent", + "Id": 7701931249281891300 + }, + "Component_[7967070680905543315]": { + "$type": "EditorInspectorComponent", + "Id": 7967070680905543315, + "ComponentOrderEntryArray": [ + { + "ComponentId": 764117453152842781 + }, + { + "ComponentId": 16805003046507210760, + "SortIndex": 1 + }, + { + "ComponentId": 8029154041194694200, + "SortIndex": 2 + }, + { + "ComponentId": 15798567753940523121, + "SortIndex": 3 + } + ] + }, + "Component_[8029154041194694200]": { + "$type": "EditorGradientTransformComponent", + "Id": 8029154041194694200, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[8660028910391135744]": { + "$type": "EditorLockComponent", + "Id": 8660028910391135744 + } + } + }, + "Entity_[263598916892318]": { + "Id": "Entity_[263598916892318]", + "Name": "LandscapeCanvas", + "Components": { + "Component_[12967746135199800648]": { + "$type": "EditorVisibilityComponent", + "Id": 12967746135199800648 + }, + "Component_[13081062505162225823]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 13081062505162225823 + }, + "Component_[16021195931295059075]": { + "$type": "EditorPendingCompositionComponent", + "Id": 16021195931295059075 + }, + "Component_[16134261272053586014]": { + "$type": "EditorEntityIconComponent", + "Id": 16134261272053586014 + }, + "Component_[17500735767968801510]": { + "$type": "EditorLandscapeCanvasComponent", + "Id": 17500735767968801510, + "Graph": { + "m_nodes": [ + { + "Key": 1, + "Value": { + "$type": "CylinderShapeNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "BushFlowerBlender" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263607506826910]", + "m_componentId": 6683290476113150719 + } + }, + { + "Key": 2, + "Value": { + "$type": "RandomNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "RandomNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263590326957726]", + "m_componentId": 8967153379180885152 + } + }, + { + "Key": 3, + "Value": { + "$type": "SpawnerAreaNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "BushSpawner" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PlacementBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 1240444912193942742 + } + }, + { + "Key": 4, + "Value": { + "$type": "PerlinNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "PerlinNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263603211859614]", + "m_componentId": 10160640699095155981 + } + }, + { + "Key": 5, + "Value": { + "$type": "RandomNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "RandomNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263577442055838]", + "m_componentId": 13412023990029767074 + } + }, + { + "Key": 6, + "Value": { + "$type": "SpawnerAreaNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "FlowerSpawner" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PlacementBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 6902499453501456890 + } + }, + { + "Key": 7, + "Value": { + "$type": "AreaBlenderNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "BushFlowerBlender" + } + } + } + ], + "m_extendableSlots": { + "InboundArea": [ + { + "m_value": { + "$type": "EntityId", + "Value": "" + } + }, + { + "m_value": { + "$type": "EntityId", + "Value": "" + }, + "m_subId": 1 + } + ] + }, + "m_vegetationEntityId": "Entity_[263607506826910]", + "m_componentId": 7229206794941734410 + } + }, + { + "Key": 8, + "Value": { + "$type": "PerlinNoiseGradientNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "PerlinNoise" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263594621925022]", + "m_componentId": 15798567753940523121 + } + }, + { + "Key": 9, + "Value": { + "$type": "InvertGradientModifierNode", + "m_propertySlots": [ + { + "Key": { + "m_name": "EntityName" + }, + "Value": { + "m_value": { + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "Value": "Invert" + } + } + } + ], + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "PreviewBounds" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263586031990430]", + "m_componentId": 8903771095804438921 + } + }, + { + "Key": 10, + "Value": { + "$type": "RotationModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 17991239182321715249 + } + }, + { + "Key": 11, + "Value": { + "$type": "DistributionFilterNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 14175761523159824993 + } + }, + { + "Key": 12, + "Value": { + "$type": "ScaleModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 2884829698060689565 + } + }, + { + "Key": 13, + "Value": { + "$type": "PositionModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263573147088542]", + "m_componentId": 11886309121583108836 + } + }, + { + "Key": 14, + "Value": { + "$type": "RotationModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 10418076448308581948 + } + }, + { + "Key": 15, + "Value": { + "$type": "DistributionFilterNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 13983302912005698250 + } + }, + { + "Key": 16, + "Value": { + "$type": "ScaleModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradient" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 13079591667184987458 + } + }, + { + "Key": 17, + "Value": { + "$type": "PositionModifierNode", + "m_inputDataSlots": [ + { + "Key": { + "m_name": "InboundGradientX" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientY" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + }, + { + "Key": { + "m_name": "InboundGradientZ" + }, + "Value": { + "m_value": { + "$type": "EntityId", + "Value": "" + } + } + } + ], + "m_vegetationEntityId": "Entity_[263581737023134]", + "m_componentId": 7912787520673119502 + } + } + ], + "m_connections": [ + { + "m_sourceEndpoint": [ + 8, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 9, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 3, + { + "m_name": "OutboundArea" + } + ], + "m_targetEndpoint": [ + 7, + { + "m_name": "InboundArea" + } + ] + }, + { + "m_sourceEndpoint": [ + 6, + { + "m_name": "OutboundArea" + } + ], + "m_targetEndpoint": [ + 7, + { + "m_name": "InboundArea", + "m_subId": 1 + } + ] + }, + { + "m_sourceEndpoint": [ + 1, + { + "m_name": "Bounds" + } + ], + "m_targetEndpoint": [ + 3, + { + "m_name": "PlacementBounds" + } + ] + }, + { + "m_sourceEndpoint": [ + 1, + { + "m_name": "Bounds" + } + ], + "m_targetEndpoint": [ + 6, + { + "m_name": "PlacementBounds" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 10, + { + "m_name": "InboundGradientZ" + } + ] + }, + { + "m_sourceEndpoint": [ + 4, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 11, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 12, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 13, + { + "m_name": "InboundGradientX" + } + ] + }, + { + "m_sourceEndpoint": [ + 2, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 13, + { + "m_name": "InboundGradientY" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 14, + { + "m_name": "InboundGradientZ" + } + ] + }, + { + "m_sourceEndpoint": [ + 9, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 15, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 16, + { + "m_name": "InboundGradient" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 17, + { + "m_name": "InboundGradientX" + } + ] + }, + { + "m_sourceEndpoint": [ + 5, + { + "m_name": "OutboundGradient" + } + ], + "m_targetEndpoint": [ + 17, + { + "m_name": "InboundGradientY" + } + ] + } + ], + "m_uiMetadata": { + "m_sceneMetadata": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "Scale": 0.3771495156249999, + "AnchorX": -498.4760437011719, + "AnchorY": 808.6978149414063 + } + } + } + }, + "m_nodeMetadata": [ + { + "Key": 2, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "GradientNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 100.0, + 580.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{2D583FA2-5659-491F-8914-6302420E3F0B}" + } + } + } + }, + { + "Key": 3, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 480.0, + 600.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{A26480C4-A80B-4B36-ADE1-918E4541EA2B}" + } + } + } + }, + { + "Key": 5, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "GradientNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 100.0, + 1540.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{6CE9EA8B-85E7-4E17-B1D6-CA97D9D65276}" + } + } + } + }, + { + "Key": 6, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 480.0, + 1560.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{8FABE49F-1D73-46FE-8616-A982EEE46DDD}" + } + } + } + }, + { + "Key": 7, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 840.0, + 1580.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{C5EEBC1D-B91B-4852-A41D-425C6FED3FB9}" + } + } + } + }, + { + "Key": 9, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "GradientModifierNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 480.0, + 2060.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5EB76670-0CBE-4F2A-9AE7-F65709D0BE92}" + } + } + } + }, + { + "Key": 10, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 147.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5CB69A73-F7BA-4F76-AB86-F343052C3D7D}" + } + } + } + }, + { + "Key": 11, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 367.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{235DEBEC-E3F8-43C1-BA7E-41475B834D70}" + } + } + } + }, + { + "Key": 12, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 288.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{9E56E7B6-2ECE-42F9-9A23-FF7D192209D2}" + } + } + } + }, + { + "Key": 13, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 6.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{61759FE6-E6F7-4F4B-819B-FBFE125D7389}" + } + } + } + }, + { + "Key": 14, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 147.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{0AC1C6C8-7CC1-4068-A87D-F4B92AE8FCAF}" + } + } + } + }, + { + "Key": 15, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 367.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5D698B90-830F-46FE-ADD8-6164EA098C3E}" + } + } + } + }, + { + "Key": 16, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 288.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{9DE6DE4A-2E6F-44BA-B292-3CD2215C1D5B}" + } + } + } + }, + { + "Key": 17, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "VegetationAreaNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 6.0, + 6.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{57FA822D-629F-4949-A8D7-D675544EBC0A}" + } + } + } + } + ] + }, + "m_nodeWrappings": [ + { + "Key": 10, + "Value": [ + 3, + 1 + ] + }, + { + "Key": 11, + "Value": [ + 3, + 6 + ] + }, + { + "Key": 12, + "Value": [ + 3, + 2 + ] + }, + { + "Key": 13, + "Value": [ + 3, + {} + ] + }, + { + "Key": 14, + "Value": [ + 6, + 1 + ] + }, + { + "Key": 15, + "Value": [ + 6, + 6 + ] + }, + { + "Key": 16, + "Value": [ + 6, + 2 + ] + }, + { + "Key": 17, + "Value": [ + 6, + {} + ] + } + ] + } + }, + "Component_[17574651838531525263]": { + "$type": "EditorEntitySortComponent", + "Id": 17574651838531525263, + "Child Entity Order": [ + "Entity_[263607506826910]" + ] + }, + "Component_[3086205760978540160]": { + "$type": "EditorLockComponent", + "Id": 3086205760978540160 + }, + "Component_[3102230753208284750]": { + "$type": "EditorInspectorComponent", + "Id": 3102230753208284750, + "ComponentOrderEntryArray": [ + { + "ComponentId": 5796173638130327223 + }, + { + "ComponentId": 17500735767968801510, + "SortIndex": 1 + } + ] + }, + "Component_[4695341524212000585]": { + "$type": "EditorOnlyEntityComponent", + "Id": 4695341524212000585 + }, + "Component_[5796173638130327223]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 5796173638130327223, + "Parent Entity": "ContainerEntity" + }, + "Component_[8278362871021770878]": { + "$type": "SelectionComponent", + "Id": 8278362871021770878 + } + } + }, + "Entity_[263603211859614]": { + "Id": "Entity_[263603211859614]", + "Name": "PerlinNoise", + "Components": { + "Component_[10006546776757889020]": { + "$type": "EditorVisibilityComponent", + "Id": 10006546776757889020 + }, + "Component_[10160640699095155981]": { + "$type": "EditorPerlinGradientComponent", + "Id": 10160640699095155981, + "Configuration": { + "randomSeed": 33, + "frequency": 0.25 + }, + "PreviewEntity": "Entity_[263603211859614]" + }, + "Component_[11506183926478566822]": { + "$type": "EditorEntitySortComponent", + "Id": 11506183926478566822 + }, + "Component_[13160180411150366392]": { + "$type": "EditorLockComponent", + "Id": 13160180411150366392 + }, + "Component_[15083697380300161163]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15083697380300161163 + }, + "Component_[15404456223257061245]": { + "$type": "EditorInspectorComponent", + "Id": 15404456223257061245, + "ComponentOrderEntryArray": [ + { + "ComponentId": 6484803030406851652 + }, + { + "ComponentId": 4912800286535654244, + "SortIndex": 1 + }, + { + "ComponentId": 2395166459126906105, + "SortIndex": 2 + }, + { + "ComponentId": 10160640699095155981, + "SortIndex": 3 + } + ] + }, + "Component_[2395166459126906105]": { + "$type": "EditorGradientTransformComponent", + "Id": 2395166459126906105, + "Configuration": { + "ShapeReference": "", + "Bounds": [ + 50.0, + 50.0, + 1.0 + ] + } + }, + "Component_[4417011400526470160]": { + "$type": "EditorEntityIconComponent", + "Id": 4417011400526470160 + }, + "Component_[4601061670472962302]": { + "$type": "EditorPendingCompositionComponent", + "Id": 4601061670472962302 + }, + "Component_[4912800286535654244]": { + "$type": "EditorReferenceShapeComponent", + "Id": 4912800286535654244, + "Configuration": { + "ShapeEntityId": "Entity_[263607506826910]" + } + }, + "Component_[6233982345083723563]": { + "$type": "SelectionComponent", + "Id": 6233982345083723563 + }, + "Component_[6484803030406851652]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 6484803030406851652, + "Parent Entity": "Entity_[263573147088542]" + }, + "Component_[6813813525871080299]": { + "$type": "EditorOnlyEntityComponent", + "Id": 6813813525871080299 + } + } + }, + "Entity_[263607506826910]": { + "Id": "Entity_[263607506826910]", + "Name": "BushFlowerBlender", + "Components": { + "Component_[13725600808872754587]": { + "$type": "EditorEntityIconComponent", + "Id": 13725600808872754587 + }, + "Component_[15959807606564505710]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 15959807606564505710, + "Parent Entity": "Entity_[263598916892318]" + }, + "Component_[1596826146661515925]": { + "$type": "EditorPendingCompositionComponent", + "Id": 1596826146661515925 + }, + "Component_[4525591661239956058]": { + "$type": "EditorVisibilityComponent", + "Id": 4525591661239956058 + }, + "Component_[6683290476113150719]": { + "$type": "EditorCylinderShapeComponent", + "Id": 6683290476113150719, + "CylinderShape": { + "Configuration": { + "Radius": 25.0 + } + } + }, + "Component_[6879394931668195375]": { + "$type": "EditorEntitySortComponent", + "Id": 6879394931668195375, + "Child Entity Order": [ + "Entity_[263573147088542]", + "Entity_[263581737023134]" + ] + }, + "Component_[7229206794941734410]": { + "$type": "EditorAreaBlenderComponent", + "Id": 7229206794941734410, + "Configuration": { + "Operations": [ + "Entity_[263573147088542]", + "Entity_[263581737023134]" + ] + }, + "PreviewEntity": "Entity_[263607506826910]" + }, + "Component_[8772711204845372843]": { + "$type": "EditorLockComponent", + "Id": 8772711204845372843 + }, + "Component_[8959345022109725835]": { + "$type": "SelectionComponent", + "Id": 8959345022109725835 + }, + "Component_[9094488642973843583]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 9094488642973843583 + }, + "Component_[9112960617967513749]": { + "$type": "EditorInspectorComponent", + "Id": 9112960617967513749, + "ComponentOrderEntryArray": [ + { + "ComponentId": 15959807606564505710 + }, + { + "ComponentId": 7229206794941734410, + "SortIndex": 1 + }, + { + "ComponentId": 6683290476113150719, + "SortIndex": 2 + } + ] + }, + "Component_[9541665258597207652]": { + "$type": "EditorOnlyEntityComponent", + "Id": 9541665258597207652 + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab b/AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab new file mode 100644 index 0000000000..a8716ec099 --- /dev/null +++ b/AutomatedTesting/Assets/Prefabs/PurpleFlower.prefab @@ -0,0 +1,129 @@ +{ + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "PurpleFlower", + "Components": { + "Component_[10519928302743666073]": { + "$type": "EditorPrefabComponent", + "Id": 10519928302743666073 + }, + "Component_[13894087802180240181]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 13894087802180240181, + "Parent Entity": "" + }, + "Component_[15788541052719571801]": { + "$type": "EditorEntityIconComponent", + "Id": 15788541052719571801 + }, + "Component_[15842981265136092481]": { + "$type": "SelectionComponent", + "Id": 15842981265136092481 + }, + "Component_[16360384897559021149]": { + "$type": "EditorInspectorComponent", + "Id": 16360384897559021149 + }, + "Component_[16713545675046303279]": { + "$type": "EditorVisibilityComponent", + "Id": 16713545675046303279 + }, + "Component_[1806734194268113785]": { + "$type": "EditorPendingCompositionComponent", + "Id": 1806734194268113785 + }, + "Component_[5392020700593853313]": { + "$type": "EditorEntitySortComponent", + "Id": 5392020700593853313, + "Child Entity Order": [ + "Entity_[14335611090324]" + ] + }, + "Component_[5995854518752659458]": { + "$type": "EditorLockComponent", + "Id": 5995854518752659458 + }, + "Component_[6963022284400845376]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 6963022284400845376 + }, + "Component_[8055275578170091546]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8055275578170091546 + } + } + }, + "Entities": { + "Entity_[14335611090324]": { + "Id": "Entity_[14335611090324]", + "Name": "PurpleFlower", + "Components": { + "Component_[10887353073528055802]": { + "$type": "EditorPendingCompositionComponent", + "Id": 10887353073528055802 + }, + "Component_[12641127425852859189]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 12641127425852859189, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{D493A670-6D82-5AE9-A2C8-A2EB02684F71}", + "subId": 284799939 + }, + "assetHint": "assets/objects/foliage/grass_flower_purple.azmodel" + } + } + } + }, + "Component_[14406733303466080015]": { + "$type": "EditorInspectorComponent", + "Id": 14406733303466080015, + "ComponentOrderEntryArray": [ + { + "ComponentId": 9231452352781000222 + }, + { + "ComponentId": 12641127425852859189, + "SortIndex": 1 + } + ] + }, + "Component_[1452384341905923012]": { + "$type": "EditorLockComponent", + "Id": 1452384341905923012 + }, + "Component_[2215454016415585892]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 2215454016415585892 + }, + "Component_[4104108067383423623]": { + "$type": "EditorVisibilityComponent", + "Id": 4104108067383423623 + }, + "Component_[4197335450471807917]": { + "$type": "SelectionComponent", + "Id": 4197335450471807917 + }, + "Component_[6877680739064997650]": { + "$type": "EditorOnlyEntityComponent", + "Id": 6877680739064997650 + }, + "Component_[7372550507186490390]": { + "$type": "EditorEntityIconComponent", + "Id": 7372550507186490390 + }, + "Component_[7673532337364366244]": { + "$type": "EditorEntitySortComponent", + "Id": 7673532337364366244 + }, + "Component_[9231452352781000222]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 9231452352781000222, + "Parent Entity": "ContainerEntity" + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist b/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist index 372cafbdce..9120d992c7 100644 --- a/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist +++ b/AutomatedTesting/Assets/VegDescriptorLists/flower_pink.vegdescriptorlist @@ -2,11 +2,11 @@ - + - + - + diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index f1e66ac492..cca20bbf8c 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -21,7 +21,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") @pytest.mark.parametrize("launcher_platform", ['windows_editor']) class TestAutomation(EditorTestSuite): - enable_prefab_system = False + enable_prefab_system = True @pytest.mark.test_case_id("C36525657") class AtomEditorComponents_BloomAdded(EditorSharedTest): @@ -120,6 +120,14 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_SSAOAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_SSAOAdded as test_module + @pytest.mark.test_case_id("C36529666") + class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module + + @pytest.mark.test_case_id("C36525660") + class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded 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 bd0477a1db..5299e55a2b 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -26,13 +26,3 @@ class TestAutomation(EditorTestSuite): @pytest.mark.test_case_id("C36525660") class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module - - # this test causes editor to crash when using slices. once automation transitions to prefabs it should pass - @pytest.mark.test_case_id("C36529666") - class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest): - from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module - - # this test causes editor to crash when using slices. once automation transitions to prefabs it should pass - @pytest.mark.test_case_id("C36525660") - class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest): - from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py index ddcd5c3c46..0c9b39e354 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py @@ -60,7 +60,7 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Add Diffuse Global Illumination level component to the level entity. @@ -86,10 +86,10 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity(): # 4. Set Quality Level property to Low diffuse_global_illumination_component.set_component_property_value( - AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low'])) + AtomComponentProperties.diffuse_global_illumination('Quality Level'), GLOBAL_ILLUMINATION_QUALITY['Low']) quality = diffuse_global_illumination_component.get_component_property_value( AtomComponentProperties.diffuse_global_illumination('Quality Level')) - Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low']) + Report.result(Tests.diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low']) # 5. Enter/Exit game mode. TestHelper.enter_game_mode(Tests.enter_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py index c050dd76d4..f9660957bb 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py @@ -67,7 +67,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Add Display Mapper level component to the level entity. @@ -102,7 +102,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity(): display_mapper_component.set_component_property_value( AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True) Report.result( - Test.enable_ldr_color_grading_lut, + Tests.enable_ldr_color_grading_lut, display_mapper_component.get_component_property_value( AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py index 56b22eb20d..ce29a1de88 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py @@ -97,7 +97,7 @@ def AtomEditorComponents_Bloom_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Bloom entity with no components. @@ -170,10 +170,12 @@ def AtomEditorComponents_Bloom_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, bloom_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not bloom_entity.exists()) # 15. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py index e840cf3cf1..eb5e24e3a6 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py @@ -95,7 +95,7 @@ def AtomEditorComponents_Decal_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Decal entity with no components. @@ -162,6 +162,7 @@ def AtomEditorComponents_Decal_AddedToEntity(): # 11. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not decal_entity.exists()) # 12. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py index 71163ece94..f1578cbf8d 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py @@ -97,7 +97,7 @@ def AtomEditorComponents_DeferredFog_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Deferred Fog entity with no components. @@ -174,10 +174,12 @@ def AtomEditorComponents_DeferredFog_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, deferred_fog_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not deferred_fog_entity.exists()) # 15. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py index e2c5f9c77d..3913420981 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py @@ -107,7 +107,7 @@ def AtomEditorComponents_DepthOfField_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a DepthOfField entity with no components. @@ -189,10 +189,12 @@ def AtomEditorComponents_DepthOfField_AddedToEntity(): # 15. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, depth_of_field_entity.exists()) # 16. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not depth_of_field_entity.exists()) # 17. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py index f42c091057..04b4f7876b 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py @@ -90,7 +90,7 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Diffuse Probe Grid entity with no components. @@ -168,10 +168,12 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, diffuse_probe_grid_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not diffuse_probe_grid_entity.exists()) # 14. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py index f3ffc0f366..19cfbedcd5 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py @@ -95,7 +95,7 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Directional Light entity with no components. @@ -168,10 +168,12 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, directional_light_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not directional_light_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py index 558d69046e..d3ea669eab 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py @@ -91,7 +91,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Display Mapper entity with no components. @@ -166,10 +166,12 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): # 11. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, display_mapper_entity.exists()) # 12. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not display_mapper_entity.exists()) # 13. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py index dddcca64fa..ec402f0f5f 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_EntityReferenceAdded.py @@ -81,7 +81,7 @@ def AtomEditorComponents_EntityReference_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Entity Reference entity with no components. @@ -139,10 +139,12 @@ def AtomEditorComponents_EntityReference_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, entity_reference_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not entity_reference_entity.exists()) # 11. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py index 6fa9660539..f60f0e18be 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py @@ -101,7 +101,7 @@ def AtomEditorComponents_ExposureControl_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Creation of Exposure Control entity with no components. @@ -169,10 +169,12 @@ def AtomEditorComponents_ExposureControl_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, exposure_control_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not exposure_control_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py index 7f0c38e289..0e8e6fa43e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py @@ -99,7 +99,7 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Global Skylight (IBL) entity with no components. @@ -176,10 +176,12 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity(): # 11. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, global_skylight_entity.exists()) # 12. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not global_skylight_entity.exists()) # 13. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py index a77a1f50a4..3c6d261f22 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py @@ -82,7 +82,7 @@ def AtomEditorComponents_Grid_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Grid entity with no components. @@ -139,10 +139,12 @@ def AtomEditorComponents_Grid_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, grid_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not grid_entity.exists()) # 11. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py index 4972079fcd..5846206e50 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py @@ -96,7 +96,7 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an HDR Color Grading entity with no components. @@ -173,10 +173,12 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, hdr_color_grading_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not hdr_color_grading_entity.exists()) # 15. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py index 0f96bc5424..fc093b60db 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py @@ -87,7 +87,7 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an HDRi Skybox with no components. @@ -158,10 +158,12 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): # 10. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, hdri_skybox_entity.exists()) # 11. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not hdri_skybox_entity.exists()) # 12. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py index 249671556d..75a04612a3 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py @@ -89,7 +89,7 @@ def AtomEditorComponents_Light_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Light entity with no components. @@ -144,10 +144,12 @@ def AtomEditorComponents_Light_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, light_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not light_entity.exists()) # 11. Look for errors asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py index afb8033426..149af73151 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LookModificationAdded.py @@ -104,7 +104,7 @@ def AtomEditorComponents_LookModification_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create an Look Modification entity with no components. @@ -192,10 +192,12 @@ def AtomEditorComponents_LookModification_AddedToEntity(): # 14. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, look_modification_entity.exists()) # 15. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not look_modification_entity.exists()) # 16. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py index c613bb23e7..919a6753f4 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py @@ -102,7 +102,7 @@ def AtomEditorComponents_Material_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Material entity with no components. @@ -184,10 +184,12 @@ def AtomEditorComponents_Material_AddedToEntity(): # 16. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, material_entity.exists()) # 17. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not material_entity.exists()) # 18. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py index 9d56753961..a87ee75b53 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py @@ -87,7 +87,7 @@ def AtomEditorComponents_Mesh_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Mesh entity with no components. @@ -151,10 +151,12 @@ def AtomEditorComponents_Mesh_AddedToEntity(): # 10. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, mesh_entity.exists()) # 11. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not mesh_entity.exists()) # 12. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py index 4226ae3dfe..56bb5eb68c 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py @@ -80,7 +80,7 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a occlusion culling plane entity with no components. @@ -140,10 +140,12 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, occlusion_culling_plane_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not occlusion_culling_plane_entity.exists()) # 11. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py index fa8c626016..e0c558811e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py @@ -89,7 +89,7 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Physical Sky entity with no components. @@ -146,10 +146,12 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, physical_sky_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not physical_sky_entity.exists()) # 11. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py index 6e4ae2d9ac..b36267433f 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py @@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a PostFX Gradient Weight Modifier entity with no components. @@ -162,10 +162,12 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_gradient_weight_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_gradient_weight_entity.exists()) # 14. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py index efef4c0a43..07fffb9bde 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py @@ -80,7 +80,7 @@ def AtomEditorComponents_postfx_layer_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a PostFX Layer entity with no components. @@ -137,10 +137,12 @@ def AtomEditorComponents_postfx_layer_AddedToEntity(): # 9. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_layer_entity.exists()) # 10. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_layer_entity.exists()) # 11. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py index 228ddfcdc5..616233d2d2 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py @@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Post FX Radius Weight Modifier entity with no components. @@ -161,10 +161,12 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_radius_weight_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_radius_weight_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py index 0a37ac6bf7..5f7d571a18 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -98,7 +98,7 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a PostFx Shape Weight Modifier entity with no components. @@ -188,10 +188,12 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): # 15. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists()) # 16. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists()) # 17. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py index 70adf9143a..85156b4db6 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py @@ -97,7 +97,7 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a Reflection Probe entity with no components. @@ -183,10 +183,12 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity(): # 13. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, reflection_probe_entity.exists()) # 14. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not reflection_probe_entity.exists()) # 15. Look for errors or asserts. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py index 15f40f8b70..09f77c4a9f 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_SSAOAdded.py @@ -94,7 +94,7 @@ def AtomEditorComponents_SSAO_AddedToEntity(): # 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") + TestHelper.open_level("Graphics", "base_empty") # Test steps begin. # 1. Create a SSAO entity with no components. @@ -163,10 +163,12 @@ def AtomEditorComponents_SSAO_AddedToEntity(): # 12. UNDO deletion. general.undo() + general.idle_wait_frames(1) Report.result(Tests.deletion_undo, ssao_entity.exists()) # 13. REDO deletion. general.redo() + general.idle_wait_frames(1) Report.result(Tests.deletion_redo, not ssao_entity.exists()) # 14. Look for errors and asserts. diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py index 36fc6003f7..ac74c611a2 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py @@ -5,9 +5,9 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ +import collections.abc from typing import List from math import isclose -import collections.abc import azlmbr.bus as bus import azlmbr.editor as editor diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt index ba607ede32..2e3e72883c 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt @@ -14,7 +14,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::DynamicVegetationTests_Main_Optimized TEST_SERIAL TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor @@ -27,7 +27,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::DynamicVegetationTests_Periodic_Optimized TEST_SERIAL TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor @@ -58,7 +58,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::GradientSignalTests_Periodic_Optimized TEST_SERIAL TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py index 66aa6d27ad..aa5e5e4f9c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py @@ -51,22 +51,18 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(): import os - import azlmbr.asset as asset import azlmbr.editor as editor import azlmbr.legacy.general as general import azlmbr.bus as bus import azlmbr.math as math - import azlmbr.prefab as prefab import editor_python_test_tools.hydra_editor_utils as hydra - from editor_python_test_tools.prefab_utils import Prefab from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -76,9 +72,9 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(): center_point = math.Vector3(512.0, 512.0, 32.0) flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") - flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0] + flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "AltFilter_PinkFlower")[0] - spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, flower_prefab) + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, flower_prefab) # Add a Vegetation Altitude Filter spawner_entity.add_component("Vegetation Altitude Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py index 06f6c2a7b4..be2c5e4d8c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py @@ -32,9 +32,7 @@ def AltitudeFilter_FilterStageToggle(): import os import azlmbr.legacy.general as general - import azlmbr.bus as bus import azlmbr.math as math - import azlmbr.prefab as prefab import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg @@ -45,17 +43,16 @@ def AltitudeFilter_FilterStageToggle(): POSTPROCESS_INSTANCE_COUNT = 34 # Open an existing simple level - helper.init_idle() - helper.open_level("", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") - flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0] + flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "AltFilter_PinkFlower2")[0] - vegetation = dynveg.create_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, flower_prefab) + vegetation = dynveg.create_temp_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, flower_prefab) # Add a Vegetation Altitude Filter to the vegetation area entity vegetation.add_component("Vegetation Altitude Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py index 544a58e242..1e9762d7d8 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py @@ -56,8 +56,7 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -67,9 +66,9 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(): center_point = math.Vector3(512.0, 512.0, 32.0) flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") - flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0] + flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "AltFilter_PinkFlower3")[0] - spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, flower_prefab) + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, flower_prefab) # Add a Vegetation Altitude Filter spawner_entity.add_component("Vegetation Altitude Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py index a856256929..7242b0b629 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py @@ -56,19 +56,21 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): """ import os + from pathlib import Path import azlmbr.bus as bus import azlmbr.editor as editor import azlmbr.legacy.general as general import azlmbr.math as math import azlmbr.vegetation as vegetation + import azlmbr.prefab as prefab import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper - def create_asset_list_entity(name, center, dynamic_slice_asset_path): + def create_asset_list_entity(name, center, target_prefab): asset_list_entity = hydra.Entity(name) asset_list_entity.create_entity( center, @@ -77,18 +79,34 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): if asset_list_entity.id.IsValid(): print(f"'{asset_list_entity.name}' created") - # Set the Asset List to a Dynamic Slice spawner with a specific slice asset selected - dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() - dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) + if target_prefab: + # Get the in-memory spawnable asset id if exists + spawnable_name = Path(target_prefab.file_path).stem + spawnable_asset_id = prefab.PrefabPublicRequestBus(bus.Broadcast, 'GetInMemorySpawnableAssetId', + spawnable_name) + + # Create the in-memory spawnable asset from given prefab if the spawnable does not exist + if not spawnable_asset_id.is_valid(): + create_spawnable_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreateInMemorySpawnableAsset', + target_prefab.file_path, + spawnable_name) + assert create_spawnable_result.IsSuccess(), \ + f"Prefab operation 'CreateInMemorySpawnableAssets' failed. Error: {create_spawnable_result.GetError()}" + spawnable_asset_id = create_spawnable_result.GetValue() + else: + spawnable_asset_id = None + + # Set the vegetation area to a prefab instance spawner with a specific prefab asset selected descriptor = hydra.get_component_property_value(asset_list_entity.components[0], - "Configuration|Embedded Assets|[0]") - descriptor.spawner = dynamic_slice_spawner + 'Configuration|Embedded Assets|[0]') + prefab_spawner = vegetation.PrefabInstanceSpawner() + prefab_spawner.SetPrefabAssetId(spawnable_asset_id) + descriptor.spawner = prefab_spawner asset_list_entity.get_set_test(0, "Configuration|Embedded Assets|[0]", descriptor) return asset_list_entity # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -96,11 +114,13 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): # 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - asset_path2 = os.path.join("Slices", "PurpleFlower.dynamicslice") - asset_list_entity = create_asset_list_entity("Asset List 1", center_point, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "AssetList_PinkFlower")[0] + purple_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + purple_flower_prefab = dynveg.create_temp_mesh_prefab(purple_flower_asset_path, "AssetList_PurpleFlower")[0] + asset_list_entity = create_asset_list_entity("Asset List 1", center_point, pink_flower_prefab) asset_list_entity2 = create_asset_list_entity("Asset List 2", center_point, None) - asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, asset_path2) + asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, purple_flower_prefab) # 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn # on center instead of corner diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py index b8b50f8110..8ea0b6b23d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py @@ -56,8 +56,7 @@ def AssetWeightSelector_InstancesExpressBasedOnWeight(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -66,14 +65,15 @@ def AssetWeightSelector_InstancesExpressBasedOnWeight(): # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors, one set to a # valid slice entity, and one set to None spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "AssetWeight_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + pink_flower_prefab) desc_asset = hydra.get_component_property_value(spawner_entity.components[2], "Configuration|Embedded Assets")[0] desc_list = [desc_asset, desc_asset] spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Slice Asset", None) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Prefab Asset", None) # Add an Asset Weight Selector component to the spawner entity spawner_entity.add_component("Vegetation Asset Weight Selector") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py index ed5501d719..be0fe8dba7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py @@ -64,16 +64,15 @@ def DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(): instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") - + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create a new entity with required vegetation area components spawner_center_point = math.Vector3(520.0, 520.0, 32.0) - asset_path = os.path.join("Slices", "1m_cube.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + cube_asset_path = os.path.join("testdata", "multi-mat_fbx", "multi-mat_1m_cube.azmodel") + cube_prefab = dynveg.create_temp_mesh_prefab(cube_asset_path, "DistanceBetween_1m_cube2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + cube_prefab) # 3) Create a surface to plant on surface_center_point = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py index c24ec3c314..b2244fdfa7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py @@ -62,16 +62,15 @@ def DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(): instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") - + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create a new entity with required vegetation area components spawner_center_point = math.Vector3(520.0, 520.0, 32.0) - asset_path = os.path.join("Slices", "1m_cube.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + cube_asset_path = os.path.join("testdata", "multi-mat_fbx", "multi-mat_1m_cube.azmodel") + cube_prefab = dynveg.create_temp_mesh_prefab(cube_asset_path, "DistanceBetween_1m_cube")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, + 16.0, 16.0, 16.0, cube_prefab) # 3) Create a surface to plant on surface_center_point = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py index d016473d5e..c1ca3cbfc6 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py @@ -39,11 +39,11 @@ def DynVegUtils_TempPrefabCreationWorks(): with Tracer() as error_tracer: # Create dictionary for prefab filenames and paths to create using helper function mesh_prefabs = { - "PinkFlower": os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel"), - "PurpleFlower": os.path.join("assets", "objects", "foliage", "grass_flower_purple.azmodel"), - "1m_Cube": os.path.join("objects", "_primitives", "_box_1x1.azmodel"), - "CedarTree": os.path.join("assets", "objects", "foliage", "cedar.azmodel"), - "Bush": os.path.join("assets", "objects", "foliage", "bush_privet_01.azmodel"), + "UtilsTest_PinkFlower": os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel"), + "UtilsTest_PurpleFlower": os.path.join("assets", "objects", "foliage", "grass_flower_purple.azmodel"), + "UtilsTest_1m_Cube": os.path.join("objects", "_primitives", "_box_1x1.azmodel"), + "UtilsTest_CedarTree": os.path.join("assets", "objects", "foliage", "cedar.azmodel"), + "UtilsTest_Bush": os.path.join("assets", "objects", "foliage", "bush_privet_01.azmodel"), } # 1) Open an existing simple level diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py index fcbf85c59d..c8581ad275 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py @@ -71,8 +71,7 @@ def DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Grab the UUID that we need for creating an Dynamic Slice Instance Spawner diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py index d0a51809b4..91315a6971 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py @@ -57,8 +57,7 @@ def EmptyInstanceSpawner_EmptySpawnerWorks(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Grab the UUID that we need for creating an Empty Spawner diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py index 471c8862fd..f83807a62b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py @@ -60,8 +60,7 @@ def InstanceSpawnerPriority_LayerAndSubPriority(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -69,9 +68,10 @@ def InstanceSpawnerPriority_LayerAndSubPriority(): # 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area spawner_center_point = math.Vector3(508.0, 508.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "Priority_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0, + pink_flower_prefab) blocker_center_point = math.Vector3(516.0, 516.0, 32.0) blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0) 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 6b5a80ee8e..79f2e4169b 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 @@ -85,18 +85,14 @@ def LayerBlender_E2E_Editor(): # 2) Create 2 vegetation areas with different meshes purple_position = math.Vector3(504.0, 512.0, 32.0) - purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity_1 = dynveg.create_dynamic_slice_vegetation_area("Purple Spawner", - purple_position, - 16.0, 16.0, 1.0, - purple_asset_path) + purple_flower_prefab_path = os.path.join("assets", "prefabs", "PurpleFlower.spawnable") + spawner_entity_1 = dynveg.create_prefab_vegetation_area("Purple Spawner", purple_position, 16.0, 16.0, 1.0, + purple_flower_prefab_path) pink_position = math.Vector3(520.0, 512.0, 32.0) - pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity_2 = dynveg.create_dynamic_slice_vegetation_area("Pink Spawner", - pink_position, - 16.0, 16.0, 1.0, - pink_asset_path) + pink_flower_prefab_path = os.path.join("assets", "prefabs", "PinkFlower.spawnable") + spawner_entity_2 = dynveg.create_prefab_vegetation_area("Pink Spawner", pink_position, 16.0, 16.0, 1.0, + pink_flower_prefab_path) base_position = math.Vector3(512.0, 512.0, 32.0) dynveg.create_surface_entity("Surface Entity", @@ -135,11 +131,11 @@ def LayerBlender_E2E_Editor(): pink_count = 0 purple_count = 0 for instance in instances: - purple_asset_path = purple_asset_path.replace("\\", "/").lower() - pink_asset_path = pink_asset_path.replace("\\", "/").lower() - if instance.descriptor.spawner.GetSliceAssetPath() == pink_asset_path: + purple_flower_prefab_path = purple_flower_prefab_path.replace("\\", "/").lower() + pink_flower_prefab_path = pink_flower_prefab_path.replace("\\", "/").lower() + if instance.descriptor.spawner.GetPrefabAssetPath() == pink_flower_prefab_path: pink_count += 1 - elif instance.descriptor.spawner.GetSliceAssetPath() == purple_asset_path: + elif instance.descriptor.spawner.GetPrefabAssetPath() == purple_flower_prefab_path: purple_count += 1 Report.result(Tests.instances_blended, pink_count == purple_count and (pink_count + purple_count == num_expected)) @@ -152,11 +148,10 @@ def LayerBlender_E2E_Editor(): components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector) - # 6) Save and export to engine + # 6) Save the created level general.save_level() - general.export_to_engine() - pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") - success = helper.wait_for_condition(lambda: os.path.exists(pak_path), 10.0) + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = helper.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) Report.result(Tests.saved_and_exported, success) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py index b89edcdeb7..5c3083ee3d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py @@ -17,7 +17,6 @@ class Tests: ) - def LayerBlocker_InstancesBlockedInConfiguredArea(): """ Summary: @@ -58,8 +57,7 @@ def LayerBlocker_InstancesBlockedInConfiguredArea(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -67,9 +65,10 @@ def LayerBlocker_InstancesBlockedInConfiguredArea(): # 2) Create a new instance spawner entity spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "Blocker_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, + 16.0, 16.0, 16.0, pink_flower_prefab) # 3) Create surface for planting on dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py index 6796f56b11..a277f14a41 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py @@ -42,16 +42,17 @@ def LayerSpawner_FilterStageToggle(): POSTPROCESS_INSTANCE_COUNT = 19 # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # Create a vegetation area with all needed components position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation_entity = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerFilter_PinkFlower")[0] + vegetation_entity = dynveg.create_temp_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, + pink_flower_prefab) vegetation_entity.add_component("Vegetation Altitude Filter") vegetation_entity.add_component("Vegetation Position Modifier") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py index 1153ae2657..dc06c298d1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py @@ -42,19 +42,16 @@ def LayerSpawner_InheritBehaviorFlag(): SURFACE_TAG = "test_tag" - def set_dynamic_slice_asset(entity_obj, component_index, dynamic_slice_asset_path): - dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() - dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) - descriptor = hydra.get_component_property_value( - entity_obj.components[component_index], "Configuration|Embedded Assets|[0]" - ) - descriptor.spawner = dynamic_slice_spawner + def set_prefab_asset(entity_obj, component_index, spawnable_prefab): + descriptor = hydra.get_component_property_value(entity_obj.components[component_index], + "Configuration|Embedded Assets|[0]") + prefab_spawner = vegetation.PrefabInstanceSpawner() + prefab_spawner.SetPrefabAssetId(spawnable_prefab) + descriptor.spawner = prefab_spawner entity_obj.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") - + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create Emitter entity and add the required components @@ -80,7 +77,9 @@ def LayerSpawner_InheritBehaviorFlag(): veg_1.create_entity( position, ["Vegetation Layer Spawner", "Shape Reference", "Vegetation Asset List"] ) - set_dynamic_slice_asset(veg_1, 2, os.path.join("Slices", "PinkFlower.dynamicslice")) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerInheritBehavior_PinkFlower")[0] + set_prefab_asset(veg_1, 2, pink_flower_prefab) veg_1.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) # Create second vegetation area and assign a valid asset @@ -88,7 +87,9 @@ def LayerSpawner_InheritBehaviorFlag(): veg_2.create_entity( position, ["Vegetation Layer Spawner", "Shape Reference", "Vegetation Asset List"] ) - set_dynamic_slice_asset(veg_2, 2, os.path.join("Slices", "PurpleFlower.dynamicslice")) + purple_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + purple_flower_prefab = dynveg.create_temp_mesh_prefab(purple_flower_asset_path, "temp_PurpleFlower")[0] + set_prefab_asset(veg_2, 2, purple_flower_prefab) veg_2.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) # Assign the vegetation areas to the Blender entity diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py index beec54b4eb..662ac63714 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py @@ -56,16 +56,14 @@ def LayerSpawner_InstancesPlantInAllSupportedShapes(): Report.result(success, result) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create basic vegetation area entity and set the properties entity_position = math.Vector3(125.0, 136.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - vegetation = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerShapePlant_PinkFlower")[0] + vegetation = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) vegetation.remove_component("Box Shape") vegetation.add_component("Shape Reference") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py index fe8863c625..deb0376c9b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py @@ -43,13 +43,13 @@ def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(): import azlmbr.legacy.general as general import azlmbr.math as math + import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set up a test environment to validate that switching viewports correctly changes which camera # the vegetation system uses. @@ -100,11 +100,12 @@ def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(): surface_height) # Create the two vegetation areas - test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - first_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size, - box_size, test_slice_asset_path) - second_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size, - box_size, test_slice_asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerViewportRefresh_PinkFlower")[0] + first_veg_entity = dynveg.create_temp_prefab_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size, + box_size, pink_flower_prefab) + second_veg_entity = dynveg.create_temp_prefab_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size, + box_size, pink_flower_prefab) # When the first viewport is active, the first area should be full of instances, and the second should be empty general.set_active_viewport(0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py index 9777a52c82..7c365d0ee5 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py @@ -50,19 +50,17 @@ def MeshBlocker_InstancesBlockedByMesh(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "MeshBlocker_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # Create surface entity to plant on dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py index 3c47edeb91..a54a8653f0 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py @@ -53,19 +53,17 @@ def MeshBlocker_InstancesBlockedByMeshHeightTuning(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "MeshBlocker_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # 3) Create surface entity to plant on dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py index cee8ebe5b6..9af17b0cd5 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py @@ -58,8 +58,7 @@ def MeshSurfaceTagEmitter_DependentOnMeshComponent(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with component "Mesh Surface Tag Emitter" entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py index d7abc66106..cd9963e844 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py @@ -47,8 +47,7 @@ def MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh" entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py index 6232cd374d..cdb2fac80b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py @@ -55,8 +55,7 @@ def PhysXColliderSurfaceTagEmitter_E2E_Editor(): return behavior_context_test_success # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Verify all of the BehaviorContext API: behavior_context = ( @@ -90,8 +89,10 @@ def PhysXColliderSurfaceTagEmitter_E2E_Editor(): dynveg.create_surface_entity("Baseline Surface", entity_center_point, 32.0, 32.0, 1.0) # Create a new entity with required vegetation area components - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "PhysXCollider_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_center_point, test_box_size, + test_box_size, test_box_size, pink_flower_prefab) # Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag spawner_entity.add_component("Vegetation Surface Mask Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py index be7e8ad754..85a1cddb95 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py @@ -64,8 +64,7 @@ def PositionModifier_AutoSnapToSurfaceWorks(): 'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max'] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -73,9 +72,10 @@ def PositionModifier_AutoSnapToSurfaceWorks(): # 2) Create a new entity with required vegetation area components and a Position Modifier spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "PosMod_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, + 16.0, pink_flower_prefab) # Add a Vegetation Position Modifier and set offset values to 0 spawner_entity.add_component("Vegetation Position Modifier") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py index 07d4fbd067..6be3dc31f7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py @@ -102,16 +102,17 @@ def PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(): return offset_success and offset_success2 # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(16.0, -5.0, 32.0) # 2) Create a new entity with required vegetation area components spawner_center_point = math.Vector3(16.0, 16.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "PosMod_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, + 1.0, pink_flower_prefab) # Add a Vegetation Position Modifier and set offset values to 0 spawner_entity.add_component("Vegetation Position Modifier") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_Embedded_E2E.py similarity index 87% rename from AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py rename to AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_Embedded_E2E.py index b4650c782e..8ce865b5d2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_Embedded_E2E.py @@ -63,12 +63,14 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): import azlmbr.entity as entity import azlmbr.math as math import azlmbr.paths as paths + import azlmbr.vegetation as vegetation import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper + # 1) Create a new, temporary level lvl_name = "tmp_level" helper.init_idle() @@ -79,13 +81,14 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): # 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path) + pink_flower_prefab_path = os.path.join("assets", "prefabs", "PinkFlower.spawnable") + spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, + pink_flower_prefab_path) spawner_entity.add_component("Script Canvas") instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas") instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, math.Uuid(), False) - spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script) + spawner_entity.get_set_test(3, "Properties", instance_counter_script) Report.result(Tests.spawner_entity_created, spawner_entity.id.IsValid() and hydra.has_components(spawner_entity.id, ["Script Canvas"])) @@ -106,11 +109,10 @@ def DynamicSliceInstanceSpawner_Embedded_E2E(): search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) - # 6) Save and export to engine + # 6) Save the created level general.save_level() - general.export_to_engine() - pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") - success = helper.wait_for_condition(lambda: os.path.exists(pak_path), 10.0) + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = helper.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) Report.result(Tests.saved_and_exported, success) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_External_E2E.py similarity index 96% rename from AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py rename to AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_External_E2E.py index a5e7e90ce2..a3e60f6337 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PrefabInstanceSpawner_External_E2E.py @@ -128,11 +128,10 @@ def DynamicSliceInstanceSpawner_External_E2E(): search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) - # 6) Save and export to engine + # 6) Save the created level general.save_level() - general.export_to_engine() - pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak") - success = helper.wait_for_condition(lambda: os.path.exists(pak_path), 10.0) + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = helper.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) Report.result(Tests.saved_and_exported, success) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py index c1ea8e03d1..877a38c231 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py @@ -81,14 +81,15 @@ def RotationModifierOverrides_InstancesRotateWithinRange(): return result # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create vegetation entity and add components entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "RotMod_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 16.0, 16.0, 16.0, + pink_flower_prefab) spawner_entity.add_component("Vegetation Rotation Modifier") # Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances. num_expected = 20 * 20 diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py index 518c11a0cf..b0999f3195 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py @@ -120,13 +120,14 @@ def RotationModifier_InstancesRotateWithinRange(): # Main Script # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Set up vegetation entities - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "RotMod_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", LEVEL_CENTER, 2.0, 2.0, 2.0, + pink_flower_prefab) additional_components = [ "Vegetation Rotation Modifier" diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py index b2ce6d8afa..42b042e26e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py @@ -91,16 +91,17 @@ def ScaleModifierOverrides_InstancesProperlyScale(): return False # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) # 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ScaleMod_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 16.0, 16.0, 16.0, + pink_flower_prefab) # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py index fd0df1c83d..8008224e3e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py @@ -84,8 +84,7 @@ def ScaleModifier_InstancesProperlyScale(): return False # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(500.49, 498.69, 46.66) general.set_current_view_rotation(-42.05, 0.00, -36.33) @@ -93,9 +92,10 @@ def ScaleModifier_InstancesProperlyScale(): # 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and # Vegetation Scale Modifier entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ScaleMod_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 16.0, 16.0, 16.0, + pink_flower_prefab) spawner_entity.add_component("Vegetation Scale Modifier") # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py index b5c00a53b7..5436855433 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py @@ -49,15 +49,16 @@ def ShapeIntersectionFilter_FilterStageToggle(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ShapeIntersection_PinkFlower")[0] + vegetation = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", position, 16.0, 16.0, 16.0, + pink_flower_prefab) # Create Surface for instances to plant on dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py index 6678a620af..e9e4c722f1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py @@ -60,8 +60,7 @@ def ShapeIntersectionFilter_InstancesPlantInAssignedShape(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -69,9 +68,10 @@ def ShapeIntersectionFilter_InstancesPlantInAssignedShape(): # 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "ShapeIntersection_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, + pink_flower_prefab) spawner_entity.add_component("Vegetation Shape Intersection Filter") # Create a planting surface diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py index c9233d15ea..07dbb3f977 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py @@ -58,15 +58,16 @@ def SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(): return False # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create a spawner entity setup with all needed components center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SlopeAlign_PinkFlower2")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, + pink_flower_prefab) # Create a sloped mesh surface for the instances to plant on center_point = math.Vector3(502.0, 512.0, 24.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py index d92babffa4..088728480b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py @@ -59,15 +59,16 @@ def SlopeAlignmentModifier_InstanceSurfaceAlignment(): return False # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create a spawner entity setup with all needed components center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SlopeAlign_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, + pink_flower_prefab) # Create a sloped mesh surface for the instances to plant on center_point = math.Vector3(502.0, 512.0, 24.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py index 0f95853156..cc04dc4cb1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py @@ -63,16 +63,17 @@ def SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 475.0, 38.0) # 2) Create a new entity with required vegetation area components center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "Slope_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, + pink_flower_prefab) # Add a Vegetation Slope Filter spawner_entity.add_component("Vegetation Slope Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py similarity index 52% rename from AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py rename to AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py index fc55080cec..182bd5510a 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks.py @@ -7,21 +7,21 @@ SPDX-License-Identifier: Apache-2.0 OR MIT class Tests: - spawner_slice_created = ( - "Spawner slice created successfully", - "Failed to create Spawner slice" + spawner_prefab_created = ( + "Spawner prefab created successfully", + "Failed to create Spawner prefab" ) instance_count_unhidden = ( "Initial instance counts are as expected", "Found an unexpected number of initial instances" ) instance_count_hidden = ( - "Instance counts upon hiding the Spawner slice are as expected", - "Unexpectedly found instances with the Spawner slice hidden" + "Instance counts upon hiding the Spawner prefab are as expected", + "Unexpectedly found instances with the Spawner prefab hidden" ) - blender_slice_created = ( - "Blender slice created successfully", - "Failed to create Blender slice" + blender_prefab_created = ( + "Blender prefab created successfully", + "Failed to create Blender prefab" ) @@ -43,81 +43,76 @@ def SpawnerSlices_SliceCreationAndVisibilityToggleWorks(): import azlmbr.math as math import azlmbr.legacy.general as general - import azlmbr.slice as slice import azlmbr.bus as bus import azlmbr.editor as editor - import azlmbr.asset as asset import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg + from editor_python_test_tools.editor_entity_utils import EditorEntity from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper - - def path_is_valid_asset(asset_path): - asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) - return asset_id.invoke("IsValid") + from editor_python_test_tools.prefab_utils import Prefab # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) - # 2) C2627900 Verifies if a slice containing the Vegetation Layer Spawner component can be created. + # 2) Verifies if a prefab containing the Vegetation Layer Spawner component can be created. # 2.1) Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - veg_1 = dynveg.create_dynamic_slice_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SpawnerPrefab_PinkFlower")[0] + veg_1 = dynveg.create_temp_prefab_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, + pink_flower_prefab) - # 2.2) Create slice from the entity - slice_path = os.path.join("slices", "TestSlice_1.slice") - slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", veg_1.id, slice_path) + # 2.2) Create prefab from the entity + spawner_prefab_filename = "TestPrefab_1" + spawner_prefab, spawner_prefab_instance = Prefab.create_prefab([veg_1], spawner_prefab_filename) - # 2.3) Verify if the slice has been created successfully - spawner_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 10.0) - Report.result(Tests.spawner_slice_created, spawner_slice_success) + # Verify if prefab is created + Report.result(Tests.spawner_prefab_created, spawner_prefab.is_prefab_loaded(spawner_prefab_filename)) - # 3) C2627904: Hiding a slice containing the component clears any visuals from the Viewport + # 3) Hiding a prefab containing the component clears any visuals from the Viewport # 3.1) Create Surface for instances to plant on dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) - # 3.2) Initially verify instance count before hiding slice + # 3.2) Initially verify instance count before hiding prefab initial_count_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 5.0) Report.result(Tests.instance_count_unhidden, initial_count_success) - # 3.3) Hide the slice and verify instance count - editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, False) + # 3.3) Hide the prefab and verify instance count + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", spawner_prefab_instance.container_entity.id, False) hidden_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0), 5.0) Report.result(Tests.instance_count_hidden, hidden_instance_count) # 3.4) Unhide the slice - editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, True) + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", spawner_prefab_instance.container_entity.id, True) - # 4) C2627905 A slice containing the Vegetation Layer Blender component can be created. + # 4) A slice containing the Vegetation Layer Blender component can be created. # 4.1) Create another vegetation entity to add to blender component - veg_2 = dynveg.create_dynamic_slice_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "") + veg_2 = dynveg.create_empty_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0) # 4.2) Create entity with Vegetation Layer Blender components_to_add = ["Box Shape", "Vegetation Layer Blender"] - blender_entity = hydra.Entity("blender_entity") - blender_entity.create_entity(position, components_to_add) + blender_entity = EditorEntity.create_editor_entity("blender_entity") + blender_entity.add_components(components_to_add) # 4.3) Pin both the vegetation areas to the blender entity - pte = hydra.get_property_tree(blender_entity.components[1]) + pte = blender_entity.components[1].get_property_tree() path = "Configuration|Vegetation Areas" pte.update_container_item(path, 0, veg_1.id) pte.add_container_item(path, 1, veg_2.id) # 4.4) Drag the simple vegetation areas under the Vegetation Layer Blender entity to create an entity hierarchy. veg_1.set_test_parent_entity(blender_entity) - veg_2.set_test_parent_entity(blender_entity) + veg_2.set_parent_entity(blender_entity.id) - # 4.5) Create slice from blender entity - slice_path = os.path.join("slices", "TestSlice_2.slice") - slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", blender_entity.id, slice_path) + # 4.5) Create prefab from blender entity + blender_prefab_filename = "TestPrefab_2" + blender_prefab, blender_prefab_instance = Prefab.create_prefab([veg_2], blender_prefab_filename) - # 4.6) Verify if the slice has been created successfully - blender_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0) - Report.result(Tests.blender_slice_created, blender_slice_success) + # 4.6) Verify if the prefab has been created successfully + Report.result(Tests.blender_prefab_created, blender_prefab.is_prefab_loaded(blender_prefab_filename)) if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py index 1ad4f305c3..3612e03bcc 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py @@ -34,13 +34,12 @@ def SurfaceDataRefreshes_RemainsStable(): import azlmbr.legacy.general as general import azlmbr.math as math + import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() world_center = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py index 3e76a03c53..b98b0f0546 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py @@ -67,8 +67,7 @@ def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(): surface_data.SurfaceTag("test_tag3")] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Set view of planting area for visual debugging general.set_current_view_position(512.0, 500.0, 38.0) @@ -76,9 +75,10 @@ def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(): # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SurfaceMaskOverrides_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, + 16.0, pink_flower_prefab) asset_list_component = spawner_entity.components[2] desc_asset = hydra.get_component_property_value(asset_list_component, "Configuration|Embedded Assets")[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py index fee62c04d1..fe17b03c6f 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py @@ -39,12 +39,12 @@ def SurfaceMaskFilter_BasicSurfaceTagCreation(): """ import azlmbr.surface_data as surface_data + + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() tag1 = surface_data.SurfaceTag() tag2 = surface_data.SurfaceTag() diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py index 3f298e793e..f5174af3bb 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py @@ -91,18 +91,16 @@ def SurfaceMaskFilter_ExclusionList(): Report.info(f"Failed to add Generated surface tag of {surface_tag}") # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SurfaceMaskTagExclusion_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # 3) Add a Vegetation Surface Mask Filter component to the entity. spawner_entity.add_component("Vegetation Surface Mask Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py index 39b72ff4a9..34d22d3d9b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py @@ -92,18 +92,16 @@ def SurfaceMaskFilter_InclusionList(): Report.info(f"Failed to add Generated surface tag of {surface_tag}") # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SurfaceMaskTagInclusion_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", entity_position, 10.0, 10.0, 10.0, + pink_flower_prefab) # 3) Add a Vegetation Surface Mask Filter component to the entity. spawner_entity.add_component("Vegetation Surface Mask Filter") diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py index 167fb8901c..7516bfb31d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py @@ -47,15 +47,16 @@ def SystemSettings_SectorPointDensity(): INSTANCE_COUNT_AFTER_DENSITY_CHANGE = 100 # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SectorPoint_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", position, 16.0, 16.0, 1.0, + pink_flower_prefab) dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) # Count the number of vegetation instances in the vegetation area diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py index 7bb78ac3e0..ef7959a3e7 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py @@ -43,15 +43,16 @@ def SystemSettings_SectorSize(): VEGETATION_INSTANCE_COUNT = 400 # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() general.set_current_view_position(512.0, 480.0, 38.0) # Create basic vegetation entity position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "SectorSize_PinkFlower")[0] + vegetation = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", position, 16.0, 16.0, 1.0, + pink_flower_prefab) dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) # Add the Vegetation Debugger component to the Level Inspector diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py index 8a6c4d9a17..b6069d1ef5 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py @@ -43,18 +43,20 @@ def VegetationInstances_DespawnWhenOutOfRange(): import azlmbr.legacy.general as general import azlmbr.math as math + import editor_python_test_tools.hydra_editor_utils as hydra from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create vegetation layer spawner world_center = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path) + pink_flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel") + pink_flower_prefab = dynveg.create_temp_mesh_prefab(pink_flower_asset_path, "RangeDespawn_PinkFlower")[0] + spawner_entity = dynveg.create_temp_prefab_vegetation_area("Instance Spawner", world_center, 16.0, 16.0, 16.0, + pink_flower_prefab) # Create a surface to spawn on dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py index 06c9c5f615..d508e390cb 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py @@ -7,21 +7,171 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import os import pytest -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') -from base import TestAutomationBase +import ly_test_tools.environment.file_system as file_system +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_main @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): +class TestAutomation(EditorTestSuite): - def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, workspace, editor, launcher_platform): + # Helpers for test asset cleanup + def cleanup_test_level(self, workspace): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) + + class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorSharedTest): + from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module + + class test_AltitudeFilter_FilterStageToggle(EditorSharedTest): + from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module + + class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorSharedTest): + from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module + + class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorSharedTest): + from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module + + class test_AssetWeightSelector_InstancesExpressBasedOnWeight(EditorSharedTest): + from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") + class test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(EditorSharedTest): + from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") + class test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(EditorSharedTest): + from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module + + class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorSharedTest): from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, workspace, editor, launcher_platform): + class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorSharedTest): from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + + class test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(EditorSharedTest): + from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module + + class test_LayerBlender_E2E_Editor(EditorSingleTest): + from .EditorScripts import LayerBlender_E2E_Editor as test_module + + # Custom setup/teardown to remove test level created during test + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + class test_LayerBlocker_InstancesBlockedInConfiguredArea(EditorSharedTest): + from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module + + class test_LayerSpawner_FilterStageToggle(EditorSharedTest): + from .EditorScripts import LayerSpawner_FilterStageToggle as test_module + + class test_LayerSpawner_InheritBehaviorFlag(EditorSharedTest): + from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module + + class test_LayerSpawner_InstancesPlantInAllSupportedShapes(EditorSharedTest): + from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/6549") + class test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(EditorSharedTest): + from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module + + class test_MeshBlocker_InstancesBlockedByMesh(EditorSharedTest): + from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module + + class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorSharedTest): + from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module + + class test_MeshSurfaceTagEmitter_DependentOnMeshComponent(EditorSharedTest): + from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module + + class test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest): + from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module + + class test_PhysXColliderSurfaceTagEmitter_E2E_Editor(EditorSharedTest): + from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module + + class test_PositionModifier_AutoSnapToSurfaceWorks(EditorSharedTest): + from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module + + class test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(EditorSharedTest): + from .EditorScripts import \ + PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module + + class test_PrefabInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest): + from .EditorScripts import PrefabInstanceSpawner_Embedded_E2E as test_module + + # Custom setup/teardown to remove test level created during test + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + class test_PrefabInstanceSpawner_External_E2E_Editor(EditorSingleTest): + from .EditorScripts import PrefabInstanceSpawner_External_E2E as test_module + + # Custom setup/teardown to remove test level created during test + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + TestAutomation.cleanup_test_level(self, workspace) + + class test_RotationModifier_InstancesRotateWithinRange(EditorSharedTest): + from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module + + class test_RotationModifierOverrides_InstancesRotateWithinRange(EditorSharedTest): + from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module + + class test_ScaleModifier_InstancesProperlyScale(EditorSharedTest): + from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module + + class test_ScaleModifierOverrides_InstancesProperlyScale(EditorSharedTest): + from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module + + class test_ShapeIntersectionFilter_FilterStageToggle(EditorSharedTest): + from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module + + class test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(EditorSharedTest): + from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module + + class test_SlopeAlignmentModifier_InstanceSurfaceAlignment(EditorSharedTest): + from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module + + class test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(EditorSharedTest): + from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module + + class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorSharedTest): + from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module + + class test_SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks(EditorSharedTest): + from .EditorScripts import SpawnerPrefabs_PrefabCreationAndVisibilityToggleWorks as test_module + + class test_SurfaceDataRefreshes_RemainsStable(EditorSharedTest): + from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module + + class test_SurfaceMaskFilter_BasicSurfaceTagCreation(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module + + class test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module + + class test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module + + class test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(EditorSharedTest): + from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module + + class test_SystemSettings_SectorPointDensity(EditorSharedTest): + from .EditorScripts import SystemSettings_SectorPointDensity as test_module + + class test_SystemSettings_SectorSize(EditorSharedTest): + from .EditorScripts import SystemSettings_SectorSize as test_module + + class test_VegetationInstances_DespawnWhenOutOfRange(EditorSharedTest): + from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py deleted file mode 100644 index 5b1e504442..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py +++ /dev/null @@ -1,192 +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 -""" - -import os -import pytest - -import ly_test_tools.environment.file_system as file_system -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(EditorTestSuite): - class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest): - from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module - - class test_AltitudeFilter_FilterStageToggle(EditorParallelTest): - from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module - - class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest): - from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation_PrefabNotEnabled(EditorTestSuite): - - enable_prefab_system = False - - # Helpers for test asset cleanup - def cleanup_test_level(self, workspace): - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], - True, True) - - def cleanup_test_slices(self, workspace): - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices", - "TestSlice_1.slice")], True, True) - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices", - "TestSlice_2.slice")], True, True) - - class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorParallelTest): - from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module - - class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorParallelTest): - from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module - - class test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(EditorSingleTest): - # Custom teardown to remove slice asset created during test - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_slices(self, workspace) - from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module - - class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorParallelTest): - from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module - - class test_AssetWeightSelector_InstancesExpressBasedOnWeight(EditorParallelTest): - from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module - - @pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155") - class test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(EditorParallelTest): - from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module - - @pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155") - class test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(EditorParallelTest): - from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module - - class test_SurfaceDataRefreshes_RemainsStable(EditorParallelTest): - from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module - - class test_VegetationInstances_DespawnWhenOutOfRange(EditorParallelTest): - from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module - - class test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(EditorParallelTest): - from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module - - class test_LayerBlocker_InstancesBlockedInConfiguredArea(EditorParallelTest): - from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module - - class test_LayerSpawner_InheritBehaviorFlag(EditorParallelTest): - from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module - - class test_LayerSpawner_InstancesPlantInAllSupportedShapes(EditorParallelTest): - from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module - - class test_LayerSpawner_FilterStageToggle(EditorParallelTest): - from .EditorScripts import LayerSpawner_FilterStageToggle as test_module - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038") - class test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(EditorParallelTest): - from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module - - class test_MeshBlocker_InstancesBlockedByMesh(EditorParallelTest): - from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module - - class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorParallelTest): - from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module - - class test_MeshSurfaceTagEmitter_DependentOnMeshComponent(EditorParallelTest): - from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module - - class test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorParallelTest): - from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - - class test_PhysXColliderSurfaceTagEmitter_E2E_Editor(EditorParallelTest): - from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module - - class test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(EditorParallelTest): - from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module - - class test_PositionModifier_AutoSnapToSurfaceWorks(EditorParallelTest): - from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module - - class test_RotationModifier_InstancesRotateWithinRange(EditorParallelTest): - from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module - - class test_RotationModifierOverrides_InstancesRotateWithinRange(EditorParallelTest): - from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module - - class test_ScaleModifier_InstancesProperlyScale(EditorParallelTest): - from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module - - class test_ScaleModifierOverrides_InstancesProperlyScale(EditorParallelTest): - from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module - - class test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(EditorParallelTest): - from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module - - class test_ShapeIntersectionFilter_FilterStageToggle(EditorParallelTest): - from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module - - class test_SlopeAlignmentModifier_InstanceSurfaceAlignment(EditorParallelTest): - from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module - - class test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(EditorParallelTest): - from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module - - class test_SurfaceMaskFilter_BasicSurfaceTagCreation(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module - - class test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module - - class test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module - - class test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(EditorParallelTest): - from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module - - class test_SystemSettings_SectorPointDensity(EditorParallelTest): - from .EditorScripts import SystemSettings_SectorPointDensity as test_module - - class test_SystemSettings_SectorSize(EditorParallelTest): - from .EditorScripts import SystemSettings_SectorSize as test_module - - class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorParallelTest): - from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module - - class test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest): - from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module - - # Custom setup/teardown to remove test level created during test - def setup(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest): - from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module - - # Custom setup/teardown to remove test level created during test - def setup(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - class test_LayerBlender_E2E_Editor(EditorSingleTest): - from .EditorScripts import LayerBlender_E2E_Editor as test_module - - # Custom setup/teardown to remove test level created during test - def setup(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) - - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py index 64f6cfdf30..bf7e76f973 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py @@ -7,277 +7,17 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import os import pytest -import sys -import ly_test_tools.environment.waiter as waiter import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra -from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole - -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') -from base import TestAutomationBase - - -@pytest.fixture -def remove_test_slice(request, workspace, project): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, - True) - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, - True) - - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, - True) - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, - True) - request.addfinalizer(teardown) - - -@pytest.fixture -def remote_console_instance(request): - console = RemoteConsole() - - def teardown(): - if console.connected: - console.stop() - - request.addfinalizer(teardown) - return console +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): - - def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module - self._run_test(request, workspace, editor, test_module) - - def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module - self._run_test(request, workspace, editor, test_module) - - def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module - self._run_test(request, workspace, editor, test_module) - - def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform): - from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") - def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform): - from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") - def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform): - from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceDataRefreshes_RemainsStable(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_VegetationInstances_DespawnWhenOutOfRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, workspace, editor, launcher_platform): - from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerSpawner_InheritBehaviorFlag(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_LayerSpawner_FilterStageToggle(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_FilterStageToggle as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038") - def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, workspace, editor, launcher_platform): - from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshBlocker_InstancesBlockedByMesh(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): - from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, workspace, editor, launcher_platform): - from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, workspace, editor, launcher_platform): - from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, workspace, editor, launcher_platform): - from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_RotationModifier_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ScaleModifier_InstancesProperlyScale(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_ShapeIntersectionFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SystemSettings_SectorPointDensity(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SystemSettings_SectorPointDensity as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SystemSettings_SectorSize(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SystemSettings_SectorSize as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, workspace, editor, launcher_platform): - from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - -@pytest.mark.SUITE_periodic -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -class TestAutomationE2E(TestAutomationBase): - - # The following tests must run in order, please do not move tests out of order - - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): - # Ensure our test level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - expected_lines = [ - "Instances found in area = 400" - ] - - hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_External_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): - # Ensure our test level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - expected_lines = [ - "Instances found in area = 400" - ] - - hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_LayerBlender_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): - # Ensure our test level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from .EditorScripts import LayerBlender_E2E_Editor as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - @pytest.mark.parametrize("launcher_platform", ['windows']) - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4170") - def test_LayerBlender_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - launcher.args.extend(["-rhi=Null"]) - launcher.start(launch_ap=False) - assert launcher.is_alive(), "Launcher failed to start" - - # Wait for test script to quit the launcher. If wait_for returns exc, test was not successful - waiter.wait_for(lambda: not launcher.is_alive(), timeout=300) +class TestAutomation(EditorTestSuite): - # Verify launcher quit successfully and did not crash - ret_code = launcher.get_returncode() - assert ret_code == 0, "Test failed. See Game.log for details" + global_extra_cmdline_args = ["-BatchMode", "-autotest_mode"] - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + class test_DynVegUtils_TempPrefabCreationWorks(EditorSharedTest): + from .EditorScripts import DynVegUtils_TempPrefabCreationWorks as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py deleted file mode 100644 index f87d6567ef..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py +++ /dev/null @@ -1,23 +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 -""" - -import os -import pytest - -import ly_test_tools.environment.file_system as file_system -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(EditorTestSuite): - - global_extra_cmdline_args = ["-BatchMode", "-autotest_mode"] - - class test_DynVegUtils_TempPrefabCreationWorks(EditorSharedTest): - from .EditorScripts import DynVegUtils_TempPrefabCreationWorks as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py index 09cd760647..6979ff1dee 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py @@ -65,8 +65,7 @@ def GradientGenerators_Incompatibilities(): } # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # For every gradient generator component, verify that they are incompatible # which each vegetation area component diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py index a4d9181744..962a0004f9 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py @@ -74,8 +74,7 @@ def GradientModifiers_Incompatibilities(): } # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # For every gradient modifier component, verify that they are incompatible # which each vegetation area and gradient generator/modifier component diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py index 3749f93bf5..87f9e1a9b1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py @@ -86,8 +86,7 @@ def GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(): return entity # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create entity with Random Noise gradient and verify gradient position after clearing pinned entity clear_entityid_check_position( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py index 3756452710..cd3fd7e1ee 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py @@ -90,8 +90,7 @@ def GradientPreviewSettings_DefaultPinnedEntityIsSelf(): ] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() for param in param_list: execute_test(param.required_components[param.accessed_component], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py index 8e85345a6f..f2980d9634 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSampling_GradientReferencesAddRemoveSuccessfully.py @@ -61,8 +61,7 @@ def GradientSampling_GradientReferencesAddRemoveSuccessfully(): Report.result(gradient_cleared_from_modifier, entity.Equal(EntityId.EntityId())) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components "Random Noise Gradient", "Gradient Transform Modifier" and "Box Shape" entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py index f653515a39..0cc325efd1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py @@ -42,8 +42,7 @@ def GradientSurfaceTagEmitter_ComponentDependencies(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create an entity with Gradient Surface Tag Emitter component position = math.Vector3(512.0, 512.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py index 90840a135d..cf48096a31 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py @@ -36,8 +36,7 @@ def GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create an entity with Gradient Surface Tag Emitter and Reference Gradient components. entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py index 5e6df4d52f..27ef248d6c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithExpectedGradients.py @@ -46,8 +46,7 @@ def GradientTransform_ComponentIncompatibleWithExpectedGradients(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components Gradient Transform Modifier and Box Shape entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py index fd65996fdd..9c23cdea75 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_ComponentIncompatibleWithSpawners.py @@ -44,8 +44,7 @@ def GradientTransform_ComponentIncompatibleWithSpawners(): return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create a new entity with components Gradient Transform Modifier and Box Shape entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py index 530503db86..6d6911ff3a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange.py @@ -54,8 +54,7 @@ def GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create entity entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py index 09cce359d5..ec142f92d2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py @@ -47,8 +47,7 @@ def GradientTransform_RequiresShape(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create a new Entity in the level entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId()) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py index 42458665e1..afe0893da9 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ProcessedImageAssignedSuccessfully.py @@ -56,8 +56,7 @@ def ImageGradient_ProcessedImageAssignedSuccessfully(): from editor_python_test_tools.utils import TestHelper as helper # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create an entity with Image Gradient and Gradient Transform Modifier components components_to_add = ["Image Gradient", "Gradient Transform Modifier", "Box Shape"] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py index 3b832d160a..85582d198a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py @@ -47,8 +47,7 @@ def ImageGradient_RequiresShape(): from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Create a new Entity in the level entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId()) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py index 3ad27eb973..d4199c31f6 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py @@ -5,67 +5,51 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os import pytest -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') -from base import TestAutomationBase +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): +class TestAutomation(EditorTestSuite): - def test_GradientGenerators_Incompatibilities(self, request, workspace, editor, launcher_platform): + class test_GradientGenerators_Incompatibilities(EditorSharedTest): from .EditorScripts import GradientGenerators_Incompatibilities as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientModifiers_Incompatibilities(self, request, workspace, editor, launcher_platform): + class test_GradientModifiers_Incompatibilities(EditorSharedTest): from .EditorScripts import GradientModifiers_Incompatibilities as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(self, request, workspace, editor, launcher_platform): + class test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(EditorSharedTest): from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientSampling_GradientReferencesAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): + class test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(EditorSharedTest): + from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module + + class test_GradientSampling_GradientReferencesAddRemoveSuccessfully(EditorSharedTest): from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientSurfaceTagEmitter_ComponentDependencies(self, request, workspace, editor, launcher_platform): + class test_GradientSurfaceTagEmitter_ComponentDependencies(EditorSharedTest): from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): + class test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest): from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientTransform_RequiresShape(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientTransform_RequiresShape as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - - def test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + class test_GradientTransform_ComponentIncompatibleWithExpectedGradients(EditorSharedTest): + from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module - def test_GradientTransform_ComponentIncompatibleWithSpawners(self, request, workspace, editor, launcher_platform): + class test_GradientTransform_ComponentIncompatibleWithSpawners(EditorSharedTest): from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) - def test_GradientTransform_ComponentIncompatibleWithExpectedGradients(self, request, workspace, editor, launcher_platform): - from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + class test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(EditorSharedTest): + from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module - def test_ImageGradient_RequiresShape(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ImageGradient_RequiresShape as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + class test_GradientTransform_RequiresShape(EditorSharedTest): + from .EditorScripts import GradientTransform_RequiresShape as test_module - def test_ImageGradient_ProcessedImageAssignedSuccessfully(self, request, workspace, editor, launcher_platform): + class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest): from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) + + class test_ImageGradient_RequiresShape(EditorSharedTest): + from .EditorScripts import ImageGradient_RequiresShape as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py deleted file mode 100644 index 6ac0658b4d..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py +++ /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 -""" - -import pytest - -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_periodic -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(EditorTestSuite): - - enable_prefab_system = False - - class test_GradientGenerators_Incompatibilities(EditorSharedTest): - from .EditorScripts import GradientGenerators_Incompatibilities as test_module - - class test_GradientModifiers_Incompatibilities(EditorSharedTest): - from .EditorScripts import GradientModifiers_Incompatibilities as test_module - - class test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(EditorSharedTest): - from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module - - class test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(EditorSharedTest): - from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module - - class test_GradientSampling_GradientReferencesAddRemoveSuccessfully(EditorSharedTest): - from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module - - class test_GradientSurfaceTagEmitter_ComponentDependencies(EditorSharedTest): - from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module - - class test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest): - from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module - - class test_GradientTransform_RequiresShape(EditorSharedTest): - from .EditorScripts import GradientTransform_RequiresShape as test_module - - class test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(EditorSharedTest): - from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module - - class test_GradientTransform_ComponentIncompatibleWithSpawners(EditorSharedTest): - from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module - - class test_GradientTransform_ComponentIncompatibleWithExpectedGradients(EditorSharedTest): - from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module - - class test_ImageGradient_RequiresShape(EditorSharedTest): - from .EditorScripts import ImageGradient_RequiresShape as test_module - - class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest): - from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py index c69ce77041..8151299cea 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py @@ -69,7 +69,7 @@ def AreaNodes_DependentComponentsAdded(): # Open an existing simple level helper.init_idle() - helper.open_level("Physics", "Base") + helper.open_level("", "Base") # Open Landscape Canvas tool and verify general.open_pane('Landscape Canvas') 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 402df133cb..e501b3ad3b 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/TestSuite_Main_Optimized.py @@ -12,6 +12,13 @@ import ly_test_tools.environment.file_system as file_system import ly_test_tools._internal.pytest_plugin as internal_plugin from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite +@pytest.mark.SUITE_periodic +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomationWithPrefabSystemEnabled(EditorTestSuite): + + class test_LandscapeCanvas_AreaNodes_DependentComponentsAdded(EditorSharedTest): + from .EditorScripts import AreaNodes_DependentComponentsAdded as test_module @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @@ -26,9 +33,6 @@ class TestAutomation(EditorTestSuite): class test_LandscapeCanvas_GradientMixer_NodeConstruction(EditorSharedTest): from .EditorScripts import GradientMixer_NodeConstruction as test_module - class test_LandscapeCanvas_AreaNodes_DependentComponentsAdded(EditorSharedTest): - from .EditorScripts import AreaNodes_DependentComponentsAdded as test_module - class test_LandscapeCanvas_AreaNodes_EntityCreatedOnNodeAdd(EditorSharedTest): from .EditorScripts import AreaNodes_EntityCreatedOnNodeAdd as test_module 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 37afeff8c0..fd6a907b2e 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 @@ -114,7 +114,8 @@ def create_dynamic_slice_vegetation_area(name, center_point, box_size_x, box_siz return spawner_entity -def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, target_prefab): +def create_temp_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, target_prefab): + """Creates a vegetation area using in-memory prefabs. Use when test is solely contained to Editor""" # Create a vegetation area entity to use as our test vegetation spawner spawner_entity = hydra.Entity(name) spawner_entity.create_entity( @@ -148,6 +149,31 @@ def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, bo return spawner_entity +def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, prefab_path): + """Creates a vegetation area using on-disk spawnable prefabs. Use when test requires saving/loading in Launcher""" + # Create a vegetation area entity to use as our test vegetation spawner + prefab_asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", prefab_path, math.Uuid(), + False) + spawner_entity = hydra.Entity(name) + spawner_entity.create_entity( + center_point, + ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] + ) + if spawner_entity.id.IsValid(): + print(f"'{spawner_entity.name}' created") + spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(box_size_x, box_size_y, + box_size_z)) + + # Set the vegetation area to a prefab instance spawner with a specific prefab asset selected + descriptor = hydra.get_component_property_value(spawner_entity.components[2], + 'Configuration|Embedded Assets|[0]') + prefab_spawner = vegetation.PrefabInstanceSpawner() + prefab_spawner.SetPrefabAssetId(prefab_asset_id) + descriptor.spawner = prefab_spawner + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) + return spawner_entity + + def create_blocker_area(name, center_point, box_size_x, box_size_y, box_size_z): # Create a Vegetation Layer Blocker area blocker_entity = hydra.Entity(name) @@ -162,6 +188,23 @@ def create_blocker_area(name, center_point, box_size_x, box_size_y, box_size_z): return blocker_entity +def create_empty_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z): + # Create a vegetation area entity to use as our test vegetation spawner + spawner_entity = EditorEntity.create_editor_entity_at(center_point, name=name) + spawner_entity.add_components(["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]) + if spawner_entity.id.IsValid(): + print(f"'{spawner_entity.get_name()}' created") + spawner_entity.components[1].set_component_property_value("Box Shape|Box Configuration|Dimensions", + math.Vector3(box_size_x, box_size_y, box_size_z)) + + # Set the vegetation area to an empty spawner + empty_spawner = vegetation.EmptyInstanceSpawner() + descriptor = spawner_entity.components[2].get_component_property_value("Configuration|Embedded Assets|[0]") + descriptor.spawner = empty_spawner + spawner_entity.components[2].set_component_property_value("Configuration|Embedded Assets|[0]", descriptor) + return spawner_entity + + def validate_instance_count(center, radius, num_expected): # Verify that we have the correct number of instances in the given area. This creates a bounding box based on a # sphere radius diff --git a/AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab b/AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab new file mode 100644 index 0000000000..f7e42e7731 --- /dev/null +++ b/AutomatedTesting/Levels/Graphics/base_empty/base_empty.prefab @@ -0,0 +1,53 @@ +{ + "ContainerEntity": { + "Id": "Entity_[1146574390643]", + "Name": "Level", + "Components": { + "Component_[10641544592923449938]": { + "$type": "EditorInspectorComponent", + "Id": 10641544592923449938 + }, + "Component_[12039882709170782873]": { + "$type": "EditorOnlyEntityComponent", + "Id": 12039882709170782873 + }, + "Component_[12265484671603697631]": { + "$type": "EditorPendingCompositionComponent", + "Id": 12265484671603697631 + }, + "Component_[14126657869720434043]": { + "$type": "EditorEntitySortComponent", + "Id": 14126657869720434043 + }, + "Component_[15230859088967841193]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 15230859088967841193, + "Parent Entity": "" + }, + "Component_[16239496886950819870]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 16239496886950819870 + }, + "Component_[5688118765544765547]": { + "$type": "EditorEntityIconComponent", + "Id": 5688118765544765547 + }, + "Component_[6545738857812235305]": { + "$type": "SelectionComponent", + "Id": 6545738857812235305 + }, + "Component_[7247035804068349658]": { + "$type": "EditorPrefabComponent", + "Id": 7247035804068349658 + }, + "Component_[9307224322037797205]": { + "$type": "EditorLockComponent", + "Id": 9307224322037797205 + }, + "Component_[9562516168917670048]": { + "$type": "EditorVisibilityComponent", + "Id": 9562516168917670048 + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Levels/Graphics/base_empty/tags.txt b/AutomatedTesting/Levels/Graphics/base_empty/tags.txt new file mode 100644 index 0000000000..0d6c1880e7 --- /dev/null +++ b/AutomatedTesting/Levels/Graphics/base_empty/tags.txt @@ -0,0 +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 diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNoParamsNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomousNotifyEvent.names diff --git a/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names new file mode 100644 index 0000000000..7255c61d23 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToAutonomous_PlayerNumberNotifyEvent.names @@ -0,0 +1,56 @@ +{ + "entries": [ + { + "base": "AuthorityToAutonomous_PlayerNumber Notify Event", + "context": "AZEventHandler", + "variant": "", + "details": { + "name": "Authority To Autonomous_ Player Number Notify Event" + }, + "slots": [ + { + "base": "player_number", + "details": { + "name": "player_number" + } + }, + { + "base": "AuthorityToAutonomous_PlayerNumber Notify Event", + "details": { + "name": "AuthorityToAutonomous_PlayerNumber Notify Event" + } + }, + { + "base": "Connect", + "details": { + "name": "Connect" + } + }, + { + "base": "Disconnect", + "details": { + "name": "Disconnect" + } + }, + { + "base": "On Connected", + "details": { + "name": "On Connected" + } + }, + { + "base": "On Disconnected", + "details": { + "name": "On Disconnected" + } + }, + { + "base": "OnEvent", + "details": { + "name": "OnEvent" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParamsNotifyEvent.names diff --git a/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names new file mode 100644 index 0000000000..3e0207a705 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNoParams_PlayFxNotifyEvent.names @@ -0,0 +1,50 @@ +{ + "entries": [ + { + "base": "AuthorityToClientNoParams_PlayFx Notify Event", + "context": "AZEventHandler", + "variant": "", + "details": { + "name": "Authority To Client No Params_ Play Fx Notify Event" + }, + "slots": [ + { + "base": "AuthorityToClientNoParams_PlayFx Notify Event", + "details": { + "name": "AuthorityToClientNoParams_PlayFx Notify Event" + } + }, + { + "base": "Connect", + "details": { + "name": "Connect" + } + }, + { + "base": "Disconnect", + "details": { + "name": "Disconnect" + } + }, + { + "base": "On Connected", + "details": { + "name": "On Connected" + } + }, + { + "base": "On Disconnected", + "details": { + "name": "On Disconnected" + } + }, + { + "base": "OnEvent", + "details": { + "name": "OnEvent" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AuthorityToClientNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNoParamsNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/AutonomousToAuthorityNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNoParamNotifyEvent.names diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthorityNotifyEvent.names diff --git a/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names new file mode 100644 index 0000000000..2505d9593a --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/AZEvents/ServerToAuthority_DealDamageNotifyEvent.names @@ -0,0 +1,56 @@ +{ + "entries": [ + { + "base": "ServerToAuthority_DealDamage Notify Event", + "context": "AZEventHandler", + "variant": "", + "details": { + "name": "Server To Authority_ Deal Damage Notify Event" + }, + "slots": [ + { + "base": "damage", + "details": { + "name": "damage" + } + }, + { + "base": "ServerToAuthority_DealDamage Notify Event", + "details": { + "name": "ServerToAuthority_DealDamage Notify Event" + } + }, + { + "base": "Connect", + "details": { + "name": "Connect" + } + }, + { + "base": "Disconnect", + "details": { + "name": "Disconnect" + } + }, + { + "base": "On Connected", + "details": { + "name": "On Connected" + } + }, + { + "base": "On Disconnected", + "details": { + "name": "On Disconnected" + } + }, + { + "base": "OnEvent", + "details": { + "name": "OnEvent" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponent.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponent.names similarity index 90% rename from Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponent.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponent.names index 22636e0453..03e949f784 100644 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponent.names +++ b/AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponent.names @@ -9,76 +9,72 @@ }, "methods": [ { - "base": "AutonomousToAuthority", + "base": "AutonomousToAuthorityByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority" + "tooltip": "When signaled, this will invoke Autonomous To Authority By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority is invoked" + "tooltip": "Signaled after Autonomous To Authority By Entity Id is invoked" }, "details": { - "name": "Autonomous To Authority" + "name": "Autonomous To Authority By Entity Id" }, "params": [ { - "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", + "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", "details": { - "name": "Network Test Player Component" + "name": "Source", + "tooltip": "The Source containing the NetworkTestPlayerComponentController" } }, { "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", "details": { - "name": "float" + "name": "some Float" } } ] }, { - "base": "ServerToAuthority", + "base": "AuthorityToClientNoParams_PlayFxByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Server To Authority" + "tooltip": "When signaled, this will invoke Authority To Client No Params_ Play Fx By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Server To Authority is invoked" + "tooltip": "Signaled after Authority To Client No Params_ Play Fx By Entity Id is invoked" }, "details": { - "name": "Server To Authority" + "name": "Authority To Client No Params_ Play Fx By Entity Id" }, "params": [ { - "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", - "details": { - "name": "Network Test Player Component" - } - }, - { - "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", "details": { - "name": "float" + "name": "Source", + "tooltip": "The Source containing the NetworkTestPlayerComponentController" } } ] }, { - "base": "AutonomousToAuthorityByEntityId", + "base": "AuthorityToAutonomous_PlayerNumberByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority By Entity Id" + "tooltip": "When signaled, this will invoke Authority To Autonomous_ Player Number By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority By Entity Id is invoked" + "tooltip": "Signaled after Authority To Autonomous_ Player Number By Entity Id is invoked" }, "details": { - "name": "Autonomous To Authority By Entity Id" + "name": "Authority To Autonomous_ Player Number By Entity Id" }, "params": [ { @@ -89,26 +85,26 @@ } }, { - "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "typeid": "{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}", "details": { - "name": "some Float" + "name": "player_number" } } ] }, { - "base": "ServerToAuthorityByEntityId", + "base": "ServerToAuthority_DealDamageByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Server To Authority By Entity Id" + "tooltip": "When signaled, this will invoke Server To Authority_ Deal Damage By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Server To Authority By Entity Id is invoked" + "tooltip": "Signaled after Server To Authority_ Deal Damage By Entity Id is invoked" }, "details": { - "name": "Server To Authority By Entity Id" + "name": "Server To Authority_ Deal Damage By Entity Id" }, "params": [ { @@ -121,24 +117,48 @@ { "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", "details": { - "name": "some Float" + "name": "damage" } } ] }, { - "base": "AutonomousToAuthorityNoParams", + "base": "AutonomousToAuthorityNoParamsByEntityId", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority No Params" + "tooltip": "When signaled, this will invoke Autonomous To Authority No Params By Entity Id" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority No Params is invoked" + "tooltip": "Signaled after Autonomous To Authority No Params By Entity Id is invoked" }, "details": { - "name": "Autonomous To Authority No Params" + "name": "Autonomous To Authority No Params By Entity Id" + }, + "params": [ + { + "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", + "details": { + "name": "Source", + "tooltip": "The Source containing the NetworkTestPlayerComponentController" + } + } + ] + }, + { + "base": "ServerToAuthority_DealDamage", + "context": "NetworkTestPlayerComponent", + "entry": { + "name": "In", + "tooltip": "When signaled, this will invoke Server To Authority_ Deal Damage" + }, + "exit": { + "name": "Out", + "tooltip": "Signaled after Server To Authority_ Deal Damage is invoked" + }, + "details": { + "name": "Server To Authority_ Deal Damage" }, "params": [ { @@ -146,22 +166,28 @@ "details": { "name": "Network Test Player Component" } + }, + { + "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "details": { + "name": "float" + } } ] }, { - "base": "AuthorityToAutonomous", + "base": "AutonomousToAuthority", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Authority To Autonomous" + "tooltip": "When signaled, this will invoke Autonomous To Authority" }, "exit": { "name": "Out", - "tooltip": "Signaled after Authority To Autonomous is invoked" + "tooltip": "Signaled after Autonomous To Authority is invoked" }, "details": { - "name": "Authority To Autonomous" + "name": "Autonomous To Authority" }, "params": [ { @@ -179,18 +205,18 @@ ] }, { - "base": "AuthorityToClientNoParams", + "base": "AuthorityToAutonomous_PlayerNumber", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Authority To Client No Params" + "tooltip": "When signaled, this will invoke Authority To Autonomous_ Player Number" }, "exit": { "name": "Out", - "tooltip": "Signaled after Authority To Client No Params is invoked" + "tooltip": "Signaled after Authority To Autonomous_ Player Number is invoked" }, "details": { - "name": "Authority To Client No Params" + "name": "Authority To Autonomous_ Player Number" }, "params": [ { @@ -198,6 +224,12 @@ "details": { "name": "Network Test Player Component" } + }, + { + "typeid": "{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}", + "details": { + "name": "int" + } } ] }, @@ -308,79 +340,47 @@ ] }, { - "base": "AuthorityToClientNoParamsByEntityId", - "context": "NetworkTestPlayerComponent", - "entry": { - "name": "In", - "tooltip": "When signaled, this will invoke Authority To Client No Params By Entity Id" - }, - "exit": { - "name": "Out", - "tooltip": "Signaled after Authority To Client No Params By Entity Id is invoked" - }, - "details": { - "name": "Authority To Client No Params By Entity Id" - }, - "params": [ - { - "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", - "details": { - "name": "Source", - "tooltip": "The Source containing the NetworkTestPlayerComponentController" - } - } - ] - }, - { - "base": "AuthorityToAutonomousByEntityId", + "base": "AutonomousToAuthorityNoParams", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Authority To Autonomous By Entity Id" + "tooltip": "When signaled, this will invoke Autonomous To Authority No Params" }, "exit": { "name": "Out", - "tooltip": "Signaled after Authority To Autonomous By Entity Id is invoked" + "tooltip": "Signaled after Autonomous To Authority No Params is invoked" }, "details": { - "name": "Authority To Autonomous By Entity Id" + "name": "Autonomous To Authority No Params" }, "params": [ { - "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", - "details": { - "name": "Source", - "tooltip": "The Source containing the NetworkTestPlayerComponentController" - } - }, - { - "typeid": "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}", + "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", "details": { - "name": "some Float" + "name": "Network Test Player Component" } } ] }, { - "base": "AutonomousToAuthorityNoParamsByEntityId", + "base": "AuthorityToClientNoParams_PlayFx", "context": "NetworkTestPlayerComponent", "entry": { "name": "In", - "tooltip": "When signaled, this will invoke Autonomous To Authority No Params By Entity Id" + "tooltip": "When signaled, this will invoke Authority To Client No Params_ Play Fx" }, "exit": { "name": "Out", - "tooltip": "Signaled after Autonomous To Authority No Params By Entity Id is invoked" + "tooltip": "Signaled after Authority To Client No Params_ Play Fx is invoked" }, "details": { - "name": "Autonomous To Authority No Params By Entity Id" + "name": "Authority To Client No Params_ Play Fx" }, "params": [ { - "typeid": "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}", + "typeid": "{CA5E5C37-98A6-04D2-E15C-1B4BFEE4C7DD}", "details": { - "name": "Source", - "tooltip": "The Source containing the NetworkTestPlayerComponentController" + "name": "Network Test Player Component" } } ] diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names b/AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names similarity index 100% rename from Gems/ScriptCanvas/Assets/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names rename to AutomatedTesting/ScriptCanvas/TranslationAssets/Classes/NetworkTestPlayerComponentNetworkInput.names diff --git a/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas b/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas index 0e5524273c..4ef5f0a8d5 100644 --- a/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas +++ b/AutomatedTesting/ScriptCanvas/instance_counter.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 3744110453276 + "id": 337666590525276931 }, "Name": "instance_counter", "Components": { @@ -87,7 +87,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 3 }, @@ -228,7 +227,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 1 }, @@ -237,7 +235,7 @@ "value": { "id": 2901262558 }, - "label": "Source" + "label": "EntityID: 0" } ], "methodType": 0, @@ -340,7 +338,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 3 }, @@ -587,26 +584,10 @@ "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}", "versionData": { "_grammarVersion": 1, - "_runtimeVersion": 1 + "_runtimeVersion": 1, + "_fileVersion": 1 }, "GraphCanvasData": [ - { - "Key": { - "id": 3744110453276 - }, - "Value": { - "ComponentData": { - "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { - "$type": "SceneComponentSaveData", - "ViewParams": { - "Scale": 0.9218543514249998, - "AnchorX": 50.98419189453125, - "AnchorY": -272.27728271484375 - } - } - } - } - }, { "Key": { "id": 3748405420572 @@ -757,6 +738,18 @@ } } } + }, + { + "Key": { + "id": 337666590525276931 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData" + } + } + } } ], "StatisticsHelper": { diff --git a/CMakeLists.txt b/CMakeLists.txt index c81581fe0f..efcc866195 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,10 @@ endfunction() # Add the projects first so the Launcher can find them include(cmake/Projects.cmake) +if(LY_EXTERNAL_SUBDIRS) + set_property(GLOBAL APPEND PROPERTY LY_EXTERNAL_SUBDIRS ${LY_EXTERNAL_SUBDIRS}) +endif() + if(NOT INSTALLED_ENGINE) # Add external subdirectories listed in the engine.json. LY_EXTERNAL_SUBDIRS is a cache variable so the user can add extra # external subdirectories. This should go before adding the rest of the targets so the targets are availbe to the launcher. diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 9d74f8e3a2..3f340e37d8 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -104,13 +104,11 @@ ly_add_target( 3rdParty::Qt::Concurrent 3rdParty::TIFF 3rdParty::squish-ccr - 3rdParty::AWSNativeSDK::STS Legacy::CryCommon Legacy::EditorCommon AZ::AzCore AZ::AzToolsFramework Gem::LmbrCentral.Static - AZ::AWSNativeSDKInit AZ::AtomCore Gem::Atom_RPI.Edit Gem::Atom_RPI.Public @@ -119,7 +117,6 @@ ly_add_target( Gem::AtomViewportDisplayInfo ${additional_dependencies} PUBLIC - 3rdParty::AWSNativeSDK::Core 3rdParty::Qt::Network Legacy::EditorCore RUNTIME_DEPENDENCIES diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 3beaf4d438..805a22e880 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -36,14 +36,6 @@ AZ_POP_DISABLE_WARNING #include #include -// Aws Native SDK -#include -#include -#include -#include -#include -#include - // AzCore #include #include @@ -144,9 +136,7 @@ AZ_POP_DISABLE_WARNING #include "Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h" -// AWSNativeSDK #include -#include #if defined(AZ_PLATFORM_WINDOWS) @@ -2125,8 +2115,6 @@ 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(); @@ -3363,6 +3351,10 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostR return GetIEditor()->GetDocument(); } + bool usePrefabSystemForLevels = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); + // If we are loading and we're in simulate mode, then switch it off before we do anything else if (GetIEditor()->GetGameEngine() && GetIEditor()->GetGameEngine()->GetSimulationMode()) { @@ -3370,6 +3362,15 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostR bool bIsDocModified = GetIEditor()->GetDocument()->IsModified(); OnSwitchPhysics(); GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified); + + if (usePrefabSystemForLevels) + { + auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get(); + if (rootSpawnableInterface) + { + rootSpawnableInterface->ProcessSpawnableQueue(); + } + } } // We're about to start loading a level, so start recording errors to display at the end. diff --git a/Code/Editor/EditorPreferencesPageAWS.cpp b/Code/Editor/EditorPreferencesPageAWS.cpp index 9279dce7bc..968969245d 100644 --- a/Code/Editor/EditorPreferencesPageAWS.cpp +++ b/Code/Editor/EditorPreferencesPageAWS.cpp @@ -101,7 +101,7 @@ void CEditorPreferencesPage_AWS::SaveSettingsRegistryFile() return; } - bool saved{}; + [[maybe_unused]] 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)) diff --git a/Code/Editor/GameEngine.cpp b/Code/Editor/GameEngine.cpp index 9786dcf1b8..8aab168b6d 100644 --- a/Code/Editor/GameEngine.cpp +++ b/Code/Editor/GameEngine.cpp @@ -18,6 +18,7 @@ // AzCore #include #include +#include #include #include diff --git a/Code/Editor/IEditorImpl.cpp b/Code/Editor/IEditorImpl.cpp index 38006c6fca..a2712b653d 100644 --- a/Code/Editor/IEditorImpl.cpp +++ b/Code/Editor/IEditorImpl.cpp @@ -16,12 +16,6 @@ // Qt #include -// AWS Native SDK -AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option") -#include -#include -AZ_POP_DISABLE_WARNING - // AzCore #include #include @@ -78,9 +72,6 @@ AZ_POP_DISABLE_WARNING // EditorCommon #include -// AWSNativeSDK -#include - #include "Core/QtEditorApplication.h" // for Editor::EditorQtApplication static CCryEditDoc * theDocument; @@ -132,7 +123,6 @@ CEditorImpl::CEditorImpl() , m_pSettingsManager(nullptr) , m_pLevelIndependentFileMan(nullptr) , m_pExportManager(nullptr) - , m_awsResourceManager(nullptr) , m_bMatEditMode(false) , m_bShowStatusText(true) , m_bInitialized(false) diff --git a/Code/Editor/IEditorImpl.h b/Code/Editor/IEditorImpl.h index 7867912941..354f631507 100644 --- a/Code/Editor/IEditorImpl.h +++ b/Code/Editor/IEditorImpl.h @@ -369,7 +369,6 @@ protected: QString m_selectFileBuffer; QString m_levelNameBuffer; - IAWSResourceManager* m_awsResourceManager; std::unique_ptr m_winWidgetManager; //! True if the editor is in material edit mode. Fast preview of materials. diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index 67d89ad967..5009c626e2 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -47,12 +47,12 @@ namespace UnitTest void ViewportMouseCursorRequestImpl::BeginCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(true); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeCaptured); } void ViewportMouseCursorRequestImpl::EndCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(false); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); } bool ViewportMouseCursorRequestImpl::IsMouseOver() const diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp index bbebac96a3..63841299be 100644 --- a/Code/Editor/MainWindow.cpp +++ b/Code/Editor/MainWindow.cpp @@ -11,11 +11,6 @@ #include -// AWs Native SDK -AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING - // Qt #include #include diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp index 8561e6dba3..9514957795 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -1128,7 +1128,7 @@ void SEditorSettings::SaveSettingsRegistryFile() return; } - bool saved{}; + [[maybe_unused]] 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; diff --git a/Code/Editor/ViewPane.cpp b/Code/Editor/ViewPane.cpp index e3494a4049..90b44d9c9d 100644 --- a/Code/Editor/ViewPane.cpp +++ b/Code/Editor/ViewPane.cpp @@ -745,7 +745,7 @@ namespace void PySetActiveViewport(unsigned int viewportIndex) { - bool success = false; + [[maybe_unused]] bool success = false; CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); if (layout) { diff --git a/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h b/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h index 4b4ad572c2..de98ce97b9 100644 --- a/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h +++ b/Code/Framework/AtomCore/AtomCore/Instance/InstanceDatabase.h @@ -17,6 +17,11 @@ #include #include +namespace AZStd +{ + class any; +} + namespace AZ { namespace Data diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h index e4d2c7612e..697ca81a49 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp index 4794b04626..4303963aa8 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.cpp @@ -7,11 +7,62 @@ */ #include +#include +#include +#include +#include +#include + +#include +#include namespace AZ::Data { + namespace DataStreamInternal + { + struct AssetDataStreamPrivate + { + //! Optional data buffer that's been directly passed in through Open(), instead of reading data from a file. + AZStd::vector m_preloadedData; + //! The current active streamer read request - tracked in case we need to cancel it prematurely + AZ::IO::FileRequestPtr m_curReadRequest{ nullptr }; + + //! Synchronization for the read request, so that it's possible to block until completion. + AZStd::mutex m_readRequestMutex; + AZStd::condition_variable m_readRequestActive; + + void SetReadRequest(AZ::IO::FileRequestPtr&& req) + { + AZStd::scoped_lock lock(m_readRequestMutex); + // The read request finished, so stop tracking it. + m_curReadRequest = AZStd::move(req); + } + void BlockUntilReadComplete() + { + AZStd::unique_lock lock(m_readRequestMutex); + m_readRequestActive.wait( + lock, + [this] + { + return m_curReadRequest == nullptr; + }); + lock.unlock(); + } + void CancelRequest() + { + AZStd::scoped_lock lock(m_readRequestMutex); + if (m_curReadRequest) + { + auto streamer = Interface::Get(); + m_curReadRequest = streamer->Cancel(m_curReadRequest); + } + } + }; + } // namespace Internal + AssetDataStream::AssetDataStream(AZ::IO::IStreamerTypes::RequestMemoryAllocator* bufferAllocator) - : m_bufferAllocator(bufferAllocator ? bufferAllocator : &m_defaultAllocator) + : m_privateData(AZStd::make_unique()) + , m_bufferAllocator(bufferAllocator ? bufferAllocator : &m_defaultAllocator) { ClearInternalStateData(); } @@ -53,9 +104,9 @@ namespace AZ::Data OpenInternal(data.size(), "(mem buffer)"); // Directly take ownership of the provided buffer - m_preloadedData = AZStd::move(data); - m_buffer = m_preloadedData.data(); - m_loadedSize = m_preloadedData.size(); + m_privateData->m_preloadedData = AZStd::move(data); + m_buffer = m_privateData->m_preloadedData.data(); + m_loadedSize = m_privateData->m_preloadedData.size(); } void AssetDataStream::Open(const AZStd::string& filePath, size_t fileOffset, size_t assetSize, @@ -65,7 +116,7 @@ namespace AZ::Data AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(!m_isOpen, "Attempting to open the stream when it is already open."); - AZ_Assert(!m_curReadRequest, "Queueing an asset stream load while one is still in progress."); + AZ_Assert(!m_privateData->m_curReadRequest, "Queueing an asset stream load while one is still in progress."); AZ_Assert(!filePath.empty(), "AssetDataStream::Open called without a valid file name."); // Initialize the state variables and start tracking the overall load timings @@ -97,11 +148,8 @@ namespace AZ::Data "Buffer for %s was expected to be %zu bytes, but is %zu bytes.", m_filePath.c_str(), m_requestedAssetSize, m_loadedSize); - { - AZStd::scoped_lock lock(m_readRequestMutex); - // The read request finished, so stop tracking it. - m_curReadRequest = nullptr; - } + // The read request finished, so stop tracking it. + m_privateData->SetReadRequest(nullptr); // Call the load callback to start processing the loaded data. if (loadCallback) @@ -115,21 +163,22 @@ namespace AZ::Data } // Notify that the load is complete, in case anyone is using BlockUntilLoadComplete to block. - m_readRequestActive.notify_one(); + m_privateData->m_readRequestActive.notify_one(); }; // Queue the raw file load with the file streamer. auto streamer = AZ::Interface::Get(); - m_curReadRequest = streamer->Read( + m_privateData->m_curReadRequest = + streamer->Read( m_filePath, *m_bufferAllocator, m_requestedAssetSize, deadline, priority, m_fileOffset); m_curDeadline = deadline; m_curPriority = priority; - streamer->SetRequestCompleteCallback(m_curReadRequest, streamerCallback); + streamer->SetRequestCompleteCallback(m_privateData->m_curReadRequest, streamerCallback); - streamer->QueueRequest(m_curReadRequest); + streamer->QueueRequest(m_privateData->m_curReadRequest); } else { @@ -139,19 +188,19 @@ namespace AZ::Data loadCallback(AZ::IO::IStreamerTypes::RequestStatus::Completed); } - m_readRequestActive.notify_one(); + m_privateData->m_readRequestActive.notify_one(); } } void AssetDataStream::Reschedule(AZStd::chrono::milliseconds newDeadline, AZ::IO::IStreamerTypes::Priority newPriority) { - if (m_curReadRequest && (newDeadline < m_curDeadline || newPriority > m_curPriority)) + if (m_privateData->m_curReadRequest && (newDeadline < m_curDeadline || newPriority > m_curPriority)) { auto deadline = AZStd::GetMin(m_curDeadline, newDeadline); auto priority = AZStd::GetMax(m_curPriority, newPriority); auto streamer = Interface::Get(); - m_curReadRequest = streamer->RescheduleRequest(m_curReadRequest, deadline, priority); + m_privateData->m_curReadRequest = streamer->RescheduleRequest(m_privateData->m_curReadRequest, deadline, priority); m_curDeadline = deadline; m_curPriority = priority; } @@ -159,15 +208,13 @@ namespace AZ::Data void AssetDataStream::BlockUntilLoadComplete() { - AZStd::unique_lock lock(m_readRequestMutex); - m_readRequestActive.wait(lock, [this] { return m_curReadRequest == nullptr; }); - lock.unlock(); + m_privateData->BlockUntilReadComplete(); } void AssetDataStream::ClearInternalStateData() { // Clear all our internal state data. - m_preloadedData.resize(0); + m_privateData->m_preloadedData.resize(0); m_buffer = nullptr; m_loadedSize = 0; m_requestedAssetSize = 0; @@ -204,10 +251,10 @@ namespace AZ::Data void AssetDataStream::Close() { AZ_Assert(m_isOpen, "Attempting to close a stream that hasn't been opened."); - AZ_Assert(m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight."); + AZ_Assert(m_privateData->m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight."); // Destroy the asset buffer and unlock the allocator, so the allocator itself knows that it is no longer needed. - if (m_buffer != m_preloadedData.data()) + if (m_buffer != m_privateData->m_preloadedData.data()) { m_bufferAllocator->Release(m_buffer); } @@ -221,12 +268,7 @@ namespace AZ::Data void AssetDataStream::RequestCancel() { - AZStd::scoped_lock lock(m_readRequestMutex); - if (m_curReadRequest) - { - auto streamer = Interface::Get(); - m_curReadRequest = streamer->Cancel(m_curReadRequest); - } + m_privateData->CancelRequest(); } void AssetDataStream::Seek(AZ::IO::OffsetType bytes, AZ::IO::GenericStream::SeekMode mode) diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h index 79822c8db2..43a4516344 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h @@ -9,17 +9,26 @@ #include #include -#include -#include -#include -#include -#include +#include +#include + +namespace AZStd +{ + template + class vector; +} namespace AZ::Data { + namespace DataStreamInternal + { + struct AssetDataStreamPrivate; + } + class AssetDataStream : public AZ::IO::GenericStream { public: + using VectorDataSource = AZStd::vector; // The default Generic Stream APIs in this class will only allow for a single sequential pass // through the data, no seeking. Reads will block when pages aren't available yet, and // pages will be marked for recycling once reading has progressed beyond them. @@ -29,10 +38,10 @@ namespace AZ::Data ~AssetDataStream() override; // Open the AssetDataStream and make a copy of the provided memory buffer. - void Open(const AZStd::vector& data); + void Open(const VectorDataSource& data); // Open the AssetDataStream and directly take ownership of a pre-populated memory buffer. - void Open(AZStd::vector&& data); + void Open(VectorDataSource&& data); // Open the AssetDataStream and load it via file streaming using OnCompleteCallback = AZStd::function; @@ -91,6 +100,8 @@ namespace AZ::Data void ClearInternalStateData(); + AZStd::unique_ptr m_privateData; + //! The allocator to use for allocating / deallocating asset buffers AZ::IO::IStreamerTypes::RequestMemoryAllocator* m_bufferAllocator{ nullptr }; @@ -106,9 +117,6 @@ namespace AZ::Data //! The amount of data that's expected to be loaded. size_t m_requestedAssetSize{ 0 }; - //! Optional data buffer that's been directly passed in through Open(), instead of reading data from a file. - AZStd::vector m_preloadedData; - //! The buffer that will hold the raw data after it's loaded from the file. void* m_buffer{ nullptr }; @@ -119,19 +127,12 @@ namespace AZ::Data //! The current offset representing how far we've read into the buffer. size_t m_curOffset{ 0 }; - //! The current active streamer read request - tracked in case we need to cancel it prematurely - AZ::IO::FileRequestPtr m_curReadRequest{ nullptr }; - //! The current request deadline. Used to avoid requesting a reschedule to the same (current) deadline. AZStd::chrono::milliseconds m_curDeadline{ AZ::IO::IStreamerTypes::s_noDeadline }; //! The current request priority. Used to avoid requesting a reschedule to the same (current) priority. AZ::IO::IStreamerTypes::Priority m_curPriority{ AZ::IO::IStreamerTypes::s_priorityMedium }; - //! Synchronization for the read request, so that it's possible to block until completion. - AZStd::mutex m_readRequestMutex; - AZStd::condition_variable m_readRequestActive; - //! Track whether or not the stream is currently open bool m_isOpen{ false }; diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp index afe3330bd3..b4b1a0d81c 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.h b/Code/Framework/AzCore/AzCore/Asset/AssetManager.h index 9666d434c1..663de2c058 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include // used as allocator for most components #include diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp b/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp new file mode 100644 index 0000000000..bc50a8513c --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * 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::Dom +{ + PathEntry::PathEntry(size_t value) + : m_value(value) + { + } + + PathEntry::PathEntry(AZ::Name value) + : m_value(AZStd::move(value)) + { + } + + PathEntry::PathEntry(AZStd::string_view value) + : m_value(AZ::Name(value)) + { + } + + PathEntry& PathEntry::operator=(size_t value) + { + m_value = value; + return *this; + } + + PathEntry& PathEntry::operator=(AZ::Name value) + { + m_value = AZStd::move(value); + return *this; + } + + PathEntry& PathEntry::operator=(AZStd::string_view value) + { + m_value = AZ::Name(value); + return *this; + } + + bool PathEntry::operator==(const PathEntry& other) const + { + return m_value == other.m_value; + } + + bool PathEntry::operator==(size_t value) const + { + return IsIndex() && GetIndex() == value; + } + + bool PathEntry::operator==(const AZ::Name& key) const + { + return IsKey() && GetKey() == key; + } + + bool PathEntry::operator==(AZStd::string_view key) const + { + return IsKey() && GetKey() == AZ::Name(key); + } + + bool PathEntry::operator!=(const PathEntry& other) const + { + return m_value != other.m_value; + } + + bool PathEntry::operator!=(size_t value) const + { + return !IsIndex() || GetIndex() != value; + } + + bool PathEntry::operator!=(const AZ::Name& key) const + { + return !IsKey() || GetKey() != key; + } + + bool PathEntry::operator!=(AZStd::string_view key) const + { + return !IsKey() || GetKey() != AZ::Name(key); + } + + void PathEntry::SetEndOfArray() + { + m_value = EndOfArrayIndex; + } + + bool PathEntry::IsEndOfArray() const + { + const size_t* result = AZStd::get_if(&m_value); + return result == nullptr ? false : ((*result) == EndOfArrayIndex); + } + + bool PathEntry::IsIndex() const + { + const size_t* result = AZStd::get_if(&m_value); + return result == nullptr ? false : ((*result) != EndOfArrayIndex); + } + + bool PathEntry::IsKey() const + { + return AZStd::holds_alternative(m_value); + } + + size_t PathEntry::GetIndex() const + { + AZ_Assert(IsIndex(), "GetIndex called on PathEntry that is not an index"); + return AZStd::get(m_value); + } + + const AZ::Name& PathEntry::GetKey() const + { + AZ_Assert(IsKey(), "Key called on PathEntry that is not a key"); + return AZStd::get(m_value); + } + + Path::Path(AZStd::initializer_list init) + : m_entries(init) + { + } + + Path::Path(AZStd::string_view pathString) + { + FromString(pathString); + } + + Path Path::operator/(const PathEntry& entry) const + { + Path newPath(*this); + newPath /= entry; + return newPath; + } + + Path Path::operator/(size_t index) const + { + return *this / PathEntry(index); + } + + Path Path::operator/(AZ::Name key) const + { + return *this / PathEntry(key); + } + + Path Path::operator/(AZStd::string_view key) const + { + return *this / PathEntry(key); + } + + Path Path::operator/(const Path& other) const + { + Path newPath(*this); + newPath /= other; + return newPath; + } + + Path& Path::operator/=(const PathEntry& entry) + { + Push(entry); + return *this; + } + + Path& Path::operator/=(size_t index) + { + return *this /= PathEntry(index); + } + + Path& Path::operator/=(AZ::Name key) + { + return *this /= PathEntry(key); + } + + Path& Path::operator/=(AZStd::string_view key) + { + return *this /= PathEntry(key); + } + + Path& Path::operator/=(const Path& other) + { + for (const PathEntry& entry : other) + { + Push(entry); + } + return *this; + } + + bool Path::operator==(const Path& other) const + { + return m_entries == other.m_entries; + } + + const Path::ContainerType& Path::GetEntries() const + { + return m_entries; + } + + void Path::Push(PathEntry entry) + { + m_entries.push_back(AZStd::move(entry)); + } + + void Path::Push(size_t entry) + { + Push(PathEntry(entry)); + } + + void Path::Push(AZ::Name entry) + { + Push(PathEntry(AZStd::move(entry))); + } + + void Path::Push(AZStd::string_view entry) + { + Push(AZ::Name(entry)); + } + + void Path::Pop() + { + m_entries.pop_back(); + } + + void Path::Clear() + { + m_entries.clear(); + } + + PathEntry Path::At(size_t index) const + { + if (index < m_entries.size()) + { + return m_entries[index]; + } + return {}; + } + + size_t Path::Size() const + { + return m_entries.size(); + } + + PathEntry& Path::operator[](size_t index) + { + return m_entries[index]; + } + + const PathEntry& Path::operator[](size_t index) const + { + return m_entries[index]; + } + + Path::ContainerType::iterator Path::begin() + { + return m_entries.begin(); + } + + Path::ContainerType::iterator Path::end() + { + return m_entries.end(); + } + + Path::ContainerType::const_iterator Path::begin() const + { + return m_entries.cbegin(); + } + + Path::ContainerType::const_iterator Path::end() const + { + return m_entries.cend(); + } + + Path::ContainerType::const_iterator Path::cbegin() const + { + return m_entries.cbegin(); + } + + Path::ContainerType::const_iterator Path::cend() const + { + return m_entries.cend(); + } + + size_t Path::size() const + { + return m_entries.size(); + } + + size_t Path::GetStringLength() const + { + size_t size = 0; + for (const PathEntry& entry : m_entries) + { + ++size; + if (entry.IsEndOfArray()) + { + size += 1; + } + else if (entry.IsIndex()) + { + const size_t index = entry.GetIndex(); + const double digitCount = index > 0 ? log10(aznumeric_cast(index + 1)) : 1.0; + size += aznumeric_cast(ceil(digitCount)); + } + else + { + const char* nameBuffer = entry.GetKey().GetCStr(); + for (size_t i = 0; nameBuffer[i]; ++i) + { + if (nameBuffer[i] == EscapeCharacter || nameBuffer[i] == PathSeparator) + { + ++size; + } + ++size; + } + } + } + return size; + } + + void Path::FormatString(char* stringBuffer, size_t bufferSize) const + { + size_t bufferIndex = 0; + + auto putChar = [&](char c) + { + if (bufferIndex == bufferSize) + { + return; + } + stringBuffer[bufferIndex++] = c; + }; + + auto writeToBuffer = [&](const char* key) + { + for (size_t keyIndex = 0; key[keyIndex]; ++keyIndex) + { + const char c = key[keyIndex]; + if (c == EscapeCharacter) + { + putChar(EscapeCharacter); + putChar(TildeSequence); + } + else if (c == PathSeparator) + { + putChar(EscapeCharacter); + putChar(ForwardSlashSequence); + } + else + { + putChar(c); + } + } + }; + + for (const PathEntry& entry : m_entries) + { + putChar(PathSeparator); + if (entry.IsEndOfArray()) + { + putChar(EndOfArrayCharacter); + } + else if (entry.IsIndex()) + { + bufferIndex += azsnprintf(&stringBuffer[bufferIndex], bufferSize - bufferIndex, "%zu", entry.GetIndex()); + } + else + { + writeToBuffer(entry.GetKey().GetCStr()); + } + } + + putChar('\0'); + } + + AZStd::string Path::ToString() const + { + AZStd::string formattedString; + const size_t size = GetStringLength(); + formattedString.resize_no_construct(size); + FormatString(formattedString.data(), size + 1); + return formattedString; + } + + void Path::AppendToString(AZStd::string& output) const + { + const size_t startIndex = output.length(); + const size_t stringLength = GetStringLength(); + output.resize_no_construct(startIndex + stringLength); + FormatString(output.data() + startIndex, stringLength + 1); + } + + void Path::FromString(AZStd::string_view pathString) + { + m_entries.clear(); + if (pathString.empty()) + { + return; + } + + size_t pathEntryCount = 0; + for (size_t i = 1; i <= pathString.size(); ++i) + { + if (pathString[i] == PathSeparator) + { + ++pathEntryCount; + } + } + m_entries.reserve(pathEntryCount); + + // Ignore a preceeding path separator and start processing after it + size_t pathIndex = pathString[0] == PathSeparator ? 1 : 0; + bool isNumber = true; + AZStd::string convertedSection; + for (size_t i = pathIndex; i <= pathString.size(); ++i) + { + if (i == pathString.size() || pathString[i] == PathSeparator) + { + AZStd::string_view section = pathString.substr(pathIndex, i - pathIndex); + if (section.size() == 1 && section[0] == EndOfArrayCharacter) + { + PathEntry entry; + entry.SetEndOfArray(); + m_entries.push_back(AZStd::move(entry)); + } + else if (isNumber && !section.empty()) + { + size_t index = 0; + ConsoleTypeHelpers::StringToValue(index, section); + m_entries.push_back(PathEntry{ index }); + } + else + { + convertedSection.clear(); + size_t lastPos = 0; + size_t posToEscape = section.find(EscapeCharacter); + while (posToEscape != AZStd::string_view::npos) + { + if (convertedSection.empty()) + { + convertedSection.reserve(section.size() - 1); + } + convertedSection += section.substr(lastPos, posToEscape - lastPos); + if (section[posToEscape + 1] == ForwardSlashSequence) + { + convertedSection += '/'; + } + else + { + convertedSection += '~'; + } + + lastPos = posToEscape + 2; + posToEscape = section.find(EscapeCharacter, posToEscape + 2); + } + + if (!convertedSection.empty()) + { + convertedSection += section.substr(lastPos); + m_entries.emplace_back(convertedSection); + } + else + { + m_entries.emplace_back(section); + } + } + pathIndex = i + 1; + isNumber = true; + continue; + } + + const char c = pathString[i]; + isNumber = isNumber && c >= '0' && c <= '9'; + } + } +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPath.h b/Code/Framework/AzCore/AzCore/DOM/DomPath.h new file mode 100644 index 0000000000..39f7c7da98 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomPath.h @@ -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 + * + */ + +#pragma once + +#include +#include +#include + +namespace AZ::Dom +{ + //! Represents the path to a direct descendant of a Value. + //! PathEntry may be one of the following: + //! - Index, a numerical index for indexing within Arrays and Nodes + //! - Key, a name for indexing within Objects and Nodes + //! - EndOfArray, a special-case indicator for representing the end of an array + //! used by the patching system to represent push / pop back operations. + class PathEntry final + { + public: + static constexpr size_t EndOfArrayIndex = size_t(-1); + + PathEntry() = default; + PathEntry(const PathEntry&) = default; + PathEntry(PathEntry&&) = default; + explicit PathEntry(size_t value); + explicit PathEntry(AZ::Name value); + explicit PathEntry(AZStd::string_view value); + + PathEntry& operator=(const PathEntry&) = default; + PathEntry& operator=(PathEntry&&) = default; + PathEntry& operator=(size_t value); + PathEntry& operator=(AZ::Name value); + PathEntry& operator=(AZStd::string_view value); + + bool operator==(const PathEntry& other) const; + bool operator==(size_t index) const; + bool operator==(const AZ::Name& key) const; + bool operator==(AZStd::string_view key) const; + bool operator!=(const PathEntry& other) const; + bool operator!=(size_t index) const; + bool operator!=(const AZ::Name& key) const; + bool operator!=(AZStd::string_view key) const; + + void SetEndOfArray(); + + bool IsEndOfArray() const; + bool IsIndex() const; + bool IsKey() const; + + size_t GetIndex() const; + const AZ::Name& GetKey() const; + + private: + AZStd::variant m_value; + }; + + //! Represents a path, represented as a series of PathEntry values, to a position in a Value. + class Path final + { + public: + using ContainerType = AZStd::vector; + static constexpr char PathSeparator = '/'; + static constexpr char EscapeCharacter = '~'; + static constexpr char TildeSequence = '0'; + static constexpr char ForwardSlashSequence = '1'; + static constexpr char EndOfArrayCharacter = '-'; + + Path() = default; + Path(const Path&) = default; + Path(Path&&) = default; + explicit Path(AZStd::initializer_list init); + //! Creates a Path from a path string, a path string is formatted per the JSON pointer specification + //! and looks like "/path/to/value/0" + explicit Path(AZStd::string_view pathString); + + template + explicit Path(InputIterator first, InputIterator last) + : m_entries(first, last) + { + } + + Path& operator=(const Path&) = default; + Path& operator=(Path&&) = default; + + Path operator/(const PathEntry&) const; + Path operator/(size_t) const; + Path operator/(AZ::Name) const; + Path operator/(AZStd::string_view) const; + Path operator/(const Path&) const; + + Path& operator/=(const PathEntry&); + Path& operator/=(size_t); + Path& operator/=(AZ::Name); + Path& operator/=(AZStd::string_view); + Path& operator/=(const Path&); + + bool operator==(const Path&) const; + + const ContainerType& GetEntries() const; + void Push(PathEntry entry); + void Push(size_t entry); + void Push(AZ::Name entry); + void Push(AZStd::string_view key); + void Pop(); + void Clear(); + PathEntry At(size_t index) const; + size_t Size() const; + + PathEntry& operator[](size_t index); + const PathEntry& operator[](size_t index) const; + + ContainerType::iterator begin(); + ContainerType::iterator end(); + ContainerType::const_iterator begin() const; + ContainerType::const_iterator end() const; + ContainerType::const_iterator cbegin() const; + ContainerType::const_iterator cend() const; + size_t size() const; + + //! Gets the length this path would require, if string-formatted. + //! The length includes the contents of the string but not a null terminator. + size_t GetStringLength() const; + //! Formats a JSON-pointer style path string into the target buffer. + //! This operation will fail if bufferSize < GetStringLength() + 1 + void FormatString(char* stringBuffer, size_t bufferSize) const; + //! Returns a JSON-pointer style path string for this path. + AZStd::string ToString() const; + void AppendToString(AZStd::string& output) const; + //! Reads a JSON-pointer style path from pathString and replaces this path's contents. + //! Paths are accepted in the following forms: + //! "/path/to/foo/0" + //! "path/to/foo/0" + void FromString(AZStd::string_view pathString); + + private: + ContainerType m_entries; + }; +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 10c8e33715..793fadd092 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -1177,4 +1178,124 @@ namespace AZ::Dom { return m_value; } + + Value& Value::operator[](const PathEntry& entry) + { + if (entry.IsEndOfArray()) + { + Array::ContainerType& array = GetArrayInternal(); + array.push_back(); + return array[array.size() - 1]; + } + return entry.IsIndex() ? operator[](entry.GetIndex()) : operator[](entry.GetKey()); + } + + const Value& Value::operator[](const PathEntry& entry) const + { + return entry.IsIndex() ? operator[](entry.GetIndex()) : operator[](entry.GetKey()); + } + + Value& Value::operator[](const Path& path) + { + Value* value = this; + for (const PathEntry& entry : path) + { + value = &value->operator[](entry); + } + return *value; + } + + const Value& Value::operator[](const Path& path) const + { + const Value* value = this; + for (const PathEntry& entry : path) + { + value = &value->operator[](entry); + } + return *value; + } + + const Value* Value::FindChild(const PathEntry& entry) const + { + if (entry.IsEndOfArray()) + { + return nullptr; + } + else if (entry.IsIndex()) + { + const Array::ContainerType& array = GetArrayInternal(); + const size_t index = entry.GetIndex(); + if (index < array.size()) + { + return &array[index]; + } + } + else + { + const Object::ContainerType& obj = GetObjectInternal(); + auto memberIt = FindMember(entry.GetKey()); + if (memberIt != obj.end()) + { + return &memberIt->second; + } + } + return nullptr; + } + + Value* Value::FindMutableChild(const PathEntry& entry) + { + if (entry.IsEndOfArray()) + { + Array::ContainerType& array = GetArrayInternal(); + array.push_back(); + return &array[array.size() - 1]; + } + else if (entry.IsIndex()) + { + Array::ContainerType& array = GetArrayInternal(); + const size_t index = entry.GetIndex(); + if (index < array.size()) + { + return &array[index]; + } + } + else + { + Object::ContainerType& obj = GetObjectInternal(); + auto memberIt = FindMutableMember(entry.GetKey()); + if (memberIt != obj.end()) + { + return &memberIt->second; + } + } + return nullptr; + } + + const Value* Value::FindChild(const Path& path) const + { + const Value* value = this; + for (const PathEntry& entry : path) + { + value = value->FindChild(entry); + if (value == nullptr) + { + return nullptr; + } + } + return value; + } + + Value* Value::FindMutableChild(const Path& path) + { + Value* value = this; + for (const PathEntry& entry : path) + { + value = value->FindMutableChild(entry); + if (value == nullptr) + { + return nullptr; + } + } + return value; + } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index d1d3c1745d..be5d855003 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -22,6 +22,8 @@ namespace AZ::Dom { + class PathEntry; + class Path; using KeyType = AZ::Name; //! The type of underlying value stored in a value. \see Value @@ -380,6 +382,17 @@ namespace AZ::Dom Visitor::Result Accept(Visitor& visitor, bool copyStrings) const; AZStd::unique_ptr GetWriteHandler(); + // Path API... + Value& operator[](const PathEntry& entry); + const Value& operator[](const PathEntry& entry) const; + Value& operator[](const Path& path); + const Value& operator[](const Path& path) const; + + const Value* FindChild(const PathEntry& entry) const; + Value* FindMutableChild(const PathEntry& entry); + const Value* FindChild(const Path& path) const; + Value* FindMutableChild(const Path& path); + //! Gets the internal value of this Value. Note that this value's types may not correspond one-to-one with the Type enumeration, //! as internally the same type might have different storage mechanisms. Where possible, prefer using the typed API. const ValueType& GetInternalValue() const; diff --git a/Code/Framework/AzCore/AzCore/IO/IStreamer.h b/Code/Framework/AzCore/AzCore/IO/IStreamer.h index 438384e687..d59ee5007e 100644 --- a/Code/Framework/AzCore/AzCore/IO/IStreamer.h +++ b/Code/Framework/AzCore/AzCore/IO/IStreamer.h @@ -15,14 +15,18 @@ #include #include #include +#include // These Streamer includes need to be moved to Streamer internals/implementation, // and pull out only what we need for visibility at IStreamer.h interface declaration. #include -#include namespace AZ::IO { + class ExternalFileRequest; + class FileRequestHandle; + + using FileRequestPtr = AZStd::intrusive_ptr; /** * Data Streamer Interface */ diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp index 6c873f3050..ca38414eaa 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.cpp @@ -137,18 +137,18 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request, args); return; } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } @@ -166,7 +166,7 @@ namespace AZ::IO { Section& delayed = m_delayedSections.front(); AZ_Assert(delayed.m_parent, "Delayed section doesn't have a reference to the original request."); - auto data = AZStd::get_if(&delayed.m_parent->GetCommand()); + auto data = AZStd::get_if(&delayed.m_parent->GetCommand()); AZ_Assert(data, "A request in the delayed queue of the BlockCache didn't have a parent with read data."); // This call can add the same section to the back of the queue if there's not // enough space. Because of this the entry needs to be removed from the delayed @@ -233,7 +233,7 @@ namespace AZ::IO } } - void BlockCache::ReadFile(FileRequest* request, FileRequest::ReadData& data) + void BlockCache::ReadFile(FileRequest* request, Requests::ReadData& data) { if (!m_next) { @@ -250,7 +250,7 @@ namespace AZ::IO m_numMetaDataRetrievalInProgress--; if (fileSizeRequest.GetStatus() == IStreamerTypes::RequestStatus::Completed) { - auto& requestInfo = AZStd::get(fileSizeRequest.GetCommand()); + auto& requestInfo = AZStd::get(fileSizeRequest.GetCommand()); if (requestInfo.m_found) { ContinueReadFile(request, requestInfo.m_fileSize); @@ -272,7 +272,7 @@ namespace AZ::IO Section main; Section epilog; - auto& data = AZStd::get(request->GetCommand()); + auto& data = AZStd::get(request->GetCommand()); if (!SplitRequest(prolog, main, epilog, data.m_path, fileLength, data.m_offset, data.m_size, reinterpret_cast(data.m_output))) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h index 90fb7ea193..68aa2da689 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/BlockCache.h @@ -21,6 +21,12 @@ namespace AZ::IO { + class RequestPath; + namespace Requests + { + struct ReadData; + } + struct BlockCacheConfig final : public IStreamerStackConfig { @@ -109,7 +115,7 @@ namespace AZ::IO using TimePoint = AZStd::chrono::system_clock::time_point; - void ReadFile(FileRequest* request, FileRequest::ReadData& data); + void ReadFile(FileRequest* request, Requests::ReadData& data); void ContinueReadFile(FileRequest* request, u64 fileLength); CacheResult ReadFromCache(FileRequest* request, Section& section, const RequestPath& filePath); CacheResult ReadFromCache(FileRequest* request, Section& section, u32 cacheBlock); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp index b80a1ea724..f155d26eef 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp @@ -101,12 +101,12 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { args.m_range = FileRange::CreateRangeForEntireFile(); m_context->PushPreparedRequest(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { args.m_range = FileRange::CreateRangeForEntireFile(); m_context->PushPreparedRequest(request); @@ -125,28 +125,28 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request, args); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { CreateDedicatedCache(request, args); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { DestroyDedicatedCache(request, args); return; } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } @@ -200,7 +200,7 @@ namespace AZ::IO } } - void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data) + void DedicatedCache::ReadFile(FileRequest* request, Requests::ReadData& data) { size_t index = FindCache(data.m_path, data.m_offset); if (index == s_fileNotFound) @@ -255,7 +255,7 @@ namespace AZ::IO StreamStackEntry::CollectStatistics(statistics); } - void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data) + void DedicatedCache::CreateDedicatedCache(FileRequest* request, Requests::CreateDedicatedCacheData& data) { size_t index = FindCache(data.m_path, data.m_range); if (index == s_fileNotFound) @@ -276,7 +276,7 @@ namespace AZ::IO m_context->MarkRequestAsCompleted(request); } - void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data) + void DedicatedCache::DestroyDedicatedCache(FileRequest* request, Requests::DestroyDedicatedCacheData& data) { size_t index = FindCache(data.m_path, data.m_range); if (index != s_fileNotFound) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h index 0ef2d879d3..85aa29a051 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h @@ -11,15 +11,21 @@ #include #include #include -#include #include +#include #include -#include #include +#include #include namespace AZ::IO { + namespace Requests + { + struct CreateDedicatedCacheData; + struct DestroyDedicatedCacheData; + } // namespace Requests + struct DedicatedCacheConfig final : public IStreamerStackConfig { @@ -56,16 +62,19 @@ namespace AZ::IO void UpdateStatus(Status& status) const override; - void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector& internalPending, - StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override; + void UpdateCompletionEstimates( + AZStd::chrono::system_clock::time_point now, + AZStd::vector& internalPending, + StreamerContext::PreparedQueue::iterator pendingBegin, + StreamerContext::PreparedQueue::iterator pendingEnd) override; void CollectStatistics(AZStd::vector& statistics) const override; private: - void CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data); - void DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data); + void CreateDedicatedCache(FileRequest* request, Requests::CreateDedicatedCacheData& data); + void DestroyDedicatedCache(FileRequest* request, Requests::DestroyDedicatedCacheData& data); - void ReadFile(FileRequest* request, FileRequest::ReadData& data); + void ReadFile(FileRequest* request, Requests::ReadData& data); size_t FindCache(const RequestPath& filename, FileRange range); size_t FindCache(const RequestPath& filename, u64 offset); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp index fc05b77b36..13c01b2efc 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.cpp @@ -12,22 +12,30 @@ #include #include -namespace AZ::IO -{ - // - // Command structures. - // +// +// Command structures. +// - FileRequest::ExternalRequestData::ExternalRequestData(FileRequestPtr&& request) - : m_request(AZStd::move(request)) - {} - - FileRequest::RequestPathStoreData::RequestPathStoreData(RequestPath path) - : m_path(AZStd::move(path)) - {} +namespace AZ::IO::Requests +{ + ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead) + : m_path(path) + , m_output(output) + , m_outputSize(outputSize) + , m_offset(offset) + , m_size(size) + , m_sharedRead(sharedRead) + { + } - FileRequest::ReadRequestData::ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size, - AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority) + ReadRequestData::ReadRequestData( + RequestPath path, + void* output, + u64 outputSize, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority) : m_path(AZStd::move(path)) , m_allocator(nullptr) , m_deadline(deadline) @@ -37,10 +45,16 @@ namespace AZ::IO , m_size(size) , m_priority(priority) , m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally. - {} + { + } - FileRequest::ReadRequestData::ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, - u64 offset, u64 size, AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority) + ReadRequestData::ReadRequestData( + RequestPath path, + IStreamerTypes::RequestMemoryAllocator* allocator, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority) : m_path(AZStd::move(path)) , m_allocator(allocator) , m_deadline(deadline) @@ -50,9 +64,10 @@ namespace AZ::IO , m_size(size) , m_priority(priority) , m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally. - {} + { + } - FileRequest::ReadRequestData::~ReadRequestData() + ReadRequestData::~ReadRequestData() { if (m_allocator != nullptr) { @@ -64,65 +79,80 @@ namespace AZ::IO } } - FileRequest::ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead) - : m_output(output) - , m_outputSize(outputSize) - , m_path(path) - , m_offset(offset) - , m_size(size) - , m_sharedRead(sharedRead) - {} + CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range) + : m_path(AZStd::move(path)) + , m_range(range) + { + } + + DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range) + : m_path(AZStd::move(path)) + , m_range(range) + { + } - FileRequest::CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize) + ExternalRequestData::ExternalRequestData(FileRequestPtr&& request) + : m_request(AZStd::move(request)) + { + } + + RequestPathStoreData::RequestPathStoreData(RequestPath path) + : m_path(AZStd::move(path)) + { + } + + CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize) : m_compressionInfo(AZStd::move(compressionInfo)) , m_output(output) , m_readOffset(readOffset) , m_readSize(readSize) - {} + { + } - FileRequest::FileExistsCheckData::FileExistsCheckData(const RequestPath& path) + FileExistsCheckData::FileExistsCheckData(const RequestPath& path) : m_path(path) - {} + { + } - FileRequest::FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path) + FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path) : m_path(path) - {} + { + } - FileRequest::CancelData::CancelData(FileRequestPtr target) + CancelData::CancelData(FileRequestPtr target) : m_target(AZStd::move(target)) - {} + { + } - FileRequest::FlushData::FlushData(RequestPath path) + FlushData::FlushData(RequestPath path) : m_path(AZStd::move(path)) - {} + { + } - FileRequest::RescheduleData::RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, + RescheduleData::RescheduleData( + FileRequestPtr target, + AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority) : m_target(AZStd::move(target)) , m_newDeadline(newDeadline) , m_newPriority(newPriority) - {} - - FileRequest::CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range) - : m_path(AZStd::move(path)) - , m_range(range) - {} - - FileRequest::DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range) - : m_path(AZStd::move(path)) - , m_range(range) - {} + { + } - FileRequest::ReportData::ReportData(ReportType reportType) + ReportData::ReportData(ReportType reportType) : m_reportType(reportType) - {} + { + } - FileRequest::CustomData::CustomData(AZStd::any data, bool failWhenUnhandled) + CustomData::CustomData(AZStd::any data, bool failWhenUnhandled) : m_data(AZStd::move(data)) , m_failWhenUnhandled(failWhenUnhandled) - {} - + { + } +} // namespace AZ::IO::Requests +namespace AZ::IO +{ // // FileRequest // @@ -145,14 +175,14 @@ namespace AZ::IO m_parent = request->m_request.m_parent; request->m_request.m_parent = this; m_dependencies++; - m_command.emplace(AZStd::move(request)); + m_command.emplace(AZStd::move(request)); } void FileRequest::CreateRequestPathStore(FileRequest* parent, RequestPath path) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'CreateRequestPathStore', but another task was already assigned."); - m_command.emplace(AZStd::move(path)); + m_command.emplace(AZStd::move(path)); SetOptionalParent(parent); } @@ -161,7 +191,7 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'ReadRequest', but another task was already assigned."); - m_command.emplace(AZStd::move(path), output, outputSize, offset, size, deadline, priority); + m_command.emplace(AZStd::move(path), output, outputSize, offset, size, deadline, priority); } void FileRequest::CreateReadRequest(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size, @@ -169,7 +199,7 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'ReadRequest', but another task was already assigned."); - m_command.emplace(AZStd::move(path), allocator, offset, size, deadline, priority); + m_command.emplace(AZStd::move(path), allocator, offset, size, deadline, priority); } void FileRequest::CreateRead(FileRequest* parent, void* output, u64 outputSize, const RequestPath& path, @@ -177,7 +207,7 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Read', but another task was already assigned."); - m_command.emplace(output, outputSize, AZStd::move(path), offset, size, sharedRead); + m_command.emplace(output, outputSize, AZStd::move(path), offset, size, sharedRead); SetOptionalParent(parent); } @@ -192,7 +222,7 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'CompressedRead', but another task was already assigned."); - m_command.emplace(AZStd::move(compressionInfo), output, readOffset, readSize); + m_command.emplace(AZStd::move(compressionInfo), output, readOffset, readSize); SetOptionalParent(parent); } @@ -200,7 +230,7 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Wait', but another task was already assigned."); - m_command.emplace(); + m_command.emplace(); SetOptionalParent(parent); } @@ -208,21 +238,21 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'FileExistsCheck', but another task was already assigned."); - m_command.emplace(path); + m_command.emplace(path); } void FileRequest::CreateFileMetaDataRetrieval(const RequestPath& path) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'FileMetaDataRetrieval', but another task was already assigned."); - m_command.emplace(path); + m_command.emplace(path); } void FileRequest::CreateCancel(FileRequestPtr target) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Cancel', but another task was already assigned."); - m_command.emplace(AZStd::move(target)); + m_command.emplace(AZStd::move(target)); } void FileRequest::CreateReschedule(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, @@ -230,28 +260,28 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Reschedule', but another task was already assigned."); - m_command.emplace(AZStd::move(target), newDeadline, newPriority); + m_command.emplace(AZStd::move(target), newDeadline, newPriority); } void FileRequest::CreateFlush(RequestPath path) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Flush', but another task was already assigned."); - m_command.emplace(AZStd::move(path)); + m_command.emplace(AZStd::move(path)); } void FileRequest::CreateFlushAll() { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'FlushAll', but another task was already assigned."); - m_command.emplace(); + m_command.emplace(); } void FileRequest::CreateDedicatedCacheCreation(RequestPath path, const FileRange& range, FileRequest* parent) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'CreateDedicateCache', but another task was already assigned."); - m_command.emplace(AZStd::move(path), range); + m_command.emplace(AZStd::move(path), range); SetOptionalParent(parent); } @@ -259,22 +289,22 @@ namespace AZ::IO { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'DestroyDedicateCache', but another task was already assigned."); - m_command.emplace(AZStd::move(path), range); + m_command.emplace(AZStd::move(path), range); SetOptionalParent(parent); } - void FileRequest::CreateReport(ReportData::ReportType reportType) + void FileRequest::CreateReport(Requests::ReportType reportType) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Report', but another task was already assigned."); - m_command.emplace(reportType); + m_command.emplace(reportType); } void FileRequest::CreateCustom(AZStd::any data, bool failWhenUnhandled, FileRequest* parent) { AZ_Assert(AZStd::holds_alternative(m_command), "Attempting to set FileRequest to 'Custom', but another task was already assigned."); - m_command.emplace(AZStd::move(data), failWhenUnhandled); + m_command.emplace(AZStd::move(data), failWhenUnhandled); SetOptionalParent(parent); } @@ -361,7 +391,7 @@ namespace AZ::IO "Request does not contain a valid command. It may have been reset already or was never assigned a command."); return true; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return args.m_failWhenUnhandled; } @@ -398,7 +428,7 @@ namespace AZ::IO const FileRequest* current = this; while (current) { - auto* link = AZStd::get_if(¤t->m_command); + auto* link = AZStd::get_if(¤t->m_command); if (!link) { current = current->m_parent; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h index dd4dad2387..7f5874c95d 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FileRequest.h @@ -27,227 +27,261 @@ namespace AZ::IO class ExternalFileRequest; using FileRequestPtr = AZStd::intrusive_ptr; +} // namespace AZ::IO - class FileRequest final +namespace AZ::IO::Requests +{ + //! Request to read data. This is a translated request and holds an absolute path and has been + //! resolved to the archive file if needed. + struct ReadData { - public: - inline constexpr static AZStd::chrono::system_clock::time_point s_noDeadlineTime = AZStd::chrono::system_clock::time_point::max(); + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - friend class StreamerContext; - friend class ExternalFileRequest; + ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead); - //! Stores a reference to the external request so it stays alive while the request is being processed. - //! This is needed because Streamer supports fire-and-forget requests since completion can be handled by - //! registering a callback. - struct ExternalRequestData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + const RequestPath& m_path; //!< The path to the file that contains the requested data. + void* m_output; //!< Target output to write the read data to. + u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger. + u64 m_offset; //!< The offset in bytes into the file. + u64 m_size; //!< The number of bytes to read from the file. + bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock. + }; - explicit ExternalRequestData(FileRequestPtr&& request); + //! Request to read data. This is an untranslated request and holds a relative path. The Scheduler + //! will translate this to the appropriate ReadData or CompressedReadData. + struct ReadRequestData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; + + ReadRequestData( + RequestPath path, + void* output, + u64 outputSize, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority); + ReadRequestData( + RequestPath path, + IStreamerTypes::RequestMemoryAllocator* allocator, + u64 offset, + u64 size, + AZStd::chrono::system_clock::time_point deadline, + IStreamerTypes::Priority priority); + ~ReadRequestData(); + + RequestPath m_path; //!< Relative path to the target file. + IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request. + AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed. + void* m_output; //!< The memory address assigned (during processing) to store the read data to. + u64 m_outputSize; //!< The memory size of the addressed used to store the read data. + u64 m_offset; //!< The offset in bytes into the file. + u64 m_size; //!< The number of bytes to read from the file. + IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline. + IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used. + }; - FileRequestPtr m_request; //!< The request that was send to Streamer. - }; + //! Creates a cache dedicated to a single file. This is best used for files where blocks are read from + //! periodically such as audio banks of video files. + struct CreateDedicatedCacheData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that - //! need a path take them by reference to the original request. In some cases a path originates from - //! within in the stack and temporary storage is needed. This struct allows for that temporary storage - //! so it can be safely referenced later. - struct RequestPathStoreData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + CreateDedicatedCacheData(RequestPath path, const FileRange& range); - explicit RequestPathStoreData(RequestPath path); + RequestPath m_path; + FileRange m_range; + }; - RequestPath m_path; - }; + //! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache + struct DestroyDedicatedCacheData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Request to read data. This is an untranslated request and holds a relative path. The Scheduler - //! will translate this to the appropriate ReadData or CompressedReadData. - struct ReadRequestData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; - - ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size, - AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority); - ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size, - AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority); - ~ReadRequestData(); - - RequestPath m_path; //!< Relative path to the target file. - IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request. - AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed. - void* m_output; //!< The memory address assigned (during processing) to store the read data to. - u64 m_outputSize; //!< The memory size of the addressed used to store the read data. - u64 m_offset; //!< The offset in bytes into the file. - u64 m_size; //!< The number of bytes to read from the file. - IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline. - IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used. - }; + DestroyDedicatedCacheData(RequestPath path, const FileRange& range); - //! Request to read data. This is a translated request and holds an absolute path and has been - //! resolved to the archive file if needed. - struct ReadData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + RequestPath m_path; + FileRange m_range; + }; - ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead); + enum class ReportType : int8_t + { + FileLocks + }; - const RequestPath& m_path; //!< The path to the file that contains the requested data. - void* m_output; //!< Target output to write the read data to. - u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger. - u64 m_offset; //!< The offset in bytes into the file. - u64 m_size; //!< The number of bytes to read from the file. - bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock. - }; + struct ReportData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow; + inline constexpr static bool s_failWhenUnhandled = false; - //! Request to read and decompress data. - struct CompressedReadData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; + explicit ReportData(ReportType reportType); - CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize); + ReportType m_reportType; + }; - CompressionInfo m_compressionInfo; - void* m_output; //!< Target output to write the read data to. - u64 m_readOffset; //!< The offset into the decompressed to start copying from. - u64 m_readSize; //!< Number of bytes to read from the decompressed file. - }; + //! Stores a reference to the external request so it stays alive while the request is being processed. + //! This is needed because Streamer supports fire-and-forget requests since completion can be handled by + //! registering a callback. + struct ExternalRequestData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - //! Holds the progress of an operation chain until this request is explicitly completed. - struct WaitData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - inline constexpr static bool s_failWhenUnhandled = true; - }; + explicit ExternalRequestData(FileRequestPtr&& request); - //! Checks to see if any node in the stack can find a file at the provided path. - struct FileExistsCheckData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + FileRequestPtr m_request; //!< The request that was send to Streamer. + }; - explicit FileExistsCheckData(const RequestPath& path); + //! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that + //! need a path take them by reference to the original request. In some cases a path originates from + //! within in the stack and temporary storage is needed. This struct allows for that temporary storage + //! so it can be safely referenced later. + struct RequestPathStoreData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - const RequestPath& m_path; - bool m_found{ false }; - }; + explicit RequestPathStoreData(RequestPath path); - //! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists - //! check. - struct FileMetaDataRetrievalData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + RequestPath m_path; + }; - explicit FileMetaDataRetrievalData(const RequestPath& path); + //! Request to read and decompress data. + struct CompressedReadData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; - const RequestPath& m_path; - u64 m_fileSize{ 0 }; - bool m_found{ false }; - }; + CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize); - //! Cancels a request in the stream stack, if possible. - struct CancelData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest; - inline constexpr static bool s_failWhenUnhandled = false; + CompressionInfo m_compressionInfo; + void* m_output; //!< Target output to write the read data to. + u64 m_readOffset; //!< The offset into the decompressed to start copying from. + u64 m_readSize; //!< Number of bytes to read from the decompressed file. + }; - explicit CancelData(FileRequestPtr target); + //! Holds the progress of an operation chain until this request is explicitly completed. + struct WaitData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + inline constexpr static bool s_failWhenUnhandled = true; + }; - FileRequestPtr m_target; //!< The request that will be canceled. - }; + //! Checks to see if any node in the stack can find a file at the provided path. + struct FileExistsCheckData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Updates the priority and deadline of a request that has not been queued yet. - struct RescheduleData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + explicit FileExistsCheckData(const RequestPath& path); - RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority); + const RequestPath& m_path; + bool m_found{ false }; + }; - FileRequestPtr m_target; //!< The request that will be rescheduled. - AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request. - IStreamerTypes::Priority m_newPriority; //!< The new priority for the request. - }; + //! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists + //! check. + struct FileMetaDataRetrievalData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - //! Flushes all references to the provided file in the streaming stack. - struct FlushData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + explicit FileMetaDataRetrievalData(const RequestPath& path); - explicit FlushData(RequestPath path); + const RequestPath& m_path; + u64 m_fileSize{ 0 }; + bool m_found{ false }; + }; - RequestPath m_path; - }; + //! Cancels a request in the stream stack, if possible. + struct CancelData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest; + inline constexpr static bool s_failWhenUnhandled = false; - //! Flushes all caches in the streaming stack. - struct FlushAllData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; - }; + explicit CancelData(FileRequestPtr target); - //! Creates a cache dedicated to a single file. This is best used for files where blocks are read from - //! periodically such as audio banks of video files. - struct CreateDedicatedCacheData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + FileRequestPtr m_target; //!< The request that will be canceled. + }; - CreateDedicatedCacheData(RequestPath path, const FileRange& range); + //! Updates the priority and deadline of a request that has not been queued yet. + struct RescheduleData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - RequestPath m_path; - FileRange m_range; - }; + RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority); - //! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache - struct DestroyDedicatedCacheData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; - inline constexpr static bool s_failWhenUnhandled = false; + FileRequestPtr m_target; //!< The request that will be rescheduled. + AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request. + IStreamerTypes::Priority m_newPriority; //!< The new priority for the request. + }; - DestroyDedicatedCacheData(RequestPath path, const FileRange& range); + //! Flushes all references to the provided file in the streaming stack. + struct FlushData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; - RequestPath m_path; - FileRange m_range; - }; + explicit FlushData(RequestPath path); - struct ReportData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow; - inline constexpr static bool s_failWhenUnhandled = false; + RequestPath m_path; + }; - enum class ReportType - { - FileLocks - }; + //! Flushes all caches in the streaming stack. + struct FlushAllData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh; + inline constexpr static bool s_failWhenUnhandled = false; + }; - explicit ReportData(ReportType reportType); + //! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored + //! in the already provided data. + struct CustomData + { + inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; - ReportType m_reportType; - }; + CustomData(AZStd::any data, bool failWhenUnhandled); - //! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored - //! in the already provided data. - struct CustomData - { - inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium; + AZStd::any m_data; //!< The data for the custom request. + bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it. + }; + using CommandVariant = AZStd::variant< + AZStd::monostate, + ExternalRequestData, + RequestPathStoreData, + ReadRequestData, + ReadData, + CompressedReadData, + WaitData, + FileExistsCheckData, + FileMetaDataRetrievalData, + CancelData, + RescheduleData, + FlushData, + FlushAllData, + CreateDedicatedCacheData, + DestroyDedicatedCacheData, + ReportData, + CustomData>; + +} // namespace AZ::IO::Requests - CustomData(AZStd::any data, bool failWhenUnhandled); +namespace AZ::IO +{ + class FileRequest final + { + public: + inline constexpr static AZStd::chrono::system_clock::time_point s_noDeadlineTime = AZStd::chrono::system_clock::time_point::max(); - AZStd::any m_data; //!< The data for the custom request. - bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it. - }; + friend class StreamerContext; + friend class ExternalFileRequest; - using CommandVariant = AZStd::variant; + using CommandVariant = Requests::CommandVariant; using OnCompletionCallback = AZStd::function; AZ_CLASS_ALLOCATOR(FileRequest, SystemAllocator, 0); @@ -278,7 +312,7 @@ namespace AZ::IO void CreateFlushAll(); void CreateDedicatedCacheCreation(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr); void CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr); - void CreateReport(ReportData::ReportType reportType); + void CreateReport(Requests::ReportType reportType); void CreateCustom(AZStd::any data, bool failWhenUnhandled = true, FileRequest* parent = nullptr); void SetCompletionCallback(OnCompletionCallback callback); @@ -325,17 +359,6 @@ namespace AZ::IO //! Command and parameters for the request. CommandVariant m_command; - //! Status of the request. - AZStd::atomic m_status{ IStreamerTypes::RequestStatus::Pending }; - - //! Called once the request has completed. This will always be called from the Streamer thread - //! and thread safety is the responsibility of called function. When assigning a lambda avoid - //! capturing a FileRequestPtr by value as this will cause a circular reference which causes - //! the FileRequestPtr to never be released and causes a memory leak. This call will - //! block the main Streamer thread until it returns so callbacks should be kept short. If - //! a longer running task is needed consider using a job to do the work. - OnCompletionCallback m_onCompletion; - //! Estimated time this request will complete. This is an estimation and depends on many //! factors which can cause it to change drastically from moment to moment. AZStd::chrono::system_clock::time_point m_estimatedCompletion; @@ -344,9 +367,21 @@ namespace AZ::IO //! other request depending on this one to complete. FileRequest* m_parent{ nullptr }; + //! Id assigned when the request is added to the pending queue. size_t m_pendingId{ 0 }; + //! Called once the request has completed. This will always be called from the Streamer thread + //! and thread safety is the responsibility of called function. When assigning a lambda avoid + //! capturing a FileRequestPtr by value as this will cause a circular reference which causes + //! the FileRequestPtr to never be released and causes a memory leak. This call will + //! block the main Streamer thread until it returns so callbacks should be kept short. If + //! a longer running task is needed consider using a job to do the work. + OnCompletionCallback m_onCompletion; + + //! Status of the request. + AZStd::atomic m_status{ IStreamerTypes::RequestStatus::Pending }; + //! The number of dependent file request that need to complete before this one is done. u16 m_dependencies{ 0 }; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp index 723a5d62c8..5087702c47 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp @@ -91,12 +91,12 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { PrepareReadRequest(request, args); } - else if constexpr (AZStd::is_same_v || - AZStd::is_same_v) + else if constexpr (AZStd::is_same_v || + AZStd::is_same_v) { PrepareDedicatedCache(request, args.m_path); } @@ -114,11 +114,11 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { m_pendingReads.push_back(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { m_pendingFileExistChecks.push_back(request); } @@ -203,7 +203,7 @@ namespace AZ::IO { FileRequest* compressedRequest = m_processingJobs[i].m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in the decompression queue in FullFileDecompressor didn't contain compression read data."); size_t bytesToDecompress = data->m_compressionInfo.m_compressedSize; @@ -255,7 +255,7 @@ namespace AZ::IO // Calculate the amount of time it will take to decompress the data. FileRequest* compressedRequest = m_readRequests[i]->GetParent(); - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); size_t bytesToDecompress = data->m_compressionInfo.m_compressedSize; auto decompressionDuration = AZStd::chrono::microseconds( @@ -290,7 +290,7 @@ namespace AZ::IO void FullFileDecompressor::EstimateCompressedReadRequest(FileRequest* request, AZStd::chrono::microseconds& cumulativeDelay, AZStd::chrono::microseconds decompressionDelay, double totalDecompressionDurationUs, double totalBytesDecompressed) const { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data) { AZStd::chrono::microseconds processingTime = decompressionDelay; @@ -343,7 +343,7 @@ namespace AZ::IO m_numRunningJobs == 0; } - void FullFileDecompressor::PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data) + void FullFileDecompressor::PrepareReadRequest(FileRequest* request, Requests::ReadRequestData& data) { CompressionInfo info; if (CompressionUtils::FindCompressionInfo(info, data.m_path.GetRelativePath())) @@ -359,7 +359,7 @@ namespace AZ::IO { FileRequest* pathStorageRequest = m_context->GetNewInternalRequest(); pathStorageRequest->CreateRequestPathStore(request, AZStd::move(info.m_archiveFilename)); - auto& pathStorage = AZStd::get(pathStorageRequest->GetCommand()); + auto& pathStorage = AZStd::get(pathStorageRequest->GetCommand()); nextRequest->CreateRead(pathStorageRequest, data.m_output, data.m_outputSize, pathStorage.m_path, info.m_offset + data.m_offset, data.m_size, info.m_isSharedPak); @@ -370,13 +370,13 @@ namespace AZ::IO auto callback = [this, nextRequest](const FileRequest& checkRequest) { AZ_PROFILE_FUNCTION(AzCore); - auto check = AZStd::get_if(&checkRequest.GetCommand()); + auto check = AZStd::get_if(&checkRequest.GetCommand()); AZ_Assert(check, "Callback in FullFileDecompressor::PrepareReadRequest expected FileExistsCheck but got another command."); if (check->m_found) { FileRequest* originalRequest = m_context->RejectRequest(nextRequest); - if (AZStd::holds_alternative(originalRequest->GetCommand())) + if (AZStd::holds_alternative(originalRequest->GetCommand())) { originalRequest = m_context->RejectRequest(originalRequest); } @@ -412,12 +412,12 @@ namespace AZ::IO AZStd::visit([request, &info, nextRequest](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { nextRequest->CreateDedicatedCacheCreation(AZStd::move(info.m_archiveFilename), FileRange::CreateRange(info.m_offset, info.m_compressedSize), request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { nextRequest->CreateDedicatedCacheDestruction(AZStd::move(info.m_archiveFilename), FileRange::CreateRange(info.m_offset, info.m_compressedSize), request); @@ -429,7 +429,7 @@ namespace AZ::IO auto callback = [this, nextRequest](const FileRequest& checkRequest) { AZ_PROFILE_FUNCTION(AzCore); - auto check = AZStd::get_if(&checkRequest.GetCommand()); + auto check = AZStd::get_if(&checkRequest.GetCommand()); AZ_Assert(check, "Callback in FullFileDecompressor::PrepareDedicatedCache expected FileExistsCheck but got another command."); if (check->m_found) @@ -461,7 +461,7 @@ namespace AZ::IO void FullFileDecompressor::FileExistsCheck(FileRequest* checkRequest) { - auto& fileCheckRequest = AZStd::get(checkRequest->GetCommand()); + auto& fileCheckRequest = AZStd::get(checkRequest->GetCommand()); CompressionInfo info; if (CompressionUtils::FindCompressionInfo(info, fileCheckRequest.m_path.GetRelativePath())) { @@ -487,7 +487,7 @@ namespace AZ::IO { if (m_readBufferStatus[i] == ReadBufferStatus::Unused) { - auto data = AZStd::get_if(&compressedReadRequest->GetCommand()); + auto data = AZStd::get_if(&compressedReadRequest->GetCommand()); AZ_Assert(data, "Compressed request that's starting a read in FullFileDecompressor didn't contain compression read data."); AZ_Assert(data->m_compressionInfo.m_decompressor, "FileRequest for FullFileDecompressor is missing a decompression callback."); @@ -549,7 +549,7 @@ namespace AZ::IO } else { - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in FullFileDecompressor that finished unsuccessfully didn't contain compression read data."); CompressionInfo& info = data->m_compressionInfo; size_t offsetAdjustment = info.m_offset - AZ_SIZE_ALIGN_DOWN(info.m_offset, aznumeric_cast(m_alignment)); @@ -591,7 +591,7 @@ namespace AZ::IO } FileRequest* waitRequest = m_readRequests[readSlot]; - AZ_Assert(AZStd::holds_alternative(waitRequest->GetCommand()), + AZ_Assert(AZStd::holds_alternative(waitRequest->GetCommand()), "File request waiting for decompression wasn't marked as being a wait operation."); FileRequest* compressedRequest = waitRequest->GetParent(); AZ_Assert(compressedRequest, "Read requests started by FullFileDecompressor is missing a parent request."); @@ -610,7 +610,7 @@ namespace AZ::IO m_readBuffers[readSlot] = nullptr; AZ::Job* decompressionJob; - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in FullFileDecompressor that's starting decompression didn't contain compression read data."); AZ_Assert(data->m_compressionInfo.m_decompressor, "FullFileDecompressor is queuing a decompression job but couldn't find a decompressor."); @@ -664,7 +664,7 @@ namespace AZ::IO FileRequest* compressedRequest = jobInfo.m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto data = AZStd::get_if(&compressedRequest->GetCommand()); + auto data = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(data, "Compressed request in FullFileDecompressor that completed decompression didn't contain compression read data."); CompressionInfo& info = data->m_compressionInfo; size_t offsetAdjustment = info.m_offset - AZ_SIZE_ALIGN_DOWN(info.m_offset, aznumeric_cast(m_alignment)); @@ -694,7 +694,7 @@ namespace AZ::IO FileRequest* compressedRequest = info.m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto request = AZStd::get_if(&compressedRequest->GetCommand()); + auto request = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(request, "Compressed request in FullFileDecompressor that's running full decompression didn't contain compression read data."); CompressionInfo& compressionInfo = request->m_compressionInfo; AZ_Assert(compressionInfo.m_decompressor, "Full decompressor job started, but there's no decompressor callback assigned."); @@ -719,7 +719,7 @@ namespace AZ::IO FileRequest* compressedRequest = info.m_waitRequest->GetParent(); AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request."); - auto request = AZStd::get_if(&compressedRequest->GetCommand()); + auto request = AZStd::get_if(&compressedRequest->GetCommand()); AZ_Assert(request, "Compressed request in FullFileDecompressor that's running partial decompression didn't contain compression read data."); CompressionInfo& compressionInfo = request->m_compressionInfo; AZ_Assert(compressionInfo.m_decompressor, "Partial decompressor job started, but there's no decompressor callback assigned."); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h index d9bd68f1a1..cb4226fd7c 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.h @@ -21,6 +21,11 @@ namespace AZ::IO { + namespace Requests + { + struct ReadRequestData; + } + struct FullFileDecompressorConfig final : public IStreamerStackConfig { @@ -87,7 +92,7 @@ namespace AZ::IO bool IsIdle() const; - void PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data); + void PrepareReadRequest(FileRequest* request, Requests::ReadRequestData& data); void PrepareDedicatedCache(FileRequest* request, const RequestPath& path); void FileExistsCheck(FileRequest* checkRequest); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp index a952e31a93..282a1e15c9 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/ReadSplitter.cpp @@ -118,7 +118,7 @@ namespace AZ::IO return; } - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data == nullptr) { StreamStackEntry::QueueRequest(request); @@ -156,7 +156,7 @@ namespace AZ::IO void ReadSplitter::QueueAlignedRead(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); if (data->m_size <= m_maxReadSize) @@ -187,7 +187,7 @@ namespace AZ::IO bool ReadSplitter::QueueAlignedRead(PendingRead& pending) { - auto data = AZStd::get_if(&pending.m_request->GetCommand()); + auto data = AZStd::get_if(&pending.m_request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); while (pending.m_readSize > 0) @@ -237,7 +237,7 @@ namespace AZ::IO void ReadSplitter::QueueBufferedRead(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); PendingRead pendingRead; @@ -262,7 +262,7 @@ namespace AZ::IO bool ReadSplitter::QueueBufferedRead(PendingRead& pending) { - auto data = AZStd::get_if(&pending.m_request->GetCommand()); + auto data = AZStd::get_if(&pending.m_request->GetCommand()); AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command."); while (pending.m_readSize > 0) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp index fe1d5a5eda..fba9c3c98a 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp @@ -6,9 +6,11 @@ * */ +#include + #include #include -#include +#include #include #include @@ -35,6 +37,8 @@ namespace AZ::IO m_threadData.m_streamStack = AZStd::move(streamStack); } + Scheduler::~Scheduler() = default; + void Scheduler::Start(const AZStd::thread_desc& threadDesc) { if (!m_isRunning) @@ -222,10 +226,10 @@ namespace AZ::IO { using Command = AZStd::decay_t; if constexpr ( - AZStd::is_same_v || - AZStd::is_same_v) + AZStd::is_same_v || + AZStd::is_same_v) { - auto parentReadRequest = next->GetCommandFromChain(); + auto parentReadRequest = next->GetCommandFromChain(); AZ_Assert(parentReadRequest != nullptr, "The issued read request can't be found for the (compressed) read command."); size_t size = parentReadRequest->m_size; @@ -234,7 +238,7 @@ namespace AZ::IO AZ_Assert(parentReadRequest->m_allocator, "The read request was issued without a memory allocator or valid output address."); u64 recommendedSize = size; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { recommendedSize = m_recommendations.CalculateRecommendedMemorySize(size, parentReadRequest->m_offset); } @@ -249,12 +253,12 @@ namespace AZ::IO parentReadRequest->m_output = allocation.m_address; parentReadRequest->m_outputSize = allocation.m_size; parentReadRequest->m_memoryType = allocation.m_type; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { args.m_output = parentReadRequest->m_output; args.m_outputSize = allocation.m_size; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { args.m_output = parentReadRequest->m_output; } @@ -267,7 +271,7 @@ namespace AZ::IO } #endif - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { m_threadData.m_lastFilePath = args.m_path; m_threadData.m_lastFileOffset = args.m_offset + args.m_size; @@ -275,7 +279,7 @@ namespace AZ::IO m_processingSize += args.m_size; #endif } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { const CompressionInfo& info = args.m_compressionInfo; m_threadData.m_lastFilePath = info.m_archiveFilename; @@ -288,15 +292,15 @@ namespace AZ::IO "Streamer queued %zu: %s", next->GetCommand().index(), parentReadRequest->m_path.GetRelativePath()); m_threadData.m_streamStack->QueueRequest(next); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return Thread_ProcessCancelRequest(next, args); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return Thread_ProcessRescheduleRequest(next, args); } - else if constexpr (AZStd::is_same_v || AZStd::is_same_v) + else if constexpr (AZStd::is_same_v || AZStd::is_same_v) { AZ_PROFILE_INTERVAL_START_COLORED(AzCore, next, ProfilerColor, "Streamer queued %zu", next->GetCommand().index()); @@ -345,7 +349,7 @@ namespace AZ::IO #endif { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { if (args.m_output == nullptr && args.m_allocator != nullptr) { @@ -393,7 +397,7 @@ namespace AZ::IO } } - void Scheduler::Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data) + void Scheduler::Thread_ProcessCancelRequest(FileRequest* request, Requests::CancelData& data) { AZ_PROFILE_INTERVAL_START_COLORED(AzCore, request, ProfilerColor, "Streamer queued cancel"); auto& pending = m_context.GetPreparedRequests(); @@ -415,7 +419,7 @@ namespace AZ::IO m_threadData.m_streamStack->QueueRequest(request); } - void Scheduler::Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data) + void Scheduler::Thread_ProcessRescheduleRequest(FileRequest* request, Requests::RescheduleData& data) { AZ_PROFILE_INTERVAL_START_COLORED(AzCore, request, ProfilerColor, "Streamer queued reschedule"); auto& pendingRequests = m_context.GetPreparedRequests(); @@ -424,7 +428,7 @@ namespace AZ::IO if (pending->WorksOn(data.m_target)) { // Read requests are the only requests that use deadlines and dynamic priorities. - auto readRequest = pending->GetCommandFromChain(); + auto readRequest = pending->GetCommandFromChain(); if (readRequest) { readRequest->m_deadline = data.m_newDeadline; @@ -463,8 +467,8 @@ namespace AZ::IO // Order is the same for both requests, so prioritize the request that are at risk of missing // it's deadline. - const FileRequest::ReadRequestData* firstRead = first->GetCommandFromChain(); - const FileRequest::ReadRequestData* secondRead = second->GetCommandFromChain(); + const Requests::ReadRequestData* firstRead = first->GetCommandFromChain(); + const Requests::ReadRequestData* secondRead = second->GetCommandFromChain(); if (firstRead == nullptr || secondRead == nullptr) { @@ -496,11 +500,11 @@ namespace AZ::IO auto sameFile = [this](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { return m_threadData.m_lastFilePath == args.m_path; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return m_threadData.m_lastFilePath == args.m_compressionInfo.m_archiveFilename; } @@ -517,11 +521,11 @@ namespace AZ::IO auto offset = [](auto&& args) -> s64 { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { return aznumeric_caster(args.m_offset); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { return aznumeric_caster(args.m_compressionInfo.m_offset); } diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h index 053a57d332..502002fd27 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -24,11 +25,19 @@ namespace AZ::IO { class FileRequest; + namespace Requests + { + struct CancelData; + struct RescheduleData; + } // namespace Requests + class Scheduler final { public: explicit Scheduler(AZStd::shared_ptr streamStack, u64 memoryAlignment = AZCORE_GLOBAL_NEW_ALIGNMENT, u64 sizeAlignment = 1, u64 granularity = 1_mib); + ~Scheduler(); + void Start(const AZStd::thread_desc& threadDesc); void Stop(); @@ -61,14 +70,14 @@ namespace AZ::IO bool Thread_ExecuteRequests(); bool Thread_PrepareRequests(AZStd::vector& outstandingRequests); void Thread_ProcessTillIdle(); - void Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data); - void Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data); + void Thread_ProcessCancelRequest(FileRequest* request, Requests::CancelData& data); + void Thread_ProcessRescheduleRequest(FileRequest* request, Requests::RescheduleData& data); enum class Order { - FirstRequest, //< The first request is the most important to process next. - SecondRequest, //< The second request is the most important to process next. - Equal //< Both requests are equally important. + FirstRequest, //!< The first request is the most important to process next. + SecondRequest, //!< The second request is the most important to process next. + Equal //!< Both requests are equally important. }; //! Determine which of the two provided requests is more important to process next. Order Thread_PrioritizeRequests(const FileRequest* first, const FileRequest* second) const; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp index cfd191b68f..20e08873da 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.cpp @@ -60,13 +60,13 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(request, "PrepareRequest was provided a null request."); - if (AZStd::holds_alternative(request->GetCommand())) + if (AZStd::holds_alternative(request->GetCommand())) { - auto& readRequest = AZStd::get(request->GetCommand()); + auto& readRequest = AZStd::get(request->GetCommand()); FileRequest* read = m_context->GetNewInternalRequest(); - read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path, - readRequest.m_offset, readRequest.m_size); + read->CreateRead( + request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path, readRequest.m_offset, readRequest.m_size); m_context->PushPreparedRequest(read); return; } @@ -79,29 +79,29 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v || - AZStd::is_same_v || - AZStd::is_same_v) + if constexpr (AZStd::is_same_v || + AZStd::is_same_v || + AZStd::is_same_v) { m_pendingRequests.push_back(request); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { CancelRequest(request, args.m_target); return; } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { Report(args); } @@ -118,15 +118,15 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileExistsRequest(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileMetaDataRetrievalRequest(request); } @@ -199,25 +199,25 @@ namespace AZ::IO AZStd::visit([&](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { targetFile = &args.m_path; readSize = args.m_size; offset = args.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { targetFile = &args.m_compressionInfo.m_archiveFilename; readSize = args.m_compressionInfo.m_compressedSize; offset = args.m_compressionInfo.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage(); startTime += averageTime; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage(); @@ -254,7 +254,7 @@ namespace AZ::IO { AZ_PROFILE_FUNCTION(AzCore); - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data, "FileRequest queued on StorageDrive to be read didn't contain read data."); SystemFile* file = nullptr; @@ -342,7 +342,7 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage); - auto& fileExists = AZStd::get(request->GetCommand()); + auto& fileExists = AZStd::get(request->GetCommand()); size_t cacheIndex = FindFileInCache(fileExists.m_path); if (cacheIndex != s_fileNotFound) { @@ -360,7 +360,7 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); TIMED_AVERAGE_WINDOW_SCOPE(m_getFileMetaDataTimeAverage); - auto& command = AZStd::get(request->GetCommand()); + auto& command = AZStd::get(request->GetCommand()); // If the file is already open, use the file handle which usually is cheaper than asking for the file by name. size_t cacheIndex = FindFileInCache(command.m_path); if (cacheIndex != s_fileNotFound) @@ -446,11 +446,11 @@ namespace AZ::IO } } - void StorageDrive::Report(const FileRequest::ReportData& data) const + void StorageDrive::Report(const Requests::ReportData& data) const { switch (data.m_reportType) { - case FileRequest::ReportData::ReportType::FileLocks: + case Requests::ReportType::FileLocks: for (u32 i = 0; i < m_fileHandles.size(); ++i) { if (m_fileHandles[i] != nullptr) diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h index d90b31eeec..25f3d7b353 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StorageDrive.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -16,6 +17,11 @@ #include #include +namespace AZ::IO::Requests +{ + struct ReportData; +} + namespace AZ::IO { struct StorageDriveConfig final : @@ -72,7 +78,7 @@ namespace AZ::IO void EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime, const RequestPath*& activeFile, u64& activeOffset) const; - void Report(const FileRequest::ReportData& data) const; + void Report(const Requests::ReportData& data) const; TimedAverageWindow m_fileOpenCloseTimeAverage; TimedAverageWindow m_getFileExistsTimeAverage; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp index e4976f7d1c..dfc156bbee 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -210,7 +211,7 @@ namespace AZ::IO IStreamerTypes::ClaimMemory claimMemory) const { AZ_Assert(request.m_request, "The request handle provided to Streamer::GetReadRequestResult is invalid."); - auto readRequest = AZStd::get_if(&request.m_request->GetCommand()); + auto readRequest = AZStd::get_if(&request.m_request->GetCommand()); if (readRequest != nullptr) { buffer = readRequest->m_output; @@ -281,14 +282,14 @@ namespace AZ::IO } } - FileRequestPtr Streamer::Report(FileRequest::ReportData::ReportType reportType) + FileRequestPtr Streamer::Report(Requests::ReportType reportType) { FileRequestPtr result = CreateRequest(); Report(result, reportType); return result; } - FileRequestPtr& Streamer::Report(FileRequestPtr& request, FileRequest::ReportData::ReportType reportType) + FileRequestPtr& Streamer::Report(FileRequestPtr& request, Requests::ReportType reportType) { request->m_request.CreateReport(reportType); return request; diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h index bb363a0c64..7e3dd1a742 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Streamer.h @@ -20,6 +20,10 @@ namespace AZStd struct thread_desc; } +namespace AZ::IO::Requests +{ + enum class ReportType : int8_t; +} namespace AZ::IO { @@ -185,9 +189,9 @@ namespace AZ::IO void RecordStatistics(); //! Tells AZ::IO::Streamer the report the information for the report to the output. - FileRequestPtr Report(FileRequest::ReportData::ReportType reportType); + FileRequestPtr Report(Requests::ReportType reportType); //! Tells AZ::IO::Streamer the report the information for the report to the output. - FileRequestPtr& Report(FileRequestPtr& request, FileRequest::ReportData::ReportType reportType); + FileRequestPtr& Report(FileRequestPtr& request, Requests::ReportType reportType); Streamer(const AZStd::thread_desc& threadDesc, AZStd::unique_ptr streamStack); diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp index 9a465021c2..25fdb9bdfb 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -207,7 +208,7 @@ namespace AZ { if (m_streamer) { - m_streamer->QueueRequest(m_streamer->Report(AZ::IO::FileRequest::ReportData::ReportType::FileLocks)); + m_streamer->QueueRequest(m_streamer->Report(AZ::IO::Requests::ReportType::FileLocks)); } } diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp index 5cd483bc55..a4979320a7 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.cpp @@ -22,6 +22,9 @@ namespace AZ static constexpr char LatePredictionName[] = "Early completions"; static constexpr char MissedDeadlinesName[] = "Missed deadlines"; #endif // AZ_STREAMER_ADD_EXTRA_PROFILING_INFO + + StreamerContext::StreamerContext() = default; + StreamerContext::~StreamerContext() { for (FileRequest* entry : m_internalRecycleBin) @@ -204,7 +207,7 @@ namespace AZ m_latePredictionsPercentageStat.GetMostRecentSample()); } } - auto readRequest = AZStd::get_if(&top->GetCommand()); + auto readRequest = AZStd::get_if(&top->GetCommand()); if (readRequest != nullptr) { m_missedDeadlinePercentageStat.PushSample(now < readRequest->m_deadline ? 0.0 : 1.0); @@ -224,7 +227,7 @@ namespace AZ top->m_onCompletion(*top); AZ_PROFILE_INTERVAL_END(AzCore, top); } - + if (parent) { AZ_Assert(parent->m_dependencies > 0, diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h index 356eb7ddac..fee8d7b56b 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerContext.h @@ -8,22 +8,27 @@ #pragma once #include -#include #include #include #include +#include #include -#include #include -#include +#include namespace AZ::IO { + class FileRequest; + class ExternalFileRequest; + + using FileRequestPtr = AZStd::intrusive_ptr; + class StreamerContext { public: using PreparedQueue = AZStd::deque; + StreamerContext(); ~StreamerContext(); //! Gets a new file request, either by creating a new instance or diff --git a/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp b/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp index 78da9e76d2..23d0eea618 100644 --- a/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Matrix3x4.cpp @@ -25,7 +25,7 @@ namespace AZ void Matrix3x4SetRowGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc) { - bool rowIsSet = false; + [[maybe_unused]] bool rowIsSet = false; if (dc.GetNumArguments() >= 5) { if (dc.IsNumber(0)) @@ -88,7 +88,7 @@ namespace AZ void Matrix3x4SetColumnGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc) { - bool columnIsSet = false; + [[maybe_unused]] bool columnIsSet = false; if (dc.GetNumArguments() >= 4) { if (dc.IsNumber(0)) @@ -133,7 +133,7 @@ namespace AZ void Matrix3x4SetTranslationGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc) { - bool translationIsSet = false; + [[maybe_unused]] bool translationIsSet = false; if (dc.GetNumArguments() == 3 && dc.IsNumber(0) && diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp index 410d235a37..b27f1a0943 100644 --- a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp @@ -77,7 +77,7 @@ namespace AZ // check open brace char c = *current++; - bool has_open_brace = false; + [[maybe_unused]] bool has_open_brace = false; if (c == '{') { c = *current++; diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp index 1b39eb81bd..2bb4faf1d5 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp +++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp @@ -85,7 +85,7 @@ namespace AZ NameDictionary::~NameDictionary() { - bool leaksDetected = false; + [[maybe_unused]] bool leaksDetected = false; for (const auto& keyValue : m_dictionary) { diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 8b8a3b377a..2c0c318648 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -116,6 +116,8 @@ set(FILES Debug/TraceReflection.h DOM/DomBackend.cpp DOM/DomBackend.h + DOM/DomPath.cpp + DOM/DomPath.h DOM/DomUtils.cpp DOM/DomUtils.h DOM/DomValue.cpp diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp index feb8bce111..f71418ae8f 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.cpp @@ -172,9 +172,9 @@ namespace AZ::IO AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(request, "PrepareRequest was provided a null request."); - if (AZStd::holds_alternative(request->GetCommand())) + if (AZStd::holds_alternative(request->GetCommand())) { - auto& readRequest = AZStd::get(request->GetCommand()); + auto& readRequest = AZStd::get(request->GetCommand()); if (IsServicedByThisDrive(readRequest.m_path.GetAbsolutePath())) { FileRequest* read = m_context->GetNewInternalRequest(); @@ -195,7 +195,7 @@ namespace AZ::IO AZStd::visit([this, request](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_path.GetAbsolutePath())) { @@ -203,8 +203,8 @@ namespace AZ::IO return; } } - else if constexpr (AZStd::is_same_v || - AZStd::is_same_v) + else if constexpr (AZStd::is_same_v || + AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_path.GetAbsolutePath())) { @@ -212,7 +212,7 @@ namespace AZ::IO return; } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { if (CancelRequest(request, args.m_target)) { @@ -221,15 +221,15 @@ namespace AZ::IO return; } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { Report(args); } @@ -254,27 +254,29 @@ namespace AZ::IO else if (!m_pendingRequests.empty()) { FileRequest* request = m_pendingRequests.front(); - hasWorked = AZStd::visit([this, request](auto&& args) - { - using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) - { - FileExistsRequest(request); - m_pendingRequests.pop_front(); - return true; - } - else if constexpr (AZStd::is_same_v) + hasWorked = AZStd::visit( + [this, request](auto&& args) { - FileMetaDataRetrievalRequest(request); - m_pendingRequests.pop_front(); - return true; - } - else - { - AZ_Assert(false, "A request was added to StorageDriveWin's pending queue that isn't supported."); - return false; - } - }, request->GetCommand()); + using Command = AZStd::decay_t; + if constexpr (AZStd::is_same_v) + { + FileExistsRequest(request); + m_pendingRequests.pop_front(); + return true; + } + else if constexpr (AZStd::is_same_v) + { + FileMetaDataRetrievalRequest(request); + m_pendingRequests.pop_front(); + return true; + } + else + { + AZ_Assert(false, "A request was added to StorageDriveWin's pending queue that isn't supported."); + return false; + } + }, + request->GetCommand()); } return StreamStackEntry::ExecuteRequests() || hasFinalizedReads || hasWorked; @@ -308,7 +310,7 @@ namespace AZ::IO FileReadInformation& read = m_readSlots_readInfo[i]; u64 totalBytesRead = m_readSizeAverage.GetTotal(); double totalReadTimeUSec = aznumeric_caster(m_readTimeAverage.GetTotal().count()); - auto readCommand = AZStd::get_if(&read.m_request->GetCommand()); + auto readCommand = AZStd::get_if(&read.m_request->GetCommand()); AZ_Assert(readCommand, "Request currently reading doesn't contain a read command."); auto endTime = read.m_startTime + AZStd::chrono::microseconds(aznumeric_cast((readCommand->m_size * totalReadTimeUSec) / totalBytesRead)); earliestSlot = AZStd::min(earliestSlot, endTime); @@ -354,25 +356,25 @@ namespace AZ::IO AZStd::visit([&](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { targetFile = &args.m_path; readSize = args.m_size; offset = args.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { targetFile = &args.m_compressionInfo.m_archiveFilename; readSize = args.m_compressionInfo.m_compressedSize; offset = args.m_compressionInfo.m_offset; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds getFileExistsTimeAverage = m_getFileExistsTimeAverage.CalculateAverage(); startTime += getFileExistsTimeAverage; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds getFileExistsTimeAverage = m_getFileMetaDataRetrievalTimeAverage.CalculateAverage(); @@ -411,15 +413,15 @@ namespace AZ::IO AZStd::visit([&, this](auto&& args) { using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v || - AZStd::is_same_v) + if constexpr (AZStd::is_same_v || + AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_path.GetAbsolutePath())) { EstimateCompletionTimeForRequest(request, startTime, activeFile, activeOffset); } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { if (IsServicedByThisDrive(args.m_compressionInfo.m_archiveFilename.GetAbsolutePath())) { @@ -435,7 +437,7 @@ namespace AZ::IO aznumeric_cast(m_pendingRequests.size()) - m_activeReads_Count; } - auto StorageDriveWin::OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const FileRequest::ReadData& data) -> OpenFileResult + auto StorageDriveWin::OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const Requests::ReadData& data) -> OpenFileResult { HANDLE file = INVALID_HANDLE_VALUE; @@ -553,7 +555,7 @@ namespace AZ::IO return false; } - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data, "Read request in StorageDriveWin doesn't contain read data."); HANDLE file = INVALID_HANDLE_VALUE; @@ -780,7 +782,7 @@ namespace AZ::IO void StorageDriveWin::FileExistsRequest(FileRequest* request) { - auto& fileExists = AZStd::get(request->GetCommand()); + auto& fileExists = AZStd::get(request->GetCommand()); AZ_PROFILE_SCOPE(AzCore, "StorageDriveWin::FileExistsRequest %s : %s", m_name.c_str(), fileExists.m_path.GetRelativePath()); @@ -836,7 +838,7 @@ namespace AZ::IO void StorageDriveWin::FileMetaDataRetrievalRequest(FileRequest* request) { - auto& command = AZStd::get(request->GetCommand()); + auto& command = AZStd::get(request->GetCommand()); AZ_PROFILE_SCOPE(AzCore, "StorageDriveWin::FileMetaDataRetrievalRequest %s : %s", m_name.c_str(), command.m_path.GetRelativePath()); @@ -1005,7 +1007,7 @@ namespace AZ::IO FileReadInformation& fileReadInfo = m_readSlots_readInfo[readSlot]; - auto readCommand = AZStd::get_if(&fileReadInfo.m_request->GetCommand()); + auto readCommand = AZStd::get_if(&fileReadInfo.m_request->GetCommand()); AZ_Assert(readCommand != nullptr, "Request stored with the overlapped I/O call did not contain a read request."); if (fileReadInfo.m_sectorAlignedOutput && !encounteredError) @@ -1147,11 +1149,11 @@ namespace AZ::IO StreamStackEntry::CollectStatistics(statistics); } - void StorageDriveWin::Report(const FileRequest::ReportData& data) const + void StorageDriveWin::Report(const Requests::ReportData& data) const { switch (data.m_reportType) { - case FileRequest::ReportData::ReportType::FileLocks: + case Requests::ReportType::FileLocks: if (m_cachesInitialized) { for (u32 i = 0; i < m_maxFileHandles; ++i) diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h index c70eb7804d..5207d094ef 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StorageDrive_Windows.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -19,6 +20,12 @@ #include #include +namespace AZ::IO::Requests +{ + struct ReadData; + struct ReportData; +} + namespace AZ::IO { class StorageDriveWin @@ -111,7 +118,7 @@ namespace AZ::IO CacheFull }; - OpenFileResult OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const FileRequest::ReadData& data); + OpenFileResult OpenFile(HANDLE& fileHandle, size_t& cacheSlot, FileRequest* request, const Requests::ReadData& data); bool ReadRequest(FileRequest* request); bool ReadRequest(FileRequest* request, size_t readSlot); bool CancelRequest(FileRequest* cancelRequest, FileRequestPtr& target); @@ -137,7 +144,7 @@ namespace AZ::IO void FinalizeSingleRequest(FileReadStatus& status, size_t readSlot, DWORD numBytesTransferred, bool isCanceled, bool encounteredError); - void Report(const FileRequest::ReportData& data) const; + void Report(const Requests::ReportData& data) const; TimedAverageWindow m_fileOpenCloseTimeAverage; TimedAverageWindow m_getFileExistsTimeAverage; diff --git a/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp b/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp index 27903495f1..3846c6cf16 100644 --- a/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp +++ b/Code/Framework/AzCore/Tests/Asset/AssetDataStreamTests.cpp @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp new file mode 100644 index 0000000000..624b24cd20 --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.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 + +namespace AZ::Dom::Benchmark +{ + using DomPathBenchmark = Tests::DomBenchmarkFixture; + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_Concatenate_InPlace)(benchmark::State& state) + { + AZ::Name entry1("entry1"); + AZ::Name entry2("entry2"); + PathEntry end; + end.SetEndOfArray(); + + for (auto _ : state) + { + Path p; + p /= entry1; + p /= entry2; + p /= 0; + p /= end; + } + + state.SetItemsProcessed(4 * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_Concatenate_InPlace); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_Concatenate_Copy)(benchmark::State& state) + { + AZ::Name entry1("entry1"); + AZ::Name entry2("entry2"); + PathEntry end; + end.SetEndOfArray(); + + for (auto _ : state) + { + Path p = Path() / entry1 / entry2 / 0 / end; + } + + state.SetItemsProcessed(4 * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_Concatenate_Copy); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_ToString)(benchmark::State& state) + { + Path p("/path/with/multiple/0/different/components/65536/999/-"); + AZStd::string s; + s.resize_no_construct(p.GetStringLength()); + + for (auto _ : state) + { + p.GetStringLength(); + p.FormatString(s.data(), s.size()); + } + + state.SetBytesProcessed(s.size() * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_ToString); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPath_FromString)(benchmark::State& state) + { + AZStd::string pathString = "/path/with/multiple/0/different/components/including-long-strings-like-this/65536/999/-"; + + for (auto _ : state) + { + Path p(pathString); + benchmark::DoNotOptimize(p); + } + + state.SetBytesProcessed(pathString.size() * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPath_FromString); + + BENCHMARK_DEFINE_F(DomPathBenchmark, DomPathEntry_IsEndOfArray)(benchmark::State& state) + { + PathEntry name("name"); + PathEntry index(0); + PathEntry endOfArray; + endOfArray.SetEndOfArray(); + + for (auto _ : state) + { + name.IsEndOfArray(); + index.IsEndOfArray(); + endOfArray.IsEndOfArray(); + } + + state.SetItemsProcessed(3 * state.iterations()); + } + BENCHMARK_REGISTER_F(DomPathBenchmark, DomPathEntry_IsEndOfArray); +} diff --git a/Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp new file mode 100644 index 0000000000..54bff9f29b --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomPathTests.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * 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::Dom::Tests +{ + class DomPathTests : public DomTestFixture + { + }; + + TEST_F(DomPathTests, EmptyPath_IsEmpty) + { + Path path(""); + EXPECT_EQ(path.GetEntries().size(), 0); + } + + TEST_F(DomPathTests, EmptyPath_IsEqualToDefault) + { + EXPECT_EQ(Path(""), Path()); + } + + TEST_F(DomPathTests, Index_IsNumeric) + { + EXPECT_EQ(Path("/0")[0], 0); + EXPECT_EQ(Path("/20")[0], 20); + EXPECT_EQ(Path("/9999")[0], 9999); + EXPECT_EQ(Path("/0/4/5/1")[3], 1); + } + + TEST_F(DomPathTests, Index_ConvertsToString) + { + Path p; + EXPECT_EQ(p.ToString(), ""); + + p.Push(0); + EXPECT_EQ(p.ToString(), "/0"); + + p.Push(1); + EXPECT_EQ(p.ToString(), "/0/1"); + + p.Push(10); + EXPECT_EQ(p.ToString(), "/0/1/10"); + + p.Push(9999); + EXPECT_EQ(p.ToString(), "/0/1/10/9999"); + + p.Pop(); + EXPECT_EQ(p.ToString(), "/0/1/10"); + } + + TEST_F(DomPathTests, Key_IsString) + { + EXPECT_EQ(Path("/foo")[0], "foo"); + EXPECT_EQ(Path("/bar")[0], "bar"); + EXPECT_EQ(Path("/foo/bar/baz12345")[0], "foo"); + EXPECT_EQ(Path("/foo/bar/baz12345")[2], "baz12345"); + EXPECT_EQ(Path("//foo")[0], ""); + } + + TEST_F(DomPathTests, Key_ConvertsToString) + { + Path p; + + p.Push("foo"); + EXPECT_EQ(p.ToString(), "/foo"); + + p.Push("bar"); + EXPECT_EQ(p.ToString(), "/foo/bar"); + + p.Push("another_key"); + EXPECT_EQ(p.ToString(), "/foo/bar/another_key"); + + p.Pop(); + EXPECT_EQ(p.ToString(), "/foo/bar"); + } + + TEST_F(DomPathTests, Key_ConvertsFromEscaped) + { + EXPECT_EQ(Path("/foo~0")[0], "foo~"); + EXPECT_EQ(Path("/foo~0bar~0~0")[0], "foo~bar~~"); + EXPECT_EQ(Path("/foo~1bar/baz")[0], "foo/bar"); + EXPECT_EQ(Path("/~1foo~1")[0], "/foo/"); + } + + TEST_F(DomPathTests, Key_ConvertsToEscaped) + { + Path p; + + p.Push("with~tilde"); + EXPECT_EQ(p.ToString(), "/with~0tilde"); + + p.Push("with/slash"); + EXPECT_EQ(p.ToString(), "/with~0tilde/with~1slash"); + + p.Clear(); + p.Push("/~with/mixed/characters~"); + EXPECT_EQ(p.ToString(), "/~1~0with~1mixed~1characters~0"); + } + + TEST_F(DomPathTests, MixedPath_Resolves) + { + EXPECT_EQ(Path("/foo/0")[0], "foo"); + EXPECT_EQ(Path("/foo/0")[1], 0); + EXPECT_EQ(Path("/42/foo/bar/0")[0], 42); + EXPECT_EQ(Path("/42/foo/bar/0")[1], "foo"); + EXPECT_EQ(Path("/42/foo/bar/0")[2], "bar"); + EXPECT_EQ(Path("/42/foo/bar/0")[3], 0); + } + + TEST_F(DomPathTests, MixedPath_ConvertsToString) + { + Path p; + + p.Push("foo"); + EXPECT_EQ(p.ToString(), "/foo"); + + p.Push(0); + EXPECT_EQ(p.ToString(), "/foo/0"); + + p.Push("another_key"); + EXPECT_EQ(p.ToString(), "/foo/0/another_key"); + + p.Push(100); + EXPECT_EQ(p.ToString(), "/foo/0/another_key/100"); + + p.Pop(); + EXPECT_EQ(p.ToString(), "/foo/0/another_key"); + } + + TEST_F(DomPathTests, OperatorOverloads_Append) + { + EXPECT_EQ(Path("/foo/bar"), Path("/foo") / Path("/bar")); + EXPECT_EQ(Path("/foo"), Path("/foo") / Path()); + EXPECT_EQ(Path("/foo/1/bar/0"), Path("/foo/1") / Path("/bar/0")); + EXPECT_EQ(Path("/foo") / 0, Path("/foo/0")); + EXPECT_EQ(Path() / "foo" / "bar", Path("/foo/bar")); + EXPECT_EQ(Path("/foo") / "bar" / "baz", Path("/foo/bar/baz")); + + Path p("/foo/bar"); + p /= "baz"; + EXPECT_EQ(p, Path("/foo/bar/baz")); + p /= Path("0/1"); + EXPECT_EQ(p, Path("/foo/bar/baz/0/1")); + } + + TEST_F(DomPathTests, EndOfArray_FromString) + { + EXPECT_FALSE(Path("/foo/-")[0].IsEndOfArray()); + EXPECT_TRUE(Path("/foo/-")[1].IsEndOfArray()); + EXPECT_TRUE(Path("/foo/-/-")[2].IsEndOfArray()); + } + + TEST_F(DomPathTests, EndOfArray_ToString) + { + EXPECT_EQ(Path("/-").ToString(), "/-"); + EXPECT_EQ(Path("/0/-").ToString(), "/0/-"); + } + + TEST_F(DomPathTests, MixedPath_AppendToString) + { + Path p("/foo/0"); + AZStd::string s; + + p.AppendToString(s); + EXPECT_EQ(s, "/foo/0"); + p.AppendToString(s); + EXPECT_EQ(s, "/foo/0/foo/0"); + } +} // namespace AZ::Dom::Tests diff --git a/Code/Framework/AzCore/Tests/Name/NameTests.cpp b/Code/Framework/AzCore/Tests/Name/NameTests.cpp index eb0a048e2f..9e94c4bfdf 100644 --- a/Code/Framework/AzCore/Tests/Name/NameTests.cpp +++ b/Code/Framework/AzCore/Tests/Name/NameTests.cpp @@ -642,7 +642,7 @@ namespace UnitTest char buffer[RandomStringBufferSize]; AZStd::sys_time_t newNameTime; - AZStd::sys_time_t existingNameTime; + [[maybe_unused]] AZStd::sys_time_t existingNameTime; AZStd::sys_time_t stringTime; { 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 e312e2058d..95b0626d7f 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 @@ -406,7 +406,7 @@ namespace AZ::IO request->CreateFileMetaDataRetrieval(path); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_FALSE(fileMetaData.m_found); EXPECT_EQ(0, fileMetaData.m_fileSize); }); @@ -424,7 +424,7 @@ namespace AZ::IO request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_TRUE(fileMetaData.m_found); EXPECT_EQ(4_kib, fileMetaData.m_fileSize); }); @@ -442,7 +442,7 @@ namespace AZ::IO request->CreateFileMetaDataRetrieval(path); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_FALSE(fileMetaData.m_found); EXPECT_EQ(0, fileMetaData.m_fileSize); }); @@ -460,7 +460,7 @@ namespace AZ::IO request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_TRUE(fileMetaData.m_found); EXPECT_EQ(16_kib, fileMetaData.m_fileSize); }); @@ -484,7 +484,7 @@ namespace AZ::IO request->SetCompletionCallback([](const FileRequest& request) { - auto& fileMetaData = AZStd::get(request.GetCommand()); + auto& fileMetaData = AZStd::get(request.GetCommand()); EXPECT_TRUE(fileMetaData.m_found); EXPECT_EQ(4_kib, fileMetaData.m_fileSize); }); @@ -502,7 +502,7 @@ namespace AZ::IO request->CreateFileExistsCheck(invalidPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_FALSE(fileExistsCheck.m_found); }); @@ -519,7 +519,7 @@ namespace AZ::IO request->CreateFileExistsCheck(path); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_FALSE(fileExistsCheck.m_found); }); @@ -535,7 +535,7 @@ namespace AZ::IO request->CreateFileExistsCheck(m_dummyRequestPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_TRUE(fileExistsCheck.m_found); }); @@ -551,7 +551,7 @@ namespace AZ::IO request->CreateFileExistsCheck(m_dummyRequestPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_TRUE(fileExistsCheck.m_found); }); @@ -573,7 +573,7 @@ namespace AZ::IO request->CreateFileExistsCheck(m_dummyRequestPath); request->SetCompletionCallback([](const FileRequest& request) { - auto& fileExistsCheck = AZStd::get(request.GetCommand()); + auto& fileExistsCheck = AZStd::get(request.GetCommand()); EXPECT_EQ(AZ::IO::IStreamerTypes::RequestStatus::Completed, request.GetStatus()); EXPECT_TRUE(fileExistsCheck.m_found); }); @@ -603,7 +603,7 @@ namespace AZ::IO AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); - auto& readRequest = AZStd::get(request.GetCommand()); + auto& readRequest = AZStd::get(request.GetCommand()); EXPECT_EQ(readRequest.m_size, fileSize); EXPECT_STREQ(readRequest.m_path.GetAbsolutePath(), m_dummyFilepath.c_str()); }; @@ -648,7 +648,7 @@ namespace AZ::IO AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); - auto& readRequest = AZStd::get(request.GetCommand()); + auto& readRequest = AZStd::get(request.GetCommand()); EXPECT_EQ(readRequest.m_size, unalignedSize); EXPECT_EQ(readRequest.m_offset, unalignedOffset); EXPECT_STREQ(readRequest.m_path.GetAbsolutePath(), m_dummyFilepath.c_str()); @@ -796,7 +796,7 @@ namespace AZ::IO AZ_POP_DISABLE_WARNING { EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed); - auto& readRequest = AZStd::get(request.GetCommand()); + auto& readRequest = AZStd::get(request.GetCommand()); EXPECT_EQ(readRequest.m_size, chunkSize); EXPECT_EQ(readRequest.m_offset, i * chunkSize); }; diff --git a/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp b/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp index 3a107da61d..d9d3623fbd 100644 --- a/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/BlockCacheTests.cpp @@ -100,7 +100,7 @@ namespace AZ::IO void QueueReadRequest(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data) { if (m_fakeFileFound) @@ -122,15 +122,15 @@ namespace AZ::IO m_context->MarkRequestAsCompleted(request); } else if ( - AZStd::holds_alternative(request->GetCommand()) || - AZStd::holds_alternative(request->GetCommand())) + AZStd::holds_alternative(request->GetCommand()) || + AZStd::holds_alternative(request->GetCommand())) { request->SetStatus(IStreamerTypes::RequestStatus::Completed); m_context->MarkRequestAsCompleted(request); } - else if (AZStd::holds_alternative(request->GetCommand())) + else if (AZStd::holds_alternative(request->GetCommand())) { - auto& data2 = AZStd::get(request->GetCommand()); + auto& data2 = AZStd::get(request->GetCommand()); data2.m_found = m_fakeFileFound; data2.m_fileSize = m_fakeFileLength; request->SetStatus(m_fakeFileFound ? IStreamerTypes::RequestStatus::Completed : IStreamerTypes::RequestStatus::Failed); @@ -158,16 +158,16 @@ namespace AZ::IO void QueueCanceledReadRequest(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); if (data) { ReadFile(data->m_output, data->m_path, data->m_offset, data->m_size); request->SetStatus(IStreamerTypes::RequestStatus::Canceled); m_context->MarkRequestAsCompleted(request); } - else if (AZStd::holds_alternative(request->GetCommand())) + else if (AZStd::holds_alternative(request->GetCommand())) { - auto& data2 = AZStd::get(request->GetCommand()); + auto& data2 = AZStd::get(request->GetCommand()); data2.m_found = true; data2.m_fileSize = m_fakeFileLength; request->SetStatus(IStreamerTypes::RequestStatus::Completed); diff --git a/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp b/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp index 9f2f0dbd6b..f465a0dafb 100644 --- a/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/FullDecompressorTests.cpp @@ -145,7 +145,7 @@ namespace AZ::IO void PrepareReadRequest(FileRequest* request) { - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); ASSERT_NE(nullptr, data); u64 size = data->m_size >> 2; diff --git a/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h b/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h index 7b784caffd..0059a25b93 100644 --- a/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h +++ b/Code/Framework/AzCore/Tests/Streamer/IStreamerMock.h @@ -9,6 +9,7 @@ #include #include +#include using namespace AZ::IO; diff --git a/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp b/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp index a68ce091b8..01fe2f6a12 100644 --- a/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/ReadSplitterTests.cpp @@ -155,7 +155,7 @@ namespace AZ::IO { EXPECT_EQ(subRequests[i]->GetParent(), readRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); ASSERT_NE(nullptr, data); EXPECT_EQ(SplitSize, data->m_size); EXPECT_EQ(SplitSize * i, data->m_offset); @@ -210,7 +210,7 @@ namespace AZ::IO { EXPECT_EQ(subRequests[i]->GetParent(), readRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); ASSERT_NE(nullptr, data); EXPECT_EQ(SplitSize, data->m_size); EXPECT_EQ(SplitSize * i, data->m_offset); @@ -230,7 +230,7 @@ namespace AZ::IO { EXPECT_EQ(subRequests[i]->GetParent(), readRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequests[i]->GetCommand()); ASSERT_NE(nullptr, data); EXPECT_EQ(SplitSize, data->m_size); EXPECT_EQ(SplitSize * (batchSize + i), data->m_offset); @@ -265,7 +265,7 @@ namespace AZ::IO m_readSplitter->QueueRequest(readRequest); ASSERT_NE(nullptr, subRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); EXPECT_NE(buffer, data->m_output); EXPECT_EQ(readSize, data->m_size); EXPECT_EQ(0, data->m_offset); @@ -311,7 +311,7 @@ namespace AZ::IO m_readSplitter->QueueRequest(readRequest); ASSERT_NE(nullptr, subRequest); - FileRequest::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); + Requests::ReadData* data = AZStd::get_if(&subRequest->GetCommand()); EXPECT_NE(buffer, data->m_output); EXPECT_EQ(readSize + offsetAdjustment, data->m_size); EXPECT_EQ(0, data->m_offset); diff --git a/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp b/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp index 6c360b97de..d1484b7409 100644 --- a/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp +++ b/Code/Framework/AzCore/Tests/Streamer/SchedulerTests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -86,7 +87,7 @@ namespace AZ::IO .WillOnce([this](FileRequest* request) { AZ_Assert(m_streamerContext, "AZ::IO::Streamer is not ready to process requests."); - auto readData = AZStd::get_if(&request->GetCommand()); + auto readData = AZStd::get_if(&request->GetCommand()); AZ_Assert(readData, "Test didn't pass in the correct request."); FileRequest* read = m_streamerContext->GetNewInternalRequest(); read->CreateRead(request, readData->m_output, readData->m_outputSize, readData->m_path, @@ -99,7 +100,7 @@ namespace AZ::IO .WillOnce([this](FileRequest* request) { AZ_Assert(m_streamerContext, "AZ::IO::Streamer is not ready to process requests."); - auto readData = AZStd::get_if(&request->GetCommand()); + auto readData = AZStd::get_if(&request->GetCommand()); AZ_Assert(readData, "Test didn't pass in the correct request."); auto output = reinterpret_cast(readData->m_output); AZ_Assert(output != nullptr, "Output buffer has not been set."); @@ -304,7 +305,7 @@ namespace AZ::IO EXPECT_CALL(*m_mock, QueueRequest(_)).Times(1) .WillOnce(Invoke([this](FileRequest* request) { - auto* read = request->GetCommandFromChain(); + auto* read = request->GetCommandFromChain(); ASSERT_NE(nullptr, read); EXPECT_LT(read->m_deadline, FileRequest::s_noDeadlineTime); EXPECT_EQ(read->m_priority, IStreamerTypes::s_priorityHighest); diff --git a/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h b/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h index 6cbec7ea8a..8a5805cccd 100644 --- a/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h +++ b/Code/Framework/AzCore/Tests/Streamer/StreamStackEntryConformityTests.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/StreamerTests.cpp b/Code/Framework/AzCore/Tests/StreamerTests.cpp index 78f9f9060f..937b6d29de 100644 --- a/Code/Framework/AzCore/Tests/StreamerTests.cpp +++ b/Code/Framework/AzCore/Tests/StreamerTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzCore/Tests/TestCatalog.h b/Code/Framework/AzCore/Tests/TestCatalog.h index 9a1fb9b5e6..bbcd952f78 100644 --- a/Code/Framework/AzCore/Tests/TestCatalog.h +++ b/Code/Framework/AzCore/Tests/TestCatalog.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace UnitTest { diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index aee6828b76..24a6601cf0 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -219,6 +219,8 @@ set(FILES DOM/DomFixtures.h DOM/DomJsonTests.cpp DOM/DomJsonBenchmarks.cpp + DOM/DomPathTests.cpp + DOM/DomPathBenchmarks.cpp DOM/DomValueTests.cpp DOM/DomValueBenchmarks.cpp ) diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp index 0c6b195434..a0f6b0608c 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp @@ -489,6 +489,21 @@ namespace AzFramework //========================================================================= void AssetCatalog::EnumerateAssets(BeginAssetEnumerationCB beginCB, AssetEnumerationCB enumerateCB, EndAssetEnumerationCB endCB) { + using AssetCatalogRequestBusContext = typename AZ::Data::AssetCatalogRequestBus::Context; + + // Setting trackCallstack to true causes the context mutex to attempt to re-lock. + // That is being avoided here as the code only wants to unlock the Context mutex if it is in a dispatch. + constexpr bool trackCallstack = false; + AssetCatalogRequestBusContext* assetCatalogContext = AZ::Data::AssetCatalogRequestBus::GetContext(trackCallstack); + + bool hasAssetCatalogMutex = false; + if (assetCatalogContext != nullptr && AZ::Data::AssetCatalogRequestBus::IsInDispatchThisThread(assetCatalogContext)) + { + hasAssetCatalogMutex = true; + // Unlock the dispatch mutex for the AssetCatalogRequestBus + assetCatalogContext->m_contextMutex.unlock(); + } + if (beginCB) { beginCB(); @@ -496,9 +511,13 @@ namespace AzFramework if (enumerateCB) { - AZStd::lock_guard lock(m_registryMutex); + // Make sure we don't hold on to any locks during the enumerateCB, so copy the registry info to a local variable + // and unlock the registryMutex before calling the callback. + m_registryMutex.lock(); + auto assetIdToInfoCopy = m_registry->m_assetIdToInfo; + m_registryMutex.unlock(); - for (auto& it : m_registry->m_assetIdToInfo) + for (auto& it : assetIdToInfoCopy) { enumerateCB(it.first, it.second); } @@ -508,6 +527,12 @@ namespace AzFramework { endCB(); } + + if (hasAssetCatalogMutex) + { + // Relock the mutex if it was unlocked earlier + assetCatalogContext->m_contextMutex.lock(); + } } //========================================================================= diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp index bb361e56de..35c23d119a 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp b/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp index 30c1aa39c0..2c2571ec76 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/Benchmark/BenchmarkCommands.cpp @@ -195,7 +195,7 @@ namespace AzFramework::AssetBenchmark // Console command: Add the given list of assets to the list of assets to load with BenchmarkLoadAssetList void BenchmarkAddAssetsToList(const AZ::ConsoleCommandContainer& parameters) { - bool allAssetsAdded = true; + [[maybe_unused]] bool allAssetsAdded = true; for (auto& assetName : parameters) { diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp index 589c805871..23d275cb78 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.cpp @@ -83,9 +83,9 @@ namespace AzFramework AZ_PROFILE_FUNCTION(AzCore); AZ_Assert(request, "PrepareRequest was provided a null request."); - if (AZStd::holds_alternative(request->GetCommand())) + if (AZStd::holds_alternative(request->GetCommand())) { - auto& readRequest = AZStd::get(request->GetCommand()); + auto& readRequest = AZStd::get(request->GetCommand()); FileRequest* read = m_context->GetNewInternalRequest(); read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path, @@ -106,14 +106,14 @@ namespace AzFramework { using namespace AZ::IO; using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v || - AZStd::is_same_v || - AZStd::is_same_v) + if constexpr (AZStd::is_same_v || + AZStd::is_same_v || + AZStd::is_same_v) { m_pendingRequests.push_back(request); return; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { if (CancelRequest(request, args.m_target)) { @@ -124,15 +124,15 @@ namespace AzFramework } else { - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { FlushCache(args.m_path); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FlushEntireCache(); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { Report(args); } @@ -152,15 +152,15 @@ namespace AzFramework { using namespace AZ::IO; using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { ReadFile(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileExistsRequest(request); } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { FileMetaDataRetrievalRequest(request); } @@ -232,23 +232,23 @@ namespace AzFramework { using namespace AZ::IO; using Command = AZStd::decay_t; - if constexpr (AZStd::is_same_v) + if constexpr (AZStd::is_same_v) { targetFile = &args.m_path; readSize = args.m_size; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { targetFile = &args.m_compressionInfo.m_archiveFilename; readSize = args.m_compressionInfo.m_compressedSize; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage(); startTime += averageTime; } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { readSize = 0; AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage(); @@ -280,7 +280,7 @@ namespace AzFramework AZ_PROFILE_FUNCTION(AzCore); - auto data = AZStd::get_if(&request->GetCommand()); + auto data = AZStd::get_if(&request->GetCommand()); AZ_Assert(data, "Request doing reading in the RemoteStorageDrive didn't contain read data."); HandleType file = InvalidHandle; @@ -397,7 +397,7 @@ namespace AzFramework TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage); - auto& fileExists = AZStd::get(request->GetCommand()); + auto& fileExists = AZStd::get(request->GetCommand()); size_t cacheIndex = FindFileInCache(fileExists.m_path); if (cacheIndex != s_fileNotFound) { @@ -430,7 +430,7 @@ namespace AzFramework AZ::u64 fileSize = 0; bool found = false; - auto& command = AZStd::get(request->GetCommand()); + auto& command = AZStd::get(request->GetCommand()); // If the file is already open, use the file handle which usually is cheaper than asking for the file by name. size_t cacheIndex = FindFileInCache(command.m_path); if (cacheIndex != s_fileNotFound) @@ -526,13 +526,13 @@ namespace AzFramework StreamStackEntry::CollectStatistics(statistics); } - void RemoteStorageDrive::Report(const AZ::IO::FileRequest::ReportData& data) const + void RemoteStorageDrive::Report(const AZ::IO::Requests::ReportData& data) const { using namespace AZ::IO; switch (data.m_reportType) { - case FileRequest::ReportData::ReportType::FileLocks: + case Requests::ReportType::FileLocks: for (AZ::u32 i = 0; i < m_fileHandles.size(); ++i) { if (m_fileHandles[i] != InvalidHandle) diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h index c3e28f2e55..de0b855dcc 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h +++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteStorageDrive.h @@ -16,6 +16,15 @@ #include #include +namespace AZ::IO +{ + class RequestPath; + namespace Requests + { + struct ReportData; + } +} + namespace AzFramework { struct RemoteStorageDriveConfig final : @@ -63,7 +72,7 @@ namespace AzFramework const AZ::IO::RequestPath*& activeFile) const; void FlushCache(const AZ::IO::RequestPath& filePath); void FlushEntireCache(); - void Report(const AZ::IO::FileRequest::ReportData& data) const; + void Report(const AZ::IO::Requests::ReportData& data) const; AZ::IO::RemoteFileIO m_fileIO; AZ::IO::TimedAverageWindow m_fileOpenCloseTimeAverage; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp index 168152f686..bb1270a387 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp index e78ff49f2d..a86ef12216 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyAutomation.cpp @@ -8,6 +8,7 @@ #include +#include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp index b51298a797..005ce291f6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h index da361f0a2b..585fb59ad6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h @@ -35,16 +35,15 @@ namespace Physics { } - virtual ~HeightMaterialPoint() = default; + ~HeightMaterialPoint() = default; static void Reflect(AZ::ReflectContext* context); - AZ_RTTI(HeightMaterialPoint, "{DF167ED4-24E6-4F7B-8AB7-42622F7DBAD3}"); + AZ_TYPE_INFO(HeightMaterialPoint, "{DF167ED4-24E6-4F7B-8AB7-42622F7DBAD3}"); float m_height{ 0.0f }; //!< Holds the height of this point in the heightfield relative to the heightfield entity location. QuadMeshType m_quadMeshType{ QuadMeshType::SubdivideUpperLeftToBottomRight }; //!< By default, create two triangles like this |\|, where this point is in the upper left corner. uint8_t m_materialIndex{ 0 }; //!< The surface material index for the upper left corner of this quad. uint16_t m_padding{ 0 }; //!< available for future use. - }; //! An interface to provide heightfield values. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp index a5ebdd04c6..1937c50555 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsScene.cpp @@ -9,6 +9,7 @@ #include #include +#include #include diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp index 2ff4780c9e..a0bb2d9366 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.cpp @@ -8,6 +8,7 @@ #include +#include #include namespace AzPhysics diff --git a/Code/Framework/AzFramework/AzFramework/Render/Intersector.h b/Code/Framework/AzFramework/AzFramework/Render/Intersector.h index acd6553c32..ba11996a65 100644 --- a/Code/Framework/AzFramework/AzFramework/Render/Intersector.h +++ b/Code/Framework/AzFramework/AzFramework/Render/Intersector.h @@ -5,25 +5,23 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include #include #include - #include +#include #include #include -#include - namespace AzFramework { - namespace RenderGeometry { - //! Implementation of IntersectorBus interface, this class contains a cached AABB list - //! of the connected entities to the intersection bus and calculates performant ray intersections against them + //! Implementation of IntersectorBus interface, this class contains a cached AABB list of the connected + //! entities to the intersection bus and calculates efficient ray intersections against them. class Intersector final : public IntersectorBus::Handler , protected IntersectionNotificationBus::Handler @@ -34,32 +32,32 @@ namespace AzFramework Intersector(AzFramework::EntityContextId contextId); ~Intersector(); - // IntersectorBus + // IntersectorBus overrides ... RayResult RayIntersect(const RayRequest& ray) override; - // IntersectionNotifications + // IntersectionNotificationBus overrides ... void OnEntityConnected(AZ::EntityId entityId) override; void OnEntityDisconnected(AZ::EntityId entityId) override; void OnGeometryChanged(AZ::EntityId entityId) override; private: - struct EntityData { EntityData(AZ::EntityId id, AZ::Aabb aabb) : m_id(id) , m_aabb(aabb) - , m_ref(1) {} + , m_ref(1) + { + } AZ::EntityId m_id; AZ::Aabb m_aabb; int m_ref; }; - + class EntityDataList { public: - int AddRef(AZ::EntityId entityId); int RemoveRef(AZ::EntityId entityId); void Update(const EntityData& newData); @@ -88,5 +86,5 @@ namespace AzFramework AZStd::vector> m_candidatesSortedByDist; AzFramework::EntityContextId m_contextId; }; - } -} + } // namespace RenderGeometry +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp index 79365f1ebe..b2c36bc1c8 100644 --- a/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Script/ScriptComponent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp index 8a24d164cd..e731b633f0 100644 --- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -931,6 +933,109 @@ namespace UnitTest EXPECT_FALSE(m_assetCatalog->DoesAssetIdMatchWildcardPattern(m_firstAssetId, "")); } + TEST_F(AssetCatalogAPITest, EnumerateAssetsListsCorrectAssets) + { + AZStd::vector foundAssets; + + AZ::Data::AssetCatalogRequestBus::Broadcast( + &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, + [&foundAssets](const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetInfo& assetInfo) + { + foundAssets.emplace_back(assetId); + }, + nullptr); + + ASSERT_EQ(foundAssets.size(), 2); + EXPECT_EQ(foundAssets[0], m_firstAssetId); + EXPECT_EQ(foundAssets[1], m_secondAssetId); + } + + TEST_F(AssetCatalogAPITest, EnumerateAssetsDoesNotBlockMutex) + { + // This test simulates a previously-occurring deadlock bug caused by having the main thread call AssetCatalog::EnumerateAssets + // with a callback that calls the AssetManager, and a loading thread running underneath the AssetManager that calls the + // AssetCatalog. + // To reproduce this state, we will do the following: + // Main Thread Job Thread + // wait on mainToJobSync + // call EnumerateAssets + // unblock mainToJobSync + // wait on jobToMainSync call AssetCatalog::X + // unblock jobToMainSync + // + // These steps should ensure that we create a theoretical "lock inversion", if EnumerateAssets locked an AssetCatalog mutex. + // Even though we're using binary semaphores instead of mutexes, we're creating the same blocking situation. + // EnumerateAssets itself should no longer lock the AssetCatalog mutex, so this test should succeed since there won't be any + // actual lock inversion. + + // Set up job manager with one thread that we can use to set up the concurrent mutex access. + AZ::AllocatorInstance::Create(); + AZ::JobManagerDesc desc; + AZ::JobManagerThreadDesc threadDesc; + desc.m_workerThreads.push_back(threadDesc); + auto jobManager = aznew AZ::JobManager(desc); + auto jobContext = aznew AZ::JobContext(*jobManager); + AZ::JobContext::SetGlobalContext(jobContext); + + AZStd::binary_semaphore mainToJobSync; + AZStd::binary_semaphore jobToMainSync; + + // Create a job whose sole purpose is to wait for EnumerateAssets to get called, call AssetCatalog, then tell EnumerateAssets + // to finish. If EnumerateAssets has a mutex held, this will deadlock. + auto job = AZ::CreateJobFunction( + [&mainToJobSync, &jobToMainSync]() mutable + { + // wait for the main thread to be inside the EnumerateAssets callback. + mainToJobSync.acquire(); + + // call AssetCatalog::X. This will deadlock if EnumerateAssets is holding an AssetCatalog mutex. + [[maybe_unused]] AZStd::string assetPath; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, AZ::Data::AssetId()); + + // signal the main thread to continue + jobToMainSync.release(); + }, + true); + job->Start(); + + bool testCompletedSuccessfully = false; + + // Call EnumerateAssets with a callback that also tries to lock the mutex. + AZ::Data::AssetCatalogRequestBus::Broadcast( + &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, + [this, &mainToJobSync, &jobToMainSync, &testCompletedSuccessfully] + (const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetInfo& assetInfo) + { + // Only run this logic on the first assetId. + if (assetId == m_firstAssetId) + { + // Tell the job it can continue + mainToJobSync.release(); + + // Wait for the job to finish calling AssetCatalog::X. This will deadlock if EnumerateAssets is holding an + // AssetCatalog mutex. + if (jobToMainSync.try_acquire_for(AZStd::chrono::seconds(5))) + { + // Only mark the test as completed successfully if we ran this logic and didn't deadlock. + testCompletedSuccessfully = true; + } + + } + }, + nullptr); + + EXPECT_TRUE(testCompletedSuccessfully); + + // Clean up the job manager + AZ::JobContext::SetGlobalContext(nullptr); + delete jobContext; + delete jobManager; + AZ::AllocatorInstance::Destroy(); + } + + + class AssetType1 : public AssetData { diff --git a/Code/Framework/AzFramework/Tests/CameraState.cpp b/Code/Framework/AzFramework/Tests/CameraState.cpp index 1f35d5a06c..974b5dd742 100644 --- a/Code/Framework/AzFramework/Tests/CameraState.cpp +++ b/Code/Framework/AzFramework/Tests/CameraState.cpp @@ -81,7 +81,7 @@ namespace UnitTest TEST_P(Rotation, Permutation) { - int expectedErrors = -1; + [[maybe_unused]] int expectedErrors = -1; AZ_TEST_START_TRACE_SUPPRESSION; // Given an orientation derived from the look at points diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp index ef29c05e61..849785ff01 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp @@ -411,18 +411,6 @@ namespace AzQtComponents } break; - case CE_HeaderSection: - { - if (qobject_cast(widget)) - { - if (TableView::drawHeaderSection(this, option, painter, widget, m_data->tableViewConfig)) - { - return; - } - } - } - break; - case CE_ComboBoxLabel: { if (qobject_cast(widget)) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp index c334bbb59a..2602ecf535 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/StylesheetPreprocessor.cpp @@ -134,7 +134,7 @@ namespace AzQtComponents QColor color; QString colorName(m_namedVariables.value(name)); - bool colorSet = false; + [[maybe_unused]] bool colorSet = false; if (QColor::isValidColor(colorName)) { color.setNamedColor(colorName); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp index f5cb90ae1a..b4b49bdcec 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.cpp @@ -38,7 +38,6 @@ namespace AzQtComponents ConfigHelpers::read(settings, QStringLiteral("FocusBorderWidth"), config.focusBorderWidth); ConfigHelpers::read(settings, QStringLiteral("FocusBorderColor"), config.focusBorderColor); ConfigHelpers::read(settings, QStringLiteral("FocusFillColor"), config.focusFillColor); - ConfigHelpers::read(settings, QStringLiteral("HeaderFillColor"), config.headerFillColor); settings.endGroup(); return config; @@ -52,7 +51,6 @@ namespace AzQtComponents config.focusBorderWidth = 1; config.focusBorderColor = QStringLiteral("#94D2FF"); config.focusFillColor = QStringLiteral("#10ffffff"); - config.headerFillColor = QStringLiteral("#2d2d2d"); return config; } @@ -177,22 +175,6 @@ namespace AzQtComponents return true; } - bool TableView::drawHeaderSection(const Style* style, const QStyleOption* option, QPainter* painter, const QWidget* widget, const Config& config) - { - Q_UNUSED(widget); - Q_UNUSED(style); - - const auto headerViewOption = qstyleoption_cast(option); - if (!headerViewOption) - { - return false; - } - - painter->fillRect(headerViewOption->rect, config.headerFillColor); - - return true; - } - bool TableView::drawFrameFocusRect(const Style* style, const QStyleOption* option, QPainter* painter, const Config& config) { Q_UNUSED(style); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h index 99fb77c3d8..6187f44dd5 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.h @@ -62,7 +62,6 @@ namespace AzQtComponents qreal focusBorderWidth; QColor focusBorderColor; QColor focusFillColor; - QColor headerFillColor; }; /*! @@ -93,7 +92,6 @@ namespace AzQtComponents private: static bool drawHeader(const Style* style, const QStyleOption* option, QPainter* painter, const QWidget* widget, const Config& config); - static bool drawHeaderSection(const Style* style, const QStyleOption* option, QPainter* painter, const QWidget* widget, const Config& config); static bool drawFrameFocusRect(const Style* style, const QStyleOption* option, QPainter* painter, const Config& config); static QRect itemViewItemRect(const Style* style, QStyle::SubElement element, const QStyleOptionViewItem* option, const QWidget* widget, const Config& config); static QSize sizeFromContents(const Style* style, QStyle::ContentsType type, const QStyleOption* option, const QSize& contentsSize, const QWidget* widget, const Config& config); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss index 47281eda7e..531a3378fa 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/TableView.qss @@ -66,7 +66,7 @@ QTreeView QHeaderView::section { height: 24px; - background: #444444; + background: #2d2d2d; border: none; font-size: 12px; diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp index 788ba62366..24e65ac540 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Gallery/TableViewPage.cpp @@ -134,6 +134,9 @@ TableViewPage::TableViewPage(QWidget* parent) auto logItemDelegate = new AzToolsFramework::Logging::LogTableItemDelegate(ui->logTableView); ui->logTableView->setItemDelegate(logItemDelegate); + // Example of changing the header section background color + ui->logTableView->setStyleSheet("QHeaderView::section { background: transparent; }"); + ui->qTableView->setModel(logModel); ui->qTableView->setAlternatingRowColors(true); ui->qTableView->setShowGrid(false); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 5711ca2608..4aab7288fa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -177,7 +177,7 @@ namespace AzToolsFramework auto data = index.data(AssetBrowserModel::Roles::EntryRole); if (data.canConvert()) { - bool isEnabled = (option.state & QStyle::State_Enabled) != 0; + [[maybe_unused]] bool isEnabled = (option.state & QStyle::State_Enabled) != 0; QStyle* style = option.widget ? option.widget->style() : QApplication::style(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h index 1a5ba60bdf..29d9b742e2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h @@ -63,9 +63,12 @@ namespace AzToolsFramework /// Registers an existing set of entities with the editor context. virtual void AddEditorEntities(const EntityList& entities) = 0; - /// Registers an existing set of entities with the editor context. + /// Triggers registered callbacks for an existing set of entities with the editor context. virtual void HandleEntitiesAdded(const EntityList& entities) = 0; + /// Creates an editor ready entity, and sends out notification for the creation. + virtual void FinalizeEditorEntity(AZ::Entity* entity) = 0; + /// Destroys an entity in the editor context. /// \return whether or not the entity was destroyed. A false return value signifies the entity did not belong to the game context. virtual bool DestroyEditorEntity(AZ::EntityId entityId) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp index 470ca8b9ea..95b3f2bf93 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp @@ -227,14 +227,8 @@ namespace AzToolsFramework { AZ::Entity* entity = CreateEntity(name); AZ_Assert(entity != nullptr, "Entity with name %s couldn't be created.", name); - if (m_isLegacySliceService) - { - FinalizeEditorEntity(entity); - } - else - { - EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnEditorEntityCreated, entity->GetId()); - } + FinalizeEditorEntity(entity); + return entity->GetId(); } @@ -263,14 +257,7 @@ namespace AzToolsFramework entity = aznew AZ::Entity(entityId, name); AZ_Assert(entity != nullptr, "Entity with name %s couldn't be created.", name); AddEntity(entity); - if (m_isLegacySliceService) - { - FinalizeEditorEntity(entity); - } - else - { - EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnEditorEntityCreated, entity->GetId()); - } + FinalizeEditorEntity(entity); return entity->GetId(); } @@ -284,10 +271,12 @@ namespace AzToolsFramework { return; } - SetupEditorEntity(entity); // Store creation undo command. + if (m_isLegacySliceService) { + SetupEditorEntity(entity); + ScopedUndoBatch undoBatch("Create Entity"); EntityCreateCommand* command = aznew EntityCreateCommand(static_cast(entity->GetId())); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h index 28cf67c0f2..4e04b81c67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h @@ -81,6 +81,7 @@ namespace AzToolsFramework void AddEditorEntity(AZ::Entity* entity) override; void AddEditorEntities(const EntityList& entities) override; void HandleEntitiesAdded(const EntityList& entities) override; + void FinalizeEditorEntity(AZ::Entity* entity) override; bool CloneEditorEntities(const EntityIdList& sourceEntities, EntityList& resultEntities, AZ::SliceComponent::EntityIdToEntityIdMap& sourceToCloneEntityIdMap) override; @@ -142,13 +143,9 @@ namespace AzToolsFramework } protected: - void OnContextEntitiesAdded(const EntityList& entities) override; void OnContextEntityRemoved(const AZ::EntityId& id) override; - // Helper function for creating editor ready entities. - void FinalizeEditorEntity(AZ::Entity* entity); - void SetupEditorEntity(AZ::Entity* entity); void SetupEditorEntities(const EntityList& entities); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp index 066bdc1654..b660e87b56 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.cpp @@ -241,25 +241,34 @@ namespace AzToolsFramework } } - void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled) + void QtEventToAzInputMapper::SetCursorMode(AzToolsFramework::CursorInputMode mode) { - if (m_capturingCursor != enabled) + if (mode != m_cursorMode) { - m_capturingCursor = enabled; - - if (m_capturingCursor) + m_cursorMode = mode; + switch (m_cursorMode) { - m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden); + case CursorInputMode::CursorModeCaptured: qApp->setOverrideCursor(Qt::BlankCursor); - } - else - { + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden); + break; + case CursorInputMode::CursorModeWrapped: + qApp->restoreOverrideCursor(); m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); + break; + case CursorInputMode::CursorModeNone: qApp->restoreOverrideCursor(); + m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible); + break; } } } + void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled) + { + SetCursorMode(enabled ? CursorInputMode::CursorModeCaptured : CursorInputMode::CursorModeNone); + } + bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event) { // Abort if processing isn't enabled. @@ -436,25 +445,80 @@ namespace AzToolsFramework return QPoint{ denormalizedX, denormalizedY }; } + void WrapCursorX(const QRect& rect, QPoint& point) + { + if (point.x() < rect.left()) + { + point.setX(rect.right() - 1); + } + else if (point.x() > rect.right()) + { + point.setX(rect.left() + 1); + } + } + + void WrapCursorY(const QRect& rect, QPoint& point) + { + if (point.y() < rect.top()) + { + point.setY(rect.bottom() - 1); + } + else if (point.y() > rect.bottom()) + { + point.setY(rect.top() + 1); + } + } + void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition) { const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition; + QScreen* screen = m_sourceWidget->screen(); + const QRect widgetRect(m_sourceWidget->mapToGlobal(QPoint(0, 0)), m_sourceWidget->size()); m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition)); m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta); - ProcessPendingMouseEvents(cursorDelta); - - if (m_capturingCursor) + switch (m_cursorMode) { - // Reset our cursor position to the previous point + case CursorInputMode::CursorModeCaptured: AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition); - } - else - { + break; + case CursorInputMode::CursorModeWrappedX: + case CursorInputMode::CursorModeWrappedY: + case CursorInputMode::CursorModeWrapped: + { + QPoint screenPos(globalCursorPosition); + switch (m_cursorMode) + { + case CursorInputMode::CursorModeWrappedX: + WrapCursorX(widgetRect, screenPos); + break; + case CursorInputMode::CursorModeWrappedY: + WrapCursorY(widgetRect, screenPos); + break; + case CursorInputMode::CursorModeWrapped: + WrapCursorX(widgetRect, screenPos); + WrapCursorY(widgetRect, screenPos); + break; + default: + // this should never happen + AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode); + break; + } + QCursor::setPos(screen, screenPos); + const QPoint screenDelta = globalCursorPosition - screenPos; + m_previousGlobalCursorPosition = globalCursorPosition - screenDelta; + } + break; + case CursorInputMode::CursorModeNone: m_previousGlobalCursorPosition = globalCursorPosition; + break; + default: + AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode); + break; } + ProcessPendingMouseEvents(cursorDelta); } void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h index 373945d445..67ef195363 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputMapper.h @@ -32,6 +32,17 @@ class QWheelEvent; namespace AzToolsFramework { + enum class CursorInputMode { + CursorModeNone, + CursorModeCaptured, //!< Sets whether or not the cursor should be constrained to the source widget and invisible. + //!< Internally, this will reset the cursor position after each move event to ensure movement + //!< events don't allow the cursor to escape. This can be used for typical camera controls + //!< like a dolly or rotation, where mouse movement is important but cursor location is not. + CursorModeWrapped, //!< Flags whether the curser is going to wrap around the soruce widget. + CursorModeWrappedX, //!< Flags whether the curser is going to wrap around the soruce widget only on the left and right side. + CursorModeWrappedY //!< Flags whether the curser is going to wrap around the soruce widget only on the top and bottom side. + }; + //! Maps events from the Qt input system to synthetic InputChannels in AzFramework //! that can be used by AzFramework::ViewportControllers. class QtEventToAzInputMapper final @@ -56,8 +67,12 @@ namespace AzToolsFramework //! Internally, this will reset the cursor position after each move event to ensure movement //! events don't allow the cursor to escape. This can be used for typical camera controls //! like a dolly or rotation, where mouse movement is important but cursor location is not. + //! @deprecated Use #SetCursorMode() void SetCursorCaptureEnabled(bool enabled); + //! Set the cursor mode. + void SetCursorMode(AzToolsFramework::CursorInputMode mode); + void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride); void ClearOverrideCursor(); @@ -176,8 +191,8 @@ namespace AzToolsFramework QWidget* m_sourceWidget; // Flags whether or not Qt events should currently be processed. bool m_enabled = true; - // Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement). - bool m_capturingCursor = false; + // Controls the cursor behavior. + AzToolsFramework::CursorInputMode m_cursorMode = AzToolsFramework::CursorInputMode::CursorModeNone; // Flags whether the cursor has been overridden. bool m_overrideCursor = false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index bde5de2f64..62003cd846 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -661,6 +661,7 @@ namespace AzToolsFramework entityOwningInstance.AddEntity(*entity, entityAlias); EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::HandleEntitiesAdded, EntityList{entity}); + EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::FinalizeEditorEntity, entity); AZ::Transform transform = AZ::Transform::CreateIdentity(); transform.SetTranslation(position); @@ -1063,7 +1064,7 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("No entities to duplicate.")); } - const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + const EntityIdList entityIdsNoFocusContainer = SanitizeEntityIdList(entityIds); if (entityIdsNoFocusContainer.empty()) { return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance.")); @@ -1202,7 +1203,7 @@ namespace AzToolsFramework PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants) { // Remove the container entity of the focused prefab from the list, if it is included. - const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + const EntityIdList entityIdsNoFocusContainer = SanitizeEntityIdList(entityIds); if (entityIdsNoFocusContainer.empty()) { @@ -1236,27 +1237,6 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited.")); } - // None of the specified entities can be marked as read only, otherwise this operation is invalid. - if (auto readOnlyEntityPublicInterface = AZ::Interface::Get()) - { - AZ::EntityId readOnlyEntityId; - for (const auto& entityId : entityIdsNoFocusContainer) - { - if (readOnlyEntityPublicInterface->IsReadOnly(entityId)) - { - readOnlyEntityId = entityId; - break; - } - } - - if (readOnlyEntityId.IsValid()) - { - return AZ::Failure(AZStd::string::format( - "Cannot delete entities because entity (id '%llu') is marked as read only.", - static_cast(readOnlyEntityId))); - } - } - // Retrieve entityList from entityIds EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer); @@ -1680,10 +1660,24 @@ namespace AzToolsFramework return AZ::Success(); } - EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer( - const EntityIdList& entityIds) const + EntityIdList PrefabPublicHandler::SanitizeEntityIdList(const EntityIdList& entityIds) const { - EntityIdList outEntityIds(entityIds); + EntityIdList outEntityIds; + + if (auto readOnlyEntityPublicInterface = AZ::Interface::Get()) + { + std::copy_if( + entityIds.begin(), entityIds.end(), AZStd::back_inserter(outEntityIds), + [readOnlyEntityPublicInterface](const AZ::EntityId& entityId) + { + return !readOnlyEntityPublicInterface->IsReadOnly(entityId); + } + ); + } + else + { + outEntityIds = entityIds; + } AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 41c9b4a292..a1a7ac9844 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -75,7 +75,10 @@ namespace AzToolsFramework Instance& commonRootEntityOwningInstance, EntityList& outEntities, AZStd::vector& outInstances) const; - EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const; + + //! Sanitizes an EntityIdList to remove entities that should not be affected by prefab operations. + //! It will identify and exclude the container entity of the root prefab instance, and all read-only entities. + EntityIdList SanitizeEntityIdList(const EntityIdList& entityIds) const; InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 98e0144f22..9648a9af09 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -506,7 +506,7 @@ namespace AzToolsFramework //Remove all Links owned by the Template from TemplateToLinkIdsMap. Template& templateToDelete = findTemplateResult->get(); const Template::Links& linkIdsToDelete = templateToDelete.GetLinks(); - bool result; + [[maybe_unused]] bool result; for (auto linkId : linkIdsToDelete) { result = RemoveLinkIdFromTemplateToLinkIdsMap(linkId); @@ -774,7 +774,7 @@ namespace AzToolsFramework } Link& link = findLinkResult->get(); - bool result; + [[maybe_unused]] bool result; result = RemoveLinkIdFromTemplateToLinkIdsMap(linkId, link); AZ_Assert(result, "Prefab - PrefabSystemComponent::RemoveLink - " diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp index 9d2c58a717..91517bd784 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp @@ -926,7 +926,7 @@ namespace AzToolsFramework const bool entityIsFromIgnoredSliceInstance = ignoreSliceInstance && ignoreSliceInstance->IsValid() && ignoreSliceInstance->GetReference()->GetSliceAsset().GetId() == instanceAddr.GetReference()->GetSliceAsset().GetId(); if (!entityIsFromIgnoredSliceInstance) { - bool foundTargetAncestor = false; + [[maybe_unused]] bool foundTargetAncestor = false; const AZ::SliceComponent::EntityList& entitiesInInstance = instanceAddr.GetInstance()->GetInstantiated()->m_entities; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h index a68b82468e..56a3e7005c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ToolsAssetCatalogComponent.h @@ -7,6 +7,7 @@ */ #pragma once +#include #include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index bc65b9088b..d61efe0b6d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -341,7 +341,8 @@ namespace AzToolsFramework } // Instantiate Prefab - if (selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer) + if (selectedEntities.size() == 0 || + selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer) { QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab...")); instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene.")); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h index 4596d70657..34b3a0a0b6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/Model/AssetCompleterModel.h @@ -10,12 +10,13 @@ #if !defined(Q_MOC_RUN) #include +#include #endif namespace AzToolsFramework { using namespace AzToolsFramework::AssetBrowser; - + //! Model storing all the files that can be suggested in the Asset Autocompleter for PropertyAssetCtrl class AssetCompleterModel : public QAbstractTableModel @@ -45,7 +46,7 @@ namespace AzToolsFramework void SetFetchEntryType(AssetBrowserEntry::AssetEntryType entryType); private: - struct AssetItem + struct AssetItem { AZStd::string m_displayName; AZStd::string m_path; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp index f49b555dfe..f37fd860ea 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEnumComboBoxCtrl.cpp @@ -45,7 +45,7 @@ namespace AzToolsFramework void PropertyEnumComboBoxCtrl::setValue(AZ::s64 value) { m_pComboBox->blockSignals(true); - bool indexWasFound = false; + [[maybe_unused]] bool indexWasFound = false; for (size_t enumValIndex = 0; enumValIndex < m_enumValues.size(); enumValIndex++) { if (m_enumValues[enumValIndex].first == value) diff --git a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp index 4c813a4bdd..8222a115f6 100644 --- a/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Input/QtEventToAzInputMapperTests.cpp @@ -44,6 +44,10 @@ namespace UnitTest QObject::connect(m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(), [this]([[maybe_unused]] const AzFramework::InputChannel* inputChannel, QEvent* event) { + if(event == nullptr) + { + return; + } const QEvent::Type eventType = event->type(); if (eventType == QEvent::Type::MouseButtonPress || @@ -96,6 +100,11 @@ namespace UnitTest m_azChannelEvents.push_back(AzEventInfo(inputChannel)); hasBeenConsumed = m_captureAzEvents; } + else if (inputChannelId == AzFramework::InputDeviceMouse::SystemCursorPosition) + { + m_azCursorPositions.push_back(*inputChannel.GetCustomData()); + hasBeenConsumed = m_captureAzEvents; + } } else if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId)) { @@ -161,6 +170,7 @@ namespace UnitTest AZStd::vector m_signalEvents; AZStd::vector m_azChannelEvents; AZStd::vector m_azTextEvents; + AZStd::vector m_azCursorPositions; bool m_captureAzEvents{ false }; bool m_captureTextEvents{ false }; @@ -512,4 +522,175 @@ namespace UnitTest return info.param.m_az.GetName(); } ); + + struct MouseMoveParam + { + AzToolsFramework::CursorInputMode mode; + int iterations; + QPoint startPos; + QPoint deltaPos; + QPoint expectedPos; + const char* name; + }; + + class MoveMoveWrapParamQtEventToAzInputMapperFixture + : public QtEventToAzInputMapperFixture + , public ::testing::WithParamInterface + { + }; + + TEST_P(MoveMoveWrapParamQtEventToAzInputMapperFixture, DISABLED_MouseMove_NoAzHandlers_VerifyMouseMovementViewport) + { + + // setup + const MouseMoveParam mouseMoveParam = GetParam(); + + AzFramework::InputChannelNotificationBus::Handler::BusConnect(); + m_captureAzEvents = true; + + m_rootWidget->move(100, 100); + QScreen* screen = m_rootWidget->screen(); + MouseMove(m_rootWidget.get(), mouseMoveParam.startPos, QPoint(0, 0)); + + // given + m_inputChannelMapper->SetCursorMode(mouseMoveParam.mode); + m_azCursorPositions.clear(); + for (float i = 0; i < mouseMoveParam.iterations; i++) + { + MouseMove(m_rootWidget.get(), m_rootWidget->mapFromGlobal(QCursor::pos(screen)), (mouseMoveParam.deltaPos / mouseMoveParam.iterations)); + } + + AZ::Vector2 accumulatedPosition(0.0f,0.0f); + for(const auto& pos: m_azCursorPositions) { + accumulatedPosition += (pos.m_normalizedPositionDelta * AZ::Vector2(aznumeric_cast(WidgetSize.width()), aznumeric_cast(WidgetSize.height()))); + } + + // validate + const QPoint endPosition = m_rootWidget->mapFromGlobal(QCursor::pos(screen)); + EXPECT_NEAR(endPosition.x(), mouseMoveParam.expectedPos.x(), 1.0f); + EXPECT_NEAR(endPosition.y(), mouseMoveParam.expectedPos.y(), 1.0f); + + EXPECT_NEAR(accumulatedPosition.GetX(), mouseMoveParam.deltaPos.x(), 1.0f); + EXPECT_NEAR(accumulatedPosition.GetY(), mouseMoveParam.deltaPos.y(), 1.0f); + + // cleanup + m_rootWidget->move(0, 0); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); + AzFramework::InputChannelNotificationBus::Handler::BusDisconnect(); + } + + INSTANTIATE_TEST_CASE_P(All, MoveMoveWrapParamQtEventToAzInputMapperFixture, + testing::Values( + // verify CursorModeWrappedX wrapping + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(40, 0), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + "CursorModeWrappedX_Test_Right" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(-40, 0), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), + "CursorModeWrappedX_Test_Left" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), + QPoint(0, -40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, -20), + "CursorModeWrappedX_Test_Top" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedX, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() + 20), + "CursorModeWrappedX_Test_Bottom" + }, + + // verify CursorModeWrappedY wrapping + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), + QPoint(40, 0), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() + 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), + "CursorModeWrappedY_Test_Right" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(-40, 0), + QPoint(-20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + "CursorModeWrappedY_Test_Left" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), + QPoint(0, -40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + "CursorModeWrappedY_Test_Top" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrappedY, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), + "CursorModeWrappedY_Test_Bottom" + }, + + // verify CursorModeWrapped wrapping + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), + QPoint(40, 0), + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), + "CursorModeWrapped_Test_Right" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(20, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(-40, 0), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() - 20, QtEventToAzInputMapperFixture::WidgetSize.height()/2), + "CursorModeWrapped_Test_Left" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), + QPoint(0, -40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + "CursorModeWrapped_Test_Top" + }, + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeWrapped, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() - 20), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, 20), + "CursorModeWrapped_Test_Bottom" + }, + // verify CursorModeCaptured + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeCaptured, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(0, 40), + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + "CursorModeCaptured" + }, + // verify CursorModeNone + MouseMoveParam {AzToolsFramework::CursorInputMode::CursorModeNone, + 40, + QPoint(QtEventToAzInputMapperFixture::WidgetSize.width() / 2, QtEventToAzInputMapperFixture::WidgetSize.height() / 2), + QPoint(40, 0), + QPoint((QtEventToAzInputMapperFixture::WidgetSize.width() / 2) + 40, (QtEventToAzInputMapperFixture::WidgetSize.height() / 2)), + "CursorModeNone" + } + ), + [](const ::testing::TestParamInfo& info) + { + return info.param.name; + } + ); + } // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/Slices.cpp b/Code/Framework/AzToolsFramework/Tests/Slices.cpp index 9b546357cd..bf59e1f8bd 100644 --- a/Code/Framework/AzToolsFramework/Tests/Slices.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Slices.cpp @@ -430,7 +430,7 @@ namespace UnitTest size_t nextIndex = 1; size_t slices = 0; size_t liveAllocs = 0; - size_t totalAllocs = 0; + [[maybe_unused]] size_t totalAllocs = 0; auto cb = [&liveAllocs](void*, const AZ::Debug::AllocationInfo&, unsigned char) { diff --git a/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp b/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp index 2724bee1a7..9d88f26278 100644 --- a/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp +++ b/Code/Framework/GridMate/GridMate/Carrier/Carrier.cpp @@ -1952,8 +1952,8 @@ CarrierThread::ProcessConnections() bool isHandshakeTimeOut = false; bool isConnectionTimeout = false; - bool isBadTrafficConditions = false; - bool isBadPackets = false; + [[maybe_unused]] bool isBadTrafficConditions = false; + [[maybe_unused]] bool isBadPackets = false; if (connection->m_isBadPackets) { isBadPackets = true; diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 9dfd6213c4..7b681c5208 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -10,14 +10,17 @@ #include #include +#include +#include #include +#include #include #include #include -#include #include #include #include + #include #include #include @@ -571,6 +574,17 @@ namespace O3DELauncher } #if !defined(AZ_MONOLITHIC_BUILD) + + #if !defined(_RELEASE) + // until CrySystem can be removed (or made to be managed by the component application), + // we need to manually clear the BudgetTracker before CrySystem is unloaded so the Budget + // pointer(s) it has references to are cleared properly + if (auto budgetTracker = AZ::Interface::Get(); budgetTracker) + { + budgetTracker->Reset(); + } + #endif // !defined(_RELEASE) + delete systemInitParams.pSystem; crySystemLibrary.reset(nullptr); #endif // !defined(AZ_MONOLITHIC_BUILD) diff --git a/Code/Legacy/CryCommon/ISystem.h b/Code/Legacy/CryCommon/ISystem.h index 6fdc8b52fe..14b78fd155 100644 --- a/Code/Legacy/CryCommon/ISystem.h +++ b/Code/Legacy/CryCommon/ISystem.h @@ -614,7 +614,6 @@ struct SSystemGlobalEnvironment ISystem* pSystem = nullptr; ILog* pLog; IMovieSystem* pMovieSystem; - ILyShine* pLyShine; SharedEnvironmentInstance* pSharedEnvironment; #if defined(AZ_RESTRICTED_PLATFORM) diff --git a/Code/Legacy/CryCommon/crycommon_files.cmake b/Code/Legacy/CryCommon/crycommon_files.cmake index a4d87c207a..479f0ec879 100644 --- a/Code/Legacy/CryCommon/crycommon_files.cmake +++ b/Code/Legacy/CryCommon/crycommon_files.cmake @@ -100,8 +100,6 @@ set(FILES platform_impl.cpp Win32specific.h Win64specific.h - LyShine/UiAssetTypes.h - LyShine/Bus/UiCursorBus.h Maestro/Bus/EditorSequenceAgentComponentBus.h Maestro/Bus/EditorSequenceBus.h Maestro/Bus/EditorSequenceComponentBus.h diff --git a/Code/Legacy/CrySystem/System.cpp b/Code/Legacy/CrySystem/System.cpp index 7cf066f72e..0439b69efc 100644 --- a/Code/Legacy/CrySystem/System.cpp +++ b/Code/Legacy/CrySystem/System.cpp @@ -134,7 +134,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) #include -#include #include #include @@ -1374,7 +1373,6 @@ bool CSystem::HandleMessage([[maybe_unused]] HWND hWnd, UINT uMsg, WPARAM wParam // Fall through intended case WM_ENTERMENULOOP: { - UiCursorBus::Broadcast(&UiCursorInterface::IncrementVisibleCounter); return true; } case WM_CAPTURECHANGED: @@ -1392,7 +1390,6 @@ bool CSystem::HandleMessage([[maybe_unused]] HWND hWnd, UINT uMsg, WPARAM wParam // Fall through intended case WM_EXITMENULOOP: { - UiCursorBus::Broadcast(&UiCursorInterface::DecrementVisibleCounter); return (uMsg != WM_CAPTURECHANGED); } case WM_SYSKEYUP: diff --git a/Code/Legacy/CrySystem/XConsole.cpp b/Code/Legacy/CrySystem/XConsole.cpp index df82e34abe..e74b1e7c56 100644 --- a/Code/Legacy/CrySystem/XConsole.cpp +++ b/Code/Legacy/CrySystem/XConsole.cpp @@ -29,7 +29,6 @@ #include #include -#include //#define DEFENCE_CVAR_HASH_LOGGING // s should point to a buffer at least 65 chars long @@ -749,8 +748,6 @@ void CXConsole::ShowConsole(bool show, const int iRequestScrollMax) if (show && !m_bConsoleActive) { - UiCursorBus::Broadcast(&UiCursorBus::Events::IncrementVisibleCounter); - AzFramework::InputSystemCursorRequestBus::EventResult(m_previousSystemCursorState, AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::GetSystemCursorState); @@ -760,8 +757,6 @@ void CXConsole::ShowConsole(bool show, const int iRequestScrollMax) } else if (!show && m_bConsoleActive) { - UiCursorBus::Broadcast(&UiCursorBus::Events::DecrementVisibleCounter); - AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::SetSystemCursorState, m_previousSystemCursorState); diff --git a/Code/Legacy/CrySystem/XML/xml.cpp b/Code/Legacy/CrySystem/XML/xml.cpp index 95755d78fe..fb0714500c 100644 --- a/Code/Legacy/CrySystem/XML/xml.cpp +++ b/Code/Legacy/CrySystem/XML/xml.cpp @@ -11,7 +11,6 @@ #include -#define XML_STATIC // Alternative to defining this here would be setting it project-wide #include #include "xml.h" #include diff --git a/Code/Tools/AWSNativeSDKInit/CMakeLists.txt b/Code/Tools/AWSNativeSDKInit/CMakeLists.txt index de3e0008c2..7f04262781 100644 --- a/Code/Tools/AWSNativeSDKInit/CMakeLists.txt +++ b/Code/Tools/AWSNativeSDKInit/CMakeLists.txt @@ -25,28 +25,28 @@ ly_add_target( AZ::AzCore ) -ly_add_target( - NAME AWSNativeSDKTestLibs STATIC - NAMESPACE AZ - FILES_CMAKE - aws_native_sdk_test_files.cmake - INCLUDE_DIRECTORIES - PUBLIC - include - tests/libs - PRIVATE - source - BUILD_DEPENDENCIES - PRIVATE - 3rdParty::AWSNativeSDK::Core - AZ::AzCore - AZ::AzTest -) - ################################################################################ # Tests ################################################################################ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME AWSNativeSDKTestLibs STATIC + NAMESPACE AZ + FILES_CMAKE + aws_native_sdk_test_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + include + tests/libs + PRIVATE + source + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::AWSNativeSDK::Core + AZ::AzCore + AZ::AzTest + ) + ly_add_target( NAME AWSNativeSDKInit.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE AZ diff --git a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp index 21b8c31cf4..1fdf8b569f 100644 --- a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp index 36450c4e65..634fdbd3bb 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp @@ -544,7 +544,7 @@ void ApplicationManagerBase::InitConnectionManager() EBUS_EVENT(AssetProcessor::ConnectionBus, SendPerPlatform, 0, message, QString::fromUtf8(message.m_platform.c_str())); }; - bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection); + [[maybe_unused]] bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection); AZ_Assert(result, "Failed to connect to AssetCatalog signal"); //Application manager related stuff diff --git a/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl b/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl index 039680dfae..4d1983691a 100644 --- a/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl +++ b/Code/Tools/AssetProcessor/native/utilities/BuilderManager.inl @@ -19,7 +19,7 @@ namespace AssetProcessor TNetResponse netResponse; netRequest.m_request = request; - AZ::u32 type; + [[maybe_unused]] AZ::u32 type; QByteArray data; AZStd::binary_semaphore wait; diff --git a/Code/Tools/ProjectManager/Source/Settings.cpp b/Code/Tools/ProjectManager/Source/Settings.cpp index 0a8b600dd9..78100848b5 100644 --- a/Code/Tools/ProjectManager/Source/Settings.cpp +++ b/Code/Tools/ProjectManager/Source/Settings.cpp @@ -41,7 +41,7 @@ namespace O3DE::ProjectManager o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder; o3deUserPath /= "ProjectManager.setreg"; - bool saved = false; + [[maybe_unused]] bool saved = false; constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; diff --git a/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp b/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp index 5c0d2503c0..2b7497951f 100644 --- a/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp +++ b/Code/Tools/SceneAPI/SceneBuilder/Tests/TestsMain.cpp @@ -25,7 +25,7 @@ protected: sceneCoreModule = AZ::DynamicModuleHandle::Create("SceneCore"); AZ_Assert(sceneCoreModule, "SceneBuilder unit tests failed to create SceneCore module."); - bool loaded = sceneCoreModule->Load(false); + [[maybe_unused]] bool loaded = sceneCoreModule->Load(false); AZ_Assert(loaded, "SceneBuilder unit tests failed to load SceneCore module."); auto init = sceneCoreModule->GetFunction(AZ::InitializeDynamicModuleFunctionName); AZ_Assert(init, "SceneBuilder unit tests failed to find the initialization function the SceneCore module."); diff --git a/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h b/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h index 33c7e1ad29..816e9bc4b4 100644 --- a/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h +++ b/Gems/AWSCore/Code/Include/Framework/AWSApiRequestJob.h @@ -188,28 +188,6 @@ namespace AWSCore { } - AwsApiRequestJob(bool queueOnSuccess, - OnSuccessFunction onSuccess, - bool queueOnFailure, - OnFailureFunction onFailure, - bool queueDelete, - IConfig* config = GetDefaultConfig() - ) : AwsApiClientJobType(false, config) - , m_queueOnSuccess{ queueOnSuccess } - , m_onSuccess{ onSuccess } - , m_queueOnFailure{ queueOnFailure } - , m_onFailure{ onFailure } - , m_queueDelete{ queueDelete } - { - } - - AwsApiRequestJob(OnSuccessFunction onSuccess, - OnFailureFunction onFailure, - IConfig* config = GetDefaultConfig() - ) : AwsApiRequestJob(true, onSuccess, true, onFailure, true, config) - { - } - RequestType request; ResultType result; ErrorType error; @@ -232,6 +210,21 @@ namespace AWSCore } protected: + + /// Constructor for creating AwsApiRequestJob Jobs that can handle queued responses + /// for OnSuccess, OnFailure, and DoCleanup + AwsApiRequestJob(OnSuccessFunction onSuccess, + OnFailureFunction onFailure, + IConfig* config = GetDefaultConfig() + ) : AwsApiClientJobType(false, config) + , m_queueOnSuccess{ true } + , m_onSuccess{ onSuccess } + , m_queueOnFailure{ true } + , m_onFailure{ onFailure } + , m_queueDelete{ true } + { + } + bool m_wasSuccess{ false }; // Flag and optional function call to queue for onSuccess events diff --git a/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h b/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h index 6d00122d99..d3eac4983a 100644 --- a/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h +++ b/Gems/AWSCore/Code/Include/Framework/ServiceRequestJob.h @@ -178,29 +178,6 @@ namespace AWSCore } } - ServiceRequestJob(bool queueOnSuccess, - OnSuccessFunction onSuccess, - bool queueOnFailure, - OnFailureFunction onFailure, - bool queueDelete, - IConfig* config = GetDefaultConfig() - ) : ServiceClientJobType{ false, config } - , m_requestUrl{ config->GetRequestUrl() } - , m_queueOnSuccess{ queueOnSuccess } - , m_onSuccess{ onSuccess } - , m_queueOnFailure{ queueOnFailure } - , m_onFailure{ onFailure } - , m_queueDelete{ queueDelete } - { - } - - ServiceRequestJob(OnSuccessFunction onSuccess, - OnFailureFunction onFailure, - IConfig* config = GetDefaultConfig() - ) : ServiceRequestJob(true, onSuccess, true, onFailure, true, config) - { - } - bool HasCredentials(IConfig* config) { if (config == nullptr) @@ -241,6 +218,21 @@ namespace AWSCore /// for replacing these parts of the url. const Aws::String& m_requestUrl; + /// Constructor for creating ServiceRequestJob Jobs that can handle queued responses + /// for OnSuccess, OnFailure, and DoCleanup + ServiceRequestJob(OnSuccessFunction onSuccess, + OnFailureFunction onFailure, + IConfig* config = GetDefaultConfig() + ) : ServiceClientJobType{ false, config } + , m_requestUrl{ config->GetRequestUrl() } + , m_queueOnSuccess{ true } + , m_onSuccess{ onSuccess } + , m_queueOnFailure{ true } + , m_onFailure{ onFailure } + , m_queueDelete{ true } + { + } + // Flag and optional function call to queue for onSuccess events bool m_queueOnSuccess{ false }; OnSuccessFunction m_onSuccess{}; diff --git a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp index 3b9352d250..c52d3be088 100644 --- a/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp +++ b/Gems/AWSCore/Code/Source/Editor/Attribution/AWSCoreAttributionManager.cpp @@ -223,7 +223,7 @@ namespace AWSCore return; } - bool saved {}; + [[maybe_unused]] 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(resolvedPathAWSPreference.c_str(), configurationMode)) diff --git a/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp b/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp index 18888c7166..2007ac01a9 100644 --- a/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp +++ b/Gems/AssetValidation/Code/Source/AssetSystemTestCommands.cpp @@ -120,9 +120,9 @@ namespace AssetValidation AZ::SimpleLcgRandom randomizer(seedValue); int lastTick = 0; AZStd::vector> heldAssets; - AZStd::size_t heldCount{ 0 }; - AZ::u64 changeCount{ 0 }; - AZ::u64 blockCount{ 0 }; + [[maybe_unused]] AZStd::size_t heldCount{ 0 }; + [[maybe_unused]] AZ::u64 changeCount{ 0 }; + [[maybe_unused]] AZ::u64 blockCount{ 0 }; AZ_TracePrintf("TestChangeAssets", "Beginning run with %zu assets\n", assetList.size()); while (!forceStop && runMs < runTime) { diff --git a/Gems/Atom/Feature/Common/Code/CMakeLists.txt b/Gems/Atom/Feature/Common/Code/CMakeLists.txt index a832c46539..0d779cc3ea 100644 --- a/Gems/Atom/Feature/Common/Code/CMakeLists.txt +++ b/Gems/Atom/Feature/Common/Code/CMakeLists.txt @@ -18,7 +18,6 @@ ly_add_target( ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake PLATFORM_INCLUDE_FILES ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake - ${common_source_dir}/${PAL_TRAIT_COMPILER_ID}/atom_feature_common_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index ea02a8e0a2..d690c57dea 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -537,7 +537,7 @@ namespace AZ request.m_buffer = m_boxIndexBuffer.get(); request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, m_boxIndices.size() * sizeof(uint16_t) }; request.m_initialData = m_boxIndices.data(); - AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); + [[maybe_unused]] AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); AZ_Error("DiffuseProbeGridFeatureProcessor", result == RHI::ResultCode::Success, "Failed to initialize box index buffer - error [%d]", result); // create index buffer view diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake deleted file mode 100644 index b3f7867308..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/GCC/atom_feature_common_gcc.cmake +++ /dev/null @@ -1,13 +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_source_files_properties( - Source/LuxCore/LuxCoreRenderer.cpp - PROPERTIES - COMPILE_OPTIONS -fexceptions -) diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake deleted file mode 100644 index 7a325ca97e..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/MSVC/atom_feature_common_msvc.cmake +++ /dev/null @@ -1,7 +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 -# -# diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 0a9782980f..f5c1be284e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -123,7 +123,7 @@ namespace AZ // create the BLAS buffers for each sub-mesh, or re-use existing BLAS objects if they were already created. // Note: all sub-meshes must either create new BLAS objects or re-use existing ones, otherwise it's an error (it's the same model in both cases) // Note: the buffer is just reserved here, the BLAS is built in the RayTracingAccelerationStructurePass - bool blasInstanceFound = false; + [[maybe_unused]] bool blasInstanceFound = false; for (uint32_t subMeshIndex = 0; subMeshIndex < mesh.m_subMeshes.size(); ++subMeshIndex) { SubMesh& subMesh = mesh.m_subMeshes[subMeshIndex]; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 0f9356428a..3ff574d977 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -397,7 +397,7 @@ namespace AZ request.m_buffer = m_boxIndexBuffer.get(); request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, m_boxIndices.size() * sizeof(uint16_t) }; request.m_initialData = m_boxIndices.data(); - AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); + [[maybe_unused]] AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request); AZ_Error("ReflectionProbeFeatureProcessor", result == RHI::ResultCode::Success, "Failed to initialize box index buffer - error [%d]", result); // create index buffer view diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index 79c05c37da..3279372052 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -364,7 +364,7 @@ namespace AZ AZ_Assert(success, "PipelineStateEntry already exists in the pending cache."); } - ResultCode resultCode = ResultCode::InvalidArgument; + [[maybe_unused]] ResultCode resultCode = ResultCode::InvalidArgument; // Increment the pending compile count on the global entry, which tracks how many pipeline states // are currently being compiled across all threads. diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp index a322380fb1..e4a90d0739 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingBlas.cpp @@ -120,7 +120,7 @@ namespace AZ AZ::RHI::BufferInitRequest scratchBufferRequest; scratchBufferRequest.m_buffer = buffers.m_scratchBuffer.get(); scratchBufferRequest.m_descriptor = scratchBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create BLAS scratch buffer"); BufferMemoryView* scratchMemoryView = static_cast(buffers.m_scratchBuffer.get())->GetBufferMemoryView(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp index 65281d923f..bddfb0df5e 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingPipelineState.cpp @@ -170,7 +170,7 @@ namespace AZ createInfo.basePipelineHandle = nullptr; createInfo.basePipelineIndex = 0; - VkResult result = vkCreateRayTracingPipelinesKHR(device.GetNativeDevice(), nullptr, nullptr, 1, &createInfo, nullptr, &m_pipeline); + [[maybe_unused]] VkResult result = vkCreateRayTracingPipelinesKHR(device.GetNativeDevice(), nullptr, nullptr, 1, &createInfo, nullptr, &m_pipeline); AZ_Assert(result == VK_SUCCESS, "vkCreateRayTracingPipelinesKHR failed"); // retrieve the shader handles diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp index d5ef121875..5e820a18ba 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingShaderTable.cpp @@ -43,7 +43,7 @@ namespace AZ AZ::RHI::BufferInitRequest shaderTableBufferRequest; shaderTableBufferRequest.m_buffer = shaderTableBuffer.get(); shaderTableBufferRequest.m_descriptor = shaderTableBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetShaderTableBufferPool()->InitBuffer(shaderTableBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetShaderTableBufferPool()->InitBuffer(shaderTableBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create shader table buffer"); BufferMemoryView* shaderTableMemoryView = static_cast(shaderTableBuffer.get())->GetBufferMemoryView(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp index 9ea9ceccce..08b933c96b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp @@ -66,7 +66,7 @@ namespace AZ AZ::RHI::BufferInitRequest tlasInstancesBufferRequest; tlasInstancesBufferRequest.m_buffer = buffers.m_tlasInstancesBuffer.get(); tlasInstancesBufferRequest.m_descriptor = tlasInstancesBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetTlasInstancesBufferPool()->InitBuffer(tlasInstancesBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetTlasInstancesBufferPool()->InitBuffer(tlasInstancesBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create TLAS instances buffer"); BufferMemoryView* tlasInstancesMemoryView = static_cast(buffers.m_tlasInstancesBuffer.get())->GetBufferMemoryView(); @@ -160,7 +160,7 @@ namespace AZ AZ::RHI::BufferInitRequest scratchBufferRequest; scratchBufferRequest.m_buffer = buffers.m_scratchBuffer.get(); scratchBufferRequest.m_descriptor = scratchBufferDescriptor; - RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); + [[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest); AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create TLAS scratch buffer"); BufferMemoryView* scratchMemoryView = static_cast(buffers.m_scratchBuffer.get())->GetBufferMemoryView(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h index 982de92739..60417adf36 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariantAsyncLoader.h @@ -7,13 +7,16 @@ */ #pragma once -#include -#include #include #include #include #include +#include +#include +#include +#include + namespace AZ { class ReflectContext; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h index 956367c126..4052a706b5 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h @@ -10,6 +10,7 @@ #include #include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index bd196a2e2b..99b3518173 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -267,7 +267,7 @@ namespace AZ return m_visScene->GetEntryCount(); } - + struct WorklistData { CullingDebugContext* m_debugCtx = nullptr; @@ -296,13 +296,13 @@ namespace AZ #endif return worklistData; } - + constexpr size_t WorkListCapacity = 5; using WorkListType = AZStd::fixed_vector; #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED static MaskedOcclusionCulling::CullingResult TestOcclusionCulling( - const AZStd::shared_ptr& worklistData, + const AZStd::shared_ptr& worklistData, AzFramework::VisibilityEntry* visibleEntry); #endif @@ -312,7 +312,7 @@ namespace AZ const View::UsageFlags viewFlags = worklistData->m_view->GetUsageFlags(); const RHI::DrawListMask drawListMask = worklistData->m_view->GetDrawListMask(); - uint32_t numDrawPackets = 0; + [[maybe_unused]] uint32_t numDrawPackets = 0; uint32_t numVisibleCullables = 0; AZ_Assert(worklist.size() > 0, "Received empty worklist in ProcessWorklist"); @@ -320,8 +320,8 @@ namespace AZ for (const AzFramework::IVisibilityScene::NodeData& nodeData : worklist) { //If a node is entirely contained within the frustum, then we can skip the fine grained culling. - bool nodeIsContainedInFrustum = - !worklistData->m_debugCtx->m_enableFrustumCulling || + bool nodeIsContainedInFrustum = + !worklistData->m_debugCtx->m_enableFrustumCulling || ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds); #ifdef AZ_CULL_PROFILE_VERBOSE @@ -465,7 +465,7 @@ namespace AZ #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED static MaskedOcclusionCulling::CullingResult TestOcclusionCulling( - const AZStd::shared_ptr& worklistData, + const AZStd::shared_ptr& worklistData, AzFramework::VisibilityEntry* visibleEntry) { if (!worklistData->m_maskedOcclusionCulling) @@ -527,9 +527,9 @@ namespace AZ #endif void CullingScene::ProcessCullablesCommon( - const Scene& scene [[maybe_unused]], - View& view, - AZ::Frustum& frustum [[maybe_unused]], + const Scene& scene [[maybe_unused]], + View& view, + AZ::Frustum& frustum [[maybe_unused]], void*& maskedOcclusionCulling [[maybe_unused]]) { AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesCommon() - %s", view.GetName().GetCStr()); @@ -898,7 +898,7 @@ namespace AZ { const Matrix4x4& worldToClip = viewPtr->GetWorldToClipMatrix(); Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip, Frustum::ReverseDepth::True); - m_debugCtx.m_frozenFrustums.insert({ viewPtr.get(), frustum }); + m_debugCtx.m_frozenFrustums.insert({ viewPtr.get(), frustum }); } } } @@ -911,7 +911,7 @@ namespace AZ } void CullingScene::EndCulling() - { + { m_cullDataConcurrencyCheck.soft_unlock(); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 1b5110e327..83ac77a390 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -322,7 +322,7 @@ namespace AZ void Scene::RemoveRenderPipeline(const RenderPipelineId& pipelineId) { - bool removed = false; + [[maybe_unused]] bool removed = false; for (auto it = m_pipelines.begin(); it != m_pipelines.end(); ++it) { if (pipelineId == (*it)->GetId()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp index e11624a921..460e671b4c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp @@ -11,13 +11,13 @@ #include +#include +#include #include #include #include - #include -#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp b/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp index fd19c6bcb8..83e8dbf085 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.cpp @@ -7,6 +7,8 @@ */ #include "AssetManagerTestFixture.h" + +#include #include #include diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 7b6be61050..150b06cabd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -157,6 +157,7 @@ namespace AtomToolsFramework components.insert( components.end(), { + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index b15cf53427..9cb00294a1 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -332,12 +332,12 @@ namespace AtomToolsFramework void RenderViewportWidget::BeginCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(true); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeCaptured); } void RenderViewportWidget::EndCursorCapture() { - m_inputChannelMapper->SetCursorCaptureEnabled(false); + m_inputChannelMapper->SetCursorMode(AzToolsFramework::CursorInputMode::CursorModeNone); } void RenderViewportWidget::SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h index 368b681b4c..03320d1dd8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h @@ -6,6 +6,3 @@ * */ #pragma once - -#define AZ_TRAIT_MATERIALEDITOR_EXT "" - diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h index 2f46a3a12f..03320d1dd8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Traits_Mac.h @@ -6,6 +6,3 @@ * */ #pragma once - -#define AZ_TRAIT_MATERIALEDITOR_EXT ".app" - diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h index 2f016a2488..03320d1dd8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Traits_Windows.h @@ -6,6 +6,3 @@ * */ #pragma once - -#define AZ_TRAIT_MATERIALEDITOR_EXT ".exe" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt index c5ceab5360..3c5de1cc1b 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt @@ -19,48 +19,10 @@ if(NOT PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED) endif() ly_add_target( - NAME ShaderManagementConsole.Document STATIC - NAMESPACE Gem - FILES_CMAKE - shadermanagementconsoledocument_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - Source - PUBLIC - Include - BUILD_DEPENDENCIES - PUBLIC - Gem::AtomToolsFramework.Static - Gem::AtomToolsFramework.Editor - Gem::Atom_RPI.Edit - Gem::Atom_RPI.Public - Gem::Atom_RHI.Reflect -) - -ly_add_target( - NAME ShaderManagementConsole.Window STATIC + NAME ShaderManagementConsole EXECUTABLE NAMESPACE Gem AUTOMOC AUTORCC - FILES_CMAKE - shadermanagementconsolewindow_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - Source - PUBLIC - Include - BUILD_DEPENDENCIES - PUBLIC - Gem::AtomToolsFramework.Static - Gem::AtomToolsFramework.Editor - Gem::Atom_RPI.Public -) - -ly_add_target( - NAME ShaderManagementConsole EXECUTABLE - NAMESPACE Gem FILES_CMAKE shadermanagementconsole_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -77,8 +39,9 @@ ly_add_target( PRIVATE Gem::AtomToolsFramework.Static Gem::AtomToolsFramework.Editor - Gem::ShaderManagementConsole.Window - Gem::ShaderManagementConsole.Document + Gem::Atom_RPI.Edit + Gem::Atom_RPI.Public + Gem::Atom_RHI.Reflect RUNTIME_DEPENDENCIES Gem::AtomToolsFramework.Editor Gem::EditorPythonBindings.Editor diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h deleted file mode 100644 index dd5cc01506..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentModule.h +++ /dev/null @@ -1,28 +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 ShaderManagementConsole -{ - //! Entry point for Shader Management Console Document library. - class ShaderManagementConsoleDocumentModule - : public AZ::Module - { - public: - AZ_RTTI(ShaderManagementConsoleDocumentModule, "{81D7A170-9284-4DE9-8D92-B6B94E8A2BDF}", AZ::Module); - AZ_CLASS_ALLOCATOR(ShaderManagementConsoleDocumentModule, AZ::SystemAllocator, 0); - - ShaderManagementConsoleDocumentModule(); - - //! Add required SystemComponents to the SystemEntity. - AZ::ComponentTypeList GetRequiredSystemComponents() const override; - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h deleted file mode 100644 index ff365ee98c..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Window/ShaderManagementConsoleWindowModule.h +++ /dev/null @@ -1,28 +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 ShaderManagementConsole -{ - //! Entry point for Shader Management Console Window library. - class ShaderManagementConsoleWindowModule - : public AZ::Module - { - public: - AZ_RTTI(ShaderManagementConsoleWindowModule, "{57D6239C-AE03-4ED8-9125-35C5B1625503}", AZ::Module); - AZ_CLASS_ALLOCATOR(ShaderManagementConsoleWindowModule, AZ::SystemAllocator, 0); - - ShaderManagementConsoleWindowModule(); - - //! Add required SystemComponents to the SystemEntity. - AZ::ComponentTypeList GetRequiredSystemComponents() const override; - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h index eb7d6b87a0..bd51616f7a 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h @@ -7,12 +7,12 @@ */ #pragma once -#include #include #include #include #include #include +#include namespace ShaderManagementConsole { diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp deleted file mode 100644 index a9415fd85b..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentModule.cpp +++ /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 - * - */ - -#include -#include -#include - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleDocumentModule::ShaderManagementConsoleDocumentModule() - { - // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. - m_descriptors.insert(m_descriptors.end(), { - ShaderManagementConsoleDocumentSystemComponent::CreateDescriptor(), - }); - } - - AZ::ComponentTypeList ShaderManagementConsoleDocumentModule::GetRequiredSystemComponents() const - { - return AZ::ComponentTypeList{ - azrtti_typeid(), - azrtti_typeid(), - }; - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentRequestBus.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h similarity index 100% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Document/ShaderManagementConsoleDocumentRequestBus.h rename to Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp deleted file mode 100644 index 55b800067a..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp +++ /dev/null @@ -1,86 +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 - -namespace ShaderManagementConsole -{ - void ShaderManagementConsoleDocumentSystemComponent::Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serialize = azrtti_cast(context)) - { - serialize->Class() - ->Version(0); - - if (AZ::EditContext* ec = serialize->GetEditContext()) - { - ec->Class("ShaderManagementConsoleDocumentSystemComponent", "Manages documents") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; - } - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ShaderManagementConsoleDocumentRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") - ->Event("GetShaderOptionCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount) - ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor) - ->Event("GetShaderVariantCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount) - ->Event("GetShaderVariantInfo", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo) - ; - } - } - - void ShaderManagementConsoleDocumentSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) - { - required.push_back(AZ_CRC_CE("AtomToolsDocumentSystemService")); - required.push_back(AZ_CRC_CE("AssetProcessorToolsConnection")); - required.push_back(AZ_CRC_CE("AssetDatabaseService")); - required.push_back(AZ_CRC_CE("PropertyManagerService")); - required.push_back(AZ_CRC_CE("RPISystem")); - } - - void ShaderManagementConsoleDocumentSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("ShaderManagementConsoleDocumentSystemService")); - } - - void ShaderManagementConsoleDocumentSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - incompatible.push_back(AZ_CRC_CE("ShaderManagementConsoleDocumentSystemService")); - } - - void ShaderManagementConsoleDocumentSystemComponent::Init() - { - } - - void ShaderManagementConsoleDocumentSystemComponent::Activate() - { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, - []() - { - return aznew ShaderManagementConsoleDocument(); - }); - } - - void ShaderManagementConsoleDocumentSystemComponent::Deactivate() - { - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h deleted file mode 100644 index 1ee825f6de..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentSystemComponent.h +++ /dev/null @@ -1,41 +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 ShaderManagementConsole -{ - //! ShaderManagementConsoleDocumentSystemComponent - class ShaderManagementConsoleDocumentSystemComponent - : public AZ::Component - { - public: - AZ_COMPONENT(ShaderManagementConsoleDocumentSystemComponent, "{1610159D-59DC-48B1-B2D1-FCE7AFD3B012}"); - - ShaderManagementConsoleDocumentSystemComponent() = default; - ~ShaderManagementConsoleDocumentSystemComponent() = default; - ShaderManagementConsoleDocumentSystemComponent(const ShaderManagementConsoleDocumentSystemComponent&) = delete; - ShaderManagementConsoleDocumentSystemComponent& operator =(const ShaderManagementConsoleDocumentSystemComponent&) = delete; - - static void Reflect(AZ::ReflectContext* context); - - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - - private: - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Init() override; - void Activate() override; - void Deactivate() override; - //////////////////////////////////////////////////////////////////////// - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h index 2897402f04..c4cb36c3c3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h @@ -7,5 +7,3 @@ */ #pragma once -#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h index 627cce90c9..c4cb36c3c3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Traits_Mac.h @@ -7,5 +7,3 @@ */ #pragma once -#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT ".app" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h index 4bd905f929..c4cb36c3c3 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Traits_Windows.h @@ -7,5 +7,3 @@ */ #pragma once -#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT ".exe" - diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp index a242716b52..ba8038319b 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp @@ -6,21 +6,90 @@ * */ -#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 +AZ_POP_DISABLE_WARNING + +void InitShaderManagementConsoleResources() +{ + // Must register qt resources from other modules + Q_INIT_RESOURCE(ShaderManagementConsole); + Q_INIT_RESOURCE(InspectorWidget); + Q_INIT_RESOURCE(AtomToolsAssetBrowser); +} + namespace ShaderManagementConsole { - //! This function returns the build system target name of "ShaderManagementConsole" - AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const + ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv) + : Base(argc, argv) { -#if !defined(LY_CMAKE_TARGET) -#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" -#endif - return AZStd::string_view{ LY_CMAKE_TARGET }; + InitShaderManagementConsoleResources(); + + QApplication::setApplicationName("O3DE Shader Management Console"); + + // The settings registry has been created at this point, so add the CMake target + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( + *AZ::SettingsRegistry::Get(), GetBuildTargetName()); + + ShaderManagementConsoleRequestBus::Handler::BusConnect(); + AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect(); + AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect(); + } + + ShaderManagementConsoleApplication::~ShaderManagementConsoleApplication() + { + ShaderManagementConsoleRequestBus::Handler::BusDisconnect(); + AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect(); + AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect(); + m_window.reset(); + } + + void ShaderManagementConsoleApplication::Reflect(AZ::ReflectContext* context) + { + Base::Reflect(context); + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("ShaderManagementConsoleRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") + ->Event("GetSourceAssetInfo", &ShaderManagementConsoleRequestBus::Events::GetSourceAssetInfo) + ->Event("FindMaterialAssetsUsingShader", &ShaderManagementConsoleRequestBus::Events::FindMaterialAssetsUsingShader ) + ->Event("GetMaterialInstanceShaderItems", &ShaderManagementConsoleRequestBus::Events::GetMaterialInstanceShaderItems) + ; + + behaviorContext->EBus("ShaderManagementConsoleDocumentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") + ->Event("GetShaderOptionCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount) + ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor) + ->Event("GetShaderVariantCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount) + ->Event("GetShaderVariantInfo", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo) + ; + } } const char* ShaderManagementConsoleApplication::GetCurrentConfigurationName() const @@ -34,25 +103,139 @@ namespace ShaderManagementConsole #endif } - ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv) - : Base(argc, argv) + void ShaderManagementConsoleApplication::StartCommon(AZ::Entity* systemEntity) { - QApplication::setApplicationName("O3DE Shader Management Console"); + Base::StartCommon(systemEntity); - // The settings registry has been created at this point, so add the CMake target - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( - *AZ::SettingsRegistry::Get(), GetBuildTargetName()); + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( + &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, + []() { return aznew ShaderManagementConsoleDocument(); }); } - void ShaderManagementConsoleApplication::CreateStaticModules(AZStd::vector& outModules) + AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const { - Base::CreateStaticModules(outModules); - outModules.push_back(aznew ShaderManagementConsoleDocumentModule); - outModules.push_back(aznew ShaderManagementConsoleWindowModule); +#if !defined(LY_CMAKE_TARGET) +#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" +#endif + //! Returns the build system target name of "ShaderManagementConsole" + return AZStd::string_view{ LY_CMAKE_TARGET }; } AZStd::vector ShaderManagementConsoleApplication::GetCriticalAssetFilters() const { return AZStd::vector({ "passes/", "config/" }); } + + QWidget* ShaderManagementConsoleApplication::GetAppMainWindow() + { + return m_window.get(); + } + + void ShaderManagementConsoleApplication::CreateMainWindow() + { + m_assetBrowserInteractions.reset(aznew ShaderManagementConsoleBrowserInteractions); + m_window.reset(aznew ShaderManagementConsoleWindow); + m_window->show(); + } + + void ShaderManagementConsoleApplication::DestroyMainWindow() + { + m_window.reset(); + } + + AZ::Data::AssetInfo ShaderManagementConsoleApplication::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) + { + bool result = false; + AZ::Data::AssetInfo assetInfo; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourceAssetFileName.c_str(), assetInfo, + watchFolder); + AZ_Error(nullptr, result, "Failed to get the asset info for the file: %s.", sourceAssetFileName.c_str()); + + return assetInfo; + } + + AZStd::vector ShaderManagementConsoleApplication::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) + { + // Collect the material types referencing the shader + AZStd::vector materialTypeSources; + + AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection; + assetDatabaseConnection.OpenDatabase(); + + assetDatabaseConnection.QuerySourceDependencyByDependsOnSource( + shaderFilePath.c_str(), nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any, + [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry) + { + AZStd::string assetExtension; + if (AzFramework::StringFunc::Path::GetExtension(sourceFileDependencyEntry.m_source.c_str(), assetExtension, false)) + { + if (assetExtension == "materialtype") + { + materialTypeSources.push_back(sourceFileDependencyEntry.m_source); + } + } + return true; + }); + + AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies; + for (const auto& materialTypeSource : materialTypeSources) + { + bool result = false; + AZ::Data::AssetInfo materialTypeSourceAssetInfo; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, materialTypeSource.c_str(), + materialTypeSourceAssetInfo, watchFolder); + + assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId( + materialTypeSourceAssetInfo.m_assetId.m_guid, materialTypeSourceAssetInfo.m_assetId.m_subId, + [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry) + { + AZStd::string assetExtension; + if (AzFramework::StringFunc::Path::GetExtension(entry.m_productName.c_str(), assetExtension, false)) + { + if (assetExtension == "azmaterial") + { + productDependencies.push_back(entry); + } + } + return true; + }); + } + + AZStd::vector results; + results.reserve(productDependencies.size()); + for (auto product : productDependencies) + { + assetDatabaseConnection.QueryCombinedByProductID( + product.m_productID, + [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined) + { + results.push_back({ combined.m_sourceGuid, combined.m_subID }); + return false; + }, + nullptr); + } + return results; + } + + AZStd::vector ShaderManagementConsoleApplication::GetMaterialInstanceShaderItems( + const AZ::Data::AssetId& assetId) + { + auto materialAsset = AZ::RPI::AssetUtils::LoadAssetById(assetId, AZ::RPI::AssetUtils::TraceLevel::Error); + + auto materialInstance = AZ::RPI::Material::Create(materialAsset); + AZ_Error( + nullptr, materialAsset, "Failed to get a material instance from product asset id: %s", + assetId.ToString().c_str()); + + if (materialInstance != nullptr) + { + return AZStd::vector( + materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end()); + } + return AZStd::vector(); + } } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h index 6b0365e6bd..3bb62d0756 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h @@ -8,12 +8,21 @@ #pragma once +#include #include +#include +#include +#include +#include +#include namespace ShaderManagementConsole { class ShaderManagementConsoleApplication : public AtomToolsFramework::AtomToolsDocumentApplication + , private ShaderManagementConsoleRequestBus::Handler + , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler + , private AzToolsFramework::EditorWindowRequestBus::Handler { public: AZ_TYPE_INFO(ShaderManagementConsole::ShaderManagementConsoleApplication, "{A31B1AEB-4DA3-49CD-884A-CC998FF7546F}"); @@ -21,13 +30,31 @@ namespace ShaderManagementConsole using Base = AtomToolsFramework::AtomToolsDocumentApplication; ShaderManagementConsoleApplication(int* argc, char*** argv); + ~ShaderManagementConsoleApplication(); // AzFramework::Application overrides... - void CreateStaticModules(AZStd::vector& outModules) override; + void Reflect(AZ::ReflectContext* context) override; const char* GetCurrentConfigurationName() const override; + void StartCommon(AZ::Entity* systemEntity) override; // AtomToolsFramework::AtomToolsApplication overrides... AZStd::string GetBuildTargetName() const override; AZStd::vector GetCriticalAssetFilters() const override; + + // AzToolsFramework::EditorWindowRequests::Bus::Handler + QWidget* GetAppMainWindow() override; + + // AtomToolsMainWindowFactoryRequestBus::Handler overrides... + void CreateMainWindow() override; + void DestroyMainWindow() override; + + // ShaderManagementConsoleRequestBus::Handler overrides... + AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override; + AZStd::vector FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override; + AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) override; + + private: + AZStd::unique_ptr m_window; + AZStd::unique_ptr m_assetBrowserInteractions; }; } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Core/ShaderManagementConsoleRequestBus.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h similarity index 100% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Include/Atom/Core/ShaderManagementConsoleRequestBus.h rename to Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp deleted file mode 100644 index eb863a195a..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleBrowserWidget.cpp +++ /dev/null @@ -1,201 +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 - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -#include -AZ_POP_DISABLE_WARNING - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleBrowserWidget::ShaderManagementConsoleBrowserWidget(QWidget* parent) - : QWidget(parent) - , m_ui(new Ui::ShaderManagementConsoleBrowserWidget) - { - using namespace AzToolsFramework::AssetBrowser; - - m_ui->setupUi(this); - - m_ui->m_searchWidget->Setup(true, true); - m_ui->m_searchWidget->SetFilterState("", AZ::RPI::ShaderAsset::Group, true); - m_ui->m_searchWidget->setMinimumSize(QSize(150, 0)); - - // Get the asset browser model - AssetBrowserModel* assetBrowserModel = nullptr; - AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel); - AZ_Assert(assetBrowserModel, "Failed to get file browser model"); - - // Hook up the data set to the tree view - m_filterModel = aznew AssetBrowserFilterModel(this); - m_filterModel->setSourceModel(assetBrowserModel); - m_filterModel->SetFilter(CreateFilter()); - - m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel); - m_ui->m_assetBrowserTreeViewWidget->SetShowSourceControlIcons(true); - m_ui->m_assetBrowserTreeViewWidget->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); - - // Maintains the tree expansion state between runs - m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); - - connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel, &AssetBrowserFilterModel::filterUpdatedSlot); - connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, [this]() - { - const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty(); - constexpr bool selectFirstFilteredIndex = true; - m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex); - }); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::activated, this, &ShaderManagementConsoleBrowserWidget::OpenSelectedEntries); - connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, [this]() { - const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - if (!selectedAssets.empty()) - { - m_ui->m_previewerFrame->Display(selectedAssets.front()); - } - else - { - m_ui->m_previewerFrame->Clear(); - } - }); - - AssetBrowserModelNotificationBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(); - } - - ShaderManagementConsoleBrowserWidget::~ShaderManagementConsoleBrowserWidget() - { - // Maintains the tree expansion state between runs - m_ui->m_assetBrowserTreeViewWidget->SaveState(); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); - AssetBrowserModelNotificationBus::Handler::BusDisconnect(); - } - - AzToolsFramework::AssetBrowser::FilterConstType ShaderManagementConsoleBrowserWidget::CreateFilter() const - { - using namespace AzToolsFramework::AssetBrowser; - - QSharedPointer sourceFilter(new EntryTypeFilter); - sourceFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Source); - - QSharedPointer folderFilter(new EntryTypeFilter); - folderFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Folder); - - QSharedPointer sourceOrFolderFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::OR)); - sourceOrFolderFilter->AddFilter(sourceFilter); - sourceOrFolderFilter->AddFilter(folderFilter); - - QSharedPointer finalFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::AND)); - finalFilter->AddFilter(sourceOrFolderFilter); - finalFilter->AddFilter(m_ui->m_searchWidget->GetFilter()); - - return finalFilter; - } - - void ShaderManagementConsoleBrowserWidget::OpenSelectedEntries() - { - const AZStd::vector entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); - - const int multiSelectPromptThreshold = 10; - if (entries.size() >= multiSelectPromptThreshold) - { - if (QMessageBox::question( - QApplication::activeWindow(), - QString("Attemptng to open %1 files").arg(entries.size()), - "Would you like to open anyway?", - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - { - return; - } - } - - for (const AssetBrowserEntry* entry : entries) - { - const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry); - if (!sourceEntry) - { - const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry); - if (productEntry) - { - sourceEntry = azrtti_cast(productEntry->GetParent()); - } - } - - if (sourceEntry) - { - if (AzFramework::StringFunc::Path::IsExtension(sourceEntry->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) - { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, sourceEntry->GetFullPath()); - } - else - { - QDesktopServices::openUrl(QUrl::fromLocalFile(sourceEntry->GetFullPath().c_str())); - } - } - } - } - - void ShaderManagementConsoleBrowserWidget::EntryAdded(const AssetBrowserEntry* entry) - { - if (m_pathToSelect.empty()) - { - return; - } - - const SourceAssetBrowserEntry* sourceEntry = azrtti_cast(entry); - if (!sourceEntry) - { - const ProductAssetBrowserEntry* productEntry = azrtti_cast(entry); - if (productEntry) - { - sourceEntry = azrtti_cast(productEntry->GetParent()); - } - } - - if (sourceEntry) - { - AZStd::string sourcePath = sourceEntry->GetFullPath(); - AzFramework::StringFunc::Path::Normalize(sourcePath); - if (m_pathToSelect == sourcePath) - { - m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect); - m_pathToSelect.clear(); - } - } - } - - void ShaderManagementConsoleBrowserWidget::OnDocumentOpened(const AZ::Uuid& documentId) - { - AZStd::string absolutePath; - AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath); - if (!absolutePath.empty()) - { - m_pathToSelect = absolutePath; - AzFramework::StringFunc::Path::Normalize(m_pathToSelect); - m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect); - } - } - -} // namespace ShaderManagementConsole - -#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp index 8e8e047e83..f4672d670e 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp @@ -6,15 +6,17 @@ * */ -#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 @@ -39,10 +41,6 @@ namespace ShaderManagementConsole setObjectName("ShaderManagementConsoleWindow"); - m_toolBar = new ShaderManagementConsoleToolBar(this); - m_toolBar->setObjectName("ToolBar"); - addToolBar(m_toolBar); - m_assetBrowser->SetFilterState("", AZ::RPI::ShaderAsset::Group, true); m_assetBrowser->SetOpenHandler([](const AZStd::string& absolutePath) { if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) @@ -63,10 +61,19 @@ namespace ShaderManagementConsole m_actionNew->setEnabled(false); m_actionSaveAsChild->setVisible(false); m_actionSaveAsChild->setEnabled(false); + m_actionSaveAll->setVisible(false); + m_actionSaveAll->setEnabled(false); OnDocumentOpened(AZ::Uuid::CreateNull()); } + bool ShaderManagementConsoleWindow::GetOpenDocumentParams(AZStd::string& openPath) + { + openPath = QFileDialog::getOpenFileName( + this, tr("Open Document"), AZ::Utils::GetProjectPath().c_str(), tr("Files (*.%1)").arg(AZ::RPI::ShaderVariantListSourceData::Extension)).toUtf8().constData(); + return !openPath.empty(); + } + QWidget* ShaderManagementConsoleWindow::CreateDocumentTabView(const AZ::Uuid& documentId) { AZStd::unordered_set optionNames; diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h index 1c7479e526..d5dd04c434 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h @@ -14,7 +14,6 @@ #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include #include AZ_POP_DISABLE_WARNING #endif @@ -36,8 +35,7 @@ namespace ShaderManagementConsole ~ShaderManagementConsoleWindow() = default; protected: + bool GetOpenDocumentParams(AZStd::string& openPath) override; QWidget* CreateDocumentTabView(const AZ::Uuid& documentId) override; - - ShaderManagementConsoleToolBar* m_toolBar = {}; }; } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp deleted file mode 100644 index a59712d572..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.cpp +++ /dev/null @@ -1,206 +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 - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -#include -AZ_POP_DISABLE_WARNING - -namespace ShaderManagementConsole -{ - void ShaderManagementConsoleWindowComponent::Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serialize = azrtti_cast(context)) - { - serialize->Class() - ->Version(0); - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ShaderManagementConsoleRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") - ->Event("GetSourceAssetInfo", &ShaderManagementConsoleRequestBus::Events::GetSourceAssetInfo) - ->Event("FindMaterialAssetsUsingShader", &ShaderManagementConsoleRequestBus::Events::FindMaterialAssetsUsingShader ) - ->Event("GetMaterialInstanceShaderItems", &ShaderManagementConsoleRequestBus::Events::GetMaterialInstanceShaderItems) - ; - } - } - - void ShaderManagementConsoleWindowComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) - { - required.push_back(AZ_CRC_CE("AssetBrowserService")); - required.push_back(AZ_CRC_CE("PropertyManagerService")); - required.push_back(AZ_CRC_CE("SourceControlService")); - required.push_back(AZ_CRC_CE("AtomToolsMainWindowSystemService")); - } - - void ShaderManagementConsoleWindowComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("ShaderManagementConsoleWindowService")); - } - - void ShaderManagementConsoleWindowComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - incompatible.push_back(AZ_CRC_CE("ShaderManagementConsoleWindowService")); - } - - void ShaderManagementConsoleWindowComponent::Init() - { - } - - void ShaderManagementConsoleWindowComponent::Activate() - { - AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect(); - ShaderManagementConsoleRequestBus::Handler::BusConnect(); - AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequests::EnableSourceControl, true); - } - - void ShaderManagementConsoleWindowComponent::Deactivate() - { - ShaderManagementConsoleRequestBus::Handler::BusDisconnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect(); - AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect(); - - m_window.reset(); - } - - QWidget* ShaderManagementConsoleWindowComponent::GetAppMainWindow() - { - return m_window.get(); - } - - void ShaderManagementConsoleWindowComponent::CreateMainWindow() - { - m_assetBrowserInteractions.reset(aznew ShaderManagementConsoleBrowserInteractions); - - m_window.reset(aznew ShaderManagementConsoleWindow); - m_window->show(); - } - - void ShaderManagementConsoleWindowComponent::DestroyMainWindow() - { - m_window.reset(); - } - - AZ::Data::AssetInfo ShaderManagementConsoleWindowComponent::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) - { - bool result = false; - AZ::Data::AssetInfo assetInfo; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourceAssetFileName.c_str(), assetInfo, - watchFolder); - AZ_Error(nullptr, result, "Failed to get the asset info for the file: %s.", sourceAssetFileName.c_str()); - - return assetInfo; - } - - AZStd::vector ShaderManagementConsoleWindowComponent::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) - { - // Collect the material types referencing the shader - AZStd::vector materialTypeSources; - - AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection; - assetDatabaseConnection.OpenDatabase(); - - assetDatabaseConnection.QuerySourceDependencyByDependsOnSource( - shaderFilePath.c_str(), - nullptr, - AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any, - [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry) { - AZStd::string assetExtension; - if (AzFramework::StringFunc::Path::GetExtension(sourceFileDependencyEntry.m_source.c_str(), assetExtension, false)) - { - if (assetExtension == "materialtype") - { - materialTypeSources.push_back(sourceFileDependencyEntry.m_source); - } - } - return true; - }); - - AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies; - for (const auto& materialTypeSource : materialTypeSources) - { - bool result = false; - AZ::Data::AssetInfo materialTypeSourceAssetInfo; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, - &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, - materialTypeSource.c_str(), - materialTypeSourceAssetInfo, - watchFolder - ); - - assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId( - materialTypeSourceAssetInfo.m_assetId.m_guid, - materialTypeSourceAssetInfo.m_assetId.m_subId, - [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry) { - AZStd::string assetExtension; - if (AzFramework::StringFunc::Path::GetExtension(entry.m_productName.c_str(), assetExtension, false)) - { - if (assetExtension == "azmaterial") - { - productDependencies.push_back(entry); - } - } - return true; - }); - } - - AZStd::vector results; - results.reserve(productDependencies.size()); - for (auto product : productDependencies) - { - assetDatabaseConnection.QueryCombinedByProductID( - product.m_productID, - [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined) { - results.push_back({combined.m_sourceGuid, combined.m_subID}); - return false; - }, - nullptr - ); - } - return results; - } - - AZStd::vector ShaderManagementConsoleWindowComponent::GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) - { - auto materialAsset = AZ::RPI::AssetUtils::LoadAssetById(assetId, AZ::RPI::AssetUtils::TraceLevel::Error); - - auto materialInstance = AZ::RPI::Material::Create(materialAsset); - AZ_Error(nullptr, materialAsset, "Failed to get a material instance from product asset id: %s", assetId.ToString().c_str()); - - if (materialInstance != nullptr) - { - return AZStd::vector(materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end()); - } - return AZStd::vector(); - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h deleted file mode 100644 index aab0f7ce37..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowComponent.h +++ /dev/null @@ -1,76 +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 - -#include -#include - -namespace ShaderManagementConsole -{ - //! ShaderManagementConsoleWindowComponent is the entry point for the Shader Management Console gem user interface, and is mainly - //! used for initialization and registration of other classes, including ShaderManagementConsoleWindow. - class ShaderManagementConsoleWindowComponent - : public AZ::Component - , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler - , private ShaderManagementConsoleRequestBus::Handler - , private AzToolsFramework::EditorWindowRequestBus::Handler - { - public: - AZ_COMPONENT(ShaderManagementConsoleWindowComponent, "{03976F19-3C74-49FE-A15F-7D3CADBA616C}"); - - static void Reflect(AZ::ReflectContext* context); - - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - - private: - // Temporary structure when generating shader variants. - struct ShaderVariantListInfo - { - AZStd::string m_materialFileName; - AZStd::vector m_shaderItems; - }; - - ////////////////////////////////////////////////////////////////////////// - // AzToolsFramework::EditorWindowRequests::Bus::Handler - QWidget* GetAppMainWindow() override; - ////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // AtomToolsMainWindowFactoryRequestBus::Handler overrides... - void CreateMainWindow() override; - void DestroyMainWindow() override; - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // ShaderManagementConsoleRequestBus::Handler overrides... - AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override; - AZStd::vector FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override; - AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) override; - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Init() override; - void Activate() override; - void Deactivate() override; - //////////////////////////////////////////////////////////////////////// - - AZStd::unique_ptr m_window; - AZStd::unique_ptr m_assetBrowserInteractions; - }; -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp deleted file mode 100644 index a13b873aee..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindowModule.cpp +++ /dev/null @@ -1,38 +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 - -void InitShaderManagementConsoleResources() -{ - // Must register qt resources from other modules - Q_INIT_RESOURCE(ShaderManagementConsole); - Q_INIT_RESOURCE(InspectorWidget); - Q_INIT_RESOURCE(AtomToolsAssetBrowser); -} - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleWindowModule::ShaderManagementConsoleWindowModule() - { - InitShaderManagementConsoleResources(); - - // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. - m_descriptors.insert(m_descriptors.end(), { - ShaderManagementConsoleWindowComponent::CreateDescriptor(), - }); - } - - AZ::ComponentTypeList ShaderManagementConsoleWindowModule::GetRequiredSystemComponents() const - { - return AZ::ComponentTypeList{ - azrtti_typeid(), - }; - } -} // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp deleted file mode 100644 index 5699713240..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp +++ /dev/null @@ -1,32 +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 - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -#include -#include -AZ_POP_DISABLE_WARNING - -namespace ShaderManagementConsole -{ - ShaderManagementConsoleToolBar::ShaderManagementConsoleToolBar(QWidget* parent) - : QToolBar(parent) - { - AzQtComponents::ToolBar::addMainToolBarStyle(this); - } - - ShaderManagementConsoleToolBar::~ShaderManagementConsoleToolBar() - { - } -} // namespace ShaderManagementConsole - -#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.h deleted file mode 100644 index c4d300d4e0..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ToolBar/ShaderManagementConsoleToolBar.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 - -#if !defined(Q_MOC_RUN) -#include -#endif - -namespace ShaderManagementConsole -{ - class ModelComboBox; - class LightingPresetComboBox; - - class ShaderManagementConsoleToolBar - : public QToolBar - { - Q_OBJECT - public: - - ShaderManagementConsoleToolBar(QWidget* parent = 0); - ~ShaderManagementConsoleToolBar(); - - private: - }; -} // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake index e832ba2784..dac14b7f16 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake @@ -7,8 +7,19 @@ # set(FILES + Source/Document/ShaderManagementConsoleDocumentRequestBus.h + Source/Document/ShaderManagementConsoleDocument.cpp + Source/Document/ShaderManagementConsoleDocument.h + + Source/Window/ShaderManagementConsoleBrowserInteractions.h + Source/Window/ShaderManagementConsoleBrowserInteractions.cpp + Source/Window/ShaderManagementConsoleWindow.h + Source/Window/ShaderManagementConsoleWindow.cpp + Source/Window/ShaderManagementConsole.qrc + Source/main.cpp Source/ShaderManagementConsoleApplication.cpp Source/ShaderManagementConsoleApplication.h + Source/ShaderManagementConsoleRequestBus.h ../Scripts/GenerateShaderVariantListForMaterials.py ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake deleted file mode 100644 index 220d2895ac..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsoledocument_files.cmake +++ /dev/null @@ -1,17 +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 - Include/Atom/Document/ShaderManagementConsoleDocumentModule.h - Include/Atom/Document/ShaderManagementConsoleDocumentRequestBus.h - Source/Document/ShaderManagementConsoleDocument.cpp - Source/Document/ShaderManagementConsoleDocument.h - Source/Document/ShaderManagementConsoleDocumentModule.cpp - Source/Document/ShaderManagementConsoleDocumentSystemComponent.cpp - Source/Document/ShaderManagementConsoleDocumentSystemComponent.h -) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake deleted file mode 100644 index fb5cc0dad6..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsolewindow_files.cmake +++ /dev/null @@ -1,22 +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 - Include/Atom/Window/ShaderManagementConsoleWindowModule.h - Include/Atom/Core/ShaderManagementConsoleRequestBus.h - Source/Window/ShaderManagementConsoleBrowserInteractions.h - Source/Window/ShaderManagementConsoleBrowserInteractions.cpp - Source/Window/ShaderManagementConsoleWindow.h - Source/Window/ShaderManagementConsoleWindow.cpp - Source/Window/ShaderManagementConsoleWindowModule.cpp - Source/Window/ShaderManagementConsole.qrc - Source/Window/ShaderManagementConsoleWindowComponent.h - Source/Window/ShaderManagementConsoleWindowComponent.cpp - Source/Window/ToolBar/ShaderManagementConsoleToolBar.h - Source/Window/ToolBar/ShaderManagementConsoleToolBar.cpp -) diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h index d1027daa36..b974b4443d 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/AssetCollectionAsyncLoader.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index 3234db2292..7c31751421 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -77,8 +77,7 @@ ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Servers NAMESPACE Gem if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( - NAME AtomLyIntegration_CommonFeatures.Editor GEM_MODULE - + NAME AtomLyIntegration_CommonFeatures.Editor.Static STATIC NAMESPACE Gem AUTOUIC AUTOMOC @@ -98,7 +97,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR BUILD_DEPENDENCIES - PRIVATE + PUBLIC Gem::AtomLyIntegration_CommonFeatures.Static Gem::Atom_RPI.Edit Gem::AtomToolsFramework.Static @@ -108,6 +107,24 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::Editor.Headers Legacy::EditorCommon Legacy::CryCommon + ) + + ly_add_target( + NAME AtomLyIntegration_CommonFeatures.Editor GEM_MODULE + NAMESPACE Gem + FILES_CMAKE + atomlyintegration_commonfeatures_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + COMPILE_DEFINITIONS + PRIVATE + ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR + BUILD_DEPENDENCIES + PRIVATE + Gem::AtomLyIntegration_CommonFeatures.Editor.Static RUNTIME_DEPENDENCIES Gem::Atom_RPI.Editor Gem::Atom_Feature_Common.Editor @@ -128,6 +145,33 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AtomLyIntegration_CommonFeatures.Editor Gem::GradientSignal.Tools ) + + ################################################################################ + # Tests + ################################################################################ + if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME AtomLyIntegration_CommonFeatures.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + atomlyintegration_commonfeatures_editor_test_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzTestShared + AZ::AzToolsFramework + AZ::AzToolsFrameworkTestCommon + Gem::AtomLyIntegration_CommonFeatures.Static + Gem::AtomLyIntegration_CommonFeatures.Editor.Static + ) + ly_add_googletest( + NAME Gem::AtomLyIntegration_CommonFeatures.Editor.Tests + ) + endif() endif() # AtomLyIntegration_CommonFeatures gem targets are required as part of the Editor and AssetProcessor diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index a68ad24adf..e26d328e17 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -82,7 +82,6 @@ namespace AZ ->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage) ->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate); } - } bool MeshComponentConfig::IsAssetSet() @@ -225,33 +224,42 @@ namespace AZ { } + static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId) + { + AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult( + contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); + return contextId; + } + void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair) { const AZ::EntityId entityId = entityComponentIdPair.GetEntityId(); m_entityComponentIdPair = entityComponentIdPair; m_transformInterface = TransformBus::FindFirstHandler(entityId); - AZ_Warning("MeshComponentController", m_transformInterface, "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin."); + AZ_Warning( + "MeshComponentController", m_transformInterface, + "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin."); m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId."); m_cachedNonUniformScale = AZ::Vector3::CreateOne(); AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale); - AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, - m_nonUniformScaleChangedHandler); + AZ::NonUniformScaleRequestBus::Event( + entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler); + const auto entityContextId = FindOwningContextId(entityId); MeshComponentRequestBus::Handler::BusConnect(entityId); TransformNotificationBus::Handler::BusConnect(entityId); MaterialReceiverRequestBus::Handler::BusConnect(entityId); MaterialComponentNotificationBus::Handler::BusConnect(entityId); AzFramework::BoundsRequestBus::Handler::BusConnect(entityId); - AzFramework::EntityContextId contextId; - AzFramework::EntityIdContextQueryBus::EventResult( - contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); - AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({entityId, contextId}); + AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({ entityId, entityContextId }); + AzFramework::RenderGeometry::IntersectionNotificationBus::Bind(m_intersectionNotificationBus, entityContextId); - //Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange + // Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange RegisterModel(); } @@ -291,6 +299,11 @@ namespace AZ { m_meshFeatureProcessor->SetTransform(m_meshHandle, world, m_cachedNonUniformScale); } + + // ensure the render geometry is kept in sync with any changes to the entity the mesh is on + AzFramework::RenderGeometry::IntersectionNotificationBus::Event( + m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged, + m_entityComponentIdPair.GetEntityId()); } void MeshComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale) @@ -301,7 +314,7 @@ namespace AZ m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale); } } - + RPI::ModelMaterialSlotMap MeshComponentController::GetModelMaterialSlots() const { Data::Asset modelAsset = GetModelAsset(); @@ -369,6 +382,9 @@ namespace AZ MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model); MaterialReceiverNotificationBus::Event(entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged); AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(entityId); + AzFramework::RenderGeometry::IntersectionNotificationBus::Event( + m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged, + m_entityComponentIdPair.GetEntityId()); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 76cf6a1b39..cecd744b80 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -162,6 +162,8 @@ namespace AZ bool m_isVisible = true; MeshComponentConfig m_configuration; AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne(); + //! Cached bus to use to notify RenderGeometry::Intersector the entity/component has changed. + AzFramework::RenderGeometry::IntersectionNotificationBus::BusPtr m_intersectionNotificationBus; MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changeEventHandler { @@ -173,6 +175,5 @@ namespace AZ [&](const AZ::Vector3& nonUniformScale) { HandleNonUniformScaleChange(nonUniformScale); } }; }; - } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.cpp new file mode 100644 index 0000000000..2d75cf96f5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/Main.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 + +class AtomLyIntegrationHook : public AZ::Test::ITestEnvironment +{ +public: + void SetupEnvironment() override + { + AZ::AllocatorInstance::Create(); + } + + void TeardownEnvironment() override + { + AZ::AllocatorInstance::Destroy(); + } +}; + +// required to support running integration tests with Qt +AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv) +{ + ::testing::InitGoogleMock(&argc, argv); + QApplication app(argc, argv); + AZ::Test::printUnusedParametersWarning(argc, argv); + AZ::Test::addTestEnvironments({ DEFAULT_UNIT_TEST_ENV, new AtomLyIntegrationHook }); + int result = RUN_ALL_TESTS(); + return result; +} + +IMPLEMENT_TEST_EXECUTABLE_MAIN(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp new file mode 100644 index 0000000000..bc1bf3a247 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Tests/MeshComponentControllerTests.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * 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 +{ + static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId) + { + AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult(contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); + return contextId; + } + + class IntersectionNotificationDetector : public AzFramework::RenderGeometry::IntersectionNotificationBus::Handler + { + public: + void Connect(const AzFramework::EntityContextId& entityContextId); + void Disconnect(); + + // IntersectionNotificationBus overrides ... + void OnEntityConnected(AZ::EntityId entityId) override; + void OnEntityDisconnected(AZ::EntityId entityId) override; + void OnGeometryChanged(AZ::EntityId entityId) override; + + AZ::EntityId m_lastEntityIdChanged; + }; + + void IntersectionNotificationDetector::Connect(const AzFramework::EntityContextId& entityContextId) + { + AzFramework::RenderGeometry::IntersectionNotificationBus::Handler::BusConnect(entityContextId); + } + + void IntersectionNotificationDetector::Disconnect() + { + AzFramework::RenderGeometry::IntersectionNotificationBus::Handler::BusDisconnect(); + } + + void IntersectionNotificationDetector::OnEntityConnected([[maybe_unused]] AZ::EntityId entityId) + { + } + + void IntersectionNotificationDetector::OnEntityDisconnected([[maybe_unused]] AZ::EntityId entityId) + { + } + + void IntersectionNotificationDetector::OnGeometryChanged(AZ::EntityId entityId) + { + m_lastEntityIdChanged = entityId; + } + + class MeshComponentControllerFixture : public ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override + { + m_meshComponentDescriptor = AZStd::unique_ptr(AZ::Render::MeshComponent::CreateDescriptor()); + m_meshComponentDescriptor->Reflect(GetApplication()->GetSerializeContext()); + + m_editorMeshComponentDescriptor = + AZStd::unique_ptr(AZ::Render::EditorMeshComponent::CreateDescriptor()); + m_editorMeshComponentDescriptor->Reflect(GetApplication()->GetSerializeContext()); + + m_entityId1 = CreateDefaultEditorEntity("Entity1"); + m_entityIds.push_back(m_entityId1); + + m_intersectionNotificationDetector.Connect(FindOwningContextId(m_entityId1)); + } + + void TearDownEditorFixtureImpl() override + { + bool entityDestroyed = false; + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + entityDestroyed, &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entityId1); + + m_intersectionNotificationDetector.Disconnect(); + + m_meshComponentDescriptor.reset(); + m_editorMeshComponentDescriptor.reset(); + } + + AZ::EntityId m_entityId1; + AzToolsFramework::EntityIdList m_entityIds; + AZStd::unique_ptr m_meshComponentDescriptor; + AZStd::unique_ptr m_editorMeshComponentDescriptor; + IntersectionNotificationDetector m_intersectionNotificationDetector; + }; + + TEST_F(MeshComponentControllerFixture, IntersectionNotificationBusIsNotifiedWhenMeshComponentControllerTransformIsModified) + { + auto* entity1 = AzToolsFramework::GetEntityById(m_entityId1); + entity1->Deactivate(); + entity1->CreateComponent(); + // note: RPI::Scene::GetFeatureProcessorForEntity(...) returns nullptr + // and so m_meshFeatureProcessor is null + AZ_TEST_START_TRACE_SUPPRESSION; + entity1->Activate(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + AZ::TransformBus::Event( + m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f))); + + using ::testing::Eq; + EXPECT_THAT(m_entityId1, Eq(m_intersectionNotificationDetector.m_lastEntityIdChanged)); + } +} // namespace UnitTest diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 0dea725d70..018795dcf2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -10,7 +10,6 @@ set(FILES Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h - Source/Module.cpp Source/Animation/EditorAttachmentComponent.h Source/Animation/EditorAttachmentComponent.cpp Source/EditorCommonFeaturesSystemComponent.h diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake similarity index 74% rename from Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake rename to Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake index 7a325ca97e..5a58124e0a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Common/Clang/atom_feature_common_clang.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_test_files.cmake @@ -5,3 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # + +set(FILES + Tests/Main.cpp + Tests/MeshComponentControllerTests.cpp) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 742031c84f..d2b675684f 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -38,6 +38,8 @@ #include #include +#include + namespace AZ::Render { static constexpr uint32_t s_maxActiveWrinkleMasks = 16; diff --git a/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp b/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp index b31d9f8ae0..6f97613c89 100644 --- a/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp +++ b/Gems/AtomTressFX/Code/Rendering/SharedBuffer.cpp @@ -16,6 +16,8 @@ #include #include +#include + namespace AZ::Render { //! Setting the constructor as private will create compile error to remind the developer to set diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp index 2d95ddf4dd..f799e8e69f 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.cpp @@ -8,6 +8,7 @@ #include +#include namespace Audio { @@ -285,5 +286,19 @@ namespace Audio return sResult; } + #endif // !AUDIO_RELEASE + CATLAudioFileEntry::CATLAudioFileEntry(const char* const filePath, IATLAudioFileEntryData* const implData) + : m_filePath(filePath) + , m_fileSize(0) + , m_useCount(0) + , m_memoryBlockAlignment(AUDIO_MEMORY_ALIGNMENT) + , m_flags(eAFF_NOTFOUND) + , m_dataScope(eADS_ALL) + , m_memoryBlock(nullptr) + , m_implData(implData) + { + } + + CATLAudioFileEntry::~CATLAudioFileEntry() = default; } // namespace Audio diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h index f1ff74c370..4dfceeebfc 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h +++ b/Gems/AudioSystem/Code/Source/Engine/ATLEntities.h @@ -379,19 +379,9 @@ namespace Audio class CATLAudioFileEntry { public: - explicit CATLAudioFileEntry(const char* const filePath = nullptr, IATLAudioFileEntryData* const implData = nullptr) - : m_filePath(filePath) - , m_fileSize(0) - , m_useCount(0) - , m_memoryBlockAlignment(AUDIO_MEMORY_ALIGNMENT) - , m_flags(eAFF_NOTFOUND) - , m_dataScope(eADS_ALL) - , m_memoryBlock(nullptr) - , m_implData(implData) - { - } + explicit CATLAudioFileEntry(const char* const filePath = nullptr, IATLAudioFileEntryData* const implData = nullptr); - ~CATLAudioFileEntry() = default; + ~CATLAudioFileEntry(); AZStd::string m_filePath; size_t m_fileSize; diff --git a/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp b/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp index ffa94ad956..d1f2187942 100644 --- a/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp b/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp index 14e9f74fd2..a197fee96b 100644 --- a/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp +++ b/Gems/AudioSystem/Code/Tests/AudioSystemTest.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h b/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h index 06646216ae..1fb48bb3fb 100644 --- a/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h +++ b/Gems/AudioSystem/Code/Tests/Mocks/FileCacheManagerMock.h @@ -13,6 +13,11 @@ #include #include +namespace AZ::IO +{ + class FileRequestHandle; +} + namespace Audio { class FileCacheManagerMock diff --git a/Gems/Blast/Code/Include/Blast/BlastSystemBus.h b/Gems/Blast/Code/Include/Blast/BlastSystemBus.h index 95ffd75057..bda684181a 100644 --- a/Gems/Blast/Code/Include/Blast/BlastSystemBus.h +++ b/Gems/Blast/Code/Include/Blast/BlastSystemBus.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include diff --git a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp index 56c785b310..70e7c28bac 100644 --- a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp +++ b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index c196f34716..bde1259c44 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -34,6 +34,8 @@ #include #include +#include + namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(ActorInstance, ActorInstanceAllocator, 0) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp index 9130636e2a..72d574508f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp @@ -123,7 +123,7 @@ namespace EMotionFX TargetType* targetBuffer = static_cast(targetVertexAttributeLayer->GetData()); // Fill the vertex attribute layer by iterating through the Atom meshes and copying over the vertex data for each. - size_t addedElements = 0; + [[maybe_unused]] size_t addedElements = 0; for (const AZ::RPI::ModelLodAsset::Mesh& atomMesh : sourceModelLod->GetMeshes()) { const uint32_t atomMeshVertexCount = atomMesh.GetVertexCount(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp index 54fb130ad3..116026e8c1 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp @@ -863,7 +863,7 @@ namespace EMotionFX } // process all objects for this frame - size_t totalBytesRead = 0; + [[maybe_unused]] size_t totalBytesRead = 0; const size_t numObjects = frameObjects.size(); for (size_t a = 0; a < numObjects; ++a) { diff --git a/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp b/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp index 3e1dac41c2..e6415817cc 100644 --- a/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp +++ b/Gems/EMotionFX/Code/Tests/TestAssetCode/SimpleActors.cpp @@ -15,6 +15,8 @@ #include #include +#include + namespace EMotionFX { SimpleJointChainActor::SimpleJointChainActor(size_t jointCount, const char* name) diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h b/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h index 2790a668a3..db39431ba1 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientPreviewRenderer.h @@ -25,7 +25,6 @@ #include #include - namespace GradientSignal { //! EditorGradientPreviewUpdateJob offloads the creation of a gradient preview image to another thread. @@ -223,7 +222,7 @@ namespace GradientSignal // If you ever want to make this use a smaller number of passes, set this value to any even number. // A value of 0 is the same as "non-interlaced", 6 would exactly use the Adam7 algorithm, etc. // It's currently clamping to 30 as a somewhat aribtrary choice. - const uint64_t maxFinalInterlacingPass = 30; + const int64_t maxFinalInterlacingPass = 30; m_finalInterlacingPass = AZ::GetMin(m_finalInterlacingPass, maxFinalInterlacingPass); // Finally, lock our mutex, modify our status variables, and start the Job. @@ -238,6 +237,8 @@ namespace GradientSignal //! Process runs exactly once for each time Start() is called on a Job, and processes on a Job worker thread. void Process() override { + AZ_PROFILE_FUNCTION(Entity); + // Guard against the case that we're trying to cancel even before we've started to run. if (!m_shouldCancel) { @@ -247,88 +248,136 @@ namespace GradientSignal // This is the "striding value". When walking directly through our preview image bits() buffer, there might be // extra pad bytes for each line due to alignment. We use this to make sure we start writing each line at the right byte offset. - const uint64_t imageBytesPerLine = m_previewImage->bytesPerLine(); - - // The following are all used for calculating our interlaced pixel updates. - - // The current interlace pass that we're on. - int64_t curPass = 0; - // The index of the first pixel for this pass. This is used to calculate the relative pixel index per pass. - uint64_t firstPixelPerPass = 0; - // Total number of pixels that we'll process per pass. After the first two passes, the amount doubles per pass till we reach 100%. - uint64_t totalPixelsPerPass = (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo) / (1LL << (m_finalInterlacingPass - curPass)); - // The general interlace formulas need a multiplier and an offset for x and y to apply to each relative pixel index. - // The pixel multipliers start high and reduce on each pass to increase the pixel density per pass. - // The pixel offsets alternate between 0 and a reducing number because we start on aligned grids, then fill in the midpoints - // of the grids on every other pass. - uint64_t xPixelMult = 1LL << (m_finalInterlacingPass / 2); - uint64_t xPixelOffset = 0; - uint64_t yPixelMult = 1LL << (m_finalInterlacingPass / 2); - uint64_t yPixelOffset = 0; - - // The heart of the processing - loop through each pixel using interlaced indexing, get the gradient value, and write it into - // the pixel buffer. On each pixel, we also check to see if the main thread requested a cancel so that we can early-out. - // The loop itself runs through the full square power-of-two bounds that encapsulates our image so that we can perform our - // interlaced indexing easily, but we skip processing any pixel that falls outside the actual image bounds. - for (uint64_t curPixel = 0; (!m_shouldCancel) && (curPixel < (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo)); curPixel++) + const int64_t imageBytesPerLine = m_previewImage->bytesPerLine(); + + // Keep track of the total number of pixels that we intend to process. For easy interlacing calculations, we always use + // square power-of-two conceptual images, but we'll skip any pixels that fall outside of our actual image bounds. + const int64_t totalPixels = (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo); + + // Preallocate buffers for our gradient lookup positions, our gradient output values, and the corresponding pixel buffer + // index to store the value into. These allow us to fetch gradient values in bulk, which is much faster than fetching them + // individually. The max size we'll need is for our last interacing pass which requests 50% of our total pixels (as + // described further below), so that's what we will preallocate. + AZStd::vector gradientLookupPositions(totalPixels / 2); + AZStd::vector gradientValues(totalPixels / 2); + AZStd::vector pixelBufferIndex(totalPixels / 2); + + // The following loop uses a variant of the Adam7 interlacing algorithm that's been generalized to work for N passes, + // instead of exactly 7 passes. The first two passes fill in 1 pixel each, and then each subsequent pass doubles the + // number of pixels it fills in, until the last pass fills in 50%. + // Note that m_finalInteracingPass contains the value of the final pass to process, not the total number of passes. + // On each pass, we'll also early-out if the main thread requested a cancellation. + for (int64_t curPass = 0; (!m_shouldCancel) && (curPass <= m_finalInterlacingPass); curPass++) { - // Check to see if we've finished the pixels for this pass and need to move on to the next pass. - if (curPixel >= (firstPixelPerPass + totalPixelsPerPass)) + gradientLookupPositions.clear(); + pixelBufferIndex.clear(); + gradientValues.clear(); + + // The general interlace formulas need a multiplier and an offset for x and y to apply to each relative pixel index. + // + // The first 3 passes are a little different than the others because they establish the base pattern: + // 1 . . . 2 . . . + // . . . . . . . . + // . . . . . . . . + // . . . . . . . . + // 3 . . . 3 . . . + // . . . . . . . . + // . . . . . . . . + // . . . . . . . . + // + // Every 2 passes from then on do the same thing, with shrinking grids. One pass fills in the grid X midpoints on the + // lines that were already processed, and the second pass fills in all the equivalent points on the Y grid midpoints + // x . 4 . x . 4 . + // . . . . . . . . + // 5 . 5 . 5 . 5 . + // . . . . . . . . + // x . 4 . x . 4 . + // . . . . . . . . + // 5 . 5 . 5 . 5 . + // . . . . . . . . + // + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // x 6 x 6 x 6 x 6 + // 7 7 7 7 7 7 7 7 + // + // The total number of pixels processed per pass starts at 1 pixel each for the first two passes, then doubles per + // pass till we reach 50% in the last pass, since all the other passes before it will have covered the other 50%. + // Ex: 7 passes will do N/64, N/64, N/32, N/16, N/8, N/4, N/2 pixels per pass. + + + // For X, we want our starting pixel offset to alternate between 0 and a decreasing power of 2 on every pass, and + // our stride to decrease by a power of 2 every two passes, ending with an offset of 0 and a stride of 1 on the last + // pass. + const int64_t xOffsetShifter = AZ::GetMin(m_finalInterlacingPass - curPass, m_finalInterlacingPass - 1); + const int64_t xPixelOffset = (curPass % 2) * (1LL << (xOffsetShifter / 2)); + const int64_t xPixelStride = 1LL << ((xOffsetShifter + 1) / 2); + + // For Y, we want our starting pixel offset and our stride to behave the same as X, except that we hold our starting + // offset and stride for one additional pass which is what causes the first 3 passes to behave differently than the + // rest. The pass offset between X and Y is also what causes the pattern to keep filling in pixels and lines that + // haven't already been processed. + const int64_t laggingPass = AZ::GetMax(curPass - 1, 0); + const int64_t yOffsetShifter = AZ::GetMin(m_finalInterlacingPass - laggingPass, m_finalInterlacingPass - 1); + const int64_t yPixelOffset = (laggingPass % 2) * (1LL << (yOffsetShifter / 2)); + const int64_t yPixelStride = 1LL << ((yOffsetShifter + 1) / 2); + + // First, we loop and fill in all the gradientLookupPositions and pixelBufferIndex values for any pixels that don't + // get culled out. We're using a power of two for calculating our interlacing offsets and strides, but we don't need + // to actually process any of those pixels that fall outside our image bounds, so we end our loops at the bounds. + for (int64_t y = yPixelOffset; y < m_imageBoundsY; y += yPixelStride) { - curPass++; - - // Adjust our interlacing formula adjustments on each pass. These will cause us to process an increasing - // number of pixels at a higher density on each pass, interleaving in a way that ensures each pixel is only - // processed once at the end. - yPixelMult = xPixelMult; - yPixelOffset = xPixelOffset; - xPixelMult = 1LL << ((m_finalInterlacingPass - curPass + 1) / 2); - xPixelOffset = (curPass % 2) * (1LL << ((m_finalInterlacingPass - curPass) / 2)); - - firstPixelPerPass += totalPixelsPerPass; - totalPixelsPerPass = (m_imageBoundsPowerOfTwo * m_imageBoundsPowerOfTwo) / (1LL << (m_finalInterlacingPass - curPass + 1)); + for (int64_t x = xPixelOffset; x < m_imageBoundsX; x += xPixelStride) + { + // Map the pixel coordinate back into world coordinates for the shape and gradient queries. Note that we + // invert world y to match the world axis. (We use "imageBoundsY- 1" to invert because our loop doesn't go all + // the way to imageBoundsY) + AZ::Vector3 uvw(static_cast(x), static_cast((m_imageBoundsY - 1) - y), 0.0f); + AZ::Vector3 position = m_previewBoundsStart + (uvw * m_pixelToBoundsScale) + m_scaledTexelOffset; + + // If our preview is only drawing what appears inside the given shape, check to see if the pixel should be + // drawn. + bool inBounds = true; + if (m_constrainToShape) + { + LmbrCentral::ShapeComponentRequestsBus::EventResult( + inBounds, m_previewEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, position); + } + + // If we're drawing this pixel, push it into our buffer of lookup positions. + if (inBounds) + { + gradientLookupPositions.emplace_back(position); + pixelBufferIndex.emplace_back(((m_centeringOffsetY + y) * imageBytesPerLine) + (m_centeringOffsetX + x)); + } + } } - // Here's where interlacing happens. If this were non-interlaced, we'd simply have the following: - // x = curPixel % m_imageBoundsPowerOfTwo - // y = curPixel / m_imageBoundsPowerOfTwo - uint64_t adjustedPixel = curPixel - firstPixelPerPass; - uint64_t x = ((adjustedPixel * xPixelMult) + xPixelOffset) % m_imageBoundsPowerOfTwo; - uint64_t y = ((((adjustedPixel * xPixelMult) + xPixelOffset) / m_imageBoundsPowerOfTwo) * yPixelMult) + yPixelOffset; + // Resize our output buffer to match our input buffer and query for all the gradient values at once. + gradientValues.resize(gradientLookupPositions.size()); + m_sampler.GetValues(gradientLookupPositions, gradientValues); - // Since we're using a power of two for calculating our interlacing, it's possible to get pixel offsets beyond the bounds - // of our actual image. We just skip those and continue on to the next pixel. - if ((x >= m_imageBoundsX) || (y >= m_imageBoundsY)) + // For each output value, run it through a filter if we were given one, then store it in the pixel buffer. + for (size_t index = 0; index < gradientLookupPositions.size(); index++) { - continue; - } - - // Now that we've calculated the pixel position, update it with the gradient value. - { - // Invert world y to match axis. (We use "imageBoundsY- 1" to invert because our loop doesn't go all the way to imageBoundsY) - AZ::Vector3 uvw(static_cast(x), static_cast((m_imageBoundsY - 1) - y), 0.0f); - - GradientSampleParams sampleParams; - sampleParams.m_position = m_previewBoundsStart + (uvw * m_pixelToBoundsScale) + m_scaledTexelOffset; - - bool inBounds = true; - if (m_constrainToShape) - { - LmbrCentral::ShapeComponentRequestsBus::EventResult(inBounds, m_previewEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, sampleParams.m_position); - } - - float sample = inBounds ? m_sampler.GetValue(sampleParams) : 0.0f; + float sample = gradientValues[index]; if (m_filterFunc) { + GradientSampleParams sampleParams; + sampleParams.m_position = gradientLookupPositions[index]; sample = m_filterFunc(sample, sampleParams); } - buffer[((m_centeringOffsetY + y) * imageBytesPerLine) + (m_centeringOffsetX + x)] = static_cast(sample * 255); - } + buffer[pixelBufferIndex[index]] = static_cast(sample * 255); - // Notify the main thread via atomic bool that the image has changed by at least one pixel. - m_refreshUI = true; + // Notify the main thread via atomic bool that the image has changed by at least one pixel. + m_refreshUI = true; + } } } @@ -356,15 +405,15 @@ namespace GradientSignal AZ::EntityId m_previewEntityId; // Values calculated during preview setup that we'll use during processing - uint64_t m_imageBoundsX = 0; - uint64_t m_imageBoundsY = 0; - uint64_t m_centeringOffsetX = 0; - uint64_t m_centeringOffsetY = 0; + int64_t m_imageBoundsX = 0; + int64_t m_imageBoundsY = 0; + int64_t m_centeringOffsetX = 0; + int64_t m_centeringOffsetY = 0; AZ::Vector3 m_previewBoundsStart; AZ::Vector3 m_pixelToBoundsScale; AZ::Vector3 m_scaledTexelOffset; - uint64_t m_imageBoundsPowerOfTwo = 1; - uint64_t m_finalInterlacingPass = 0; + int64_t m_imageBoundsPowerOfTwo = 1; + int64_t m_finalInterlacingPass = 0; // Communication / synchronization mechanisms between the different threads AZStd::mutex m_previewMutex; @@ -456,3 +505,4 @@ namespace GradientSignal EditorGradientPreviewUpdateJob* m_updateJob = nullptr; }; } //namespace GradientSignal + diff --git a/Gems/GraphModel/Code/Source/Model/Node.cpp b/Gems/GraphModel/Code/Source/Model/Node.cpp index f51bd1f617..e55b7c05a4 100644 --- a/Gems/GraphModel/Code/Source/Model/Node.cpp +++ b/Gems/GraphModel/Code/Source/Model/Node.cpp @@ -68,7 +68,7 @@ namespace GraphModel CreateSlotData(m_inputEventSlots, m_inputEventSlotDefinitions); CreateSlotData(m_outputEventSlots, m_outputEventSlotDefinitions); - int numExtendableSlots = 0; + [[maybe_unused]] int numExtendableSlots = 0; for (auto it = m_extendableSlots.begin(); it != m_extendableSlots.end(); it++) { numExtendableSlots += aznumeric_cast(it->second.size()); diff --git a/Gems/GraphModel/Code/Source/Model/Slot.cpp b/Gems/GraphModel/Code/Source/Model/Slot.cpp index 08b52760cb..108067cc93 100644 --- a/Gems/GraphModel/Code/Source/Model/Slot.cpp +++ b/Gems/GraphModel/Code/Source/Model/Slot.cpp @@ -491,7 +491,7 @@ namespace GraphModel // multiple supported types, Slot::GetDataType() will call GetParentNode() // to try and resolve its type, which will be a nullptr at this point // because the parent won't be valid yet - bool valueTypeSupported = false; + [[maybe_unused]] bool valueTypeSupported = false; DataTypePtr valueDataType = GetGraphContext()->GetDataTypeForValue(m_value); for (DataTypePtr dataType : GetSupportedDataTypes()) { diff --git a/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp b/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp index acd1bc8dae..91ba63ddd1 100644 --- a/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Asset/AssetSystemDebugComponent.cpp @@ -9,7 +9,9 @@ #include "AssetSystemDebugComponent.h" #include "ISystem.h" #include "IRenderAuxGeom.h" + #include "AzCore/Asset/AssetManager.h" +#include #include #include #include diff --git a/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp b/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp index 827e20b02b..3b14822076 100644 --- a/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Audio/AudioAreaEnvironmentComponent.cpp @@ -8,6 +8,7 @@ #include "AudioAreaEnvironmentComponent.h" +#include #include #include #include diff --git a/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp b/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp index 3fe399d05d..ccaa1700f9 100644 --- a/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp +++ b/Gems/LmbrCentral/Code/Source/Builders/CopyDependencyBuilder/FontBuilderWorker/FontBuilderWorker.cpp @@ -9,7 +9,6 @@ #include "FontBuilderWorker.h" #include -#include #include #include @@ -52,7 +51,8 @@ namespace CopyDependencyBuilder if (fileExtension == "font" || fileExtension == "fontfamily") { - return azrtti_typeid(); + static AZ::Data::AssetType fontAssetType("{57767D37-0EBE-43BE-8F60-AB36D2056EF8}"); // form UiAssetTypes.h in the LyShine gem + return fontAssetType; } return AZ::Data::AssetType::CreateNull(); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp index 8b697f35f2..7767e4f239 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp @@ -19,8 +19,6 @@ #include #include -#include - #include #include #include diff --git a/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp b/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp index 30a716b142..e90252e54a 100644 --- a/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp +++ b/Gems/LyShine/Code/Editor/CommandCanvasPropertiesChange.cpp @@ -64,7 +64,7 @@ void CommandCanvasPropertiesChange::Recreate(bool isUndo) // Create a new canvas from the XML and release the old canvas, use the new entity context for // the new canvas const AZStd::string& xml = isUndo ? m_undoXml : m_redoXml; - gEnv->pLyShine->ReloadCanvasFromXml(xml, newEntityContext); + AZ::Interface::Get()->ReloadCanvasFromXml(xml, newEntityContext); // Tell the editor window to use the new entity context m_editorWindow->ReplaceEntityContext(newEntityContext); diff --git a/Gems/LyShine/Code/Editor/EditorWindow.cpp b/Gems/LyShine/Code/Editor/EditorWindow.cpp index 7b60474ed5..8f1d574ae5 100644 --- a/Gems/LyShine/Code/Editor/EditorWindow.cpp +++ b/Gems/LyShine/Code/Editor/EditorWindow.cpp @@ -315,7 +315,7 @@ EditorWindow::~EditorWindow() // unload the preview mode canvas if it exists (e.g. if we close the editor window while in preview mode) if (m_previewModeCanvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvas(m_previewModeCanvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(m_previewModeCanvasEntityId, false); } delete m_sliceLibraryTree; @@ -564,7 +564,7 @@ bool EditorWindow::OnPreWarning(const char* /*window*/, const char* /*fileName*/ void EditorWindow::DestroyCanvas(const UiCanvasMetadata& canvasMetadata) { - gEnv->pLyShine->ReleaseCanvas(canvasMetadata.m_canvasEntityId, true); + AZ::Interface::Get()->ReleaseCanvas(canvasMetadata.m_canvasEntityId, true); } bool EditorWindow::IsCanvasTabMetadataValidForTabIndex(int index) @@ -950,14 +950,14 @@ bool EditorWindow::LoadCanvas(const QString& canvasFilename, bool autoLoad, bool bool errorsOnLoad = false; if (canvasFilename.isEmpty()) { - canvasEntityId = gEnv->pLyShine->CreateCanvasInEditor(entityContext); + canvasEntityId = AZ::Interface::Get()->CreateCanvasInEditor(entityContext); } else { // Collect errors and warnings during the canvas load AZ::Debug::TraceMessageBus::Handler::BusConnect(); - canvasEntityId = gEnv->pLyShine->LoadCanvasInEditor(assetIdPathname.c_str(), sourceAssetPathName.c_str(), entityContext); + canvasEntityId = AZ::Interface::Get()->LoadCanvasInEditor(assetIdPathname.c_str(), sourceAssetPathName.c_str(), entityContext); // Stop receiving error and warning events AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); @@ -1603,7 +1603,7 @@ void EditorWindow::ToggleEditorMode() EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, m_previewModeCanvasEntityId); if (entity) { - gEnv->pLyShine->ReleaseCanvas(m_previewModeCanvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(m_previewModeCanvasEntityId, false); } m_previewModeCanvasEntityId.SetInvalid(); } diff --git a/Gems/LyShine/Code/Editor/GuideHelpers.cpp b/Gems/LyShine/Code/Editor/GuideHelpers.cpp index b6d7acc57a..0713782063 100644 --- a/Gems/LyShine/Code/Editor/GuideHelpers.cpp +++ b/Gems/LyShine/Code/Editor/GuideHelpers.cpp @@ -134,7 +134,7 @@ namespace GuideHelpers AZ::Matrix4x4 transform; EBUS_EVENT_ID_RESULT(transform, canvasEntityId, UiCanvasBus, GetCanvasToViewportMatrix); - AZ::Vector2 viewportSize(aznumeric_cast(viewport->size().width()), aznumeric_cast(viewport->size().height())); + AZ::Vector2 viewportSize = viewport->GetRenderViewportSize(); AZ::Color guideColor; EBUS_EVENT_ID_RESULT(guideColor, canvasEntityId, UiEditorCanvasBus, GetGuideColor); @@ -167,8 +167,7 @@ namespace GuideHelpers void DrawGhostGuideLine(Draw2dHelper& draw2d, AZ::EntityId canvasEntityId, bool guideIsVertical, ViewportWidget* viewport, const AZ::Vector2& canvasPoint) { AZ::Vector2 viewportPoint = CanvasHelpers::GetViewportPoint(canvasEntityId, canvasPoint); - - AZ::Vector2 viewportSize(aznumeric_cast(viewport->size().width()), aznumeric_cast(viewport->size().height())); + AZ::Vector2 viewportSize = viewport->GetRenderViewportSize(); // the line is drawn as the inverse of the background color AZ::Color guideColor(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp b/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp index 229101d4c0..3ec6980cfb 100644 --- a/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp +++ b/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp @@ -211,9 +211,9 @@ namespace LyShineEditor void LyShineEditorSystemComponent::OnStopPlayInEditor() { // reset UI system - if (gEnv->pLyShine) + if (AZ::Interface::Get()) { - gEnv->pLyShine->Reset(); + AZ::Interface::Get()->Reset(); } } } diff --git a/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp b/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp index 9bb98dd575..809803c3a7 100644 --- a/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp +++ b/Gems/LyShine/Code/Editor/PropertyHandlerEntityIdComboBox.cpp @@ -156,7 +156,7 @@ PropertyEntityIdComboBoxCtrl::PropertyEntityIdComboBoxCtrl(QWidget* pParent) void PropertyEntityIdComboBoxCtrl::setValue(AZ::EntityId value) { m_pComboBox->blockSignals(true); - bool indexWasFound = false; + [[maybe_unused]] bool indexWasFound = false; for (size_t enumValIndex = 0; enumValIndex < m_enumValues.size(); enumValIndex++) { if (m_enumValues[enumValIndex].first == value) diff --git a/Gems/LyShine/Code/Editor/QtHelpers.cpp b/Gems/LyShine/Code/Editor/QtHelpers.cpp index d9147463ed..3b38894016 100644 --- a/Gems/LyShine/Code/Editor/QtHelpers.cpp +++ b/Gems/LyShine/Code/Editor/QtHelpers.cpp @@ -29,18 +29,4 @@ namespace QtHelpers bool inWidget = (localPos.x() >= 0 && localPos.x() < size.width() && localPos.y() >= 0 && localPos.y() < size.height()); return inWidget; } - - float GetHighDpiScaleFactor(const QWidget& widget) - { - return static_cast(QHighDpiScaling::factor(widget.windowHandle()->screen())); - } - - QSize GetDpiScaledViewportSize(const QWidget& widget) - { - float dpiScale = GetHighDpiScaleFactor(widget); - float width = ceilf(widget.size().width() * dpiScale); - float height = ceilf(widget.size().height() * dpiScale); - return QSize(static_cast(width), static_cast(height)); - } - } // namespace QtHelpers diff --git a/Gems/LyShine/Code/Editor/QtHelpers.h b/Gems/LyShine/Code/Editor/QtHelpers.h index 4a9e7091ff..1b5f561871 100644 --- a/Gems/LyShine/Code/Editor/QtHelpers.h +++ b/Gems/LyShine/Code/Editor/QtHelpers.h @@ -16,9 +16,4 @@ namespace QtHelpers AZ::Vector2 MapGlobalPosToLocalVector2(const QWidget* widget, const QPoint& pos); bool IsGlobalPosInWidget(const QWidget* widget, const QPoint& pos); - - float GetHighDpiScaleFactor(const QWidget& widget); - - QSize GetDpiScaledViewportSize(const QWidget& widget); - } // namespace QtHelpers diff --git a/Gems/LyShine/Code/Editor/RulerWidget.cpp b/Gems/LyShine/Code/Editor/RulerWidget.cpp index 0a9209ba38..c82a64ac14 100644 --- a/Gems/LyShine/Code/Editor/RulerWidget.cpp +++ b/Gems/LyShine/Code/Editor/RulerWidget.cpp @@ -61,6 +61,14 @@ void RulerWidget::paintEvent([[maybe_unused]] QPaintEvent* event) float scale = translationAndScale.scale; float translation = (m_orientation == Orientation::Horizontal) ? translationAndScale.translation.GetX() : translationAndScale.translation.GetY(); + // Convert back to qt widget coords for painting + float dpiScaleFactor = m_editorWindow->GetViewport()->WidgetToViewportFactor(); + if (dpiScaleFactor != 0.0f) + { + scale /= dpiScaleFactor; + translation /= dpiScaleFactor; + } + // If the viewport is really small then scale can be zero (or very close) which would cause a divide by zero in later math so we just don't paint anything const float epsilon = 0.00001f; if (scale < epsilon) @@ -87,7 +95,9 @@ void RulerWidget::paintEvent([[maybe_unused]] QPaintEvent* event) void RulerWidget::mousePressEvent(QMouseEvent* ev) { // start a drag interaction to create a guide - AZ::Vector2 viewportMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + AZ::Vector2 localMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + float dpiScaleFactor = m_editorWindow->GetViewport()->WidgetToViewportFactor(); + AZ::Vector2 viewportMousePos = localMousePos * dpiScaleFactor; bool isVertical = m_orientation == Orientation::Vertical; m_dragInteraction = new ViewportAddGuideInteraction(m_editorWindow, m_editorWindow->GetCanvas(), isVertical, viewportMousePos); } @@ -98,7 +108,10 @@ void RulerWidget::mouseMoveEvent(QMouseEvent* ev) // We only get the events if the mouse is pressed down. So we only get here when adding a ruler. if (m_dragInteraction) { - AZ::Vector2 viewportMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + AZ::Vector2 localMousePos = QtHelpers::MapGlobalPosToLocalVector2(m_editorWindow->GetViewport(), ev->globalPos()); + float dpiScaleFactor = m_editorWindow->GetViewport()->WidgetToViewportFactor(); + AZ::Vector2 viewportMousePos = localMousePos * dpiScaleFactor; + m_dragInteraction->Update(viewportMousePos); } diff --git a/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp b/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp index 93d77bd174..b9ca4cd32c 100644 --- a/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp +++ b/Gems/LyShine/Code/Editor/SpriteBorderEditor.cpp @@ -7,6 +7,7 @@ */ #include "SpriteBorderEditorCommon.h" +#include #include #include #include @@ -35,7 +36,7 @@ SpriteBorderEditor::SpriteBorderEditor(const char* path, QWidget* parent) setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint); // Make sure the sprite can load and be displayed before continuing - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePath.toLatin1().constData()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePath.toLatin1().constData()); QString fullPath = Path::GamePathToFullPath(m_sprite->GetTexturePathname().c_str()); const bool imageWontLoad = !m_sprite || QPixmap(fullPath).isNull(); if (imageWontLoad) diff --git a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp index 7d81a12477..ee89b213ef 100644 --- a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp +++ b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp @@ -336,7 +336,7 @@ namespace ViewportHelpers draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, GetDpiScaledSize(16.0f), 1.0f); + draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, 8.0f, 1.0f); } } @@ -347,9 +347,11 @@ namespace ViewportHelpers const AZ::Vector2 textLabelOffset(10.0f, -10.0f); QPoint viewportCursorPos = viewport->mapFromGlobal(QCursor::pos()); AZ::Vector2 textPos = AZ::Vector2(aznumeric_cast(viewportCursorPos.x()), aznumeric_cast(viewportCursorPos.y())) + textLabelOffset; + float dpiScale = viewport->WidgetToViewportFactor(); + textPos *= dpiScale; draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(textLabel.c_str(), textPos, GetDpiScaledSize(16.0f), 1.0f); + draw2d.DrawText(textLabel.c_str(), textPos, 8.0f, 1.0f); } } // namespace ViewportHelpers diff --git a/Gems/LyShine/Code/Editor/ViewportIcon.cpp b/Gems/LyShine/Code/Editor/ViewportIcon.cpp index 33f2f40ffe..bdbad6afb2 100644 --- a/Gems/LyShine/Code/Editor/ViewportIcon.cpp +++ b/Gems/LyShine/Code/Editor/ViewportIcon.cpp @@ -296,7 +296,7 @@ void ViewportIcon::DrawDistanceLine(Draw2dHelper& draw2d, AZ::Vector2 start, AZ: draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(rotation); - draw2d.DrawText(textBuf, textPos, 16.0f * ViewportIcon::GetDpiScaleFactor(), 1.0f); + draw2d.DrawText(textBuf, textPos, 8.0f, 1.0f); } void ViewportIcon::DrawAnchorLinesSplit(Draw2dHelper& draw2d, AZ::Vector2 anchorPos1, AZ::Vector2 anchorPos2, diff --git a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp index a39a7b9cac..7bd55be4bc 100644 --- a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp +++ b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp @@ -674,13 +674,13 @@ void ViewportInteraction::MouseReleaseEvent(QMouseEvent* ev, { // test to see if the mouse position is inside the viewport on each axis const QPoint& pos = ev->pos(); - const QSize& size = m_editorWindow->GetViewport()->size(); + const AZ::Vector2 size = m_editorWindow->GetViewport()->GetRenderViewportSize(); ViewportDragInteraction::EndState inWidget; - if (pos.x() >= 0 && pos.x() < size.width()) - inWidget = pos.y() >= 0 && pos.y() < size.height() ? ViewportDragInteraction::EndState::Inside : ViewportDragInteraction::EndState::OutsideY; + if (pos.x() >= 0 && pos.x() < size.GetX()) + inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::Inside : ViewportDragInteraction::EndState::OutsideY; else - inWidget = pos.y() >= 0 && pos.y() < size.height() ? ViewportDragInteraction::EndState::OutsideX : ViewportDragInteraction::EndState::OutsideXY; + inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::OutsideX : ViewportDragInteraction::EndState::OutsideXY; // Some interactions end differently depending on whether the mouse was released inside or outside the viewport m_dragInteraction->EndInteraction(inWidget); @@ -709,12 +709,12 @@ void ViewportInteraction::MouseReleaseEvent(QMouseEvent* ev, UpdateCursor(); } -void ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) +bool ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) { if (m_leftButtonIsActive || m_middleButtonIsActive) { // Ignore event. - return; + return false; } const QPoint numDegrees(ev->angleDelta()); @@ -735,6 +735,8 @@ void ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) SetCanvasToViewportScale(QuantizeZoomScale(newScale), &pivotPoint); } + + return true; } bool ViewportInteraction::KeyPressEvent(QKeyEvent* ev) @@ -921,27 +923,27 @@ void ViewportInteraction::GetScaleToFitTransformProps(const AZ::Vector2* newCanv EBUS_EVENT_ID_RESULT(canvasSize, m_editorWindow->GetCanvas(), UiCanvasBus, GetCanvasSize); } - QSize viewportSize = QtHelpers::GetDpiScaledViewportSize(*m_editorWindow->GetViewport()); - const int viewportWidth = viewportSize.width(); - const int viewportHeight = viewportSize.height(); + AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize(); + const float viewportWidth = viewportSize.GetX(); + const float viewportHeight = viewportSize.GetY(); // We pad the edges of the viewport to allow the user to easily see the borders of // the canvas edges, which is especially helpful if there are anchors sitting on // the edges of the canvas. static const int canvasBorderPaddingInPixels = 32; AZ::Vector2 viewportPaddedSize( - aznumeric_cast(viewportWidth - canvasBorderPaddingInPixels), - aznumeric_cast(viewportHeight - canvasBorderPaddingInPixels)); + viewportWidth - canvasBorderPaddingInPixels, + viewportHeight - canvasBorderPaddingInPixels); // Guard against very small viewports if (viewportPaddedSize.GetX() <= 0.0f) { - viewportPaddedSize.SetX(aznumeric_cast(viewportWidth)); + viewportPaddedSize.SetX(viewportWidth); } if (viewportPaddedSize.GetY() <= 0.0f) { - viewportPaddedSize.SetY(aznumeric_cast(viewportHeight)); + viewportPaddedSize.SetY(viewportHeight); } // Use a "scale to fit" approach @@ -949,8 +951,8 @@ void ViewportInteraction::GetScaleToFitTransformProps(const AZ::Vector2* newCanv viewportPaddedSize.GetX() / canvasSize.GetX(), viewportPaddedSize.GetY() / canvasSize.GetY()); - const int scaledCanvasWidth = static_cast(canvasSize.GetX() * canvasToViewportScale); - const int scaledCanvasHeight = static_cast(canvasSize.GetY() * canvasToViewportScale); + const float scaledCanvasWidth = canvasSize.GetX() * canvasToViewportScale; + const float scaledCanvasHeight = canvasSize.GetY() * canvasToViewportScale; // Centers the canvas within the viewport propsOut.translation = AZ::Vector3( @@ -1008,9 +1010,12 @@ void ViewportInteraction::SetCanvasToViewportScale(float newScale, Vec2i* option } else { + AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize(); + const float viewportWidth = viewportSize.GetX(); + const float viewportHeight = viewportSize.GetY(); pivotPoint = Vec2i( - static_cast(m_editorWindow->GetViewport()->size().width() * 0.5f), - static_cast(m_editorWindow->GetViewport()->size().height() * 0.5f)); + static_cast(viewportWidth * 0.5f), + static_cast(viewportHeight * 0.5f)); } // Get the distance between our pivot point and the upper-left corner of the diff --git a/Gems/LyShine/Code/Editor/ViewportInteraction.h b/Gems/LyShine/Code/Editor/ViewportInteraction.h index a104d87634..b6417977ab 100644 --- a/Gems/LyShine/Code/Editor/ViewportInteraction.h +++ b/Gems/LyShine/Code/Editor/ViewportInteraction.h @@ -77,7 +77,7 @@ public: // member functions const QTreeWidgetItemRawPtrQList& selectedItems); void MouseReleaseEvent(QMouseEvent* ev, const QTreeWidgetItemRawPtrQList& selectedItems); - void MouseWheelEvent(QWheelEvent* ev); + bool MouseWheelEvent(QWheelEvent* ev); bool KeyPressEvent(QKeyEvent* ev); bool KeyReleaseEvent(QKeyEvent* ev); diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.cpp b/Gems/LyShine/Code/Editor/ViewportWidget.cpp index b481e9cbf1..32231058bf 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.cpp +++ b/Gems/LyShine/Code/Editor/ViewportWidget.cpp @@ -181,7 +181,7 @@ namespace EBUS_EVENT_ID_RESULT(handled, canvasEntityId, UiCanvasBus, HandleInputEvent, inputSnapshot, viewportPos, activeModifierKeys); // Execute events that have been queued during the input event handler - gEnv->pLyShine->ExecuteQueuedEvents(); + AZ::Interface::Get()->ExecuteQueuedEvents(); return handled; } @@ -192,7 +192,7 @@ namespace EBUS_EVENT_ID_RESULT(handled, canvasEntityId, UiCanvasBus, HandleTextEvent, textUTF8); // Execute events that have been queued during the input event handler - gEnv->pLyShine->ExecuteQueuedEvents(); + AZ::Interface::Get()->ExecuteQueuedEvents(); return handled; } @@ -265,7 +265,7 @@ ViewportWidget::~ViewportWidget() // Notify LyShine that this is no longer a valid UiRenderer. // Only one viewport/renderer is currently supported in the UI Editor - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); lyShine->SetUiRendererForEditor(nullptr); } @@ -276,7 +276,7 @@ void ViewportWidget::InitUiRenderer() // Notify LyShine that this is the UiRenderer to be used for rendering // UI canvases that are loaded in the UI Editor. // Only one viewport/renderer is currently supported in the UI Editor - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); lyShine->SetUiRendererForEditor(m_uiRenderer); m_draw2d = AZStd::make_shared(GetViewportContext()); @@ -351,6 +351,17 @@ void ViewportWidget::SetRedrawEnabled(bool enabled) m_canvasRenderIsEnabled = enabled; } +AZ::Vector2 ViewportWidget::GetRenderViewportSize() const +{ + AZ::Vector2 widgetSize(aznumeric_cast(size().width()), aznumeric_cast(size().height())); + return widgetSize * WidgetToViewportFactor(); +} + +float ViewportWidget::WidgetToViewportFactor() const +{ + return GetViewportContext()->GetDpiScalingFactor(); +} + void ViewportWidget::PickItem(AZ::EntityId entityId) { AzToolsFramework::EditorPickModeRequestBus::Broadcast( @@ -496,7 +507,7 @@ void ViewportWidget::OnRenderTick() return; } - const float dpiScale = QtHelpers::GetHighDpiScaleFactor(*this); + const float dpiScale = WidgetToViewportFactor(); ViewportIcon::SetDpiScaleFactor(dpiScale); UiEditorMode editorMode = m_editorWindow->GetEditorMode(); @@ -510,6 +521,12 @@ void ViewportWidget::OnRenderTick() } } +//////////////////////////////////////////////////////////////////////////////////////////////////// +void ViewportWidget::OnViewportDpiScalingChanged(float dpiScale) +{ + ViewportIcon::SetDpiScaleFactor(dpiScale); +} + void ViewportWidget::RefreshTick() { #ifdef LYSHINE_EDITOR_TODO // still need this? @@ -640,27 +657,34 @@ void ViewportWidget::mouseReleaseEvent(QMouseEvent* ev) void ViewportWidget::wheelEvent(QWheelEvent* ev) { + bool handled = false; UiEditorMode editorMode = m_editorWindow->GetEditorMode(); - QWheelEvent scaledEvent( - WidgetToViewport(ev->position()), - ev->globalPosition(), - ev->pixelDelta(), - ev->angleDelta(), - ev->buttons(), - ev->modifiers(), - ev->phase(), - ev->inverted() - ); - if (editorMode == UiEditorMode::Edit) { + QWheelEvent scaledEvent( + WidgetToViewport(ev->position()), + ev->globalPosition(), + ev->pixelDelta(), + ev->angleDelta(), + ev->buttons(), + ev->modifiers(), + ev->phase(), + ev->inverted() + ); + // in Edit mode just send input to ViewportInteraction - m_viewportInteraction->MouseWheelEvent(&scaledEvent); + handled = m_viewportInteraction->MouseWheelEvent(&scaledEvent); } - RenderViewportWidget::wheelEvent(ev); - - Refresh(); + if (handled) + { + ev->accept(); + Refresh(); + } + else + { + RenderViewportWidget::wheelEvent(ev); + } } bool ViewportWidget::eventFilter([[maybe_unused]] QObject* watched, QEvent* event) @@ -975,8 +999,7 @@ void ViewportWidget::RenderEditMode() EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, false, canvasSize); // Render this canvas - QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this); - AZ::Vector2 viewportSize(static_cast(scaledViewportSize.width()), static_cast(scaledViewportSize.height())); + AZ::Vector2 viewportSize = GetRenderViewportSize(); EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, RenderCanvasInEditorViewport, false, viewportSize); m_draw2d->SetSortKey(topLayerKey); @@ -1085,15 +1108,12 @@ void ViewportWidget::UpdatePreviewMode(float deltaTime) if (canvasEntityId.IsValid()) { - QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this); - AZ::Vector2 viewportSize(static_cast(scaledViewportSize.width()), static_cast(scaledViewportSize.height())); - // Get the canvas size AZ::Vector2 canvasSize = m_editorWindow->GetPreviewCanvasSize(); if (canvasSize.GetX() == 0.0f && canvasSize.GetY() == 0.0f) { // special value of (0,0) means use the viewport size - canvasSize = viewportSize; + canvasSize = GetRenderViewportSize();; } // Set the target size of the canvas @@ -1103,7 +1123,7 @@ void ViewportWidget::UpdatePreviewMode(float deltaTime) EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, true); // Execute events that have been queued during the canvas update - gEnv->pLyShine->ExecuteQueuedEvents(); + AZ::Interface::Get()->ExecuteQueuedEvents(); } } @@ -1127,8 +1147,7 @@ void ViewportWidget::RenderPreviewMode() if (canvasEntityId.IsValid()) { - QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this); - AZ::Vector2 viewportSize(static_cast(scaledViewportSize.width()), static_cast(scaledViewportSize.height())); + AZ::Vector2 viewportSize = GetRenderViewportSize(); // Get the canvas size AZ::Vector2 canvasSize = m_editorWindow->GetPreviewCanvasSize(); @@ -1208,13 +1227,13 @@ void ViewportWidget::RenderPreviewMode() void ViewportWidget::RenderViewportBackground() { - QSize viewportSize = QtHelpers::GetDpiScaledViewportSize(*this); + AZ::Vector2 viewportSize = GetRenderViewportSize(); AZ::Color backgroundColor = ViewportHelpers::backgroundColorDark; const AZ::Data::Instance& image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); Draw2dHelper draw2d(m_draw2d.get()); draw2d.SetImageColor(backgroundColor.GetAsVector3()); - draw2d.DrawImage(image, AZ::Vector2(0.0f, 0.0f), AZ::Vector2(static_cast(viewportSize.width()), static_cast(viewportSize.height()))); + draw2d.DrawImage(image, AZ::Vector2(0.0f, 0.0f), viewportSize); } void ViewportWidget::SetupShortcuts() diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.h b/Gems/LyShine/Code/Editor/ViewportWidget.h index 722335aa93..c285e0d969 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.h +++ b/Gems/LyShine/Code/Editor/ViewportWidget.h @@ -72,6 +72,12 @@ public: // member functions //! Used by ViewportInteraction for drawing ViewportHighlight* GetViewportHighlight() { return m_viewportHighlight.get(); } + //! Get the size of the RPI render viewport + AZ::Vector2 GetRenderViewportSize() const; + + //! Get the widget to viewport scale factor + float WidgetToViewportFactor() const; + bool IsInObjectPickMode() { return m_inObjectPickMode; } void PickItem(AZ::EntityId entityId); @@ -156,6 +162,7 @@ private: // member functions // AZ::RPI::ViewportContextNotificationBus::Handler overrides... void OnRenderTick() override; + void OnViewportDpiScalingChanged(float dpiScale) override; //! Update UI canvases when in edit mode void UpdateEditMode(float deltaTime); @@ -187,12 +194,6 @@ private: // data bool AcceptsMimeData(const QMimeData* mimeData); - double WidgetToViewportFactor() const - { - // Needed for high DPI mode on windows - return devicePixelRatioF(); - } - QPointF WidgetToViewport(const QPointF &point) const; EditorWindow* m_editorWindow; diff --git a/Code/Legacy/CryCommon/LyShine/Bus/UiCursorBus.h b/Gems/LyShine/Code/Include/LyShine/Bus/UiCursorBus.h similarity index 100% rename from Code/Legacy/CryCommon/LyShine/Bus/UiCursorBus.h rename to Gems/LyShine/Code/Include/LyShine/Bus/UiCursorBus.h diff --git a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h index 77dc5ac601..e880ca54a7 100644 --- a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h @@ -473,9 +473,9 @@ public: // static member functions //! Helper to get the default IDraw2d interface static IDraw2d* GetDefaultDraw2d() { - if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment + if (AZ::Interface::Get()) { - IDraw2d* draw2d = gEnv->pLyShine->GetDraw2d(); + IDraw2d* draw2d = AZ::Interface::Get()->GetDraw2d(); return reinterpret_cast(draw2d); } @@ -485,9 +485,9 @@ public: // static member functions //! Helper to load a texture static AZ::Data::Instance LoadTexture(const AZStd::string& pathName) { - if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment + if (AZ::Interface::Get()) { - return gEnv->pLyShine->LoadTexture(pathName); + return AZ::Interface::Get()->LoadTexture(pathName); } return nullptr; diff --git a/Gems/LyShine/Code/Include/LyShine/ILyShine.h b/Gems/LyShine/Code/Include/LyShine/ILyShine.h index 71ca6ec074..d05e105c87 100644 --- a/Gems/LyShine/Code/Include/LyShine/ILyShine.h +++ b/Gems/LyShine/Code/Include/LyShine/ILyShine.h @@ -38,7 +38,10 @@ namespace AZ::RPI class ILyShine { public: - virtual ~ILyShine(){} + AZ_RTTI(ILyShine, "{652ED9D7-0782-44E8-BCE7-65DD38C90907}"); + + ILyShine() = default; + virtual ~ILyShine() = default; //! Delete this object virtual void Release() = 0; diff --git a/Code/Legacy/CryCommon/LyShine/UiAssetTypes.h b/Gems/LyShine/Code/Include/LyShine/UiAssetTypes.h similarity index 100% rename from Code/Legacy/CryCommon/LyShine/UiAssetTypes.h rename to Gems/LyShine/Code/Include/LyShine/UiAssetTypes.h diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index b362ed2106..df26a905ad 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -124,7 +124,7 @@ AllocateConstIntCVar(CLyShine, CV_ui_RunUnitTestsOnStartup); #endif //////////////////////////////////////////////////////////////////////////////////////////////////// -CLyShine::CLyShine([[maybe_unused]] ISystem* system) +CLyShine::CLyShine() : AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityUI()) , AzFramework::InputTextEventListener(AzFramework::InputTextEventListener::GetPriorityUI()) , m_draw2d(new CDraw2d) @@ -386,8 +386,8 @@ void CLyShine::Update(float deltaTimeInSeconds) } // Tell the UI system the size of the viewport we are rendering to - this drives the - // canvas size for full screen UI canvases. It needs to be set before either pLyShine->Update or - // pLyShine->Render are called. It must match the viewport size that the input system is using. + // canvas size for full screen UI canvases. It needs to be set before either lyShine->Update or + // lyShine->Render are called. It must match the viewport size that the input system is using. SetViewportSize(m_uiRenderer->GetViewportSize()); // Guard against nested updates. This can occur if a canvas update below triggers the load screen component's @@ -708,10 +708,10 @@ void CLyShine::RenderUiCursor() //////////////////////////////////////////////////////////////////////////////////////////////////// void CLyShine::DebugReportDrawCalls(IConsoleCmdArgs* cmdArgs) { - if (gEnv->pLyShine) + if (AZ::Interface::Get()) { // We want to use an internal-only non-static function so downcast to CLyShine - CLyShine* pLyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); // There is an optional parameter which is a name to include in the output filename AZStd::string name; @@ -721,7 +721,7 @@ void CLyShine::DebugReportDrawCalls(IConsoleCmdArgs* cmdArgs) } // Use the canvas manager to access all the loaded canvases - pLyShine->m_uiCanvasManager->DebugReportDrawCalls(name); + lyShine->m_uiCanvasManager->DebugReportDrawCalls(name); } } #endif @@ -742,7 +742,7 @@ void CLyShine::RunUnitTests(IConsoleCmdArgs* cmdArgs) return; } - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); AZ_Assert(lyShine, "Attempting to run unit-tests prior to LyShine initialization!"); TextMarkup::UnitTest(cmdArgs); diff --git a/Gems/LyShine/Code/Source/LyShine.h b/Gems/LyShine/Code/Source/LyShine.h index eab52258c1..2fd0f80210 100644 --- a/Gems/LyShine/Code/Source/LyShine.h +++ b/Gems/LyShine/Code/Source/LyShine.h @@ -48,7 +48,7 @@ class CLyShine public: //! Create the LyShine object, the given system pointer is stored internally - CLyShine(ISystem* system); + CLyShine(); // ILyShine diff --git a/Gems/LyShine/Code/Source/LyShineDebug.cpp b/Gems/LyShine/Code/Source/LyShineDebug.cpp index 7e99652f45..3502699525 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.cpp +++ b/Gems/LyShine/Code/Source/LyShineDebug.cpp @@ -997,7 +997,7 @@ static AZ::Entity* CreateButton(const char* name, bool atRoot, AZ::EntityId pare EBUS_EVENT_ID(buttonId, UiInteractableStatesBus, SetStateAlpha, UiInteractableStatesInterface::StatePressed, buttonId, pressedColor.GetA()); AZStd::string pathname = "Textures/Basic/Button_Sliced_Normal.sprite"; - ISprite* sprite = gEnv->pLyShine->LoadSprite(pathname); + ISprite* sprite = AZ::Interface::Get()->LoadSprite(pathname); EBUS_EVENT_ID(buttonId, UiImageBus, SetSprite, sprite); EBUS_EVENT_ID(buttonId, UiImageBus, SetImageType, UiImageInterface::ImageType::Sliced); @@ -1096,7 +1096,7 @@ static AZ::Entity* CreateTextInput(const char* name, bool atRoot, AZ::EntityId p EBUS_EVENT_ID(textInputId, UiInteractableStatesBus, SetStateAlpha, UiInteractableStatesInterface::StatePressed, textInputId, pressedColor.GetA()); AZStd::string pathname = "Textures/Basic/Button_Sliced_Normal.sprite"; - ISprite* sprite = gEnv->pLyShine->LoadSprite(pathname); + ISprite* sprite = AZ::Interface::Get()->LoadSprite(pathname); EBUS_EVENT_ID(textInputId, UiImageBus, SetSprite, sprite); EBUS_EVENT_ID(textInputId, UiImageBus, SetImageType, UiImageInterface::ImageType::Sliced); @@ -1192,7 +1192,7 @@ static void DestroyTestCanvas() delete g_testActionListener2; g_testActionListener2 = nullptr; - gEnv->pLyShine->ReleaseCanvas(g_testCanvasId, false); + AZ::Interface::Get()->ReleaseCanvas(g_testCanvasId, false); g_testCanvasId.SetInvalid(); } } @@ -1216,7 +1216,7 @@ static void TestCanvasCreate ([[maybe_unused]] IConsoleCmdArgs* Cmd) DestroyTestCanvas(); // test creation of canvas and some simple elements - AZ::EntityId canvasEntityId = gEnv->pLyShine->CreateCanvas(); + AZ::EntityId canvasEntityId = AZ::Interface::Get()->CreateCanvas(); UiCanvasInterface* canvas = UiCanvasBus::FindFirstHandler(canvasEntityId); if (!canvas) { diff --git a/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp b/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp index c59cb5cb34..e0916d43b2 100644 --- a/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp +++ b/Gems/LyShine/Code/Source/LyShineLoadScreen.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -63,7 +64,7 @@ namespace LyShine AZ_ErrorOnce(nullptr, false, "NotifyGameLoadStart needs to be removed/ported to use Atom"); return false; #if 0 - if (!gEnv || gEnv->pRenderer || !gEnv->pLyShine) + if (!gEnv || gEnv->pRenderer || !AZ::Interface::Get()) { return false; } @@ -103,7 +104,7 @@ namespace LyShine return false; //TODO: gEnv->pRenderer is always null, fix the logic below #if 0 - if (!gEnv || gEnv->pRenderer || !gEnv->pLyShine) + if (!gEnv || gEnv->pRenderer || !AZ::Interface::Get()) { return false; } @@ -140,22 +141,22 @@ namespace LyShine void LyShineLoadScreenComponent::UpdateAndRender([[maybe_unused]] float deltaTimeInSeconds) { AZ_Assert(m_isPlaying, "LyShineLoadScreenComponent should not be connected to LoadScreenUpdateNotificationBus while not playing"); - AZ_ErrorOnce(nullptr, m_isPlaying && gEnv && gEnv->pLyShine, "UpdateAndRender needs to be removed/ported to use Atom"); + AZ_ErrorOnce(nullptr, m_isPlaying && AZ::Interface::Get(), "UpdateAndRender needs to be removed/ported to use Atom"); //TODO: gEnv->pRenderer is always null, fix the logic below #if 0 - if (m_isPlaying && gEnv && gEnv->pLyShine && gEnv->pRenderer) + if (m_isPlaying && gEnv && AZ::Interface::Get() && gEnv->pRenderer) { AZ_Assert(GetCurrentThreadId() == gEnv->mMainThreadId, "UpdateAndRender should only be called from the main thread"); // update the animation system - gEnv->pLyShine->Update(deltaTimeInSeconds); + AZ::Interface::Get()->Update(deltaTimeInSeconds); // Render. gEnv->pRenderer->SetViewport(0, 0, gEnv->pRenderer->GetOverlayWidth(), gEnv->pRenderer->GetOverlayHeight()); gEnv->pRenderer->BeginFrame(); - gEnv->pLyShine->Render(); + AZ::Interface::Get()->Render(); gEnv->pRenderer->EndFrame(); } #endif @@ -177,7 +178,7 @@ namespace LyShine m_isPlaying = false; - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { AZ::Entity* canvasEntity = nullptr; @@ -187,8 +188,8 @@ namespace LyShine EBUS_EVENT_RESULT(canvasEntity, AZ::ComponentApplicationBus, FindEntity, m_gameCanvasEntityId); if (canvasEntity) { - gEnv->pLyShine->ReleaseCanvas(m_gameCanvasEntityId, false); - gEnv->pLyShine->OnLoadScreenUnloaded(); + AZ::Interface::Get()->ReleaseCanvas(m_gameCanvasEntityId, false); + AZ::Interface::Get()->OnLoadScreenUnloaded(); } } @@ -198,8 +199,8 @@ namespace LyShine EBUS_EVENT_RESULT(canvasEntity, AZ::ComponentApplicationBus, FindEntity, m_levelCanvasEntityId); if (canvasEntity) { - gEnv->pLyShine->ReleaseCanvas(m_levelCanvasEntityId, false); - gEnv->pLyShine->OnLoadScreenUnloaded(); + AZ::Interface::Get()->ReleaseCanvas(m_levelCanvasEntityId, false); + AZ::Interface::Get()->OnLoadScreenUnloaded(); } } } @@ -234,7 +235,7 @@ namespace LyShine return AZ::EntityId(); } - AZ::EntityId canvasId = gEnv->pLyShine->LoadCanvas(path); + AZ::EntityId canvasId = AZ::Interface::Get()->LoadCanvas(path); AZ_Warning("LoadScreenComponent", canvasId.IsValid(), "Can't load canvas: %s", path.c_str()); if (!canvasId.IsValid()) { diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp index 787797e462..7253935c2d 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp @@ -384,16 +384,16 @@ namespace LyShine // When module is linked statically, we'll share the application's gEnv pointer. gEnv = system.GetGlobalEnvironment(); #endif - m_pLyShine = new CLyShine(gEnv->pSystem); - gEnv->pLyShine = m_pLyShine; + m_lyShine = AZStd::make_unique(); + AZ::Interface::Register(m_lyShine.get()); system.GetILevelSystem()->AddListener(this); BroadcastCursorImagePathname(); - if (gEnv->pLyShine) + if (AZ::Interface::Get()) { - gEnv->pLyShine->PostInit(); + AZ::Interface::Get()->PostInit(); } } @@ -402,18 +402,20 @@ namespace LyShine { system.GetILevelSystem()->RemoveListener(this); - gEnv->pLyShine = nullptr; - delete m_pLyShine; - m_pLyShine = nullptr; + if (m_lyShine) + { + AZ::Interface::Unregister(m_lyShine.get()); + m_lyShine.reset(); + } } //////////////////////////////////////////////////////////////////////// void LyShineSystemComponent::OnUnloadComplete([[maybe_unused]] const char* levelName) { // Perform level unload procedures for the LyShine UI system - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { - gEnv->pLyShine->OnLevelUnload(); + AZ::Interface::Get()->OnLevelUnload(); } } diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.h b/Gems/LyShine/Code/Source/LyShineSystemComponent.h index d2cdcf6761..001ddd6fab 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.h +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.h @@ -107,7 +107,7 @@ namespace LyShine protected: // data - CLyShine* m_pLyShine = nullptr; + AZStd::unique_ptr m_lyShine; AzFramework::SimpleAssetReference m_cursorImagePathname; diff --git a/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp b/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp index 4b0ff56241..679f13b333 100644 --- a/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp +++ b/Gems/LyShine/Code/Source/Script/UiCanvasLuaBus.cpp @@ -114,7 +114,7 @@ AZ::EntityId UiCanvasLuaProxy::LoadCanvas(const char* canvasFilename) CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "UiCanvasLuaProxy:LoadCanvas is deprecated. Please use UiCanvasManagerBus:LoadCanvas instead\n"); - return gEnv->pLyShine->LoadCanvas(canvasFilename); + return AZ::Interface::Get()->LoadCanvas(canvasFilename); } void UiCanvasLuaProxy::UnloadCanvas(AZ::EntityId canvasEntityId) @@ -130,7 +130,7 @@ void UiCanvasLuaProxy::UnloadCanvas(AZ::EntityId canvasEntityId) UiCanvasComponent* canvasComponent = canvasEntity->FindComponent(); if (canvasComponent) { - gEnv->pLyShine->ReleaseCanvas(canvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(canvasEntityId, false); } else { diff --git a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp index ea6e3681a3..0a7161ecb3 100644 --- a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp @@ -254,9 +254,9 @@ namespace UiRenderer* GetUiRendererForGame() { - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); return lyShine->GetUiRenderer(); } return nullptr; @@ -264,9 +264,9 @@ namespace UiRenderer* GetUiRendererForEditor() { - if (gEnv && gEnv->pLyShine) + if (AZ::Interface::Get()) { - CLyShine* lyShine = static_cast(gEnv->pLyShine); + CLyShine* lyShine = static_cast(AZ::Interface::Get()); return lyShine->GetUiRendererForEditor(); } return nullptr; diff --git a/Gems/LyShine/Code/Source/UiImageComponent.cpp b/Gems/LyShine/Code/Source/UiImageComponent.cpp index a8b4210d6b..d412695ac3 100644 --- a/Gems/LyShine/Code/Source/UiImageComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageComponent.cpp @@ -588,7 +588,7 @@ void UiImageComponent::SetSpritePathname(AZStd::string spritePath) //////////////////////////////////////////////////////////////////////////////////////////////////// bool UiImageComponent::SetSpritePathnameIfExists(AZStd::string spritePath) { - if (gEnv->pLyShine->DoesSpriteTextureAssetExist(spritePath)) + if (AZ::Interface::Get()->DoesSpriteTextureAssetExist(spritePath)) { SetSpritePathname(spritePath); return true; @@ -1179,7 +1179,7 @@ void UiImageComponent::Init() // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } @@ -1192,14 +1192,14 @@ void UiImageComponent::Init() { if (!m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } } else if (m_spriteType == UiImageInterface::SpriteType::RenderTarget) { if (!m_renderTargetName.empty()) { - m_sprite = gEnv->pLyShine->CreateSprite(m_renderTargetName.c_str()); + m_sprite = AZ::Interface::Get()->CreateSprite(m_renderTargetName.c_str()); } } else @@ -2527,7 +2527,7 @@ void UiImageComponent::OnSpritePathnameChange() if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } // if listening for notifications from a current sprite then disconnect @@ -2557,7 +2557,7 @@ void UiImageComponent::OnSpriteRenderTargetNameChange() if (!m_renderTargetName.empty()) { - newSprite = gEnv->pLyShine->CreateSprite(m_renderTargetName.c_str()); + newSprite = AZ::Interface::Get()->CreateSprite(m_renderTargetName.c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp index 3559dc6306..3981777f2d 100644 --- a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp @@ -52,7 +52,7 @@ namespace spriteList.reserve(imageList.size()); for (auto& textureAssetRef : imageList) { - ISprite* sprite = gEnv->pLyShine->LoadSprite(textureAssetRef.GetAssetPath().c_str()); + ISprite* sprite = AZ::Interface::Get()->LoadSprite(textureAssetRef.GetAssetPath().c_str()); if (sprite) { spriteList.push_back(sprite); @@ -383,7 +383,7 @@ void UiImageSequenceComponent::Init() // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } diff --git a/Gems/LyShine/Code/Source/UiInteractableState.cpp b/Gems/LyShine/Code/Source/UiInteractableState.cpp index d2c09201c6..18bacb3a9f 100644 --- a/Gems/LyShine/Code/Source/UiInteractableState.cpp +++ b/Gems/LyShine/Code/Source/UiInteractableState.cpp @@ -274,7 +274,7 @@ UiInteractableStateSprite::UiInteractableStateSprite(AZ::EntityId target, const if (!m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } } @@ -297,7 +297,7 @@ void UiInteractableStateSprite::Init(AZ::EntityId interactableEntityId) // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } @@ -306,7 +306,7 @@ void UiInteractableStateSprite::Init(AZ::EntityId interactableEntityId) // are not loaded then load them if (!m_sprite && !m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } if (!m_sprite) @@ -366,7 +366,7 @@ void UiInteractableStateSprite::OnSpritePathnameChange() if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp b/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp index b999480940..b9f986c14a 100644 --- a/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp +++ b/Gems/LyShine/Code/Source/UiParticleEmitterComponent.cpp @@ -1429,14 +1429,14 @@ void UiParticleEmitterComponent::Init() // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } if (!m_sprite && !m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } m_currentAspectRatio = m_particleSize.GetX() / m_particleSize.GetY(); @@ -1927,7 +1927,7 @@ void UiParticleEmitterComponent::OnSpritePathnameChange() if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp index 933cca424a..e186e1158d 100644 --- a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp +++ b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp @@ -8,6 +8,7 @@ #include "UiCanvasAssetRefComponent.h" #include #include +#include #include #include #include @@ -106,11 +107,11 @@ AZ::EntityId UiCanvasAssetRefComponent::LoadCanvas() // Check if we already have a referenced UI canvas, if so release it if (m_canvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + AZ::Interface::Get()->ReleaseCanvasDeferred(m_canvasEntityId); m_canvasEntityId.SetInvalid(); } - m_canvasEntityId = gEnv->pLyShine->LoadCanvas(canvasPath.c_str()); + m_canvasEntityId = AZ::Interface::Get()->LoadCanvas(canvasPath.c_str()); EBUS_EVENT_ID(GetEntityId(), UiCanvasAssetRefNotificationBus, OnCanvasLoadedIntoEntity, m_canvasEntityId); EBUS_EVENT_ID(GetEntityId(), UiCanvasRefNotificationBus, OnCanvasRefChanged, GetEntityId(), m_canvasEntityId); @@ -124,7 +125,7 @@ void UiCanvasAssetRefComponent::UnloadCanvas() { if (m_canvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + AZ::Interface::Get()->ReleaseCanvasDeferred(m_canvasEntityId); m_canvasEntityId.SetInvalid(); EBUS_EVENT_ID(GetEntityId(), UiCanvasRefNotificationBus, OnCanvasRefChanged, GetEntityId(), m_canvasEntityId); @@ -243,7 +244,7 @@ void UiCanvasAssetRefComponent::Deactivate() { if (m_canvasEntityId.IsValid()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + AZ::Interface::Get()->ReleaseCanvasDeferred(m_canvasEntityId); m_canvasEntityId.SetInvalid(); } diff --git a/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp b/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp index 8a3d9f2af6..652cec3c0f 100644 --- a/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp +++ b/Gems/LyShine/Code/Tests/UiTooltipComponentTest.cpp @@ -144,7 +144,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnHover); @@ -170,7 +169,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnHover); @@ -194,7 +192,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnPress); @@ -218,7 +215,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnPress); @@ -242,7 +238,6 @@ namespace UnitTest SSystemGlobalEnvironment env; SSystemGlobalEnvironment* prevEnv = gEnv; gEnv = &env; - gEnv->pLyShine = nullptr; auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip(); uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnClick); diff --git a/Gems/LyShine/Code/lyshine_static_files.cmake b/Gems/LyShine/Code/lyshine_static_files.cmake index 925658e756..c1515f5978 100644 --- a/Gems/LyShine/Code/lyshine_static_files.cmake +++ b/Gems/LyShine/Code/lyshine_static_files.cmake @@ -11,6 +11,7 @@ set(FILES Include/LyShine/IRenderGraph.h Include/LyShine/ISprite.h Include/LyShine/ILyShine.h + Include/LyShine/UiAssetTypes.h Include/LyShine/UiBase.h Include/LyShine/UiLayoutCellBase.h Include/LyShine/UiSerializeHelpers.h @@ -26,6 +27,7 @@ set(FILES Include/LyShine/Bus/UiCanvasManagerBus.h Include/LyShine/Bus/UiCanvasUpdateNotificationBus.h Include/LyShine/Bus/UiCheckboxBus.h + Include/LyShine/Bus/UiCursorBus.h Include/LyShine/Bus/UiDraggableBus.h Include/LyShine/Bus/UiDropdownBus.h Include/LyShine/Bus/UiDropdownOptionBus.h diff --git a/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp b/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp index f2da79fa99..7619764fbc 100644 --- a/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp +++ b/Gems/LyShineExamples/Code/Source/LyShineExamplesCppExample.cpp @@ -47,7 +47,7 @@ namespace LyShineExamples // Remove the existing example canvas if it exists DestroyCanvas(); - AZ::EntityId canvasEntityId = gEnv->pLyShine->CreateCanvas(); + AZ::EntityId canvasEntityId = AZ::Interface::Get()->CreateCanvas(); if (!canvasEntityId.IsValid()) { return; @@ -90,7 +90,7 @@ namespace LyShineExamples m_healthBar.SetInvalid(); - gEnv->pLyShine->ReleaseCanvas(m_canvasId, false); + AZ::Interface::Get()->ReleaseCanvas(m_canvasId, false); m_canvasId.SetInvalid(); } } diff --git a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp index e8b0e4370a..c228176b4f 100644 --- a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp +++ b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp @@ -284,7 +284,7 @@ namespace LyShineExamples // If this is called from RC.exe for example these pointers will not be set. In that case // we only need to be able to load, init and save the component. It will never be // activated. - if (!(gEnv && gEnv->pLyShine)) + if (!AZ::Interface::Get()) { return; } @@ -294,7 +294,7 @@ namespace LyShineExamples { if (!m_spritePathname.GetAssetPath().empty()) { - m_sprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + m_sprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } } @@ -399,7 +399,7 @@ namespace LyShineExamples if (!m_spritePathname.GetAssetPath().empty()) { // Load the new texture. - newSprite = gEnv->pLyShine->LoadSprite(m_spritePathname.GetAssetPath().c_str()); + newSprite = AZ::Interface::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str()); } SAFE_RELEASE(m_sprite); diff --git a/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp b/Gems/MessagePopup/Code/Source/LyShineMessagePopup.cpp index 40fed3448b..de54bef4eb 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("@products@/ui/canvases/defaultmessagepopup.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/defaultmessagepopup.uicanvas"); break; case EPopupButtons::EPopupButtons_Confirm: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_confirm.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_confirm.uicanvas"); isNavigationSupported = true; break; case EPopupButtons::EPopupButtons_YesNo: - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_yesno.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/defaultmessagepopup_yesno.uicanvas"); isNavigationSupported = true; break; } } else if (_kind == EPopupKind_Toaster) { - canvasEntityId = gEnv->pLyShine->LoadCanvas("@products@/ui/canvases/toaster.uicanvas"); + canvasEntityId = AZ::Interface::Get()->LoadCanvas("@products@/ui/canvases/toaster.uicanvas"); } if (canvasEntityId.IsValid()) @@ -183,7 +183,7 @@ namespace MessagePopup { // get the canvas ID in the clientdata LyShine::CanvasId canvasId = *((LyShine::CanvasId*)&_popupInfo.m_clientData); - AZ::EntityId canvasEntityId = gEnv->pLyShine->FindCanvasById(canvasId); + AZ::EntityId canvasEntityId = AZ::Interface::Get()->FindCanvasById(canvasId); if (canvasEntityId.IsValid()) { // Hide the cursor if it was shown in LyShineMessagePopup::OnShowPopup @@ -197,7 +197,7 @@ namespace MessagePopup // Disable the popup EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetEnabled, false); - gEnv->pLyShine->ReleaseCanvas(canvasEntityId, false); + AZ::Interface::Get()->ReleaseCanvas(canvasEntityId, false); UiCanvasNotificationBus::MultiHandler::BusDisconnect(canvasEntityId); m_activePopupIdsByCanvasId.erase(canvasEntityId); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e095af4ae7..893799a296 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -1126,7 +1126,7 @@ namespace Multiplayer netBindComponent->NotifyServerMigration(GetRemoteHostId()); } - bool didSucceed = true; + [[maybe_unused]] bool didSucceed = true; EntityMigrationMessage message; message.m_netEntityId = replicator->GetEntityHandle().GetNetEntityId(); message.m_prefabEntityId = netBindComponent->GetPrefabEntityId(); diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp index 1e57999b6e..cde8740694 100644 --- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp +++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.cpp @@ -72,8 +72,8 @@ namespace NvCloth // Maximum number of spheres and capsules is imposed by NvCloth library size_t sphereCount = 0; size_t capsuleCount = 0; - bool maxSphereCountReachedWarned = false; - bool maxCapsuleCountReachedWarned = false; + [[maybe_unused]] bool maxSphereCountReachedWarned = false; + [[maybe_unused]] bool maxCapsuleCountReachedWarned = false; AZStd::vector sphereColliders; AZStd::vector capsuleColliders; diff --git a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h index a400f742de..baac5b3776 100644 --- a/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h +++ b/Gems/NvCloth/Code/Source/Components/ClothComponentMesh/ActorClothColliders.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace NvCloth diff --git a/Gems/NvCloth/Code/Source/Utils/AssetHelper.h b/Gems/NvCloth/Code/Source/Utils/AssetHelper.h index e3e5a5e7ad..4a40fe26f0 100644 --- a/Gems/NvCloth/Code/Source/Utils/AssetHelper.h +++ b/Gems/NvCloth/Code/Source/Utils/AssetHelper.h @@ -10,6 +10,7 @@ #include #include +#include #include diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h index 3f6d001112..11215ef29b 100644 --- a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h index 843fffd4c8..c7a50c43f5 100644 --- a/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h +++ b/Gems/PhysX/Code/Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp index fb0a38fa18..c6def5f0ab 100644 --- a/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHeightfieldColliderComponent.cpp @@ -189,6 +189,9 @@ namespace PhysX configuration.m_entityId = GetEntityId(); configuration.m_debugName = GetEntity()->GetName(); + // Update material selection from the mapping + Utils::SetMaterialsFromHeightfieldProvider(GetEntityId(), m_colliderConfig.m_materialSelection); + AzPhysics::ShapeColliderPairList colliderShapePairs; colliderShapePairs.emplace_back(AZStd::make_shared(m_colliderConfig), m_shapeConfig); configuration.m_colliderAndShapeData = colliderShapePairs; diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 59b659a0bf..d99a76f306 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,7 @@ namespace PhysX m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::Offset, false); } - AZ::Crc32 EditorShapeColliderComponent::SubdivisionCountVisibility() + AZ::Crc32 EditorShapeColliderComponent::SubdivisionCountVisibility() const { if (m_shapeType == ShapeType::Cylinder) { @@ -64,6 +65,17 @@ namespace PhysX return AZ::Edit::PropertyVisibility::Hide; } + AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() const + { + if ((m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) + && GetEntity()->FindComponent() == nullptr) + { + return AZ::Edit::PropertyVisibility::Show; + } + + return AZ::Edit::PropertyVisibility::Hide; + } + void EditorShapeColliderComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -74,6 +86,7 @@ namespace PhysX ->Field("DebugDrawSettings", &EditorShapeColliderComponent::m_colliderDebugDraw) ->Field("ShapeConfigs", &EditorShapeColliderComponent::m_shapeConfigs) ->Field("SubdivisionCount", &EditorShapeColliderComponent::m_subdivisionCount) + ->Field("SingleSided", &EditorShapeColliderComponent::m_singleSided) ; if (auto editContext = serializeContext->GetEditContext()) @@ -100,6 +113,10 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSubdivisionCountChange) ->Attribute(AZ::Edit::Attributes::Visibility, &EditorShapeColliderComponent::SubdivisionCountVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_singleSided, "Single sided", + "If enabled, planar shapes will only collide from one direction (not valid for shapes attached to dynamic rigid bodies)") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSingleSidedChange) + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorShapeColliderComponent::SingleSidedVisibility) ; } } @@ -107,16 +124,16 @@ namespace PhysX void EditorShapeColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("PhysicsWorldBodyService", 0x944da0cc)); - provided.push_back(AZ_CRC("PhysXColliderService", 0x4ff43f7c)); - provided.push_back(AZ_CRC("PhysXTriggerService", 0x3a117d7b)); - provided.push_back(AZ_CRC("PhysXShapeColliderService", 0x98a7e779)); + provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService")); + provided.push_back(AZ_CRC_CE("PhysXColliderService")); + provided.push_back(AZ_CRC_CE("PhysXTriggerService")); + provided.push_back(AZ_CRC_CE("PhysXShapeColliderService")); } void EditorShapeColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC("TransformService", 0x8ee22c50)); - required.push_back(AZ_CRC("ShapeService", 0xe86aa5fe)); + required.push_back(AZ_CRC_CE("TransformService")); + required.push_back(AZ_CRC_CE("ShapeService")); } void EditorShapeColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) @@ -126,7 +143,6 @@ namespace PhysX incompatible.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); incompatible.push_back(AZ_CRC_CE("CompoundShapeService")); incompatible.push_back(AZ_CRC_CE("DiskShapeService")); - incompatible.push_back(AZ_CRC_CE("QuadShapeService")); incompatible.push_back(AZ_CRC_CE("TubeShapeService")); incompatible.push_back(AZ_CRC_CE("ReferenceShapeService")); } @@ -206,6 +222,12 @@ namespace PhysX break; } + case ShapeType::QuadSingleSided: + case ShapeType::QuadDoubleSided: + { + // a quad shape has no interior, just return an empty vector of sample points + break; + } default: AZ_WarningOnce("PhysX Shape Collider Component", false, "Unsupported shape type in UpdateCachedSamplePoints"); break; @@ -331,6 +353,11 @@ namespace PhysX UpdateCylinderConfig(uniformScale); } + else if (shapeCrc == ShapeConstants::Quad) + { + UpdateQuadConfig(overallScale); + } + else { m_shapeType = !shapeCrc ? ShapeType::None : ShapeType::Unsupported; @@ -354,6 +381,59 @@ namespace PhysX m_geometryCache.m_boxDimensions = scale * boxDimensions; } + void EditorShapeColliderComponent::UpdateQuadConfig(const AZ::Vector3& scale) + { + LmbrCentral::QuadShapeConfig quadShapeConfig; + LmbrCentral::QuadShapeComponentRequestBus::EventResult(quadShapeConfig, GetEntityId(), + &LmbrCentral::QuadShapeComponentRequests::GetQuadConfiguration); + + const float minDimension = 1e-3f; // used to prevent the dimensions being 0 in any direction + const float xDim = AZ::GetMax(minDimension, quadShapeConfig.m_width); + const float yDim = AZ::GetMax(minDimension, quadShapeConfig.m_height); + + if (m_singleSided) + { + AZStd::vector cookedData; + + constexpr AZ::u32 vertexCount = 4; + const AZ::Vector3 vertices[vertexCount] = + { + AZ::Vector3(-0.5f * xDim, -0.5f * yDim, 0.0f), + AZ::Vector3(-0.5f * xDim, 0.5f * yDim, 0.0f), + AZ::Vector3(0.5f * xDim, 0.5f * yDim, 0.0f), + AZ::Vector3(0.5f * xDim, -0.5f * yDim, 0.0f), + }; + + constexpr AZ::u32 indexCount = 6; + const AZ::u32 indices[indexCount] = + { + 0, 2, 1, + 0, 3, 2 + }; + + bool cookingResult = false; + Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory, + vertices, vertexCount, indices, indexCount, cookedData); + + Physics::CookedMeshShapeConfiguration shapeConfig; + shapeConfig.SetCookedMeshData(cookedData.data(), cookedData.size(), + Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh); + shapeConfig.m_scale = scale; + + SetShapeConfig(ShapeType::QuadSingleSided, shapeConfig); + } + else + { + // it's not possible to create a perfectly 2d convex in PhysX, so the best we can do is a very thin box + const float zDim = AZ::GetMax(minDimension, 1e-3f * AZ::GetMin(xDim, yDim)); + const AZ::Vector3 boxDimensions(xDim, yDim, zDim); + + SetShapeConfig(ShapeType::QuadDoubleSided, Physics::BoxShapeConfiguration(boxDimensions)); + + m_shapeConfigs.back()->m_scale = scale; + } + } + void EditorShapeColliderComponent::UpdateCapsuleConfig(const AZ::Vector3& scale) { LmbrCentral::CapsuleShapeConfig lmbrCentralCapsuleShapeConfig; @@ -425,6 +505,12 @@ namespace PhysX } } + void EditorShapeColliderComponent::OnSingleSidedChange() + { + UpdateShapeConfigs(); + CreateStaticEditorCollider(); + } + AZ::u32 EditorShapeColliderComponent::OnSubdivisionCountChange() { const AZ::Vector3 uniformScale = Utils::GetUniformScale(GetEntityId()); @@ -615,7 +701,9 @@ namespace PhysX m_editorSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName); } + UpdateSingleSidedSettings(); UpdateShapeConfigs(); + UpdateTriggerSettings(); // Debug drawing m_colliderDebugDraw.Connect(GetEntityId()); @@ -767,6 +855,7 @@ namespace PhysX if (changeReason == LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged) { UpdateShapeConfigs(); + UpdateTriggerSettings(); CreateStaticEditorCollider(); Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); @@ -849,4 +938,46 @@ namespace PhysX { return m_colliderConfig.m_isTrigger; } + + void EditorShapeColliderComponent::UpdateTriggerSettings() + { + if (m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) + { + if (!m_previousIsTrigger.has_value()) + { + m_previousIsTrigger = m_colliderConfig.m_isTrigger; + m_colliderConfig.m_isTrigger = false; + } + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, false); + } + else + { + if (m_previousIsTrigger.has_value()) + { + m_colliderConfig.m_isTrigger = m_previousIsTrigger.value(); + m_previousIsTrigger.reset(); + } + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, true); + } + } + + void EditorShapeColliderComponent::UpdateSingleSidedSettings() + { + if (GetEntity()->FindComponent()) + { + if (!m_previousSingleSided.has_value()) + { + m_previousSingleSided = m_singleSided; + } + m_singleSided = false; + } + else + { + if (m_previousSingleSided.has_value()) + { + m_singleSided = m_previousSingleSided.value(); + m_previousSingleSided.reset(); + } + } + } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 3422d4959f..09d5f803fc 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -41,6 +41,8 @@ namespace PhysX Sphere, PolygonPrism, Cylinder, + QuadDoubleSided, + QuadSingleSided, Unsupported }; @@ -89,6 +91,7 @@ namespace PhysX AZ::u32 OnConfigurationChanged(); void UpdateShapeConfigs(); void UpdateBoxConfig(const AZ::Vector3& scale); + void UpdateQuadConfig(const AZ::Vector3& scale); void UpdateCapsuleConfig(const AZ::Vector3& scale); void UpdateSphereConfig(const AZ::Vector3& scale); void UpdateCylinderConfig(const AZ::Vector3& scale); @@ -102,7 +105,9 @@ namespace PhysX void RefreshUiProperties(); AZ::u32 OnSubdivisionCountChange(); - AZ::Crc32 SubdivisionCountVisibility(); + AZ::Crc32 SubdivisionCountVisibility() const; + void OnSingleSidedChange(); + AZ::Crc32 SingleSidedVisibility() const; // AZ::Component void Activate() override; @@ -137,6 +142,9 @@ namespace PhysX AZ::Aabb GetColliderShapeAabb() override; bool IsTrigger() override; + void UpdateTriggerSettings(); + void UpdateSingleSidedSettings(); + Physics::ColliderConfiguration m_colliderConfig; //!< Stores collision layers, whether the collider is a trigger, etc. DebugDraw::Collider m_colliderDebugDraw; //!< Handles drawing the collider based on global and local AzPhysics::SceneInterface* m_sceneInterface = nullptr; @@ -151,6 +159,9 @@ namespace PhysX //! @note 16 is the number of subdivisions in the debug cylinder that is loaded as a mesh (not generated procedurally) AZ::u8 m_subdivisionCount = 16; mutable GeometryCache m_geometryCache; //!< Cached data for generating sample points inside the attached shape. + AZStd::optional m_previousIsTrigger; //!< Stores the previous trigger setting if the shape is changed to one which does not support triggers. + bool m_singleSided = false; //!< Used for 2d shapes like quad which may be treated as either single or doubled sided. + AZStd::optional m_previousSingleSided; //!< Stores the previous single sided setting when unable to support single-sided shapes (such as when used with a dynamic rigid body). AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physXConfigChangedHandler; AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_onMaterialLibraryChangedEventHandler; diff --git a/Gems/PhysX/Code/Source/ForceRegionComponent.cpp b/Gems/PhysX/Code/Source/ForceRegionComponent.cpp index f8f5f1991e..eba902049d 100644 --- a/Gems/PhysX/Code/Source/ForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/ForceRegionComponent.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include diff --git a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp index c1fa09f21f..b219253b6f 100644 --- a/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/HeightfieldColliderComponent.cpp @@ -140,6 +140,10 @@ namespace PhysX Physics::HeightfieldShapeConfiguration& configuration = static_cast(*m_shapeConfig.second); configuration = Utils::CreateHeightfieldShapeConfiguration(GetEntityId()); + + // Update material selection from the mapping + Physics::ColliderConfiguration* colliderConfig = m_shapeConfig.first.get(); + Utils::SetMaterialsFromHeightfieldProvider(GetEntityId(), colliderConfig->m_materialSelection); } void HeightfieldColliderComponent::RefreshHeightfield() diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp index 975fec2181..343332b2cd 100644 --- a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp +++ b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.cpp @@ -6,18 +6,20 @@ * */ -#include -#include -#include -#include +#include #include #include #include #include -#include #include +#include +#include +#include +#include +#include + namespace PhysX::Utils { struct PxJointActorData diff --git a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h index da7b3dc4e2..d5aa082763 100644 --- a/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h +++ b/Gems/PhysX/Code/Source/Joint/PhysXJointUtils.h @@ -14,8 +14,15 @@ #include +#include + namespace PhysX { + struct D6JointLimitConfiguration; + struct FixedJointConfiguration; + struct BallJointConfiguration; + struct HingeJointConfiguration; + namespace JointConstants { // Setting joint limits to very small values can cause extreme stability problems, so clamp above a small diff --git a/Gems/PhysX/Code/Source/JointComponent.cpp b/Gems/PhysX/Code/Source/JointComponent.cpp index d5c04598a5..085fc34b12 100644 --- a/Gems/PhysX/Code/Source/JointComponent.cpp +++ b/Gems/PhysX/Code/Source/JointComponent.cpp @@ -5,17 +5,18 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include #include -#include namespace PhysX { diff --git a/Gems/PhysX/Code/Source/Material.cpp b/Gems/PhysX/Code/Source/Material.cpp index be79728cc5..fa131b5159 100644 --- a/Gems/PhysX/Code/Source/Material.cpp +++ b/Gems/PhysX/Code/Source/Material.cpp @@ -7,9 +7,10 @@ */ #include "Material.h" +#include #include -#include #include +#include #include namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp index 1151c0ffff..220b870413 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp @@ -5,13 +5,10 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - #include + #include #include -#include -#include -#include #include #include #include @@ -20,6 +17,12 @@ #include #include +#include +#include +#include + +#include + namespace PhysX::Utils::Characters { AZ::Outcome GetNodeIndex(const Physics::RagdollConfiguration& configuration, const AZStd::string& nodeName) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp index 34fe3d0295..3e240f3611 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/RagdollNode.cpp @@ -5,16 +5,17 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - -#include -#include -#include #include + #include #include #include - #include +#include +#include +#include +#include + namespace PhysX { diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 7615a175d1..2c00fd188d 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -422,7 +422,7 @@ namespace PhysX return d1.m_depth < d2.m_depth; }); - bool massesClamped = false; + [[maybe_unused]] bool massesClamped = false; for (const auto& nodeDepth : nodeDepths) { const size_t nodeIndex = nodeDepth.m_index; diff --git a/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp b/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp index 2d05612d74..ece6acbd6f 100644 --- a/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.cpp @@ -6,15 +6,16 @@ * */ +#include #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h b/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h index 15f9bfb36e..d987b7194c 100644 --- a/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h +++ b/Gems/PhysX/Code/Source/Pipeline/StreamWrapper.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace PhysX diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index dfac43b703..6c02697e90 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -7,14 +7,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include @@ -31,6 +23,16 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace PhysX { AZ_CLASS_ALLOCATOR_IMPL(PhysXScene, AZ::SystemAllocator, 0); diff --git a/Gems/PhysX/Code/Source/ShapeColliderComponent.h b/Gems/PhysX/Code/Source/ShapeColliderComponent.h index 2065f17dcf..cfc51f08ed 100644 --- a/Gems/PhysX/Code/Source/ShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/ShapeColliderComponent.h @@ -30,6 +30,7 @@ namespace PhysX static const AZ::Crc32 Sphere = AZ_CRC("Sphere", 0x55f96687); static const AZ::Crc32 PolygonPrism = AZ_CRC("PolygonPrism", 0xd6b50036); static const AZ::Crc32 Cylinder = AZ_CRC("Cylinder", 0x9b045bea); + static const AZ::Crc32 Quad = AZ_CRC("QuadShape", 0x40d75e14); } // namespace ShapeConstants /// Component that provides a collider based on geometry from a shape component. diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index 39c0dfc686..ad6c923241 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -5,18 +5,20 @@ * 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 // only enable physx timestep warning when not running debug or in Release #if !defined(DEBUG) && !defined(RELEASE) diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index fecea57d9c..950404820d 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -66,6 +66,68 @@ namespace PhysX } } + AZStd::pair GetPhysXMaterialIndicesFromHeightfieldSamples( + const AZStd::vector& samples, + const int32_t row, const int32_t col, + const int32_t numRows, const int32_t numCols) + { + uint8_t materialIndex0 = 0; + uint8_t materialIndex1 = 0; + + const bool lastRowIndex = (row == (numRows - 1)); + const bool lastColumnIndex = (col == (numCols - 1)); + + // In PhysX, the material indices refer to the quad down and to the right of the sample. + // If we're in the last row or last column, there aren't any quads down or to the right, + // so just clear these out. + if (lastRowIndex || lastColumnIndex) + { + return { materialIndex0, materialIndex1 }; + } + + auto GetIndex = [numCols](int32_t row, int32_t col) + { + return (row * numCols) + col; + }; + + // Our source data is providing one material index per vertex, but PhysX wants one material index + // per triangle. The heuristic that we'll go with for selecting the material index is to choose + // the material for the vertex that's not on the diagonal of each triangle. + // Ex: A *---* B + // | / | For this, we'll use A for index0 and D for index1. + // C *---* D + // + // Ex: A *---* B + // | \ | For this, we'll use C for index0 and B for index1. + // C *---* D + // + // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this + // causes incorrect or unpredictable physics material mappings. + + const Physics::HeightMaterialPoint& currentSample = samples[GetIndex(row, col)]; + + switch (currentSample.m_quadMeshType) + { + case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: + materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; + materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: + materialIndex0 = currentSample.m_materialIndex; + materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; + break; + case Physics::QuadMeshType::Hole: + materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; + materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; + break; + default: + AZ_Assert(false, "Unhandled case in GetPhysXMaterialIndicesFromHeightfieldSamples"); + break; + } + + return { materialIndex0, materialIndex1 }; + } + void CreatePxGeometryFromHeightfield( Physics::HeightfieldShapeConfiguration& heightfieldConfig, physx::PxGeometryHolder& pxGeometry) { @@ -116,12 +178,8 @@ namespace PhysX for (int32_t row = 0; row < numRows; row++) { - const bool lastRowIndex = (row == (numRows - 1)); - for (int32_t col = 0; col < numCols; col++) { - const bool lastColumnIndex = (col == (numCols - 1)); - auto GetIndex = [numCols](int32_t row, int32_t col) { return (row * numCols) + col; @@ -134,52 +192,15 @@ namespace PhysX AZ_Assert(currentSample.m_materialIndex < physxMaximumMaterialIndex, "MaterialIndex must be less than 128"); currentPhysxSample.height = azlossy_cast( AZ::GetClamp(currentSample.m_height, minHeightBounds, maxHeightBounds) * scaleFactor); - if (lastRowIndex || lastColumnIndex) - { - // In PhysX, the material indices refer to the quad down and to the right of the sample. - // If we're in the last row or last column, there aren't any quads down or to the right, - // so just clear these out. - currentPhysxSample.materialIndex0 = 0; - currentPhysxSample.materialIndex1 = 0; - } - else + + auto [materialIndex0, materialIndex1] = GetPhysXMaterialIndicesFromHeightfieldSamples(samples, row, col, numRows, numCols); + currentPhysxSample.materialIndex0 = materialIndex0; + currentPhysxSample.materialIndex1 = materialIndex1; + + if (currentSample.m_quadMeshType == Physics::QuadMeshType::SubdivideUpperLeftToBottomRight) { - // Our source data is providing one material index per vertex, but PhysX wants one material index - // per triangle. The heuristic that we'll go with for selecting the material index is to choose - // the material for the vertex that's not on the diagonal of each triangle. - // Ex: A *---* B - // | / | For this, we'll use A for index0 and D for index1. - // C *---* D - // - // Ex: A *---* B - // | \ | For this, we'll use C for index0 and B for index1. - // C *---* D - // - // This is a pretty arbitrary choice, so the heuristic might need to be revisited over time if this - // causes incorrect or unpredictable physics material mappings. - - switch (currentSample.m_quadMeshType) - { - case Physics::QuadMeshType::SubdivideUpperLeftToBottomRight: - currentPhysxSample.materialIndex0 = samples[GetIndex(row + 1, col)].m_materialIndex; - currentPhysxSample.materialIndex1 = samples[GetIndex(row, col + 1)].m_materialIndex; - // Set the tesselation flag to say that we need to go from UL to BR - currentPhysxSample.materialIndex0.setBit(); - break; - case Physics::QuadMeshType::SubdivideBottomLeftToUpperRight: - currentPhysxSample.materialIndex0 = currentSample.m_materialIndex; - currentPhysxSample.materialIndex1 = samples[GetIndex(row + 1, col + 1)].m_materialIndex; - break; - case Physics::QuadMeshType::Hole: - currentPhysxSample.materialIndex0 = physx::PxHeightFieldMaterial::eHOLE; - currentPhysxSample.materialIndex1 = physx::PxHeightFieldMaterial::eHOLE; - break; - default: - AZ_Warning("PhysX Heightfield", false, "Unhandled case in CreatePxGeometryFromConfig"); - currentPhysxSample.materialIndex0 = 0; - currentPhysxSample.materialIndex1 = 0; - break; - } + // Set the tesselation flag to say that we need to go from UL to BR + currentPhysxSample.setTessFlag(); } } } @@ -1562,6 +1583,20 @@ namespace PhysX return configuration; } + + void SetMaterialsFromHeightfieldProvider(const AZ::EntityId& heightfieldProviderId, Physics::MaterialSelection& materialSelection) + { + AZStd::vector materialList; + Physics::HeightfieldProviderRequestsBus::EventResult( + materialList, heightfieldProviderId, &Physics::HeightfieldProviderRequestsBus::Events::GetMaterialList); + + materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray(materialList.size(), "")); + + for (int i = 0; i < materialList.size(); ++i) + { + materialSelection.SetMaterialId(materialList[i], i); + } + } } // namespace Utils namespace ReflectionUtils diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index 525db03315..5244926aa2 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -188,8 +188,15 @@ namespace PhysX //! Returns defaultValue if the input is infinite or NaN, otherwise returns the input unchanged. const AZ::Vector3& Sanitize(const AZ::Vector3& input, const AZ::Vector3& defaultValue = AZ::Vector3::CreateZero()); + AZStd::pair GetPhysXMaterialIndicesFromHeightfieldSamples( + const AZStd::vector& samples, + const int32_t row, const int32_t col, + const int32_t numRows, const int32_t numCols); + Physics::HeightfieldShapeConfiguration CreateHeightfieldShapeConfiguration(AZ::EntityId entityId); + void SetMaterialsFromHeightfieldProvider(const AZ::EntityId& heightfieldProviderId, Physics::MaterialSelection& materialSelection); + namespace Geometry { using PointList = AZStd::vector; diff --git a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp index ef112d8623..5f830acc8a 100644 --- a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using ::testing::NiceMock; using ::testing::Return; @@ -27,18 +28,28 @@ namespace PhysXEditorTests { AZStd::vector GetSamples() { - AZStd::vector samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, - { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight } }; + AZStd::vector samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 0 }, + { 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 1 }, + { 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 2 }, + { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 0 }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 1 }, + { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 2 }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 0 }, + { 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 1 }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight, 2 } }; return samples; } + AZStd::vector GetMaterialList() + { + AZStd::vector materials{ + {Physics::MaterialId::FromUUID("{EC976D51-2C26-4C1E-BBF2-75BAAAFA162C}")}, + {Physics::MaterialId::FromUUID("{B9836F51-A235-4781-95E3-A6302BEE9EFF}")}, + {Physics::MaterialId::FromUUID("{7E060707-BB03-47EB-B046-4503C7145B6E}")} + }; + return materials; + } + EntityPtr SetupHeightfieldComponent() { // create an editor entity with a shape collider component and a box shape component @@ -78,6 +89,7 @@ namespace PhysXEditorTests x = -3.0f; y = 3.0f; }); + ON_CALL(mockShapeRequests, GetMaterialList).WillByDefault(Return(GetMaterialList())); } EntityPtr TestCreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity) @@ -89,6 +101,89 @@ namespace PhysXEditorTests return gameEntity; } + class PhysXEditorHeightfieldFixture : public PhysXEditorFixture + { + public: + void SetUp() override + { + PhysXEditorFixture::SetUp(); + PopulateDefaultMaterialLibrary(); + + m_editorEntity = SetupHeightfieldComponent(); + m_editorMockShapeRequests = AZStd::make_unique>(m_editorEntity->GetId()); + SetupMockMethods(*m_editorMockShapeRequests.get()); + m_editorEntity->Activate(); + + m_gameEntity = TestCreateActiveGameEntityFromEditorEntity(m_editorEntity.get()); + m_gameMockShapeRequests = AZStd::make_unique>(m_gameEntity->GetId()); + SetupMockMethods(*m_gameMockShapeRequests.get()); + m_gameEntity->Activate(); + } + + void TearDown() override + { + CleanupHeightfieldComponent(); + + m_editorEntity = nullptr; + m_gameEntity = nullptr; + m_editorMockShapeRequests = nullptr; + m_gameMockShapeRequests = nullptr; + + PhysXEditorFixture::TearDown(); + } + + void PopulateDefaultMaterialLibrary() + { + AZ::Data::AssetId assetId = AZ::Data::AssetId(AZ::Uuid::Create()); + + // Create an asset out of our Script Event + Physics::MaterialLibraryAsset* matLibAsset = aznew Physics::MaterialLibraryAsset; + { + const AZStd::vector matIds = GetMaterialList(); + + for (const Physics::MaterialId& matId : matIds) + { + Physics::MaterialFromAssetConfiguration matConfig; + matConfig.m_id = matId; + matConfig.m_configuration.m_surfaceType = matId.GetUuid().ToString(); + matLibAsset->AddMaterialData(matConfig); + } + } + + // Note: There is no interface to simply update material library asset. It has to go via updating the entire configuration which causes assets reloading. + // It makes sense as a safety mechanism in the Editor but makes it harder to write tests. + // Hence have to work around it via const_cast here to be able to simply set the generated asset into configuration. + AzPhysics::SystemConfiguration* sysConfig = const_cast(AZ::Interface::Get()->GetConfiguration()); + + AZ::Data::Asset assetData(assetId, matLibAsset, AZ::Data::AssetLoadBehavior::Default); + sysConfig->m_materialLibraryAsset = assetData; + } + + Physics::Material* GetMaterialFromRaycast(float x, float y) + { + AzPhysics::RayCastRequest request; + request.m_start = AZ::Vector3(x, y, 5.0f); + request.m_direction = AZ::Vector3(0.0f, 0.0f, -1.0f); + request.m_distance = 10.0f; + + //query the scene + auto* sceneInterface = AZ::Interface::Get(); + AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(m_defaultSceneHandle, &request); + EXPECT_EQ(result.m_hits.size(), 1); + + if (result) + { + return result.m_hits[0].m_material; + } + + return nullptr; + }; + + EntityPtr m_editorEntity; + EntityPtr m_gameEntity; + AZStd::unique_ptr> m_editorMockShapeRequests; + AZStd::unique_ptr> m_gameMockShapeRequests; + }; TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesSatisfiedEntityIsValid) { @@ -149,21 +244,13 @@ namespace PhysXEditorTests CleanupHeightfieldComponent(); } - TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry) + TEST_F(PhysXEditorHeightfieldFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry) { - EntityPtr editorEntity = SetupHeightfieldComponent(); - NiceMock mockShapeRequests(editorEntity->GetId()); - SetupMockMethods(mockShapeRequests); - editorEntity->Activate(); - - EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get()); - NiceMock mockShapeRequests2(gameEntity->GetId()); - SetupMockMethods(mockShapeRequests2); - gameEntity->Activate(); + AZ::EntityId gameEntityId = m_gameEntity->GetId(); AzPhysics::SimulatedBody* staticBody = nullptr; AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( - staticBody, gameEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); + staticBody, gameEntityId, &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); @@ -183,7 +270,7 @@ namespace PhysXEditorTests int32_t numRows{ 0 }; int32_t numColumns{ 0 }; Physics::HeightfieldProviderRequestsBus::Event( - gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); + gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); EXPECT_EQ(numColumns, heightfield->getNbColumns()); EXPECT_EQ(numRows, heightfield->getNbRows()); @@ -194,12 +281,12 @@ namespace PhysXEditorTests float minHeightBounds{ 0.0f }; float maxHeightBounds{ 0.0f }; Physics::HeightfieldProviderRequestsBus::Event( - gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds, + gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds, maxHeightBounds); AZStd::vector samples; Physics::HeightfieldProviderRequestsBus::EventResult( - samples, gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + samples, gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); const float halfBounds{ (maxHeightBounds - minHeightBounds) / 2.0f }; const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds; @@ -208,7 +295,82 @@ namespace PhysXEditorTests EXPECT_EQ(samplePhysX.height, azlossy_cast(samplePhysics.m_height * scaleFactor)); } } - CleanupHeightfieldComponent(); + } + + TEST_F(PhysXEditorHeightfieldFixture, EditorHeightfieldColliderComponentHeightfieldColliderCorrectMaterials) + { + AZ::EntityId gameEntityId = m_gameEntity->GetId(); + + int32_t numRows{ 0 }; + int32_t numColumns{ 0 }; + Physics::HeightfieldProviderRequestsBus::Event( + gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); + + EXPECT_EQ(numRows, 3); + EXPECT_EQ(numColumns, 3); + + AZStd::vector samples; + Physics::HeightfieldProviderRequestsBus::EventResult( + samples, gameEntityId, &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + + AzPhysics::SimulatedBody* staticBody = nullptr; + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( + staticBody, gameEntityId, &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); + + const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); + PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); + + physx::PxShape* shape = nullptr; + pxRigidStatic->getShapes(&shape, 1, 0); + + physx::PxHeightFieldGeometry heightfieldGeometry; + shape->getHeightFieldGeometry(heightfieldGeometry); + + physx::PxHeightField* heightfield = heightfieldGeometry.heightField; + + AZStd::vector physicsSurfaceTypes; + for (Physics::MaterialId materialId : GetMaterialList()) + { + physicsSurfaceTypes.emplace_back(materialId.GetUuid().ToString()); + } + + // PhysX Heightfield cooking doesn't map 1-1 sample material indices to triangle material indices + // Hence hardcoding the expected material indices in the test + const AZStd::array physicsMaterialsValidationDataIndex = {0, 2, 1, 1}; + + for (int sampleRow = 0; sampleRow < numRows; ++sampleRow) + { + for (int sampleColumn = 0; sampleColumn < numColumns; ++sampleColumn) + { + physx::PxHeightFieldSample samplePhysX = heightfield->getSample(sampleRow, sampleColumn); + + auto [materialIndex0, materialIndex1] = PhysX::Utils::GetPhysXMaterialIndicesFromHeightfieldSamples(samples, sampleRow, sampleColumn, numRows, numColumns); + EXPECT_EQ(samplePhysX.materialIndex0, materialIndex0); + EXPECT_EQ(samplePhysX.materialIndex1, materialIndex1); + + if (sampleRow != numRows - 1 && sampleColumn != numColumns - 1) + { + const float x_offset = -0.25f; + const float y_offset = 0.75f; + const float secondRayOffset = 0.5f; + + float rayX = x_offset + sampleColumn; + float rayY = y_offset + sampleRow; + + Physics::Material* mat1 = GetMaterialFromRaycast(rayX, rayY); + EXPECT_NE(mat1, nullptr); + + Physics::Material* mat2 = GetMaterialFromRaycast(rayX + secondRayOffset, rayY + secondRayOffset); + EXPECT_NE(mat2, nullptr); + + if (mat1) + { + AZStd::string expectedMaterialName = physicsSurfaceTypes[physicsMaterialsValidationDataIndex[sampleRow * 2 + sampleColumn]]; + EXPECT_EQ(mat1->GetSurfaceTypeName(), expectedMaterialName); + } + } + } + } } } // namespace PhysXEditorTests diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp index fa794c58a5..272d9157b2 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp @@ -70,6 +70,24 @@ namespace PhysXEditorTests } } + void PhysXEditorFixture::ConnectToPVD() + { + auto* debug = AZ::Interface::Get(); + if (debug) + { + debug->ConnectToPvd(); + } + } + + void PhysXEditorFixture::DisconnectFromPVD() + { + auto* debug = AZ::Interface::Get(); + if (debug) + { + debug->DisconnectFromPvd(); + } + } + // DefaultWorldBus AzPhysics::SceneHandle PhysXEditorFixture::GetDefaultSceneHandle() const { diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.h b/Gems/PhysX/Code/Tests/EditorTestUtilities.h index 9dfe40f366..26566f724a 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.h +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.h @@ -48,6 +48,9 @@ namespace PhysXEditorTests void SetUp() override; void TearDown() override; + void ConnectToPVD(); + void DisconnectFromPVD(); + // DefaultWorldBus AzPhysics::SceneHandle GetDefaultSceneHandle() const override; diff --git a/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp b/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp index 2366f65e07..f79b7c8d34 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp +++ b/Gems/PhysX/Code/Tests/PhysXTestUtil.cpp @@ -9,6 +9,7 @@ #include "PhysXTestUtil.h" #include #include +#include namespace PhysX { diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index 06ba4f3e9c..c9dcbe64d1 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -452,21 +453,55 @@ namespace PhysXEditorTests EXPECT_TRUE(aabb.GetMin().IsClose(translation - 0.5f * scale * boxDimensions)); } - void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) + void SetBoolValueOnComponent(AZ::Component* component, AZ::Crc32 name, bool value) { AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy; - instanceDataHierarchy.AddRootInstance(editorShapeColliderComponent); + instanceDataHierarchy.AddRootInstance(component); instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_WRITE); AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode = - instanceDataHierarchy.FindNodeByPartialAddress({ AZ_CRC("Trigger", 0x1a6b0f5d) }); + instanceDataHierarchy.FindNodeByPartialAddress({ name }); if (instanceNode) { - instanceNode->Write(isTrigger); + instanceNode->Write(value); } } + void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) + { + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC_CE("Trigger"), isTrigger); + } + + bool GetBoolValueFromComponent(AZ::Component* component, AZ::Crc32 name) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); + AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy; + instanceDataHierarchy.AddRootInstance(component); + instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_READ); + AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode = + instanceDataHierarchy.FindNodeByPartialAddress({ name }); + bool value = false; + instanceNode->Read(value); + return value; + } + + bool IsTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) + { + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC_CE("Trigger")); + } + + void SetSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool singleSided) + { + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC_CE("SingleSided"), singleSided); + } + + bool IsSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) + { + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC_CE("SingleSided")); + } + EntityPtr CreateRigidBox(const AZ::Vector3& boxDimensions, const AZ::Vector3& position) { EntityPtr rigidBodyEditorEntity = CreateInactiveEditorEntity("RigidBodyEditorEntity"); @@ -566,4 +601,147 @@ namespace PhysXEditorTests EXPECT_THAT(aabb.GetMin(), UnitTest::IsClose(-0.5f * boxDimensions * parentScale)); } + class PhysXEditorParamBoolFixture + : public ::testing::WithParamInterface + , public PhysXEditorFixture + { + }; + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_ShapeColliderWithQuadShapeNonUniformlyScalesCorrectly) + { + // test both single and double-sided quad colliders + bool singleSided = GetParam(); + + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, singleSided); + editorEntity->CreateComponent(); + const auto entityId = editorEntity->GetId(); + + editorEntity->Activate(); + + LmbrCentral::QuadShapeComponentRequestBus::Event(entityId, &LmbrCentral::QuadShapeComponentRequests::SetQuadWidth, 1.2f); + LmbrCentral::QuadShapeComponentRequestBus::Event(entityId, &LmbrCentral::QuadShapeComponentRequests::SetQuadHeight, 0.8f); + + // update the transform scale and non-uniform scale + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, 3.0f); + AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3(1.5f, 0.5f, 1.0f)); + + // make a game entity and check that its AABB is as expected + EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); + AZ::Aabb aabb = gameEntity->FindComponent()->GetAabb(); + + EXPECT_NEAR(aabb.GetMin().GetX(), -2.7f, 1e-3f); + EXPECT_NEAR(aabb.GetMin().GetY(), -0.6f, 1e-3f); + EXPECT_NEAR(aabb.GetMax().GetX(), 2.7f, 1e-3f); + EXPECT_NEAR(aabb.GetMax().GetY(), 0.6f, 1e-3f); + } + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_TriggerSettingIsRememberedWhenSwitchingToQuadAndBack) + { + bool initialTriggerSetting = GetParam(); + + // create an editor entity with a box component (which does support trigger) + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + auto* boxShapeComponent = editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetTrigger(shapeColliderComponent, initialTriggerSetting); + editorEntity->Activate(); + + // the trigger setting should be what it was set to + EXPECT_EQ(IsTrigger(shapeColliderComponent), initialTriggerSetting); + + // deactivate the entity and swap the box for a quad (which does not support trigger) + editorEntity->Deactivate(); + editorEntity->RemoveComponent(boxShapeComponent); + auto* quadShapeComponent = editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + editorEntity->Activate(); + + // the trigger setting should now be false, because quad shape does not support triggers + EXPECT_FALSE(IsTrigger(shapeColliderComponent)); + + // swap back to a box shape + editorEntity->Deactivate(); + editorEntity->RemoveComponent(quadShapeComponent); + editorEntity->AddComponent(boxShapeComponent); + editorEntity->Activate(); + + // the original trigger setting should have been remembered + EXPECT_EQ(IsTrigger(shapeColliderComponent), initialTriggerSetting); + + // the quad shape component is no longer attached to the entity so won't be automatically cleared up + delete quadShapeComponent; + } + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_SingleSidedSettingIsRememberedWhenAddingAndRemovingRigidBody) + { + bool initialSingleSidedSetting = GetParam(); + + // create an editor entity without a rigid body (that means both single-sided and double-sided quads are valid) + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, initialSingleSidedSetting); + editorEntity->Activate(); + + // verify that the single sided setting matches the initial value + EXPECT_EQ(IsSingleSided(shapeColliderComponent), initialSingleSidedSetting); + + // add an editor rigid body component (this should mean single-sided quads are not supported) + editorEntity->Deactivate(); + auto rigidBodyComponent = editorEntity->CreateComponent(); + editorEntity->Activate(); + + EXPECT_FALSE(IsSingleSided(shapeColliderComponent)); + + // remove the editor rigid body component (the previous single-sided setting should be restored) + editorEntity->Deactivate(); + editorEntity->RemoveComponent(rigidBodyComponent); + editorEntity->Activate(); + + EXPECT_EQ(IsSingleSided(shapeColliderComponent), initialSingleSidedSetting); + + // the rigid body component is no longer attached to the entity so won't be automatically cleared up + delete rigidBodyComponent; + } + + INSTANTIATE_TEST_CASE_P(PhysXEditorTest, PhysXEditorParamBoolFixture, ::testing::Bool()); + + TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_SingleSidedQuadDoesNotCollideFromBelow) + { + // create an editor entity without a rigid body (that means both single-sided and double-sided quads are valid), positioned at the origin + EntityPtr editorQuadEntity = CreateInactiveEditorEntity("QuadEntity"); + editorQuadEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorQuadEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, true); + editorQuadEntity->Activate(); + LmbrCentral::QuadShapeComponentRequestBus::Event(editorQuadEntity->GetId(), &LmbrCentral::QuadShapeComponentRequests::SetQuadHeight, 10.0f); + LmbrCentral::QuadShapeComponentRequestBus::Event(editorQuadEntity->GetId(), &LmbrCentral::QuadShapeComponentRequests::SetQuadWidth, 10.0f); + + // add a second entity with a box collider and a rigid body, positioned below the quad + EntityPtr editorBoxEntity = CreateInactiveEditorEntity("BoxEntity"); + editorBoxEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId); + editorBoxEntity->CreateComponent(); + editorBoxEntity->CreateComponent(); + editorBoxEntity->Activate(); + AZ::TransformBus::Event(editorBoxEntity->GetId(), &AZ::TransformBus::Events::SetWorldTranslation, -AZ::Vector3::CreateAxisZ()); + + EntityPtr gameQuadEntity = CreateActiveGameEntityFromEditorEntity(editorQuadEntity.get()); + EntityPtr gameBoxEntity = CreateActiveGameEntityFromEditorEntity(editorBoxEntity.get()); + + // give the box enough upward velocity to rise above the level of the quad + // simulate for enough time that the box would have reached the top of its trajectory and fallen back past the starting point if + // it hadn't collided with the top of the quad + const int numTimesteps = 100; + Physics::RigidBodyRequestBus::Event(gameBoxEntity->GetId(), &Physics::RigidBodyRequests::SetLinearVelocity, AZ::Vector3::CreateAxisZ(6.0f)); + PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, numTimesteps); + + // the box should travel through the base of the quad because it has no collision from that direction + // and land on the top surface of the quad, which does have collision + float finalHeight = 0.0f; + AZ::TransformBus::EventResult(finalHeight, gameBoxEntity->GetId(), &AZ::TransformBus::Events::GetWorldZ); + + EXPECT_GT(finalHeight, 0.0f); + } } // namespace PhysXEditorTests diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp index 65115f2790..746809a7ca 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp @@ -8,10 +8,7 @@ #include "SystemComponent.h" -#include -#include -#include -#include +#include #include #include @@ -19,20 +16,24 @@ #include #include +#include +#include +#include +#include + +#include #include #include #include #include #include -#include - -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include namespace PhysXDebug { diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index 27397f05f7..3e071570bd 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -536,8 +536,7 @@ namespace Profiler ImGui::Columns(1, "TimelineColumn", true); // Timeline - if (ImGui::BeginChild( - "Timeline", { 0, 0 }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) + if (ImGui::BeginChild("Timeline", { 0, 0 }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { // Find the next frame boundary after the viewport's right bound and draw until that tick auto nextFrameBoundaryItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), m_viewportEndTick); diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names b/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names index 9f6af426d0..72e48aa400 100644 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names +++ b/Gems/ScriptCanvas/Assets/TranslationAssets/Classes/MaterialData.names @@ -5,7 +5,8 @@ "context": "BehaviorClass", "variant": "", "details": { - "name": "Material Data" + "name": "Material Data", + "category": "Rendering" }, "methods": [ { diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names b/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names index 8ce3fd36df..120c8f8ef3 100644 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names +++ b/Gems/ScriptCanvas/Assets/TranslationAssets/Nodes/Input_InputHandler.names @@ -1,7 +1,7 @@ { "entries": [ { - "base": "{0B0AC61B-4BBA-42BF-BDCD-DAF2D3CA41A8}", + "base": "{0A2EB488-5A6A-E166-BB62-23FF81499E33}", "context": "ScriptCanvas::Node", "variant": "", "details": { @@ -10,6 +10,13 @@ "tooltip": "Handle processed input events found in input binding assets" }, "slots": [ + { + "base": "Input_Connect Event_0", + "details": { + "name": "Connect Event", + "tooltip": "Connect to input event name as defined in an input binding asset." + } + }, { "base": "DataInput_Event Name_0", "details": { @@ -17,27 +24,34 @@ } }, { - "base": "DataOutput_Value_0", + "base": "Output_On Connect Event_0", "details": { - "name": "Value" + "name": "On Connect Event", + "tooltip": "Connect to input event name as defined in an input binding asset." } }, { - "base": "Output_Pressed_0", + "base": "Output_Pressed_1", "details": { "name": "Pressed", "tooltip": "Signaled when the input event begins." } }, { - "base": "Output_Held_1", + "base": "DataOutput_value_0", + "details": { + "name": "value" + } + }, + { + "base": "Output_Held_2", "details": { "name": "Held", "tooltip": "Signaled while the input event is active." } }, { - "base": "Output_Released_2", + "base": "Output_Released_3", "details": { "name": "Released", "tooltip": "Signaled when the input event ends." diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp index da0e9c6045..86fdc0342b 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.cpp @@ -145,7 +145,7 @@ namespace ScriptCanvasEditor ScriptEvents::ScriptEventsAsset* data = asset.GetAs(); if (data) { - const ScriptEvents::ScriptEvent* previousDefinition = nullptr; + [[maybe_unused]] const ScriptEvents::ScriptEvent* previousDefinition = nullptr; ScriptEvents::ScriptEventsAsset* previousData = m_asset.GetAs(); if (previousData) { diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp index 60d903d748..9387b58c71 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp @@ -881,6 +881,23 @@ namespace ScriptCanvasEditor return ""; } + void NodePaletteDockWidget::NavigateToTranslationFile(GraphCanvas::NodePaletteTreeItem* nodePaletteItem) + { + if (nodePaletteItem) + { + AZ::IO::Path gemPath = GetGemPath("ScriptCanvas.Editor"); + gemPath = gemPath / AZ::IO::Path("TranslationAssets"); + gemPath = gemPath / nodePaletteItem->GetTranslationDataPath(); + gemPath.ReplaceExtension(".names"); + + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + if (fileIO && fileIO->Exists(gemPath.c_str())) + { + AzQtComponents::ShowFileOnDesktop(gemPath.c_str()); + } + } + } + void NodePaletteDockWidget::GenerateTranslation() { QModelIndexList indexList = GetTreeView()->selectionModel()->selectedRows(); @@ -894,6 +911,16 @@ namespace ScriptCanvasEditor GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); nodePaletteItem->GenerateTranslationData(); } + + if (indexList.size() == 1) + { + QModelIndex sourceIndex = filterModel->mapToSource(indexList[0]); + if (sourceIndex.isValid()) + { + GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); + NavigateToTranslationFile(nodePaletteItem); + } + } } void NodePaletteDockWidget::OpenTranslationData() @@ -909,19 +936,7 @@ namespace ScriptCanvasEditor QModelIndex sourceIndex = filterModel->mapToSource(index); GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast(sourceIndex.internalPointer()); - if (nodePaletteItem) - { - AZ::IO::Path gemPath = GetGemPath("ScriptCanvas.Editor"); - gemPath = gemPath / AZ::IO::Path("TranslationAssets"); - gemPath = gemPath / nodePaletteItem->GetTranslationDataPath(); - gemPath.ReplaceExtension(".names"); - - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO->Exists(gemPath.c_str())) - { - AzQtComponents::ShowFileOnDesktop(gemPath.c_str()); - } - } + NavigateToTranslationFile(nodePaletteItem); } } } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h index 9f5fa0511f..3ee26e3f71 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h @@ -209,6 +209,7 @@ namespace ScriptCanvasEditor void HandleTreeItemDoubleClicked(GraphCanvas::GraphCanvasTreeItem* treeItem); void OpenTranslationData(); void GenerateTranslation(); + void NavigateToTranslationFile(GraphCanvas::NodePaletteTreeItem*); void ConfigureHelper(); void ParseCycleTargets(GraphCanvas::GraphCanvasTreeItem* treeItem); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp index f703bb17b1..c00c663f68 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h b/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h index 64a0e49bca..922bfb1dbc 100644 --- a/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h +++ b/Gems/ScriptEvents/Code/Source/ScriptEventsSystemComponent.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/Gems/Terrain/Code/CMakeLists.txt b/Gems/Terrain/Code/CMakeLists.txt index 69a1bef3c0..44e33b7008 100644 --- a/Gems/Terrain/Code/CMakeLists.txt +++ b/Gems/Terrain/Code/CMakeLists.txt @@ -98,7 +98,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAME Terrain.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem FILES_CMAKE - terrain_files.cmake terrain_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp index c74a478dad..a27139f062 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp @@ -6,12 +6,14 @@ * */ + #include #include #include #include #include +#include #include #include #include diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp index d689d2635c..4a0d260a74 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp @@ -291,7 +291,7 @@ namespace Terrain modelAssetCreator.Begin(AZ::Uuid::CreateRandom()); uint16_t gridSize = GridSize; - float gridSpacing = GridSpacing; + [[maybe_unused]] float gridSpacing = GridSpacing; for (uint32_t i = 0; i < AZ::RPI::ModelLodAsset::LodCountMax && gridSize > 0; ++i) { diff --git a/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h b/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h index a9565aedbe..d9bae70a31 100644 --- a/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h +++ b/Gems/WhiteBox/Code/Source/Viewport/WhiteBoxVertexTranslationModifier.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/Registry/gem_autoload.materialeditor.setreg b/Registry/gem_autoload.materialeditor.setreg index fd00ff05c7..653cc80056 100644 --- a/Registry/gem_autoload.materialeditor.setreg +++ b/Registry/gem_autoload.materialeditor.setreg @@ -28,6 +28,9 @@ "ScriptCanvas.Editor": { "AutoLoad": false }, + "ScriptCanvasDeveloper": { + "AutoLoad": false + }, "ScriptCanvasPhysics": { "AutoLoad": false }, diff --git a/Registry/gem_autoload.shadermanagementconsole.setreg b/Registry/gem_autoload.shadermanagementconsole.setreg index fd00ff05c7..653cc80056 100644 --- a/Registry/gem_autoload.shadermanagementconsole.setreg +++ b/Registry/gem_autoload.shadermanagementconsole.setreg @@ -28,6 +28,9 @@ "ScriptCanvas.Editor": { "AutoLoad": false }, + "ScriptCanvasDeveloper": { + "AutoLoad": false + }, "ScriptCanvasPhysics": { "AutoLoad": false }, diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 8bd4fbe29a..672259b076 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -315,7 +315,7 @@ class EditorTestSuite(): return 8 ## Internal ## - _TIMEOUT_CRASH_LOG = 20 # Maximum time (seconds) for waiting for a crash file, in secondss + _TIMEOUT_CRASH_LOG = 20 # Maximum time (seconds) for waiting for a crash file, in seconds _TEST_FAIL_RETCODE = 0xF # Return code for test failure @pytest.fixture(scope="class") @@ -772,7 +772,8 @@ class EditorTestSuite(): return_code = editor.get_returncode() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) # Save the editor log - workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name)) + workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name), + f'({run_id}){log_name}') if return_code == 0: test_result = Result.Pass.create(test_spec, output, editor_log_content) else: @@ -847,7 +848,8 @@ class EditorTestSuite(): return_code = editor.get_returncode() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) # Save the editor log - workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name)) + workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name), + f'({run_id}){log_name}') if return_code == 0: # No need to scrap the output, as all the tests have passed for test_spec in test_spec_list: diff --git a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake index c6a262926c..d231954c49 100644 --- a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake +++ b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake @@ -11,7 +11,7 @@ ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-android TARGETS expat PACKAGE_HASH 8c626bd58c2f9bb82cb654c2483be60e67f3a601547dee43686638a5e662bf3e) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 8db3099a5c..ee91415a6d 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -13,7 +13,7 @@ ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev3-multiplatform TARGETS pybind11 PACKAGE_HASH dccb5546607b8b31cd207033aaf24ab044ce6e188a9f12411236a010f9e0c4ff) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-linux TARGETS expat PACKAGE_HASH 07621d684fd909e2768e696a2652cfb1e975093f738193cfdcb60a016a9a9d4e) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 95f7c89930..1bcad01c1d 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -13,7 +13,7 @@ ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev3-multiplatform TARGETS pybind11 PACKAGE_HASH dccb5546607b8b31cd207033aaf24ab044ce6e188a9f12411236a010f9e0c4ff) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-mac TARGETS expat PACKAGE_HASH 231ec2cb8ef9ddeef891e5bd7215ad91864e3410a22fad5ab8355e7bf53621fe) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 1f7cb8d28f..c872708a3a 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -13,7 +13,7 @@ ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev3-multiplatform TARGETS pybind11 PACKAGE_HASH dccb5546607b8b31cd207033aaf24ab044ce6e188a9f12411236a010f9e0c4ff) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-windows TARGETS expat PACKAGE_HASH 123d81dcd0e30306fdb0d062348b992e68358cc31f0a15a98c4c04fc68dc437e) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake index 4b99efc9d0..3f1bb204f7 100644 --- a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake +++ b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake @@ -11,7 +11,7 @@ ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME expat-2.4.2-rev1-ios TARGETS expat PACKAGE_HASH 2e4f1ecaa9d7e9eb2f6f3b4a32d1b1a12605accf0ab3975175553b90917eb2d5) ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) diff --git a/cmake/Configurations.cmake b/cmake/Configurations.cmake index 63af3d8810..20d94024bb 100644 --- a/cmake/Configurations.cmake +++ b/cmake/Configurations.cmake @@ -42,6 +42,8 @@ include(cmake/ConfigurationTypes.cmake) # \arg:LINK_SHARED # \arg:LINK_SHARED_${CONFIGURATION} # +# Note: COMPILATION_C/COMPILATION_CXX are mutually exclusive with COMPILATION. You can only specify COMPILATION for C/C++ flags or +# a combination of COMPILATION_C/COMPILATION_CXX for the separate c/c++ flags separately. function(ly_append_configurations_options) set(options) @@ -80,15 +82,14 @@ function(ly_append_configurations_options) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILATION_STR}" PARENT_SCOPE) endif() if(ly_append_configurations_options_COMPILATION_C) - string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION}") - string(APPEND CMAKE_C_FLAGS " " ${COMPILATION_STR}) - set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} PARENT_SCOPE) + string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION_C}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILATION_STR}" PARENT_SCOPE) endif() if(ly_append_configurations_options_COMPILATION_CXX) - string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION}") - string(APPEND CMAKE_CXX_FLAGS " " ${COMPILATION_STR}) - set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE) + string(REPLACE ";" " " COMPILATION_STR "${ly_append_configurations_options_COMPILATION_CXX}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILATION_STR}" PARENT_SCOPE) endif() + if(ly_append_configurations_options_LINK) string(REPLACE ";" " " LINK_STR "${ly_append_configurations_options_LINK}") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${LINK_STR}" PARENT_SCOPE) diff --git a/cmake/Platform/Android/Toolchain_android.cmake b/cmake/Platform/Android/Toolchain_android.cmake index 974eb33681..23b51a4c70 100644 --- a/cmake/Platform/Android/Toolchain_android.cmake +++ b/cmake/Platform/Android/Toolchain_android.cmake @@ -36,9 +36,17 @@ endif() if(NOT ANDROID_ABI MATCHES "^arm64-") message(FATAL_ERROR "Only the 64-bit ANDROID_ABI's are supported. arm64-v8a can be used if not set") endif() + +set(MIN_NATIVE_API_LEVEL 24) + if(NOT ANDROID_NATIVE_API_LEVEL) - set(ANDROID_NATIVE_API_LEVEL 24) + set(ANDROID_NATIVE_API_LEVEL ${MIN_NATIVE_API_LEVEL}) endif() + +if(${ANDROID_NATIVE_API_LEVEL} VERSION_LESS ${MIN_NATIVE_API_LEVEL}) + message(FATAL_ERROR "Unsupported Android native API version ${ANDROID_NATIVE_API_LEVEL}. Must be ${MIN_NATIVE_API_LEVEL} or above") +endif() + set(ANDROID_PLATFORM android-${ANDROID_NATIVE_API_LEVEL}) # Make a backup of the CMAKE_FIND_ROOT_PATH since it will be altered by the NDK toolchain file and needs to be restored after the input diff --git a/cmake/Platform/Common/GCC/Configurations_gcc.cmake b/cmake/Platform/Common/GCC/Configurations_gcc.cmake index 17c2474fb8..3db96306ee 100644 --- a/cmake/Platform/Common/GCC/Configurations_gcc.cmake +++ b/cmake/Platform/Common/GCC/Configurations_gcc.cmake @@ -20,7 +20,17 @@ endif() ly_append_configurations_options( - COMPILATION + + COMPILATION_C + -fno-exceptions + -fvisibility=hidden + -Wall + -Werror + + ${LY_GCC_GCOV_FLAGS} + ${LY_GCC_GPROF_FLAGS} + + COMPILATION_CXX -fno-exceptions -fvisibility=hidden -Wall @@ -40,10 +50,8 @@ ly_append_configurations_options( -Wno-unused-value -Wno-unused-variable -Wno-format-truncation - -Wno-reorder -Wno-uninitialized -Wno-array-bounds - -Wno-class-memaccess -Wno-nonnull-compare -Wno-strict-aliasing -Wno-unused-result @@ -58,18 +66,14 @@ ly_append_configurations_options( -Wno-enum-compare -Wno-int-in-bool-context -Wno-sequence-point - -Wno-delete-non-virtual-dtor -Wno-comment - -Wno-reorder -Wno-restrict -Wno-format-overflow - - COMPILATION_C - -Wno-absolute-value - - COMPILATION_CXX -fvisibility-inlines-hidden -Wno-invalid-offsetof + -Wno-class-memaccess + -Wno-delete-non-virtual-dtor + -Wno-reorder COMPILATION_DEBUG -O0 # No optimization diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 46130f1345..e42664c107 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -445,9 +445,10 @@ function(ly_setup_cmake_install) ) endforeach() - # Transform the LY_EXTERNAL_SUBDIRS list into a json array + # Transform the LY_EXTERNAL_SUBDIRS global property list into a json array set(indent " ") - foreach(external_subdir ${LY_EXTERNAL_SUBDIRS}) + get_property(external_subdirs GLOBAL PROPERTY LY_EXTERNAL_SUBDIRS) + foreach(external_subdir ${external_subdirs}) cmake_path(RELATIVE_PATH external_subdir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE engine_rel_external_subdir) list(APPEND relative_external_subdirs "\"${engine_rel_external_subdir}\"") endforeach() @@ -639,7 +640,8 @@ function(ly_setup_assets) # the install layout from the root directory. Such as /Cache. # This is also done to avoid globbing thousands of files in subdirectories that shouldn't # be processed. - foreach(gem_candidate_dir IN LISTS LY_EXTERNAL_SUBDIRS LY_PROJECTS) + get_property(external_subdirs GLOBAL PROPERTY LY_EXTERNAL_SUBDIRS) + foreach(gem_candidate_dir IN LISTS external_subdirs LY_PROJECTS) file(REAL_PATH ${gem_candidate_dir} gem_candidate_dir BASE_DIRECTORY ${LY_ROOT_FOLDER}) # Don't recurse immediately in order to exclude transient source artifacts file(GLOB diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index 5e414e7738..a12006bd53 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -181,7 +181,7 @@ def main(args): default=-1) parser.add_argument(ANDROID_NATIVE_API_LEVEL, - help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_ANDROID_SDK_PLATFORM})', + help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_NATIVE_API_LEVEL})', type=int, default=-1) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 51f89d7829..b55689b871 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -813,7 +813,7 @@ try { } } - pipelineProperties.add(parameters(pipelineParameters)) + pipelineProperties.add(parameters(pipelineParameters.unique())) properties(pipelineProperties) // Stash the INCREMENTAL_BUILD_SCRIPT_PATH since all nodes will use it diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index c505d8e940..da538b22ec 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -35,7 +35,7 @@ "PARAMETERS": { "CONFIGURATION":"debug", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -50,7 +50,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_UNITY_BUILD=FALSE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -102,7 +102,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -118,7 +118,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\mono_android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_MONOLITHIC_GAME=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=24 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_MONOLITHIC_GAME=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" diff --git a/scripts/build/Platform/Android/pipeline.json b/scripts/build/Platform/Android/pipeline.json index f0292f4501..2e426c7891 100644 --- a/scripts/build/Platform/Android/pipeline.json +++ b/scripts/build/Platform/Android/pipeline.json @@ -1,9 +1,9 @@ { "ENV": { "GRADLE_HOME": "C:/Gradle/gradle-7.0", - "NODE_LABEL": "windows-b3c8994f1", + "NODE_LABEL": "windows-a4a28adb2", "LY_3RDPARTY_PATH": "D:/workspace/3rdParty", - "LY_NDK_DIR": "C:/ly/3rdParty/android-ndk/r21d", + "LY_NDK_DIR": "C:/Android/android-sdk/ndk/21.4.7075529", "TIMEOUT": 30, "WORKSPACE": "D:/workspace", "MOUNT_VOLUME": true diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 798550370f..c77c262692 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -9,6 +9,7 @@ REM SETLOCAL EnableDelayedExpansion CALL "%~dp0env_windows.cmd" +IF NOT %ERRORLEVEL%==0 EXIT /b 1 IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. @@ -21,7 +22,7 @@ ECHO [ci_build] cmake --version cmake --version IF ERRORLEVEL 1 ( ECHO [ci_build] CMAKE not found! - exit /b 1 + EXIT /b 1 ) REM Compute half the amount of processors so some jobs can run @@ -47,14 +48,14 @@ IF NOT EXIST CMakeCache.txt ( ) ) IF DEFINED RUN_CONFIGURE ( - ECHO [ci_build] %CONFIGURE_CMD% - %CONFIGURE_CMD% + call ECHO [ci_build] %CONFIGURE_CMD% + call %CONFIGURE_CMD% IF NOT !ERRORLEVEL!==0 GOTO :error ECHO !CONFIGURE_CMD!> %LAST_CONFIGURE_CMD_FILE% ) -ECHO [ci_build] cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% -cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% +call ECHO [ci_build] cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% +call cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS% IF NOT %ERRORLEVEL%==0 GOTO :error POPD diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index 78b5ca6c1a..c6f46ef575 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -13,17 +13,21 @@ SETLOCAL EnableExtensions EnableDelayedExpansion where /Q cmake IF NOT %ERRORLEVEL%==0 ( ECHO [ci_build] CMake not found - GOTO :error -) - -IF NOT "%COMMAND_CWD%"=="" ( - ECHO [ci_build] Changing CWD to %COMMAND_CWD% - CD %COMMAND_CWD% + GOTO :errorlocal ) REM Ending the local environment to be able to propagate the TMP/TEMP variables to the calling script ENDLOCAL +IF NOT "%COMMAND_CWD%"=="" ( + call ECHO [ci_build] Changing CWD to %COMMAND_CWD% + call CD %COMMAND_CWD% + IF ERRORLEVEL 1 ( + ECHO [ci_build] Failed to change directory to %COMMAND_CWD% + GOTO :error + ) +) + REM Jenkins does not defined TMP IF "%TMP%"=="" ( IF "%WORKSPACE%"=="" ( @@ -41,6 +45,7 @@ IF "%TMP%"=="" ( EXIT /b 0 -:error +:errorlocal ENDLOCAL +:error EXIT /b 1 \ No newline at end of file diff --git a/scripts/build/build_node/Platform/Windows/install_android.ps1 b/scripts/build/build_node/Platform/Windows/install_android.ps1 index 0a725af573..497ec508cc 100644 --- a/scripts/build/build_node/Platform/Windows/install_android.ps1 +++ b/scripts/build/build_node/Platform/Windows/install_android.ps1 @@ -10,16 +10,16 @@ choco install -y android-sdk $Env:ANDROID_HOME = "C:\Android\android-sdk" setx ANDROID_HOME "C:\Android\android-sdk" -$apis_packages = '"add-ons;addon-google_apis-google-19" "add-ons;addon-google_apis-google-21" "add-ons;addon-google_apis-google-22" "add-ons;addon-google_apis-google-23" "add-ons;addon-google_apis-google-24"' -$android_packages = '"platforms;android-19" "platforms;android-21" "platforms;android-22" "platforms;android-23" "platforms;android-24" "platforms;android-25" "platforms;android-26" "platforms;android-27" "platforms;android-28" "platforms;android-29" "platforms;android-30"' +$android_packages = '"platforms;android-28" "platforms;android-29" "platforms;android-30"' $googleplay_packages = '"extras;google;market_apk_expansion" "extras;google;market_licensing"' $build_tools = '"build-tools;30.0.2" "tools"' +$ndk = '"ndk;21.4.7075529"' $sdkmanager = "C:\Android\android-sdk\tools\bin\sdkmanager.bat" -Start-Process -FilePath $sdkmanager -ArgumentList $apis_packages -NoNewWindow -Wait Start-Process -FilePath $sdkmanager -ArgumentList $android_packages -NoNewWindow -Wait Start-Process -FilePath $sdkmanager -ArgumentList $googleplay_packages -NoNewWindow -Wait Start-Process -FilePath $sdkmanager -ArgumentList $build_tools -NoNewWindow -Wait +Start-Process -FilePath $sdkmanager -ArgumentList $ndk -NoNewWindow -Wait Write-Host "Installing Gradle and Ninja" Import-Module C:\ProgramData\chocolatey\helpers\chocolateyInstaller.psm1 #Grade needs a custom installer due to being hardcoded to C:\Programdata in Chocolatey diff --git a/scripts/o3de/tests/unit_test_manifest.py b/scripts/o3de/tests/unit_test_manifest.py index c7d7a7573b..c8eb916bf3 100644 --- a/scripts/o3de/tests/unit_test_manifest.py +++ b/scripts/o3de/tests/unit_test_manifest.py @@ -29,10 +29,17 @@ class TestGetTemplatesForCreation: def get_project_templates() -> list: return [] + @staticmethod + def get_gem_templates() -> list: + return [] + @staticmethod def get_engine_templates() -> list: return [pathlib.Path('D:/o3de/Templates/DefaultProject'), pathlib.Path('D:/o3de/Templates/DefaultGem')] + @staticmethod + def get_all_gems(project_path: pathlib.Path = None) -> list: + return [] @pytest.mark.parametrize("expected_template_paths", [ pytest.param([]) @@ -50,17 +57,25 @@ class TestGetTemplatesForCreation: as get_manifest_templates_patch, \ patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates)\ as get_project_templates_patch, \ + patch('o3de.manifest.get_gem_templates', side_effect=self.get_gem_templates) \ + as get_gem_templates_patch, \ patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates)\ as get_engine_templates_patch, \ + patch('o3de.manifest.get_all_gems', side_effect=self.get_all_gems) \ + as get_all_gems_patch, \ patch('o3de.validation.valid_o3de_template_json', return_value=True) \ as validate_template_json,\ patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) \ as validate_project_json,\ patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) \ - as validate_gem_json: + as validate_gem_json,\ + patch('o3de.manifest.load_o3de_manifest') as load_o3de_manifest_patch: templates = manifest.get_templates_for_generic_creation() assert templates == expected_template_paths + # make sure the o3de manifest isn't attempted to be loaded + load_o3de_manifest_patch.assert_not_called() + @pytest.mark.parametrize("expected_template_paths", [ pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject')]) @@ -78,17 +93,25 @@ class TestGetTemplatesForCreation: as get_manifest_templates_patch, \ patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ as get_project_templates_patch, \ + patch('o3de.manifest.get_gem_templates', side_effect=self.get_gem_templates) \ + as get_gem_templates_patch, \ patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ as get_engine_templates_patch, \ + patch('o3de.manifest.get_all_gems', side_effect=self.get_all_gems) \ + as get_all_gems_patch, \ patch('o3de.validation.valid_o3de_template_json', return_value=True) \ as validate_template_json, \ patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) \ as validate_project_json, \ patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) \ - as validate_gem_json: + as validate_gem_json,\ + patch('o3de.manifest.load_o3de_manifest') as load_o3de_manifest_patch: templates = manifest.get_templates_for_project_creation() assert templates == expected_template_paths + # make sure the o3de manifest isn't attempted to be loaded + load_o3de_manifest_patch.assert_not_called() + @pytest.mark.parametrize("expected_template_paths", [ pytest.param([pathlib.Path('D:/o3de/Templates/DefaultGem')]) @@ -106,13 +129,21 @@ class TestGetTemplatesForCreation: as get_manifest_templates_patch, \ patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ as get_project_templates_patch, \ + patch('o3de.manifest.get_gem_templates', side_effect=self.get_gem_templates) \ + as get_gem_templates_patch, \ patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ as get_engine_templates_patch, \ + patch('o3de.manifest.get_all_gems', side_effect=self.get_all_gems) \ + as get_all_gems_patch, \ patch('o3de.validation.valid_o3de_template_json', return_value=True) \ as validate_template_json, \ patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) \ as validate_project_json, \ patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) \ - as validate_gem_json: + as validate_gem_json, \ + patch('o3de.manifest.load_o3de_manifest') as load_o3de_manifest_patch: templates = manifest.get_templates_for_gem_creation() - assert templates == expected_template_paths \ No newline at end of file + assert templates == expected_template_paths + + # make sure the o3de manifest isn't attempted to be loaded + load_o3de_manifest_patch.assert_not_called()