Merge branch 'development' of https://github.com/o3de/o3de into jckand/EditorPrefabTests

Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com>
This commit is contained in:
jckand-amzn
2022-01-05 13:00:24 -06:00
102 changed files with 3443 additions and 3742 deletions
@@ -0,0 +1,705 @@
{
"ContainerEntity": {
"Id": "ContainerEntity",
"Name": "Hermanubis",
"Components": {
"Component_[10182366347512475253]": {
"$type": "EditorPrefabComponent",
"Id": 10182366347512475253
},
"Component_[12917798267488243668]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12917798267488243668
},
"Component_[3261249813163778338]": {
"$type": "EditorOnlyEntityComponent",
"Id": 3261249813163778338
},
"Component_[3837204912784440039]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 3837204912784440039
},
"Component_[4272963378099646759]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 4272963378099646759,
"Parent Entity": ""
},
"Component_[4848458548047175816]": {
"$type": "EditorVisibilityComponent",
"Id": 4848458548047175816
},
"Component_[5787060997243919943]": {
"$type": "EditorInspectorComponent",
"Id": 5787060997243919943
},
"Component_[7804170251266531779]": {
"$type": "EditorLockComponent",
"Id": 7804170251266531779
},
"Component_[7874177159288365422]": {
"$type": "EditorEntitySortComponent",
"Id": 7874177159288365422
},
"Component_[8018146290632383969]": {
"$type": "EditorEntityIconComponent",
"Id": 8018146290632383969
},
"Component_[8452360690590857075]": {
"$type": "SelectionComponent",
"Id": 8452360690590857075
}
}
},
"Entities": {
"Entity_[250006174949]": {
"Id": "Entity_[250006174949]",
"Name": "WorldOrigin",
"Components": {
"Component_[13379444112629774116]": {
"$type": "EditorEntityIconComponent",
"Id": 13379444112629774116
},
"Component_[13797113876161133062]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 13797113876161133062,
"Parent Entity": "ContainerEntity"
},
"Component_[16382506042739704306]": {
"$type": "EditorInspectorComponent",
"Id": 16382506042739704306,
"ComponentOrderEntryArray": [
{
"ComponentId": 13797113876161133062
},
{
"ComponentId": 8816319458242680670,
"SortIndex": 1
}
]
},
"Component_[2147729086581105478]": {
"$type": "EditorOnlyEntityComponent",
"Id": 2147729086581105478
},
"Component_[2433100672102773575]": {
"$type": "SelectionComponent",
"Id": 2433100672102773575
},
"Component_[4832829387489613630]": {
"$type": "EditorVisibilityComponent",
"Id": 4832829387489613630
},
"Component_[5585931842723227683]": {
"$type": "EditorEntitySortComponent",
"Id": 5585931842723227683,
"Child Entity Order": [
"Entity_[254301142245]"
]
},
"Component_[7088004383223117498]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 7088004383223117498
},
"Component_[7856264459806503732]": {
"$type": "EditorPendingCompositionComponent",
"Id": 7856264459806503732
},
"Component_[8816319458242680670]": {
"$type": "AZ::Render::EditorGridComponent",
"Id": 8816319458242680670
},
"Component_[930042309700959235]": {
"$type": "EditorLockComponent",
"Id": 930042309700959235
}
}
},
"Entity_[254301142245]": {
"Id": "Entity_[254301142245]",
"Name": "GlobalSkylight",
"Components": {
"Component_[10076500561520682485]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 10076500561520682485,
"Parent Entity": "Entity_[250006174949]"
},
"Component_[12626877995248630950]": {
"$type": "EditorInspectorComponent",
"Id": 12626877995248630950,
"ComponentOrderEntryArray": [
{
"ComponentId": 10076500561520682485
},
{
"ComponentId": 8158442301445120126,
"SortIndex": 1
},
{
"ComponentId": 7260006984216245935,
"SortIndex": 2
}
]
},
"Component_[13040837632921717329]": {
"$type": "SelectionComponent",
"Id": 13040837632921717329
},
"Component_[1390505494369101864]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 1390505494369101864
},
"Component_[6733278858932131836]": {
"$type": "EditorVisibilityComponent",
"Id": 6733278858932131836
},
"Component_[7260006984216245935]": {
"$type": "AZ::Render::EditorImageBasedLightComponent",
"Id": 7260006984216245935,
"Controller": {
"Configuration": {
"diffuseImageAsset": {
"assetId": {
"guid": "{3B78EA69-7CF0-56A7-A49A-110B88412666}",
"subId": 3000
},
"assetHint": "lightingpresets/greenwich_park_02_4k_iblskyboxcm_ibldiffuse.exr.streamingimage"
},
"specularImageAsset": {
"assetId": {
"guid": "{3B78EA69-7CF0-56A7-A49A-110B88412666}",
"subId": 2000
},
"assetHint": "lightingpresets/greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage"
}
}
}
},
"Component_[7944006745008331817]": {
"$type": "EditorEntitySortComponent",
"Id": 7944006745008331817
},
"Component_[8158442301445120126]": {
"$type": "AZ::Render::EditorHDRiSkyboxComponent",
"Id": 8158442301445120126,
"Controller": {
"Configuration": {
"CubemapAsset": {
"assetId": {
"guid": "{3B78EA69-7CF0-56A7-A49A-110B88412666}",
"subId": 1000
},
"assetHint": "lightingpresets/greenwich_park_02_4k_iblskyboxcm.exr.streamingimage"
}
}
}
},
"Component_[8255370213772594097]": {
"$type": "EditorOnlyEntityComponent",
"Id": 8255370213772594097
},
"Component_[8551180373364097938]": {
"$type": "EditorLockComponent",
"Id": 8551180373364097938
},
"Component_[8852330656608249928]": {
"$type": "EditorPendingCompositionComponent",
"Id": 8852330656608249928
},
"Component_[8913694496991926693]": {
"$type": "EditorEntityIconComponent",
"Id": 8913694496991926693
}
}
},
"Entity_[258596109541]": {
"Id": "Entity_[258596109541]",
"Name": "Hermanubis_stone",
"Components": {
"Component_[1026780512255775175]": {
"$type": "EditorEntityIconComponent",
"Id": 1026780512255775175
},
"Component_[10882452951986489612]": {
"$type": "SelectionComponent",
"Id": 10882452951986489612
},
"Component_[12454042755417175050]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12454042755417175050,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{1F650917-AA74-5107-9C49-648C957B33DA}",
"subId": 275904906
},
"assetHint": "materialeditor/viewportmodels/hermanubis.azmodel"
},
"LodOverride": 255
}
}
},
"Component_[13691807045809495479]": {
"$type": "EditorMaterialComponent",
"Id": 13691807045809495479,
"Controller": {
"Configuration": {
"materials": {
"{}": {
"MaterialAsset": {
"assetId": {
"guid": "{FF6412B6-F86E-54C8-835C-04F08190D81B}"
},
"assetHint": "objects/hermanubis/hermanubis_stone.azmaterial"
}
}
}
}
},
"materialSlotsByLodEnabled": true
},
"Component_[14490373655742304057]": {
"$type": "EditorPendingCompositionComponent",
"Id": 14490373655742304057
},
"Component_[15248132570755287431]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 15248132570755287431
},
"Component_[16950375358457415777]": {
"$type": "EditorVisibilityComponent",
"Id": 16950375358457415777
},
"Component_[17852268252813268496]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 17852268252813268496,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
1.1189539432525635,
0.0,
0.0
]
}
},
"Component_[3867610358542973898]": {
"$type": "EditorEntitySortComponent",
"Id": 3867610358542973898
},
"Component_[7717372065847089412]": {
"$type": "EditorOnlyEntityComponent",
"Id": 7717372065847089412
},
"Component_[7783943040473764331]": {
"$type": "EditorInspectorComponent",
"Id": 7783943040473764331,
"ComponentOrderEntryArray": [
{
"ComponentId": 17852268252813268496
},
{
"ComponentId": 12454042755417175050,
"SortIndex": 1
},
{
"ComponentId": 13691807045809495479,
"SortIndex": 2
}
]
},
"Component_[833407121913837256]": {
"$type": "EditorLockComponent",
"Id": 833407121913837256
}
}
},
"Entity_[262891076837]": {
"Id": "Entity_[262891076837]",
"Name": "Hermanubis_brass",
"Components": {
"Component_[1026780512255775175]": {
"$type": "EditorEntityIconComponent",
"Id": 1026780512255775175
},
"Component_[10882452951986489612]": {
"$type": "SelectionComponent",
"Id": 10882452951986489612
},
"Component_[12454042755417175050]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12454042755417175050,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{1F650917-AA74-5107-9C49-648C957B33DA}",
"subId": 275904906
},
"assetHint": "materialeditor/viewportmodels/hermanubis.azmodel"
},
"LodOverride": 255
}
}
},
"Component_[13691807045809495479]": {
"$type": "EditorMaterialComponent",
"Id": 13691807045809495479,
"Controller": {
"Configuration": {
"materials": {
"{}": {
"MaterialAsset": {
"assetId": {
"guid": "{B3AC2305-1DE6-54AA-AAD5-5E77C75E5BB5}"
},
"assetHint": "objects/hermanubis/hermanubis_brass.azmaterial"
}
}
}
}
},
"materialSlotsByLodEnabled": true
},
"Component_[14490373655742304057]": {
"$type": "EditorPendingCompositionComponent",
"Id": 14490373655742304057
},
"Component_[15248132570755287431]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 15248132570755287431
},
"Component_[16950375358457415777]": {
"$type": "EditorVisibilityComponent",
"Id": 16950375358457415777
},
"Component_[17852268252813268496]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 17852268252813268496,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-1.4824472665786743,
-0.034557100385427475,
0.0
]
}
},
"Component_[3867610358542973898]": {
"$type": "EditorEntitySortComponent",
"Id": 3867610358542973898
},
"Component_[7717372065847089412]": {
"$type": "EditorOnlyEntityComponent",
"Id": 7717372065847089412
},
"Component_[7783943040473764331]": {
"$type": "EditorInspectorComponent",
"Id": 7783943040473764331,
"ComponentOrderEntryArray": [
{
"ComponentId": 17852268252813268496
},
{
"ComponentId": 12454042755417175050,
"SortIndex": 1
},
{
"ComponentId": 13691807045809495479,
"SortIndex": 2
}
]
},
"Component_[833407121913837256]": {
"$type": "EditorLockComponent",
"Id": 833407121913837256
}
}
},
"Entity_[267186044133]": {
"Id": "Entity_[267186044133]",
"Name": "SphereLight",
"Components": {
"Component_[10922228943444131599]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 10922228943444131599
},
"Component_[11625534306113165068]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 11625534306113165068,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
0.2636711895465851,
2.2845842838287354,
0.22468790411949158
]
}
},
"Component_[12372418243816154216]": {
"$type": "EditorSphereShapeComponent",
"Id": 12372418243816154216,
"ShapeColor": [
0.3289234936237335,
0.7307698130607605,
0.14859239757061005,
1.0
]
},
"Component_[12579170654872581897]": {
"$type": "EditorLockComponent",
"Id": 12579170654872581897
},
"Component_[12844637542561882557]": {
"$type": "EditorOnlyEntityComponent",
"Id": 12844637542561882557
},
"Component_[13087890528096920855]": {
"$type": "EditorInspectorComponent",
"Id": 13087890528096920855,
"ComponentOrderEntryArray": [
{
"ComponentId": 11625534306113165068
},
{
"ComponentId": 13427905514841050195,
"SortIndex": 1
},
{
"ComponentId": 12372418243816154216,
"SortIndex": 2
}
]
},
"Component_[13427905514841050195]": {
"$type": "AZ::Render::EditorAreaLightComponent",
"Id": 13427905514841050195,
"Controller": {
"Configuration": {
"LightType": 1,
"Color": [
0.3289234936237335,
0.7307698130607605,
0.14859239757061005
],
"IntensityMode": 1,
"Intensity": 676.7677001953125,
"AttenuationRadius": 226.51287841796875
}
}
},
"Component_[15364092815744365073]": {
"$type": "SelectionComponent",
"Id": 15364092815744365073
},
"Component_[2481373975540551564]": {
"$type": "EditorPendingCompositionComponent",
"Id": 2481373975540551564
},
"Component_[4101167782224846352]": {
"$type": "EditorEntityIconComponent",
"Id": 4101167782224846352
},
"Component_[8664715119660216219]": {
"$type": "EditorEntitySortComponent",
"Id": 8664715119660216219
},
"Component_[8952093761729701957]": {
"$type": "EditorVisibilityComponent",
"Id": 8952093761729701957,
"VisibilityFlag": false
}
}
},
"Entity_[275775978725]": {
"Id": "Entity_[275775978725]",
"Name": "TubeLight",
"Components": {
"Component_[10922228943444131599]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 10922228943444131599,
"DisabledComponents": [
{
"$type": "EditorSphereShapeComponent",
"Id": 12372418243816154216,
"ShapeColor": [
0.3289234936237335,
0.7307698130607605,
0.14859239757061005,
1.0
]
}
]
},
"Component_[11625534306113165068]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 11625534306113165068,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-4.275930881500244,
0.5104026794433594,
2.3807857036590576
],
"Rotate": [
270.0043029785156,
0.16617189347743988,
268.51611328125
]
}
},
"Component_[12579170654872581897]": {
"$type": "EditorLockComponent",
"Id": 12579170654872581897
},
"Component_[12844637542561882557]": {
"$type": "EditorOnlyEntityComponent",
"Id": 12844637542561882557
},
"Component_[13087890528096920855]": {
"$type": "EditorInspectorComponent",
"Id": 13087890528096920855,
"ComponentOrderEntryArray": [
{
"ComponentId": 11625534306113165068
},
{
"ComponentId": 13427905514841050195,
"SortIndex": 1
},
{
"ComponentId": 12372418243816154216,
"SortIndex": 2
},
{
"ComponentId": 2193911499802409037,
"SortIndex": 3
}
]
},
"Component_[13427905514841050195]": {
"$type": "AZ::Render::EditorAreaLightComponent",
"Id": 13427905514841050195,
"Controller": {
"Configuration": {
"LightType": 3,
"Color": [
0.8521705865859985,
0.7865872979164124,
0.6079347133636475
],
"IntensityMode": 1,
"Intensity": 10000.0,
"AttenuationRadius": 21608.193359375
}
}
},
"Component_[15364092815744365073]": {
"$type": "SelectionComponent",
"Id": 15364092815744365073
},
"Component_[2193911499802409037]": {
"$type": "EditorCapsuleShapeComponent",
"Id": 2193911499802409037,
"ShapeColor": [
0.8521705865859985,
0.7865872979164124,
0.6079347133636475,
1.0
],
"CapsuleShape": {
"Configuration": {
"Height": 5.0,
"Radius": 0.10000000149011612
}
}
},
"Component_[2481373975540551564]": {
"$type": "EditorPendingCompositionComponent",
"Id": 2481373975540551564
},
"Component_[4101167782224846352]": {
"$type": "EditorEntityIconComponent",
"Id": 4101167782224846352
},
"Component_[8664715119660216219]": {
"$type": "EditorEntitySortComponent",
"Id": 8664715119660216219
},
"Component_[8952093761729701957]": {
"$type": "EditorVisibilityComponent",
"Id": 8952093761729701957,
"VisibilityFlag": false
}
}
},
"Entity_[482743502241]": {
"Id": "Entity_[482743502241]",
"Name": "Camera1",
"Components": {
"Component_[10672707967016183310]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 10672707967016183310
},
"Component_[13520081755303040361]": {
"$type": "EditorLockComponent",
"Id": 13520081755303040361
},
"Component_[13650522584195762912]": {
"$type": "SelectionComponent",
"Id": 13650522584195762912
},
"Component_[14204465933176839167]": {
"$type": "EditorOnlyEntityComponent",
"Id": 14204465933176839167
},
"Component_[14509697511269710983]": {
"$type": "EditorEntitySortComponent",
"Id": 14509697511269710983
},
"Component_[271930369355383880]": {
"$type": "EditorEntityIconComponent",
"Id": 271930369355383880
},
"Component_[5015186380056948439]": {
"$type": "EditorInspectorComponent",
"Id": 5015186380056948439
},
"Component_[6297637832938894772]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 6297637832938894772,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-0.0018702250672504306,
2.9982283115386963,
3.0017592906951904
],
"Rotate": [
20.080352783203125,
-0.020488755777478218,
179.92381286621094
]
}
},
"Component_[6611378759823339947]": {
"$type": "EditorPendingCompositionComponent",
"Id": 6611378759823339947
},
"Component_[8475839846509409509]": {
"$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent",
"Id": 8475839846509409509,
"Controller": {
"Configuration": {
"Field of View": 90.00020599365234,
"EditorEntityId": 478448534945
}
}
},
"Component_[9659542522325095386]": {
"$type": "EditorVisibilityComponent",
"Id": 9659542522325095386
}
}
}
}
}
@@ -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
@@ -0,0 +1,943 @@
{
"ContainerEntity": {
"Id": "ContainerEntity",
"Name": "hermanubis_high",
"Components": {
"Component_[10182366347512475253]": {
"$type": "EditorPrefabComponent",
"Id": 10182366347512475253
},
"Component_[12917798267488243668]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12917798267488243668
},
"Component_[3261249813163778338]": {
"$type": "EditorOnlyEntityComponent",
"Id": 3261249813163778338
},
"Component_[3837204912784440039]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 3837204912784440039
},
"Component_[4272963378099646759]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 4272963378099646759,
"Parent Entity": ""
},
"Component_[4848458548047175816]": {
"$type": "EditorVisibilityComponent",
"Id": 4848458548047175816
},
"Component_[5787060997243919943]": {
"$type": "EditorInspectorComponent",
"Id": 5787060997243919943
},
"Component_[7804170251266531779]": {
"$type": "EditorLockComponent",
"Id": 7804170251266531779
},
"Component_[7874177159288365422]": {
"$type": "EditorEntitySortComponent",
"Id": 7874177159288365422,
"Child Entity Order": [
"Entity_[243647107259]",
"Entity_[247179151093]",
"Entity_[262891076837]",
"Entity_[242884183797]",
"Entity_[258596109541]",
"Entity_[267186044133]",
"Entity_[275775978725]",
"Entity_[250006174949]"
]
},
"Component_[8018146290632383969]": {
"$type": "EditorEntityIconComponent",
"Id": 8018146290632383969
},
"Component_[8452360690590857075]": {
"$type": "SelectionComponent",
"Id": 8452360690590857075
}
}
},
"Entities": {
"Entity_[242884183797]": {
"Id": "Entity_[242884183797]",
"Name": "Hermanubis_stone",
"Components": {
"Component_[1026780512255775175]": {
"$type": "EditorEntityIconComponent",
"Id": 1026780512255775175
},
"Component_[10882452951986489612]": {
"$type": "SelectionComponent",
"Id": 10882452951986489612
},
"Component_[12454042755417175050]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12454042755417175050,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{35A45F31-F1C1-5076-8B51-FF599E2EEBAA}",
"subId": 274433667
},
"assetHint": "objects/hermanubis/hermanubis_high.azmodel"
},
"LodOverride": 255
}
}
},
"Component_[13691807045809495479]": {
"$type": "EditorMaterialComponent",
"Id": 13691807045809495479,
"Controller": {
"Configuration": {
"materials": {
"{}": {
"MaterialAsset": {
"assetId": {
"guid": "{A78EA2FC-688B-5E3A-A7F6-DB413D11D4CF}"
},
"assetHint": "materials/presets/pbr/metal_chrome_matte.azmaterial"
}
}
}
}
},
"materialSlotsByLodEnabled": true
},
"Component_[14490373655742304057]": {
"$type": "EditorPendingCompositionComponent",
"Id": 14490373655742304057
},
"Component_[15248132570755287431]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 15248132570755287431
},
"Component_[16950375358457415777]": {
"$type": "EditorVisibilityComponent",
"Id": 16950375358457415777
},
"Component_[17852268252813268496]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 17852268252813268496,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
3.810185432434082,
0.0,
0.0
]
}
},
"Component_[3867610358542973898]": {
"$type": "EditorEntitySortComponent",
"Id": 3867610358542973898
},
"Component_[7717372065847089412]": {
"$type": "EditorOnlyEntityComponent",
"Id": 7717372065847089412
},
"Component_[7783943040473764331]": {
"$type": "EditorInspectorComponent",
"Id": 7783943040473764331,
"ComponentOrderEntryArray": [
{
"ComponentId": 17852268252813268496
},
{
"ComponentId": 12454042755417175050,
"SortIndex": 1
},
{
"ComponentId": 13691807045809495479,
"SortIndex": 2
}
]
},
"Component_[833407121913837256]": {
"$type": "EditorLockComponent",
"Id": 833407121913837256
}
}
},
"Entity_[243647107259]": {
"Id": "Entity_[243647107259]",
"Name": "Camera1",
"Components": {
"Component_[11276153162797125616]": {
"$type": "GenericComponentWrapper",
"Id": 11276153162797125616,
"m_template": {
"$type": "FlyCameraInputComponent"
}
},
"Component_[11484120648160206262]": {
"$type": "EditorLockComponent",
"Id": 11484120648160206262
},
"Component_[14251459960897306807]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 14251459960897306807,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-0.10533800721168518,
-4.001697063446045,
3.061025619506836
],
"Rotate": [
-19.998117446899414,
0.01881762035191059,
-0.051706261932849884
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
"Component_[149351061984148634]": {
"$type": "EditorOnlyEntityComponent",
"Id": 149351061984148634
},
"Component_[15121925351155689107]": {
"$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent",
"Id": 15121925351155689107,
"Controller": {
"Configuration": {
"EditorEntityId": 243647107259
}
}
},
"Component_[15327903729148812780]": {
"$type": "EditorPendingCompositionComponent",
"Id": 15327903729148812780
},
"Component_[17667820301809320373]": {
"$type": "EditorEntityIconComponent",
"Id": 17667820301809320373
},
"Component_[17708351813187009272]": {
"$type": "EditorVisibilityComponent",
"Id": 17708351813187009272
},
"Component_[17941668830905411554]": {
"$type": "SelectionComponent",
"Id": 17941668830905411554
},
"Component_[48451466091772435]": {
"$type": "EditorEntitySortComponent",
"Id": 48451466091772435
},
"Component_[6163614082436403601]": {
"$type": "EditorInspectorComponent",
"Id": 6163614082436403601,
"ComponentOrderEntryArray": [
{
"ComponentId": 14251459960897306807
},
{
"ComponentId": 15121925351155689107,
"SortIndex": 1
},
{
"ComponentId": 11935019334576395684,
"SortIndex": 2
}
]
},
"Component_[8660334631968180943]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 8660334631968180943
}
}
},
"Entity_[247179151093]": {
"Id": "Entity_[247179151093]",
"Name": "Hermanubis_brass",
"Components": {
"Component_[1026780512255775175]": {
"$type": "EditorEntityIconComponent",
"Id": 1026780512255775175
},
"Component_[10882452951986489612]": {
"$type": "SelectionComponent",
"Id": 10882452951986489612
},
"Component_[12454042755417175050]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12454042755417175050,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{35A45F31-F1C1-5076-8B51-FF599E2EEBAA}",
"subId": 274433667
},
"assetHint": "objects/hermanubis/hermanubis_high.azmodel"
},
"LodOverride": 255
}
}
},
"Component_[13691807045809495479]": {
"$type": "EditorMaterialComponent",
"Id": 13691807045809495479,
"Controller": {
"Configuration": {
"materials": {
"{}": {
"MaterialAsset": {
"assetId": {
"guid": "{80E0F2A7-1EE4-597F-80EF-985C65BCE2EB}"
},
"assetHint": "materials/presets/pbr/metal_brass.azmaterial"
}
}
}
}
},
"materialSlotsByLodEnabled": true
},
"Component_[14490373655742304057]": {
"$type": "EditorPendingCompositionComponent",
"Id": 14490373655742304057
},
"Component_[15248132570755287431]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 15248132570755287431
},
"Component_[16950375358457415777]": {
"$type": "EditorVisibilityComponent",
"Id": 16950375358457415777
},
"Component_[17852268252813268496]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 17852268252813268496,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-4.019397258758545,
-0.034557100385427475,
0.0
]
}
},
"Component_[3867610358542973898]": {
"$type": "EditorEntitySortComponent",
"Id": 3867610358542973898
},
"Component_[7717372065847089412]": {
"$type": "EditorOnlyEntityComponent",
"Id": 7717372065847089412
},
"Component_[7783943040473764331]": {
"$type": "EditorInspectorComponent",
"Id": 7783943040473764331,
"ComponentOrderEntryArray": [
{
"ComponentId": 17852268252813268496
},
{
"ComponentId": 12454042755417175050,
"SortIndex": 1
},
{
"ComponentId": 13691807045809495479,
"SortIndex": 2
}
]
},
"Component_[833407121913837256]": {
"$type": "EditorLockComponent",
"Id": 833407121913837256
}
}
},
"Entity_[250006174949]": {
"Id": "Entity_[250006174949]",
"Name": "WorldOrigin",
"Components": {
"Component_[13379444112629774116]": {
"$type": "EditorEntityIconComponent",
"Id": 13379444112629774116
},
"Component_[13797113876161133062]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 13797113876161133062,
"Parent Entity": "ContainerEntity"
},
"Component_[16382506042739704306]": {
"$type": "EditorInspectorComponent",
"Id": 16382506042739704306,
"ComponentOrderEntryArray": [
{
"ComponentId": 13797113876161133062
},
{
"ComponentId": 8816319458242680670,
"SortIndex": 1
}
]
},
"Component_[2147729086581105478]": {
"$type": "EditorOnlyEntityComponent",
"Id": 2147729086581105478
},
"Component_[2433100672102773575]": {
"$type": "SelectionComponent",
"Id": 2433100672102773575
},
"Component_[4832829387489613630]": {
"$type": "EditorVisibilityComponent",
"Id": 4832829387489613630
},
"Component_[5585931842723227683]": {
"$type": "EditorEntitySortComponent",
"Id": 5585931842723227683,
"Child Entity Order": [
"Entity_[254301142245]"
]
},
"Component_[7088004383223117498]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 7088004383223117498
},
"Component_[7856264459806503732]": {
"$type": "EditorPendingCompositionComponent",
"Id": 7856264459806503732
},
"Component_[8816319458242680670]": {
"$type": "AZ::Render::EditorGridComponent",
"Id": 8816319458242680670
},
"Component_[930042309700959235]": {
"$type": "EditorLockComponent",
"Id": 930042309700959235
}
}
},
"Entity_[254301142245]": {
"Id": "Entity_[254301142245]",
"Name": "GlobalSkylight",
"Components": {
"Component_[10076500561520682485]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 10076500561520682485,
"Parent Entity": "Entity_[250006174949]"
},
"Component_[12626877995248630950]": {
"$type": "EditorInspectorComponent",
"Id": 12626877995248630950,
"ComponentOrderEntryArray": [
{
"ComponentId": 10076500561520682485
},
{
"ComponentId": 8158442301445120126,
"SortIndex": 1
},
{
"ComponentId": 7260006984216245935,
"SortIndex": 2
}
]
},
"Component_[13040837632921717329]": {
"$type": "SelectionComponent",
"Id": 13040837632921717329
},
"Component_[1390505494369101864]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 1390505494369101864
},
"Component_[6733278858932131836]": {
"$type": "EditorVisibilityComponent",
"Id": 6733278858932131836
},
"Component_[7260006984216245935]": {
"$type": "AZ::Render::EditorImageBasedLightComponent",
"Id": 7260006984216245935,
"Controller": {
"Configuration": {
"diffuseImageAsset": {
"assetId": {
"guid": "{3B78EA69-7CF0-56A7-A49A-110B88412666}",
"subId": 3000
},
"assetHint": "lightingpresets/greenwich_park_02_4k_iblskyboxcm_ibldiffuse.exr.streamingimage"
},
"specularImageAsset": {
"assetId": {
"guid": "{3B78EA69-7CF0-56A7-A49A-110B88412666}",
"subId": 2000
},
"assetHint": "lightingpresets/greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage"
}
}
}
},
"Component_[7944006745008331817]": {
"$type": "EditorEntitySortComponent",
"Id": 7944006745008331817
},
"Component_[8158442301445120126]": {
"$type": "AZ::Render::EditorHDRiSkyboxComponent",
"Id": 8158442301445120126,
"Controller": {
"Configuration": {
"CubemapAsset": {
"assetId": {
"guid": "{3B78EA69-7CF0-56A7-A49A-110B88412666}",
"subId": 1000
},
"assetHint": "lightingpresets/greenwich_park_02_4k_iblskyboxcm.exr.streamingimage"
}
}
}
},
"Component_[8255370213772594097]": {
"$type": "EditorOnlyEntityComponent",
"Id": 8255370213772594097
},
"Component_[8551180373364097938]": {
"$type": "EditorLockComponent",
"Id": 8551180373364097938
},
"Component_[8852330656608249928]": {
"$type": "EditorPendingCompositionComponent",
"Id": 8852330656608249928
},
"Component_[8913694496991926693]": {
"$type": "EditorEntityIconComponent",
"Id": 8913694496991926693
}
}
},
"Entity_[258596109541]": {
"Id": "Entity_[258596109541]",
"Name": "Hermanubis_stone",
"Components": {
"Component_[1026780512255775175]": {
"$type": "EditorEntityIconComponent",
"Id": 1026780512255775175
},
"Component_[10882452951986489612]": {
"$type": "SelectionComponent",
"Id": 10882452951986489612
},
"Component_[12454042755417175050]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12454042755417175050,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{35A45F31-F1C1-5076-8B51-FF599E2EEBAA}",
"subId": 274433667
},
"assetHint": "objects/hermanubis/hermanubis_high.azmodel"
},
"LodOverride": 255
}
}
},
"Component_[13691807045809495479]": {
"$type": "EditorMaterialComponent",
"Id": 13691807045809495479,
"Controller": {
"Configuration": {
"materials": {
"{}": {
"MaterialAsset": {
"assetId": {
"guid": "{B7C712DC-4839-58BB-B522-A4F120084CF5}"
},
"assetHint": "materials/presets/pbr/metal_chrome.azmaterial"
}
}
}
}
},
"materialSlotsByLodEnabled": true
},
"Component_[14490373655742304057]": {
"$type": "EditorPendingCompositionComponent",
"Id": 14490373655742304057
},
"Component_[15248132570755287431]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 15248132570755287431
},
"Component_[16950375358457415777]": {
"$type": "EditorVisibilityComponent",
"Id": 16950375358457415777
},
"Component_[17852268252813268496]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 17852268252813268496,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
1.1189539432525635,
0.0,
0.0
]
}
},
"Component_[3867610358542973898]": {
"$type": "EditorEntitySortComponent",
"Id": 3867610358542973898
},
"Component_[7717372065847089412]": {
"$type": "EditorOnlyEntityComponent",
"Id": 7717372065847089412
},
"Component_[7783943040473764331]": {
"$type": "EditorInspectorComponent",
"Id": 7783943040473764331,
"ComponentOrderEntryArray": [
{
"ComponentId": 17852268252813268496
},
{
"ComponentId": 12454042755417175050,
"SortIndex": 1
},
{
"ComponentId": 13691807045809495479,
"SortIndex": 2
}
]
},
"Component_[833407121913837256]": {
"$type": "EditorLockComponent",
"Id": 833407121913837256
}
}
},
"Entity_[262891076837]": {
"Id": "Entity_[262891076837]",
"Name": "Hermanubis_brass",
"Components": {
"Component_[1026780512255775175]": {
"$type": "EditorEntityIconComponent",
"Id": 1026780512255775175
},
"Component_[10882452951986489612]": {
"$type": "SelectionComponent",
"Id": 10882452951986489612
},
"Component_[12454042755417175050]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12454042755417175050,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{35A45F31-F1C1-5076-8B51-FF599E2EEBAA}",
"subId": 274433667
},
"assetHint": "objects/hermanubis/hermanubis_high.azmodel"
},
"LodOverride": 255
}
}
},
"Component_[13691807045809495479]": {
"$type": "EditorMaterialComponent",
"Id": 13691807045809495479,
"Controller": {
"Configuration": {
"materials": {
"{}": {
"MaterialAsset": {
"assetId": {
"guid": "{CAF50181-3384-5DF2-9304-C6E48E83C72D}"
},
"assetHint": "materials/presets/pbr/metal_chrome_polished.azmaterial"
}
}
}
}
},
"materialSlotsByLodEnabled": true
},
"Component_[14490373655742304057]": {
"$type": "EditorPendingCompositionComponent",
"Id": 14490373655742304057
},
"Component_[15248132570755287431]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 15248132570755287431
},
"Component_[16950375358457415777]": {
"$type": "EditorVisibilityComponent",
"Id": 16950375358457415777
},
"Component_[17852268252813268496]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 17852268252813268496,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-1.4824472665786743,
-0.034557100385427475,
0.0
]
}
},
"Component_[3867610358542973898]": {
"$type": "EditorEntitySortComponent",
"Id": 3867610358542973898
},
"Component_[7717372065847089412]": {
"$type": "EditorOnlyEntityComponent",
"Id": 7717372065847089412
},
"Component_[7783943040473764331]": {
"$type": "EditorInspectorComponent",
"Id": 7783943040473764331,
"ComponentOrderEntryArray": [
{
"ComponentId": 17852268252813268496
},
{
"ComponentId": 12454042755417175050,
"SortIndex": 1
},
{
"ComponentId": 13691807045809495479,
"SortIndex": 2
}
]
},
"Component_[833407121913837256]": {
"$type": "EditorLockComponent",
"Id": 833407121913837256
}
}
},
"Entity_[267186044133]": {
"Id": "Entity_[267186044133]",
"Name": "SphereLight",
"Components": {
"Component_[10922228943444131599]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 10922228943444131599
},
"Component_[11625534306113165068]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 11625534306113165068,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
0.2636711895465851,
2.2845842838287354,
0.22468790411949158
]
}
},
"Component_[12372418243816154216]": {
"$type": "EditorSphereShapeComponent",
"Id": 12372418243816154216,
"ShapeColor": [
0.3289234936237335,
0.7307698130607605,
0.14859239757061005,
1.0
]
},
"Component_[12579170654872581897]": {
"$type": "EditorLockComponent",
"Id": 12579170654872581897
},
"Component_[12844637542561882557]": {
"$type": "EditorOnlyEntityComponent",
"Id": 12844637542561882557
},
"Component_[13087890528096920855]": {
"$type": "EditorInspectorComponent",
"Id": 13087890528096920855,
"ComponentOrderEntryArray": [
{
"ComponentId": 11625534306113165068
},
{
"ComponentId": 13427905514841050195,
"SortIndex": 1
},
{
"ComponentId": 12372418243816154216,
"SortIndex": 2
}
]
},
"Component_[13427905514841050195]": {
"$type": "AZ::Render::EditorAreaLightComponent",
"Id": 13427905514841050195,
"Controller": {
"Configuration": {
"LightType": 1,
"Color": [
0.3289234936237335,
0.7307698130607605,
0.14859239757061005
],
"IntensityMode": 1,
"Intensity": 676.7677001953125,
"AttenuationRadius": 226.51287841796875
}
}
},
"Component_[15364092815744365073]": {
"$type": "SelectionComponent",
"Id": 15364092815744365073
},
"Component_[2481373975540551564]": {
"$type": "EditorPendingCompositionComponent",
"Id": 2481373975540551564
},
"Component_[4101167782224846352]": {
"$type": "EditorEntityIconComponent",
"Id": 4101167782224846352
},
"Component_[8664715119660216219]": {
"$type": "EditorEntitySortComponent",
"Id": 8664715119660216219
},
"Component_[8952093761729701957]": {
"$type": "EditorVisibilityComponent",
"Id": 8952093761729701957,
"VisibilityFlag": false
}
}
},
"Entity_[275775978725]": {
"Id": "Entity_[275775978725]",
"Name": "TubeLight",
"Components": {
"Component_[10922228943444131599]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 10922228943444131599,
"DisabledComponents": [
{
"$type": "EditorSphereShapeComponent",
"Id": 12372418243816154216,
"ShapeColor": [
0.3289234936237335,
0.7307698130607605,
0.14859239757061005,
1.0
]
}
]
},
"Component_[11625534306113165068]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 11625534306113165068,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
-4.275930881500244,
0.5104026794433594,
2.3807857036590576
],
"Rotate": [
270.0043029785156,
0.16617189347743988,
268.51611328125
]
}
},
"Component_[12579170654872581897]": {
"$type": "EditorLockComponent",
"Id": 12579170654872581897
},
"Component_[12844637542561882557]": {
"$type": "EditorOnlyEntityComponent",
"Id": 12844637542561882557
},
"Component_[13087890528096920855]": {
"$type": "EditorInspectorComponent",
"Id": 13087890528096920855,
"ComponentOrderEntryArray": [
{
"ComponentId": 11625534306113165068
},
{
"ComponentId": 13427905514841050195,
"SortIndex": 1
},
{
"ComponentId": 12372418243816154216,
"SortIndex": 2
},
{
"ComponentId": 2193911499802409037,
"SortIndex": 3
}
]
},
"Component_[13427905514841050195]": {
"$type": "AZ::Render::EditorAreaLightComponent",
"Id": 13427905514841050195,
"Controller": {
"Configuration": {
"LightType": 3,
"Color": [
0.8521705865859985,
0.7865872979164124,
0.6079347133636475
],
"IntensityMode": 1,
"Intensity": 10000.0,
"AttenuationRadius": 21608.193359375
}
}
},
"Component_[15364092815744365073]": {
"$type": "SelectionComponent",
"Id": 15364092815744365073
},
"Component_[2193911499802409037]": {
"$type": "EditorCapsuleShapeComponent",
"Id": 2193911499802409037,
"ShapeColor": [
0.8521705865859985,
0.7865872979164124,
0.6079347133636475,
1.0
],
"CapsuleShape": {
"Configuration": {
"Height": 5.0,
"Radius": 0.10000000149011612
}
}
},
"Component_[2481373975540551564]": {
"$type": "EditorPendingCompositionComponent",
"Id": 2481373975540551564
},
"Component_[4101167782224846352]": {
"$type": "EditorEntityIconComponent",
"Id": 4101167782224846352
},
"Component_[8664715119660216219]": {
"$type": "EditorEntitySortComponent",
"Id": 8664715119660216219
},
"Component_[8952093761729701957]": {
"$type": "EditorVisibilityComponent",
"Id": 8952093761729701957,
"VisibilityFlag": false
}
}
}
}
}
@@ -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
-111
View File
@@ -1,111 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCBMFC_H
#define CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCBMFC_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QPushButton>
#include "ConsoleSCB.h"
#endif
class QMenu;
class ConsoleWidget;
class QFocusEvent;
namespace Ui {
class ConsoleMFC;
}
namespace MFC
{
struct ConsoleLine
{
QString text;
bool newLine;
};
typedef std::deque<ConsoleLine> Lines;
class ConsoleLineEdit
: public QLineEdit
{
Q_OBJECT
public:
explicit ConsoleLineEdit(QWidget* parent = nullptr);
protected:
void mousePressEvent(QMouseEvent* ev) override;
void mouseDoubleClickEvent(QMouseEvent* ev) override;
void keyPressEvent(QKeyEvent* ev) override;
bool event(QEvent* ev) override;
signals:
void variableEditorRequested();
void setWindowTitle(const QString&);
private:
void DisplayHistory(bool bForward);
QStringList m_history;
unsigned int m_historyIndex;
bool m_bReusedHistory;
};
class ConsoleTextEdit
: public QTextEdit
{
Q_OBJECT
public:
explicit ConsoleTextEdit(QWidget* parent = nullptr);
};
class CConsoleSCB
: public QWidget
{
Q_OBJECT
public:
explicit CConsoleSCB(QWidget* parent = nullptr);
~CConsoleSCB();
static void RegisterViewClass();
void SetInputFocus();
void AddToConsole(const QString& text, bool bNewLine);
void FlushText();
void showPopupAndSetTitle();
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
static CConsoleSCB* GetCreatedInstance();
static void AddToPendingLines(const QString& text, bool bNewLine); // call this function instead of AddToConsole() until an instance of CConsoleSCB exists to prevent messages from getting lost
public Q_SLOTS:
void OnStyleSettingsChanged();
private Q_SLOTS:
void showVariableEditor();
private:
QScopedPointer<Ui::ConsoleMFC> ui;
int m_richEditTextLength;
Lines m_lines;
static Lines s_pendingLines;
QList<QColor> m_colorTable;
SEditorSettings::ConsoleColorTheme m_backgroundTheme;
};
} // namespace MFC
#endif // CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCB_H
-132
View File
@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConsoleMFC</class>
<widget class="QWidget" name="Console">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Console</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="container2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>20</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="MFC::ConsoleLineEdit" name="lineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MFC::ConsoleLineEdit</class>
<extends>QLineEdit</extends>
<header>ConsoleSCBMFC.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="ConsoleSCB.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,48 +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 "EditorDefs.h"
#include "HotTrackingTreeCtrl.h"
// Qt
#include <QMouseEvent>
CHotTrackingTreeCtrl::CHotTrackingTreeCtrl(QWidget* parent)
: QTreeWidget(parent)
{
setMouseTracking(true);
m_hHoverItem = nullptr;
}
void CHotTrackingTreeCtrl::mouseMoveEvent(QMouseEvent* event)
{
QTreeWidgetItem* hItem = itemAt(event->pos());
if (m_hHoverItem != nullptr)
{
QFont font = m_hHoverItem->font(0);
font.setBold(false);
m_hHoverItem->setFont(0, font);
m_hHoverItem = nullptr;
}
if (hItem != nullptr)
{
QFont font = hItem->font(0);
font.setBold(true);
hItem->setFont(0, font);
m_hHoverItem = hItem;
}
QTreeWidget::mouseMoveEvent(event);
}
#include <Controls/moc_HotTrackingTreeCtrl.cpp>
@@ -1,33 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QTreeWidget>
#endif
class CHotTrackingTreeCtrl
: public QTreeWidget
{
Q_OBJECT
public:
CHotTrackingTreeCtrl(QWidget* parent = 0);
virtual ~CHotTrackingTreeCtrl(){};
protected:
void mouseMoveEvent(QMouseEvent* event) override;
private:
QTreeWidgetItem* m_hHoverItem;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
-567
View File
@@ -1,567 +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 "EditorDefs.h"
#include "ImageListCtrl.h"
// Qt
#include <QPainter>
#include <QScrollBar>
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::CImageListCtrl(QWidget* parent)
: QAbstractItemView(parent)
, m_itemSize(60, 60)
, m_borderSize(4, 4)
, m_style(DefaultStyle)
{
setItemDelegate(new QImageListDelegate(this));
setAutoFillBackground(false);
QPalette p = palette();
p.setColor(QPalette::Highlight, QColor(255, 55, 50));
setPalette(p);
horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setRange(0, 0);
}
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::~CImageListCtrl()
{
}
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::ListStyle CImageListCtrl::Style() const
{
return m_style;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetStyle(ListStyle style)
{
m_style = style;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
const QSize& CImageListCtrl::ItemSize() const
{
return m_itemSize;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetItemSize(QSize size)
{
Q_ASSERT(size.isValid());
m_itemSize = size;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
const QSize& CImageListCtrl::BorderSize() const
{
return m_borderSize;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetBorderSize(QSize size)
{
Q_ASSERT(size.isValid());
m_borderSize = size;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
QModelIndexList CImageListCtrl::ItemsInRect(const QRect& rect) const
{
QModelIndexList list;
if (!model())
{
return list;
}
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().intersects(rect))
{
list << model()->index(i.key(), 0, rootIndex());
}
}
return list;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::paintEvent(QPaintEvent* event)
{
QAbstractItemView::paintEvent(event);
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
if (m_geometry.isEmpty() && rowCount)
{
updateGeometries();
}
QPainter painter(viewport());
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
painter.setBackground(palette().window());
painter.setFont(font());
QStyleOptionViewItem option;
option.palette = palette();
option.font = font();
option.fontMetrics = fontMetrics();
option.decorationAlignment = Qt::AlignCenter;
const QRect visibleRect(QPoint(horizontalOffset(), verticalOffset()), viewport()->contentsRect().size());
painter.translate(-horizontalOffset(), -verticalOffset());
for (int r = 0; r < rowCount; ++r)
{
const QModelIndex& index = model()->index(r, 0, rootIndex());
option.rect = m_geometry.value(r);
if (!option.rect.intersects(visibleRect))
{
continue;
}
option.state = QStyle::State_None;
if (selectionModel()->isSelected(index))
{
option.state |= QStyle::State_Selected;
}
if (currentIndex() == index)
{
option.state |= QStyle::State_HasFocus;
}
QAbstractItemDelegate* idt = itemDelegate(index);
idt->paint(&painter, option, index);
}
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::rowsInserted(const QModelIndex& parent, int start, int end)
{
QAbstractItemView::rowsInserted(parent, start, end);
if (isVisible())
{
scheduleDelayedItemsLayout();
}
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::updateGeometries()
{
ClearItemGeometries();
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
const int nPageHorz = viewport()->width();
const int nPageVert = viewport()->height();
if (nPageHorz == 0 || nPageVert == 0 || rowCount <= 0)
{
return;
}
int x = m_borderSize.width();
int y = m_borderSize.height();
const int nItemWidth = m_itemSize.width() + m_borderSize.width();
if (m_style == HorizontalStyle)
{
for (int row = 0; row < rowCount; ++row)
{
m_geometry.insert(row, QRect(QPoint(x, y), m_itemSize));
x += nItemWidth;
}
horizontalScrollBar()->setPageStep(viewport()->width());
horizontalScrollBar()->setRange(0, x - viewport()->width());
}
else
{
const int nTextHeight = fontMetrics().height();
const int nItemHeight = m_itemSize.height() + m_borderSize.height() + nTextHeight;
int nNumOfHorzItems = nPageHorz / nItemWidth;
if (nNumOfHorzItems <= 0)
{
nNumOfHorzItems = 1;
}
for (int row = 0; row < rowCount; ++row)
{
m_geometry.insert(row, QRect(QPoint(x, y), m_itemSize));
if ((row + 1) % nNumOfHorzItems == 0)
{
y += nItemHeight;
x = m_borderSize.width();
}
else
{
x += nItemWidth;
}
}
verticalScrollBar()->setPageStep(viewport()->height());
verticalScrollBar()->setRange(0, (y + nItemHeight) - viewport()->height());
}
}
//////////////////////////////////////////////////////////////////////////
QModelIndex CImageListCtrl::indexAt(const QPoint& point) const
{
if (!model())
{
return QModelIndex();
}
const QPoint p = point +
QPoint(horizontalOffset(), verticalOffset());
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().contains(p))
{
return model()->index(i.key(), 0, rootIndex());
}
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::scrollTo(const QModelIndex& index, ScrollHint hint)
{
if (!index.isValid())
{
return;
}
QRect rect = m_geometry.value(index.row());
switch (hint)
{
case EnsureVisible:
if (horizontalOffset() > rect.right())
{
horizontalScrollBar()->setValue(rect.left());
}
else if ((horizontalOffset() + viewport()->width()) < rect.left())
{
horizontalScrollBar()->setValue(rect.right() - viewport()->width());
}
if (verticalOffset() > rect.bottom())
{
verticalScrollBar()->setValue(rect.top());
}
else if ((verticalOffset() + viewport()->height()) < rect.top())
{
verticalScrollBar()->setValue(rect.bottom() - viewport()->height());
}
break;
case PositionAtTop:
horizontalScrollBar()->setValue(rect.left());
verticalScrollBar()->setValue(rect.top());
break;
case PositionAtBottom:
horizontalScrollBar()->setValue(rect.right() - viewport()->width());
verticalScrollBar()->setValue(rect.bottom() - viewport()->height());
break;
case PositionAtCenter:
horizontalScrollBar()->setValue(rect.center().x() - (viewport()->width() / 2));
verticalScrollBar()->setValue(rect.center().y() - (viewport()->height() / 2));
break;
}
}
//////////////////////////////////////////////////////////////////////////
QRect CImageListCtrl::visualRect(const QModelIndex& index) const
{
if (!index.isValid())
{
return QRect();
}
if (!m_geometry.contains(index.row()))
{
return QRect();
}
return m_geometry.value(index.row())
.translated(-horizontalOffset(), -verticalOffset());
}
//////////////////////////////////////////////////////////////////////////
QRect CImageListCtrl::ItemGeometry(const QModelIndex& index) const
{
Q_ASSERT(index.model() == model());
Q_ASSERT(m_geometry.contains(index.row()));
return m_geometry.value(index.row());
}
void CImageListCtrl::SetItemGeometry(const QModelIndex& index, const QRect& rect)
{
Q_ASSERT(index.model() == model());
m_geometry.insert(index.row(), rect);
update(rect);
}
void CImageListCtrl::ClearItemGeometries()
{
m_geometry.clear();
}
//////////////////////////////////////////////////////////////////////////
int CImageListCtrl::horizontalOffset() const
{
return horizontalScrollBar()->value();
}
//////////////////////////////////////////////////////////////////////////
int CImageListCtrl::verticalOffset() const
{
return verticalScrollBar()->value();
}
//////////////////////////////////////////////////////////////////////////
bool CImageListCtrl::isIndexHidden([[maybe_unused]] const QModelIndex& index) const
{
return false; /* not supported */
}
//////////////////////////////////////////////////////////////////////////
QModelIndex CImageListCtrl::moveCursor(CursorAction cursorAction, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
if (!model())
{
return QModelIndex();
}
const int rowCount = model()->rowCount();
if (0 == rowCount)
{
return QModelIndex();
}
switch (cursorAction)
{
case MoveHome:
return model()->index(0, 0, rootIndex());
case MoveEnd:
return model()->index(rowCount - 1, 0, rootIndex());
case MovePrevious:
{
QModelIndex current = currentIndex();
if (current.isValid())
{
return model()->index((current.row() - 1) % rowCount, 0, rootIndex());
}
} break;
case MoveNext:
{
QModelIndex current = currentIndex();
if (current.isValid())
{
return model()->index((current.row() + 1) % rowCount, 0, rootIndex());
}
} break;
case MoveUp:
case MoveDown:
case MoveLeft:
case MoveRight:
case MovePageUp:
case MovePageDown:
/* TODO */
break;
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
{
if (!model())
{
return;
}
const QRect lrect =
rect.translated(horizontalOffset(), verticalOffset());
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().intersects(lrect))
{
selectionModel()->select(model()->index(i.key(), 0, rootIndex()), flags);
}
}
}
//////////////////////////////////////////////////////////////////////////
QRegion CImageListCtrl::visualRegionForSelection(const QItemSelection& selection) const
{
QRegion region;
foreach(const QModelIndex &index, selection.indexes())
{
region += visualRect(index);
}
return region;
}
//////////////////////////////////////////////////////////////////////////
QImageListDelegate::QImageListDelegate(QObject* parent)
: QAbstractItemDelegate(parent)
{
}
//////////////////////////////////////////////////////////////////////////
void QImageListDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option, const QModelIndex& index) const
{
painter->save();
painter->setFont(option.font);
if (option.rect.isValid())
{
painter->setClipRect(option.rect);
}
QRect innerRect = option.rect.adjusted(1, 1, -1, -1);
QRect textRect(innerRect.left(), innerRect.bottom() - option.fontMetrics.height(),
innerRect.width(), option.fontMetrics.height() + 1);
/* fill item background */
painter->fillRect(option.rect, option.palette.color(QPalette::Base));
/* draw image */
if (index.data(Qt::DecorationRole).isValid())
{
const QPixmap& p = index.data(Qt::DecorationRole).value<QPixmap>();
if (p.isNull() || p.size() == QSize(1, 1))
{
emit InvalidPixmapGenerated(index);
}
else
{
painter->drawPixmap(innerRect, p);
}
}
/* draw text */
const QColor trColor = option.palette.color(QPalette::Shadow);
painter->fillRect(textRect, (option.state & QStyle::State_Selected) ?
trColor.lighter() : trColor);
if (option.state & QStyle::State_Selected)
{
painter->setPen(QPen(option.palette.color(QPalette::HighlightedText)));
QFont f = painter->font();
f.setBold(true);
painter->setFont(f);
}
else
{
painter->setPen(QPen(option.palette.color(QPalette::Text)));
}
painter->drawText(textRect, index.data(Qt::DisplayRole).toString(),
QTextOption(option.decorationAlignment));
painter->setPen(QPen(option.palette.color(QPalette::Shadow)));
painter->drawRect(textRect);
/* draw border */
if (option.state & QStyle::State_Selected)
{
QPen pen(option.palette.color(QPalette::Highlight));
pen.setWidth(2);
painter->setPen(pen);
painter->drawRect(innerRect);
}
else
{
painter->setPen(QPen(option.palette.color(QPalette::Shadow)));
painter->drawRect(option.rect);
}
if (option.state & QStyle::State_HasFocus)
{
QPen pen(Qt::DotLine);
pen.setColor(option.palette.color(QPalette::AlternateBase));
painter->setPen(pen);
painter->drawRect(option.rect);
}
painter->restore();
}
//////////////////////////////////////////////////////////////////////////
QSize QImageListDelegate::sizeHint(const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index) const
{
return option.rect.size();
}
//////////////////////////////////////////////////////////////////////////
QVector<int> QImageListDelegate::paintingRoles() const
{
return QVector<int>() << Qt::DecorationRole << Qt::DisplayRole;
}
#include <Controls/moc_ImageListCtrl.cpp>
-97
View File
@@ -1,97 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QAbstractItemView>
#include <QHash>
#endif
//////////////////////////////////////////////////////////////////////////
// Custom control to display list of images.
//////////////////////////////////////////////////////////////////////////
class CImageListCtrl
: public QAbstractItemView
{
Q_OBJECT
public:
enum ListStyle
{
DefaultStyle,
HorizontalStyle
};
public:
CImageListCtrl(QWidget* parent = nullptr);
~CImageListCtrl();
ListStyle Style() const;
void SetStyle(ListStyle style);
const QSize& ItemSize() const;
void SetItemSize(QSize size);
const QSize& BorderSize() const;
void SetBorderSize(QSize size);
// Get all items inside specified rectangle.
QModelIndexList ItemsInRect(const QRect& rect) const;
QModelIndex indexAt(const QPoint& point) const override;
void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override;
QRect visualRect(const QModelIndex& index) const override;
protected:
QRect ItemGeometry(const QModelIndex& index) const;
void SetItemGeometry(const QModelIndex& index, const QRect& rect);
void ClearItemGeometries();
int horizontalOffset() const override;
int verticalOffset() const override;
bool isIndexHidden(const QModelIndex& index) const override;
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags) override;
QRegion visualRegionForSelection(const QItemSelection& selection) const override;
void paintEvent(QPaintEvent* event) override;
void rowsInserted(const QModelIndex& parent, int start, int end) override;
void updateGeometries() override;
private:
QHash<int, QRect> m_geometry;
QSize m_itemSize;
QSize m_borderSize;
ListStyle m_style;
};
class QImageListDelegate
: public QAbstractItemDelegate
{
Q_OBJECT
signals:
void InvalidPixmapGenerated(const QModelIndex& index) const;
public:
QImageListDelegate(QObject* parent = nullptr);
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QVector<int> paintingRoles() const override;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
-67
View File
@@ -1,67 +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 "EditorDefs.h"
#include "MultiMonHelper.h"
// Qt
#include <QScreen>
////////////////////////////////////////////////////////////////////////////
void ClipOrCenterRectToMonitor(QRect *prc, const UINT flags)
{
const QScreen* currentScreen = nullptr;
QRect rc;
Q_ASSERT(prc);
const auto screens = qApp->screens();
for (auto screen : screens)
{
if (screen->geometry().contains(prc->center()))
{
currentScreen = screen;
break;
}
}
if (!currentScreen)
{
return;
}
const int w = prc->width();
const int h = prc->height();
if (flags & MONITOR_WORKAREA)
{
rc = currentScreen->availableGeometry();
}
else
{
rc = currentScreen->geometry();
}
// center or clip the passed rect to the monitor rect
if (flags & MONITOR_CENTER)
{
prc->setLeft(rc.left() + (rc.right() - rc.left() - w) / 2);
prc->setTop(rc.top() + (rc.bottom() - rc.top() - h) / 2);
prc->setRight(prc->left() + w);
prc->setBottom(prc->top() + h);
}
else
{
prc->setLeft(qMax(rc.left(), qMin(rc.right() - w, prc->left())));
prc->setTop(qMax(rc.top(), qMin(rc.bottom() - h, prc->top())));
prc->setRight(prc->left() + w);
prc->setBottom(prc->top() + h);
}
}
-44
View File
@@ -1,44 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
#define CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
#pragma once
// Taken from: http://msdn.microsoft.com/en-us/library/dd162826(v=vs.85).aspx
#define MONITOR_CENTER 0x0001 // center rect to monitor
#define MONITOR_CLIP 0x0000 // clip rect to monitor
#define MONITOR_WORKAREA 0x0002 // use monitor work area
#define MONITOR_AREA 0x0000 // use monitor entire area
//
// ClipOrCenterRectToMonitor
//
// The most common problem apps have when running on a
// multimonitor system is that they "clip" or "pin" windows
// based on the SM_CXSCREEN and SM_CYSCREEN system metrics.
// Because of app compatibility reasons these system metrics
// return the size of the primary monitor.
//
// This shows how you use the multi-monitor functions
// to do the same thing.
//
// params:
// prc : pointer to QRect to modify
// flags : some combination of the MONITOR_* flags above
//
// example:
//
// ClipOrCenterRectToMonitor(&aRect, MONITOR_CLIP | MONITOR_WORKAREA);
//
// Takes parameter pointer to RECT "aRect" and flags MONITOR_CLIP | MONITOR_WORKAREA
// This will modify aRect without resizing it so that it remains within the on-screen boundaries.
void ClipOrCenterRectToMonitor(QRect *prc, const UINT flags);
#endif // CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
-143
View File
@@ -1,143 +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 "EditorDefs.h"
#include "NumberCtrl.h"
QNumberCtrl::QNumberCtrl(QWidget* parent)
: QDoubleSpinBox(parent)
, m_bMouseDown(false)
, m_bDragged(false)
, m_bUndoEnabled(false)
, m_prevValue(0)
{
connect(this, &QAbstractSpinBox::editingFinished, this, &QNumberCtrl::onEditingFinished);
}
void QNumberCtrl::changeEvent(QEvent* event)
{
if (event->type() == QEvent::EnabledChange)
{
setButtonSymbols(isEnabled() ? UpDownArrows : NoButtons);
}
QDoubleSpinBox::changeEvent(event);
}
void QNumberCtrl::SetRange(double newMin, double newMax)
{
// Avoid setting this value if its close to the current value, because otherwise qt will pump events into the queue to redraw/etc.
if ( (!AZ::IsClose(this->minimum(), newMin, DBL_EPSILON)) || (!AZ::IsClose(this->maximum(), newMax, DBL_EPSILON)) )
{
setRange(newMin, newMax);
}
}
void QNumberCtrl::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
emit mousePressed();
m_bMouseDown = true;
m_bDragged = false;
m_mousePos = event->pos();
if (m_bUndoEnabled && !CUndo::IsRecording())
{
GetIEditor()->BeginUndo();
}
emit dragStarted();
grabMouse();
}
QDoubleSpinBox::mousePressEvent(event);
}
void QNumberCtrl::mouseReleaseEvent(QMouseEvent* event)
{
QDoubleSpinBox::mouseReleaseEvent(event);
if (event->button() == Qt::LeftButton)
{
m_bMouseDown = m_bDragged = false;
emit valueUpdated();
emit valueChanged();
if (m_bUndoEnabled && CUndo::IsRecording())
{
GetIEditor()->AcceptUndo(m_undoText);
}
emit dragFinished();
releaseMouse();
m_prevValue = value();
emit mouseReleased();
}
}
void QNumberCtrl::mouseMoveEvent(QMouseEvent* event)
{
QDoubleSpinBox::mousePressEvent(event);
if (m_bMouseDown)
{
m_bDragged = true;
int dy = event->pos().y() - m_mousePos.y();
setValue(value() - singleStep() * dy);
emit valueUpdated();
m_mousePos = event->pos();
}
}
void QNumberCtrl::EnableUndo(const QString& undoText)
{
m_undoText = undoText;
m_bUndoEnabled = true;
}
void QNumberCtrl::focusInEvent(QFocusEvent* event)
{
m_prevValue = value();
QDoubleSpinBox::focusInEvent(event);
}
void QNumberCtrl::onEditingFinished()
{
bool undo = m_bUndoEnabled && !CUndo::IsRecording() && m_prevValue != value();
if (undo)
{
GetIEditor()->BeginUndo();
}
emit valueUpdated();
emit valueChanged();
if (undo)
{
GetIEditor()->AcceptUndo(m_undoText);
}
m_prevValue = value();
}
#include <Controls/moc_NumberCtrl.cpp>
-64
View File
@@ -1,64 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
#pragma once
// NumberCtrl.h : header file
//
#if !defined(Q_MOC_RUN)
#include <QDoubleSpinBox>
#endif
class QNumberCtrl
: public QDoubleSpinBox
{
Q_OBJECT
public:
QNumberCtrl(QWidget* parent = nullptr);
bool IsDragging() const { return m_bDragged; }
//! If called will enable undo with given text when control is modified.
void EnableUndo(const QString& undoText);
void SetRange(double newMin, double maxRange);
Q_SIGNALS:
void dragStarted();
void dragFinished();
void valueUpdated();
void valueChanged();
void mouseReleased();
void mousePressed();
protected:
void changeEvent(QEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
void onEditingFinished();
void onValueChanged(double d);
bool m_bMouseDown;
bool m_bDragged;
QPoint m_mousePos;
bool m_bUndoEnabled;
double m_prevValue;
QString m_undoText;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
-93
View File
@@ -1,93 +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 "EditorDefs.h"
#include "TextEditorCtrl.h"
// CTextEditorCtrl
CTextEditorCtrl::CTextEditorCtrl(QWidget* pParent)
: QTextEdit(pParent)
{
m_bModified = true;
QFont font;
font.setFamily("Courier New");
font.setFixedPitch(true);
font.setPointSize(10);
setFont(font);
setLineWrapMode(NoWrap);
connect(this, &QTextEdit::textChanged, this, &CTextEditorCtrl::OnChange);
}
CTextEditorCtrl::~CTextEditorCtrl()
{
}
// CTextEditorCtrl message handlers
void CTextEditorCtrl::LoadFile(const QString& sFileName)
{
if (m_filename == sFileName)
{
return;
}
m_filename = sFileName;
clear();
CCryFile file(sFileName.toUtf8().data(), "rb");
if (file.Open(sFileName.toUtf8().data(), "rb"))
{
size_t length = file.GetLength();
QByteArray text;
text.resize(static_cast<int>(length));
file.ReadRaw(text.data(), length);
setPlainText(text);
}
m_bModified = false;
}
//////////////////////////////////////////////////////////////////////////
void CTextEditorCtrl::SaveFile(const QString& sFileName)
{
if (sFileName.isEmpty())
{
return;
}
if (!CFileUtil::OverwriteFile(sFileName.toUtf8().data()))
{
return;
}
QFile file(sFileName);
file.open(QFile::WriteOnly);
file.write(toPlainText().toUtf8());
m_bModified = false;
}
//////////////////////////////////////////////////////////////////////////
void CTextEditorCtrl::OnChange()
{
m_bModified = true;
}
#include <Controls/moc_TextEditorCtrl.cpp>
-42
View File
@@ -1,42 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
#pragma once
// CTextEditorCtrl
#if !defined(Q_MOC_RUN)
#include <QTextEdit>
#endif
class CTextEditorCtrl
: public QTextEdit
{
Q_OBJECT
public:
CTextEditorCtrl(QWidget* pParent = nullptr);
virtual ~CTextEditorCtrl();
void LoadFile(const QString& sFileName);
void SaveFile(const QString& sFileName);
QString GetFilename() const { return m_filename; }
bool IsModified() const { return m_bModified; }
//! Must be called after OnChange message.
void OnChange();
protected:
QString m_filename;
bool m_bModified;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
-322
View File
@@ -1,322 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
#define CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
#pragma once
#include <iterator>
namespace TreeCtrlUtils
{
template <typename P>
class TreeItemIterator
: public P
{
public:
typedef P Traits;
//iterator traits, required by STL
typedef ptrdiff_t difference_type;
typedef HTREEITEM value_type;
typedef HTREEITEM* pointer;
typedef HTREEITEM& reference;
typedef std::forward_iterator_tag iterator_category;
TreeItemIterator()
: pCtrl(0)
, hItem(0) {}
explicit TreeItemIterator(const P& traits)
: P(traits)
, pCtrl(0)
, hItem(0) {}
TreeItemIterator(const TreeItemIterator& other)
: P(other)
, pCtrl(other.pCtrl)
, hItem(other.hItem) {}
TreeItemIterator(CTreeCtrl* pCtrl, HTREEITEM hItem)
: pCtrl(pCtrl)
, hItem(hItem) {}
TreeItemIterator(CTreeCtrl* pCtrl, HTREEITEM hItem, const P& traits)
: P(traits)
, pCtrl(pCtrl)
, hItem(hItem) {}
HTREEITEM operator*() {return hItem; }
bool operator==(const TreeItemIterator& other) const {return pCtrl == other.pCtrl && hItem == other.hItem; }
bool operator!=(const TreeItemIterator& other) const {return pCtrl != other.pCtrl || hItem != other.hItem; }
TreeItemIterator& operator++()
{
HTREEITEM hNextItem = 0;
if (RecurseToChildren(hItem))
{
hNextItem = (pCtrl ? pCtrl->GetChildItem(hItem) : 0);
}
while (pCtrl && hItem && !hNextItem)
{
hNextItem = pCtrl->GetNextSiblingItem(hItem);
if (!hNextItem)
{
hItem = pCtrl->GetParentItem(hItem);
}
}
hItem = hNextItem;
return *this;
}
TreeItemIterator operator++(int) {TreeItemIterator old = *this; ++(*this); return old; }
CTreeCtrl* pCtrl;
HTREEITEM hItem;
};
class NonRecursiveTreeItemIteratorTraits
{
public:
bool RecurseToChildren(HTREEITEM hItem) {return false; }
};
typedef TreeItemIterator<NonRecursiveTreeItemIteratorTraits> NonRecursiveTreeItemIterator;
class RecursiveTreeItemIteratorTraits
{
public:
bool RecurseToChildren(HTREEITEM hItem) {return true; }
};
typedef TreeItemIterator<RecursiveTreeItemIteratorTraits> RecursiveTreeItemIterator;
inline RecursiveTreeItemIterator BeginTreeItemsRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
if (hItem == 0)
{
hItem = (pCtrl ? pCtrl->GetRootItem() : 0);
}
return RecursiveTreeItemIterator(pCtrl, hItem);
}
inline RecursiveTreeItemIterator EndTreeItemsRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
HTREEITEM hEndItem = 0;
HTREEITEM hParent = hItem;
do
{
if (hParent)
{
hEndItem = pCtrl->GetNextSiblingItem(hParent);
}
hParent = (pCtrl && hParent ? pCtrl->GetParentItem(hParent) : 0);
}
while (hParent && !hEndItem);
return RecursiveTreeItemIterator(pCtrl, hEndItem);
}
inline NonRecursiveTreeItemIterator BeginTreeItemsNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
if (hItem == 0)
{
hItem = (pCtrl ? pCtrl->GetRootItem() : 0);
}
if (hItem)
{
hItem = pCtrl->GetChildItem(hItem);
}
return NonRecursiveTreeItemIterator(pCtrl, hItem);
}
inline NonRecursiveTreeItemIterator EndTreeItemsNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
HTREEITEM hEndItem = 0;
HTREEITEM hParent = 0;
while (hParent && !hEndItem)
{
hParent = (pCtrl && hItem ? pCtrl->GetParentItem(hItem) : 0);
if (hParent)
{
hEndItem = pCtrl->GetNextSiblingItem(hParent);
}
}
return NonRecursiveTreeItemIterator(pCtrl, hEndItem);
}
template <typename T, typename P>
class TreeItemDataIterator
{
public:
typedef T Type;
typedef TreeItemIterator<P> InternalIterator;
//iterator traits, required by STL
typedef ptrdiff_t difference_type;
typedef Type* value_type;
typedef Type** pointer;
typedef Type*& reference;
typedef std::forward_iterator_tag iterator_category;
TreeItemDataIterator() {}
TreeItemDataIterator(const TreeItemDataIterator& other)
: iterator(other.iterator) {AdvanceToValidIterator(); }
explicit TreeItemDataIterator(const InternalIterator& iterator)
: iterator(iterator) {AdvanceToValidIterator(); }
Type* operator*() {return reinterpret_cast<Type*>(iterator.pCtrl->GetItemData(iterator.hItem)); }
bool operator==(const TreeItemDataIterator& other) const {return iterator == other.iterator; }
bool operator!=(const TreeItemDataIterator& other) const {return iterator != other.iterator; }
HTREEITEM GetTreeItem() {return iterator.hItem; }
TreeItemDataIterator& operator++()
{
++iterator;
AdvanceToValidIterator();
return *this;
}
TreeItemDataIterator operator++(int) {TreeItemDataIterator old = *this; ++(*this); return old; }
private:
void AdvanceToValidIterator()
{
while (iterator.pCtrl && iterator.hItem && !iterator.pCtrl->GetItemData(iterator.hItem))
{
++iterator;
}
}
InternalIterator iterator;
};
template <typename T>
class RecursiveItemDataIteratorType
{
public: typedef TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> type;
};
template <typename T>
inline TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> BeginTreeItemDataRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits>(BeginTreeItemsRecursive(pCtrl, hItem));
}
template <typename T>
inline TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> EndTreeItemDataRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits>(EndTreeItemsRecursive(pCtrl, hItem));
}
template <typename T>
class NonRecursiveItemDataIteratorType
{
typedef TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> type;
};
template <typename T>
inline TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> BeginTreeItemDataNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits>(BeginTreeItemsNonRecursive(pCtrl, hItem));
}
template <typename T>
inline TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> EndTreeItemDataNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits>(EndTreeItemsNonRecursive(pCtrl, hItem));
}
class SelectedTreeItemIterator
{
public:
SelectedTreeItemIterator()
: pCtrl(0)
, hItem(0) {}
SelectedTreeItemIterator(const SelectedTreeItemIterator& other)
: pCtrl(other.pCtrl)
, hItem(other.hItem) {}
SelectedTreeItemIterator(CXTTreeCtrl* pCtrl, HTREEITEM hItem)
: pCtrl(pCtrl)
, hItem(hItem) {}
HTREEITEM operator*() {return hItem; }
bool operator==(const SelectedTreeItemIterator& other) const {return pCtrl == other.pCtrl && hItem == other.hItem; }
bool operator!=(const SelectedTreeItemIterator& other) const {return pCtrl != other.pCtrl || hItem != other.hItem; }
SelectedTreeItemIterator& operator++()
{
hItem = (pCtrl ? pCtrl->GetNextSelectedItem(hItem) : 0);
return *this;
}
SelectedTreeItemIterator operator++(int) {SelectedTreeItemIterator old = *this; ++(*this); return old; }
CXTTreeCtrl* pCtrl;
HTREEITEM hItem;
};
SelectedTreeItemIterator BeginSelectedTreeItems(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemIterator(pCtrl, (pCtrl ? pCtrl->GetFirstSelectedItem() : 0));
}
SelectedTreeItemIterator EndSelectedTreeItems(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemIterator(pCtrl, 0);
}
template <typename T>
class SelectedTreeItemDataIterator
{
public:
typedef T Type;
typedef SelectedTreeItemIterator InternalIterator;
SelectedTreeItemDataIterator() {}
SelectedTreeItemDataIterator(const SelectedTreeItemDataIterator& other)
: iterator(other.iterator) {AdvanceToValidIterator(); }
explicit SelectedTreeItemDataIterator(const InternalIterator& iterator)
: iterator(iterator) {AdvanceToValidIterator(); }
Type* operator*() {return reinterpret_cast<Type*>(iterator.pCtrl->GetItemData(iterator.hItem)); }
bool operator==(const SelectedTreeItemDataIterator& other) const {return iterator == other.iterator; }
bool operator!=(const SelectedTreeItemDataIterator& other) const {return iterator != other.iterator; }
HTREEITEM GetTreeItem() {return iterator.hItem; }
SelectedTreeItemDataIterator& operator++()
{
++iterator;
AdvanceToValidIterator();
return *this;
}
SelectedTreeItemDataIterator operator++(int) {SelectedTreeItemDataIterator old = *this; ++(*this); return old; }
private:
void AdvanceToValidIterator()
{
while (iterator.pCtrl && iterator.hItem && !iterator.pCtrl->GetItemData(iterator.hItem))
{
++iterator;
}
}
InternalIterator iterator;
};
template <typename T>
SelectedTreeItemDataIterator<T> BeginSelectedTreeItemData(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemDataIterator<T>(BeginSelectedTreeItems(pCtrl));
}
template <typename T>
SelectedTreeItemDataIterator<T> EndSelectedTreeItemData(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemDataIterator<T>(EndSelectedTreeItems(pCtrl));
}
}
#endif // CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
-542
View File
@@ -1,542 +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 "EditorDefs.h"
#include "EditorPanelUtils.h"
#include <AzCore/Utils/Utils.h>
// Qt
#include <QInputDialog>
#include <QFileDialog>
#include <QXmlStreamReader>
// Editor
#include "IEditorPanelUtils.h"
#include "Objects/EntityObject.h"
#include "CryEditDoc.h"
#include "ViewManager.h"
#include "Controls/QToolTipWidget.h"
#include "Objects/SelectionGroup.h"
#ifndef PI
#define PI 3.14159265358979323f
#endif
struct ToolTip
{
bool isValid;
QString title;
QString content;
QString specialContent;
QString disabledContent;
};
// internal implementation for better compile times - should also never be used externally, use IParticleEditorUtils interface for that.
class CEditorPanelUtils_Impl
: public IEditorPanelUtils
{
public:
void SetViewportDragOperation(void(* dropCallback)(CViewport* viewport, int dragPointX, int dragPointY, void* custom), void* custom) override
{
for (int i = 0; i < GetIEditor()->GetViewManager()->GetViewCount(); i++)
{
GetIEditor()->GetViewManager()->GetView(i)->SetGlobalDropCallback(dropCallback, custom);
}
}
public:
int PreviewWindow_GetDisplaySettingsDebugFlags(CDisplaySettings* settings) override
{
CRY_ASSERT(settings);
return settings->GetDebugFlags();
}
void PreviewWindow_SetDisplaySettingsDebugFlags(CDisplaySettings* settings, int flags) override
{
CRY_ASSERT(settings);
settings->SetDebugFlags(flags);
}
protected:
QVector<HotKey> hotkeys;
bool m_hotkeysAreEnabled;
public:
bool HotKey_Import() override
{
QVector<QPair<QString, QString> > keys;
QString filepath = QFileDialog::getOpenFileName(nullptr, "Select shortcut configuration to load",
QString(), "HotKey Config Files (*.hkxml)");
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly))
{
return false;
}
QXmlStreamReader stream(&file);
bool result = true;
while (!stream.isEndDocument())
{
if (stream.isStartElement())
{
if (stream.name() == "HotKey")
{
QPair<QString, QString> key;
QXmlStreamAttributes att = stream.attributes();
for (QXmlStreamAttribute attr : att)
{
if (attr.name().compare(QLatin1String("path"), Qt::CaseInsensitive) == 0)
{
key.first = attr.value().toString();
}
if (attr.name().compare(QLatin1String("sequence"), Qt::CaseInsensitive) == 0)
{
key.second = attr.value().toString();
}
}
if (!key.first.isEmpty())
{
keys.push_back(key); // we allow blank key sequences for unassigned shortcuts
}
else
{
result = false; //but not blank paths!
}
}
}
stream.readNext();
}
file.close();
if (result)
{
HotKey_BuildDefaults();
for (QPair<QString, QString> key : keys)
{
for (int j = 0; j < hotkeys.count(); j++)
{
if (hotkeys[j].path.compare(key.first, Qt::CaseInsensitive) == 0)
{
hotkeys[j].SetPath(key.first.toStdString().c_str());
hotkeys[j].SetSequenceFromString(key.second.toStdString().c_str());
}
}
}
}
return result;
}
void HotKey_Export() override
{
auto settingDir = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / "Editor" / "Plugins" / "ParticleEditorPlugin" / "settings";
QString filepath = QFileDialog::getSaveFileName(nullptr, "Select shortcut configuration to load", settingDir.c_str(), "HotKey Config Files (*.hkxml)");
QFile file(filepath);
if (!file.open(QIODevice::WriteOnly))
{
return;
}
QXmlStreamWriter stream(&file);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement("HotKeys");
for (HotKey key : hotkeys)
{
stream.writeStartElement("HotKey");
stream.writeAttribute("path", key.path);
stream.writeAttribute("sequence", key.sequence.toString());
stream.writeEndElement();
}
stream.writeEndElement();
stream.writeEndDocument();
file.close();
}
QKeySequence HotKey_GetShortcut(const char* path) override
{
for (HotKey combo : hotkeys)
{
if (combo.IsMatch(path))
{
return combo.sequence;
}
}
return QKeySequence();
}
bool HotKey_IsPressed(const QKeyEvent* event, const char* path) override
{
if (!m_hotkeysAreEnabled)
{
return false;
}
unsigned int keyInt = 0;
//Capture any modifiers
Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
if (modifiers & Qt::ShiftModifier)
{
keyInt += Qt::SHIFT;
}
if (modifiers & Qt::ControlModifier)
{
keyInt += Qt::CTRL;
}
if (modifiers & Qt::AltModifier)
{
keyInt += Qt::ALT;
}
if (modifiers & Qt::MetaModifier)
{
keyInt += Qt::META;
}
//Capture any key
keyInt += event->key();
QString t0 = QKeySequence(keyInt).toString();
QString t1 = HotKey_GetShortcut(path).toString();
//if strings match then shortcut is pressed
if (t1.compare(t0, Qt::CaseInsensitive) == 0)
{
return true;
}
return false;
}
bool HotKey_IsPressed(const QShortcutEvent* event, const char* path) override
{
if (!m_hotkeysAreEnabled)
{
return false;
}
QString t0 = event->key().toString();
QString t1 = HotKey_GetShortcut(path).toString();
//if strings match then shortcut is pressed
if (t1.compare(t0, Qt::CaseInsensitive) == 0)
{
return true;
}
return false;
}
bool HotKey_LoadExisting() override
{
QSettings settings("O3DE", "O3DE");
QString group = "Hotkeys/";
HotKey_BuildDefaults();
int size = settings.beginReadArray(group);
for (int i = 0; i < size; i++)
{
settings.setArrayIndex(i);
QPair<QString, QString> hotkey;
hotkey.first = settings.value("name").toString();
hotkey.second = settings.value("keySequence").toString();
if (!hotkey.first.isEmpty())
{
for (int j = 0; j < hotkeys.count(); j++)
{
if (hotkeys[j].path.compare(hotkey.first, Qt::CaseInsensitive) == 0)
{
hotkeys[j].SetPath(hotkey.first.toStdString().c_str());
hotkeys[j].SetSequenceFromString(hotkey.second.toStdString().c_str());
}
}
}
}
settings.endArray();
if (hotkeys.isEmpty())
{
return false;
}
return true;
}
void HotKey_SaveCurrent() override
{
QSettings settings("O3DE", "O3DE");
QString group = "Hotkeys/";
settings.remove("Hotkeys/");
settings.sync();
settings.beginWriteArray(group);
int saveIndex = 0;
for (HotKey key : hotkeys)
{
if (!key.path.isEmpty())
{
settings.setArrayIndex(saveIndex++);
settings.setValue("name", key.path);
settings.setValue("keySequence", key.sequence.toString());
}
}
settings.endArray();
settings.sync();
}
void HotKey_BuildDefaults() override
{
m_hotkeysAreEnabled = true;
QVector<QPair<QString, QString> > keys;
while (hotkeys.count() > 0)
{
hotkeys.takeAt(0);
}
//MENU SELECTION SHORTCUTS////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("Menus.File Menu", "Alt+F"));
keys.push_back(QPair<QString, QString>("Menus.Edit Menu", "Alt+E"));
keys.push_back(QPair<QString, QString>("Menus.View Menu", "Alt+V"));
//FILE MENU SHORTCUTS/////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("File Menu.Create new emitter", "Ctrl+N"));
keys.push_back(QPair<QString, QString>("File Menu.Create new library", "Ctrl+Shift+N"));
keys.push_back(QPair<QString, QString>("File Menu.Create new folder", ""));
keys.push_back(QPair<QString, QString>("File Menu.Import", "Ctrl+I"));
keys.push_back(QPair<QString, QString>("File Menu.Import level library", "Ctrl+Shift+I"));
keys.push_back(QPair<QString, QString>("File Menu.Save", "Ctrl+S"));
keys.push_back(QPair<QString, QString>("File Menu.Close", "Ctrl+Q"));
//EDIT MENU SHORTCUTS/////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("Edit Menu.Copy", "Ctrl+C"));
keys.push_back(QPair<QString, QString>("Edit Menu.Paste", "Ctrl+V"));
keys.push_back(QPair<QString, QString>("Edit Menu.Duplicate", "Ctrl+D"));
keys.push_back(QPair<QString, QString>("Edit Menu.Undo", "Ctrl+Z"));
keys.push_back(QPair<QString, QString>("Edit Menu.Redo", "Ctrl+Shift+Z"));
keys.push_back(QPair<QString, QString>("Edit Menu.Group", "Ctrl+Alt+O"));
keys.push_back(QPair<QString, QString>("Edit Menu.Ungroup", "Ctrl+Alt+P"));
keys.push_back(QPair<QString, QString>("Edit Menu.Rename", "Ctrl+R"));
keys.push_back(QPair<QString, QString>("Edit Menu.Reset", ""));
keys.push_back(QPair<QString, QString>("Edit Menu.Edit Hotkeys", ""));
keys.push_back(QPair<QString, QString>("Edit Menu.Assign to selected", "Ctrl+Space"));
keys.push_back(QPair<QString, QString>("Edit Menu.Insert Comment", "Ctrl+Alt+M"));
keys.push_back(QPair<QString, QString>("Edit Menu.Enable/Disable Emitter", "Ctrl+E"));
keys.push_back(QPair<QString, QString>("File Menu.Enable All", ""));
keys.push_back(QPair<QString, QString>("File Menu.Disable All", ""));
keys.push_back(QPair<QString, QString>("Edit Menu.Delete", "Del"));
//VIEW MENU SHORTCUTS/////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("View Menu.Reset Layout", ""));
//PLAYBACK CONTROL////////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("Previewer.Play/Pause Toggle", "Space"));
keys.push_back(QPair<QString, QString>("Previewer.Step forward through time", "c"));
keys.push_back(QPair<QString, QString>("Previewer.Loop Toggle", "z"));
keys.push_back(QPair<QString, QString>("Previewer.Reset Playback", "x"));
keys.push_back(QPair<QString, QString>("Previewer.Focus", "Ctrl+F"));
keys.push_back(QPair<QString, QString>("Previewer.Zoom In", "w"));
keys.push_back(QPair<QString, QString>("Previewer.Zoom Out", "s"));
keys.push_back(QPair<QString, QString>("Previewer.Pan Left", "a"));
keys.push_back(QPair<QString, QString>("Previewer.Pan Right", "d"));
for (QPair<QString, QString> key : keys)
{
unsigned int index = hotkeys.count();
hotkeys.push_back(HotKey());
hotkeys[index].SetPath(key.first.toStdString().c_str());
hotkeys[index].SetSequenceFromString(key.second.toStdString().c_str());
}
}
void HotKey_SetKeys(QVector<HotKey> keys) override
{
hotkeys = keys;
}
QVector<HotKey> HotKey_GetKeys() override
{
return hotkeys;
}
QString HotKey_GetPressedHotkey(const QKeyEvent* event) override
{
if (!m_hotkeysAreEnabled)
{
return "";
}
for (HotKey key : hotkeys)
{
if (HotKey_IsPressed(event, key.path.toUtf8()))
{
return key.path;
}
}
return "";
}
QString HotKey_GetPressedHotkey(const QShortcutEvent* event) override
{
if (!m_hotkeysAreEnabled)
{
return "";
}
for (HotKey key : hotkeys)
{
if (HotKey_IsPressed(event, key.path.toUtf8()))
{
return key.path;
}
}
return "";
}
//building the default hotkey list re-enables hotkeys
//do not use this when rebuilding the default list is a possibility.
void HotKey_SetEnabled(bool val) override
{
m_hotkeysAreEnabled = val;
}
bool HotKey_IsEnabled() const override
{
return m_hotkeysAreEnabled;
}
protected:
QMap<QString, ToolTip> m_tooltips;
void ToolTip_ParseNode(XmlNodeRef node)
{
if (QString(node->getTag()).compare("tooltip", Qt::CaseInsensitive) != 0)
{
unsigned int childCount = node->getChildCount();
for (unsigned int i = 0; i < childCount; i++)
{
ToolTip_ParseNode(node->getChild(i));
}
}
QString title = node->getAttr("title");
QString content = node->getAttr("content");
QString specialContent = node->getAttr("special_content");
QString disabledContent = node->getAttr("disabled_content");
QMap<QString, ToolTip>::iterator itr = m_tooltips.insert(node->getAttr("path"), ToolTip());
itr->isValid = true;
itr->title = title;
itr->content = content;
itr->specialContent = specialContent;
itr->disabledContent = disabledContent;
unsigned int childCount = node->getChildCount();
for (unsigned int i = 0; i < childCount; i++)
{
ToolTip_ParseNode(node->getChild(i));
}
}
ToolTip GetToolTip(QString path)
{
if (m_tooltips.contains(path))
{
return m_tooltips[path];
}
ToolTip temp;
temp.isValid = false;
return temp;
}
public:
void ToolTip_LoadConfigXML(QString filepath) override
{
XmlNodeRef node = GetIEditor()->GetSystem()->LoadXmlFromFile(filepath.toStdString().c_str());
ToolTip_ParseNode(node);
}
void ToolTip_BuildFromConfig(IQToolTip* tooltip, QString path, QString option, QString optionalData = "", bool isEnabled = true) override
{
AZ_Assert(tooltip, "tooltip cannot be null");
QString title = ToolTip_GetTitle(path, option);
QString content = ToolTip_GetContent(path, option);
QString specialContent = ToolTip_GetSpecialContentType(path, option);
QString disabledContent = ToolTip_GetDisabledContent(path, option);
// Even if these items are empty, we set them anyway to clear out any data that was left over from when the tooltip was used for a different object.
tooltip->SetTitle(title);
tooltip->SetContent(content);
//this only handles simple creation...if you need complex call this then add specials separate
if (!specialContent.contains("::"))
{
tooltip->AddSpecialContent(specialContent, optionalData);
}
if (!isEnabled) // If disabled, add disabled value
{
tooltip->AppendContent(disabledContent);
}
}
QString ToolTip_GetTitle(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).title;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).title;
}
return GetToolTip(path).title;
}
QString ToolTip_GetContent(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).content;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).content;
}
return GetToolTip(path).content;
}
QString ToolTip_GetSpecialContentType(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).specialContent;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).specialContent;
}
return GetToolTip(path).specialContent;
}
QString ToolTip_GetDisabledContent(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).disabledContent;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).disabledContent;
}
return GetToolTip(path).disabledContent;
}
};
IEditorPanelUtils* CreateEditorPanelUtils()
{
return new CEditorPanelUtils_Impl();
}
-16
View File
@@ -1,16 +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
*
*/
// Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#ifndef CRYINCLUDE_CRYEDITOR_EDITORPANELUTILS_H
#define CRYINCLUDE_CRYEDITOR_EDITORPANELUTILS_H
#pragma once
struct IEditorPanelUtils;
IEditorPanelUtils* CreateEditorPanelUtils();
#endif
-3
View File
@@ -69,7 +69,6 @@ class CSelectionTreeManager;
struct SEditorSettings;
class CGameExporter;
class IAWSResourceManager;
struct IEditorPanelUtils;
namespace WinWidget
{
@@ -526,8 +525,6 @@ struct IEditor
virtual IEditorMaterialManager* GetIEditorMaterialManager() = 0; // Vladimir@Conffx
//! Returns IconManager.
virtual IIconManager* GetIconManager() = 0;
//! Get Panel Editor Utilities
virtual IEditorPanelUtils* GetEditorPanelUtils() = 0;
//! Get Music Manager.
virtual CMusicManager* GetMusicManager() = 0;
virtual float GetTerrainElevation(float x, float y) = 0;
-13
View File
@@ -81,9 +81,6 @@ AZ_POP_DISABLE_WARNING
// AWSNativeSDK
#include <AWSNativeSDKInit/AWSNativeSDKInit.h>
#include "IEditorPanelUtils.h"
#include "EditorPanelUtils.h"
#include "Core/QtEditorApplication.h" // for Editor::EditorQtApplication
static CCryEditDoc * theDocument;
@@ -143,7 +140,6 @@ CEditorImpl::CEditorImpl()
, m_QtApplication(static_cast<Editor::EditorQtApplication*>(qApp))
, m_pImageUtil(nullptr)
, m_pLogFile(nullptr)
, m_panelEditorUtils(nullptr)
{
// note that this is a call into EditorCore.dll, which stores the g_pEditorPointer for all shared modules that share EditorCore.dll
// this means that they don't need to do SetIEditor(...) themselves and its available immediately
@@ -167,8 +163,6 @@ CEditorImpl::CEditorImpl()
m_pDisplaySettings->LoadRegistry();
m_pPluginManager = new CPluginManager;
m_panelEditorUtils = CreateEditorPanelUtils();
m_pObjectManager = new CObjectManager;
m_pViewManager = new CViewManager;
m_pIconManager = new CIconManager;
@@ -301,8 +295,6 @@ CEditorImpl::~CEditorImpl()
SAFE_DELETE(m_pViewManager)
SAFE_DELETE(m_pObjectManager) // relies on prefab manager
SAFE_DELETE(m_panelEditorUtils);
// some plugins may be exporter - this must be above plugin manager delete.
SAFE_DELETE(m_pExportManager);
@@ -1658,8 +1650,3 @@ void CEditorImpl::DestroyQMimeData(QMimeData* data) const
{
delete data;
}
IEditorPanelUtils* CEditorImpl::GetEditorPanelUtils()
{
return m_panelEditorUtils;
}
-2
View File
@@ -298,7 +298,6 @@ public:
IEditorMaterialManager* GetIEditorMaterialManager() override; // Vladimir@Conffx
IImageUtil* GetImageUtil() override; // Vladimir@conffx
SEditorSettings* GetEditorSettings() override;
IEditorPanelUtils* GetEditorPanelUtils() override;
ILogFile* GetLogFile() override { return m_pLogFile; }
void UnloadPlugins() override;
@@ -356,7 +355,6 @@ protected:
CErrorsDlg* m_pErrorsDlg;
//! Source control interface.
ISourceControl* m_pSourceControl;
IEditorPanelUtils* m_panelEditorUtils;
CSelectionTreeManager* m_pSelectionTreeManager;
-131
View File
@@ -1,131 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#ifndef CRYINCLUDE_CRYEDITOR_IPANELEDITORUTILS_H
#define CRYINCLUDE_CRYEDITOR_IPANELEDITORUTILS_H
#pragma once
#include "Cry_Vector3.h"
#include "DisplaySettings.h"
#include "Include/IDisplayViewport.h"
#include "Include/IIconManager.h"
#include <qstringlist.h>
#include <qkeysequence.h>
#include <qevent.h>
#include <QShortcutEvent>
class CBaseObject;
class CViewport;
class IQToolTip;
struct HotKey
{
HotKey()
: path("")
, sequence(QKeySequence())
{
}
void CopyFrom(const HotKey& other)
{
path = other.path;
sequence = other.sequence;
}
void SetPath(const char* _path)
{
path = QString(_path);
}
void SetSequenceFromString(const char* _sequence)
{
sequence = QKeySequence::fromString(_sequence);
}
void SetSequence(const QKeySequence& other)
{
sequence = other;
}
bool IsMatch(QString _path)
{
return path.compare(_path, Qt::CaseInsensitive) == 0;
}
bool IsMatch(QKeySequence _sequence)
{
return sequence.matches(_sequence);
}
bool operator < (const HotKey& other) const
{
//split the paths into lists compare per level
QStringList m_categories = path.split('.');
QStringList o_categories = other.path.split('.');
int m_catSize = m_categories.size();
int o_catSize = o_categories.size();
int size = (m_catSize < o_catSize) ? m_catSize : o_catSize;
//sort categories to keep them together
for (int i = 0; i < size; i++)
{
if (m_categories[i] < o_categories[i])
{
return true;
}
if (m_categories[i] > o_categories[i])
{
return false;
}
}
//if comparing a category and a item in that category the category is < item
return m_catSize > o_catSize;
}
QKeySequence sequence;
QString path;
};
struct IEditorPanelUtils
{
virtual ~IEditorPanelUtils() {}
virtual void SetViewportDragOperation(void(*)(CViewport* viewport, int dragPointX, int dragPointY, void* custom), void* custom) = 0;
//PREVIEW WINDOW UTILS////////////////////////////////////////////////////
virtual int PreviewWindow_GetDisplaySettingsDebugFlags(CDisplaySettings* settings) = 0;
virtual void PreviewWindow_SetDisplaySettingsDebugFlags(CDisplaySettings* settings, int flags) = 0;
//HOTKEY UTILS////////////////////////////////////////////////////////////
virtual bool HotKey_Import() = 0;
virtual void HotKey_Export() = 0;
virtual QKeySequence HotKey_GetShortcut(const char* path) = 0;
virtual bool HotKey_IsPressed(const QKeyEvent* event, const char* path) = 0;
virtual bool HotKey_IsPressed(const QShortcutEvent* event, const char* path) = 0;
virtual bool HotKey_LoadExisting() = 0;
virtual void HotKey_SaveCurrent() = 0;
virtual void HotKey_BuildDefaults() = 0;
virtual void HotKey_SetKeys(QVector<HotKey> keys) = 0;
virtual QVector<HotKey> HotKey_GetKeys() = 0;
virtual QString HotKey_GetPressedHotkey(const QKeyEvent* event) = 0;
virtual QString HotKey_GetPressedHotkey(const QShortcutEvent* event) = 0;
virtual void HotKey_SetEnabled(bool val) = 0;
virtual bool HotKey_IsEnabled() const = 0;
//TOOLTIP UTILS///////////////////////////////////////////////////////////
//! Loads a table of tooltip configuration data from an xml file.
virtual void ToolTip_LoadConfigXML(QString filepath) = 0;
//! Initializes a QToolTipWidget from loaded configuration data (see ToolTip_LoadConfigXML())
//! \param tooltip Will be initialized using loaded configuration data
//! \param path Variable serialization path. Will be used as the key for looking up data in the configuration table. (ex: "Rotation.Rotation_Rate_X")
//! \param option Name of a sub-option of the variable specified by "path". (ex: "Emitter_Strength" will look up the tooltip data for "Rotation.Rotation_Rate_X.Emitter_Strength")
//! \param optionalData The argument to be used with "special_content" feature. See ToolTip_GetSpecialContentType() and QToolTipWidget::AddSpecialContent().
//! \param isEnabled If false, the tooltip will indicate the reason why the widget is disabled.
virtual void ToolTip_BuildFromConfig(IQToolTip* tooltip, QString path, QString option, QString optionalData = "", bool isEnabled = true) = 0;
virtual QString ToolTip_GetTitle(QString path, QString option = "") = 0;
virtual QString ToolTip_GetContent(QString path, QString option = "") = 0;
virtual QString ToolTip_GetSpecialContentType(QString path, QString option = "") = 0;
virtual QString ToolTip_GetDisabledContent(QString path, QString option = "") = 0;
};
#endif
-1
View File
@@ -187,6 +187,5 @@ public:
MOCK_METHOD0(UnloadPlugins, void());
MOCK_METHOD0(LoadPlugins, void());
MOCK_METHOD1(GetSearchPath, QString(EEditorPathName));
MOCK_METHOD0(GetEditorPanelUtils, IEditorPanelUtils* ());
};
-14
View File
@@ -334,23 +334,12 @@ set(FILES
Controls/ConsoleSCB.qrc
Controls/FolderTreeCtrl.cpp
Controls/FolderTreeCtrl.h
Controls/HotTrackingTreeCtrl.cpp
Controls/HotTrackingTreeCtrl.h
Controls/ImageHistogramCtrl.cpp
Controls/ImageHistogramCtrl.h
Controls/ImageListCtrl.cpp
Controls/ImageListCtrl.h
Controls/MultiMonHelper.cpp
Controls/MultiMonHelper.h
Controls/NumberCtrl.cpp
Controls/NumberCtrl.h
Controls/NumberCtrl.h
Controls/SplineCtrl.cpp
Controls/SplineCtrl.h
Controls/SplineCtrlEx.cpp
Controls/SplineCtrlEx.h
Controls/TextEditorCtrl.cpp
Controls/TextEditorCtrl.h
Controls/TimelineCtrl.cpp
Controls/TimelineCtrl.h
Controls/WndGridHelper.h
@@ -796,9 +785,6 @@ set(FILES
ViewportTitleDlg.h
EditorEnvironment.cpp
EditorEnvironment.h
IEditorPanelUtils.h
EditorPanelUtils.h
EditorPanelUtils.cpp
)
@@ -6,8 +6,155 @@
*
*/
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/AllocatorManager.h>
#define RECORDING_ENABLED 0
#if RECORDING_ENABLED
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/scoped_lock.h>
namespace
{
class DebugAllocator
{
public:
using pointer_type = void*;
using size_type = AZStd::size_t;
using difference_type = AZStd::ptrdiff_t;
using allow_memory_leaks = AZStd::false_type; ///< Regular allocators should not leak.
AZ_FORCE_INLINE pointer_type allocate(size_t byteSize, size_t alignment, int = 0)
{
return AZ_OS_MALLOC(byteSize, alignment);
}
AZ_FORCE_INLINE size_type resize(pointer_type, size_type)
{
return 0;
}
AZ_FORCE_INLINE void deallocate(pointer_type ptr, size_type, size_type)
{
AZ_OS_FREE(ptr);
}
};
#pragma pack(push, 1)
struct alignas(1) AllocatorOperation
{
enum OperationType : size_t
{
ALLOCATE,
DEALLOCATE
};
OperationType m_type: 1;
size_t m_size : 28; // Can represent up to 256Mb requests
size_t m_alignment : 7; // Can represent up to 128 alignment
size_t m_recordId : 28; // Can represent up to 256M simultaneous requests, we reuse ids
};
#pragma pack(pop)
static_assert(sizeof(AllocatorOperation) == 8);
static AZStd::mutex s_operationsMutex = {};
static constexpr size_t s_maxNumberOfAllocationsToRecord = 16384;
static size_t s_numberOfAllocationsRecorded = 0;
static constexpr size_t s_allocationOperationCount = 5 * 1024;
static AZStd::array<AllocatorOperation, s_allocationOperationCount> s_operations = {};
static uint64_t s_operationCounter = 0;
static unsigned int s_nextRecordId = 1;
using AllocatorOperationByAddress = AZStd::unordered_map<void*, AllocatorOperation, AZStd::less<void*>, DebugAllocator>;
static AllocatorOperationByAddress s_allocatorOperationByAddress;
using AvailableRecordIds = AZStd::vector<unsigned int, DebugAllocator>;
AvailableRecordIds s_availableRecordIds;
void RecordAllocatorOperation(AllocatorOperation::OperationType type, void* ptr, size_t size = 0, size_t alignment = 0)
{
AZStd::scoped_lock<AZStd::mutex> lock(s_operationsMutex);
if (s_operationCounter == s_allocationOperationCount)
{
AZ::IO::SystemFile file;
int mode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND | AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
if (!file.Exists("memoryrecordings.bin"))
{
mode |= AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE;
}
file.Open("memoryrecordings.bin", mode);
if (file.IsOpen())
{
file.Write(&s_operations, sizeof(AllocatorOperation) * s_allocationOperationCount);
file.Close();
}
s_operationCounter = 0;
}
AllocatorOperation& operation = s_operations[s_operationCounter++];
operation.m_type = type;
if (type == AllocatorOperation::OperationType::ALLOCATE)
{
if (s_numberOfAllocationsRecorded > s_maxNumberOfAllocationsToRecord)
{
// reached limit of allocations, dont record anymore
--s_operationCounter;
return;
}
++s_numberOfAllocationsRecorded;
operation.m_size = size;
operation.m_alignment = alignment;
unsigned int recordId = 0;
if (!s_availableRecordIds.empty())
{
recordId = s_availableRecordIds.back();
s_availableRecordIds.pop_back();
}
else
{
recordId = s_nextRecordId;
++s_nextRecordId;
}
operation.m_recordId = recordId;
auto it = s_allocatorOperationByAddress.emplace(ptr, operation);
if (!it.second)
{
// double alloc or resize, leave the current record and return the id
operation = it.first->second;
s_availableRecordIds.emplace_back(recordId);
}
}
else
{
if (ptr == nullptr)
{
// common scenario, just record the operation
operation.m_size = 0;
operation.m_alignment = 0;
operation.m_recordId = 0; // recordId = 0 will flag this case
}
else
{
auto it = s_allocatorOperationByAddress.find(ptr);
if (it != s_allocatorOperationByAddress.end())
{
operation.m_size = it->second.m_size;
operation.m_alignment = it->second.m_alignment;
operation.m_recordId = it->second.m_recordId;
s_availableRecordIds.push_back(it->second.m_recordId);
s_allocatorOperationByAddress.erase(it);
}
else
{
// just dont record this operation
--s_operationCounter;
}
}
}
}
}
#endif
namespace AZ
{
@@ -150,6 +297,10 @@ namespace AZ
records->RegisterAllocation(ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1);
}
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, byteSize, alignment);
#endif
}
void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t alignment, Debug::AllocationInfo* info)
@@ -162,6 +313,9 @@ namespace AZ
records->UnregisterAllocation(ptr, byteSize, alignment, info);
}
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr, byteSize, alignment);
#endif
}
void AllocatorBase::ProfileReallocationBegin([[maybe_unused]] void* ptr, [[maybe_unused]] size_t newSize)
@@ -176,6 +330,10 @@ namespace AZ
ProfileDeallocation(ptr, 0, 0, &info);
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr);
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, newPtr, newSize, newAlignment);
#endif
}
void AllocatorBase::ProfileReallocation(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
@@ -193,6 +351,9 @@ namespace AZ
records->ResizeAllocation(ptr, newSize);
}
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, newSize);
#endif
}
bool AllocatorBase::OnOutOfMemory(size_t byteSize, size_t alignment, int flags, const char* name, const char* fileName, int lineNum)
@@ -115,6 +115,7 @@ namespace AZ
m_ownMemoryBlock[i] = false;
}
AZ_Assert(m_desc.m_numMemoryBlocks > 0, "At least one memory block is required");
for (int i = 0; i < m_desc.m_numMemoryBlocks; ++i)
{
if (m_desc.m_memoryBlocks[i] == nullptr) // Allocate memory block if requested!
@@ -131,17 +132,6 @@ namespace AZ
m_capacity += m_desc.m_memoryBlocksByteSize[i];
}
if (m_desc.m_numMemoryBlocks == 0)
{
// Create default memory space if we can to serve for default allocations
m_memSpaces[0] = AZDLMalloc::create_mspace(0, m_desc.m_isMultithreadAlloc);
if (m_memSpaces[0])
{
AZDLMalloc::mspace_az_set_expandable(m_memSpaces[0], true);
m_capacity = Platform::GetHeapCapacity();
}
}
}
HeapSchema::~HeapSchema()
@@ -32,17 +32,11 @@ namespace AZ
*/
struct Descriptor
{
Descriptor()
: m_numMemoryBlocks(0)
, m_isMultithreadAlloc(true)
{}
static const int m_memoryBlockAlignment = 64 * 1024;
static const int m_maxNumBlocks = 5;
int m_numMemoryBlocks; ///< Number of memory blocks to use.
void* m_memoryBlocks[m_maxNumBlocks]; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
size_t m_memoryBlocksByteSize[m_maxNumBlocks]; ///< Sizes of different memory blocks, if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
bool m_isMultithreadAlloc; ///< Set to true to enable multi threading safe allocation.
int m_numMemoryBlocks = 1; ///< Number of memory blocks to use.
void* m_memoryBlocks[m_maxNumBlocks] = {}; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
size_t m_memoryBlocksByteSize[m_maxNumBlocks] = {4 * 1024}; ///< Sizes of different memory blocks, if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
bool m_isMultithreadAlloc = true; ///< Set to true to enable multi threading safe allocation.
};
HeapSchema(const Descriptor& desc);
@@ -18,7 +18,6 @@
#define AZCORE_SYSTEM_ALLOCATOR_HPHA 1
#define AZCORE_SYSTEM_ALLOCATOR_MALLOC 2
#define AZCORE_SYSTEM_ALLOCATOR_HEAP 3
#if !defined(AZCORE_SYSTEM_ALLOCATOR)
// define the default
@@ -29,8 +28,6 @@
#include <AzCore/Memory/HphaSchema.h>
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
#include <AzCore/Memory/MallocSchema.h>
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
#include <AzCore/Memory/HeapSchema.h>
#else
#error "Invalid allocator selected for SystemAllocator"
#endif
@@ -44,8 +41,6 @@ namespace AZ
static AZStd::aligned_storage<sizeof(HphaSchema), AZStd::alignment_of<HphaSchema>::value>::type g_systemSchema;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
static AZStd::aligned_storage<sizeof(MallocSchema), AZStd::alignment_of<MallocSchema>::value>::type g_systemSchema;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
static AZStd::aligned_storage<sizeof(HeapSchema), AZStd::alignment_of<HeapSchema>::value>::type g_systemSchema;
#endif
//////////////////////////////////////////////////////////////////////////
@@ -118,11 +113,6 @@ namespace AZ
heapDesc.m_systemChunkSize = desc.m_heap.m_systemChunkSize;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
MallocSchema::Descriptor heapDesc;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
HeapSchema::Descriptor heapDesc;
memcpy(heapDesc.m_memoryBlocks, desc.m_heap.m_memoryBlocks, sizeof(heapDesc.m_memoryBlocks));
memcpy(heapDesc.m_memoryBlocksByteSize, desc.m_heap.m_memoryBlocksByteSize, sizeof(heapDesc.m_memoryBlocksByteSize));
heapDesc.m_numMemoryBlocks = desc.m_heap.m_numMemoryBlocks;
#endif
if (&AllocatorInstance<SystemAllocator>::Get() == this) // if we are the system allocator
{
@@ -132,8 +122,6 @@ namespace AZ
m_allocator = new (&g_systemSchema) HphaSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = new (&g_systemSchema) MallocSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
m_allocator = new (&g_systemSchema) HeapSchema(heapDesc);
#endif
g_isSystemSchemaUsed = true;
isReady = true;
@@ -149,8 +137,6 @@ namespace AZ
m_allocator = azcreate(HphaSchema, (heapDesc), SystemAllocator);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = azcreate(MallocSchema, (heapDesc), SystemAllocator);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
m_allocator = azcreate(HeapSchema, (heapDesc), SystemAllocator);
#endif
if (m_allocator == nullptr)
{
@@ -186,8 +172,6 @@ namespace AZ
static_cast<HphaSchema*>(m_allocator)->~HphaSchema();
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
static_cast<HeapSchema*>(m_allocator)->~HeapSchema();
#endif
g_isSystemSchemaUsed = false;
}
@@ -45,9 +45,8 @@ namespace AZ
}
else
{
SerializerMap::const_iterator serializerIter = m_jsonSerializers.find(typeId);
AZ_Assert(serializerIter != m_jsonSerializers.end(), "Attempting to unregister a serializer that has not been registered yet with typeid %s", typeId.ToString<AZStd::string>().c_str());
m_jsonSerializers.erase(serializerIter);
[[maybe_unused]] size_t erased = m_jsonSerializers.erase(typeId);
AZ_Assert(erased == 1, "Attempting to unregister a serializer that has not been registered yet with typeid %s", typeId.ToString<AZStd::string>().c_str());
return SerializerBuilder(this, m_jsonSerializers.end());
}
}
+5
View File
@@ -146,6 +146,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
PROPERTY COMPILE_DEFINITIONS
VALUES AZCORETEST_DLL_NAME=\"$<TARGET_FILE_NAME:AzCore.Tests>\"
)
ly_add_target_files(
TARGETS AzCore.Tests
FILES ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Memory/AllocatorBenchmarkRecordings.bin
OUTPUT_SUBDIRECTORY Tests/AzCore/Memory
)
endif()
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:281ba03e79ecba90b313a0b17bdba87c57d76b504b6e38d579b5eabd995902cc
size 245760
@@ -0,0 +1,591 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/PlatformIncl.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/BestFitExternalMapAllocator.h>
#include <AzCore/Memory/HeapSchema.h>
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/Memory/MallocSchema.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Memory/PoolSchema.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Utils/Utils.h>
#include <benchmark/benchmark.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes();
size_t GetMemorySize(void* memory);
}
/// <summary>
/// Test allocator wrapper that redirects the calls to the passed TAllocator by using AZ::AllocatorInstance.
/// It also creates/destroys the TAllocator type (to reflect what happens at runtime)
/// </summary>
/// <typeparam name="TAllocator">Allocator type to wrap</typeparam>
template<typename TAllocator>
class TestAllocatorWrapper
{
public:
static void SetUp()
{
AZ::AllocatorInstance<TAllocator>::Create();
}
static void TearDown()
{
AZ::AllocatorInstance<TAllocator>::Destroy();
}
static void* Allocate(size_t byteSize, size_t alignment)
{
return AZ::AllocatorInstance<TAllocator>::Get().Allocate(byteSize, alignment);
}
static void DeAllocate(void* ptr, size_t byteSize = 0)
{
AZ::AllocatorInstance<TAllocator>::Get().DeAllocate(ptr, byteSize);
}
static void* ReAllocate(void* ptr, size_t newSize, size_t newAlignment)
{
return AZ::AllocatorInstance<TAllocator>::Get().ReAllocate(ptr, newSize, newAlignment);
}
static size_t Resize(void* ptr, size_t newSize)
{
return AZ::AllocatorInstance<TAllocator>::Get().Resize(ptr, newSize);
}
static void GarbageCollect()
{
AZ::AllocatorInstance<TAllocator>::Get().GarbageCollect();
}
static size_t NumAllocatedBytes()
{
return AZ::AllocatorInstance<TAllocator>::Get().NumAllocatedBytes() +
AZ::AllocatorInstance<TAllocator>::Get().GetUnAllocatedMemory();
}
static size_t GetSize(void* ptr)
{
return AZ::AllocatorInstance<TAllocator>::Get().AllocationSize(ptr);
}
};
/// <summary>
/// Basic allocator used as a baseline. This allocator is the most basic allocation possible with the OS (AZ_OS_MALLOC).
/// MallocSchema cannot be used here because it has extra logic that we don't want to use as a baseline.
/// </summary>
class RawMallocAllocator {};
template<>
class TestAllocatorWrapper<RawMallocAllocator>
{
public:
TestAllocatorWrapper()
{
s_numAllocatedBytes = 0;
}
static void SetUp()
{
s_numAllocatedBytes = 0;
}
static void TearDown()
{
}
// IAllocatorAllocate
static void* Allocate(size_t byteSize, size_t)
{
s_numAllocatedBytes += byteSize;
// Don't pass an alignment since we wont be able to get the memory size without also passing the alignment
return AZ_OS_MALLOC(byteSize, 1);
}
static void DeAllocate(void* ptr, size_t = 0)
{
s_numAllocatedBytes -= Platform::GetMemorySize(ptr);
AZ_OS_FREE(ptr);
}
static void* ReAllocate(void* ptr, size_t newSize, size_t)
{
s_numAllocatedBytes -= Platform::GetMemorySize(ptr);
AZ_OS_FREE(ptr);
s_numAllocatedBytes += newSize;
return AZ_OS_MALLOC(newSize, 1);
}
static size_t Resize(void* ptr, size_t newSize)
{
AZ_UNUSED(ptr);
AZ_UNUSED(newSize);
return 0;
}
static void GarbageCollect() {}
static size_t NumAllocatedBytes()
{
return s_numAllocatedBytes;
}
static size_t GetSize(void* ptr)
{
return Platform::GetMemorySize(ptr);
}
private:
static size_t s_numAllocatedBytes;
};
size_t TestAllocatorWrapper<RawMallocAllocator>::s_numAllocatedBytes = 0;
// Some allocator are not fully declared, those we simply setup from the schema
class MallocSchemaAllocator : public AZ::SimpleSchemaAllocator<AZ::MallocSchema>
{
public:
AZ_TYPE_INFO(MallocSchemaAllocator, "{3E68224F-E676-402C-8276-CE4B49C05E89}");
MallocSchemaAllocator()
: AZ::SimpleSchemaAllocator<AZ::MallocSchema>("MallocSchemaAllocator", "")
{}
};
// We use both this HphaSchemaAllocator and the SystemAllocator configured with Hpha because the SystemAllocator
// has extra things
class HphaSchemaAllocator : public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
{
public:
AZ_TYPE_INFO(HphaSchemaAllocator, "{6563AB4B-A68E-4499-8C98-D61D640D1F7F}");
HphaSchemaAllocator()
: AZ::SimpleSchemaAllocator<AZ::HphaSchema>("TestHphaSchemaAllocator", "")
{}
};
// For the SystemAllocator we inherit so we have a different stack. The SystemAllocator is used globally so we dont want
// to get that data affecting the benchmark
class TestSystemAllocator : public AZ::SystemAllocator
{
public:
AZ_TYPE_INFO(TestSystemAllocator, "{360D4DAA-D65D-4D5C-A6FA-1A4C5261C35C}");
TestSystemAllocator()
: AZ::SystemAllocator()
{
}
};
// Allocated bytes reported by the allocator
static const char* s_counterAllocatorMemory = "Allocator_Memory";
// Allocated bytes as counted by the benchmark
static const char* s_counterBenchmarkMemory = "Benchmark_Memory";
enum AllocationSize
{
SMALL,
BIG,
MIXED,
COUNT
};
static const size_t s_kiloByte = 1024;
static const size_t s_megaByte = s_kiloByte * s_kiloByte;
using AllocationSizeArray = AZStd::array<size_t, 10>;
static const AZStd::array<AllocationSizeArray, COUNT> s_allocationSizes = {
/* SMALL */ AllocationSizeArray{ 2, 16, 20, 59, 100, 128, 160, 250, 300, 512 },
/* BIG */ AllocationSizeArray{ 513, s_kiloByte, 2 * s_kiloByte, 4 * s_kiloByte, 10 * s_kiloByte, 64 * s_kiloByte, 128 * s_kiloByte, 200 * s_kiloByte, s_megaByte, 2 * s_megaByte },
/* MIXED */ AllocationSizeArray{ 2, s_kiloByte, 59, 4 * s_kiloByte, 128, 200 * s_kiloByte, 250, s_megaByte, 512, 2 * s_megaByte }
};
template <typename TAllocator>
class AllocatorBenchmarkFixture
: public ::benchmark::Fixture
{
protected:
using TestAllocatorType = TestAllocatorWrapper<TAllocator>;
virtual void internalSetUp(const ::benchmark::State& state)
{
if (state.thread_index == 0) // Only setup in the first thread
{
TestAllocatorType::SetUp();
m_allocations.resize(state.threads);
for (auto& perThreadAllocations : m_allocations)
{
perThreadAllocations.resize(state.range(0), nullptr);
}
}
}
virtual void internalTearDown(const ::benchmark::State& state)
{
if (state.thread_index == 0) // Only setup in the first thread
{
m_allocations.clear();
m_allocations.shrink_to_fit();
TestAllocatorType::TearDown();
}
}
AZStd::vector<void*>& GetPerThreadAllocations(size_t threadIndex)
{
return m_allocations[threadIndex];
}
public:
void SetUp(const ::benchmark::State& state) override
{
internalSetUp(state);
}
void SetUp(::benchmark::State& state) override
{
internalSetUp(state);
}
void TearDown(const ::benchmark::State& state) override
{
internalTearDown(state);
}
void TearDown(::benchmark::State& state) override
{
internalTearDown(state);
}
private:
AZStd::vector<AZStd::vector<void*>> m_allocations;
};
template <typename TAllocator, AllocationSize TAllocationSize>
class AllocationBenchmarkFixture
: public AllocatorBenchmarkFixture<TAllocator>
{
using base = AllocatorBenchmarkFixture<TAllocator>;
using TestAllocatorType = typename base::TestAllocatorType;
public:
void Benchmark(benchmark::State& state)
{
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
const size_t numberOfAllocations = perThreadAllocations.size();
size_t totalAllocationSize = 0;
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
totalAllocationSize += allocationSize;
state.ResumeTiming();
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
state.PauseTiming();
}
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
TestAllocatorType::DeAllocate(perThreadAllocations[allocationIndex], allocationSize);
perThreadAllocations[allocationIndex] = nullptr;
}
TestAllocatorType::GarbageCollect();
state.SetItemsProcessed(numberOfAllocations);
}
}
};
template <typename TAllocator, AllocationSize TAllocationSize>
class DeAllocationBenchmarkFixture
: public AllocatorBenchmarkFixture<TAllocator>
{
using base = AllocatorBenchmarkFixture<TAllocator>;
using TestAllocatorType = typename base::TestAllocatorType;
public:
void Benchmark(benchmark::State& state)
{
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
const size_t numberOfAllocations = perThreadAllocations.size();
size_t totalAllocationSize = 0;
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
totalAllocationSize += allocationSize;
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
}
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
state.ResumeTiming();
TestAllocatorType::DeAllocate(perThreadAllocations[allocationIndex], allocationSize);
state.PauseTiming();
perThreadAllocations[allocationIndex] = nullptr;
}
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
state.SetItemsProcessed(numberOfAllocations);
TestAllocatorType::GarbageCollect();
}
}
};
template<typename TAllocator>
class RecordedAllocationBenchmarkFixture : public ::benchmark::Fixture
{
using TestAllocatorType = TestAllocatorWrapper<TAllocator>;
virtual void internalSetUp()
{
TestAllocatorType::SetUp();
}
void internalTearDown()
{
TestAllocatorType::TearDown();
}
#pragma pack(push, 1)
struct alignas(1) AllocatorOperation
{
enum OperationType : size_t
{
ALLOCATE,
DEALLOCATE
};
OperationType m_type : 1;
size_t m_size : 28; // Can represent up to 256Mb requests
size_t m_alignment : 7; // Can represent up to 128 alignment
size_t m_recordId : 28; // Can represent up to 256M simultaneous requests, we reuse ids
};
#pragma pack(pop)
static_assert(sizeof(AllocatorOperation) == 8);
public:
void SetUp(const ::benchmark::State&) override
{
internalSetUp();
}
void SetUp(::benchmark::State&) override
{
internalSetUp();
}
void TearDown(const ::benchmark::State&) override
{
internalTearDown();
}
void TearDown(::benchmark::State&) override
{
internalTearDown();
}
void Benchmark(benchmark::State& state)
{
for (auto _ : state)
{
state.PauseTiming();
AZStd::unordered_map<size_t, void*> pointerRemapping;
constexpr size_t allocationOperationCount = 5 * 1024;
AZStd::array<AllocatorOperation, allocationOperationCount> m_operations = {};
[[maybe_unused]] const size_t operationSize = sizeof(AllocatorOperation);
size_t totalAllocationSize = 0;
size_t itemsProcessed = 0;
for (size_t i = 0; i < 100; ++i) // play the recording multiple times to get a good stable sample, this way we can keep a smaller recording
{
AZ::IO::SystemFile file;
AZ::IO::FixedMaxPathString filePath = AZ::Utils::GetExecutableDirectory();
filePath += "/Tests/AzCore/Memory/AllocatorBenchmarkRecordings.bin";
if (!file.Open(filePath.c_str(), AZ::IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
return;
}
size_t elementsRead =
file.Read(sizeof(AllocatorOperation) * allocationOperationCount, &m_operations) / sizeof(AllocatorOperation);
itemsProcessed += elementsRead;
while (elementsRead > 0)
{
for (size_t operationIndex = 0; operationIndex < elementsRead; ++operationIndex)
{
const AllocatorOperation& operation = m_operations[operationIndex];
if (operation.m_type == AllocatorOperation::ALLOCATE)
{
const auto it = pointerRemapping.emplace(operation.m_recordId, nullptr);
if (it.second) // otherwise already allocated
{
state.ResumeTiming();
void* ptr = TestAllocatorType::Allocate(operation.m_size, operation.m_alignment);
state.PauseTiming();
totalAllocationSize += operation.m_size;
it.first->second = ptr;
}
else
{
// Doing a resize, dont account for this memory change, this operation is rare and we dont have
// the size of the previous allocation
state.ResumeTiming();
TestAllocatorType::Resize(it.first->second, operation.m_size);
state.PauseTiming();
}
}
else // AllocatorOperation::DEALLOCATE:
{
if (operation.m_recordId)
{
const auto ptrIt = pointerRemapping.find(operation.m_recordId);
if (ptrIt != pointerRemapping.end())
{
totalAllocationSize -= operation.m_size;
state.ResumeTiming();
TestAllocatorType::DeAllocate(
ptrIt->second,
/*operation.m_size*/ 0); // size is not correct after a resize, a 0 size deals with it
state.PauseTiming();
pointerRemapping.erase(ptrIt);
}
}
else // deallocate(nullptr) are recorded
{
// Just to account of the call of deallocate(nullptr);
state.ResumeTiming();
TestAllocatorType::DeAllocate(nullptr, /*operation.m_size*/ 0);
state.PauseTiming();
}
}
}
elementsRead =
file.Read(sizeof(AllocatorOperation) * allocationOperationCount, &m_operations) / sizeof(AllocatorOperation);
itemsProcessed += elementsRead;
}
file.Close();
// Deallocate the remainder (since we stopped the recording middle-game)(there are leaks as well)
for (const auto& pointerMapping : pointerRemapping)
{
state.ResumeTiming();
TestAllocatorType::DeAllocate(pointerMapping.second);
state.PauseTiming();
}
itemsProcessed += pointerRemapping.size();
pointerRemapping.clear();
}
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
state.SetItemsProcessed(itemsProcessed);
TestAllocatorType::GarbageCollect();
}
}
};
// For non-threaded ranges, run 100, 400, 1600 amounts
static void RunRanges(benchmark::internal::Benchmark* b)
{
for (int i = 0; i < 6; i += 2)
{
b->Arg((1 << i) * 100);
}
}
static void RecordedRunRanges(benchmark::internal::Benchmark* b)
{
b->Iterations(1);
}
// For threaded ranges, run just 200, multi-threaded will already multiply by thread
static void ThreadedRunRanges(benchmark::internal::Benchmark* b)
{
b->Arg(100);
}
// Test under and over-subscription of threads vs the amount of CPUs available
static const unsigned int MaxThreadRange = 2 * AZStd::thread::hardware_concurrency();
#define BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME, ...) \
BENCHMARK_TEMPLATE_DEFINE_F(FIXTURE, TESTNAME, __VA_ARGS__)(benchmark::State& state) { Benchmark(state); } \
BENCHMARK_REGISTER_F(FIXTURE, TESTNAME)
// We test small/big/mixed allocations in single-threaded environments. For multi-threaded environments, we test mixed since
// the multi threaded fixture will run multiple passes (1, 2, 4, ... until 2*hardware_concurrency)
#define BM_REGISTER_SIZE_FIXTURES(FIXTURE, TESTNAME, ALLOCATORTYPE) \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_SMALL, ALLOCATORTYPE, SMALL)->Apply(RunRanges); \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_BIG, ALLOCATORTYPE, BIG)->Apply(RunRanges); \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_MIXED, ALLOCATORTYPE, MIXED)->Apply(RunRanges); \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_MIXED_THREADED, ALLOCATORTYPE, MIXED)->ThreadRange(2, MaxThreadRange)->Apply(ThreadedRunRanges);
#define BM_REGISTER_ALLOCATOR(TESTNAME, ALLOCATORTYPE) \
namespace BM_##TESTNAME \
{ \
BM_REGISTER_SIZE_FIXTURES(AllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE); \
BM_REGISTER_SIZE_FIXTURES(DeAllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE); \
BM_REGISTER_TEMPLATE(RecordedAllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE)->Apply(RecordedRunRanges); \
}
/// Warm up benchmark used to prepare the OS for allocations. Most OS keep allocations for a process somehow
/// reserved. So the first allocations run always get a bigger impact in a process. This warm up allocator runs
/// all the benchmarks and is just used for the the next allocators to report more consistent results.
BM_REGISTER_ALLOCATOR(WarmUpAllocator, RawMallocAllocator);
BM_REGISTER_ALLOCATOR(RawMallocAllocator, RawMallocAllocator);
BM_REGISTER_ALLOCATOR(MallocSchemaAllocator, MallocSchemaAllocator);
BM_REGISTER_ALLOCATOR(HphaSchemaAllocator, HphaSchemaAllocator);
BM_REGISTER_ALLOCATOR(SystemAllocator, TestSystemAllocator);
//BM_REGISTER_ALLOCATOR(BestFitExternalMapAllocator, BestFitExternalMapAllocator); // Requires to pre-allocate blocks and cannot work as a general-purpose allocator
//BM_REGISTER_ALLOCATOR(HeapSchemaAllocator, TestHeapSchemaAllocator); // Requires to pre-allocate blocks and cannot work as a general-purpose allocator
//BM_REGISTER_SCHEMA(PoolSchema); // Requires special alignment requests while allocating
#undef BM_REGISTER_ALLOCATOR
#undef BM_REGISTER_SIZE_FIXTURES
#undef BM_REGISTER_TEMPLATE
} // Benchmark
#endif // HAVE_BENCHMARK
@@ -10,10 +10,6 @@
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/std/containers/vector.h>
#if defined(HAVE_BENCHMARK)
#include <benchmark/benchmark.h>
#endif // HAVE_BENCHMARK
class HphaSchema_TestAllocator
: public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
{
@@ -112,87 +108,3 @@ namespace UnitTest
HphaSchemaTestFixture,
::testing::ValuesIn(s_mixedInstancesParameters));
}
#if defined(HAVE_BENCHMARK)
namespace Benchmark
{
class HphaSchemaBenchmarkFixture
: public ::benchmark::Fixture
{
void internalSetUp()
{
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Create();
}
void internalTearDown()
{
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Destroy();
}
public:
void SetUp(const benchmark::State&) override
{
internalSetUp();
}
void SetUp(benchmark::State&) override
{
internalSetUp();
}
void TearDown(const benchmark::State&) override
{
internalTearDown();
}
void TearDown(benchmark::State&) override
{
internalTearDown();
}
static void BM_Allocations(benchmark::State& state, const AllocationSizeArray& allocationArray)
{
AZStd::vector<void*> allocations;
while (state.KeepRunning())
{
state.PauseTiming();
const size_t allocationIndex = allocations.size();
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
state.ResumeTiming();
void* allocation = AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().Allocate(allocationSize, 0);
state.PauseTiming();
allocations.emplace_back(allocation);
state.ResumeTiming();
}
const size_t numberOfAllocations = allocations.size();
state.SetItemsProcessed(numberOfAllocations);
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().DeAllocate(allocations[allocationIndex], allocationArray[allocationIndex % allocationArray.size()]);
}
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().GarbageCollect();
}
};
// Small allocations, these are allocations that are going to end up in buckets in the HphaSchema
BENCHMARK_F(HphaSchemaBenchmarkFixture, SmallAllocations)(benchmark::State& state)
{
BM_Allocations(state, s_smallAllocationSizes);
}
BENCHMARK_F(HphaSchemaBenchmarkFixture, BigAllocations)(benchmark::State& state)
{
BM_Allocations(state, s_bigAllocationSizes);
}
BENCHMARK_F(HphaSchemaBenchmarkFixture, MixedAllocations)(benchmark::State& state)
{
BM_Allocations(state, s_mixedAllocationSizes);
}
} // Benchmark
#endif // HAVE_BENCHMARK
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/Trace.h>
#include <malloc.h>
#include <sys/resource.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes()
{
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
return rusage.ru_maxrss * 1024L;
}
size_t GetMemorySize(void* memory)
{
return memory ? malloc_usable_size(memory) : 0;
}
}
}
@@ -8,4 +8,5 @@
set(FILES
Tests/UtilsTests_Android.cpp
Tests/Memory/AllocatorBenchmarks_Android.cpp
)
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/Trace.h>
#include <malloc.h>
#include <sys/resource.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes()
{
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
return rusage.ru_maxrss * 1024L;
}
size_t GetMemorySize(void* memory)
{
return memory ? malloc_usable_size(memory) : 0;
}
}
}
@@ -9,4 +9,5 @@
set(FILES
Tests/UtilsTests_Linux.cpp
../Common/UnixLike/Tests/UtilsTests_UnixLike.cpp
Tests/Memory/AllocatorBenchmarks_Linux.cpp
)
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/Trace.h>
#include <malloc/malloc.h>
#include <sys/resource.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes()
{
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
return rusage.ru_maxrss;
}
size_t GetMemorySize(void* memory)
{
return memory ? malloc_size(memory) : 0;
}
}
}
@@ -9,4 +9,5 @@
set(FILES
../Common/Apple/Tests/UtilsTests_Apple.cpp
../Common/UnixLike/Tests/UtilsTests_UnixLike.cpp
Tests/Memory/AllocatorBenchmarks_Mac.cpp
)
@@ -0,0 +1,40 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/Trace.h>
#include <malloc.h>
#include <psapi.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes()
{
EmptyWorkingSet(GetCurrentProcess());
size_t memoryUsage = 0;
MEMORY_BASIC_INFORMATION mbi = { 0 };
unsigned char* pEndRegion = nullptr;
while (sizeof(mbi) == VirtualQuery(pEndRegion, &mbi, sizeof(mbi))) {
pEndRegion += mbi.RegionSize;
if ((mbi.AllocationProtect & PAGE_READWRITE) && (mbi.State & MEM_COMMIT)) {
memoryUsage += mbi.RegionSize;
}
}
return memoryUsage;
}
size_t GetMemorySize(void* memory)
{
return memory ? _aligned_msize(memory, 1, 0) : 0;
}
}
}
@@ -9,6 +9,7 @@
set(FILES
../Common/WinAPI/Tests/UtilsTests_WinAPI.cpp
Tests/IO/Streamer/StorageDriveTests_Windows.cpp
Tests/Memory/AllocatorBenchmarks_Windows.cpp
Tests/Memory/OverrunDetectionAllocator_Windows.cpp
Tests/Serialization_Windows.cpp
)
@@ -327,17 +327,13 @@ namespace JsonSerializationTests
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
}
#if GTEST_HAS_DEATH_TEST
using JsonSerializationDeathTests = JsonRegistrationContextTests;
TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts)
TEST_F(JsonRegistrationContextTests, DoubleUnregisterSerializer_Asserts)
{
ASSERT_DEATH({
SerializerWithOneType::Reflect(m_jsonRegistrationContext.get());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
}, ".*"
);
SerializerWithOneType::Reflect(m_jsonRegistrationContext.get());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
AZ_TEST_START_ASSERTTEST;
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
AZ_TEST_STOP_ASSERTTEST(1);
}
#endif // GTEST_HAS_DEATH_TEST
} //namespace JsonSerializationTests
@@ -170,6 +170,7 @@ set(FILES
Math/Vector3Tests.cpp
Math/Vector4PerformanceTests.cpp
Math/Vector4Tests.cpp
Memory/AllocatorBenchmarks.cpp
Memory/AllocatorManager.cpp
Memory/HphaSchema.cpp
Memory/HphaSchemaErrorDetection.cpp
@@ -8,4 +8,6 @@
set(FILES
native/FileWatcher/FileWatcher_linux.cpp
native/FileWatcher/FileWatcher_linux.h
native/FileWatcher/FileWatcher_platform.h
)
@@ -5,7 +5,10 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/string/fixed_string.h>
#include <native/FileWatcher/FileWatcher.h>
#include <native/FileWatcher/FileWatcher_platform.h>
#include <QDirIterator>
#include <QHash>
@@ -15,155 +18,127 @@
#include <sys/inotify.h>
static constexpr int s_handleToFolderMapLockTimeout = 1000; // 1 sec timeout for obtaining the handle to folder map lock
static constexpr size_t s_iNotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time
static constexpr size_t s_iNotifyEventSize = sizeof(struct inotify_event);
static constexpr size_t s_iNotifyReadBufferSize = s_iNotifyMaxEntries * s_iNotifyEventSize;
static constexpr size_t s_inotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time
static constexpr size_t s_inotifyEventSize = sizeof(struct inotify_event);
static constexpr size_t s_inotifyReadBufferSize = s_inotifyMaxEntries * s_inotifyEventSize;
struct FolderRootWatch::PlatformImplementation
bool FileWatcher::PlatformImplementation::Initialize()
{
PlatformImplementation() = default;
int m_iNotifyHandle = -1;
QMutex m_handleToFolderMapLock;
QHash<int, QString> m_handleToFolderMap;
bool Initialize()
if (m_inotifyHandle < 0)
{
if (m_iNotifyHandle < 0)
{
// The CLOEXEC flag prevents the inotify watchers from copying on fork/exec
m_iNotifyHandle = inotify_init1(IN_CLOEXEC);
}
return (m_iNotifyHandle >= 0);
// The CLOEXEC flag prevents the inotify watchers from copying on fork/exec
m_inotifyHandle = inotify_init1(IN_CLOEXEC);
[[maybe_unused]] const auto err = errno;
[[maybe_unused]] AZStd::fixed_string<255> errorString;
AZ_Warning("FileWatcher", (m_inotifyHandle >= 0), "Unable to initialize inotify, file monitoring will not be available: %s\n", strerror_r(err, errorString.data(), errorString.capacity()));
}
void Finalize()
{
if (m_iNotifyHandle >= 0)
{
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
QHashIterator<int, QString> iter(m_handleToFolderMap);
while (iter.hasNext())
{
iter.next();
int watchHandle = iter.key();
inotify_rm_watch(m_iNotifyHandle, watchHandle);
}
m_handleToFolderMap.clear();
m_handleToFolderMapLock.unlock();
::close(m_iNotifyHandle);
m_iNotifyHandle = -1;
}
}
void AddWatchFolder(QString folder)
{
if (m_iNotifyHandle >= 0)
{
// Clean up the path before accepting it as a watch folder
QString cleanPath = QDir::cleanPath(folder);
// Add the folder to watch and track it
int watchHandle = inotify_add_watch(m_iNotifyHandle,
cleanPath.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
m_handleToFolderMap[watchHandle] = cleanPath;
m_handleToFolderMapLock.unlock();
// Add all the subfolders to watch and track them
QDirIterator dirIter(folder, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (dirIter.hasNext())
{
QString dirName = dirIter.next();
if (dirName.endsWith("/.") || dirName.endsWith("/.."))
{
continue;
}
int watchHandle = inotify_add_watch(m_iNotifyHandle,
dirName.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
m_handleToFolderMap[watchHandle] = dirName;
m_handleToFolderMapLock.unlock();
}
}
}
void RemoveWatchFolder(int watchHandle)
{
if (m_iNotifyHandle >= 0)
{
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
QHash<int, QString>::iterator handleToRemove = m_handleToFolderMap.find(watchHandle);
if (handleToRemove != m_handleToFolderMap.end())
{
inotify_rm_watch(m_iNotifyHandle, watchHandle);
m_handleToFolderMap.erase(handleToRemove);
}
m_handleToFolderMapLock.unlock();
}
}
};
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
FolderRootWatch::FolderRootWatch(const QString rootFolder)
: m_root(rootFolder)
, m_shutdownThreadSignal(false)
, m_fileWatcher(nullptr)
, m_platformImpl(new PlatformImplementation())
{
return (m_inotifyHandle >= 0);
}
FolderRootWatch::~FolderRootWatch()
void FileWatcher::PlatformImplementation::Finalize()
{
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
Stop();
if (m_inotifyHandle < 0)
{
return;
}
delete m_platformImpl;
{
QMutexLocker lock{&m_handleToFolderMapLock};
for (const auto& watchHandle : m_handleToFolderMap.keys())
{
inotify_rm_watch(m_inotifyHandle, watchHandle);
}
m_handleToFolderMap.clear();
}
::close(m_inotifyHandle);
m_inotifyHandle = -1;
}
bool FolderRootWatch::Start()
void FileWatcher::PlatformImplementation::AddWatchFolder(QString folder, bool recursive)
{
if (m_inotifyHandle < 0)
{
return;
}
// Clean up the path before accepting it as a watch folder
QString cleanPath = QDir::cleanPath(folder);
// Add the folder to watch and track it
int watchHandle = inotify_add_watch(m_inotifyHandle,
cleanPath.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
if (watchHandle < 0)
{
[[maybe_unused]] const auto err = errno;
[[maybe_unused]] AZStd::fixed_string<255> errorString;
AZ_Warning("FileWatcher", false, "inotify_add_watch failed for path %s: %s", cleanPath.toUtf8().constData(), strerror_r(err, errorString.data(), errorString.capacity()));
return;
}
{
QMutexLocker lock{&m_handleToFolderMapLock};
m_handleToFolderMap[watchHandle] = cleanPath;
}
// Add all the contents (files and directories) to watch and track them
QDirIterator dirIter(folder, QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files, (recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags) | QDirIterator::FollowSymlinks);
while (dirIter.hasNext())
{
QString dirName = dirIter.next();
watchHandle = inotify_add_watch(m_inotifyHandle,
dirName.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
if (watchHandle < 0)
{
[[maybe_unused]] const auto err = errno;
[[maybe_unused]] AZStd::fixed_string<255> errorString;
AZ_Warning("FileWatcher", false, "inotify_add_watch failed for path %s: %s", dirName.toUtf8().constData(), strerror_r(err, errorString.data(), errorString.capacity()));
return;
}
QMutexLocker lock{&m_handleToFolderMapLock};
m_handleToFolderMap[watchHandle] = dirName;
}
}
void FileWatcher::PlatformImplementation::RemoveWatchFolder(int watchHandle)
{
if (m_inotifyHandle < 0)
{
return;
}
QMutexLocker lock{&m_handleToFolderMapLock};
if (m_handleToFolderMap.remove(watchHandle))
{
inotify_rm_watch(m_inotifyHandle, watchHandle);
}
}
bool FileWatcher::PlatformStart()
{
// inotify will be used by linux to monitor file changes within directories under the root folder
if (!m_platformImpl->Initialize())
{
return false;
}
m_platformImpl->AddWatchFolder(m_root);
for (const auto& [directory, recursive] : m_folderWatchRoots)
{
if (QDir(directory).exists())
{
m_platformImpl->AddWatchFolder(directory, recursive);
}
}
m_shutdownThreadSignal = false;
m_thread = std::thread([this]() { WatchFolderLoop(); });
return true;
}
void FolderRootWatch::Stop()
void FileWatcher::PlatformStop()
{
m_shutdownThreadSignal = true;
@@ -172,64 +147,78 @@ void FolderRootWatch::Stop()
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
}
void FolderRootWatch::WatchFolderLoop()
void FileWatcher::WatchFolderLoop()
{
char eventBuffer[s_iNotifyReadBufferSize];
char eventBuffer[s_inotifyReadBufferSize];
while (!m_shutdownThreadSignal)
{
ssize_t bytesRead = ::read(m_platformImpl->m_iNotifyHandle, eventBuffer, s_iNotifyReadBufferSize);
ssize_t bytesRead = ::read(m_platformImpl->m_inotifyHandle, eventBuffer, s_inotifyReadBufferSize);
if (bytesRead < 0)
{
// Break out of the loop when the notify handle was closed (outside of this thread)
break;
}
else if (bytesRead > 0)
if (!bytesRead)
{
for (size_t index=0; index<bytesRead;)
continue;
}
for (size_t index=0; index<bytesRead;)
{
const auto* event = reinterpret_cast<inotify_event*>(&eventBuffer[index]);
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE))
{
struct inotify_event *event = ( struct inotify_event * ) &eventBuffer[ index ];
const QString pathStr = QDir(m_platformImpl->m_handleToFolderMap[event->wd]).absoluteFilePath(event->name);
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE ))
if (event->mask & (IN_CREATE | IN_MOVED_TO))
{
QString pathStr = QString("%1%2%3").arg(m_platformImpl->m_handleToFolderMap[event->wd], QDir::separator(), event->name);
if (event->mask & IN_ISDIR)
{
// New Directory, see if it should be added to the watched directories
// It is only added if it is a child of a recursively watched directory
const auto found = AZStd::find_if(begin(m_folderWatchRoots), end(m_folderWatchRoots), [this, event](const WatchRoot& watchRoot)
{
return watchRoot.m_directory == m_platformImpl->m_handleToFolderMap[event->wd];
});
if (event->mask & (IN_CREATE | IN_MOVED_TO))
{
if ( event->mask & IN_ISDIR )
// If the path is not in m_folderWatchRoots, it must
// be a new subdirectory of a subdirectory of some
// other root that is being watched recursively.
// Maintain the recursive nature of that root.
const bool shouldAddFolder = (found == end(m_folderWatchRoots)) ? true : found->m_recursive;
if (shouldAddFolder)
{
// New Directory, add it to the watch
m_platformImpl->AddWatchFolder(pathStr);
}
else
{
ProcessNewFileEvent(pathStr);
m_platformImpl->AddWatchFolder(pathStr, true);
}
}
else if (event->mask & (IN_DELETE | IN_MOVED_FROM))
else
{
if (event->mask & IN_ISDIR)
{
// Directory Deleted, remove it from the watch
m_platformImpl->RemoveWatchFolder(event->wd);
}
else
{
ProcessDeleteFileEvent(pathStr);
}
}
else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR))
{
ProcessModifyFileEvent(pathStr);
rawFileAdded(pathStr, {});
}
}
index += s_iNotifyEventSize + event->len;
else if (event->mask & (IN_DELETE | IN_MOVED_FROM))
{
if (event->mask & IN_ISDIR)
{
// Directory Deleted, remove it from the watch
m_platformImpl->RemoveWatchFolder(event->wd);
}
else
{
rawFileRemoved(pathStr, {});
}
}
else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR))
{
rawFileModified(pathStr, {});
}
}
index += s_inotifyEventSize + event->len;
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* 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 <FileWatcher/FileWatcher.h>
#include <QMutex>
#include <QHash>
class FileWatcher::PlatformImplementation
{
public:
bool Initialize();
void Finalize();
void AddWatchFolder(QString folder, bool recursive);
void RemoveWatchFolder(int watchHandle);
int m_inotifyHandle = -1;
QMutex m_handleToFolderMapLock;
QHash<int, QString> m_handleToFolderMap;
};
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <native/FileWatcher/FileWatcher_linux.h>
@@ -8,4 +8,6 @@
set(FILES
native/FileWatcher/FileWatcher_macos.cpp
native/FileWatcher/FileWatcher_mac.h
native/FileWatcher/FileWatcher_platform.h
)
@@ -0,0 +1,20 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* 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 <native/FileWatcher/FileWatcher.h>
#include <CoreServices/CoreServices.h>
class FileWatcher::PlatformImplementation
{
public:
FSEventStreamRef m_stream = nullptr;
CFRunLoopRef m_runLoop = nullptr;
};
@@ -6,47 +6,23 @@
*
*/
#include <native/FileWatcher/FileWatcher.h>
#include <native/FileWatcher/FileWatcher_platform.h>
#include <native/utilities/BatchApplicationManager.h>
#include <AzCore/Debug/Trace.h>
#include <CoreServices/CoreServices.h>
void FileEventStreamCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]);
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() : m_stream(nullptr), m_runLoop(nullptr) { }
FSEventStreamRef m_stream;
CFRunLoopRef m_runLoop;
QString m_renameFileDirectory;
};
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
FolderRootWatch::FolderRootWatch(const QString rootFolder)
: m_root(rootFolder)
, m_shutdownThreadSignal(false)
, m_fileWatcher(nullptr)
, m_platformImpl(new PlatformImplementation())
{
}
FolderRootWatch::~FolderRootWatch()
{
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
Stop();
delete m_platformImpl;
}
bool FolderRootWatch::Start()
bool FileWatcher::PlatformStart()
{
m_shutdownThreadSignal = false;
CFStringRef rootPath = CFStringCreateWithCString(kCFAllocatorDefault, m_root.toStdString().data(), kCFStringEncodingMacRoman);
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&rootPath, 1, NULL);
CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, this->m_folderWatchRoots.size(), nullptr);
for (const auto& root : this->m_folderWatchRoots)
{
CFArrayAppendValue(pathsToWatch, root.m_directory.toCFString());
}
// The larger this number, the larger the delay between the kernel knowing a file changed
// and us actually consuming the event. It is very important for asset processor to deal with
@@ -60,11 +36,12 @@ bool FolderRootWatch::Start()
// Set ourselves as the value for the context info field so that in the callback
// we get passed into it and the callback can call our public API to handle
// the file change events
FSEventStreamContext streamContext;
::memset(&streamContext, 0, sizeof(streamContext));
streamContext.info = this;
FSEventStreamContext streamContext{
/*.version =*/ 0,
/*.info =*/ this,
};
m_platformImpl->m_stream = FSEventStreamCreate(NULL,
m_platformImpl->m_stream = FSEventStreamCreate(nullptr,
FileEventStreamCallback,
&streamContext,
pathsToWatch,
@@ -72,24 +49,25 @@ bool FolderRootWatch::Start()
timeBetweenKernelUpdateAndNotification,
kFSEventStreamCreateFlagFileEvents);
AZ_Error("FileWatcher", (m_platformImpl->m_stream != nullptr), "FSEventStreamCreate returned a nullptr. No file events will be reported for %s", m_root.toStdString().c_str());
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
AZ_Error("FileWatcher", (m_platformImpl->m_stream != nullptr), "FSEventStreamCreate returned a nullptr. No file events will be reported.");
const CFIndex pathCount = CFArrayGetCount(pathsToWatch);
for(CFIndex i = 0; i < pathCount; ++i)
{
CFRelease(CFArrayGetValueAtIndex(pathsToWatch, i));
}
CFRelease(pathsToWatch);
CFRelease(rootPath);
return (m_platformImpl->m_stream != nullptr);
return m_platformImpl->m_stream != nullptr;
}
void FolderRootWatch::Stop()
void FileWatcher::PlatformStop()
{
m_shutdownThreadSignal = true;
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
FSEventStreamStop(m_platformImpl->m_stream);
@@ -97,7 +75,7 @@ void FolderRootWatch::Stop()
FSEventStreamRelease(m_platformImpl->m_stream);
}
void FolderRootWatch::WatchFolderLoop()
void FileWatcher::WatchFolderLoop()
{
// Use a half second timeout interval so that we can check if
// m_shutdownThreadSignal has been changed while we were running the RunLoop
@@ -117,14 +95,14 @@ void FolderRootWatch::WatchFolderLoop()
void FileEventStreamCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
{
FolderRootWatch* watcher = reinterpret_cast<FolderRootWatch*>(clientCallBackInfo);
auto* watcher = reinterpret_cast<FileWatcher*>(clientCallBackInfo);
const char** filePaths = reinterpret_cast<const char**>(eventPaths);
for (int i = 0; i < numEvents; ++i)
{
QFileInfo fileInfo(QDir::cleanPath(filePaths[i]));
QString fileAndPath = fileInfo.absoluteFilePath();
const QFileInfo fileInfo(QDir::cleanPath(filePaths[i]));
const QString fileAndPath = fileInfo.absoluteFilePath();
if (!fileInfo.isHidden())
{
@@ -133,38 +111,38 @@ void FileEventStreamCallback(ConstFSEventStreamRef streamRef, void *clientCallBa
// so check for all of them
if (eventFlags[i] & kFSEventStreamEventFlagItemCreated)
{
watcher->ProcessNewFileEvent(fileAndPath);
watcher->rawFileAdded(fileAndPath, {});
}
if (eventFlags[i] & kFSEventStreamEventFlagItemModified)
{
watcher->ProcessModifyFileEvent(fileAndPath);
watcher->rawFileModified(fileAndPath, {});
}
if (eventFlags[i] & kFSEventStreamEventFlagItemRemoved)
{
watcher->ProcessDeleteFileEvent(fileAndPath);
watcher->rawFileRemoved(fileAndPath, {});
}
if (eventFlags[i] & kFSEventStreamEventFlagItemRenamed)
{
if (fileInfo.exists())
{
watcher->ProcessNewFileEvent(fileAndPath);
watcher->rawFileAdded(fileAndPath, {});
// macOS does not send out an event for the directory being
// modified when a file has been renamed but the FileWatcher
// API expects it so send out the modification event ourselves.
watcher->ProcessModifyFileEvent(fileInfo.absolutePath());
watcher->rawFileModified(fileInfo.absolutePath(), {});
}
else
{
watcher->ProcessDeleteFileEvent(fileAndPath);
watcher->rawFileRemoved(fileAndPath, {});
// macOS does not send out an event for the directory being
// modified when a file has been renamed but the FileWatcher
// API expects it so send out the modification event ourselves.
watcher->ProcessModifyFileEvent(fileInfo.absolutePath());
watcher->rawFileModified(fileInfo.absolutePath(), {});
}
}
}
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <native/FileWatcher/FileWatcher_mac.h>
@@ -1,130 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <native/FileWatcher/FileWatcher.h>
#include <AzCore/PlatformIncl.h>
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { }
HANDLE m_directoryHandle;
HANDLE m_ioHandle;
};
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
FolderRootWatch::FolderRootWatch(const QString rootFolder)
: m_root(rootFolder)
, m_shutdownThreadSignal(false)
, m_fileWatcher(nullptr)
, m_platformImpl(new PlatformImplementation())
{
}
FolderRootWatch::~FolderRootWatch()
{
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
Stop();
delete m_platformImpl;
}
bool FolderRootWatch::Start()
{
m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE)
{
m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0);
if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE)
{
m_shutdownThreadSignal = false;
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
return true;
}
}
return false;
}
void FolderRootWatch::Stop()
{
m_shutdownThreadSignal = true;
CloseHandle(m_platformImpl->m_ioHandle);
m_platformImpl->m_ioHandle = nullptr;
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
CloseHandle(m_platformImpl->m_directoryHandle);
m_platformImpl->m_directoryHandle = nullptr;
}
void FolderRootWatch::WatchFolderLoop()
{
FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000];
QString path;
OVERLAPPED aOverlapped;
LPOVERLAPPED pOverlapped;
DWORD dwByteCount;
ULONG_PTR ulKey;
while (!m_shutdownThreadSignal)
{
::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList));
::memset(&aOverlapped, 0, sizeof(aOverlapped));
if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr))
{
//wait for up to a second for I/O to signal
dwByteCount = 0;
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE))
{
//if we are signaled to shutdown bypass
if (!m_shutdownThreadSignal && ulKey)
{
if (dwByteCount)
{
int offset = 0;
FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList;
do
{
pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset);
path.clear();
path.append(m_root);
path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2));
QString file = QDir::toNativeSeparators(QDir::cleanPath(path));
switch (pFileNotifyInformation->Action)
{
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME:
ProcessNewFileEvent(file);
break;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
ProcessDeleteFileEvent(file);
break;
case FILE_ACTION_MODIFIED:
ProcessModifyFileEvent(file);
break;
}
offset = pFileNotifyInformation->NextEntryOffset;
} while (offset);
}
}
}
}
}
}
@@ -7,6 +7,8 @@
#
set(FILES
native/FileWatcher/FileWatcher_win.cpp
native/FileWatcher/FileWatcher_platform.h
native/FileWatcher/FileWatcher_windows.cpp
native/FileWatcher/FileWatcher_windows.h
native/resource.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <native/FileWatcher/FileWatcher_windows.h>
@@ -1,130 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <native/FileWatcher/FileWatcher.h>
#include <AzCore/PlatformIncl.h>
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { }
HANDLE m_directoryHandle;
HANDLE m_ioHandle;
};
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
FolderRootWatch::FolderRootWatch(const QString rootFolder)
: m_root(rootFolder)
, m_shutdownThreadSignal(false)
, m_fileWatcher(nullptr)
, m_platformImpl(new PlatformImplementation())
{
}
FolderRootWatch::~FolderRootWatch()
{
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
Stop();
delete m_platformImpl;
}
bool FolderRootWatch::Start()
{
m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE)
{
m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0);
if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE)
{
m_shutdownThreadSignal = false;
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
return true;
}
}
return false;
}
void FolderRootWatch::Stop()
{
m_shutdownThreadSignal = true;
CloseHandle(m_platformImpl->m_ioHandle);
m_platformImpl->m_ioHandle = nullptr;
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
CloseHandle(m_platformImpl->m_directoryHandle);
m_platformImpl->m_directoryHandle = nullptr;
}
void FolderRootWatch::WatchFolderLoop()
{
FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000];
QString path;
OVERLAPPED aOverlapped;
LPOVERLAPPED pOverlapped;
DWORD dwByteCount;
ULONG_PTR ulKey;
while (!m_shutdownThreadSignal)
{
::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList));
::memset(&aOverlapped, 0, sizeof(aOverlapped));
if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr))
{
//wait for up to a second for I/O to signal
dwByteCount = 0;
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE))
{
//if we are signaled to shutdown bypass
if (!m_shutdownThreadSignal && ulKey)
{
if (dwByteCount)
{
int offset = 0;
FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList;
do
{
pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset);
path.clear();
path.append(m_root);
path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2));
QString file = QDir::toNativeSeparators(QDir::cleanPath(path));
switch (pFileNotifyInformation->Action)
{
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME:
ProcessNewFileEvent(file);
break;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
ProcessDeleteFileEvent(file);
break;
case FILE_ACTION_MODIFIED:
ProcessModifyFileEvent(file);
break;
}
offset = pFileNotifyInformation->NextEntryOffset;
} while (offset);
}
}
}
}
}
}
@@ -0,0 +1,153 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* 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 <AzCore/std/tuple.h>
#include <AzCore/std/utils.h>
#include <native/FileWatcher/FileWatcher.h>
#include <native/FileWatcher/FileWatcher_platform.h>
#include <QDir>
bool FileWatcher::PlatformStart()
{
m_shutdownThreadSignal = false;
bool allSucceeded = true;
for (const auto& [directory, recursive] : m_folderWatchRoots)
{
if (QDir(directory).exists())
{
allSucceeded &= m_platformImpl->AddWatchFolder(directory, recursive);
}
}
return allSucceeded;
}
bool FileWatcher::PlatformImplementation::AddWatchFolder(QString root, bool recursive)
{
HandleUniquePtr directoryHandle{::CreateFileW(
root.toStdWString().data(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
nullptr
)};
if (directoryHandle.get() == INVALID_HANDLE_VALUE)
{
AZ_Warning("FileWatcher", false, "Failed to start watching %s", root.toUtf8().constData());
return false;
}
// Associate this file handle with our existing io completion port handle
if (!::CreateIoCompletionPort(directoryHandle.get(), m_ioHandle.get(), /*CompletionKey =*/ static_cast<ULONG_PTR>(PlatformImplementation::EventType::FileRead), 1))
{
return false;
}
auto id = AZStd::make_unique<OVERLAPPED>();
auto* idp = id.get();
const auto& [folderWatch, inserted] = m_folderRootWatches.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(idp),
AZStd::forward_as_tuple(AZStd::move(id), AZStd::move(directoryHandle), root, recursive));
if (!inserted)
{
return false;
}
return folderWatch->second.ReadChanges();
}
bool FileWatcher::PlatformImplementation::FolderRootWatch::ReadChanges()
{
// Register to get directory change notifications for our directory handle
return ::ReadDirectoryChangesW(
m_directoryHandle.get(),
&m_fileNotifyInformationList,
sizeof(m_fileNotifyInformationList),
m_recursive,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME,
nullptr,
m_overlapped.get(),
nullptr
);
}
void FileWatcher::PlatformStop()
{
m_shutdownThreadSignal = true;
// Send a special signal to the child thread, that is blocked in a GetQueuedCompletionStatus call, with a completion
// key set to Shutdown. The child thread will stop its processing when it receives this value for the completion key
PostQueuedCompletionStatus(m_platformImpl->m_ioHandle.get(), 0, /*CompletionKey =*/ static_cast<ULONG_PTR>(PlatformImplementation::EventType::Shutdown), nullptr);
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
}
}
void FileWatcher::WatchFolderLoop()
{
LPOVERLAPPED directoryId = nullptr;
ULONG_PTR completionKey = 0;
while (!m_shutdownThreadSignal)
{
DWORD dwByteCount = 0;
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle.get(), &dwByteCount, &completionKey, &directoryId, INFINITE))
{
if (m_shutdownThreadSignal || completionKey == static_cast<ULONG_PTR>(PlatformImplementation::EventType::Shutdown))
{
break;
}
if (dwByteCount == 0)
{
continue;
}
const auto foundFolderRoot = m_platformImpl->m_folderRootWatches.find(directoryId);
if (foundFolderRoot == end(m_platformImpl->m_folderRootWatches))
{
continue;
}
PlatformImplementation::FolderRootWatch& folderRoot = foundFolderRoot->second;
// Initialize offset to 1 to ensure that the first iteration is always processed
DWORD offset = 1;
for (
const FILE_NOTIFY_INFORMATION* pFileNotifyInformation = reinterpret_cast<const FILE_NOTIFY_INFORMATION*>(&folderRoot.m_fileNotifyInformationList);
offset;
pFileNotifyInformation = reinterpret_cast<const FILE_NOTIFY_INFORMATION*>(reinterpret_cast<const char*>(pFileNotifyInformation) + offset)
){
const QString file = QDir::toNativeSeparators(QDir(folderRoot.m_directoryRoot)
.filePath(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2)));
switch (pFileNotifyInformation->Action)
{
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME:
rawFileAdded(file, {});
break;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
rawFileRemoved(file, {});
break;
case FILE_ACTION_MODIFIED:
rawFileModified(file, {});
break;
}
offset = pFileNotifyInformation->NextEntryOffset;
}
folderRoot.ReadChanges();
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/typetraits/aligned_storage.h>
#include <AzCore/std/typetraits/remove_pointer.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <native/FileWatcher/FileWatcher.h>
#include <AzCore/PlatformIncl.h>
struct HandleDeleter
{
void operator()(HANDLE handle)
{
if (handle && handle != INVALID_HANDLE_VALUE)
{
CloseHandle(handle);
}
}
};
using HandleUniquePtr = AZStd::unique_ptr<AZStd::remove_pointer_t<HANDLE>, HandleDeleter>;
class FileWatcher::PlatformImplementation
{
public:
bool AddWatchFolder(QString folder, bool recursive);
struct FolderRootWatch
{
FolderRootWatch(AZStd::unique_ptr<OVERLAPPED>&& overlapped, HandleUniquePtr&& directoryHandle, QString root, bool recursive)
: m_overlapped(AZStd::move(overlapped))
, m_directoryHandle(AZStd::move(directoryHandle))
, m_directoryRoot(AZStd::move(root))
, m_recursive(recursive)
{
}
bool ReadChanges();
AZStd::unique_ptr<OVERLAPPED> m_overlapped; // Identifies this root watch
HandleUniquePtr m_directoryHandle;
QString m_directoryRoot;
bool m_recursive;
AZStd::aligned_storage_t<64 * 1024, sizeof(DWORD)> m_fileNotifyInformationList{};
};
enum class EventType
{
FileRead,
Shutdown
};
AZStd::unordered_map<LPOVERLAPPED, FolderRootWatch> m_folderRootWatches;
HandleUniquePtr m_ioHandle{CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, /*CompletionKey =*/ static_cast<ULONG_PTR>(EventType::FileRead), 1)};
};
@@ -44,7 +44,6 @@ set(FILES
native/FileProcessor/FileProcessor.h
native/FileWatcher/FileWatcher.cpp
native/FileWatcher/FileWatcher.h
native/FileWatcher/FileWatcherAPI.h
native/InternalBuilders/SettingsRegistryBuilder.cpp
native/InternalBuilders/SettingsRegistryBuilder.h
native/resourcecompiler/JobsModel.cpp
@@ -6,154 +6,119 @@
*
*/
#include "FileWatcher.h"
#include "AzCore/std/containers/vector.h"
#include <native/assetprocessor.h>
#include <native/FileWatcher/FileWatcher_platform.h>
#include <QFileInfo>
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
void FolderRootWatch::ProcessNewFileEvent(const QString& file)
//! IsSubfolder(folderA, folderB)
//! returns whether folderA is a subfolder of folderB
//! assumptions: absolute paths
static bool IsSubfolder(const QString& folderA, const QString& folderB)
{
FileChangeInfo info;
info.m_action = FileAction::FileAction_Added;
info.m_filePath = file;
const bool invoked = QMetaObject::invokeMethod(m_fileWatcher, "AnyFileChange", Qt::QueuedConnection, Q_ARG(FileChangeInfo, info));
Q_ASSERT(invoked);
}
// lets avoid allocating or messing with memory - this is a MAJOR hotspot as it is called for any file change even in the cache!
if (folderA.length() <= folderB.length())
{
return false;
}
void FolderRootWatch::ProcessDeleteFileEvent(const QString& file)
{
FileChangeInfo info;
info.m_action = FileAction::FileAction_Removed;
info.m_filePath = file;
const bool invoked = QMetaObject::invokeMethod(m_fileWatcher, "AnyFileChange", Qt::QueuedConnection, Q_ARG(FileChangeInfo, info));
Q_ASSERT(invoked);
}
using AZStd::begin;
using AZStd::end;
void FolderRootWatch::ProcessModifyFileEvent(const QString& file)
{
FileChangeInfo info;
info.m_action = FileAction::FileAction_Modified;
info.m_filePath = file;
const bool invoked = QMetaObject::invokeMethod(m_fileWatcher, "AnyFileChange", Qt::QueuedConnection, Q_ARG(FileChangeInfo, info));
Q_ASSERT(invoked);
constexpr auto isSlash = [](const QChar c) constexpr
{
return c == AZ::IO::WindowsPathSeparator || c == AZ::IO::PosixPathSeparator;
};
const auto firstPathSeparator = AZStd::find_if(begin(folderB), end(folderB), [&isSlash](const QChar c)
{
return isSlash(c);
});
// Follow the convention used by AZ::IO::Path, and use a case-sensitive comparison on Posix paths
const bool useCaseSensitiveCompare = (firstPathSeparator == end(folderB)) ? true : (*firstPathSeparator == AZ::IO::PosixPathSeparator);
return AZStd::equal(begin(folderB), end(folderB), begin(folderA), [isSlash, useCaseSensitiveCompare](const QChar charAtB, const QChar charAtA)
{
if (isSlash(charAtA))
{
return isSlash(charAtB);
}
if (useCaseSensitiveCompare)
{
return charAtA == charAtB;
}
return charAtA.toLower() == charAtB.toLower();
});
}
//////////////////////////////////////////////////////////////////////////
/// FileWatcher
FileWatcher::FileWatcher()
: m_nextHandle(0)
: m_platformImpl(AZStd::make_unique<PlatformImplementation>())
{
qRegisterMetaType<FileChangeInfo>("FileChangeInfo");
auto makeFilter = [this](auto signal)
{
return [this, signal](QString path)
{
const auto foundWatchRoot = AZStd::find_if(begin(m_folderWatchRoots), end(m_folderWatchRoots), [path](const WatchRoot& watchRoot)
{
return Filter(path, watchRoot);
});
if (foundWatchRoot == end(m_folderWatchRoots))
{
return;
}
AZStd::invoke(signal, this, path);
};
};
// The rawFileAdded signals are emitted by the watcher thread. Use a queued
// connection so that the consumers of the notification process the
// notification on the main thread.
connect(this, &FileWatcher::rawFileAdded, this, makeFilter(&FileWatcher::fileAdded), Qt::QueuedConnection);
connect(this, &FileWatcher::rawFileRemoved, this, makeFilter(&FileWatcher::fileRemoved), Qt::QueuedConnection);
connect(this, &FileWatcher::rawFileModified, this, makeFilter(&FileWatcher::fileModified), Qt::QueuedConnection);
}
FileWatcher::~FileWatcher()
{
disconnect();
StopWatching();
}
int FileWatcher::AddFolderWatch(FolderWatchBase* pFolderWatch)
void FileWatcher::AddFolderWatch(QString directory, bool recursive)
{
if (!pFolderWatch)
// Search for an already monitored root that is a parent of `directory`,
// that is already watching subdirectories recursively
const auto found = AZStd::find_if(begin(m_folderWatchRoots), end(m_folderWatchRoots), [directory](const WatchRoot& root)
{
return -1;
return root.m_recursive && IsSubfolder(directory, root.m_directory);
});
if (found != end(m_folderWatchRoots))
{
// This directory is already watched
return;
}
FolderRootWatch* pFolderRootWatch = nullptr;
//create a new root and start listening for changes
m_folderWatchRoots.push_back({directory, recursive});
//see if this a sub folder of an already watched root
for (auto rootsIter = m_folderWatchRoots.begin(); !pFolderRootWatch && rootsIter != m_folderWatchRoots.end(); ++rootsIter)
//since we created a new root, see if the new root is a super folder
//of other roots, if it is then then fold those roots into the new super root
if (recursive)
{
if (FolderWatchBase::IsSubfolder(pFolderWatch->m_folder, (*rootsIter)->m_root))
AZStd::erase_if(m_folderWatchRoots, [directory](const WatchRoot& root)
{
pFolderRootWatch = *rootsIter;
}
return IsSubfolder(root.m_directory, directory);
});
}
bool bCreatedNewRoot = false;
//if its not a sub folder
if (!pFolderRootWatch)
{
//create a new root and start listening for changes
pFolderRootWatch = new FolderRootWatch(pFolderWatch->m_folder);
//make sure the folder watcher(s) get deleted before this
pFolderRootWatch->setParent(this);
bCreatedNewRoot = true;
}
pFolderRootWatch->m_fileWatcher = this;
QObject::connect(this, &FileWatcher::AnyFileChange, pFolderWatch, &FolderWatchBase::OnAnyFileChange);
if (bCreatedNewRoot)
{
if (m_startedWatching)
{
pFolderRootWatch->Start();
}
//since we created a new root, see if the new root is a super folder
//of other roots, if it is then then fold those roots into the new super root
for (auto rootsIter = m_folderWatchRoots.begin(); rootsIter != m_folderWatchRoots.end(); )
{
if (FolderWatchBase::IsSubfolder((*rootsIter)->m_root, pFolderWatch->m_folder))
{
//union the sub folder map over to the new root
pFolderRootWatch->m_subFolderWatchesMap.insert((*rootsIter)->m_subFolderWatchesMap);
//clear the old root sub folders map so they don't get deleted when we
//delete the old root as they are now pointed to by the new root
(*rootsIter)->m_subFolderWatchesMap.clear();
//delete the empty old root, deleting a root will call Stop()
//automatically which kills the thread
delete *rootsIter;
//remove the old root pointer form the watched list
rootsIter = m_folderWatchRoots.erase(rootsIter);
}
else
{
++rootsIter;
}
}
//add the new root to the watched roots
m_folderWatchRoots.push_back(pFolderRootWatch);
}
//add to the root
pFolderRootWatch->m_subFolderWatchesMap.insert(m_nextHandle, pFolderWatch);
m_nextHandle++;
return m_nextHandle - 1;
}
void FileWatcher::RemoveFolderWatch(int handle)
void FileWatcher::ClearFolderWatches()
{
for (auto rootsIter = m_folderWatchRoots.begin(); rootsIter != m_folderWatchRoots.end(); )
{
//find an element by the handle
auto foundIter = (*rootsIter)->m_subFolderWatchesMap.find(handle);
if (foundIter != (*rootsIter)->m_subFolderWatchesMap.end())
{
//remove the element
(*rootsIter)->m_subFolderWatchesMap.erase(foundIter);
//we removed a folder watch, if it's empty then there is no reason to keep watching it.
if ((*rootsIter)->m_subFolderWatchesMap.empty())
{
delete(*rootsIter);
rootsIter = m_folderWatchRoots.erase(rootsIter);
}
else
{
++rootsIter;
}
}
else
{
++rootsIter;
}
}
m_folderWatchRoots.clear();
}
void FileWatcher::StartWatching()
@@ -164,12 +129,18 @@ void FileWatcher::StartWatching()
return;
}
for (FolderRootWatch* root : m_folderWatchRoots)
if (PlatformStart())
{
root->Start();
m_thread = AZStd::thread({/*.name=*/ "AssetProcessor FileWatcher thread"}, [this]{
WatchFolderLoop();
});
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "File Change Monitoring started.\n");
}
else
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "File Change Monitoring failed to start.\n");
}
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "File Change Monitoring started.\n");
m_startedWatching = true;
}
@@ -177,17 +148,35 @@ void FileWatcher::StopWatching()
{
if (!m_startedWatching)
{
AZ_Warning("FileWatcher", false, "StartWatching() called when is not watching for file changes.");
AZ_Warning("FileWatcher", false, "StopWatching() called when is not watching for file changes.");
return;
}
for (FolderRootWatch* root : m_folderWatchRoots)
{
root->Stop();
}
PlatformStop();
m_startedWatching = false;
}
#include "native/FileWatcher/moc_FileWatcher.cpp"
#include "native/FileWatcher/moc_FileWatcherAPI.cpp"
bool FileWatcher::Filter(QString path, const WatchRoot& watchRoot)
{
if (!IsSubfolder(path, watchRoot.m_directory))
{
return false;
}
if (!watchRoot.m_recursive)
{
// filter out subtrees too.
QStringRef subRef = path.rightRef(path.length() - watchRoot.m_directory.length());
if ((subRef.indexOf('/') != -1) || (subRef.indexOf('\\') != -1))
{
return false; // filter this out.
}
// we don't care about subdirs. IsDir is more expensive so we do it after the above filter.
if (QFileInfo(path).isDir())
{
return false;
}
}
return true;
}
@@ -5,62 +5,21 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef FILEWATCHER_COMPONENT_H
#define FILEWATCHER_COMPONENT_H
//////////////////////////////////////////////////////////////////////////
#pragma once
#if !defined(Q_MOC_RUN)
#include "FileWatcherAPI.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/std/parallel/thread.h>
#include <QMap>
#include <QVector>
#include <QString>
#include <QObject>
#include <thread>
#endif
class FileWatcher;
//////////////////////////////////////////////////////////////////////////
//! FolderRootWatch
/*! Class used for holding a point in the files system from which file changes are tracked.
* */
class FolderRootWatch
: public QObject
{
Q_OBJECT
friend class FileWatcher;
public:
FolderRootWatch(const QString rootFolder);
virtual ~FolderRootWatch();
void ProcessNewFileEvent(const QString& file);
void ProcessDeleteFileEvent(const QString& file);
void ProcessModifyFileEvent(const QString& file);
void ProcessRenameFileEvent(const QString& fileOld, const QString& fileNew);
public Q_SLOTS:
bool Start();
void Stop();
private:
void WatchFolderLoop();
private:
std::thread m_thread;
QString m_root;
QMap<int, FolderWatchBase*> m_subFolderWatchesMap;
volatile bool m_shutdownThreadSignal;
FileWatcher* m_fileWatcher;
// Can't use unique_ptr because this is a QObject and Qt's magic sauce is
// unable to determine the size of the unique_ptr and so fails to compile
struct PlatformImplementation;
PlatformImplementation* m_platformImpl;
};
//////////////////////////////////////////////////////////////////////////
//! FileWatcher
/*! Class that handles creation and deletion of FolderRootWatches based on
@@ -73,23 +32,47 @@ class FileWatcher
public:
FileWatcher();
virtual ~FileWatcher();
~FileWatcher() override;
//////////////////////////////////////////////////////////////////////////
virtual int AddFolderWatch(FolderWatchBase* pFolderWatch);
virtual void RemoveFolderWatch(int handle);
void AddFolderWatch(QString directory, bool recursive = true);
void ClearFolderWatches();
//////////////////////////////////////////////////////////////////////////
void StartWatching();
void StopWatching();
Q_SIGNALS:
void AnyFileChange(FileChangeInfo info);
// These signals are emitted when a file under a watched path changes
void fileAdded(QString filePath);
void fileRemoved(QString filePath);
void fileModified(QString filePath);
// These signals are emitted by the platform implementations when files
// change. Some platforms' file watch APIs do not support non-recursive
// watches, so the signals are filtered before being forwarded to the
// non-"raw" fileAdded/Removed/Modified signals above.
void rawFileAdded(QString filePath, QPrivateSignal);
void rawFileRemoved(QString filePath, QPrivateSignal);
void rawFileModified(QString filePath, QPrivateSignal);
private:
int m_nextHandle;
AZStd::vector<FolderRootWatch*> m_folderWatchRoots;
bool m_startedWatching = false;
};
bool PlatformStart();
void PlatformStop();
void WatchFolderLoop();
#endif//FILEWATCHER_COMPONENT_H
class PlatformImplementation;
friend class PlatformImplementation;
struct WatchRoot
{
QString m_directory;
bool m_recursive;
};
static bool Filter(QString path, const WatchRoot& watchRoot);
AZStd::unique_ptr<PlatformImplementation> m_platformImpl;
AZStd::vector<WatchRoot> m_folderWatchRoots;
AZStd::thread m_thread;
bool m_startedWatching = false;
AZStd::atomic_bool m_shutdownThreadSignal = false;
};
@@ -1,222 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef FILEWATCHERAPI_H
#define FILEWATCHERAPI_H
#include <QString>
#include <QObject>
#include <QDir>
//////////////////////////////////////////////////////////////////////////
//! FileAction
/*! Enum for which file changes are tracked.
* */
enum FileAction
{
FileAction_None = 0x00,
FileAction_Added = 0x01,
FileAction_Removed = 0x02,
FileAction_Modified = 0x04,
FileAction_Any = 0xFF,
};
inline FileAction operator | (FileAction a, FileAction b)
{
return static_cast<FileAction>(static_cast<int>(a) | static_cast<int>(b));
}
inline FileAction operator & (FileAction a, FileAction b)
{
return static_cast<FileAction>(static_cast<int>(a) & static_cast<int>(b));
}
//////////////////////////////////////////////////////////////////////////
//! FileChangeInfo
/*! Struct for passing along information about file changes.
* */
struct FileChangeInfo
{
FileChangeInfo()
: m_action(FileAction::FileAction_None)
{}
FileChangeInfo(const FileChangeInfo& rhs)
: m_action(rhs.m_action)
, m_filePath(rhs.m_filePath)
, m_filePathOld(rhs.m_filePathOld)
{
}
FileAction m_action;
QString m_filePath;
QString m_filePathOld;
};
Q_DECLARE_METATYPE(FileChangeInfo)
//////////////////////////////////////////////////////////////////////////
//! FolderWatchBase
/*! Class for filtering file changes generated from a root watch. Define your own
*! custom filtering by deriving from this base class and implement your own
*! custom code for what to do when receiving a file change notification.
* */
class FolderWatchBase
: public QObject
{
Q_OBJECT
public:
FolderWatchBase(const QString strFolder, bool bWatchSubtree = true, FileAction fileAction = FileAction::FileAction_Any)
: m_folder(strFolder)
, m_watchSubtree(bWatchSubtree)
, m_fileAction(fileAction)
{
m_folder = QDir::toNativeSeparators(QDir::cleanPath(m_folder) + "/");
}
//! IsSubfolder(folderA, folderB)
//! returns whether folderA is a subfolder of folderB
//! assumptions: absolute paths, case insensitive
static bool IsSubfolder(const QString& folderA, const QString& folderB)
{
// lets avoid allocating or messing with memory - this is a MAJOR hotspot as it is called for any file change even in the cache!
int sizeB = folderB.length();
int sizeA = folderA.length();
if (sizeA <= sizeB)
{
return false;
}
QChar slash1 = QChar('\\');
QChar slash2 = QChar('/');
int posA = 0;
// A is going to be the longer one, so use B:
for (int idx = 0; idx < sizeB; ++idx)
{
QChar charAtA = folderA.at(posA);
QChar charAtB = folderB.at(idx);
if ((charAtB == slash1) || (charAtB == slash2))
{
if ((charAtA != slash1) && (charAtA != slash2))
{
return false;
}
++posA;
}
else
{
if (charAtA.toLower() != charAtB.toLower())
{
return false;
}
++posA;
}
}
return true;
}
QString m_folder;
bool m_watchSubtree;
FileAction m_fileAction;
public Q_SLOTS:
void OnAnyFileChange(FileChangeInfo info)
{
//if they set a file action then respect it by rejecting non matching file actions
if (info.m_action & m_fileAction)
{
//is the file is in the folder or subtree (if specified) then call OnFileChange
if (FolderWatchBase::IsSubfolder(info.m_filePath, m_folder))
{
OnFileChange(info);
}
}
}
virtual void OnFileChange(const FileChangeInfo& info) = 0;
};
//////////////////////////////////////////////////////////////////////////
//! FolderWatchCallbackEx
/*! Class implements a more complex filtering that can optionally filter for file
*! extension and call different callback for different kinds of file changes
*! generated from a root watch.
*! Notes:
*! - empty extension "" catches all file changes
*! - extension should not include the leading "."
* */
class FolderWatchCallbackEx
: public FolderWatchBase
{
Q_OBJECT
public:
FolderWatchCallbackEx(const QString strFolder, const QString extension, bool bWatchSubtree)
: FolderWatchBase(strFolder, bWatchSubtree)
, m_extension(extension)
{
}
QString m_extension;
//on file change call the change callback if passes extension then route
//to specific file action type callback
virtual void OnFileChange(const FileChangeInfo& info)
{
//if they set an extension to watch for only let matching extensions through
QFileInfo fileInfo(info.m_filePath);
if (!m_watchSubtree)
{
// filter out subtrees too.
QStringRef subRef = info.m_filePath.rightRef(info.m_filePath.length() - m_folder.length());
if ((subRef.indexOf('/') != -1) || (subRef.indexOf('\\') != -1))
{
return; // filter this out.
}
// we don't care about subdirs. IsDir is more expensive so we do it after the above filter.
if (fileInfo.isDir())
{
return;
}
}
if (m_extension.isEmpty() || fileInfo.completeSuffix().compare(m_extension, Qt::CaseInsensitive) == 0)
{
if (info.m_action & FileAction::FileAction_Any)
{
Q_EMIT fileChange(info);
}
if (info.m_action & FileAction::FileAction_Added)
{
Q_EMIT fileAdded(info.m_filePath);
}
if (info.m_action & FileAction::FileAction_Removed)
{
Q_EMIT fileRemoved(info.m_filePath);
}
if (info.m_action & FileAction::FileAction_Modified)
{
Q_EMIT fileModified(info.m_filePath);
}
}
}
Q_SIGNALS:
void fileChange(FileChangeInfo info);
void fileAdded(QString filePath);
void fileRemoved(QString filePath);
void fileModified(QString filePath);
};
#endif//FILEWATCHERAPI_H
@@ -24,14 +24,12 @@ void FileWatcherUnitTestRunner::StartTest()
FileWatcher fileWatcher;
FolderWatchCallbackEx folderWatch(tempPath, "", true);
fileWatcher.AddFolderWatch(&folderWatch);
fileWatcher.AddFolderWatch(tempPath);
fileWatcher.StartWatching();
{ // test a single file create/write
bool foundFile = false;
auto connection = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileAdded, this, [&](QString filename)
auto connection = QObject::connect(&fileWatcher, &FileWatcher::fileAdded, this, [&](QString filename)
{
AZ_TracePrintf(AssetProcessor::DebugChannel, "Single file test Found asset: %s.\n", filename.toUtf8().data());
foundFile = true;
@@ -66,7 +64,7 @@ void FileWatcherUnitTestRunner::StartTest()
const unsigned long maxFiles = 10000;
QSet<QString> outstandingFiles;
auto connection = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileAdded, this, [&](QString filename)
auto connection = QObject::connect(&fileWatcher, &FileWatcher::fileAdded, this, [&](QString filename)
{
outstandingFiles.remove(filename);
});
@@ -122,7 +120,7 @@ void FileWatcherUnitTestRunner::StartTest()
{ // test deletion
bool foundFile = false;
auto connection = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileRemoved, this, [&](QString filename)
auto connection = QObject::connect(&fileWatcher, &FileWatcher::fileRemoved, this, [&](QString filename)
{
AZ_TracePrintf(AssetProcessor::DebugChannel, "Deleted asset: %s...\n", filename.toUtf8().data());
foundFile = true;
@@ -155,7 +153,7 @@ void FileWatcherUnitTestRunner::StartTest()
{
bool fileAddCalled = false;
QString fileAddName;
auto connectionAdd = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileAdded, this, [&](QString filename)
auto connectionAdd = QObject::connect(&fileWatcher, &FileWatcher::fileAdded, this, [&](QString filename)
{
fileAddCalled = true;
fileAddName = filename;
@@ -163,7 +161,7 @@ void FileWatcherUnitTestRunner::StartTest()
bool fileRemoveCalled = false;
QString fileRemoveName;
auto connectionRemove = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileRemoved, this, [&](QString filename)
auto connectionRemove = QObject::connect(&fileWatcher, &FileWatcher::fileRemoved, this, [&](QString filename)
{
fileRemoveCalled = true;
fileRemoveName = filename;
@@ -171,7 +169,7 @@ void FileWatcherUnitTestRunner::StartTest()
QStringList fileModifiedNames;
bool fileModifiedCalled = false;
auto connectionModified = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileModified, this, [&](QString filename)
auto connectionModified = QObject::connect(&fileWatcher, &FileWatcher::fileModified, this, [&](QString filename)
{
fileModifiedCalled = true;
fileModifiedNames.append(filename);
@@ -21,7 +21,6 @@
#include "native/assetprocessor.h"
#endif
class FolderWatchCallbackEx;
class QCoreApplication;
namespace AZ
@@ -433,63 +433,77 @@ void ApplicationManagerBase::DestroyPlatformConfiguration()
void ApplicationManagerBase::InitFileMonitor()
{
m_folderWatches.reserve(m_platformConfiguration->GetScanFolderCount());
m_watchHandles.reserve(m_platformConfiguration->GetScanFolderCount());
for (int folderIdx = 0; folderIdx < m_platformConfiguration->GetScanFolderCount(); ++folderIdx)
{
const AssetProcessor::ScanFolderInfo& info = m_platformConfiguration->GetScanFolderAt(folderIdx);
FolderWatchCallbackEx* newFolderWatch = new FolderWatchCallbackEx(info.ScanPath(), "", info.RecurseSubFolders());
// hook folder watcher to assess files on add/modify
// relevant files will be sent to resource compiler
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded,
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessAddedFile);
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileModified,
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessModifiedFile);
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved,
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessDeletedFile);
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded, [this](QString path) { m_fileStateCache->AddFile(path); });
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileModified, [this](QString path) { m_fileStateCache->UpdateFile(path); });
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved, [this](QString path) { m_fileStateCache->RemoveFile(path); });
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded, [](QString path) { AZ::Interface<AssetProcessor::ExcludedFolderCacheInterface>::Get()->FileAdded(path); });
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded,
m_fileProcessor.get(), &AssetProcessor::FileProcessor::AssessAddedFile);
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved,
m_fileProcessor.get(), &AssetProcessor::FileProcessor::AssessDeletedFile);
m_folderWatches.push_back(AZStd::unique_ptr<FolderWatchCallbackEx>(newFolderWatch));
m_watchHandles.push_back(m_fileWatcher.AddFolderWatch(newFolderWatch));
m_fileWatcher.AddFolderWatch(info.ScanPath(), info.RecurseSubFolders());
}
// also hookup monitoring for the cache (output directory)
QDir cacheRoot;
if (AssetUtilities::ComputeProjectCacheRoot(cacheRoot))
{
FolderWatchCallbackEx* newFolderWatch = new FolderWatchCallbackEx(cacheRoot.absolutePath(), "", true);
m_fileWatcher.AddFolderWatch(cacheRoot.absolutePath(), true);
}
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded, [this](QString path) { m_fileStateCache->AddFile(path); });
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileModified, [this](QString path) { m_fileStateCache->UpdateFile(path); });
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved, [this](QString path) { m_fileStateCache->RemoveFile(path); });
if (m_platformConfiguration->GetScanFolderCount() || !cacheRoot.path().isEmpty())
{
const auto cachePath = QDir::toNativeSeparators(cacheRoot.absolutePath());
// we only care about cache root deletions.
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved,
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessDeletedFile);
const auto OnFileAdded = [this, cachePath](QString path)
{
const bool isCacheRoot = path.startsWith(cachePath);
if (isCacheRoot)
{
m_fileStateCache->AddFile(path);
}
else
{
m_assetProcessorManager->AssessAddedFile(path);
m_fileStateCache->AddFile(path);
AZ::Interface<AssetProcessor::ExcludedFolderCacheInterface>::Get()->FileAdded(path);
m_fileProcessor->AssetProcessor::FileProcessor::AssessAddedFile(path);
}
};
m_folderWatches.push_back(AZStd::unique_ptr<FolderWatchCallbackEx>(newFolderWatch));
m_watchHandles.push_back(m_fileWatcher.AddFolderWatch(newFolderWatch));
const auto OnFileModified = [this, cachePath](QString path)
{
const bool isCacheRoot = path.startsWith(cachePath);
if (isCacheRoot)
{
m_assetProcessorManager->AssessModifiedFile(path);
}
else
{
m_assetProcessorManager->AssessModifiedFile(path);
m_fileStateCache->UpdateFile(path);
}
};
const auto OnFileRemoved = [this, cachePath](QString path)
{
const bool isCacheRoot = path.startsWith(cachePath);
if (isCacheRoot)
{
m_fileStateCache->RemoveFile(path);
m_assetProcessorManager->AssessDeletedFile(path);
}
else
{
m_assetProcessorManager->AssessDeletedFile(path);
m_fileStateCache->RemoveFile(path);
m_fileProcessor->AssessDeletedFile(path);
}
};
connect(&m_fileWatcher, &FileWatcher::fileAdded, OnFileAdded);
connect(&m_fileWatcher, &FileWatcher::fileModified, OnFileModified);
connect(&m_fileWatcher, &FileWatcher::fileRemoved, OnFileRemoved);
}
}
void ApplicationManagerBase::DestroyFileMonitor()
{
for (int watchHandle : m_watchHandles)
{
m_fileWatcher.RemoveFolderWatch(watchHandle);
}
m_folderWatches.resize(0);
m_fileWatcher.ClearFolderWatches();
}
void ApplicationManagerBase::DestroyApplicationServer()
@@ -798,8 +812,6 @@ ApplicationManager::BeforeRunStatus ApplicationManagerBase::BeforeRun()
qRegisterMetaType<AzFramework::AssetSystem::AssetStatus>("AzFramework::AssetSystem::AssetStatus");
qRegisterMetaType<AzFramework::AssetSystem::AssetStatus>("AssetStatus");
qRegisterMetaType<FileChangeInfo>("FileChangeInfo");
qRegisterMetaType<AssetProcessor::AssetScanningStatus>("AssetScanningStatus");
qRegisterMetaType<AssetProcessor::NetworkRequestID>("NetworkRequestID");
@@ -47,7 +47,6 @@ namespace AssetProcessor
class ApplicationServer;
class ConnectionManager;
class FolderWatchCallbackEx;
class ControlRequestHandler;
class ApplicationManagerBase
@@ -192,9 +191,7 @@ protected:
bool m_sourceControlReady = false;
bool m_fullIdle = false;
AZStd::vector<AZStd::unique_ptr<FolderWatchCallbackEx> > m_folderWatches;
FileWatcher m_fileWatcher;
AZStd::vector<int> m_watchHandles;
AssetProcessor::PlatformConfiguration* m_platformConfiguration = nullptr;
AssetProcessor::AssetProcessorManager* m_assetProcessorManager = nullptr;
AssetProcessor::AssetCatalog* m_assetCatalog = nullptr;
@@ -23,7 +23,6 @@
#include <native/resourcecompiler/RCBuilder.h>
#include <AssetBuilder/AssetBuilderInfo.h>
class FolderWatchCallbackEx;
class QCoreApplication;
namespace AssetProcessor
@@ -5,7 +5,7 @@
"ClassData": {
"PassTemplate": {
"Name": "DiffuseCompositePassTemplate",
"PassClass": "DiffuseCompositePass",
"PassClass": "FullScreenTriangle",
"Slots": [
{
"Name": "DownsampledIrradianceInput",
@@ -96,7 +96,6 @@
#include <DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h>
#include <DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h>
#include <DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h>
#include <DiffuseGlobalIllumination/DiffuseCompositePass.h>
#include <ReflectionScreenSpace/ReflectionScreenSpaceTracePass.h>
#include <ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h>
#include <ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.h>
@@ -279,7 +278,6 @@ namespace AZ
passSystem->AddPassCreator(Name("DiffuseProbeGridClassificationPass"), &Render::DiffuseProbeGridClassificationPass::Create);
passSystem->AddPassCreator(Name("DiffuseProbeGridDownsamplePass"), &Render::DiffuseProbeGridDownsamplePass::Create);
passSystem->AddPassCreator(Name("DiffuseProbeGridRenderPass"), &Render::DiffuseProbeGridRenderPass::Create);
passSystem->AddPassCreator(Name("DiffuseCompositePass"), &Render::DiffuseCompositePass::Create);
passSystem->AddPassCreator(Name("LuminanceHistogramGeneratorPass"), &LuminanceHistogramGeneratorPass::Create);
@@ -1,45 +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 <DiffuseGlobalIllumination/DiffuseCompositePass.h>
#include <DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h>
namespace AZ
{
namespace Render
{
// --- Dedicated class for disabling ---
RPI::Ptr<DiffuseCompositePass> DiffuseCompositePass::Create(const RPI::PassDescriptor& descriptor)
{
RPI::Ptr<DiffuseCompositePass> pass = aznew DiffuseCompositePass(descriptor);
return AZStd::move(pass);
}
DiffuseCompositePass::DiffuseCompositePass(const RPI::PassDescriptor& descriptor)
: RPI::FullscreenTrianglePass(descriptor)
{ }
bool DiffuseCompositePass::IsEnabled() const
{
const RPI::Scene* scene = GetScene();
if (!scene)
{
return false;
}
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor =
scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetVisibleRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return false;
}
return true;
}
} // namespace Render
} // namespace AZ
@@ -1,36 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
namespace AZ
{
namespace Render
{
//! A class for DiffuseComposite to allow for disabling
class DiffuseCompositePass final
: public RPI::FullscreenTrianglePass
{
AZ_RPI_PASS(DiffuseCompositePass);
public:
AZ_RTTI(DiffuseCompositePass, "{F3DBEBCB-66F8-465C-A06B-DFA76B9D4856}", AZ::RPI::FullscreenTrianglePass);
AZ_CLASS_ALLOCATOR(DiffuseCompositePass, SystemAllocator, 0);
~DiffuseCompositePass() = default;
static RPI::Ptr<DiffuseCompositePass> Create(const RPI::PassDescriptor& descriptor);
bool IsEnabled() const override;
private:
DiffuseCompositePass(const RPI::PassDescriptor& descriptor);
};
} // namespace Render
} // namespace AZ
@@ -134,8 +134,6 @@ set(FILES
Source/DiffuseGlobalIllumination/DiffuseProbeGridDownsamplePass.h
Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp
Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h
Source/DiffuseGlobalIllumination/DiffuseCompositePass.cpp
Source/DiffuseGlobalIllumination/DiffuseCompositePass.h
Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp
Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h
Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp
@@ -285,13 +285,19 @@ namespace AZ::Render
const double frameIntervalSeconds = m_fpsInterval.count();
auto ClampedFloatDisplay = [](double value, const char* format) -> AZStd::string
{
constexpr float upperLimit = 10000.0f;
return value > upperLimit ? "inf" : AZStd::string::format(format, value);
};
DrawLine(
AZStd::string::format(
"FPS %.1f [%.0f..%.0f], %.1fms/frame, avg over %.1fs",
averageFPS,
minFPS,
maxFPS,
averageFrameMs,
"FPS %s [%s..%s], %sms/frame, avg over %.1fs",
ClampedFloatDisplay(averageFPS, "%.1f").c_str(),
ClampedFloatDisplay(minFPS, "%.0f").c_str(),
ClampedFloatDisplay(maxFPS, "%.0f").c_str(),
ClampedFloatDisplay(averageFrameMs, "%.1f").c_str(),
frameIntervalSeconds),
AZ::Colors::Yellow);
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d72cec207a7677ba027eac72f41285907237e04a45ebacf64341de86fc6f022d
size 159115308
@@ -1,39 +1,33 @@
{
"description": "",
"materialType": "Materials/Types/StandardPBR.materialtype",
"parentMaterial": "Materials/Presets/PBR/metal_brass.material",
"propertyLayoutVersion": 3,
"materialType": "Materials/Types/StandardPBR.materialtype",
"materialTypeVersion": 4,
"properties": {
"occlusion": {
"diffuseTextureMap": "Objects/Lucy/Lucy_ao.tif",
"diffuseTextureMapUv": "Unwrapped"
},
"baseColor": {
"color": [
0.6745098233222961,
0.48627451062202456,
0.19607843458652497,
1.0
],
"factor": 1.0,
"textureBlendMode": "Lerp",
"textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png",
"textureMap": "Hermanubis_bronze_BaseColor.png",
"textureMapUv": "Unwrapped"
},
"general": {
"applySpecularAA": true
},
"metallic": {
"textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png",
"textureMap": "Hermanubis_bronze_Metallic.png",
"textureMapUv": "Unwrapped"
},
"normal": {
"flipY": true,
"textureMap": "Objects/Lucy/Lucy_Normal.png",
"textureMap": "Hermanubis_Normal.png",
"textureMapUv": "Unwrapped"
},
"occlusion": {
"diffuseTextureMap": "Hermanubis_ao.tif",
"diffuseTextureMapUv": "Unwrapped"
},
"roughness": {
"textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png",
"textureMap": "Hermanubis_bronze_Roughness.png",
"textureMapUv": "Unwrapped",
"upperBound": 0.6767677068710327
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c9d1030b9467b58d640fbedf1bc58ab9a5f7d68811b2452dd8d60114287b731
size 12410812
@@ -1,13 +1,9 @@
{
"description": "",
"materialType": "Materials/Types/StandardPBR.materialtype",
"parentMaterial": "Materials/Presets/PBR/metal_brass.material",
"propertyLayoutVersion": 3,
"materialType": "Materials/Types/StandardPBR.materialtype",
"materialTypeVersion": 4,
"properties": {
"occlusion": {
"diffuseTextureMap": "Objects/Lucy/Lucy_ao.tif",
"diffuseTextureMapUv": "Unwrapped"
},
"baseColor": {
"color": [
1.0,
@@ -17,7 +13,7 @@
],
"factor": 1.0,
"textureBlendMode": "Lerp",
"textureMap": "Objects/Lucy/Lucy_Stone_BaseColor.png",
"textureMap": "Hermanubis_Stone_BaseColor.png",
"textureMapUv": "Unwrapped"
},
"clearCoat": {
@@ -39,13 +35,17 @@
},
"normal": {
"flipY": true,
"textureMap": "Objects/Lucy/Lucy_Normal.png",
"textureMap": "Hermanubis_Normal.png",
"textureMapUv": "Unwrapped"
},
"occlusion": {
"diffuseTextureMap": "Hermanubis_ao.tif",
"diffuseTextureMapUv": "Unwrapped"
},
"roughness": {
"factor": 1.0,
"lowerBound": 0.15000000596046449,
"textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png",
"lowerBound": 0.15000000596046448,
"textureMap": "Hermanubis_bronze_Roughness.png",
"textureMapUv": "Unwrapped",
"upperBound": 0.7300000190734863
}
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:00e19e317613be5420fd78bac1159e66d1c4deeb1f32cd4fc8c20b1ea3a5ead1
size 153114272
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a4a65d139a6088dd4ac34f3ba3f6a7a98b8fe9545150ee7d9879fbc2a55d8d4
size 9022128
@@ -261,6 +261,11 @@ namespace AZ::Render
m_lightShapeDelegate->SetPhotometricUnit(m_configuration.m_intensityMode);
m_lightShapeDelegate->SetIntensity(m_configuration.m_intensity);
}
if (m_configuration.m_attenuationRadiusMode == LightAttenuationRadiusMode::Automatic)
{
AttenuationRadiusChanged();
}
}
void AreaLightComponentController::ChromaChanged()
@@ -224,6 +224,11 @@ namespace EMotionFX
poseIndexB = poseIndexA;
*outWeight = 0.0f;
}
else if ((*outWeight > 1.0f - MCore::Math::epsilon))
{
poseIndexA = poseIndexB;
*outWeight = 0.0f;
}
// Search complete: the input weight is between m_paramWeights[i] and m_paramWeights[i - 1]
// Calculate the blend weight and get the nodes and then return
@@ -6,7 +6,6 @@
*
*/
// include the required headers
#include "EMotionFXConfig.h"
#include "EMotionFXManager.h"
#include "Importer/Importer.h"
@@ -29,6 +28,7 @@
#include <EMotionFX/Source/Allocators.h>
#include <EMotionFX/Source/DebugDraw.h>
#include <EMotionFX/Source/MotionData/MotionDataFactory.h>
#include <EMotionFX/Source/PoseDataFactory.h>
#include <Integration/Rendering/RenderActorSettings.h>
namespace EMotionFX
@@ -76,6 +76,7 @@ namespace EMotionFX
gEMFX.Get()->SetRecorder (Recorder::Create());
gEMFX.Get()->SetMotionInstancePool (MotionInstancePool::Create());
gEMFX.Get()->SetDebugDraw (aznew DebugDraw());
gEMFX.Get()->SetPoseDataFactory (aznew PoseDataFactory());
gEMFX.Get()->SetGlobalSimulationSpeed (1.0f);
// set the number of threads
@@ -124,6 +125,7 @@ namespace EMotionFX
m_recorder = nullptr;
m_motionInstancePool = nullptr;
m_debugDraw = nullptr;
m_poseDataFactory = nullptr;
m_unitType = MCore::Distance::UNITTYPE_METERS;
m_globalSimulationSpeed = 1.0f;
m_isInEditorMode = false;
@@ -170,6 +172,9 @@ namespace EMotionFX
delete m_debugDraw;
m_debugDraw = nullptr;
delete m_poseDataFactory;
m_poseDataFactory = nullptr;
m_renderActorSettings.reset();
m_eventManager->Destroy();
@@ -37,6 +37,7 @@ namespace EMotionFX
class MotionInstancePool;
class EventDataFactory;
class DebugDraw;
class PoseDataFactory;
// versions
#define EMFX_HIGHVERSION 4
@@ -188,6 +189,8 @@ namespace EMotionFX
*/
MCORE_INLINE DebugDraw* GetDebugDraw() const { return m_debugDraw; }
MCORE_INLINE PoseDataFactory* GetPoseDataFactory() const { return m_poseDataFactory; }
/**
* Get the render actor settings
* @result A pointer to global render actor settings.
@@ -357,6 +360,7 @@ namespace EMotionFX
EventManager* m_eventManager; /**< The motion event manager. */
SoftSkinManager* m_softSkinManager; /**< The softskin manager. */
AnimGraphManager* m_animGraphManager; /**< The animgraph manager. */
PoseDataFactory* m_poseDataFactory;
Recorder* m_recorder; /**< The recorder. */
MotionInstancePool* m_motionInstancePool; /**< The motion instance pool. */
DebugDraw* m_debugDraw; /**< The debug drawing system. */
@@ -433,6 +437,8 @@ namespace EMotionFX
*/
void SetMotionInstancePool(MotionInstancePool* pool);
void SetPoseDataFactory(PoseDataFactory* poseDataFactory) { m_poseDataFactory = poseDataFactory; }
/**
* Set the number of threads to use.
* @param numThreads The number of threads to use internally. This must be a value of 1 or above.
@@ -520,5 +526,6 @@ namespace EMotionFX
MCORE_INLINE Recorder& GetRecorder() { return *GetEMotionFX().GetRecorder(); } /**< Get the recorder. */
MCORE_INLINE MotionInstancePool& GetMotionInstancePool() { return *GetEMotionFX().GetMotionInstancePool(); } /**< Get the motion instance pool. */
MCORE_INLINE DebugDraw& GetDebugDraw() { return *GetEMotionFX().GetDebugDraw(); } /**< Get the debug drawing. */
MCORE_INLINE PoseDataFactory& GetPoseDataFactory() { return *GetEMotionFX().GetPoseDataFactory(); }
MCORE_INLINE AZ::Render::RenderActorSettings& GetRenderActorSettings() { return *GetEMotionFX().GetRenderActorSettings(); }/**< Get the render actor settings. */
} // namespace EMotionFX
+35 -1
View File
@@ -1428,4 +1428,38 @@ namespace EMotionFX
GetEMotionFX().GetThreadData(m_actorInstance->GetThreadIndex())->GetPosePool().FreePose(tempPose);
}
} // namespace EMotionFX
void Pose::DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Color& color, bool drawPoseDatas) const
{
debugDisplay.SetColor(color);
debugDisplay.DepthTestOff();
const Skeleton* skeleton = m_actorInstance->GetActor()->GetSkeleton();
const size_t numEnabledJoints = m_actorInstance->GetNumEnabledNodes();
for (size_t i = 0; i < numEnabledJoints; ++i)
{
const size_t jointIndex = m_actorInstance->GetEnabledNode(i);
const size_t parentIndex = skeleton->GetNode(jointIndex)->GetParentIndex();
if (parentIndex != InvalidIndex)
{
const AZ::Vector3 startPos = GetWorldSpaceTransform(jointIndex).m_position;
const AZ::Vector3 endPos = GetWorldSpaceTransform(parentIndex).m_position;
debugDisplay.DrawSolidCylinder(/*center=*/(startPos + endPos) * 0.5f,
/*direction=*/(endPos - startPos).GetNormalizedSafe(),
/*radius=*/0.005f,
/*height=*/(endPos - startPos).GetLength(),
/*drawShaded=*/false);
}
}
if (drawPoseDatas)
{
for (const auto& poseDataItem : m_poseDatas)
{
PoseData* poseData = poseDataItem.second.get();
poseData->DebugDraw(debugDisplay, color);
}
}
}
} // namespace EMotionFX
+9 -5
View File
@@ -10,10 +10,10 @@
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <EMotionFX/Source/PoseData.h>
#include <EMotionFX/Source/Transform.h>
namespace EMotionFX
{
// forward declarations
@@ -25,10 +25,6 @@ namespace EMotionFX
class Skeleton;
class MotionLinkData;
/**
*
*
*/
class EMFX_API Pose
{
MCORE_MEMORYOBJECTCATEGORY(Pose, EMFX_DEFAULT_ALIGNMENT, EMFX_MEMCATEGORY_POSE);
@@ -192,6 +188,14 @@ namespace EMotionFX
template <class T>
T* GetAndPreparePoseData(ActorInstance* linkToActorInstance) { return azdynamic_cast<T*>(GetAndPreparePoseData(azrtti_typeid<T>(), linkToActorInstance)); }
/**
* Draw debug visualization for the given pose.
* @param[in] debugDisplay Debug display request bus to spawn the render commands.
* @param[in] color The color the skeletal pose should be in.
* @param[in] drawPoseDatas Draw the pose data debug visualizations (e.g. joint velocities) along with the actual skeletal pose. [Default = false]
*/
void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Color& color, bool drawPoseDatas = false) const;
private:
mutable AZStd::vector<Transform> m_localSpaceTransforms;
mutable AZStd::vector<Transform> m_modelSpaceTransforms;
@@ -12,9 +12,9 @@
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <EMotionFX/Source/EMotionFXConfig.h>
namespace EMotionFX
{
class Actor;
@@ -40,6 +40,8 @@ namespace EMotionFX
virtual void Blend(const Pose* destPose, float weight) = 0;
virtual void DebugDraw([[maybe_unused]] AzFramework::DebugDisplayRequests& debugDisplay, [[maybe_unused]] const AZ::Color& color) const {}
bool IsUsed() const { return m_isUsed; }
void SetIsUsed(bool isUsed) { m_isUsed = isUsed; }
@@ -8,13 +8,20 @@
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <EMotionFX/Source/Allocators.h>
#include <EMotionFX/Source/EMotionFXConfig.h>
#include <EMotionFX/Source/PoseDataFactory.h>
#include <EMotionFX/Source/PoseDataRagdoll.h>
namespace EMotionFX
{
AZ_CLASS_ALLOCATOR_IMPL(PoseDataFactory, PoseAllocator, 0)
PoseDataFactory::PoseDataFactory()
{
AddPoseDataType(azrtti_typeid<PoseDataRagdoll>());
}
PoseData* PoseDataFactory::Create(Pose* pose, const AZ::TypeId& type)
{
AZ::SerializeContext* context = nullptr;
@@ -34,13 +41,13 @@ namespace EMotionFX
return result;
}
const AZStd::unordered_set<AZ::TypeId>& PoseDataFactory::GetTypeIds()
void PoseDataFactory::AddPoseDataType(const AZ::TypeId& poseDataType)
{
static AZStd::unordered_set<AZ::TypeId> typeIds =
{
azrtti_typeid<PoseDataRagdoll>()
};
m_poseDataTypeIds.emplace(poseDataType);
}
return typeIds;
const AZStd::unordered_set<AZ::TypeId>& PoseDataFactory::GetTypeIds() const
{
return m_poseDataTypeIds;
}
} // namespace EMotionFX
@@ -25,7 +25,18 @@ namespace EMotionFX
class EMFX_API PoseDataFactory
{
public:
AZ_RTTI(PoseDataFactory, "{F10014A0-2B6A-44E5-BA53-0E11ED566701}")
AZ_CLASS_ALLOCATOR_DECL
PoseDataFactory();
virtual ~PoseDataFactory() = default;
static PoseData* Create(Pose* pose, const AZ::TypeId& type);
static const AZStd::unordered_set<AZ::TypeId>& GetTypeIds();
void AddPoseDataType(const AZ::TypeId& poseDataType);
const AZStd::unordered_set<AZ::TypeId>& GetTypeIds() const;
private:
AZStd::unordered_set<AZ::TypeId> m_poseDataTypeIds;
};
} // namespace EMotionFX
@@ -353,7 +353,6 @@ void UiImageComponent::SetOverrideSprite(ISprite* sprite, AZ::u32 cellIndex)
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiImageComponent::Render(LyShine::IRenderGraph* renderGraph)
{
// get fade value (tracked by UiRenderer) and compute the desired alpha for the image
float fade = renderGraph->GetAlphaFade();
float desiredAlpha = m_overrideAlpha * fade;
@@ -376,9 +375,8 @@ void UiImageComponent::Render(LyShine::IRenderGraph* renderGraph)
ImageType imageType = m_imageType;
#ifdef LYSHINE_ATOM_TODO // support default white texture
// if there is no texture we will just use a white texture and want to stretch it
const bool spriteOrTextureIsNull = sprite == nullptr || sprite->GetTexture() == nullptr;
const bool spriteOrTextureIsNull = sprite == nullptr || sprite->GetImage() == nullptr;
// Zero texture size may occur even if the UiImageComponent has a valid non-zero-sized texture,
// because a canvas can be requested to Render() before the texture asset is done loading.
@@ -399,12 +397,6 @@ void UiImageComponent::Render(LyShine::IRenderGraph* renderGraph)
{
imageType = ImageType::Stretched;
}
#else
if (sprite == nullptr)
{
imageType = ImageType::Stretched;
}
#endif
switch (imageType)
{
@@ -72,36 +72,25 @@ namespace LyShineExamples
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiCustomImageComponent::Render([[maybe_unused]] LyShine::IRenderGraph* renderGraph)
void UiCustomImageComponent::Render(LyShine::IRenderGraph* renderGraph)
{
#ifdef LYSHINE_ATOM_TODO // [GHI #3568] Convert draws to use Atom
// get fade value (tracked by UiRenderer) and compute the desired alpha for the image
float fade = renderGraph->GetAlphaFade();
float desiredAlpha = m_overrideAlpha * fade;
uint8 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
// if desired alpha is zero then no need to do any more
if (desiredPackedAlpha == 0)
{
return;
}
ISprite* sprite = (m_overrideSprite) ? m_overrideSprite : m_sprite;
ITexture* texture = (sprite) ? sprite->GetTexture() : nullptr;
if (!texture)
{
// if there is no texture we will just use a white texture
// TODO: Get a default atom texture here when possible
//texture = ???->EF_GetTextureByID(???->GetWhiteTextureId());
}
if (m_isRenderCacheDirty)
{
RenderToCache(renderGraph);
m_isRenderCacheDirty = false;
}
// if desired alpha is zero then no need to do any more
if (desiredPackedAlpha == 0)
{
return;
}
// Render cache is now valid - render using the cache
// If the fade value has changed we need to update the alpha values in the vertex colors but we do
@@ -109,7 +98,7 @@ namespace LyShineExamples
if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
{
// go through all the cached vertices and update the alpha values
UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
LyShine::UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
desiredPackedColor.a = desiredPackedAlpha;
for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
{
@@ -117,11 +106,12 @@ namespace LyShineExamples
}
}
ISprite* sprite = (m_overrideSprite) ? m_overrideSprite : m_sprite;
AZ::Data::Instance<AZ::RPI::Image> image = sprite->GetImage();
bool isTextureSRGB = false;
bool isTexturePremultipliedAlpha = false; // we are not rendering from a render target with alpha in it
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
renderGraph->AddPrimitive(&m_cachedPrimitive, texture, m_clamp, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
#endif
renderGraph->AddPrimitive(&m_cachedPrimitive, image, m_clamp, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
}
////////////////////////////////////////////////////////////////////////////////////////////////////

Some files were not shown because too many files have changed in this diff Show More