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:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -187,6 +187,5 @@ public:
|
||||
MOCK_METHOD0(UnloadPlugins, void());
|
||||
MOCK_METHOD0(LoadPlugins, void());
|
||||
MOCK_METHOD1(GetSearchPath, QString(EEditorPathName));
|
||||
MOCK_METHOD0(GetEditorPanelUtils, IEditorPanelUtils* ());
|
||||
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+31
@@ -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
|
||||
)
|
||||
|
||||
+40
@@ -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
|
||||
)
|
||||
|
||||
+152
-163
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+153
@@ -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);
|
||||
|
||||
|
||||
-45
@@ -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
|
||||
|
||||
+11
-5
@@ -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
|
||||
+10
-16
@@ -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
|
||||
+10
-10
@@ -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
|
||||
+5
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user