diff --git a/Assets/Editor/Prefabs/Default_Level.prefab b/Assets/Editor/Prefabs/Default_Level.prefab new file mode 100644 index 0000000000..fb82c5ab03 --- /dev/null +++ b/Assets/Editor/Prefabs/Default_Level.prefab @@ -0,0 +1,666 @@ +{ + "Source": "Default_Level.prefab", + "ContainerEntity": { + "Id": "Entity_[1146574390643]", + "Name": "Level", + "Components": { + "Component_[10641544592923449938]": { + "$type": "EditorInspectorComponent", + "Id": 10641544592923449938 + }, + "Component_[12039882709170782873]": { + "$type": "EditorOnlyEntityComponent", + "Id": 12039882709170782873 + }, + "Component_[12265484671603697631]": { + "$type": "EditorPendingCompositionComponent", + "Id": 12265484671603697631 + }, + "Component_[14126657869720434043]": { + "$type": "EditorEntitySortComponent", + "Id": 14126657869720434043 + }, + "Component_[15230859088967841193]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 15230859088967841193, + "Parent Entity": "", + "Cached World Transform Parent": "" + }, + "Component_[16239496886950819870]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 16239496886950819870 + }, + "Component_[5688118765544765547]": { + "$type": "EditorEntityIconComponent", + "Id": 5688118765544765547 + }, + "Component_[6545738857812235305]": { + "$type": "SelectionComponent", + "Id": 6545738857812235305 + }, + "Component_[7247035804068349658]": { + "$type": "EditorPrefabComponent", + "Id": 7247035804068349658 + }, + "Component_[9307224322037797205]": { + "$type": "EditorLockComponent", + "Id": 9307224322037797205 + }, + "Component_[9562516168917670048]": { + "$type": "EditorVisibilityComponent", + "Id": 9562516168917670048 + } + }, + "IsDependencyReady": true + }, + "Entities": { + "Entity_[1155164325235]": { + "Id": "Entity_[1155164325235]", + "Name": "Sun", + "Components": { + "Component_[10440557478882592717]": { + "$type": "SelectionComponent", + "Id": 10440557478882592717 + }, + "Component_[13620450453324765907]": { + "$type": "EditorLockComponent", + "Id": 13620450453324765907 + }, + "Component_[2134313378593666258]": { + "$type": "EditorInspectorComponent", + "Id": 2134313378593666258 + }, + "Component_[234010807770404186]": { + "$type": "EditorVisibilityComponent", + "Id": 234010807770404186 + }, + "Component_[2970359110423865725]": { + "$type": "EditorEntityIconComponent", + "Id": 2970359110423865725 + }, + "Component_[3722854130373041803]": { + "$type": "EditorOnlyEntityComponent", + "Id": 3722854130373041803 + }, + "Component_[5992533738676323195]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 5992533738676323195 + }, + "Component_[7378860763541895402]": { + "$type": "AZ::Render::EditorDirectionalLightComponent", + "Id": 7378860763541895402, + "Controller": { + "Configuration": { + "Intensity": 1.0, + "CameraEntityId": "", + "ShadowFilterMethod": 1, + "ShadowmapSize": "Size1024", + "Pcf Method": 1 + } + } + }, + "Component_[7892834440890947578]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 7892834440890947578, + "Parent Entity": "Entity_[1176639161715]", + "Transform Data": { + "Translate": [ + 0.0, + 0.0, + 13.487043380737305 + ], + "Rotate": [ + -76.13099670410156, + -0.847000002861023, + -15.8100004196167 + ] + }, + "Cached World Transform": { + "Translation": [ + 0.0, + 0.0, + 9.442070960998536 + ], + "Rotation": [ + -0.6098860502243042, + -0.09055805951356888, + -0.10376212745904924, + 0.7804304361343384 + ] + }, + "Cached World Transform Parent": "Entity_[1176639161715]" + }, + "Component_[8599729549570828259]": { + "$type": "EditorEntitySortComponent", + "Id": 8599729549570828259 + }, + "Component_[952797371922080273]": { + "$type": "EditorPendingCompositionComponent", + "Id": 952797371922080273 + } + }, + "IsDependencyReady": true + }, + "Entity_[1159459292531]": { + "Id": "Entity_[1159459292531]", + "Name": "Ground", + "Components": { + "Component_[11701138785793981042]": { + "$type": "SelectionComponent", + "Id": 11701138785793981042 + }, + "Component_[12260880513256986252]": { + "$type": "EditorEntityIconComponent", + "Id": 12260880513256986252 + }, + "Component_[13711420870643673468]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 13711420870643673468 + }, + "Component_[138002849734991713]": { + "$type": "EditorOnlyEntityComponent", + "Id": 138002849734991713 + }, + "Component_[16578565737331764849]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 16578565737331764849, + "Parent Entity": "Entity_[1176639161715]", + "Cached World Transform": { + "Translation": [ + 0.0, + 0.0, + 0.0 + ] + }, + "Cached World Transform Parent": "Entity_[1176639161715]" + }, + "Component_[16919232076966545697]": { + "$type": "EditorInspectorComponent", + "Id": 16919232076966545697 + }, + "Component_[5182430712893438093]": { + "$type": "EditorMaterialComponent", + "Id": 5182430712893438093, + "materialSlots": [ + { + "id": { + "materialAssetId": { + "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "subId": 803645540 + } + } + } + ], + "materialSlotsByLod": [ + [ + { + "id": { + "lodIndex": 0, + "materialAssetId": { + "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "subId": 803645540 + } + } + } + ] + ] + }, + "Component_[5675108321710651991]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 5675108321710651991, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "subId": 277333723 + }, + "assetHint": "objects/groudplane/groundplane_521x521m.azmodel" + } + } + } + }, + "Component_[5681893399601237518]": { + "$type": "EditorEntitySortComponent", + "Id": 5681893399601237518 + }, + "Component_[592692962543397545]": { + "$type": "EditorPendingCompositionComponent", + "Id": 592692962543397545 + }, + "Component_[7090012899106946164]": { + "$type": "EditorLockComponent", + "Id": 7090012899106946164 + }, + "Component_[9410832619875640998]": { + "$type": "EditorVisibilityComponent", + "Id": 9410832619875640998 + } + }, + "IsDependencyReady": true + }, + "Entity_[1163754259827]": { + "Id": "Entity_[1163754259827]", + "Name": "Camera", + "Components": { + "Component_[11895140916889160460]": { + "$type": "EditorEntityIconComponent", + "Id": 11895140916889160460 + }, + "Component_[16880285896855930892]": { + "$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent", + "Id": 16880285896855930892, + "Controller": { + "Configuration": { + "Field of View": 55.0, + "EditorEntityId": 8929576024571800510 + } + } + }, + "Component_[17187464423780271193]": { + "$type": "EditorLockComponent", + "Id": 17187464423780271193 + }, + "Component_[17495696818315413311]": { + "$type": "EditorEntitySortComponent", + "Id": 17495696818315413311 + }, + "Component_[18086214374043522055]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 18086214374043522055, + "Parent Entity": "Entity_[1176639161715]", + "Transform Data": { + "Translate": [ + -2.300000190734864, + -3.9368600845336916, + 1.0 + ], + "Rotate": [ + -2.050307512283325, + 1.9552897214889529, + -43.62335586547852 + ] + }, + "Cached World Transform": { + "Translation": [ + -11.904647827148438, + 13.392678260803223, + -3.0449724197387697 + ], + "Rotation": [ + -0.02294669672846794, + 0.00919158011674881, + -0.37172695994377139, + 0.9280129671096802 + ] + }, + "Cached World Transform Parent": "Entity_[1176639161715]" + }, + "Component_[18387556550380114975]": { + "$type": "SelectionComponent", + "Id": 18387556550380114975 + }, + "Component_[2654521436129313160]": { + "$type": "EditorVisibilityComponent", + "Id": 2654521436129313160 + }, + "Component_[5265045084611556958]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 5265045084611556958 + }, + "Component_[7169798125182238623]": { + "$type": "EditorPendingCompositionComponent", + "Id": 7169798125182238623 + }, + "Component_[8866210352157164042]": { + "$type": "EditorInspectorComponent", + "Id": 8866210352157164042 + }, + "Component_[9129253381063760879]": { + "$type": "EditorOnlyEntityComponent", + "Id": 9129253381063760879 + } + }, + "IsDependencyReady": true + }, + "Entity_[1168049227123]": { + "Id": "Entity_[1168049227123]", + "Name": "Grid", + "Components": { + "Component_[11443347433215807130]": { + "$type": "EditorEntityIconComponent", + "Id": 11443347433215807130 + }, + "Component_[11779275529534764488]": { + "$type": "SelectionComponent", + "Id": 11779275529534764488 + }, + "Component_[14249419413039427459]": { + "$type": "EditorInspectorComponent", + "Id": 14249419413039427459 + }, + "Component_[15448581635946161318]": { + "$type": "AZ::Render::EditorGridComponent", + "Id": 15448581635946161318, + "Controller": { + "Configuration": { + "primarySpacing": 4.0, + "primaryColor": [ + 0.501960813999176, + 0.501960813999176, + 0.501960813999176 + ], + "secondarySpacing": 0.5, + "secondaryColor": [ + 0.250980406999588, + 0.250980406999588, + 0.250980406999588 + ] + } + } + }, + "Component_[1843303322527297409]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 1843303322527297409 + }, + "Component_[380249072065273654]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 380249072065273654, + "Parent Entity": "Entity_[1176639161715]", + "Cached World Transform": { + "Translation": [ + 0.0, + 0.0, + 0.0 + ] + }, + "Cached World Transform Parent": "Entity_[1176639161715]" + }, + "Component_[7476660583684339787]": { + "$type": "EditorPendingCompositionComponent", + "Id": 7476660583684339787 + }, + "Component_[7557626501215118375]": { + "$type": "EditorEntitySortComponent", + "Id": 7557626501215118375 + }, + "Component_[7984048488947365511]": { + "$type": "EditorVisibilityComponent", + "Id": 7984048488947365511 + }, + "Component_[8118181039276487398]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8118181039276487398 + }, + "Component_[9189909764215270515]": { + "$type": "EditorLockComponent", + "Id": 9189909764215270515 + } + }, + "IsDependencyReady": true + }, + "Entity_[1172344194419]": { + "Id": "Entity_[1172344194419]", + "Name": "Shader Ball", + "Components": { + "Component_[10789351944715265527]": { + "$type": "EditorOnlyEntityComponent", + "Id": 10789351944715265527 + }, + "Component_[12037033284781049225]": { + "$type": "EditorEntitySortComponent", + "Id": 12037033284781049225 + }, + "Component_[13759153306105970079]": { + "$type": "EditorPendingCompositionComponent", + "Id": 13759153306105970079 + }, + "Component_[14135560884830586279]": { + "$type": "EditorInspectorComponent", + "Id": 14135560884830586279 + }, + "Component_[16247165675903986673]": { + "$type": "EditorVisibilityComponent", + "Id": 16247165675903986673 + }, + "Component_[18082433625958885247]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 18082433625958885247 + }, + "Component_[6472623349872972660]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 6472623349872972660, + "Parent Entity": "Entity_[1176639161715]", + "Transform Data": { + "Rotate": [ + 0.0, + 0.10000000149011612, + 180.0 + ] + }, + "Cached World Transform": { + "Translation": [ + 0.0, + 0.0, + 0.0 + ], + "Rotation": [ + 0.0008726645028218627, + 0.0, + 0.9999996423721314, + 0.0 + ] + }, + "Cached World Transform Parent": "Entity_[1176639161715]" + }, + "Component_[6495255223970673916]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 6495255223970673916, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}", + "subId": 281415304 + }, + "assetHint": "objects/shaderball/shaderball_default_1m.azmodel" + } + } + } + }, + "Component_[8056625192494070973]": { + "$type": "SelectionComponent", + "Id": 8056625192494070973 + }, + "Component_[8550141614185782969]": { + "$type": "EditorEntityIconComponent", + "Id": 8550141614185782969 + }, + "Component_[9439770997198325425]": { + "$type": "EditorLockComponent", + "Id": 9439770997198325425 + } + }, + "IsDependencyReady": true + }, + "Entity_[1176639161715]": { + "Id": "Entity_[1176639161715]", + "Name": "Atom Default Environment", + "Components": { + "Component_[10757302973393310045]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 10757302973393310045, + "Parent Entity": "Entity_[1146574390643]", + "Cached World Transform": { + "Translation": [ + 0.0, + 0.0, + 0.0 + ] + }, + "Cached World Transform Parent": "Entity_[1146574390643]" + }, + "Component_[14505817420424255464]": { + "$type": "EditorInspectorComponent", + "Id": 14505817420424255464, + "ComponentOrderEntryArray": [ + { + "ComponentId": 10757302973393310045 + } + ] + }, + "Component_[14988041764659020032]": { + "$type": "EditorLockComponent", + "Id": 14988041764659020032 + }, + "Component_[15808690248755038124]": { + "$type": "SelectionComponent", + "Id": 15808690248755038124 + }, + "Component_[15900837685796817138]": { + "$type": "EditorVisibilityComponent", + "Id": 15900837685796817138 + }, + "Component_[3298767348226484884]": { + "$type": "EditorOnlyEntityComponent", + "Id": 3298767348226484884 + }, + "Component_[4076975109609220594]": { + "$type": "EditorPendingCompositionComponent", + "Id": 4076975109609220594 + }, + "Component_[5679760548946028854]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 5679760548946028854 + }, + "Component_[5855590796136709437]": { + "$type": "EditorEntitySortComponent", + "Id": 5855590796136709437, + "ChildEntityOrderEntryArray": [ + { + "EntityId": "Entity_[1155164325235]" + }, + { + "EntityId": "Entity_[1180934129011]", + "SortIndex": 1 + }, + { + "EntityId": "Entity_[1172344194419]", + "SortIndex": 2 + }, + { + "EntityId": "Entity_[1168049227123]", + "SortIndex": 3 + }, + { + "EntityId": "Entity_[1163754259827]", + "SortIndex": 4 + }, + { + "EntityId": "Entity_[1159459292531]", + "SortIndex": 5 + } + ] + }, + "Component_[9277695270015777859]": { + "$type": "EditorEntityIconComponent", + "Id": 9277695270015777859 + } + }, + "IsDependencyReady": true + }, + "Entity_[1180934129011]": { + "Id": "Entity_[1180934129011]", + "Name": "Global Sky", + "Components": { + "Component_[11231930600558681245]": { + "$type": "AZ::Render::EditorHDRiSkyboxComponent", + "Id": 11231930600558681245, + "Controller": { + "Configuration": { + "CubemapAsset": { + "assetId": { + "guid": "{215E47FD-D181-5832-B1AB-91673ABF6399}", + "subId": 1000 + }, + "assetHint": "lightingpresets/highcontrast/goegap_4k_skyboxcm.exr.streamingimage" + } + } + } + }, + "Component_[11980494120202836095]": { + "$type": "SelectionComponent", + "Id": 11980494120202836095 + }, + "Component_[1428633914413949476]": { + "$type": "EditorLockComponent", + "Id": 1428633914413949476 + }, + "Component_[14936200426671614999]": { + "$type": "AZ::Render::EditorImageBasedLightComponent", + "Id": 14936200426671614999, + "Controller": { + "Configuration": { + "diffuseImageAsset": { + "assetId": { + "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}", + "subId": 3000 + }, + "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_ibldiffuse.exr.streamingimage" + }, + "specularImageAsset": { + "assetId": { + "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}", + "subId": 2000 + }, + "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_iblspecular.exr.streamingimage" + } + } + } + }, + "Component_[14994774102579326069]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 14994774102579326069 + }, + "Component_[15417479889044493340]": { + "$type": "EditorPendingCompositionComponent", + "Id": 15417479889044493340 + }, + "Component_[15826613364991382688]": { + "$type": "EditorEntitySortComponent", + "Id": 15826613364991382688 + }, + "Component_[1665003113283562343]": { + "$type": "EditorOnlyEntityComponent", + "Id": 1665003113283562343 + }, + "Component_[3704934735944502280]": { + "$type": "EditorEntityIconComponent", + "Id": 3704934735944502280 + }, + "Component_[5698542331457326479]": { + "$type": "EditorVisibilityComponent", + "Id": 5698542331457326479 + }, + "Component_[6644513399057217122]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 6644513399057217122, + "Parent Entity": "Entity_[1176639161715]", + "Cached World Transform": { + "Translation": [ + 0.0, + 0.0, + 0.0 + ] + }, + "Cached World Transform Parent": "Entity_[1176639161715]" + }, + "Component_[931091830724002070]": { + "$type": "EditorInspectorComponent", + "Id": 931091830724002070 + } + }, + "IsDependencyReady": true + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings index 5c4c862583..b65133fbb0 100644 --- a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings +++ b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /preset=AlbedoWithGenericAlpha /reduce="es3:2,ios:2,osx_gl:0,pc:0,provo:0" \ No newline at end of file +/autooptimizefile=0 /preset=AlbedoWithGenericAlpha /reduce="android:2,ios:2,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings index 441a11bc68..e8da408b36 100644 --- a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings +++ b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /preset=Albedo /reduce="es3:3,ios:3,osx_gl:0,pc:0,provo:0" \ No newline at end of file +/autooptimizefile=0 /preset=Albedo /reduce="android:3,ios:3,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Gem/Code/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/tool_dependencies.cmake index e2e57d4012..1d70c02b1c 100644 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/tool_dependencies.cmake @@ -12,7 +12,7 @@ # Extracted from Editor.xml set(GEM_DEPENDENCIES Gem::Maestro.Editor - Gem::TextureAtlas + Gem::TextureAtlas.Editor Gem::LmbrCentral.Editor Gem::LyShine.Editor Gem::HttpRequestor diff --git a/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt index b406ea77de..f463210cb0 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt @@ -16,16 +16,16 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) # Enable after installing NodeJS and CDK on jenkins Windows AMI. - #ly_add_pytest( - # NAME AutomatedTesting::AWSTests - # TEST_SUITE periodic - # TEST_SERIAL - # PATH ${CMAKE_CURRENT_LIST_DIR}/AWS/${PAL_PLATFORM_NAME}/ - # RUNTIME_DEPENDENCIES - # Legacy::Editor - # AZ::AssetProcessor - # AutomatedTesting.Assets - # COMPONENT - # AWS - #) + ly_add_pytest( + NAME AutomatedTesting::AWSTests + TEST_SUITE periodic + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/${PAL_PLATFORM_NAME}/ + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + AWS + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/__init__.py new file mode 100644 index 0000000000..8caef52682 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/__init__.py @@ -0,0 +1,11 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/__init__.py new file mode 100644 index 0000000000..8caef52682 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/__init__.py @@ -0,0 +1,11 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/__init__.py new file mode 100644 index 0000000000..8caef52682 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/__init__.py @@ -0,0 +1,11 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py index 5997701870..7b9c549f6c 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py @@ -68,6 +68,7 @@ class TestAWSClientAuthAnonymousCredentials(object): log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor) launcher.args = ['+LoadLevel', level] + launcher.args.extend(['-rhi=null']) with launcher.start(launch_ap=False): result = log_monitor.monitor_log_for_lines( diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py index da4898b8a9..89b859dd0f 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py @@ -67,6 +67,7 @@ class TestAWSClientAuthPasswordSignIn(object): log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor) launcher.args = ['+LoadLevel', 'AWS/ClientAuthPasswordSignUp'] + launcher.args.extend(['-rhi=null']) with launcher.start(launch_ap=False): result = log_monitor.monitor_log_for_lines( @@ -87,6 +88,7 @@ class TestAWSClientAuthPasswordSignIn(object): ) launcher.args = ['+LoadLevel', 'AWS/ClientAuthPasswordSignIn'] + launcher.args.extend(['-rhi=null']) with launcher.start(launch_ap=False): result = log_monitor.monitor_log_for_lines( diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py index b3fa3011ce..dc462e0556 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py @@ -10,8 +10,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ import os +from os.path import abspath import pytest import json +import logging + +logger = logging.getLogger(__name__) AWS_RESOURCE_MAPPINGS_KEY = 'AWSResourceMappings' AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY = 'AccountId' @@ -57,9 +61,9 @@ class ResourceMappings: stacks = response.get('Stacks', []) assert len(stacks) == 1, f'{stack_name} is invalid.' - self.__write_resource_mappings(stacks[0].get('Outputs', [])) + self._write_resource_mappings(stacks[0].get('Outputs', [])) - def __write_resource_mappings(self, outputs, append_feature_name = True) -> None: + def _write_resource_mappings(self, outputs, append_feature_name = True) -> None: with open(self._resource_mapping_file_path) as file_content: resource_mappings = json.load(file_content) @@ -129,8 +133,10 @@ def resource_mappings( :return: ResourceMappings class object. """ - path = f'{workspace.paths.engine_root()}\\{project}\\Config\\{resource_mappings_filename}' - resource_mappings_obj = ResourceMappings(path, aws_utils.assume_session().region_name, feature_name, + path = f'{workspace.paths.engine_root()}/{project}/Config/{resource_mappings_filename}' + logger.info(f'Resource mapping path : {path}') + logger.info(f'Resource mapping resolved path : {abspath(path)}') + resource_mappings_obj = ResourceMappings(abspath(path), aws_utils.assume_session().region_name, feature_name, aws_utils.assume_account_id(), workspace, aws_utils.client('cloudformation')) diff --git a/AutomatedTesting/Gem/PythonTests/AWS/common/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/common/__init__.py new file mode 100644 index 0000000000..8caef52682 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/common/__init__.py @@ -0,0 +1,11 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + diff --git a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py index 818dc23079..ecf08cfcbd 100644 --- a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py +++ b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py @@ -31,13 +31,13 @@ class TestPythonAssetProcessing(object): unexpected_lines = [] expected_lines = [ 'Mock asset exists', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel) found' + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found' ] timeout = 180 halt_on_unexpected = False diff --git a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py index cd9adfdbcf..a7907778b2 100644 --- a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py +++ b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py @@ -38,16 +38,16 @@ def test_azmodel_product(generatedModelAssetPath, expectedSubId): assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False) assetIdString = assetId.to_string() if (assetIdString.endswith(':' + expectedSubId) is False): - raise_and_stop(f'Asset has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath})!') + raise_and_stop(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath}), expected {expectedSubId}!') else: print(f'Expected subId for asset ({generatedModelAssetPath}) found') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel', '10315ae0') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel', '10661093') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel', '10af8810') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel', '10f8c263') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel', '100ac47f') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel', '105d8e0c') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel', '1002d464') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive.azmodel', '1024be55') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative.azmodel', '1052c94e') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive.azmodel', '10130556') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative.azmodel', '1065724d') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive.azmodel', '10d16e68') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative.azmodel', '10a71973') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel', '10412075') azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt') diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py index e729ee9882..9a5b93ca80 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py @@ -34,10 +34,10 @@ def ap_all_platforms_setup_fixture(request, workspace, ap_setup_fixture) -> Dict # Specific platform cache locations resources["pc_cache_location"] = os.path.join(cache_dir, "pc") - resources["es3_cache_location"] = os.path.join(cache_dir, "es3") + resources["android_cache_location"] = os.path.join(cache_dir, "android") resources["ios_cache_location"] = os.path.join(cache_dir, "ios") - resources["osx_gl_cache_location"] = os.path.join(cache_dir, "osx_gl") + resources["mac_cache_location"] = os.path.join(cache_dir, "mac") resources["provo_cache_location"] = os.path.join(cache_dir, "provo") - resources["all_platforms"] = ["pc", "es3", "ios", "osx_gl", "provo"] + resources["all_platforms"] = ["pc", "android", "ios", "mac", "provo"] return resources diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py index 580816e7b5..7a85cb1813 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py @@ -54,7 +54,7 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> platforms = [platform.strip() for platform in platforms.split(",")] else: # No commandline argument provided, default to mac and pc - platforms = ["pc", "osx_gl"] + platforms = ["pc", "mac"] class BundlerBatchFixture: """ @@ -241,11 +241,11 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> def get_platform_flag(self, platform_name: str) -> int: if (platform_name == "pc"): return 1 - elif (platform_name == "es3"): + elif (platform_name == "android"): return 2 elif (platform_name == "ios"): return 4 - elif (platform_name == "osx_gl"): + elif (platform_name == "mac"): return 8 elif (platform_name == "server"): return 128 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py index d236e87aa2..8738e8acdf 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py @@ -460,9 +460,9 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ helper = bundler_batch_helper # fmt:off - assert "pc" in helper["platforms"] and "osx_gl" in helper["platforms"], \ + assert "pc" in helper["platforms"] and "mac" in helper["platforms"], \ "This test requires both PC and MAC platforms to be enabled. " \ - "Please rerun with commandline option: '--bundle_platforms=pc,osx_gl'" + "Please rerun with commandline option: '--bundle_platforms=pc,mac'" # fmt:on seed_list = os.path.join(workspace.paths.engine_root(), "Engine", "SeedAssetList.seed") # Engine seed list @@ -502,7 +502,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): for bundle_file in bundle_files.values(): assert os.path.isfile(bundle_file) - # This asset is created on osx_gl platform but not on windows + # This asset is created on mac platform but not on windows file_to_check = b"engineassets/shading/defaultprobe_cm.dds.5" # [use byte str because file is in binary] # Extract the delta catalog file from pc archive. {file_to_check} SHOULD NOT be present for PC @@ -512,11 +512,11 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): f"{file_to_check} was found in DeltaCatalog.xml in pc bundle file {bundle_files['pc']}" # fmt:on - # Extract the delta catalog file from osx_gl archive. {file_to_check} SHOULD be present for MAC - file_contents = helper.extract_file_content(bundle_files["osx_gl"], "DeltaCatalog.xml") + # Extract the delta catalog file from mac archive. {file_to_check} SHOULD be present for MAC + file_contents = helper.extract_file_content(bundle_files["mac"], "DeltaCatalog.xml") # fmt:off assert file_to_check in file_contents, \ - f"{file_to_check} was not found in DeltaCatalog.xml in darwin bundle file {bundle_files['osx_gl']}" + f"{file_to_check} was not found in DeltaCatalog.xml in darwin bundle file {bundle_files['mac']}" # fmt:on # Gather checksums for first set of bundles @@ -613,7 +613,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], addSeed=test_asset, - platform="pc,osx_gl", + platform="pc,mac", ) # Validate both mac and pc are activated for seed @@ -626,7 +626,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], removePlatformFromSeeds="", - platform="osx_gl", + platform="mac", ) # Validate only pc platform for seed. Save file contents to variable all_lines = check_seed_platform(helper["seed_list_file"], test_asset, helper["platform_values"]["pc"]) @@ -646,7 +646,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], addPlatformToSeeds="", - platform="osx_gl", + platform="mac", ) # Validate Mac platform was added back on. Save file contents # fmt:off @@ -670,7 +670,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], removeSeed=test_asset, - platform="pc,osx_gl", + platform="pc,mac", ) # Validate seed was removed from file @@ -697,9 +697,9 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): env = ap_setup_fixture # fmt:off - assert "pc" in helper["platforms"] and "osx_gl" in helper["platforms"], \ + assert "pc" in helper["platforms"] and "mac" in helper["platforms"], \ "This test requires both PC and MAC platforms to be enabled. " \ - "Please rerun with commandline option: '--bundle_platforms=pc,osx_gl'" + "Please rerun with commandline option: '--bundle_platforms=pc,mac'" # fmt:on # Test assets arranged in common lists: six (0-5) .txt files and .dat files @@ -717,16 +717,16 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): file_platforms = { "txtfile_0.txt": "pc", "txtfile_1.txt": "pc", - "txtfile_2.txt": "pc,osx_gl", - "txtfile_3.txt": "pc,osx_gl", - "txtfile_4.txt": "osx_gl", - "txtfile_5.txt": "osx_gl", + "txtfile_2.txt": "pc,mac", + "txtfile_3.txt": "pc,mac", + "txtfile_4.txt": "mac", + "txtfile_5.txt": "mac", "datfile_0.dat": "pc", "datfile_1.dat": "pc", - "datfile_2.dat": "pc,osx_gl", - "datfile_3.dat": "pc,osx_gl", - "datfile_4.dat": "osx_gl", - "datfile_5.dat": "osx_gl", + "datfile_2.dat": "pc,mac", + "datfile_3.dat": "pc,mac", + "datfile_4.dat": "mac", + "datfile_5.dat": "mac", } # Comparison rules files and their associated 'comparisonType' flags @@ -741,7 +741,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Get our test assets ready and processed utils.prepare_test_assets(env["tests_dir"], "C16877178", env["project_test_assets_dir"]) - asset_processor.batch_process(timeout=timeout, fastscan=False, platforms="pc,osx_gl") + asset_processor.batch_process(timeout=timeout, fastscan=False, platforms="pc,mac") # *** Some helper functions *** # @@ -759,7 +759,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_assetLists( assetListFile=os.path.join(helper["test_dir"], asset_list_file_name), seedListFile=os.path.join(helper["test_dir"], seed_file_name), - platform="pc,osx_gl", + platform="pc,mac", ) def get_platform_assets(asset_name_list: List[str]) -> Dict[str, List[str]]: @@ -769,7 +769,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): for asset_name in asset_name_list: if "pc" in file_platforms[asset_name]: win_assets.append(asset_name) - if "osx_gl" in file_platforms[asset_name]: + if "mac" in file_platforms[asset_name]: mac_assets.append(asset_name) return {"win": win_assets, "mac": mac_assets} @@ -798,7 +798,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Get platform result file names win_asset_list_file = helper.platform_file_name(request_file, platforms["pc"]) - mac_asset_list_file = helper.platform_file_name(request_file, platforms["osx_gl"]) + mac_asset_list_file = helper.platform_file_name(request_file, platforms["mac"]) # Get expected platforms for each asset in asset_names platform_files = get_platform_assets(asset_names) @@ -879,14 +879,14 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # fmt:on # End verify_asset_list_contents() - def run_compare_command_and_verify(platform_arg: str, expect_pc_output: bool, expect_osx_gl_output: bool) -> None: + def run_compare_command_and_verify(platform_arg: str, expect_pc_output: bool, expect_mac_output: bool) -> None: # Expected asset list to equal result of comparison expected_pc_asset_list = None - expected_osx_gl_asset_list = None + expected_mac_asset_list = None # Last output file. Use this for comparison to 'expected' output_pc_asset_list = None - output_osx_gl_asset_list = None + output_mac_asset_list = None # Add the platform to the file name to match what the Bundler will create last_output_arg = output_arg.split(",")[-1] @@ -895,10 +895,10 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): expected_pc_asset_list = os.path.join(helper["test_dir"], helper.platform_file_name(expected_asset_list, platform)) output_pc_asset_list = helper.platform_file_name(last_output_arg, platform) - if expect_osx_gl_output: - platform = platforms["osx_gl"] - expected_osx_gl_asset_list = os.path.join(helper["test_dir"], helper.platform_file_name(expected_asset_list, platform)) - output_osx_gl_asset_list = helper.platform_file_name(last_output_arg, platform) + if expect_mac_output: + platform = platforms["mac"] + expected_mac_asset_list = os.path.join(helper["test_dir"], helper.platform_file_name(expected_asset_list, platform)) + output_mac_asset_list = helper.platform_file_name(last_output_arg, platform) # Build execution command cmd = generate_compare_command(platform_arg) @@ -911,15 +911,15 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): verify_asset_list_contents(expected_pc_asset_list, output_pc_asset_list) fs.delete([output_pc_asset_list], True, True) - if expect_osx_gl_output: - verify_asset_list_contents(expected_osx_gl_asset_list, output_osx_gl_asset_list) - fs.delete([output_osx_gl_asset_list], True, True) + if expect_mac_output: + verify_asset_list_contents(expected_mac_asset_list, output_mac_asset_list) + fs.delete([output_mac_asset_list], True, True) # End run_compare_command_and_verify() # Generate command, run and validate for each platform run_compare_command_and_verify("pc", True, False) - run_compare_command_and_verify("osx_gl", False, True) - run_compare_command_and_verify("pc,osx_gl", True, True) + run_compare_command_and_verify("mac", False, True) + run_compare_command_and_verify("pc,mac", True, True) #run_compare_command_and_verify(None, True, True) # End compare_and_check() diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py index 50b3af1438..0d830b39e2 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py @@ -102,7 +102,7 @@ class TestsAssetProcessorBatch_AllPlatforms(object): def test_RunAPBatch_TwoPlatforms_ExitCodeZero(self, asset_processor): asset_processor.create_temp_asset_root() asset_processor.enable_asset_processor_platform("pc") - asset_processor.enable_asset_processor_platform("osx_gl") + asset_processor.enable_asset_processor_platform("mac") result, _ = asset_processor.batch_process() assert result, "AP Batch failed" diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py index b64a592c1d..fccd750573 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py @@ -26,19 +26,23 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("level", ["auto_test"]) class TestAtomEditorComponentsMain(object): + """Holds tests for Atom components.""" - @pytest.mark.test_case_id( - "C32078130", # Display Mapper - "C32078129", # Light - "C32078131", # Radius Weight Modifier - "C32078127", # PostFX Layer - "C32078125", # Physical Sky - "C32078115", # Global Skylight (IBL) - "C32078121", # Exposure Control - "C32078120", # Directional Light - "C32078119", # DepthOfField - "C32078118") # Decal (Atom) def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform): + """ + Please review the hydra script run by this test for more specific test info. + Tests the following Atom components and verifies all "expected_lines" appear in Editor.log: + 1. Display Mapper + 2. Light + 3. Radius Weight Modifier + 4. PostFX Layer + 5. Physical Sky + 6. Global Skylight (IBL) + 7. Exposure Control + 8. Directional Light + 9. DepthOfField + 10. Decal (Atom) + """ cfg_args = [level] expected_lines = [ diff --git a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt index e8f3349df4..834254134e 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt @@ -39,4 +39,19 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ COMPONENT Editor ) + + ly_add_pytest( + NAME AutomatedTesting::EditorTests_Sandbox + TEST_SUITE sandbox + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR} + PYTEST_MARKS "SUITE_sandbox" + TIMEOUT 1500 + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + Editor + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py b/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py index c2d515e250..f887560a19 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py @@ -39,7 +39,7 @@ class TestDocking(object): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) @pytest.mark.test_case_id("C6376081") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_sandbox def test_Docking_BasicDockedTools(self, request, editor, level, launcher_platform): expected_lines = [ "The tools are all docked together in a tabbed widget", diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index 70a22f9e2a..c2da1343de 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -39,7 +39,7 @@ class TestMenus(object): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) @pytest.mark.test_case_id("C16780783", "C2174438") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_sandbox def test_Menus_EditMenuOptions_Work(self, request, editor, level, launcher_platform): expected_lines = [ "Undo Action triggered", @@ -113,7 +113,7 @@ class TestMenus(object): ) @pytest.mark.test_case_id("C16780778") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_sandbox def test_Menus_FileMenuOptions_Work(self, request, editor, level, launcher_platform): expected_lines = [ "New Level Action triggered", diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt index 72e3bec3df..c7fd43c7b2 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt @@ -13,22 +13,21 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ ## DynVeg ## - # Temporarily moving all tests to periodic suite - SPEC-6553 - #ly_add_pytest( - # NAME AutomatedTesting::DynamicVegetationTests_Main - # TEST_SERIAL - # TEST_SUITE main - # PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - # PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark" - # TIMEOUT 1500 - # RUNTIME_DEPENDENCIES - # AZ::AssetProcessor - # Legacy::Editor - # AutomatedTesting.GameLauncher - # AutomatedTesting.Assets - # COMPONENT - # LargeWorlds - #) + ly_add_pytest( + NAME AutomatedTesting::DynamicVegetationTests_Main + TEST_SERIAL + TEST_SUITE main + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg + PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark" + TIMEOUT 1500 + RUNTIME_DEPENDENCIES + AZ::AssetProcessor + Legacy::Editor + AutomatedTesting.GameLauncher + AutomatedTesting.Assets + COMPONENT + LargeWorlds + ) ly_add_pytest( @@ -137,21 +136,21 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ LargeWorlds ) ## LandscapeCanvas ## - # Temporarily moving all tests to periodic suite - SPEC-6553 - #ly_add_pytest( - # NAME AutomatedTesting::LandscapeCanvasTests_Main - # TEST_SERIAL - # TEST_SUITE main - # PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/landscape_canvas - # PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark" - # TIMEOUT 1500 - # RUNTIME_DEPENDENCIES - # AZ::AssetProcessor - # Legacy::Editor - # AutomatedTesting.Assets - # COMPONENT - # LargeWorlds - #) + + ly_add_pytest( + NAME AutomatedTesting::LandscapeCanvasTests_Main + TEST_SERIAL + TEST_SUITE main + PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas + PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark" + TIMEOUT 1500 + RUNTIME_DEPENDENCIES + AZ::AssetProcessor + Legacy::Editor + AutomatedTesting.Assets + COMPONENT + LargeWorlds + ) ly_add_pytest( NAME AutomatedTesting::LandscapeCanvasTests_Periodic diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py index 4f58a23a19..730a557a9e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py @@ -23,6 +23,25 @@ class TestSurfaceMaskFilter_BasicSurfaceTagCreation(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="TestSurfaceMaskFilter_BasicSurfaceTagCreation", args=["level"]) def run_test(self): + """ + Summary: + Verifies basic surface tag value equality + + Expected Behavior: + Surface tags of the same name are equal, and different names aren't. + + Test Steps: + 1) Open level + 2) Create 2 new surface tags of identical names and verify they resolve as equal. + 3) Create another new tag of a different name and verify they resolve as different. + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ self.log("SurfaceTag test started") # Create a level diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py index c25761d655..46c5483988 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py @@ -33,6 +33,25 @@ class TestVegetationInstances_DespawnWhenOutOfRange(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix='VegetationInstances_DespawnWhenOutOfRange', args=['level']) def run_test(self): + """ + Summary: + Verifies that vegetation instances properly spawn/despawn based on camera range. + + Expected Behavior: + Vegetation instances despawn when out of camera range. + + Test Steps: + 1) Create a new level + 2) Create a simple vegetation area, and set the view position near the spawner. Verify instances plant. + 3) Move the view position away from the spawner. Verify instances despawn. + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ # Create a new level self.test_success = self.create_level( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py index 9898570692..ead1e8779c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py @@ -41,7 +41,7 @@ class TestDynamicSliceInstanceSpawner(object): return console @pytest.mark.test_case_id("C28851763") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_main @pytest.mark.dynveg_area @pytest.mark.parametrize("launcher_platform", ['windows_editor']) def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, editor, level, workspace, project, diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py index 7bd8484cf4..ca71cd2137 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py @@ -37,7 +37,7 @@ class TestEmptyInstanceSpawner(object): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) @pytest.mark.test_case_id("C28851762") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_main @pytest.mark.dynveg_area def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, editor, level, launcher_platform): cfg_args = [level] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py index cc9a15bba0..c37bc9780f 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py @@ -28,8 +28,21 @@ class TestGradientGeneratorIncompatibilities(EditorTestHelper): def run_test(self): """ Summary: - Verify that Entities are not active when a Gradient Generator and incompatible component are both present - on the same Entity. + This test verifies that components are disabled when conflicting components are present on the same entity. + + Expected Behavior: + Gradient Generator components are incompatible with Vegetation area components. + + Test Steps: + 1) Create a new level + 2) Create a new entity in the level + 3) Add each Gradient Generator component to an entity, and add a Vegetation Area component to the same entity + 4) Verify that components are only enabled when entity is free of a conflicting component + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py index b7d12d074a..f2edc2924e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py @@ -28,8 +28,21 @@ class TestGradientModifiersIncompatibilities(EditorTestHelper): def run_test(self): """ Summary: - Verify that Entities are not active when a Gradient Modifier and incompatible component are both present - on the same Entity. + This test verifies that components are disabled when conflicting components are present on the same entity. + + Expected Behavior: + Gradient Modifier components are incompatible with Vegetation area components. + + Test Steps: + 1) Create a new level + 2) Create a new entity in the level + 3) Add each Gradient Modifier component to an entity, and add a Vegetation Area component to the same entity + 4) Verify that components are only enabled when entity is free of a conflicting component + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py index c37ee36265..45da74d6cd 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py @@ -9,19 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ -""" -The below cases are combined in this script -C2676829 -C3961326 -C3980659 -C3980664 -C3980669 -C3416548 -C2676823 -C3961321 -C2676826 -""" - import os import sys diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py index 5a758b9d89..b8f4114d30 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py @@ -44,7 +44,21 @@ class TestGradientPreviewSettings(EditorTestHelper): def run_test(self): """ Summary: - Verify if the current entity is set to the pin preview to shape entity by default for several components. + This test verifies default values for the pinned entity for Gradient Preview settings. + + Expected Behavior: + Pinned entity is self for all gradient generator/modifiers. + + Test Steps: + 1) Create a new level + 2) Create a new entity in the level + 3) Add each Gradient Generator component to an entity, and verify the Pin Preview to Shape property is set to + self + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py index a16e37e0fc..8e2d0611af 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py @@ -31,11 +31,21 @@ class TestGradientSurfaceTagEmitterDependencies(EditorTestHelper): def run_test(self): """ Summary: - Component has a dependency on a Gradient component + This test verifies that the Gradient Surface Tag Emitter component is dependent on a gradient component. Expected Result: - Component is disabled until a Gradient Generator, Modifier or Gradient Reference component - (and any sub-dependencies) is added to the entity. + Gradient Surface Tag Emitter component is disabled until a Gradient Generator, Modifier or Gradient Reference + component (and any sub-dependencies) is added to the entity. + + Test Steps: + 1) Open level + 2) Create a new entity with a Gradient Surface Tag Emitter component + 3) Verify the component is disabled until a dependent component is also added to the entity + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py index e1e901f2f7..2311363db9 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py @@ -28,8 +28,20 @@ class TestGradientTransformRequiresShape(EditorTestHelper): def run_test(self): """ Summary: - Verify that Gradient Transform Modifier component requires a - Shape component before the Entity can become active. + This test verifies that the Gradient Transform Modifier component is dependent on a shape component. + + Expected Result: + Gradient Transform Modifier component is disabled until a shape component is added to the entity. + + Test Steps: + 1) Open level + 2) Create a new entity with a Gradient Transform Modifier component + 3) Verify the component is disabled until a shape component is also added to the entity + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py index dab8e6928a..a5d9632fd6 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py @@ -28,8 +28,20 @@ class TestImageGradientRequiresShape(EditorTestHelper): def run_test(self): """ Summary: - Verify that Image Gradient component requires a - Shape component before the Entity can become active. + This test verifies that the Image Gradient component is dependent on a shape component. + + Expected Result: + Gradient Transform Modifier component is disabled until a shape component is added to the entity. + + Test Steps: + 1) Open level + 2) Create a new entity with a Image Gradient component + 3) Verify the component is disabled until a shape component is also added to the entity + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py index d1e0b68ef4..c41d153cfa 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py @@ -33,6 +33,26 @@ class TestAreaNodeComponentDependency(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="AreaNodeComponentDependency", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with + proper dependent components. + + Expected Behavior: + All expected component dependencies are met when adding an area node to a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the area nodes to the graph area, and ensure the proper dependent components are added + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py index 4e429a192b..fb977b4987 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py @@ -33,7 +33,25 @@ class TestGradientNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="AreaNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + Expected Behavior: + New entities are created when dragging area nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the area nodes to the graph area, and ensure a new entity is created + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId newEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py index 38f8641b4c..57ba8fc006 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py @@ -34,7 +34,26 @@ class TestAreaNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="AreaNodeEntityDelete", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when area nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the area nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global createdEntityId createdEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py index 26062c01f8..60527b64d2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py @@ -9,24 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ - -""" -C22602072 - Graph is updated when underlying components are added/removed - -1. Open Level. -2. Find LandscapeCanvas named entity. -3. Ensure Vegetation Distribution Component is present on the BushSpawner entity. -4. Open graph and ensure Distribution Filter wrapped node is present. -5. Delete the Vegetation Distribution Filter component from the BushSpawner entity via Entity Inspector. -6. Ensure the Vegetation Distribution Filter component was deleted from the BushSpawner entity and node is no longer -present in the graph. -7. Add Vegetation Altitude Filter to the BushSpawner entity through Entity Inspector. -8. Ensure Altitude Filter was added to the BushSpawner node in the open graph. -9. Add a new entity with unique name as a child of the Landscape Canvas entity. -10. Add a Box Shape component to the new child entity. -11. Ensure Box Shape node is present on the open graph. -""" - import os import sys @@ -50,6 +32,36 @@ class TestComponentUpdatesUpdateGraph(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="ComponentUpdatesUpdateGraph", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas graphs update properly when components are added/removed outside of + Landscape Canvas. + + Expected Behavior: + Graphs properly reflect component changes made to entities outside of Landscape Canvas. + + Test Steps: + 1. Open Level + 2. Find LandscapeCanvas named entity + 3. Ensure Vegetation Distribution Component is present on the BushSpawner entity + 4. Open graph and ensure Distribution Filter wrapped node is present + 5. Delete the Vegetation Distribution Filter component from the BushSpawner entity via Entity Inspector + 6. Ensure the Vegetation Distribution Filter component was deleted from the BushSpawner entity and node is + no longer present in the graph + 7. Add Vegetation Altitude Filter to the BushSpawner entity through Entity Inspector + 8. Ensure Altitude Filter was added to the BushSpawner node in the open graph + 9. Add a new entity with unique name as a child of the Landscape Canvas entity + 10. Add a Box Shape component to the new child entity + 11. Ensure Box Shape node is present on the open graph + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + # Create a new empty level and instantiate LC_BushFlowerBlender.slice self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py index 5fed13985d..4b5e03abbc 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py @@ -37,6 +37,25 @@ class TestCreateNewGraph(EditorTestHelper): print("New root entity created") def run_test(self): + """ + Summary: + This test verifies that new graphs can be created in Landscape Canvas. + + Expected Behavior: + New graphs can be created, and proper entity is created to hold graph data with a Landscape Canvas component. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Ensures the root entity created contains a Landscape Canvas component + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ self.test_success = self.create_level( self.args["level"], heightmap_resolution=128, diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py index 7fd3f075e0..81e24b20e1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py @@ -33,7 +33,25 @@ class TestDisabledNodeDuplication(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="DisabledNodeDuplication", args=["level"]) def run_test(self): + """ + Summary: + This test verifies Editor stability after duplicating disabled Landscape Canvas nodes. + Expected Behavior: + Editor remains stable and free of crashes. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Create several new nodes, disable the nodes via disabling/deleting components, and duplicate the nodes + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId newEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py index 27ab6fded3..61c4cf9ac2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py @@ -9,17 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ - -""" -C30813586 - Editor remains stable after Undoing deletion of a node on a slice entity - -1. Open level with instantiated slice. -2. Open the graph. -3. Find the BushSpawner's Vegetation Layer Spawner node. -4. Delete the node. -5. Undo to restore the node. -""" - import os import sys @@ -44,7 +33,26 @@ class TestUndoNodeDeleteSlice(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="UndoNodeDeleteSlice", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies Editor stability after undoing the deletion of nodes on a slice entity. + + Expected Behavior: + Editor remains stable and free of crashes. + + Test Steps: + 1) Create a new level + 2) Instantiate a slice with a Landscape Canvas setup + 3) Find a specific node on the graph, and delete it + 4) Restore the node with Undo + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ # Create a new empty level and instantiate LC_BushFlowerBlender.slice self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py index ca3bc04f47..124baf9d2e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py @@ -34,6 +34,27 @@ class TestGradientMixerNodeConstruction(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientMixerNodeConstruction", args=["level"]) def run_test(self): + """ + Summary: + This test verifies a Gradient Mixer vegetation setup can be constructed through Landscape Canvas. + + Expected Behavior: + Entities contain all required components and component references after creating nodes and setting connections + on a Landscape Canvas graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Add all necessary nodes to the graph and set connections to form a Gradient Mixer setup + 4) Verify all components and component references were properly set during graph construction + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py index d40b19e7db..aa98eb3dc3 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py @@ -33,6 +33,25 @@ class TestGradientModifierNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientModifierNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + + Expected Behavior: + New entities are created when dragging Gradient Modifier nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py index dc263924d1..6a82b05039 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py @@ -34,7 +34,26 @@ class TestGradientModifierNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientModifierNodeEntityDelete", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when Gradient Modifier nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global createdEntityId createdEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py index 5e203e1892..f9360fe356 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py @@ -33,6 +33,27 @@ class TestGradientNodeComponentDependency(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientNodeComponentDependency", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with + proper dependent components. + + Expected Behavior: + All expected component dependencies are met when adding a Gradient Modifier node to a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient Modifier nodes to the graph area, and ensure the proper dependent components are + added + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py index 6d4a2f58a7..8aaad9b81d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py @@ -32,6 +32,25 @@ class TestGradientNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + + Expected Behavior: + New entities are created when dragging Gradient nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py index 2b49e3a911..d74b86d0bf 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py @@ -34,6 +34,26 @@ class TestGradientNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientNodeEntityDelete", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when Gradient nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global createdEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py index d3ad5c1c1e..6aa539b554 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py @@ -31,6 +31,26 @@ class TestGraphClosedOnEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphClosedOnEntityDelete", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that Landscape Canvas graphs are auto-closed when the corresponding entity is deleted. + + Expected Behavior: + When a Landscape Canvas root entity is deleted, the corresponding graph automatically closes. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Delete the automatically created entity + 4) Verify the open graph is closed + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newRootEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py index b7b0008eb2..ebc75ab621 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py @@ -29,7 +29,26 @@ class TestGraphClosedOnLevelChange(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphClosedOnLevelChange", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that Landscape Canvas graphs are auto-closed when the currently open level changes. + + Expected Behavior: + When a new level is loaded in the Editor, open Landscape Canvas graphs are automatically closed. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Open a different level + 4) Verify the open graph is closed + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ # Create a new empty level self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py index efd1cc5a55..4b018aeb45 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py @@ -29,6 +29,26 @@ class TestGraphClosedTabbedGraph(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphClosedTabbedGraph", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that Landscape Canvas tabbed graphs can be independently closed. + + Expected Behavior: + Closing a tabbed graph only closes the appropriate graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create several new graphs + 3) Close one of the open graphs + 4) Ensure the graph properly closed, and other open graphs remain open + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ # Create a new empty level self.test_success = self.create_level( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py index f350d37178..f94a6c2e3a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py @@ -9,21 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ - -""" -C22715182 - Components are updated when nodes are added/removed/updated - -1. Open Level. -2. Open the graph on LC_BushFlowerBlender.slice -3. Find the Rotation Modifier node on the BushSpawner entity -4. Delete the Rotation Modifier node -5. Ensure the Vegetation Rotation Modifier component is removed from the BushSpawner entity -6. Delete the Vegetation Layer Spawner node from the graph -7. Ensure BushSpawner entity is deleted -8. Change connection from second Rotation Modifier node to a different Gradient -9. Ensure Gradient reference on component is updated -""" - import os import sys @@ -50,6 +35,31 @@ class TestGraphUpdatesUpdateComponents(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphUpdatesUpdateComponents", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that components are properly updated as nodes are added/removed/updated. + + Expected Behavior: + Landscape Canvas node CRUD properly updates component entities. + + Test Steps: + 1. Open Level. + 2. Open the graph on LC_BushFlowerBlender.slice + 3. Find the Rotation Modifier node on the BushSpawner entity + 4. Delete the Rotation Modifier node + 5. Ensure the Vegetation Rotation Modifier component is removed from the BushSpawner entity + 6. Delete the Vegetation Layer Spawner node from the graph + 7. Ensure BushSpawner entity is deleted + 8. Change connection from second Rotation Modifier node to a different Gradient + 9. Ensure Gradient reference on component is updated + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ # Create a new empty level and instantiate LC_BushFlowerBlender.slice self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py index 176429885f..c3857e1393 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py @@ -30,6 +30,26 @@ class TestLandscapeCanvasComponentAddedRemoved(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="LandscapeCanvasComponentAddedRemoved", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas component can be added to/removed from an entity. + + Expected Behavior: + Closing a tabbed graph only closes the appropriate graph. + + Test Steps: + 1) Create a new level + 2) Create a new entity + 3) Add a Landscape Canvas component to the entity + 4) Remove the Landscape Canvas component from the entity + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ # Create a new empty level self.test_success = self.create_level( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py index e0f13adaa9..f174a52610 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py @@ -30,12 +30,21 @@ class TestLandscapeCanvasSliceCreateInstantiate(EditorTestHelper): def run_test(self): """ Summary: - C22602016 A slice containing the LandscapeCanvas component can be created/instantiated. + A slice containing the LandscapeCanvas component can be created/instantiated. Expected Result: - Slice is created and processed successfully and free of errors/warnings. - Another copy of the slice is instantiated. - + Slice is created/processed/instantiated successfully and free of errors/warnings. + + Test Steps: + 1) Create a new level + 2) Create a new entity with a Landscape Canvas component + 3) Create a slice of the new entity + 4) Instantiate a new copy of the slice + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. :return: None """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py index ecc529b9b4..82a2abf5ea 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py @@ -34,6 +34,27 @@ class TestLayerBlenderNodeConstruction(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="LayerBlenderNodeConstruction", args=["level"]) def run_test(self): + """ + Summary: + This test verifies a Layer Blender vegetation setup can be constructed through Landscape Canvas. + + Expected Behavior: + Entities contain all required components and component references after creating nodes and setting connections + on a Landscape Canvas graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Add all necessary nodes to the graph and set connections to form a Layer Blender setup + 4) Verify all components and component references were properly set during graph construction + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py index 00fcb5170c..df3c549fff 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py @@ -34,6 +34,25 @@ class TestLayerExtenderNodeComponentEntitySync(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="LayerExtenderNodeComponentEntitySync", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that all wrapped nodes can be successfully added to/removed from parent nodes. + + Expected Behavior: + All wrapped extender nodes can be added to/removed from appropriate parent nodes. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Add Area Blender and Layer Spawner nodes to the graph, and add/remove each extender node to/from each + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py index bd10e5f4c6..cd4915ea24 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py @@ -33,6 +33,25 @@ class TestShapeNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="ShapeNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + + Expected Behavior: + New entities are created when dragging shape nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the shape nodes to the graph area, and ensure a new entity is created + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py index f71f5ae906..fcfbe03576 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py @@ -34,7 +34,27 @@ class TestShapeNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="ShapeNodeEntityDelete", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when shape nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the shape nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + def onEntityCreated(parameters): global createdEntityId createdEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py index 968f39c64d..183c3f7ccb 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py @@ -33,6 +33,27 @@ class TestSlotConnectionsUpdateComponents(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="SlotConnectionsUpdateComponents", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas slot connections properly update component references. + + Expected Behavior: + A reference created through slot connections in Landscape Canvas is reflected in the Entity Inspector. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Several nodes are added to a graph, and connections are set between the nodes + 4) Component references are verified via Entity Inspector + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + # Retrieve the proper component TypeIds per component name componentNames = [ 'Random Noise Gradient', diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py index efeba3b74a..855764fa6f 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py @@ -118,7 +118,7 @@ class TestGraphComponentSync(object): @pytest.mark.test_case_id('C15987206') @pytest.mark.SUITE_main - def test_LandscapeCanvas_GradientMixerNodeConstruction(self, request, editor, level, launcher_platform): + def test_LandscapeCanvas_GradientMixer_NodeConstruction(self, request, editor, level, launcher_platform): """ Verifies a Gradient Mixer can be setup in Landscape Canvas and all references are property set. """ @@ -141,7 +141,7 @@ class TestGraphComponentSync(object): @pytest.mark.test_case_id('C21333743') @pytest.mark.SUITE_periodic - def test_LandscapeCanvas_LayerBlenderNodeConstruction(self, request, editor, level, launcher_platform): + def test_LandscapeCanvas_LayerBlender_NodeConstruction(self, request, editor, level, launcher_platform): """ Verifies a Layer Blender can be setup in Landscape Canvas and all references are property set. """ diff --git a/AutomatedTesting/Levels/WaterSample/WaterSample.ly b/AutomatedTesting/Levels/WaterSample/WaterSample.ly deleted file mode 100644 index b1899f3710..0000000000 --- a/AutomatedTesting/Levels/WaterSample/WaterSample.ly +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d49aceca5ad4e0b9f46c8127afb5c53b68aa30272950b1abd66fba310977ff0c -size 15032 diff --git a/AutomatedTesting/Levels/WaterSample/filelist.xml b/AutomatedTesting/Levels/WaterSample/filelist.xml deleted file mode 100644 index d14b2fdaf2..0000000000 --- a/AutomatedTesting/Levels/WaterSample/filelist.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/halfsphere.cgf b/AutomatedTesting/Levels/WaterSample/halfsphere.cgf deleted file mode 100644 index 4426d8a232..0000000000 --- a/AutomatedTesting/Levels/WaterSample/halfsphere.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f221acd847ec8a15e1333a5163d6d0fd886b8eda46fa7b133f76ddbf1d11216 -size 41472 diff --git a/AutomatedTesting/Levels/WaterSample/halfsphere2.cgf b/AutomatedTesting/Levels/WaterSample/halfsphere2.cgf deleted file mode 100644 index c776ff68b8..0000000000 --- a/AutomatedTesting/Levels/WaterSample/halfsphere2.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c8e5dcfbe65fd2fd8ea29a38a96e703683c544fd42b9424857b1df3718c7775a -size 41472 diff --git a/AutomatedTesting/Levels/WaterSample/level.pak b/AutomatedTesting/Levels/WaterSample/level.pak deleted file mode 100644 index 1753ef4b93..0000000000 --- a/AutomatedTesting/Levels/WaterSample/level.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0378911c27933302042550d5a031a5f9104296162edc2b21e44893f1b8cff969 -size 44124 diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/Environment.xml b/AutomatedTesting/Levels/WaterSample/leveldata/Environment.xml deleted file mode 100644 index 6a95c631bb..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/Environment.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/TerrainTexture.xml b/AutomatedTesting/Levels/WaterSample/leveldata/TerrainTexture.xml deleted file mode 100644 index 21741afe52..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/TerrainTexture.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/TimeOfDay.xml b/AutomatedTesting/Levels/WaterSample/leveldata/TimeOfDay.xml deleted file mode 100644 index 60ad405904..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/TimeOfDay.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/VegetationMap.dat b/AutomatedTesting/Levels/WaterSample/leveldata/VegetationMap.dat deleted file mode 100644 index dce5631cd0..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/VegetationMap.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9 -size 63 diff --git a/AutomatedTesting/Levels/WaterSample/pool.cgf b/AutomatedTesting/Levels/WaterSample/pool.cgf deleted file mode 100644 index 04bec52a62..0000000000 --- a/AutomatedTesting/Levels/WaterSample/pool.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12ca8f1942331abde4d58724aea22609c8d7951cc415afa6e5f1c550a14e67b0 -size 363624 diff --git a/AutomatedTesting/Levels/WaterSample/pool2.cgf b/AutomatedTesting/Levels/WaterSample/pool2.cgf deleted file mode 100644 index 204306f8a8..0000000000 --- a/AutomatedTesting/Levels/WaterSample/pool2.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5b525a410730d84c0b3e97396d392e1e72f4b894742ddef3de4ede5542b0f8e -size 86148 diff --git a/AutomatedTesting/Levels/WaterSample/tags.txt b/AutomatedTesting/Levels/WaterSample/tags.txt deleted file mode 100644 index 0d6c1880e7..0000000000 --- a/AutomatedTesting/Levels/WaterSample/tags.txt +++ /dev/null @@ -1,12 +0,0 @@ -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 diff --git a/AutomatedTesting/Levels/WaterSample/terraintexture.pak b/AutomatedTesting/Levels/WaterSample/terraintexture.pak deleted file mode 100644 index fe3604a050..0000000000 --- a/AutomatedTesting/Levels/WaterSample/terraintexture.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d85 -size 22 diff --git a/AutomatedTesting/Levels/WaterSample/woodland_canyon_river.mtl b/AutomatedTesting/Levels/WaterSample/woodland_canyon_river.mtl deleted file mode 100644 index 4548bca421..0000000000 --- a/AutomatedTesting/Levels/WaterSample/woodland_canyon_river.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings b/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings index 013c774e9e..a4e1a9a3c5 100644 --- a/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings +++ b/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:0,pc:0,provo:0" \ No newline at end of file +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/preview.png b/AutomatedTesting/preview.png index 2191a0ebc2..3d4fe78063 100644 --- a/AutomatedTesting/preview.png +++ b/AutomatedTesting/preview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6 -size 5466 +oid sha256:40949893ed7009eeaa90b7ce6057cb6be9dfaf7b162e3c26ba9dadf985939d7d +size 2038 diff --git a/CMakeLists.txt b/CMakeLists.txt index 63177e9d60..a7e42613cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,10 @@ foreach(external_directory ${LY_EXTERNAL_SUBDIRS}) endforeach() # The following steps have to be done after all targets are registered: +# Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic +# builds until after all the targets are known +ly_delayed_generate_static_modules_inl() + # 1. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls # to provide applications with the filenames of gem modules to load # This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES diff --git a/Code/CryEngine/CryCommon/ISystem.h b/Code/CryEngine/CryCommon/ISystem.h index f863804f3d..653776f55b 100644 --- a/Code/CryEngine/CryCommon/ISystem.h +++ b/Code/CryEngine/CryCommon/ISystem.h @@ -125,7 +125,7 @@ enum ESystemConfigPlatform { CONFIG_INVALID_PLATFORM = 0, CONFIG_PC = 1, - CONFIG_OSX_GL = 2, + CONFIG_MAC = 2, CONFIG_OSX_METAL = 3, CONFIG_ANDROID = 4, CONFIG_IOS = 5, diff --git a/Code/CryEngine/CryCommon/LyShine/IDraw2d.h b/Code/CryEngine/CryCommon/LyShine/IDraw2d.h index 16fdfceca3..76a71c9e24 100644 --- a/Code/CryEngine/CryCommon/LyShine/IDraw2d.h +++ b/Code/CryEngine/CryCommon/LyShine/IDraw2d.h @@ -11,7 +11,6 @@ */ #pragma once -#include #include #include #include @@ -84,7 +83,7 @@ public: // types //! If this is not passed then the defaults below are used struct TextOptions { - IFFont* font; //!< default is "default" + AZStd::string fontName; //!< default is "default" unsigned int effectIndex; //!< default is 0 AZ::Vector3 color; //!< default is (1,1,1) HAlign horizontalAlignment; //!< default is HAlign::Left diff --git a/Code/CryEngine/CrySystem/System.h b/Code/CryEngine/CrySystem/System.h index b91b1ba059..a258030f70 100644 --- a/Code/CryEngine/CrySystem/System.h +++ b/Code/CryEngine/CrySystem/System.h @@ -729,7 +729,7 @@ protected: // ------------------------------------------------------------- CCmdLine* m_pCmdLine; string m_currentLanguageAudio; - string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_es3.cfg or system_android_opengl.cfg or system_windows_pc.cfg + string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_android.cfg or system_windows_pc.cfg std::vector< std::pair > m_updateTimes; diff --git a/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java b/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java index b5d3de8164..5c1a120df6 100644 --- a/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java +++ b/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java @@ -244,7 +244,7 @@ public class LumberyardActivity extends NativeActivity boolean useMainObb = GetBooleanResource("use_main_obb"); boolean usePatchObb = GetBooleanResource("use_patch_obb"); - if (IsBootstrapInAPK() && (useMainObb || usePatchObb)) + if (AreAssetsInAPK() && (useMainObb || usePatchObb)) { Log.d(TAG, "Using OBB expansion files for game assets"); @@ -421,12 +421,12 @@ public class LumberyardActivity extends NativeActivity } //////////////////////////////////////////////////////////////// - private boolean IsBootstrapInAPK() + private boolean AreAssetsInAPK() { try { - InputStream bootstrap = getAssets().open("bootstrap.cfg", AssetManager.ACCESS_UNKNOWN); - bootstrap.close(); + InputStream engine = getAssets().open("engine.json", AssetManager.ACCESS_UNKNOWN); + engine.close(); return true; } catch (IOException exception) diff --git a/Code/Framework/AzCore/AzCore/Android/Utils.cpp b/Code/Framework/AzCore/AzCore/Android/Utils.cpp index efbbf50d1d..d6435c67be 100644 --- a/Code/Framework/AzCore/AzCore/Android/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Android/Utils.cpp @@ -148,7 +148,7 @@ namespace AZ } } - AZ_Assert(false, "Failed to locate the bootstrap.cfg path"); + AZ_Assert(false, "Failed to locate the engine.json path"); return nullptr; } diff --git a/Code/Framework/AzCore/AzCore/Android/Utils.h b/Code/Framework/AzCore/AzCore/Android/Utils.h index 222fac80ad..0862d53aa4 100644 --- a/Code/Framework/AzCore/AzCore/Android/Utils.h +++ b/Code/Framework/AzCore/AzCore/Android/Utils.h @@ -73,8 +73,8 @@ namespace AZ //! \return The pointer position of the relative asset path AZ::IO::FixedMaxPath StripApkPrefix(const char* filePath); - //! Searches application storage and the APK for bootstrap.cfg. Will return nullptr - //! if bootstrap.cfg is not found. + //! Searches application storage and the APK for engine.json. Will return nullptr + //! if engine.json is not found. const char* FindAssetsDirectory(); //! Calls into Java to show the splash screen on the main UI (Java) thread diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index 8a170f5d89..f03b1aac76 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -462,8 +462,6 @@ namespace AZ // for the application root. CalculateAppRoot(); - // Merge the bootstrap.cfg file into the Settings Registry as soon as the OSAllocator has been created. - SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry); SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry); diff --git a/Code/Framework/AzCore/AzCore/Component/TransformBus.h b/Code/Framework/AzCore/AzCore/Component/TransformBus.h index 2003b949e2..b180e97332 100644 --- a/Code/Framework/AzCore/AzCore/Component/TransformBus.h +++ b/Code/Framework/AzCore/AzCore/Component/TransformBus.h @@ -172,78 +172,10 @@ namespace AZ //! Rotation modifiers //! @{ - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation in the world. - //! The origin of the axes is the entity's position in world space. - //! @param eulerAnglesRadians A three-dimensional vector, containing Euler angles in radians, to rotate the entity by. - virtual void SetRotation([[maybe_unused]] const AZ::Vector3& eulerAnglesRadians) {} - - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation around the world's X axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The X coordinate Euler angle in radians to use for the entity's rotation. - virtual void SetRotationX([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation around the world's Y axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Y coordinate Euler angle in radians to use for the entity's rotation. - virtual void SetRotationY([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation around the world's Z axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Z coordinate Euler angle in radians to use for the entity's rotation. - virtual void SetRotationZ([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use SetLocalRotationQuaternion() //! Sets the entity's rotation in the world in quaternion notation. //! The origin of the axes is the entity's position in world space. //! @param quaternion A quaternion that represents the rotation to use for the entity. - virtual void SetRotationQuaternion([[maybe_unused]] const AZ::Quaternion& quaternion) {} - - //! @deprecated Use RotateAroundLocalX() - //! Rotates the entity around the world's X axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Euler angle in radians by which to rotate the entity around the X axis. - virtual void RotateByX([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use RotateAroundLocalY() - //! Rotates the entity around the world's Y axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Euler angle in radians by which to rotate the entity around the Y axis. - virtual void RotateByY([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use RotateAroundLocalZ() - //! Rotates the entity around the world's Z axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Euler angle in radians by which to rotate the entity around the Z axis. - virtual void RotateByZ([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation in the world in Euler angles rotation in radians. - //! @return A three-dimensional vector, containing Euler angles in radians, that represents the entity's rotation. - virtual AZ::Vector3 GetRotationEulerRadians() { return AZ::Vector3(FLT_MAX); } - - //! @deprecated Use GetLocalRotationQuaternion() - //! Gets the entity's rotation in the world in quaternion format. - //! @return A quaternion that represents the entity's rotation in world space. - virtual AZ::Quaternion GetRotationQuaternion() { return AZ::Quaternion::CreateZero(); } - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation around the world's X axis. - //! @return The Euler angle in radians by which the the entity is rotated around the X axis in world space. - virtual float GetRotationX() { return FLT_MAX; } - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation around the world's Y axis. - //! @return The Euler angle in radians by which the the entity is rotated around the Y axis in world space. - virtual float GetRotationY() { return FLT_MAX; } - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation around the world's Z axis. - //! @return The Euler angle in radians by which the the entity is rotated around the Z axis in world space. - virtual float GetRotationZ() { return FLT_MAX; } + virtual void SetWorldRotationQuaternion([[maybe_unused]] const AZ::Quaternion& quaternion) {} //! Get angles in radian for each principle axis around which the world transform is //! rotated in the order of z-axis and y-axis and then x-axis. @@ -287,71 +219,28 @@ namespace AZ //! Scale modifiers //! @{ - //! @deprecated Use SetLocalScale() - //! Scales the entity along the world's axes. The origin of the axes is the entity's position in the world. - //! @param scale A three-dimensional vector that represents the multipliers with which to scale the entity in world space. - virtual void SetScale([[maybe_unused]] const AZ::Vector3& scale) {} - - //! @deprecated Use SetLocalScaleX() - //! Scales the entity along the world's X axis. The origin of the axis is the entity's position in the world. - //! @param scaleX The multiplier by which to scale the entity along the X axis in world space. - virtual void SetScaleX([[maybe_unused]] float scaleX) {} - - //! @deprecated Use SetLocalScaleY() - //! Scales the entity along the world's Y axis. The origin of the axis is the entity's position in the world. - //! @param scaleY The multiplier by which to scale the entity along the Y axis in world space. - virtual void SetScaleY([[maybe_unused]] float scaleY) {} - - //! @deprecated Use SetLocalScaleZ() - //! Scales the entity along the world's Z axis. The origin of the axis is the entity's position in the world. - //! @param scaleZ The multiplier by which to scale the entity along the Z axis in world space. - virtual void SetScaleZ([[maybe_unused]] float scaleZ) {} - - //! @deprecated Use GetLocalScale() - //! Gets the scale of the entity in world space. - //! @return A three-dimensional vector that represents the scale of the entity in world space. - virtual AZ::Vector3 GetScale() { return AZ::Vector3(FLT_MAX); } - - //! @deprecated Use GetLocalScale() - //! Gets the amount by which an entity is scaled along the world's X axis. - //! @return The amount by which an entity is scaled along the X axis in world space. - virtual float GetScaleX() { return FLT_MAX; } - - //! @deprecated Use GetLocalScale() - //! Gets the amount by which an entity is scaled along the world's Y axis. - //! @return The amount by which an entity is scaled along the Y axis in world space. - virtual float GetScaleY() { return FLT_MAX; } - - //! @deprecated Use GetLocalScale() - //! Gets the amount by which an entity is scaled along the world's Z axis. - //! @return The amount by which an entity is scaled along the Z axis in world space. - virtual float GetScaleZ() { return FLT_MAX; } - //! Set local scale of the transform. - //! @param scale The new scale to set along three local axes. + //! @param scale The new scale to set. virtual void SetLocalScale([[maybe_unused]] const AZ::Vector3& scale) {} - //! Set local scale of the transform on x-axis. - //! @param scaleX The new x-axis scale to set. - virtual void SetLocalScaleX([[maybe_unused]] float scaleX) {} + //! Get the scale value in local space. + //! @return The scale value in local space. + virtual AZ::Vector3 GetLocalScale() { return AZ::Vector3(FLT_MAX); } - //! Set local scale of the transform on y-axis. - //! @param scaleY The new y-axis scale to set. - virtual void SetLocalScaleY([[maybe_unused]] float scaleY) {} + //! Get the scale value in world space. + //! @return The scale value in world space. + virtual AZ::Vector3 GetWorldScale() { return AZ::Vector3(FLT_MAX); } - //! Set local scale of the transform on z-axis. - //! @param scaleZ The new z-axis scale to set. - virtual void SetLocalScaleZ([[maybe_unused]] float scaleZ) {} + //! Set the uniform scale value in local space. + virtual void SetLocalUniformScale([[maybe_unused]] float scale) {} - //! Get the scale value on each axis in local space - //! @return The scale value of type Vector3 along each axis in local space. - virtual AZ::Vector3 GetLocalScale() { return AZ::Vector3(FLT_MAX); } + //! Get the uniform scale value in local space. + //! @return The uniform scale value in local space. + virtual float GetLocalUniformScale() { return FLT_MAX; } - //! Get the scale value on each axis in world space. - //! Note the transform will be skewed when it is rotated and has a parent transform scaled, in which - //! case the returned world-scale from this function will be inaccurate. - //! @return The scale value of type Vector3 along each axis in world space. - virtual AZ::Vector3 GetWorldScale() { return AZ::Vector3(FLT_MAX); } + //! Get the uniform scale value in world space. + //! @return The uniform scale value in world space. + virtual float GetWorldUniformScale() { return FLT_MAX; } //! @} //! Transform hierarchy diff --git a/Code/Framework/AzCore/AzCore/Math/Quaternion.cpp b/Code/Framework/AzCore/AzCore/Math/Quaternion.cpp index 143fe59ca7..06443c0698 100644 --- a/Code/Framework/AzCore/AzCore/Math/Quaternion.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Quaternion.cpp @@ -348,13 +348,20 @@ namespace AZ return result.GetW() >= 0.0f ? result : -result; } - const Quaternion Quaternion::CreateFromEulerAnglesDegrees(Vector3& anglesInDegrees) + const Quaternion Quaternion::CreateFromEulerAnglesDegrees(const Vector3& anglesInDegrees) { Quaternion result; result.SetFromEulerDegrees(anglesInDegrees); return result; } + const Quaternion Quaternion::CreateFromEulerAnglesRadians(const Vector3& anglesInRadians) + { + Quaternion result; + result.SetFromEulerRadians(anglesInRadians); + return result; + } + Quaternion Quaternion::Slerp(const Quaternion& dest, float t) const { const float DestDot = Dot(dest); diff --git a/Code/Framework/AzCore/AzCore/Math/Quaternion.h b/Code/Framework/AzCore/AzCore/Math/Quaternion.h index c4502063de..4d3b6641db 100644 --- a/Code/Framework/AzCore/AzCore/Math/Quaternion.h +++ b/Code/Framework/AzCore/AzCore/Math/Quaternion.h @@ -83,8 +83,11 @@ namespace AZ static Quaternion CreateShortestArc(const Vector3& v1, const Vector3& v2); - /// Creates a quaternion using rotation in degrees about the axes. First rotated about the X axis, followed by the Y axis, then the Z axis. - static const Quaternion CreateFromEulerAnglesDegrees(Vector3& anglesInDegrees); + //! Creates a quaternion using rotation in degrees about the axes. First rotated about the X axis, followed by the Y axis, then the Z axis. + static const Quaternion CreateFromEulerAnglesDegrees(const Vector3& anglesInDegrees); + + //! Creates a quaternion using rotation in radians about the axes. First rotated about the X axis, followed by the Y axis, then the Z axis. + static const Quaternion CreateFromEulerAnglesRadians(const Vector3& anglesInRadians); //! Stores the vector to an array of 4 floats. The floats need only be 4 byte aligned, 16 byte alignment is not required. void StoreToFloat4(float* values) const; diff --git a/Code/Framework/AzCore/AzCore/Math/Random.h b/Code/Framework/AzCore/AzCore/Math/Random.h index 8b28f6aaad..8b2763df50 100644 --- a/Code/Framework/AzCore/AzCore/Math/Random.h +++ b/Code/Framework/AzCore/AzCore/Math/Random.h @@ -126,17 +126,16 @@ namespace AZ m_offsets.fill(1); // Halton sequences start at index 1. m_increments.fill(1); // By default increment by 1 between each number. } - - //! Returns a Halton sequence in an array of N length - template - AZStd::array, N> GetHaltonSequence() + + //! Fills a provided container from begin to end with a Halton sequence. + //! Entries are expected to be, or implicitly converted to, AZStd::array. + template + void FillHaltonSequence(Iterator begin, Iterator end) { - AZStd::array, N> result; - AZStd::array indices = m_offsets; // Generator that returns the Halton number for all bases for a single entry. - auto f = [&] () + auto f = [&]() { AZStd::array item; for (auto d = 0; d < Dimensions; ++d) @@ -147,12 +146,20 @@ namespace AZ return item; }; - AZStd::generate(result.begin(), result.end(), f); + AZStd::generate(begin, end, f); + } + + //! Returns a Halton sequence in an array of N length. + template + AZStd::array, N> GetHaltonSequence() + { + AZStd::array, N> result; + FillHaltonSequence(result.begin(), result.end()); return result; } //! Sets the offsets per dimension to start generating a sequence from. - //! By default, there is no offset (offset of 0 corresponds to starting at index 1) + //! By default, there is no offset (offset of 0 corresponds to starting at index 1). void SetOffsets(AZStd::array offsets) { m_offsets = offsets; diff --git a/Code/Framework/AzCore/AzCore/Math/Spline.h b/Code/Framework/AzCore/AzCore/Math/Spline.h index 912c10f46f..1adfc9b2fa 100644 --- a/Code/Framework/AzCore/AzCore/Math/Spline.h +++ b/Code/Framework/AzCore/AzCore/Math/Spline.h @@ -441,10 +441,10 @@ namespace AZ const Transform& worldFromLocal, const Vector3& src, const Vector3& dir, const Spline& spline) { Transform worldFromLocalNormalized = worldFromLocal; - const Vector3 scale = worldFromLocalNormalized.ExtractScale(); + const float scale = worldFromLocalNormalized.ExtractUniformScale(); const Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse(); - const Vector3 localRayOrigin = localFromWorldNormalized.TransformPoint(src) * scale.GetReciprocal(); + const Vector3 localRayOrigin = localFromWorldNormalized.TransformPoint(src) / scale; const Vector3 localRayDirection = localFromWorldNormalized.TransformVector(dir); return spline.GetNearestAddressRay(localRayOrigin, localRayDirection); } diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.cpp b/Code/Framework/AzCore/AzCore/Math/Transform.cpp index bb3f764492..9090a9e94e 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Transform.cpp @@ -284,10 +284,15 @@ namespace AZ Method("GetRotation", &Transform::GetRotation)-> Method("SetRotation", &Transform::SetRotation)-> Method("GetScale", &Transform::GetScale)-> - Method("SetScale", &Transform::SetScale)-> + Method("GetUniformScale", &Transform::GetUniformScale)-> + Method("SetScale", &Transform::SetScale)-> + Method("SetUniformScale", &Transform::SetUniformScale)-> Method("ExtractScale", &Transform::ExtractScale)-> Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> + Method("ExtractUniformScale", &Transform::ExtractUniformScale)-> + Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> Method("MultiplyByScale", &Transform::MultiplyByScale)-> + Method("MultiplyByUniformScale", &Transform::MultiplyByUniformScale)-> Method("GetInverse", &Transform::GetInverse)-> Method("Invert", &Transform::Invert)-> Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> @@ -306,6 +311,7 @@ namespace AZ Method("CreateFromMatrix3x3", &Transform::CreateFromMatrix3x3)-> Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)-> Method("CreateScale", &Transform::CreateScale)-> + Method("CreateUniformScale", &Transform::CreateUniformScale)-> Method("CreateTranslation", &Transform::CreateTranslation)-> Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues); } diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.h b/Code/Framework/AzCore/AzCore/Math/Transform.h index eb1a12a912..7ae86edd89 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.h +++ b/Code/Framework/AzCore/AzCore/Math/Transform.h @@ -89,8 +89,11 @@ namespace AZ static Transform CreateFromMatrix3x4(const Matrix3x4& value); - //! Sets the matrix to be a scale matrix, translation is set to zero. - static Transform CreateScale(const Vector3& scale); + //! Sets the transform to apply scale only, no rotation or translation. + static Transform CreateScale(const AZ::Vector3& scale); + + //! Sets the transform to apply (uniform) scale only, no rotation or translation. + static Transform CreateUniformScale(const float scale); //! Sets the matrix to be a translation matrix, rotation part is set to identity. static Transform CreateTranslation(const Vector3& translation); @@ -119,13 +122,19 @@ namespace AZ const Quaternion& GetRotation() const; void SetRotation(const Quaternion& rotation); - const Vector3& GetScale() const; + Vector3 GetScale() const; + float GetUniformScale() const; void SetScale(const Vector3& v); + void SetUniformScale(const float scale); - //! Sets the transforms scale to a unit value and returns the previous scale value. + //! Sets the transform's scale to a unit value and returns the previous scale value. Vector3 ExtractScale(); - void MultiplyByScale(const Vector3& scale); + //! Sets the transform's scale to a unit value and returns the previous scale value. + float ExtractUniformScale(); + + void MultiplyByScale(const AZ::Vector3& scale); + void MultiplyByUniformScale(float scale); Transform operator*(const Transform& rhs) const; Transform& operator*=(const Transform& rhs); diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.inl b/Code/Framework/AzCore/AzCore/Math/Transform.inl index 63425e6e41..a7d5e72749 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.inl +++ b/Code/Framework/AzCore/AzCore/Math/Transform.inl @@ -65,6 +65,7 @@ namespace AZ AZ_MATH_INLINE Transform Transform::CreateScale(const Vector3& scale) { + AZ_WarningOnce("Transform", false, "CreateScale is deprecated, please use CreateUniformScale instead."); Transform result; result.m_rotation = Quaternion::CreateIdentity(); result.m_scale = scale; @@ -72,6 +73,15 @@ namespace AZ return result; } + AZ_MATH_INLINE Transform Transform::CreateUniformScale(float scale) + { + Transform result; + result.m_rotation = Quaternion::CreateIdentity(); + result.m_scale = Vector3(scale); + result.m_translation = Vector3::CreateZero(); + return result; + } + AZ_MATH_INLINE Transform Transform::CreateTranslation(const Vector3& translation) { Transform result; @@ -150,24 +160,50 @@ namespace AZ m_rotation = rotation; } - AZ_MATH_INLINE const Vector3& Transform::GetScale() const + AZ_MATH_INLINE Vector3 Transform::GetScale() const { + AZ_WarningOnce("Transform", false, "GetScale is deprecated, please use GetUniformScale instead."); return m_scale; } + AZ_MATH_INLINE float Transform::GetUniformScale() const + { + return m_scale.GetMaxElement(); + } + AZ_MATH_INLINE void Transform::SetScale(const Vector3& scale) { + AZ_WarningOnce("Transform", false, "SetScale is deprecated, please use SetUniformScale instead."); m_scale = scale; } + AZ_MATH_INLINE void Transform::SetUniformScale(const float scale) + { + m_scale = Vector3(scale); + } + AZ_MATH_INLINE Vector3 Transform::ExtractScale() { + AZ_WarningOnce("Transform", false, "ExtractScale is deprecated, please use ExtractUniformScale instead."); const Vector3 scale = m_scale; m_scale = Vector3::CreateOne(); return scale; } + AZ_MATH_INLINE float Transform::ExtractUniformScale() + { + const float scale = m_scale.GetMaxElement(); + m_scale = Vector3::CreateOne(); + return scale; + } + AZ_MATH_INLINE void Transform::MultiplyByScale(const Vector3& scale) + { + AZ_WarningOnce("Transform", false, "MultiplyByScale is deprecated, please use MultiplyByUniformScale instead."); + m_scale *= scale; + } + + AZ_MATH_INLINE void Transform::MultiplyByUniformScale(float scale) { m_scale *= scale; } @@ -233,7 +269,7 @@ namespace AZ AZ_MATH_INLINE void Transform::Orthogonalize() { - *this = GetOrthogonalized(); + m_scale = Vector3::CreateOne(); } AZ_MATH_INLINE bool Transform::IsClose(const Transform& rhs, float tolerance) const diff --git a/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp b/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp index 0a3e02ee0d..86bc1c36ea 100644 --- a/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp @@ -60,7 +60,7 @@ namespace AZ { // Scale is transitioning to a single uniform scale value, but since it's still internally represented as a Vector3, // we need to pick one number to use for load/store operations. - float scale = transformInstance->GetScale().GetMaxElement(); + float scale = transformInstance->GetUniformScale(); JSR::ResultCode loadResult = ContinueLoadingFromJsonObjectField(&scale, azrtti_typeid(), inputValue, ScaleTag, context); @@ -124,8 +124,8 @@ namespace AZ // Scale is transitioning to a single uniform scale value, but since it's still internally represented as a Vector3, // we need to pick one number to use for load/store operations. - float scale = transformInstance->GetScale().GetMaxElement(); - float defaultScale = defaultTransformInstance ? defaultTransformInstance->GetScale().GetMaxElement() : 0.0f; + float scale = transformInstance->GetUniformScale(); + float defaultScale = defaultTransformInstance ? defaultTransformInstance->GetUniformScale() : 0.0f; JSR::ResultCode storeResult = ContinueStoringToJsonObjectField( outputValue, ScaleTag, &scale, defaultTransformInstance ? &defaultScale : nullptr, azrtti_typeid(), diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp index 63aad1ecf4..e31c3b0a1e 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp @@ -19,7 +19,7 @@ namespace AZ { inline namespace PlatformDefaults { - static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient }; + static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformAndroid, PlatformIOS, PlatformMac, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient }; const char* PlatformIdToPalFolder(AZ::PlatformId platform) { @@ -31,11 +31,11 @@ namespace AZ { case AZ::PC: return "PC"; - case AZ::ES3: + case AZ::ANDROID_ID: return "Android"; case AZ::IOS: return "iOS"; - case AZ::OSX: + case AZ::MAC_ID: return "Mac"; case AZ::PROVO: return "Provo"; @@ -66,11 +66,11 @@ namespace AZ } else if (osPlatform == PlatformCodeNameMac) { - return PlatformOSX; + return PlatformMac; } else if (osPlatform == PlatformCodeNameAndroid) { - return PlatformES3; + return PlatformAndroid; } else if (osPlatform == PlatformCodeNameiOS) { @@ -207,13 +207,13 @@ namespace AZ platformCodes.emplace_back(PlatformCodeNameWindows); platformCodes.emplace_back(PlatformCodeNameLinux); break; - case PlatformId::ES3: + case PlatformId::ANDROID_ID: platformCodes.emplace_back(PlatformCodeNameAndroid); break; case PlatformId::IOS: platformCodes.emplace_back(PlatformCodeNameiOS); break; - case PlatformId::OSX: + case PlatformId::MAC_ID: platformCodes.emplace_back(PlatformCodeNameMac); break; case PlatformId::PROVO: diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h index 2d67c860cd..ba8c55f5f5 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h @@ -27,9 +27,9 @@ namespace AZ inline namespace PlatformDefaults { constexpr char PlatformPC[] = "pc"; - constexpr char PlatformES3[] = "es3"; + constexpr char PlatformAndroid[] = "android"; constexpr char PlatformIOS[] = "ios"; - constexpr char PlatformOSX[] = "osx_gl"; + constexpr char PlatformMac[] = "mac"; constexpr char PlatformProvo[] = "provo"; constexpr char PlatformSalem[] = "salem"; constexpr char PlatformJasper[] = "jasper"; @@ -54,9 +54,9 @@ namespace AZ AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int, (Invalid, -1), PC, - ES3, + ANDROID_ID, IOS, - OSX, + MAC_ID, PROVO, SALEM, JASPER, @@ -73,9 +73,9 @@ namespace AZ { Platform_NONE = 0x00, Platform_PC = 1 << PlatformId::PC, - Platform_ES3 = 1 << PlatformId::ES3, + Platform_ANDROID = 1 << PlatformId::ANDROID_ID, Platform_IOS = 1 << PlatformId::IOS, - Platform_OSX = 1 << PlatformId::OSX, + Platform_MAC = 1 << PlatformId::MAC_ID, Platform_PROVO = 1 << PlatformId::PROVO, Platform_SALEM = 1 << PlatformId::SALEM, Platform_JASPER = 1 << PlatformId::JASPER, @@ -87,7 +87,7 @@ namespace AZ // A special platform that will always correspond to all non-server platforms, even if new ones are added Platform_ALL_CLIENT = 1ULL << 31, - AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER, + AllNamedPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER, }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags); diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp index 0258869a0c..d56140be28 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp @@ -28,8 +28,8 @@ namespace AZ return "Android64"; case PlatformID::PLATFORM_APPLE_IOS: return "iOS"; - case PlatformID::PLATFORM_APPLE_OSX: - return "OSX"; + case PlatformID::PLATFORM_APPLE_MAC: + return "Mac"; #if defined(AZ_EXPAND_FOR_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS) #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\ case PlatformID::PLATFORM_##PUBLICNAME:\ diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h index ce1a11d8ce..e8e7cef6dd 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h @@ -23,7 +23,7 @@ namespace AZ PLATFORM_WINDOWS_64, PLATFORM_LINUX_64, PLATFORM_APPLE_IOS, - PLATFORM_APPLE_OSX, + PLATFORM_APPLE_MAC, PLATFORM_ANDROID_64, // ARMv8 / 64-bit #if defined(AZ_EXPAND_FOR_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS) #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\ diff --git a/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl b/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl index 079c70c878..537d3e5c83 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl +++ b/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl @@ -1020,29 +1020,60 @@ namespace AZ } }; - /// OnDemand reflection for AZStd::set + + template + class Iterator_VM> + { + public: + using ContainerType = AZStd::unordered_set; + using IteratorType = typename ContainerType::iterator; + Iterator_VM(ContainerType& container) + : m_iterator(container.begin()) + , m_end(container.end()) + {} + + const t_Key& GetKeyUnchecked() const + { + return *m_iterator; + } + + bool IsNotAtEnd() const + { + return m_iterator != m_end; + } + + t_Key& ModValueUnchecked() + { + return *m_iterator; + } + + void Next() + { + ++m_iterator; + } + + private: + IteratorType m_iterator; + IteratorType m_end; + }; + + /// OnDemand reflection for AZStd::unordered_set template struct OnDemandReflection< AZStd::unordered_set > { using ContainerType = AZStd::unordered_set; using KeyListType = AZStd::vector; - static AZ::Outcome Erase(ContainerType& thisMap, Key& key) + using ValueIteratorType = Iterator_VM; + + static bool EraseCheck_VM(ContainerType& thisSet, Key& key) { - const auto result = thisMap.erase(key); - if (result) - { - return AZ::Success(); - } - else - { - return AZ::Failure(); - } + return thisSet.erase(key) != 0; } - static void Insert(ContainerType& thisSet, Key& key) + static ContainerType& ErasePost_VM(ContainerType& thisSet, [[maybe_unused]] Key&) { - thisSet.insert(key); + return thisSet; } static KeyListType GetKeys(ContainerType& thisSet) @@ -1055,6 +1086,17 @@ namespace AZ return keys; } + static ContainerType& Insert(ContainerType& thisSet, Key& key) + { + thisSet.insert(key); + return thisSet; + } + + static ValueIteratorType Iterate_VM(ContainerType& thisContainer) + { + return ValueIteratorType(thisContainer); + } + static void Swap(ContainerType& thisSet, ContainerType& otherSet) { thisSet.swap(otherSet); @@ -1064,33 +1106,68 @@ namespace AZ { if (BehaviorContext* behaviorContext = azrtti_cast(context)) { + BranchOnResultInfo emptyBranchInfo; + emptyBranchInfo.m_returnResultInBranches = true; + emptyBranchInfo.m_trueToolTip = "The container is empty"; + emptyBranchInfo.m_falseToolTip = "The container is not empty"; + auto ContainsTransparent = [](const ContainerType& containerType, typename ContainerType::key_type& key)->bool { return containerType.contains(key); }; + ExplicitOverloadInfo explicitOverloadInfo; behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly) ->Attribute(AZ::ScriptCanvasAttributes::PrettyName, ScriptCanvasOnDemandReflection::OnDemandPrettyName::Get(*behaviorContext)) ->Attribute(AZ::Script::Attributes::ToolTip, ScriptCanvasOnDemandReflection::OnDemandToolTip::Get(*behaviorContext)) ->Attribute(AZ::Script::Attributes::Category, ScriptCanvasOnDemandReflection::OnDemandCategoryName::Get(*behaviorContext)) ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::ScriptOwn) ->Method("BucketCount", static_cast(&ContainerType::bucket_count)) - ->Method("Erase", &Erase) - ->Method("Empty", [](ContainerType& thisSet)->bool { return thisSet.empty(); }) + ->Method("Empty", static_cast(&ContainerType::empty), { { { "Container", "The container to check if it is empty", nullptr, {} } } }) + ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Is Empty", "Containers")) + ->Attribute(AZ::ScriptCanvasAttributes::BranchOnResult, emptyBranchInfo) + ->Method("EraseCheck_VM", &EraseCheck_VM) + ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent) + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Method("Erase", &ErasePost_VM) + ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent) + ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Erase", "Containers")) + ->Attribute(AZ::ScriptCanvasAttributes::CheckedOperation, CheckedOperationInfo("EraseCheck_VM", {}, "Out", "Key Not Found", true)) + ->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "ContainerGroup", "" }, { "ContainerGroup" })) ->Method("contains", ContainsTransparent) + ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Has Key", "Containers")) ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent) ->Method("Insert", &Insert) + ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent) + ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Insert", "Containers")) + ->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "ContainerGroup", "", "" }, { "ContainerGroup" })) ->Method(k_sizeName, [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->size()); }) ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) ->Method("GetKeys", &GetKeys) ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Method("GetSize", [](ContainerType& thisPtr) { return aznumeric_cast(thisPtr.size()); }) + ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Get Size", "Containers")) + ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent) ->Method("Reserve", static_cast(&ContainerType::reserve)) ->Method("Swap", &Swap) + ->Method("Clear", [](ContainerType& thisContainer)->ContainerType& { thisContainer.clear(); return thisContainer; }) + ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent) + ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Clear All Elements", "Containers")) + ->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "ContainerGroup" }, { "ContainerGroup" })) + ->Method(k_iteratorConstructorName, &Iterate_VM) + ; + + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::ScriptOwn) + ->Method(k_iteratorGetKeyName, &ValueIteratorType::GetKeyUnchecked) + ->Method(k_iteratorModValueName, &ValueIteratorType::ModValueUnchecked) + ->Method(k_iteratorIsNotAtEndName, &ValueIteratorType::IsNotAtEnd) + ->Method(k_iteratorNextName, &ValueIteratorType::Next) ; } } - }; template <> diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.cpp b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.cpp index 635d160434..31a2cfc09e 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.cpp +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.cpp @@ -165,7 +165,7 @@ namespace AZ if (HasResult() != overload->HasResult()) { - AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all"); + AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all: %s", m_name.c_str()); return false; } @@ -176,7 +176,7 @@ namespace AZ if (!(methodResult->m_typeId == overloadResult->m_typeId && methodResult->m_traits == overloadResult->m_traits)) { - AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all"); + AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all: %s", m_name.c_str()); return false; } } @@ -575,7 +575,7 @@ namespace AZ } else { - AZ_Error("BehaviorContext", false, "safety check declared for method %s but it was not found in the class"); + AZ_Error("BehaviorContext", false, "Method: %s, declared safety check: %s, but it was not found in class: %s", method.m_name.c_str(), m_name.c_str(), checkedOperationInfo.m_safetyCheckName.c_str()); } } } diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.cpp b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.cpp index c64b86ae0f..f6aac0fa16 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.cpp +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.cpp @@ -34,10 +34,17 @@ namespace BehaviorContextUtilitiesCPP using argument_type = const BehaviorParameter*; using result_type = size_t; result_type operator()(const argument_type& value) const - { - result_type result = AZStd::hash()(value->m_typeId); - AZStd::hash_combine(result, CleanTraits(value->m_traits)); - return result; + { + if (value) + { + result_type result = AZStd::hash()(value->m_typeId); + AZStd::hash_combine(result, CleanTraits(value->m_traits)); + return result; + } + else + { + return 0; + } } }; @@ -45,7 +52,11 @@ namespace BehaviorContextUtilitiesCPP { bool operator()(const BehaviorParameter* left, const BehaviorParameter* right) const { - return left->m_typeId == right->m_typeId && CleanTraits(left->m_traits) == CleanTraits(right->m_traits); + return (left == nullptr && right == nullptr) + || (left != nullptr + && right != nullptr + && left->m_typeId == right->m_typeId + && CleanTraits(left->m_traits) == CleanTraits(right->m_traits)); } }; @@ -137,7 +148,7 @@ namespace AZ for (size_t argIndex = 0, argSentinel = overload.GetNumArguments(); argIndex < argSentinel; ++argIndex) { auto overloadedArgIter = variance.m_input.find(argIndex); - if (overloadedArgIter != variance.m_input.end()) + if (overloadedArgIter != variance.m_input.end() && overloadedArgIter->second[overloadIndex]) { // if this doesn't work try the type name overloadName += ReplaceCppArtifacts(overloadedArgIter->second[overloadIndex]->m_name); @@ -185,16 +196,24 @@ namespace AZ { auto argument = overloads[overloadIndex].first->GetArgument(0); - const bool isThisPointer - = (argument->m_traits & AZ::BehaviorParameter::Traits::TR_THIS_PTR) != 0 - || AZ::FindAttribute(AZ::Script::Attributes::TreatAsMemberFunction, overloads[overloadIndex].first->m_attributes); + if (argument) + { + const bool isThisPointer + = (argument->m_traits & AZ::BehaviorParameter::Traits::TR_THIS_PTR) != 0 + || AZ::FindAttribute(AZ::Script::Attributes::TreatAsMemberFunction, overloads[overloadIndex].first->m_attributes); - oneArgIsThisPointer = oneArgIsThisPointer || isThisPointer; + oneArgIsThisPointer = oneArgIsThisPointer || isThisPointer; + } types.insert(argument); stripedArgs.emplace_back(argument); } + if (types.size() == overloads.size()) + { + variance.m_unambiguousInput.insert(0); + } + if (types.size() > 1 && (onThis == VariantOnThis::Yes || !oneArgIsThisPointer)) { variance.m_input.insert(AZStd::make_pair(0, stripedArgs)); @@ -210,11 +229,15 @@ namespace AZ for (size_t overloadIndex = 0, overloadSentinel = overloads.size(); overloadIndex < overloadSentinel; ++overloadIndex) { auto argument = overloads[overloadIndex].first->GetArgument(argIndex); - types.insert(argument); stripedArgs.emplace_back(argument); } + if (types.size() == overloads.size()) + { + variance.m_unambiguousInput.insert(0); + } + if (types.size() > 1) { variance.m_input.insert(AZStd::make_pair(argIndex, stripedArgs)); diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.h b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.h index 4663635cdf..0dc9ecc969 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.h +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.h @@ -27,6 +27,8 @@ namespace AZ struct OverloadVariance { AZStd::unordered_map> m_input; + // the indices of inputs that make selection of overload unambiguous + AZStd::unordered_set m_unambiguousInput; AZStd::vector m_output; }; diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp index ac1e61a5aa..9bf6247e97 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp @@ -2048,10 +2048,6 @@ LUA_API const Node* lua_getDummyNode() return true; } - else - { - AZ_Warning("Script", false, "Index %d is not a function!", functionIndex); - } return false; } @@ -2078,7 +2074,6 @@ LUA_API const Node* lua_getDummyNode() } else { - AZ_Warning("Script", lua_isnil(m_nativeContext, -1), "Name %s exists but is not a function!", functionName); lua_pop(m_nativeContext, 1); } @@ -5888,7 +5883,6 @@ LUA_API const Node* lua_getDummyNode() else { lua_pop(m_impl->m_lua, 1); - AZ_Warning("Script", false, "%s is not a function!", functionName); } return false; } @@ -5906,7 +5900,6 @@ LUA_API const Node* lua_getDummyNode() else { lua_pop(m_impl->m_lua, 1); - AZ_Warning("Script", false, "CacheIndex %d is not a function!", cachedIndex); } return false; } diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp index 015554538f..11d4db2e07 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp @@ -937,7 +937,7 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection) ->Enum(PlatformID::PLATFORM_LINUX_64)>("Linux") ->Enum(PlatformID::PLATFORM_ANDROID_64)>("Android64") ->Enum(PlatformID::PLATFORM_APPLE_IOS)>("iOS") - ->Enum(PlatformID::PLATFORM_APPLE_OSX)>("OSX") + ->Enum(PlatformID::PLATFORM_APPLE_MAC)>("Mac") #if defined(AZ_EXPAND_FOR_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS) #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\ ->Enum(PlatformID::PLATFORM_##PUBLICNAME)>(#CodeName) diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp index 93d12acba3..9c4641741e 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp @@ -10,6 +10,7 @@ * */ +#include "AzCore/RTTI/TypeInfo.h" #include #include #include @@ -61,6 +62,13 @@ namespace AZ if (classData->m_azRtti && classData->m_azRtti->GetGenericTypeId() != typeId) { + if (((classData->m_azRtti->GetTypeTraits() & (AZ::TypeTraits::is_signed | AZ::TypeTraits::is_unsigned)) != AZ::TypeTraits{0}) && + context.GetSerializeContext()->GetUnderlyingTypeId(typeId) == classData->m_typeId) + { + // This value is from an enum, where a field has been reflected using ClassBuilder::Field, but the enum + // type itself has not been reflected using EnumBuilder. Treat it as an enum. + return LoadEnum(object, *classData, value, context); + } serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId()); if (serializer) { @@ -77,21 +85,18 @@ namespace AZ { return LoadEnum(object, *classData, value, context); } - else if (classData->m_container) + if (classData->m_container) { return context.Report(Tasks::ReadField, Outcomes::Unsupported, "The Json Serializer uses custom serializers to load containers. If this message is encountered " "then a serializer for the target containers is missing, isn't registered or doesn't exist."); } - else if (value.IsObject()) + if (value.IsObject()) { return LoadClass(object, *classData, value, context); } - else - { - return context.Report(Tasks::ReadField, Outcomes::Unsupported, - AZStd::string::format("Reading into targets of type '%s' is not supported.", classData->m_name)); - } + return context.Report(Tasks::ReadField, Outcomes::Unsupported, + AZStd::string::format("Reading into targets of type '%s' is not supported.", classData->m_name)); } JsonSerializationResult::ResultCode JsonDeserializer::LoadToPointer(void* object, const Uuid& typeId, @@ -233,8 +238,16 @@ namespace AZ AZ::TypeId underlyingTypeId = AZ::TypeId::CreateNull(); if (!attributeReader.Read(underlyingTypeId)) { - return context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, - "Unable to find underlying type of enum in class data."); + // for non-reflected enums, the passed-in classData already represents the enum's underlying type + if (context.GetSerializeContext()->GetUnderlyingTypeId(classData.m_typeId) == classData.m_typeId) + { + underlyingTypeId = classData.m_typeId; + } + else + { + return context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, + "Unable to find underlying type of enum in class data."); + } } const SerializeContext::ClassData* underlyingClassData = context.GetSerializeContext()->FindClassData(underlyingTypeId); diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 82bf1db484..5870c66633 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -494,13 +494,6 @@ namespace AZ::SettingsRegistryMergeUtils return configFileParsed; } - void MergeSettingsToRegistry_Bootstrap(SettingsRegistryInterface& registry) - { - ConfigParserSettings parserSettings; - parserSettings.m_registryRootPointerPath = BootstrapSettingsRootKey; - MergeSettingsToRegistry_ConfigFile(registry, "bootstrap.cfg", parserSettings); - } - void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry) { using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index 576066c29f..b482530d24 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -172,9 +172,6 @@ namespace AZ::SettingsRegistryMergeUtils bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath, const ConfigParserSettings& configParserSettings); - //! Loads bootstrap.cfg into the Settings Registry. This file does not support specializations. - void MergeSettingsToRegistry_Bootstrap(SettingsRegistryInterface& registry); - //! Extracts file path information from the environment and bootstrap to calculate the various file paths and adds those //! to the Settings Registry under the FilePathsRootKey. void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry); diff --git a/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h b/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h index d361e79f05..42dcd3e2f7 100644 --- a/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h +++ b/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h @@ -13,5 +13,5 @@ namespace AZ { - static const PlatformID g_currentPlatform = PlatformID::PLATFORM_APPLE_OSX; + static const PlatformID g_currentPlatform = PlatformID::PLATFORM_APPLE_MAC; } diff --git a/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.cpp b/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.cpp index 38b9cd609b..9c6f85f08a 100644 --- a/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.cpp +++ b/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.cpp @@ -31,6 +31,8 @@ namespace UnitTest ErrorHandler::ErrorHandler(const char* errorPattern) : m_errorCount(0) , m_warningCount(0) + , m_expectedErrorCount(0) + , m_expectedWarningCount(0) , m_errorPattern(errorPattern) { AZ::Debug::TraceMessageBus::Handler::BusConnect(); @@ -51,6 +53,16 @@ namespace UnitTest return m_warningCount; } + int ErrorHandler::GetExpectedErrorCount() const + { + return m_expectedErrorCount; + } + + int ErrorHandler::GetExpectedWarningCount() const + { + return m_expectedWarningCount; + } + bool ErrorHandler::SuppressExpectedErrors([[maybe_unused]] const char* window, const char* message) { return AZStd::string(message).find(m_errorPattern) != AZStd::string::npos; @@ -61,7 +73,9 @@ namespace UnitTest [[maybe_unused]] const char* func, const char* message) { m_errorCount++; - return SuppressExpectedErrors(window, message); + bool suppress = SuppressExpectedErrors(window, message); + m_expectedErrorCount += suppress; + return suppress; } bool ErrorHandler::OnPreWarning( @@ -69,7 +83,9 @@ namespace UnitTest [[maybe_unused]] const char* func, const char* message) { m_warningCount++; - return SuppressExpectedErrors(window, message); + bool suppress = SuppressExpectedErrors(window, message); + m_expectedWarningCount += suppress; + return suppress; } bool ErrorHandler::OnPrintf(const char* window, const char* message) diff --git a/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.h b/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.h index 044c3d1111..56ef340d22 100644 --- a/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.h +++ b/Code/Framework/AzCore/Tests/AZTestShared/Utils/Utils.h @@ -30,8 +30,14 @@ namespace UnitTest public: explicit ErrorHandler(const char* errorPattern); ~ErrorHandler(); + //! Returns the total number of errors encountered (including those which match the expected pattern). int GetErrorCount() const; + //! Returns the total number of warnings encountered (including those which match the expected pattern). int GetWarningCount() const; + //! Returns the number of errors encountered which matched the expected pattern. + int GetExpectedErrorCount() const; + //! Returns the number of warnings encountered which matched the expected pattern. + int GetExpectedWarningCount() const; bool SuppressExpectedErrors(const char* window, const char* message); // AZ::Debug::TraceMessageBus @@ -44,6 +50,8 @@ namespace UnitTest AZStd::string m_errorPattern; int m_errorCount; int m_warningCount; + int m_expectedErrorCount; + int m_expectedWarningCount; }; } diff --git a/Code/Framework/AzCore/Tests/Math/MathTestData.h b/Code/Framework/AzCore/Tests/Math/MathTestData.h index cd3ade7854..c82c5caea7 100644 --- a/Code/Framework/AzCore/Tests/Math/MathTestData.h +++ b/Code/Framework/AzCore/Tests/Math/MathTestData.h @@ -61,8 +61,8 @@ namespace MathTestData }; static const AZ::Transform NonOrthogonalTransforms[] = { - AZ::Transform::CreateScale(AZ::Vector3(2.4f, 0.3f, 1.7f)), - AZ::Transform::CreateRotationX(2.2f) * AZ::Transform::CreateScale(AZ::Vector3(0.2f, 0.8f, 1.4f)) + AZ::Transform::CreateUniformScale(2.4f), + AZ::Transform::CreateRotationX(2.2f) * AZ::Transform::CreateUniformScale(0.8f) }; static const AZ::Transform OrthogonalTransforms[] = { diff --git a/Code/Framework/AzCore/Tests/Math/ObbTests.cpp b/Code/Framework/AzCore/Tests/Math/ObbTests.cpp index a90d66f288..de267b4265 100644 --- a/Code/Framework/AzCore/Tests/Math/ObbTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ObbTests.cpp @@ -59,11 +59,11 @@ namespace UnitTest TEST(MATH_Obb, TestScaleTransform) { Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); - Vector3 scaleFactors = Vector3(1.0f, 2.0f, 3.0f); - Transform transform = Transform::CreateScale(scaleFactors); + float scale = 3.0f; + Transform transform = Transform::CreateUniformScale(scale); obb = transform * obb; - EXPECT_THAT(obb.GetPosition(), IsClose(Vector3(1.0f, 4.0f, 9.0f))); - EXPECT_THAT(obb.GetHalfLengths(), IsClose(Vector3(0.5f, 1.0f, 1.5f))); + EXPECT_THAT(obb.GetPosition(), IsClose(Vector3(3.0f, 6.0f, 9.0f))); + EXPECT_THAT(obb.GetHalfLengths(), IsClose(Vector3(1.5f, 1.5f, 1.5f))); } TEST(MATH_Obb, TestSetPosition) diff --git a/Code/Framework/AzCore/Tests/Math/RandomTests.cpp b/Code/Framework/AzCore/Tests/Math/RandomTests.cpp index ace7d99704..7fe3acff54 100644 --- a/Code/Framework/AzCore/Tests/Math/RandomTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/RandomTests.cpp @@ -24,7 +24,7 @@ namespace UnitTest EXPECT_FLOAT_EQ(5981.0f / 15625.0f, GetHaltonNumber(4321, 5)); } - TEST(MATH_Random, HaltonSequence) + TEST(MATH_Random, HaltonSequenceStandard) { HaltonSequence<3> sequence({ 2, 3, 5 }); auto regularSequence = sequence.GetHaltonSequence<5>(); @@ -48,7 +48,11 @@ namespace UnitTest EXPECT_FLOAT_EQ(5.0f / 8.0f, regularSequence[4][0]); EXPECT_FLOAT_EQ(7.0f / 9.0f, regularSequence[4][1]); EXPECT_FLOAT_EQ(1.0f / 25.0f, regularSequence[4][2]); - + } + + TEST(MATH_Random, HaltonSequenceOffsets) + { + HaltonSequence<3> sequence({ 2, 3, 5 }); sequence.SetOffsets({ 1, 2, 3 }); auto offsetSequence = sequence.GetHaltonSequence<2>(); @@ -59,10 +63,15 @@ namespace UnitTest EXPECT_FLOAT_EQ(3.0f / 4.0f, offsetSequence[1][0]); EXPECT_FLOAT_EQ(4.0f / 9.0f, offsetSequence[1][1]); EXPECT_FLOAT_EQ(1.0f / 25.0f, offsetSequence[1][2]); - + } + + TEST(MATH_Random, HaltonSequenceIncrements) + { + HaltonSequence<3> sequence({ 2, 3, 5 }); + sequence.SetOffsets({ 1, 2, 3 }); sequence.SetIncrements({ 1, 2, 3 }); auto incrementedSequence = sequence.GetHaltonSequence<2>(); - + EXPECT_FLOAT_EQ(1.0f / 4.0f, incrementedSequence[0][0]); EXPECT_FLOAT_EQ(1.0f / 9.0f, incrementedSequence[0][1]); EXPECT_FLOAT_EQ(4.0f / 5.0f, incrementedSequence[0][2]); @@ -71,4 +80,35 @@ namespace UnitTest EXPECT_FLOAT_EQ(7.0f / 9.0f, incrementedSequence[1][1]); EXPECT_FLOAT_EQ(11.0f / 25.0f, incrementedSequence[1][2]); } + + TEST(MATH_Random, FillHaltonSequence) + { + HaltonSequence<3> sequence({ 2, 3, 5 }); + auto regularSequence = sequence.GetHaltonSequence<5>(); + + struct Point + { + Point() = default; + Point(AZStd::array arr) + :x(arr[0]) + ,y(arr[1]) + ,z(arr[2]) + {} + + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + }; + + AZStd::array ownedContainer; + sequence.FillHaltonSequence(ownedContainer.begin(), ownedContainer.end()); + + for (size_t i = 0; i < regularSequence.size(); ++i) + { + EXPECT_FLOAT_EQ(regularSequence[i][0], ownedContainer[i].x); + EXPECT_FLOAT_EQ(regularSequence[i][1], ownedContainer[i].y); + EXPECT_FLOAT_EQ(regularSequence[i][2], ownedContainer[i].z); + } + } + } diff --git a/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp index 400a845913..943aba9b76 100644 --- a/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp @@ -180,13 +180,13 @@ namespace Benchmark } } - BENCHMARK_F(BM_MathTransform, CreateScale)(benchmark::State& state) + BENCHMARK_F(BM_MathTransform, CreateUniformScale)(benchmark::State& state) { for (auto _ : state) { for (auto& testData : m_testDataArray) { - AZ::Transform result = AZ::Transform::CreateScale(testData.v3); + AZ::Transform result = AZ::Transform::CreateUniformScale(testData.value[0]); benchmark::DoNotOptimize(result); } } @@ -344,39 +344,39 @@ namespace Benchmark } } - BENCHMARK_F(BM_MathTransform, GetScale)(benchmark::State& state) + BENCHMARK_F(BM_MathTransform, GetUniformScale)(benchmark::State& state) { for (auto _ : state) { for (auto& testData : m_testDataArray) { - AZ::Vector3 result = testData.t1.GetScale(); + float result = testData.t1.GetUniformScale(); benchmark::DoNotOptimize(result); } } } - BENCHMARK_F(BM_MathTransform, SetScale)(benchmark::State& state) + BENCHMARK_F(BM_MathTransform, SetUniformScale)(benchmark::State& state) { for (auto _ : state) { for (auto& testData : m_testDataArray) { AZ::Transform testTransform = testData.t2; - testTransform.SetScale(testData.v3); + testTransform.SetUniformScale(testData.value[0]); benchmark::DoNotOptimize(testTransform); } } } - BENCHMARK_F(BM_MathTransform, ExtractScale)(benchmark::State& state) + BENCHMARK_F(BM_MathTransform, ExtractUniformScale)(benchmark::State& state) { for (auto _ : state) { for (auto& testData : m_testDataArray) { AZ::Transform testTransform = testData.t2; - AZ::Vector3 result = testTransform.ExtractScale(); + float result = testTransform.ExtractUniformScale(); benchmark::DoNotOptimize(result); } } diff --git a/Code/Framework/AzCore/Tests/Math/TransformTests.cpp b/Code/Framework/AzCore/Tests/Math/TransformTests.cpp index d0343211c3..529347f31d 100644 --- a/Code/Framework/AzCore/Tests/Math/TransformTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/TransformTests.cpp @@ -159,37 +159,14 @@ namespace UnitTest INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateFromQuaternionFixture, ::testing::ValuesIn(MathTestData::UnitQuaternions)); - using TransformCreateFromMatrix3x3Fixture = ::testing::TestWithParam; - - TEST_P(TransformCreateFromMatrix3x3Fixture, CreateFromMatrix3x3) - { - const AZ::Matrix3x3 matrix3x3 = GetParam(); - const AZ::Transform transform = AZ::Transform::CreateFromMatrix3x3(matrix3x3); - EXPECT_THAT(transform.GetTranslation(), IsClose(AZ::Vector3::CreateZero())); - const AZ::Vector3 vector(2.3f, -0.6, 1.8f); - EXPECT_THAT(transform.TransformPoint(vector), IsClose(matrix3x3 * vector)); - } - - TEST_P(TransformCreateFromMatrix3x3Fixture, CreateFromMatrix3x3AndTranslation) - { - const AZ::Matrix3x3 matrix3x3 = GetParam(); - const AZ::Vector3 translation(-2.6f, 1.7f, 0.8f); - const AZ::Transform transform = AZ::Transform::CreateFromMatrix3x3AndTranslation(matrix3x3, translation); - EXPECT_THAT(transform.GetTranslation(), IsClose(translation)); - const AZ::Vector3 vector(2.3f, -0.6, 1.8f); - EXPECT_THAT(transform.TransformPoint(vector), IsClose(matrix3x3 * vector + translation)); - } - - INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformCreateFromMatrix3x3Fixture, ::testing::ValuesIn(MathTestData::Matrix3x3s)); - - TEST(MATH_Transform, CreateScale) + TEST(MATH_Transform, CreateUniformScale) { - const AZ::Vector3 scale(1.7f, 0.3f, 2.4f); - const AZ::Transform transform = AZ::Transform::CreateScale(scale); + const float scale = 1.7f; + const AZ::Transform transform = AZ::Transform::CreateUniformScale(scale); const AZ::Vector3 vector(0.2f, -1.6f, 0.4f); EXPECT_THAT(transform.GetTranslation(), IsClose(AZ::Vector3::CreateZero())); const AZ::Vector3 transformedVector = transform.TransformPoint(vector); - const AZ::Vector3 expected(0.34f, -0.48f, 0.96f); + const AZ::Vector3 expected(0.34f, -2.72f, 0.68f); EXPECT_THAT(transformedVector, IsClose(expected)); } @@ -237,10 +214,10 @@ namespace UnitTest TEST(MATH_Transform, MultiplyByTransform) { const AZ::Transform transform1 = AZ::Transform::CreateRotationY(0.3f); - const AZ::Transform transform2 = AZ::Transform::CreateScale(AZ::Vector3(1.3f, 1.5f, 0.4f)); + const AZ::Transform transform2 = AZ::Transform::CreateUniformScale(1.3f); const AZ::Transform transform3 = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.42f, 0.46f, -0.66f, 0.42f), AZ::Vector3(2.8f, -3.7f, 1.6f)); - const AZ::Transform transform4 = AZ::Transform::CreateRotationX(-0.7f) * AZ::Transform::CreateScale(AZ::Vector3(0.6f, 1.3f, 0.7f)); + const AZ::Transform transform4 = AZ::Transform::CreateRotationX(-0.7f) * AZ::Transform::CreateUniformScale(0.6f); AZ::Transform transform5 = transform1; transform5 *= transform4; const AZ::Vector3 vector(1.9f, 2.3f, 0.2f); @@ -254,14 +231,14 @@ namespace UnitTest TEST(MATH_Transform, TranslationCorrectInTransformHierarchy) { AZ::Transform parent = AZ::Transform::CreateRotationZ(AZ::DegToRad(45.0f)); - parent.SetScale(AZ::Vector3(3.0f, 2.0f, 1.0f)); + parent.SetUniformScale(3.0f); parent.SetTranslation(AZ::Vector3(0.2f, 0.3f, 0.4f)); AZ::Transform child = AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f)); child.SetTranslation(AZ::Vector3(0.5f, 0.6f, 0.7f)); const AZ::Transform overallTransform = parent * child; const AZ::Vector3 overallTranslation = overallTransform.GetTranslation(); - const AZ::Vector3 expectedTranslation(0.412132f, 2.20919f, 1.1f); - EXPECT_THAT(overallTranslation, IsClose(AZ::Vector3(0.412132f, 2.20919f, 1.1f))); + const AZ::Vector3 expectedTranslation(-0.012132f, 2.633452f, 2.5f); + EXPECT_THAT(overallTranslation, IsClose(expectedTranslation)); } TEST(MATH_Transform, TransformPointVector3) @@ -337,14 +314,14 @@ namespace UnitTest TEST_P(TransformScaleFixture, Scale) { const AZ::Transform orthogonalTransform = GetParam(); - EXPECT_THAT(orthogonalTransform.GetScale(), IsClose(AZ::Vector3::CreateOne())); + EXPECT_NEAR(orthogonalTransform.GetUniformScale(), 1.0f, AZ::Constants::Tolerance); AZ::Transform unscaledTransform = orthogonalTransform; - unscaledTransform.ExtractScale(); - EXPECT_THAT(unscaledTransform.GetScale(), IsClose(AZ::Vector3::CreateOne())); - const AZ::Vector3 scale(2.8f, 0.7f, 1.3f); + unscaledTransform.ExtractUniformScale(); + EXPECT_NEAR(unscaledTransform.GetUniformScale(), 1.0f, AZ::Constants::Tolerance); + const float scale = 2.8f; AZ::Transform scaledTransform = orthogonalTransform; - scaledTransform.MultiplyByScale(scale); - EXPECT_THAT(scaledTransform.GetScale(), IsClose(scale)); + scaledTransform.MultiplyByUniformScale(scale); + EXPECT_NEAR(scaledTransform.GetUniformScale(), scale, AZ::Constants::Tolerance); } INSTANTIATE_TEST_CASE_P(MATH_Transform, TransformScaleFixture, ::testing::ValuesIn(MathTestData::OrthogonalTransforms)); @@ -353,24 +330,11 @@ namespace UnitTest { EXPECT_TRUE(AZ::Transform::CreateIdentity().IsOrthogonal()); EXPECT_TRUE(AZ::Transform::CreateRotationZ(0.3f).IsOrthogonal()); - EXPECT_FALSE(AZ::Transform::CreateScale(AZ::Vector3(0.8f, 0.3f, 1.2f)).IsOrthogonal()); + EXPECT_FALSE(AZ::Transform::CreateUniformScale(0.8f).IsOrthogonal()); EXPECT_TRUE(AZ::Transform::CreateFromQuaternion(AZ::Quaternion(-0.52f, -0.08f, 0.56f, 0.64f)).IsOrthogonal()); AZ::Transform transform; transform.SetFromEulerRadians(AZ::Vector3(0.2f, 0.4f, 0.1f)); EXPECT_TRUE(transform.IsOrthogonal()); - - // want to test each possible way the transform could fail to be orthogonal, which we can do by testing for one - // axis, then using a rotation which cycles the axes - const AZ::Transform axisCycle = AZ::Transform::CreateFromQuaternion(AZ::Quaternion(0.5f, 0.5f, 0.5f, 0.5f)); - - // a transform which is normalized in 2 axes, but not the third - AZ::Transform nonOrthogonalTransform1 = AZ::Transform::CreateScale(AZ::Vector3(1.0f, 1.0f, 2.0f)); - - for (int i = 0; i < 3; i++) - { - EXPECT_FALSE(nonOrthogonalTransform1.IsOrthogonal()); - nonOrthogonalTransform1 = axisCycle * nonOrthogonalTransform1; - } } using TransformSetFromEulerDegreesFixture = ::testing::TestWithParam; @@ -459,16 +423,17 @@ namespace UnitTest { const char* objectStreamBuffer = R"DELIMITER( - + )DELIMITER"; AZ::Transform* deserializedTransform = AZ::Utils::LoadObjectFromBuffer(objectStreamBuffer, strlen(objectStreamBuffer) + 1); const AZ::Vector3 expectedTranslation(513.7845459f, 492.5420837f, 32.0000000f); - const AZ::Vector3 expectedScale(1.5f, 0.5f, 1.2f); + const float expectedScale = 1.5f; const AZ::Quaternion expectedRotation(0.2624075f, 0.4405251f, 0.2029076f, 0.8342113f); const AZ::Transform expectedTransform = - AZ::Transform::CreateFromQuaternionAndTranslation(expectedRotation, expectedTranslation) * AZ::Transform::CreateScale(expectedScale); + AZ::Transform::CreateFromQuaternionAndTranslation(expectedRotation, expectedTranslation) * + AZ::Transform::CreateUniformScale(expectedScale); EXPECT_TRUE(deserializedTransform->IsClose(expectedTransform)); azfree(deserializedTransform); diff --git a/Code/Framework/AzCore/Tests/ScriptMath.cpp b/Code/Framework/AzCore/Tests/ScriptMath.cpp index cb673e92e7..dce63051c9 100644 --- a/Code/Framework/AzCore/Tests/ScriptMath.cpp +++ b/Code/Framework/AzCore/Tests/ScriptMath.cpp @@ -1275,10 +1275,10 @@ namespace UnitTest script->Execute("AZTestAssert(t1:TransformVector(Vector3(1, 0, 0)):IsClose(Vector3(1, 0, 0)))"); script->Execute("AZTestAssert(t1:TransformVector(Vector3(0, 1, 0)):IsClose(Vector3(0, 0.866, 0.5)))"); script->Execute("AZTestAssert(t1:TransformVector(Vector3(0, 0, 1)):IsClose(Vector3(0, -0.5, 0.866)))"); - script->Execute("t1 = Transform.CreateScale(Vector3(1, 2, 3))"); - script->Execute("AZTestAssert(t1:TransformVector(Vector3(1, 0, 0)):IsClose(Vector3(1, 0, 0)))"); + script->Execute("t1 = Transform.CreateUniformScale(2)"); + script->Execute("AZTestAssert(t1:TransformVector(Vector3(1, 0, 0)):IsClose(Vector3(2, 0, 0)))"); script->Execute("AZTestAssert(t1:TransformVector(Vector3(0, 1, 0)):IsClose(Vector3(0, 2, 0)))"); - script->Execute("AZTestAssert(t1:TransformVector(Vector3(0, 0, 1)):IsClose(Vector3(0, 0, 3)))"); + script->Execute("AZTestAssert(t1:TransformVector(Vector3(0, 0, 1)):IsClose(Vector3(0, 0, 2)))"); script->Execute("t1 = Transform.CreateTranslation(Vector3(1, 2, 3))"); script->Execute("AZTestAssert(t1:TransformVector(Vector3(1, 0, 0)):IsClose(Vector3(1, 0, 0)))"); script->Execute("AZTestAssert(t1:TransformVector(Vector3(0, 1, 0)):IsClose(Vector3(0, 1, 0)))"); @@ -1341,19 +1341,19 @@ namespace UnitTest script->Execute("AZTestAssert(t3:GetTranslation():IsClose(Vector3(-5.90, 25.415, 19.645), 0.001))"); ////test inverse, should handle non-orthogonal matrices - script->Execute("t1 = Transform.CreateRotationX(1) * Transform.CreateScale(Vector3(1, 2, 3))"); + script->Execute("t1 = Transform.CreateRotationX(1) * Transform.CreateUniformScale(2)"); script->Execute("AZTestAssert((t1*t1:GetInverse()):IsClose(Transform.CreateIdentity()))"); ////scale access - script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(40)) * Transform.CreateScale(Vector3(2, 3, 4))"); - script->Execute("AZTestAssert(t1:GetScale():IsClose(Vector3(2, 3, 4)))"); - script->Execute("AZTestAssert(t1:ExtractScale():IsClose(Vector3(2, 3, 4)))"); - script->Execute("AZTestAssert(t1:GetScale():IsClose(Vector3.CreateOne()))"); - script->Execute("t1:MultiplyByScale(Vector3(3, 4, 5))"); - script->Execute("AZTestAssert(t1:GetScale():IsClose(Vector3(3, 4, 5)))"); + script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(40)) * Transform.CreateUniformScale(3)"); + script->Execute("AZTestAssertFloatClose(t1:GetUniformScale(), 3)"); + script->Execute("AZTestAssertFloatClose(t1:ExtractUniformScale(), 3)"); + script->Execute("AZTestAssertFloatClose(t1:GetUniformScale(), 1)"); + script->Execute("t1:MultiplyByUniformScale(2)"); + script->Execute("AZTestAssertFloatClose(t1:GetUniformScale(), 2)"); ////orthogonalize - script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(30)) * Transform.CreateScale(Vector3(2, 3, 4))"); + script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(30)) * Transform.CreateUniformScale(3)"); script->Execute("t1:SetTranslation(Vector3(1,2,3))"); script->Execute("t2 = t1:GetOrthogonalized()"); script->Execute("AZTestAssertFloatClose(t2:GetBasisX():GetLength(), 1)"); @@ -1372,7 +1372,7 @@ namespace UnitTest script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(30))"); script->Execute("t1:SetTranslation(Vector3(1, 2, 3))"); script->Execute("AZTestAssert(t1:IsOrthogonal(0.05))"); - script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(30)) * Transform.CreateScale(Vector3(2, 3, 4))"); + script->Execute("t1 = Transform.CreateRotationX(Math.DegToRad(30)) * Transform.CreateUniformScale(2)"); script->Execute("AZTestAssert( not t1:IsOrthogonal(0.05))"); ////IsClose diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases.h b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases.h index b01dbe9b0d..ae313632af 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases.h +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases.h @@ -21,7 +21,7 @@ namespace JsonSerializationTests { using JsonSerializationTestCases = ::testing::Types< // Structures - SimpleClass, SimpleInheritence, MultipleInheritence, SimpleNested, SimpleEnumWrapper, + SimpleClass, SimpleInheritence, MultipleInheritence, SimpleNested, SimpleEnumWrapper, NonReflectedEnumWrapper, // Pointers SimpleNullPointer, SimpleAssignedPointer, ComplexAssignedPointer, ComplexNullInheritedPointer, ComplexAssignedDifferentInheritedPointer, ComplexAssignedSameInheritedPointer, diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.cpp index 5be031d70a..6da3120f59 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.cpp @@ -373,6 +373,57 @@ namespace JsonSerializationTests return MakeInstanceWithoutDefaults(AZStd::move(instance), json); } + // NonReflectedEnumWrapper + bool NonReflectedEnumWrapper::Equals(const NonReflectedEnumWrapper& rhs, bool fullReflection) const + { + return !fullReflection || (m_enumClass == rhs.m_enumClass && m_rawEnum== rhs.m_rawEnum); + } + + void NonReflectedEnumWrapper::Reflect(AZStd::unique_ptr& context, bool fullReflection) + { + if (fullReflection) + { + // Note that the enums are not reflected using context->Enum<> + + context->Class() + ->Field("enumClass", &NonReflectedEnumWrapper::m_enumClass) + ->Field("rawEnum", &NonReflectedEnumWrapper::m_rawEnum); + } + } + + InstanceWithSomeDefaults NonReflectedEnumWrapper::GetInstanceWithSomeDefaults() + { + auto instance = AZStd::make_unique(); + instance->m_enumClass = NonReflectedEnumWrapper::SimpleEnumClass::Option2; + + const char* strippedDefaults = R"( + { + "enumClass": 2 + })"; + const char* keptDefaults = R"( + { + "enumClass": 2, + "rawEnum": 0 + })"; + + return MakeInstanceWithSomeDefaults(AZStd::move(instance), + strippedDefaults, keptDefaults); + } + + InstanceWithoutDefaults NonReflectedEnumWrapper::GetInstanceWithoutDefaults() + { + auto instance = AZStd::make_unique(); + instance->m_enumClass = NonReflectedEnumWrapper::SimpleEnumClass::Option2; + instance->m_rawEnum = NonReflectedEnumWrapper::SimpleRawEnum::RawOption1; + + const char* json = R"( + { + "enumClass": 2, + "rawEnum": 1 + })"; + return MakeInstanceWithoutDefaults(AZStd::move(instance), json); + } + // TemplatedClass bool TemplatedClass::Equals(const TemplatedClass& rhs, bool fullReflection) const diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.h b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.h index db5db23fba..1830ca9e6f 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.h +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TestCases_Classes.h @@ -134,6 +134,35 @@ namespace JsonSerializationTests SimpleRawEnum m_rawEnum{}; }; + struct NonReflectedEnumWrapper + { + enum class SimpleEnumClass + { + Option1 = 1, + Option2, + }; + enum SimpleRawEnum + { + RawOption1 = 1, + RawOption2, + }; + AZ_CLASS_ALLOCATOR(NonReflectedEnumWrapper, AZ::SystemAllocator, 0); + AZ_RTTI(NonReflectedEnumWrapper, "{A80D5B6B-2FD1-46E9-A7A9-44C5E2650526}"); + + static constexpr bool SupportsPartialDefaults = true; + + NonReflectedEnumWrapper() = default; + virtual ~NonReflectedEnumWrapper() = default; + + bool Equals(const NonReflectedEnumWrapper& rhs, bool fullReflection) const; + static void Reflect(AZStd::unique_ptr& context, bool fullReflection); + static InstanceWithSomeDefaults GetInstanceWithSomeDefaults(); + static InstanceWithoutDefaults GetInstanceWithoutDefaults(); + + SimpleEnumClass m_enumClass{}; + SimpleRawEnum m_rawEnum{}; + }; + template struct TemplatedClass { @@ -158,5 +187,7 @@ namespace AZ { AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::SimpleEnumWrapper::SimpleEnumClass, "{AF6F1964-5B20-4689-BF23-F36B9C9AAE6A}"); AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::SimpleEnumWrapper::SimpleRawEnum, "{EB24207F-B48F-4D8B-940D-3CD06A371739}"); + AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::NonReflectedEnumWrapper::SimpleEnumClass, "{E80E4A41-B29E-4B7C-B630-3B599172C837}"); + AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::NonReflectedEnumWrapper::SimpleRawEnum, "{C42AF28D-4F84-4540-972A-5B6EEFAB13FF}"); AZ_TYPE_INFO_TEMPLATE(JsonSerializationTests::TemplatedClass, "{CA4ADF74-66E7-4D16-B4AC-F71278C60EC7}", AZ_TYPE_INFO_TYPENAME); } diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp index 12711b1e1a..7eabd6e5e0 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp @@ -112,7 +112,7 @@ namespace JsonSerializationTests AZ::Transform testTransform = AZ::Transform::CreateIdentity(); AZ::Transform expectedTransform = AZ::Transform::CreateFromQuaternion(AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f)); - expectedTransform.SetScale(AZ::Vector3(5.5f)); + expectedTransform.SetUniformScale(5.5f); rapidjson::Document json; json.Parse(R"({ "Rotation": [ 0.25, 0.5, 0.75, 1.0 ], "Scale": 5.5 })"); @@ -128,7 +128,7 @@ namespace JsonSerializationTests { AZ::Transform testTransform = AZ::Transform::CreateIdentity(); AZ::Transform expectedTransform = AZ::Transform::CreateTranslation(AZ::Vector3(2.25f, 3.5f, 4.75f)); - expectedTransform.SetScale(AZ::Vector3(5.5f)); + expectedTransform.SetUniformScale(5.5f); rapidjson::Document json; json.Parse(R"({ "Translation": [ 2.25, 3.5, 4.75 ], "Scale": 5.5 })"); @@ -189,7 +189,7 @@ namespace JsonSerializationTests TEST_F(JsonTransformSerializerTests, Load_FullySetTransform_ReturnsSuccessWithOnlyScale) { AZ::Transform testTransform = AZ::Transform::CreateIdentity(); - AZ::Transform expectedTransform = AZ::Transform::CreateScale(AZ::Vector3(5.5f)); + AZ::Transform expectedTransform = AZ::Transform::CreateUniformScale(5.5f); rapidjson::Document json; json.Parse(R"({ "Scale" : 5.5 })"); diff --git a/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp b/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp index 36b9757ce3..751d9ded6c 100644 --- a/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp +++ b/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp @@ -372,15 +372,15 @@ mac_remote_filesystem=0 -- We need to know this before we establish VFS because different platform assets -- are stored in different root folders in the cache. These correspond to the names -- In the asset processor config file. This value also controls what config file is read --- when you read system_xxxx_xxxx.cfg (for example, system_windows_pc.cfg or system_android_es3.cfg) +-- when you read system_xxxx_xxxx.cfg (for example, system_windows_pc.cfg or system_android_android.cfg) -- by default, pc assets (in the 'pc' folder) are used, with RC being fed 'pc' as the platform -- by default on console we use the default assets=pc for better iteration times -- we should turn on console specific assets only when in release and/or testing assets and/or loading performance -- that way most people will not need to have 3 different caches taking up disk space assets = pc -android_assets = es3 +android_assets = android ios_assets = ios -mac_assets = osx_gl +mac_assets = mac -- Add the IP address of your console to the white list that will connect to the asset processor here -- You can list addresses or CIDR's. CIDR's are helpful if you are using DHCP. A CIDR looks like an ip address with @@ -438,9 +438,9 @@ mac_wait_for_connect=0 ConfigFileParams::SettingsKeyValuePair{"/ios_remote_filesystem", AZ::s64{0}}, ConfigFileParams::SettingsKeyValuePair{"/mac_remote_filesystem", AZ::s64{0}}, ConfigFileParams::SettingsKeyValuePair{"/assets", AZStd::string_view{"pc"}}, - ConfigFileParams::SettingsKeyValuePair{"/android_assets", AZStd::string_view{"es3"}}, + ConfigFileParams::SettingsKeyValuePair{"/android_assets", AZStd::string_view{"android"}}, ConfigFileParams::SettingsKeyValuePair{"/ios_assets", AZStd::string_view{"ios"}}, - ConfigFileParams::SettingsKeyValuePair{"/mac_assets", AZStd::string_view{"osx_gl"}}, + ConfigFileParams::SettingsKeyValuePair{"/mac_assets", AZStd::string_view{"mac"}}, ConfigFileParams::SettingsKeyValuePair{"/connect_to_remote", AZ::s64{0}}, ConfigFileParams::SettingsKeyValuePair{"/windows_connect_to_remote", AZ::s64{1}}, ConfigFileParams::SettingsKeyValuePair{"/android_connect_to_remote", AZ::s64{0}}, @@ -478,20 +478,20 @@ test_asset_processor_tag = test_value [Platform pc] tags=tools,renderer,dx12,vulkan -[Platform es3] +[Platform android] tags=android,mobile,renderer,vulkan ; With Comments at the end [Platform ios] tags=mobile,renderer,metal -[Platform osx_gl] +[Platform mac] tags=tools,renderer,metal)" , AZStd::fixed_vector{ ConfigFileParams::SettingsKeyValuePair{"/test_asset_processor_tag", AZStd::string_view{"test_value"}}, ConfigFileParams::SettingsKeyValuePair{"/Platform pc/tags", AZStd::string_view{"tools,renderer,dx12,vulkan"}}, - ConfigFileParams::SettingsKeyValuePair{"/Platform es3/tags", AZStd::string_view{"android,mobile,renderer,vulkan"}}, + ConfigFileParams::SettingsKeyValuePair{"/Platform android/tags", AZStd::string_view{"android,mobile,renderer,vulkan"}}, ConfigFileParams::SettingsKeyValuePair{"/Platform ios/tags", AZStd::string_view{"mobile,renderer,metal"}}, - ConfigFileParams::SettingsKeyValuePair{"/Platform osx_gl/tags", AZStd::string_view{"tools,renderer,metal"}}, + ConfigFileParams::SettingsKeyValuePair{"/Platform mac/tags", AZStd::string_view{"tools,renderer,metal"}}, }} ) ); diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index e02892de4e..c65ba373f8 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -679,8 +679,6 @@ namespace AzFramework { auto fileIoBase = m_archiveFileIO.get(); // Set up the default file aliases based on the settings registry - fileIoBase->SetAlias("@assets@", ""); - fileIoBase->SetAlias("@root@", GetEngineRoot()); fileIoBase->SetAlias("@engroot@", GetEngineRoot()); fileIoBase->SetAlias("@projectroot@", GetEngineRoot()); fileIoBase->SetAlias("@exefolder@", GetExecutableFolder()); @@ -694,8 +692,8 @@ namespace AzFramework pathAliases.clear(); if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) { - fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str()); fileIoBase->SetAlias("@assets@", pathAliases.c_str()); + fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str()); fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@ } pathAliases.clear(); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index b0285616df..4a80db2b24 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -2008,13 +2008,12 @@ namespace AZ::IO // if no bind root is specified, compute one: strBindRoot = !bindRoot.empty() ? bindRoot : szFullPath->ParentPath().Native(); - // Check if archive file disk exist on disk or inside of pak. - bool bFileExists = IsFileExist(szFullPath->Native()); - - if (!bFileExists && (nFactoryFlags & ZipDir::CacheFactory::FLAGS_READ_ONLY)) + // Check if archive file disk exist on disk. + const bool pakOnDisk = FileIOBase::GetDirectInstance()->Exists(szFullPath->c_str()); + if (!pakOnDisk && (nFactoryFlags & ZipDir::CacheFactory::FLAGS_READ_ONLY)) { // Archive file not found. - AZ_TracePrintf("Archive", "Cannot open Archive file %s\n", szFullPath->c_str()); + AZ_TracePrintf("Archive", "Archive file %s does not exist\n", szFullPath->c_str()); return nullptr; } @@ -2492,8 +2491,6 @@ namespace AZ::IO void Archive::FindCompressionInfo(bool& found, AZ::IO::CompressionInfo& info, const AZStd::string_view filename) { - constexpr uint32_t s_compressionTag = static_cast('Z') << 24 | static_cast('C') << 16 | static_cast('R') << 8 | static_cast('Y'); - if (!found) { auto correctedFilename = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(filename); @@ -2519,7 +2516,6 @@ namespace AZ::IO found = true; info.m_archiveFilename.InitFromRelativePath(archive->GetFilePath()); - info.m_compressionTag.m_code = s_compressionTag; info.m_offset = pFileData->GetFileDataOffset(); info.m_compressedSize = entry->desc.lSizeCompressed; info.m_uncompressedSize = entry->desc.lSizeUncompressed; @@ -2539,9 +2535,8 @@ namespace AZ::IO break; } - info.m_decompressor = [&s_compressionTag]([[maybe_unused]] const AZ::IO::CompressionInfo& info, const void* compressed, size_t compressedSize, void* uncompressed, size_t uncompressedBufferSize)->bool + info.m_decompressor = []([[maybe_unused]] const AZ::IO::CompressionInfo& info, const void* compressed, size_t compressedSize, void* uncompressed, size_t uncompressedBufferSize)->bool { - AZ_Assert(info.m_compressionTag.m_code == s_compressionTag, "Provided compression info isn't supported by this decompressor."); size_t nSizeUncompressed = uncompressedBufferSize; return ZipDir::ZipRawUncompress(uncompressed, &nSizeUncompressed, compressed, compressedSize) == 0; }; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp index 678f4e40bf..1794ae90e7 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp @@ -50,6 +50,7 @@ namespace AZ::IO , tWrite{ writeTime } { } + ArchiveFileIterator::ArchiveFileIterator(FindData* findData, AZStd::string_view filename, const FileDesc& fileDesc) : m_findData{ findData } , m_filename{ filename } @@ -108,13 +109,10 @@ namespace AZ::IO AZ::StringFunc::Path::GetFullPath(directory.c_str(), searchDirectory); AZ::StringFunc::Path::GetFullFileName(directory.c_str(), pattern); } - AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool { AZ::IO::FileDesc fileDesc; - - AZStd::string fullFilePath; - AZ::StringFunc::Path::GetFullFileName(filePath, fullFilePath); + AZStd::string filePathEntry{filePath}; if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath)) { @@ -135,9 +133,8 @@ namespace AZ::IO fileDesc.tAccess = fileDesc.tWrite; fileDesc.tCreate = fileDesc.tWrite; } - [[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(fullFilePath), fileDesc); - AZ_Assert(result.second, "Failed to insert FindData entry for %s", fullFilePath.c_str()); - + [[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(filePathEntry), fileDesc); + AZ_Assert(result.second, "Failed to insert FindData entry for filePath %s", filePath); return true; }); } @@ -273,7 +270,9 @@ namespace AZ::IO } auto pakFileIter = m_mapFiles.begin(); - fileIterator.m_filename = pakFileIter->first; + AZStd::string fullFilePath; + AZ::StringFunc::Path::GetFullFileName(pakFileIter->first.c_str(), fullFilePath); + fileIterator.m_filename = AZStd::move(fullFilePath); fileIterator.m_fileDesc = pakFileIter->second; fileIterator.m_lastFetchValid = true; diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp index 020feffc47..7280c4af5c 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp @@ -308,6 +308,56 @@ namespace AzFramework } } + //--------------------------------------------------------------------- + GenerateRelativeSourcePathRequest::GenerateRelativeSourcePathRequest(const AZ::OSString& sourcePath) + { + AZ_Assert(!sourcePath.empty(), "GenerateRelativeSourcePathRequest: asset path is empty"); + m_sourcePath = sourcePath; + } + + unsigned int GenerateRelativeSourcePathRequest::GetMessageType() const + { + return MessageType; + } + + void GenerateRelativeSourcePathRequest::Reflect(AZ::ReflectContext* context) + { + auto serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("SourcePath", &GenerateRelativeSourcePathRequest::m_sourcePath); + } + } + + //--------------------------------------------------------------------- + GenerateRelativeSourcePathResponse::GenerateRelativeSourcePathResponse( + bool resolved, const AZ::OSString& relativeSourcePath, const AZ::OSString& rootFolder) + { + m_relativeSourcePath = relativeSourcePath; + m_resolved = resolved; + m_rootFolder = rootFolder; + } + + unsigned int GenerateRelativeSourcePathResponse::GetMessageType() const + { + return GenerateRelativeSourcePathRequest::MessageType; + } + + void GenerateRelativeSourcePathResponse::Reflect(AZ::ReflectContext* context) + { + auto serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("RelativeSourcePath", &GenerateRelativeSourcePathResponse::m_relativeSourcePath) + ->Field("RootFolder", &GenerateRelativeSourcePathResponse::m_rootFolder) + ->Field("Resolved", &GenerateRelativeSourcePathResponse::m_resolved); + } + } + //--------------------------------------------------------------------- GetFullSourcePathFromRelativeProductPathRequest::GetFullSourcePathFromRelativeProductPathRequest(const AZ::OSString& relativeProductPath) { diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h index 9661e61828..c15f75e3e7 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h @@ -288,6 +288,45 @@ namespace AzFramework bool m_resolved; }; + ////////////////////////////////////////////////////////////////////////// + class GenerateRelativeSourcePathRequest : public BaseAssetProcessorMessage + { + public: + AZ_CLASS_ALLOCATOR(GenerateRelativeSourcePathRequest, AZ::OSAllocator, 0); + AZ_RTTI(GenerateRelativeSourcePathRequest, "{B3865033-F5A3-4749-8147-7B1AB04D5F6D}", + BaseAssetProcessorMessage); + static void Reflect(AZ::ReflectContext* context); + + // For people that are debugging the network messages and just see MessageType as a value, + // the CRC value below is 739777771 (0x2C181CEB) + static constexpr unsigned int MessageType = + AZ_CRC_CE("AssetSystem::GenerateRelativeSourcePathRequest"); + + GenerateRelativeSourcePathRequest() = default; + GenerateRelativeSourcePathRequest(const AZ::OSString& sourcePath); + unsigned int GetMessageType() const override; + + AZ::OSString m_sourcePath; + }; + + class GenerateRelativeSourcePathResponse : public BaseAssetProcessorMessage + { + public: + AZ_CLASS_ALLOCATOR(GenerateRelativeSourcePathResponse, AZ::OSAllocator, 0); + AZ_RTTI(GenerateRelativeSourcePathResponse, "{938D33DB-C8F6-4FA4-BC81-2F139A9BE1D7}", + BaseAssetProcessorMessage); + static void Reflect(AZ::ReflectContext* context); + + GenerateRelativeSourcePathResponse() = default; + GenerateRelativeSourcePathResponse( + bool resolved, const AZ::OSString& relativeSourcePath, const AZ::OSString& rootFolder); + unsigned int GetMessageType() const override; + + AZ::OSString m_relativeSourcePath; + AZ::OSString m_rootFolder; ///< This is the folder it was found in (the watched/scanned folder, such as gems /assets/ folder) + bool m_resolved; + }; + ////////////////////////////////////////////////////////////////////////// class GetFullSourcePathFromRelativeProductPathRequest : public BaseAssetProcessorMessage diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp index 83c4907468..6b19084c2a 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp @@ -202,6 +202,7 @@ namespace AzFramework // Requests GetUnresolvedDependencyCountsRequest::Reflect(context); GetRelativeProductPathFromFullSourceOrProductPathRequest::Reflect(context); + GenerateRelativeSourcePathRequest::Reflect(context); GetFullSourcePathFromRelativeProductPathRequest::Reflect(context); SourceAssetInfoRequest::Reflect(context); AssetInfoRequest::Reflect(context); @@ -234,6 +235,7 @@ namespace AzFramework // Responses GetUnresolvedDependencyCountsResponse::Reflect(context); GetRelativeProductPathFromFullSourceOrProductPathResponse::Reflect(context); + GenerateRelativeSourcePathResponse::Reflect(context); GetFullSourcePathFromRelativeProductPathResponse::Reflect(context); SourceAssetInfoResponse::Reflect(context); AssetInfoResponse::Reflect(context); diff --git a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h index 682e886061..ee8b79bf06 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h +++ b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #include #include -#include #include #include +#include namespace AzFramework { @@ -64,15 +64,13 @@ namespace AzFramework the EditContext. TController can friend itself to the editor component to make this work if required. */ template - class ComponentAdapter - : public AZ::Component + class ComponentAdapter : public AZ::Component { public: - AZ_RTTI((ComponentAdapter, "{644A9187-4FDB-42C1-9D59-DD75304B551A}", TController, TConfiguration), AZ::Component); ComponentAdapter() = default; - ComponentAdapter(const TConfiguration& configuration); + explicit ComponentAdapter(const TConfiguration& configuration); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); @@ -85,7 +83,6 @@ namespace AzFramework void Deactivate() override; protected: - static void Reflect(AZ::ReflectContext* context); // AZ::Component overrides ... diff --git a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl index a1b0826193..5c36ff0bf7 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl +++ b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include @@ -32,10 +32,12 @@ namespace AzFramework if (auto serializeContext = azrtti_cast(context)) { + // clang-format off serializeContext->Class() ->Version(1) ->Field("Controller", &ComponentAdapter::m_controller) ; + // clang-format on } } @@ -66,9 +68,6 @@ namespace AzFramework GetDependentServicesHelper(services, typename AZ::HasComponentDependentServices::type()); } - ////////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - template void ComponentAdapter::Init() { @@ -78,7 +77,7 @@ namespace AzFramework template void ComponentAdapter::Activate() { - m_controller.Activate(GetEntityId()); + ComponentActivateHelper::Activate(m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId())); } template diff --git a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h index 158ee95f39..f0ef262a71 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h +++ b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h @@ -13,6 +13,7 @@ #pragma once #include +#include namespace AzFramework { @@ -27,18 +28,43 @@ namespace AzFramework template struct ComponentInitHelper { - static void Init(T& common) + static void Init([[maybe_unused]] T& controller) { - AZ_UNUSED(common); } }; template struct ComponentInitHelper().Init())>> { - static void Init(T& common) + static void Init(T& controller) { - common.Init(); + controller.Init(); + } + }; + + template + struct ComponentActivateHelper + { + static void Activate([[maybe_unused]] T& controller, [[maybe_unused]] const AZ::EntityComponentIdPair& entityComponentIdPair) + { + } + }; + + template + struct ComponentActivateHelper().Activate(AZ::EntityId()))>> + { + static void Activate(T& controller, const AZ::EntityComponentIdPair& entityComponentIdPair) + { + controller.Activate(entityComponentIdPair.GetEntityId()); + } + }; + + template + struct ComponentActivateHelper().Activate(AZ::EntityComponentIdPair()))>> + { + static void Activate(T& controller, const AZ::EntityComponentIdPair& entityComponentIdPair) + { + controller.Activate(entityComponentIdPair); } }; diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp index 9083cb7d0d..49adab2252 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp @@ -327,99 +327,13 @@ namespace AzFramework return localZ; } - void TransformComponent::SetRotation(const AZ::Vector3& eulerAnglesRadian) + void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) { - AZ_Warning("TransformComponent", false, "SetRotation is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::ConvertEulerRadiansToQuaternion(eulerAnglesRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationQuaternion(const AZ::Quaternion& quaternion) - { - AZ_Warning("TransformComponent", false, "SetRotationQuaternion is deprecated, please use SetLocalRotationQuaternion"); - AZ::Transform newWorldTransform = m_worldTM; newWorldTransform.SetRotation(quaternion); SetWorldTM(newWorldTransform); } - void TransformComponent::SetRotationX(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "SetRotationX is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationX(eulerAngleRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationY(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "SetRotationY is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationY(eulerAngleRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationZ(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "SetRotationZ is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationZ(eulerAngleRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::RotateByX(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "RotateByX is deprecated, please use RotateAroundLocalX"); - RotateAroundLocalX(eulerAngleRadian); - } - - void TransformComponent::RotateByY(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "RotateByY is deprecated, please use RotateAroundLocalY"); - RotateAroundLocalY(eulerAngleRadian); - } - - void TransformComponent::RotateByZ(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "RotateByZ is deprecated, please use RotateAroundLocalZ"); - RotateAroundLocalZ(eulerAngleRadian); - } - - AZ::Vector3 TransformComponent::GetRotationEulerRadians() - { - AZ_Warning("TransformComponent", false, "GetRotationEulerRadians is deprecated, please use GetWorldRotation"); - return m_worldTM.GetRotation().GetEulerRadians(); - } - - AZ::Quaternion TransformComponent::GetRotationQuaternion() - { - AZ_Warning("TransformComponent", false, "GetRotationQuaternion is deprecated, please use GetWorldRotationQuaternion"); - return m_worldTM.GetRotation(); - } - - float TransformComponent::GetRotationX() - { - AZ_Warning("TransformComponent", false, "GetRotationX is deprecated, please use GetWorldRotation"); - return GetRotationEulerRadians().GetX(); - } - - float TransformComponent::GetRotationY() - { - AZ_Warning("TransformComponent", false, "GetRotationY is deprecated, please use GetWorldRotation"); - return GetRotationEulerRadians().GetY(); - } - - float TransformComponent::GetRotationZ() - { - AZ_Warning("TransformComponent", false, "GetRotationZ is deprecated, please use GetWorldRotation"); - return GetRotationEulerRadians().GetZ(); - } - AZ::Vector3 TransformComponent::GetWorldRotation() { return m_worldTM.GetRotation().GetEulerRadians(); @@ -432,46 +346,26 @@ namespace AzFramework void TransformComponent::SetLocalRotation(const AZ::Vector3& eulerRadianAngles) { - AZ::Transform newLocalTM = AZ::ConvertEulerRadiansToTransform(eulerRadianAngles); - newLocalTM.SetScale(m_localTM.GetScale()); - newLocalTM.SetTranslation(m_localTM.GetTranslation()); + AZ::Transform newLocalTM = m_localTM; + newLocalTM.SetRotation(AZ::Quaternion::CreateFromEulerAnglesRadians(eulerRadianAngles)); SetLocalTM(newLocalTM); } void TransformComponent::SetLocalRotationQuaternion(const AZ::Quaternion& quaternion) { - AZ::Transform newLocalTM; - newLocalTM.SetScale(m_localTM.GetScale()); - newLocalTM.SetTranslation(m_localTM.GetTranslation()); + AZ::Transform newLocalTM = m_localTM; newLocalTM.SetRotation(quaternion); SetLocalTM(newLocalTM); } static AZ::Transform RotateAroundLocalHelper(float eulerAngleRadian, const AZ::Transform& localTM, AZ::Vector3 axis) { - //get the existing translation and scale - AZ::Vector3 translation = localTM.GetTranslation(); - AZ::Vector3 scale = localTM.GetScale(); - //normalize the axis before creating rotation axis.Normalize(); AZ::Quaternion rotate = AZ::Quaternion::CreateFromAxisAngle(axis, eulerAngleRadian); - //create new rotation transform - AZ::Quaternion currentRotate = localTM.GetRotation(); - AZ::Quaternion newRotate = rotate * currentRotate; - newRotate.Normalize(); - - //scale - AZ::Transform newLocalTM = AZ::Transform::CreateScale(scale); - - //rotate - AZ::Transform rotateLocalTM = AZ::Transform::CreateFromQuaternion(newRotate); - newLocalTM = rotateLocalTM * newLocalTM; - - //translate - newLocalTM.SetTranslation(translation); - + AZ::Transform newLocalTM = localTM; + newLocalTM.SetRotation((rotate * localTM.GetRotation()).GetNormalized()); return newLocalTM; } @@ -512,75 +406,6 @@ namespace AzFramework return m_localTM.GetRotation(); } - void TransformComponent::SetScale(const AZ::Vector3& scale) - { - AZ_Warning("TransformComponent", false, "SetScale is deprecated, please use SetLocalScale"); - - if (!m_worldTM.GetScale().IsClose(scale)) - { - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetScale(scale); - SetWorldTM(newWorldTransform); - } - } - - void TransformComponent::SetScaleX(float scaleX) - { - AZ_Warning("TransformComponent", false, "SetScaleX is deprecated, please use SetLocalScaleX"); - - AZ::Vector3 newScale = m_worldTM.GetScale(); - newScale.SetX(scaleX); - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetScale(newScale); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetScaleY(float scaleY) - { - AZ_Warning("TransformComponent", false, "SetScaleY is deprecated, please use SetLocalScaleY"); - - AZ::Vector3 newScale = m_worldTM.GetScale(); - newScale.SetY(scaleY); - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetScale(newScale); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetScaleZ(float scaleZ) - { - AZ_Warning("TransformComponent", false, "SetScaleZ is deprecated, please use SetLocalScaleZ"); - - AZ::Vector3 newScale = m_worldTM.GetScale(); - newScale.SetZ(scaleZ); - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetScale(newScale); - SetWorldTM(newWorldTransform); - } - - AZ::Vector3 TransformComponent::GetScale() - { - AZ_Warning("TransformComponent", false, "GetScale is deprecated, please use GetLocalScale"); - return m_worldTM.GetScale(); - } - - float TransformComponent::GetScaleX() - { - AZ_Warning("TransformComponent", false, "GetScaleX is deprecated, please use GetLocalScale"); - return m_worldTM.GetScale().GetX(); - } - - float TransformComponent::GetScaleY() - { - AZ_Warning("TransformComponent", false, "GetScaleY is deprecated, please use GetLocalScale"); - return m_worldTM.GetScale().GetY(); - } - - float TransformComponent::GetScaleZ() - { - AZ_Warning("TransformComponent", false, "GetScaleZ is deprecated, please use GetLocalScale"); - return m_worldTM.GetScale().GetZ(); - } - void TransformComponent::SetLocalScale(const AZ::Vector3& scale) { AZ::Transform newLocalTM = m_localTM; @@ -588,41 +413,31 @@ namespace AzFramework SetLocalTM(newLocalTM); } - void TransformComponent::SetLocalScaleX(float scaleX) + AZ::Vector3 TransformComponent::GetLocalScale() { - AZ::Transform newLocalTM = m_localTM; - AZ::Vector3 newScale = newLocalTM.GetScale(); - newScale.SetX(scaleX); - newLocalTM.SetScale(newScale); - SetLocalTM(newLocalTM); + return m_localTM.GetScale(); } - void TransformComponent::SetLocalScaleY(float scaleY) + AZ::Vector3 TransformComponent::GetWorldScale() { - AZ::Transform newLocalTM = m_localTM; - AZ::Vector3 newScale = newLocalTM.GetScale(); - newScale.SetY(scaleY); - newLocalTM.SetScale(newScale); - SetLocalTM(newLocalTM); + return m_worldTM.GetScale(); } - void TransformComponent::SetLocalScaleZ(float scaleZ) + void TransformComponent::SetLocalUniformScale(float scale) { AZ::Transform newLocalTM = m_localTM; - AZ::Vector3 newScale = newLocalTM.GetScale(); - newScale.SetZ(scaleZ); - newLocalTM.SetScale(newScale); + newLocalTM.SetUniformScale(scale); SetLocalTM(newLocalTM); } - AZ::Vector3 TransformComponent::GetLocalScale() + float TransformComponent::GetLocalUniformScale() { - return m_localTM.GetScale(); + return m_localTM.GetUniformScale(); } - AZ::Vector3 TransformComponent::GetWorldScale() + float TransformComponent::GetWorldUniformScale() { - return m_worldTM.GetScale(); + return m_worldTM.GetUniformScale(); } AZStd::vector TransformComponent::GetChildren() @@ -929,45 +744,7 @@ namespace AzFramework ->Event("GetLocalX", &AZ::TransformBus::Events::GetLocalX) ->Event("GetLocalY", &AZ::TransformBus::Events::GetLocalY) ->Event("GetLocalZ", &AZ::TransformBus::Events::GetLocalZ) - ->Event("RotateByX", &AZ::TransformBus::Events::RotateByX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("RotateByY", &AZ::TransformBus::Events::RotateByY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("RotateByZ", &AZ::TransformBus::Events::RotateByZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetEulerRotation", &AZ::TransformBus::Events::SetRotation) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationQuaternion", &AZ::TransformBus::Events::SetRotationQuaternion) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationX", &AZ::TransformBus::Events::SetRotationX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationY", &AZ::TransformBus::Events::SetRotationY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationZ", &AZ::TransformBus::Events::SetRotationZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetEulerRotation", &AZ::TransformBus::Events::GetRotationEulerRadians) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationQuaternion", &AZ::TransformBus::Events::GetRotationQuaternion) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationX", &AZ::TransformBus::Events::GetRotationX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationY", &AZ::TransformBus::Events::GetRotationY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationZ", &AZ::TransformBus::Events::GetRotationZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Event("SetWorldRotationQuaternion", &AZ::TransformBus::Events::SetWorldRotationQuaternion) ->Event("GetWorldRotation", &AZ::TransformBus::Events::GetWorldRotation) ->Event("GetWorldRotationQuaternion", &AZ::TransformBus::Events::GetWorldRotationQuaternion) ->Event("SetLocalRotation", &AZ::TransformBus::Events::SetLocalRotation) @@ -979,34 +756,7 @@ namespace AzFramework ->Event("GetLocalRotationQuaternion", &AZ::TransformBus::Events::GetLocalRotationQuaternion) ->Attribute("Rotation", AZ::Edit::Attributes::PropertyRotation) ->VirtualProperty("Rotation", "GetLocalRotationQuaternion", "SetLocalRotationQuaternion") - ->Event("SetScale", &AZ::TransformBus::Events::SetScale) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetScaleX", &AZ::TransformBus::Events::SetScaleX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetScaleY", &AZ::TransformBus::Events::SetScaleY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetScaleZ", &AZ::TransformBus::Events::SetScaleZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetScale", &AZ::TransformBus::Events::GetScale) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetScaleX", &AZ::TransformBus::Events::GetScaleX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetScaleY", &AZ::TransformBus::Events::GetScaleY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetScaleZ", &AZ::TransformBus::Events::GetScaleZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) ->Event("SetLocalScale", &AZ::TransformBus::Events::SetLocalScale) - ->Event("SetLocalScaleX", &AZ::TransformBus::Events::SetLocalScaleX) - ->Event("SetLocalScaleY", &AZ::TransformBus::Events::SetLocalScaleY) - ->Event("SetLocalScaleZ", &AZ::TransformBus::Events::SetLocalScaleZ) ->Event("GetLocalScale", &AZ::TransformBus::Events::GetLocalScale) ->Attribute("Scale", AZ::Edit::Attributes::PropertyScale) ->VirtualProperty("Scale", "GetLocalScale", "SetLocalScale") diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h index 0dd53d84ed..9009c6bff9 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h @@ -112,22 +112,7 @@ namespace AzFramework float GetLocalZ() override; // Rotation modifiers - void SetRotation(const AZ::Vector3& eulerAnglesRadian) override; - void SetRotationQuaternion(const AZ::Quaternion& quaternion) override; - void SetRotationX(float eulerAngleRadian) override; - void SetRotationY(float eulerAngleRadian) override; - void SetRotationZ(float eulerAngleRadian) override; - - void RotateByX(float eulerAngleRadian) override; - void RotateByY(float eulerAngleRadian) override; - void RotateByZ(float eulerAngleRadian) override; - - AZ::Vector3 GetRotationEulerRadians() override; - AZ::Quaternion GetRotationQuaternion() override; - - float GetRotationX() override; - float GetRotationY() override; - float GetRotationZ() override; + void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override; AZ::Vector3 GetWorldRotation() override; AZ::Quaternion GetWorldRotationQuaternion() override; @@ -143,24 +128,14 @@ namespace AzFramework AZ::Quaternion GetLocalRotationQuaternion() override; // Scale Modifiers - void SetScale(const AZ::Vector3& scale) override; - void SetScaleX(float scaleX) override; - void SetScaleY(float scaleY) override; - void SetScaleZ(float scaleZ) override; - - AZ::Vector3 GetScale() override; - float GetScaleX() override; - float GetScaleY() override; - float GetScaleZ() override; - void SetLocalScale(const AZ::Vector3& scale) override; - void SetLocalScaleX(float scaleX) override; - void SetLocalScaleY(float scaleY) override; - void SetLocalScaleZ(float scaleZ) override; - AZ::Vector3 GetLocalScale() override; AZ::Vector3 GetWorldScale() override; + void SetLocalUniformScale(float scale) override; + float GetLocalUniformScale() override; + float GetWorldUniformScale() override; + // Transform hierarchy AZStd::vector GetChildren() override; AZStd::vector GetAllDescendants() override; diff --git a/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h b/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h index b64b61e22c..04a0572bb9 100644 --- a/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -42,11 +43,15 @@ namespace AzFramework { ViewportId m_drawViewportId = InvalidViewportId; //!< Viewport to draw into AZ::Vector3 m_position; //!< world space position for 3d draws, screen space x,y,depth for 2d. - AZ::Color m_color = AZ::Colors::White; //!< Color to draw the text + AZ::Color m_color = AZ::Colors::White; //!< Color to draw the text + unsigned int m_effectIndex = 0; //!< effect index to apply AZ::Vector2 m_scale = AZ::Vector2(1.0f); //!< font scale - float m_lineSpacing; //!< Spacing between new lines, as a percentage of m_scale. + float m_textSizeFactor = 12.0f; //!< font size in pixels + float m_lineSpacing = 1.0f; //!< Spacing between new lines, as a percentage of m_scale. TextHorizontalAlignment m_hAlign = TextHorizontalAlignment::Left; //!< Horizontal text alignment TextVerticalAlignment m_vAlign = TextVerticalAlignment::Top; //!< Vertical text alignment + bool m_useTransform = false; //!< Use specified transform + AZ::Matrix3x4 m_transform = AZ::Matrix3x4::Identity(); //!< Transform to apply to text quads bool m_monospace = false; //!< disable character proportional spacing bool m_depthTest = false; //!< Test character against the depth buffer bool m_virtual800x600ScreenSize = true; //!< Text placement and size are scaled relative to a virtual 800x600 resolution diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h index 239d93cf32..97c841e8f8 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h @@ -102,7 +102,7 @@ namespace Physics /// Is the ragdoll currently simulated? /// @result True in case the ragdoll is simulated, false if not. - virtual bool IsSimulated() = 0; + virtual bool IsSimulated() const = 0; /// Writes the state for all of the bodies in the ragdoll to the provided output. /// The caller owns the output state and can safely manipulate it without affecting the physics simulation. diff --git a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp index 985bc4665d..2742b90f4c 100644 --- a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp @@ -46,7 +46,6 @@ namespace AzFramework::ProjectManager // Store the Command line to the Setting Registry AZ::SettingsRegistryImpl settingsRegistry; AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine); - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(settingsRegistry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); // Retrieve Command Line from Settings Registry, it may have been updated by the call to FindEngineRoot() // in MergeSettingstoRegistry_ConfigFile @@ -99,51 +98,18 @@ namespace AzFramework::ProjectManager AZ::AllocatorInstance::Create(); } { - const char projectsScript[] = "projects.py"; + AZStd::string filename = "o3de"; + AZ::IO::FixedMaxPath executablePath = AZ::Utils::GetExecutableDirectory(); + executablePath /= filename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION; - AZ_Warning("ProjectManager", false, "No project provided - launching project selector."); - - if (engineRootPath.empty()) + if (!AZ::IO::SystemFile::Exists(executablePath.c_str())) { - AZ_Error("ProjectManager", false, "Couldn't find engine root"); + AZ_Error("ProjectManager", false, "%s not found", executablePath.c_str()); return false; } - auto projectManagerPath = engineRootPath / "scripts" / "project_manager"; - - if (!AZ::IO::SystemFile::Exists((projectManagerPath / projectsScript).c_str())) - { - AZ_Error("ProjectManager", false, "%s not found at %s!", projectsScript, projectManagerPath.c_str()); - return false; - } - AZ::IO::FixedMaxPathString executablePath; - AZ::Utils::GetExecutablePathReturnType result = AZ::Utils::GetExecutablePath(executablePath.data(), executablePath.capacity()); - if (result.m_pathStored != AZ::Utils::ExecutablePathResult::Success) - { - AZ_Error("ProjectManager", false, "Could not determine executable path!"); - return false; - } - AZ::IO::FixedMaxPath parentPath(executablePath.c_str()); - auto exeFolder = parentPath.ParentPath(); - AZStd::fixed_string<8> debugOption; - auto lastSep = exeFolder.Native().find_last_of(AZ_CORRECT_FILESYSTEM_SEPARATOR); - if (lastSep != AZStd::string_view::npos) - { - exeFolder = exeFolder.Native().substr(lastSep + 1); - } - if (exeFolder == "debug") - { - // We need to use the debug version of the python interpreter to load up our debug version of our libraries which work with the debug version of QT living in this folder - debugOption = "debug "; - } - AZ::IO::FixedMaxPath pythonPath = engineRootPath / "python"; - pythonPath /= AZ_TRAIT_AZFRAMEWORK_PYTHON_SHELL; - auto cmdPath = AZ::IO::FixedMaxPathString::format("%s %s%s --executable_path=%s --parent_pid=%" PRIu32, pythonPath.Native().c_str(), - debugOption.c_str(), (projectManagerPath / projectsScript).c_str(), executablePath.c_str(), AZ::Platform::GetCurrentProcessId()); AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - - processLaunchInfo.m_commandlineParameters = cmdPath; - processLaunchInfo.m_showWindow = false; + processLaunchInfo.m_commandlineParameters = executablePath.String(); launchSuccess = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); } if (ownsSystemAllocator) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 5e507b3498..79cea647e4 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -19,10 +19,13 @@ #include #include -namespace AzFramework +namespace AZ { class ReflectContext; +} +namespace AzFramework +{ class Spawnable final : public AZ::Data::AssetData { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp index a528797f63..97169ebeb3 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp @@ -14,6 +14,10 @@ namespace AzFramework { + // + // SpawnableEntityContainerView + // + SpawnableEntityContainerView::SpawnableEntityContainerView(AZ::Entity** begin, size_t length) : m_begin(begin) , m_end(begin + length) @@ -52,6 +56,9 @@ namespace AzFramework } + // + // SpawnableConstEntityContainerView + // SpawnableConstEntityContainerView::SpawnableConstEntityContainerView(AZ::Entity** begin, size_t length) : m_begin(begin) @@ -91,6 +98,136 @@ namespace AzFramework } + // + // SpawnableIndexEntityPair + // + + SpawnableIndexEntityPair::SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator) + : m_entity(entityIterator) + , m_index(indexIterator) + { + } + + AZ::Entity* SpawnableIndexEntityPair::GetEntity() + { + return *m_entity; + } + + const AZ::Entity* SpawnableIndexEntityPair::GetEntity() const + { + return *m_entity; + } + + size_t SpawnableIndexEntityPair::GetIndex() const + { + return *m_index; + } + + // + // SpawnableIndexEntityIterator + // + + SpawnableIndexEntityIterator::SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator) + : m_value(entityIterator, indexIterator) + { + } + + SpawnableIndexEntityIterator& SpawnableIndexEntityIterator::operator++() + { + ++m_value.m_entity; + ++m_value.m_index; + return *this; + } + + SpawnableIndexEntityIterator SpawnableIndexEntityIterator::operator++(int) + { + SpawnableIndexEntityIterator result = *this; + ++m_value.m_entity; + ++m_value.m_index; + return result; + } + + SpawnableIndexEntityIterator& SpawnableIndexEntityIterator::operator--() + { + --m_value.m_entity; + --m_value.m_index; + return *this; + } + + SpawnableIndexEntityIterator SpawnableIndexEntityIterator::operator--(int) + { + SpawnableIndexEntityIterator result = *this; + --m_value.m_entity; + --m_value.m_index; + return result; + } + + bool SpawnableIndexEntityIterator::operator==(const SpawnableIndexEntityIterator& rhs) + { + return m_value.m_entity == rhs.m_value.m_entity && m_value.m_index == rhs.m_value.m_index; + } + + bool SpawnableIndexEntityIterator::operator!=(const SpawnableIndexEntityIterator& rhs) + { + return m_value.m_entity != rhs.m_value.m_entity || m_value.m_index != rhs.m_value.m_index; + } + + SpawnableIndexEntityPair& SpawnableIndexEntityIterator::operator*() + { + return m_value; + } + + const SpawnableIndexEntityPair& SpawnableIndexEntityIterator::operator*() const + { + return m_value; + } + + SpawnableIndexEntityPair* SpawnableIndexEntityIterator::operator->() + { + return &m_value; + } + + const SpawnableIndexEntityPair* SpawnableIndexEntityIterator::operator->() const + { + return &m_value; + } + + + // + // SpawnableConstIndexEntityContainerView + // + + SpawnableConstIndexEntityContainerView::SpawnableConstIndexEntityContainerView( + AZ::Entity** beginEntity, size_t* beginIndices, size_t length) + : m_begin(beginEntity, beginIndices) + , m_end(beginEntity + length, beginIndices + length) + { + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::begin() + { + return m_begin; + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::end() + { + return m_end; + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::cbegin() + { + return m_begin; + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::cend() + { + return m_end; + } + + + // + // EntitySpawnTicket + // EntitySpawnTicket::EntitySpawnTicket(EntitySpawnTicket&& rhs) : m_payload(rhs.m_payload) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index ac66288ff2..69bca8e111 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -58,6 +58,72 @@ namespace AzFramework AZ::Entity** m_end; }; + class SpawnableIndexEntityPair + { + public: + friend class SpawnableIndexEntityIterator; + + AZ::Entity* GetEntity(); + const AZ::Entity* GetEntity() const; + size_t GetIndex() const; + + private: + SpawnableIndexEntityPair() = default; + SpawnableIndexEntityPair(const SpawnableIndexEntityPair&) = default; + SpawnableIndexEntityPair(SpawnableIndexEntityPair&&) = default; + SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator); + + SpawnableIndexEntityPair& operator=(const SpawnableIndexEntityPair&) = default; + SpawnableIndexEntityPair& operator=(SpawnableIndexEntityPair&&) = default; + + AZ::Entity** m_entity { nullptr }; + size_t* m_index { nullptr }; + }; + + class SpawnableIndexEntityIterator + { + public: + // Limited to bidirectional iterator as there's no use case for extending it further, but can be extended if a use case is found. + using iterator_category = AZStd::bidirectional_iterator_tag; + using value_type = SpawnableIndexEntityPair; + using difference_type = size_t; + using pointer = SpawnableIndexEntityPair*; + using reference = SpawnableIndexEntityPair&; + + SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator); + + SpawnableIndexEntityIterator& operator++(); + SpawnableIndexEntityIterator operator++(int); + SpawnableIndexEntityIterator& operator--(); + SpawnableIndexEntityIterator operator--(int); + + bool operator==(const SpawnableIndexEntityIterator& rhs); + bool operator!=(const SpawnableIndexEntityIterator& rhs); + + SpawnableIndexEntityPair& operator*(); + const SpawnableIndexEntityPair& operator*() const; + SpawnableIndexEntityPair* operator->(); + const SpawnableIndexEntityPair* operator->() const; + + private: + SpawnableIndexEntityPair m_value; + }; + + class SpawnableConstIndexEntityContainerView + { + public: + SpawnableConstIndexEntityContainerView(AZ::Entity** beginEntity, size_t* beginIndices, size_t length); + + const SpawnableIndexEntityIterator& begin(); + const SpawnableIndexEntityIterator& end(); + const SpawnableIndexEntityIterator& cbegin(); + const SpawnableIndexEntityIterator& cend(); + + private: + SpawnableIndexEntityIterator m_begin; + SpawnableIndexEntityIterator m_end; + }; + //! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that be used as a template. A ticket can //! be reused for multiple calls on the same spawnable and is safe to use by multiple threads at the same time. Entities created //! from the spawnable may be tracked by the ticket and so using the same ticket is needed to despawn the exact entities created @@ -88,6 +154,7 @@ namespace AzFramework using EntityDespawnCallback = AZStd::function; using ReloadSpawnableCallback = AZStd::function; using ListEntitiesCallback = AZStd::function; + using ListIndicesEntitiesCallback = AZStd::function; using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; @@ -140,6 +207,15 @@ namespace AzFramework //! @param ticket Only the entities associated with this ticket will be listed. //! @param listCallback Required callback that will be called to list the entities on. virtual void ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) = 0; + //! List all entities that are spawned using this ticket with their spawnable index. + //! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity + //! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return + //! both the entities and this index. The index can be used with SpawnEntities to create the same entities again. Note that + //! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be + //! created. + //! @param ticket Only the entities associated with this ticket will be listed. + //! @param listCallback Required callback that will be called to list the entities and indices on. + virtual void ListIndicesAndEntities(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 8045766686..7e20f7b265 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -25,6 +25,8 @@ namespace AzFramework void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); + SpawnAllEntitiesCommand queueEntry; queueEntry.m_ticket = &ticket; queueEntry.m_completionCallback = AZStd::move(completionCallback); @@ -40,6 +42,8 @@ namespace AzFramework EntitySpawnTicket& ticket, AZStd::vector entityIndices, EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); + SpawnEntitiesCommand queueEntry; queueEntry.m_ticket = &ticket; queueEntry.m_entityIndices = AZStd::move(entityIndices); @@ -54,6 +58,8 @@ namespace AzFramework void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); + DespawnAllEntitiesCommand queueEntry; queueEntry.m_ticket = &ticket; queueEntry.m_completionCallback = AZStd::move(completionCallback); @@ -67,6 +73,8 @@ namespace AzFramework void SpawnableEntitiesManager::ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); + ReloadSpawnableCommand queueEntry; queueEntry.m_ticket = &ticket; queueEntry.m_spawnable = AZStd::move(spawnable); @@ -81,6 +89,7 @@ namespace AzFramework void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); ListEntitiesCommand queueEntry; queueEntry.m_ticket = &ticket; @@ -92,9 +101,25 @@ namespace AzFramework } } + void SpawnableEntitiesManager::ListIndicesAndEntities(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback) + { + AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); + + ListIndicesEntitiesCommand queueEntry; + queueEntry.m_ticket = &ticket; + queueEntry.m_listCallback = AZStd::move(listCallback); + { + AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); + queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; + m_pendingRequestQueue.push(AZStd::move(queueEntry)); + } + } + void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) { AZ_Assert(listCallback, "ClaimEntities called on spawnable entities without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to ClaimEntities hasn't been initialized."); ClaimEntitiesCommand queueEntry; queueEntry.m_ticket = &ticket; @@ -109,6 +134,7 @@ namespace AzFramework void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback) { AZ_Assert(completionCallback, "Barrier on spawnable entities called without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to Barrier hasn't been initialized."); BarrierCommand queueEntry; queueEntry.m_ticket = &ticket; @@ -499,6 +525,27 @@ namespace AzFramework } } + bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + { + Ticket& ticket = GetTicketPayload(*request.m_ticket); + if (request.m_ticketId == ticket.m_currentTicketId) + { + AZ_Assert( + ticket.m_spawnedEntities.size() == ticket.m_spawnedEntityIndices.size(), + "Entities and indices on spawnable ticket have gone out of sync."); + request.m_listCallback( + *request.m_ticket, + SpawnableConstIndexEntityContainerView( + ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntityIndices.begin(), ticket.m_spawnedEntities.size())); + ticket.m_currentTicketId++; + return true; + } + else + { + return false; + } + } + bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) { Ticket& ticket = GetTicketPayload(*request.m_ticket); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index e20f58ac76..3481ab180a 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -36,6 +36,7 @@ namespace AzFramework { public: AZ_RTTI(AzFramework::SpawnableEntitiesManager, "{6E14333F-128C-464C-94CA-A63B05A5E51C}"); + AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0); enum class CommandQueueStatus : bool { @@ -58,6 +59,7 @@ namespace AzFramework ReloadSpawnableCallback completionCallback = {}) override; void ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) override; + void ListIndicesAndEntities(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback) override; void ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) override; void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback) override; @@ -123,6 +125,12 @@ namespace AzFramework EntitySpawnTicket* m_ticket; uint32_t m_ticketId; }; + struct ListIndicesEntitiesCommand + { + ListIndicesEntitiesCallback m_listCallback; + EntitySpawnTicket* m_ticket; + uint32_t m_ticketId; + }; struct ClaimEntitiesCommand { ClaimEntitiesCallback m_listCallback; @@ -141,8 +149,9 @@ namespace AzFramework uint32_t m_ticketId; }; - using Requests = AZStd::variant; + using Requests = AZStd::variant< + SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand, ListEntitiesCommand, + ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>; AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext); @@ -155,6 +164,7 @@ namespace AzFramework bool ProcessRequest(DespawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(ListEntitiesCommand& request, AZ::SerializeContext& serializeContext); + bool ProcessRequest(ListIndicesEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(ClaimEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(BarrierCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(DestroyTicketCommand& request, AZ::SerializeContext& serializeContext); diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h index 884562d7e8..28971dc779 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h @@ -41,6 +41,7 @@ namespace AzManipulatorTestFramework AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + float DeviceScalingFactor() override; private: // ViewportInteractionRequestBus ... bool GridSnappingEnabled(); diff --git a/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp b/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp index ebef9dea30..7d32187a74 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp @@ -127,4 +127,9 @@ namespace AzManipulatorTestFramework { return {}; } -} // namespace AzManipulatorTestFramework + + float ViewportInteraction::DeviceScalingFactor() + { + return 1.0f; + } +}// namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzNetworking/CMakeLists.txt b/Code/Framework/AzNetworking/CMakeLists.txt index 0fe95441ce..c6673058d7 100644 --- a/Code/Framework/AzNetworking/CMakeLists.txt +++ b/Code/Framework/AzNetworking/CMakeLists.txt @@ -65,5 +65,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME AZ::AzNetworking.Tests ) + + ly_add_googletest( + NAME AZ::AzNetworking.Tests.Sandbox + TARGET AZ::AzNetworking.Tests + TEST_SUITE sandbox + ) + endif() diff --git a/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp b/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp index eed12e1881..7cc3af5a51 100644 --- a/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp +++ b/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp @@ -129,7 +129,7 @@ namespace UnitTest #if AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS TEST_F(TcpTransportTests, DISABLED_TestSingleClient) #else - TEST_F(TcpTransportTests, TestSingleClient) + TEST_F(TcpTransportTests, SUITE_sandbox_TestSingleClient) #endif // AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS { TestTcpServer testServer; @@ -157,7 +157,7 @@ namespace UnitTest #if AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS TEST_F(TcpTransportTests, DISABLED_TestMultipleClients) #else - TEST_F(TcpTransportTests, TestMultipleClients) + TEST_F(TcpTransportTests, SUITE_sandbox_TestMultipleClients) #endif // AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS { constexpr uint32_t NumTestClients = 50; diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Local.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Local.svg new file mode 100644 index 0000000000..2017cabe21 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Local.svg @@ -0,0 +1,8 @@ + + + Icon / Local + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Parent.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Parent.svg new file mode 100644 index 0000000000..c0b9580985 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Parent.svg @@ -0,0 +1,8 @@ + + + Icon / Parent + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/World.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/World.svg new file mode 100644 index 0000000000..4d77775e3d --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/World.svg @@ -0,0 +1,8 @@ + + + Icon / World + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc index 8ea4755a24..00fa95d094 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc @@ -354,6 +354,7 @@ img/UI20/toolbar/Grid.svg img/UI20/toolbar/Lighting.svg img/UI20/toolbar/Load.svg + img/UI20/toolbar/Local.svg img/UI20/toolbar/Locked.svg img/UI20/toolbar/LUA.svg img/UI20/toolbar/Material.svg @@ -362,6 +363,7 @@ img/UI20/toolbar/Object_follow_terrain.svg img/UI20/toolbar/Object_height.svg img/UI20/toolbar/Object_list.svg + img/UI20/toolbar/Parent.svg img/UI20/toolbar/particle.svg img/UI20/toolbar/Play.svg img/UI20/toolbar/Redo.svg @@ -380,6 +382,7 @@ img/UI20/toolbar/undo.svg img/UI20/toolbar/Unlocked.svg img/UI20/toolbar/Vertex_snapping.svg + img/UI20/toolbar/World.svg img/UI20/toolbar/X_axis.svg img/UI20/toolbar/Y_axis.svg img/UI20/toolbar/Z_axis.svg diff --git a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h index 0090ce066b..855b4fe416 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h +++ b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h @@ -39,4 +39,3 @@ #define AZ_TRAIT_DISABLE_FAILED_EMOTION_FX_EDITOR_TESTS true #define AZ_TRAIT_DISABLE_FAILED_METRICS_TESTS true -#define AZ_TRAIT_DISABLE_ASSET_JOB_PARALLEL_TESTS true diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h index 98e4c6b5eb..715de30bfa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h @@ -60,10 +60,20 @@ namespace AzToolsFramework //! and is generally checked into source control. virtual const char* GetAbsoluteDevRootFolderPath() = 0; - /// Convert a full source path like "c:\\dev\gamename\\blah\\test.tga" into a relative product path. + /// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path. /// asset paths never mention their alias and are relative to the asset cache root virtual bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) = 0; + /** Convert a source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative source path, like "blah/test.tga". + * If no valid relative path could be created, the input source path will be returned in relativePath. + * @param sourcePath partial or full path to a source file. (The file doesn't need to exist) + * @param relativePath the output relative path for the source file, if a valid one could be created + * @param rootFilePath the root path that relativePath is relative to + * @return true if a valid relative path was created, false if it wasn't + */ + virtual bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFilePath) = 0; + /// Convert a relative asset path like "blah/test.tga" to a full source path path. /// Once the asset processor has finished building, this function is capable of handling even when the extension changes /// or when the source is in a different folder or in a different location (such as inside gems) @@ -110,14 +120,14 @@ namespace AzToolsFramework /** * Query to see if a specific asset platform is enabled - * @param platform the asset platform to check e.g. es3, ios, etc. + * @param platform the asset platform to check e.g. android, ios, etc. * @return true if enabled, false otherwise */ virtual bool IsAssetPlatformEnabled(const char* platform) = 0; /** * Get the total number of pending assets left to process for a specific asset platform - * @param platform the asset platform to check e.g. es3, ios, etc. + * @param platform the asset platform to check e.g. android, ios, etc. * @return -1 if the process fails, a positive number otherwise */ virtual int GetPendingAssetsForPlatform(const char* platform) = 0; @@ -302,7 +312,7 @@ namespace AzToolsFramework inline const char* GetHostAssetPlatform() { #if defined(AZ_PLATFORM_MAC) - return "osx_gl"; + return "mac"; #elif defined(AZ_PLATFORM_WINDOWS) return "pc"; #elif defined(AZ_PLATFORM_LINUX) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h new file mode 100644 index 0000000000..a2264b5058 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h @@ -0,0 +1,80 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include + +#include + +namespace AzToolsFramework +{ + //! An interface for loading simple icon assets and rendering them to screen on a per-viewport basis. + class EditorViewportIconDisplayInterface + { + public: + AZ_RTTI(EditorViewportIconDisplayInterface, "{D5190B58-2561-4F3F-B793-F1E7D454CDF2}"); + + using IconId = AZ::s32; + static constexpr IconId InvalidIconId = -1; + + enum class CoordinateSpace : AZ::u8 + { + ScreenSpace, + WorldSpace + }; + + //! These draw parameters control rendering for a single icon to a single viewport. + struct DrawParameters + { + //! The ViewportId to render to. + AzFramework::ViewportId m_viewport = AzFramework::InvalidViewportId; + //! The icon ID, retrieved from GetOrLoadIconForPath, to render to screen. + IconId m_icon = InvalidIconId; + //! The color, including opacity, to render the icon with. White will render the icon as opaque in its original color. + AZ::Color m_color = AZ::Colors::White; + //! The position to render the icon to, in world or screen space depending on m_positionSpace. + AZ::Vector3 m_position; + //! The coordinate system to use for m_position. + //! ScreenSpace will accept m_position in the form of [X, Y, Depth], where X & Y are screen coordinates in + //! pixels and Depth is a z-ordering depth value from 0.0f to 1.0f. + //! WorldSpace will accept a 3D vector in world space coordinates that will be translated back into screen + //! space when the icon is rendered. + CoordinateSpace m_positionSpace = CoordinateSpace::ScreenSpace; + //! The size to render the icon as, in pixels. + AZ::Vector2 m_size; + }; + + //! The current load status of an icon retrieved by GetOrLoadIconForPath. + enum class IconLoadStatus : AZ::u8 + { + Unloaded, + Loading, + Loaded, + Error + }; + + //! Draws an icon to a viewport given a set of draw parameters. + //! Requires an IconId retrieved from GetOrLoadIconForPath. + virtual void DrawIcon(const DrawParameters& drawParameters) = 0; + //! Retrieves a reusable IconId for an icon at a given path. + //! This will load the icon, if it has not already been loaded. + //! @param path should be a relative asset path to an icon image asset. + //! png and svg icons are currently supported. + virtual IconId GetOrLoadIconForPath(AZStd::string_view path) = 0; + //! Gets the current load status of an icon retrieved via GetOrLoadIconForPath. + virtual IconLoadStatus GetIconLoadStatus(IconId icon) = 0; + }; + + using EditorViewportIconDisplay = AZ::Interface; +} //namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index 82fa3f94f5..72150b1b57 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -239,6 +239,11 @@ namespace AzToolsFramework */ virtual int RemoveDirtyEntity(AZ::EntityId target) = 0; + /*! + * Clears the dirty entity set. + */ + virtual void ClearDirtyEntities() = 0; + /*! * \return true if an undo/redo operation is in progress. */ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index e77704c920..88057787bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -1354,6 +1354,11 @@ namespace AzToolsFramework return static_cast(m_dirtyEntities.erase(entityId)); } + void ToolsApplication::ClearDirtyEntities() + { + m_dirtyEntities.clear(); + } + void ToolsApplication::UndoPressed() { if (m_undoStack) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h index 6c836ac888..bafced67bd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h @@ -85,6 +85,7 @@ namespace AzToolsFramework void AddDirtyEntity(AZ::EntityId entityId) override; int RemoveDirtyEntity(AZ::EntityId entityId) override; + void ClearDirtyEntities() override; bool IsDuringUndoRedo() override { return m_isDuringUndoRedo; } void UndoPressed() override; void RedoPressed() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp index 5529829913..4966d9cce9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp @@ -265,6 +265,30 @@ namespace AzToolsFramework return response.m_resolved; } + bool AssetSystemComponent::GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFilePath) + { + AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance(); + if (!engineConnection || !engineConnection->IsConnected()) + { + relativePath = sourcePath; + return false; + } + + AzFramework::AssetSystem::GenerateRelativeSourcePathRequest request(sourcePath); + AzFramework::AssetSystem::GenerateRelativeSourcePathResponse response; + if (!SendRequest(request, response)) + { + AZ_Error("Editor", false, "Failed to send GenerateRelativeSourcePath request for %s", sourcePath.c_str()); + relativePath = sourcePath; + return false; + } + + relativePath = response.m_relativeSourcePath; + rootFilePath = response.m_rootFolder; + return response.m_resolved; + } + bool AssetSystemComponent::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath) { auto foundIt = m_assetSourceRelativePathToFullPathCache.find(relPath); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h index 399ee1ac9d..9d839c60f5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h @@ -63,6 +63,8 @@ namespace AzToolsFramework const char* GetAbsoluteDevGameFolderPath() override; const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override; + bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override; bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath) override; bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override; bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp index 9ab50a803c..92711f45b2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.cpp @@ -81,9 +81,20 @@ namespace AzToolsFramework m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_" + name); + bool selectedAsset = false; + for (auto& assetId : selection.GetSelectedAssetIds()) { - m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId); + if (assetId.IsValid()) + { + selectedAsset = true; + m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId); + } + } + + if (!selectedAsset) + { + m_ui->m_assetBrowserTreeViewWidget->SelectFolder(selection.GetDefaultDirectory()); } setWindowTitle(tr("Pick %1").arg(m_selection.GetTitle())); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.cpp index 65f361dc83..83734a24c3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.cpp @@ -93,6 +93,16 @@ namespace AzToolsFramework m_selectedAssetIds.push_back(selectedAssetId); } + void AssetSelectionModel::SetDefaultDirectory(AZStd::string_view defaultDirectory) + { + m_defaultDirectory = defaultDirectory; + } + + AZStd::string_view AssetSelectionModel::GetDefaultDirectory() const + { + return m_defaultDirectory; + } + AZStd::vector& AssetSelectionModel::GetResults() { return m_results; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.h index 59cc9d05e2..5e9d23602a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetSelectionModel.h @@ -47,6 +47,9 @@ namespace AzToolsFramework const AZStd::vector& GetSelectedAssetIds() const; void SetSelectedAssetIds(const AZStd::vector& selectedAssetIds); void SetSelectedAssetId(const AZ::Data::AssetId& selectedAssetId); + + void SetDefaultDirectory(AZStd::string_view defaultDirectory); + AZStd::string_view GetDefaultDirectory() const; AZStd::vector& GetResults(); const AssetBrowserEntry* GetResult(); @@ -72,6 +75,7 @@ namespace AzToolsFramework AZStd::vector m_selectedAssetIds; AZStd::vector m_results; + AZStd::string m_defaultDirectory; QString m_title; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index eeee433835..6cd60b0015 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -270,7 +271,20 @@ namespace AzToolsFramework return false; } - bool AssetBrowserTreeView::SelectEntry(const QModelIndex& idxParent, const AZStd::vector& entries, const uint32_t entryPathIndex) + void AssetBrowserTreeView::SelectFolder(AZStd::string_view folderPath) + { + if (folderPath.size() == 0) + { + return; + } + + AZStd::vector entries; + AZ::StringFunc::Tokenize(folderPath, entries, "/"); + + SelectEntry(QModelIndex(), entries, 0, true); + } + + bool AssetBrowserTreeView::SelectEntry(const QModelIndex& idxParent, const AZStd::vector& entries, const uint32_t entryPathIndex, bool useDisplayName) { if (entries.empty()) { @@ -285,30 +299,43 @@ namespace AzToolsFramework auto rowIdx = model()->index(idx, 0, idxParent); auto rowEntry = GetEntryFromIndex(rowIdx); - // Check if this entry name matches the query - if (rowEntry && AzFramework::StringFunc::Equal(entry.c_str(), rowEntry->GetName().c_str(), true)) + if (rowEntry) { - // Final entry found - set it as the selected element - if (entryPathIndex == entries.size() - 1) - { - selectionModel()->clear(); - selectionModel()->select(rowIdx, QItemSelectionModel::Select); - setCurrentIndex(rowIdx); - return true; - } + // Check if this entry name matches the query + AZStd::string_view compareName = useDisplayName ? (const char*)(rowEntry->GetDisplayName().toUtf8()) : rowEntry->GetName().c_str(); - // If this isn't the final entry, it needs to be a folder for the path to be valid (otherwise, early out) - if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder) + if (AzFramework::StringFunc::Equal(entry.c_str(), compareName, true)) { - // Folder found - if the final entry is found, expand this folder so the final entry is viewable in the Asset Browser (otherwise, early out) - if (SelectEntry(rowIdx, entries, entryPathIndex + 1)) + // Final entry found - set it as the selected element + if (entryPathIndex == entries.size() - 1) { - expand(rowIdx); + if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder) + { + // Expand the item itself if it is a folder + expand(rowIdx); + } + + selectionModel()->clear(); + selectionModel()->select(rowIdx, QItemSelectionModel::Select); + setCurrentIndex(rowIdx); + return true; } + + // If this isn't the final entry, it needs to be a folder for the path to be valid (otherwise, early out) + if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder) + { + // Folder found - if the final entry is found, expand this folder so the final entry is viewable in the Asset + // Browser (otherwise, early out) + if (SelectEntry(rowIdx, entries, entryPathIndex + 1, useDisplayName)) + { + expand(rowIdx); + return true; + } + } + + return false; } - - return false; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h index 697396b09e..19cbd3745a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h @@ -60,6 +60,8 @@ namespace AzToolsFramework AZStd::vector GetSelectedAssets() const; + void SelectFolder(AZStd::string_view folderPath); + ////////////////////////////////////////////////////////////////////////// // AssetBrowserViewRequestBus void SelectProduct(AZ::Data::AssetId assetID) override; @@ -67,6 +69,7 @@ namespace AzToolsFramework void ClearFilter() override; void Update() override; + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -105,7 +108,7 @@ namespace AzToolsFramework QString m_name; bool SelectProduct(const QModelIndex& idxParent, AZ::Data::AssetId assetID); - bool SelectEntry(const QModelIndex& idxParent, const AZStd::vector& entryPathTokens, const uint32_t entryPathIndex = 0); + bool SelectEntry(const QModelIndex& idxParent, const AZStd::vector& entryPathTokens, const uint32_t entryPathIndex = 0, bool useDisplayName = false); //! Grab one entry from the source thumbnail list and update it void UpdateSCThumbnails(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index 8412361657..32ea9db3da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -56,5 +56,7 @@ namespace AzToolsFramework virtual void StartPlayInEditor() = 0; virtual void StopPlayInEditor() = 0; + + virtual void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) = 0; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index da529d9349..439789f11b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -14,9 +14,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -222,12 +224,11 @@ namespace AzToolsFramework AzToolsFramework::Prefab::TemplateId templateId = m_prefabSystemComponent->GetTemplateIdFromFilePath(relativePath); m_rootInstance->SetTemplateSourcePath(relativePath); + if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) { - // This has not been loaded yet, this is the case of being saved with a different name. - // Create it m_rootInstance->m_containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent()); - HandleEntitiesAdded({m_rootInstance->m_containerEntity.get()}); + HandleEntitiesAdded({ m_rootInstance->m_containerEntity.get() }); AzToolsFramework::Prefab::PrefabDom dom; bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom); @@ -236,7 +237,8 @@ namespace AzToolsFramework AZ_Error("Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename)); return false; } - templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(dom)); + templateId = m_prefabSystemComponent->AddTemplate(relativePath, AZStd::move(dom)); + if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) { AZ_Error("Prefab", false, "Couldn't add new template id '%i' when saving file '%.*s'", templateId, AZ_STRING_ARG(filename)); @@ -263,6 +265,71 @@ namespace AzToolsFramework return false; } + void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) + { + AZ::IO::Path relativePath = m_loaderInterface->GetRelativePathToProject(filename); + AzToolsFramework::Prefab::TemplateId templateId = m_prefabSystemComponent->GetTemplateIdFromFilePath(relativePath); + + m_rootInstance->SetTemplateSourcePath(relativePath); + + AZStd::string watchFolder; + AZ::Data::AssetInfo assetInfo; + bool sourceInfoFound = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, templateFilename.c_str(), + assetInfo, watchFolder); + + if (sourceInfoFound) + { + AZStd::string fullPath; + AZ::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), fullPath); + + // Get the default prefab and copy the Dom over to the new template being saved + Prefab::TemplateId defaultId = m_loaderInterface->LoadTemplateFromFile(fullPath.c_str()); + Prefab::PrefabDom& dom = m_prefabSystemComponent->FindTemplateDom(defaultId); + + Prefab::PrefabDom levelDefaultDom; + levelDefaultDom.CopyFrom(dom, levelDefaultDom.GetAllocator()); + + Prefab::PrefabDomPath sourcePath("/Source"); + sourcePath.Set(levelDefaultDom, relativePath.c_str()); + + templateId = m_prefabSystemComponent->AddTemplate(relativePath, AZStd::move(levelDefaultDom)); + } + else + { + m_rootInstance->m_containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent()); + HandleEntitiesAdded({ m_rootInstance->m_containerEntity.get() }); + + AzToolsFramework::Prefab::PrefabDom dom; + bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom); + if (!success) + { + AZ_Error( + "Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename)); + return; + } + templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(dom)); + } + + if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) + { + AZ_Error("Prefab", false, "Couldn't create new template id '%i' when creating new level '%.*s'", templateId, AZ_STRING_ARG(filename)); + return; + } + + Prefab::TemplateId prevTemplateId = m_rootInstance->GetTemplateId(); + m_rootInstance->SetTemplateId(templateId); + + if (prevTemplateId != Prefab::InvalidTemplateId && templateId != prevTemplateId) + { + // Make sure we only have one level template loaded at a time + m_prefabSystemComponent->RemoveTemplate(prevTemplateId); + } + + m_prefabSystemComponent->PropagateTemplateChanges(templateId); + } + Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::CreatePrefab( const AZStd::vector& entities, AZStd::vector>&& nestedPrefabInstances, AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h index 3be9b95df0..d8eb81dd40 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h @@ -170,6 +170,8 @@ namespace AzToolsFramework void StartPlayInEditor() override; void StopPlayInEditor() override; + void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) override; + protected: AZ::SliceComponent::SliceInstanceAddress GetOwningSlice() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp index 906ea98357..14dcf5e55d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp @@ -614,7 +614,7 @@ namespace AzToolsFramework AZ::Quaternion oldEntityRotation; AZ::TransformBus::EventResult(oldEntityRotation, id, &AZ::TransformBus::Events::GetWorldRotationQuaternion); - transformComponent->SetRotationQuaternion(oldEntityRotation); + transformComponent->SetWorldRotationQuaternion(oldEntityRotation); // Ensure the existing hierarchy is maintained AZ::EntityId oldParentEntityId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp index a8fe0e55bd..8874e0dcd9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp @@ -31,7 +31,7 @@ namespace AzToolsFramework rayProportion, lineSegmentProportion, worldClosestPositionRay, worldClosestPositionLineSegment); AZ::Transform worldFromLocalNormalized = worldFromLocal; - const AZ::Vector3 scale = worldFromLocalNormalized.ExtractScale() * nonUniformScale; + const AZ::Vector3 scale = worldFromLocalNormalized.ExtractUniformScale() * nonUniformScale; const AZ::Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse(); return { (localFromWorldNormalized.TransformPoint(worldClosestPositionLineSegment)) / scale }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp index 87d966fe84..aa84fc5752 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp @@ -59,7 +59,7 @@ namespace AzToolsFramework ? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip) : AZ::Vector3::CreateZero(); - const AZ::Vector3 localScale = localTransform.GetScale(); + const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale()); const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform); // calculate scale amount to snap, to align to round scale value const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index db0baa1479..e6c70079df 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -113,7 +113,7 @@ namespace AzToolsFramework /// noise in the value returned when dealing with values far from the origin. inline float ScaleReciprocal(const AZ::Transform& transform) { - return Round3(transform.GetScale().GetReciprocal().GetMinElement()); + return Round3(1.0f / transform.GetUniformScale()); } /// Find the reciprocal of the non-uniform scale. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp index fba7e35078..cd08a95af7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp @@ -39,7 +39,7 @@ namespace AzToolsFramework AZ::Transform result; result.SetRotation(m_space.GetRotation() * localTransform.GetRotation()); result.SetTranslation(m_space.TransformPoint(m_nonUniformScale * localTransform.GetTranslation())); - result.SetScale(m_space.GetScale() * localTransform.GetScale()); + result.SetScale(m_space.GetScale() * localTransform.GetUniformScale()); return result; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp index 3af09f644d..caeedd834f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ScaleManipulators.cpp @@ -82,9 +82,7 @@ namespace AzToolsFramework m_uniformScaleManipulator->SetVisualOrientationOverride( QuaternionFromTransformNoScaling(localTransform)); - m_uniformScaleManipulator->SetLocalTransform( - AZ::Transform::CreateTranslation(localTransform.GetTranslation()) * - AZ::Transform::CreateScale(localTransform.GetScale())); + m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity()); } void ScaleManipulators::SetLocalPositionImpl(const AZ::Vector3& localPosition) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Maths/TransformUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Maths/TransformUtils.h index 247be33839..a3cc12566f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Maths/TransformUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Maths/TransformUtils.h @@ -23,7 +23,7 @@ namespace AzToolsFramework inline AZ::Transform TransformNormalizedScale(const AZ::Transform& transform) { AZ::Transform transformNormalizedScale = transform; - transformNormalizedScale.SetScale(AZ::Vector3::CreateOne()); + transformNormalizedScale.SetUniformScale(1.0f); return transformNormalizedScale; } @@ -33,8 +33,7 @@ namespace AzToolsFramework inline AZ::Transform TransformUniformScale(const AZ::Transform& transform) { AZ::Transform transformUniformScale = transform; - const float maxScale = transformUniformScale.GetScale().GetMaxElement(); - transformUniformScale.SetScale(AZ::Vector3(maxScale)); + transformUniformScale.SetUniformScale(transformUniformScale.GetUniformScale()); return transformUniformScale; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp index a21c5301aa..6d3ddedd51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp @@ -276,18 +276,14 @@ namespace AzToolsFramework PrefabDomValueReference linkPatchesReference = PrefabDomUtils::FindPrefabDomValue(linkDom, PrefabDomUtils::PatchesName); - // This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them. - if (!linkPatchesReference.has_value()) - { - /* - If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the - linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to - associate them with the linkDom's allocator. - */ - PrefabDom patchesCopy; - patchesCopy.CopyFrom(patches, linkDom.GetAllocator()); - linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator()); - } + /* + If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the + linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to + associate them with the linkDom's allocator. + */ + PrefabDom patchesCopy; + patchesCopy.CopyFrom(patches, linkDom.GetAllocator()); + linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator()); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp index 308749ab28..2fb22ea8e8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp @@ -234,5 +234,10 @@ namespace AzToolsFramework } } + PrefabDomValueReference Link::GetLinkPatches() + { + return PrefabDomUtils::FindPrefabDomValue(m_linkDom, PrefabDomUtils::PatchesName); + } + } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h index 073e619f20..c8f43b291e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h @@ -79,6 +79,8 @@ namespace AzToolsFramework */ void AddLinkIdToInstanceDom(PrefabDomValue& instanceDomValue); + PrefabDomValueReference GetLinkPatches(); + private: /** diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index 7b4761c39a..2965148172 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -41,7 +41,7 @@ namespace AzToolsFramework [[maybe_unused]] bool result = settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); - AZ_Assert(result, "Couldn't retrieve project root path"); + AZ_Warning("Prefab", result, "Couldn't retrieve project root path"); m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred(); AZ::Interface::Register(this); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 579f465eb2..0181050a32 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -10,8 +10,6 @@ * */ -#include - #include #include #include @@ -28,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -98,9 +97,13 @@ namespace AzToolsFramework AZStd::string("Could not create a new prefab out of the entities provided - invalid selection.")); } + AZStd::unordered_map oldEntityAliases; + // Detach the retrieved entities for (AZ::Entity* entity : entities) { + AZ::EntityId entityId = entity->GetId(); + oldEntityAliases.emplace(entityId, commonRootEntityOwningInstance->get().GetEntityAlias(entityId)->get()); commonRootEntityOwningInstance->get().DetachEntity(entity->GetId()).release(); } @@ -110,15 +113,18 @@ namespace AzToolsFramework { AZStd::unique_ptr outInstance = commonRootEntityOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias()); - auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId()); + LinkId detachingInstanceLinkId = nestedInstance->GetLinkId(); + auto linkRef = m_prefabSystemComponentInterface->FindLink(detachingInstanceLinkId); + AZ_Assert(linkRef.has_value(), "Unable to find link with id '%llu' during prefab creation.", detachingInstanceLinkId); - if (linkRef.has_value()) - { - PrefabDom oldLinkPatches; - oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator()); + PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches(); + AZ_Assert( + linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.", + detachingInstanceLinkId); - nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(oldLinkPatches)); - } + PrefabDom linkPatchesCopy; + linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator()); + nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(linkPatchesCopy)); RemoveLink(outInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); @@ -182,6 +188,24 @@ namespace AzToolsFramework if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get())) { previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + previousPatch.Accept(writer); + QString previousPatchString(buffer.GetString()); + + for (AZ::Entity* entity : entities) + { + AZ::EntityId entityId = entity->GetId(); + AZStd::string oldEntityAlias = oldEntityAliases[entityId]; + EntityAliasOptionalReference newEntityAlias = instanceToCreate->get().GetEntityAlias(entityId); + AZ_Assert( + newEntityAlias.has_value(), + "Could not fetch entity alias for entity with id '%llu' during prefab creation.", + static_cast(entityId)); + ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get()); + } + + previousPatch.Parse(previousPatchString.toUtf8().constData()); } // These link creations shouldn't be undone because that would put the template in a non-usable state if a user @@ -203,36 +227,23 @@ namespace AzToolsFramework m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter); m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId); - // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step - m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter)); - - // Save these changes as patches to the link - PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); - linkUpdate->SetParent(undoBatch.GetUndoBatch()); - linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId()); - - linkUpdate->Redo(); + // We won't parent this undo node to the undo batch so that the newly created template and link will remain + // unaffected by undo actions. This is needed so that any future instantiations of the template will work. + PrefabUndoLinkUpdate linkUpdate = PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); + linkUpdate.Capture(reparentPatch, nestedInstance->GetLinkId()); + linkUpdate.Redo(); } }); - + // Create a link between the templates of the newly created instance and the instance it's being parented under. CreateLink( instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch)); - for (AZ::Entity* topLevelEntity : topLevelEntities) - { - AZ::EntityId topLevelEntityId = topLevelEntity->GetId(); - if (topLevelEntityId.IsValid()) - { - m_prefabUndoCache.UpdateCache(topLevelEntity->GetId()); - - // Parenting entities would mark entities as dirty. But we want to unmark the top level entities as dirty because - // if we don't, the template created would be updated and cause issues with undo operation followed by instantiation. - ToolsApplicationRequests::Bus::Broadcast( - &ToolsApplicationRequests::Bus::Events::RemoveDirtyEntity, topLevelEntity->GetId()); - } - } + // This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab. + // We are doing this so that the changes in those enities are not queued up twice for propagation. + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities); // Select Container Entity { @@ -824,15 +835,7 @@ namespace AzToolsFramework // This will cover both cases where an alias could be used in a normal entity vs. an instance for (auto aliasMapIter : oldAliasToNewAliasMap) { - QString oldAliasQuotes = QString("\"%1\"").arg(aliasMapIter.first.c_str()); - QString newAliasQuotes = QString("\"%1\"").arg(aliasMapIter.second.c_str()); - - newEntityDomString.replace(oldAliasQuotes, newAliasQuotes); - - QString oldAliasPathRef = QString("/%1").arg(aliasMapIter.first.c_str()); - QString newAliasPathRef = QString("/%1").arg(aliasMapIter.second.c_str()); - - newEntityDomString.replace(oldAliasPathRef, newAliasPathRef); + ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second); } // Create the new Entity DOM from parsing the JSON string @@ -1233,5 +1236,18 @@ namespace AzToolsFramework return true; } + + void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias) + { + QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data()); + QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data()); + + stringToReplace.replace(oldAliasQuotes, newAliasQuotes); + + QString oldAliasPathRef = QString("/%1").arg(oldAlias.data()); + QString newAliasPathRef = QString("/%1").arg(newAlias.data()); + + stringToReplace.replace(oldAliasPathRef, newAliasPathRef); + } } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 223a725c6c..7e2357dd44 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -14,12 +14,15 @@ #include #include +#include #include #include #include #include +class QString; + namespace AzToolsFramework { using EntityList = AZStd::vector; @@ -27,7 +30,6 @@ namespace AzToolsFramework namespace Prefab { class Instance; - class InstanceEntityMapperInterface; class InstanceToTemplateInterface; class PrefabLoaderInterface; @@ -130,6 +132,8 @@ namespace AzToolsFramework bool IsCyclicalDependencyFound( InstanceOptionalConstReference instance, const AZStd::unordered_set& templateSourcePaths); + void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias); + static Instance* GetParentInstance(Instance* instance); static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant); static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp index 4b73166b3d..38a6156ec5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceUtilities.cpp @@ -1475,10 +1475,8 @@ namespace AzToolsFramework // to avoid pushing them to the slice. // Only scale is preserved on the root entity of a slice. transformComponent->SetParent(AZ::EntityId()); - AZ::Vector3 scale = transformComponent->GetLocalScale(); transformComponent->SetWorldTranslation(AZ::Vector3::CreateZero()); transformComponent->SetLocalRotation(AZ::Vector3::CreateZero()); - transformComponent->SetLocalScale(scale); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h index 6950717499..0ca5853adc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once -#include -#include -#include #include #include +#include +#include +#include namespace AzToolsFramework { @@ -31,7 +31,7 @@ namespace AzToolsFramework To use the EditorComponentAdapter, 3 classes are required: - a class that implements the functions required for TController (see below) - a configuration struct/class which extends AZ::ComponentConfig - - A runtime component that will be generated by the editor comoinent on export + - A runtime component that will be generated by the editor component on export The concrete component extends the adapter and implements behavior which is unique to the component. @@ -64,15 +64,15 @@ namespace AzToolsFramework the EditContext. TController can friend itself to the editor component to make this work if required. */ template - class EditorComponentAdapter - : public EditorComponentBase + class EditorComponentAdapter : public EditorComponentBase { public: - - AZ_RTTI((EditorComponentAdapter, "{2F5A3669-FFE9-4CD7-B9E2-7FC8100CF1A2}", TController, TRuntimeComponent, TConfiguration), EditorComponentBase); + AZ_RTTI( + (EditorComponentAdapter, "{2F5A3669-FFE9-4CD7-B9E2-7FC8100CF1A2}", TController, TRuntimeComponent, TConfiguration), + EditorComponentBase); EditorComponentAdapter() = default; - EditorComponentAdapter(const TConfiguration& configuration); + explicit EditorComponentAdapter(const TConfiguration& configuration); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); @@ -86,7 +86,6 @@ namespace AzToolsFramework void BuildGameEntity(AZ::Entity* gameEntity) override; protected: - static void Reflect(AZ::ReflectContext* context); // AZ::Component overrides ... diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl index 04619b079d..b8bd24589a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include @@ -28,23 +28,21 @@ namespace AzToolsFramework template void EditorComponentAdapter::Reflect(AZ::ReflectContext* context) { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(1) - ->Field("Controller", &EditorComponentAdapter::m_controller) - ; + serializeContext->Class()->Version(1)->Field( + "Controller", &EditorComponentAdapter::m_controller); if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class( - "EditorComponentAdapter", "") + // clang-format off + editContext->Class("EditorComponentAdapter", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorComponentAdapter::m_controller, "Controller", "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorComponentAdapter::OnConfigurationChanged) - ; + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorComponentAdapter::OnConfigurationChanged); + // clang-format on } } } @@ -53,27 +51,35 @@ namespace AzToolsFramework // Get*Services functions template - void EditorComponentAdapter::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetProvidedServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetProvidedServicesHelper(services, typename AZ::HasComponentProvidedServices::type()); + AzFramework::Components::GetProvidedServicesHelper( + services, typename AZ::HasComponentProvidedServices::type()); } template - void EditorComponentAdapter::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetRequiredServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetRequiredServicesHelper(services, typename AZ::HasComponentRequiredServices::type()); + AzFramework::Components::GetRequiredServicesHelper( + services, typename AZ::HasComponentRequiredServices::type()); } template - void EditorComponentAdapter::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetIncompatibleServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetIncompatibleServicesHelper(services, typename AZ::HasComponentIncompatibleServices::type()); + AzFramework::Components::GetIncompatibleServicesHelper( + services, typename AZ::HasComponentIncompatibleServices::type()); } template - void EditorComponentAdapter::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetDependentServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetDependentServicesHelper(services, typename AZ::HasComponentDependentServices::type()); + AzFramework::Components::GetDependentServicesHelper( + services, typename AZ::HasComponentDependentServices::type()); } ////////////////////////////////////////////////////////////////////////// @@ -99,7 +105,8 @@ namespace AzToolsFramework if (ShouldActivateController()) { - m_controller.Activate(GetEntityId()); + AzFramework::Components::ComponentActivateHelper::Activate( + m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId())); } } @@ -122,7 +129,8 @@ namespace AzToolsFramework } template - bool EditorComponentAdapter::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + bool EditorComponentAdapter::WriteOutConfig( + AZ::ComponentConfig* outBaseConfig) const { if (auto config = azrtti_cast(outBaseConfig)) { @@ -139,7 +147,8 @@ namespace AzToolsFramework if (ShouldActivateController()) { - m_controller.Activate(GetEntityId()); + AzFramework::Components::ComponentActivateHelper::Activate( + m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId())); } return AZ::Edit::PropertyRefreshLevels::None; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp index 48a7a37487..c46bb158c6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -313,8 +314,7 @@ namespace AzToolsFramework // if we do not yet have a valid texture id, request it using the entity icon path if (m_entityIconTextureId == 0) { - EditorRequestBus::BroadcastResult( - m_entityIconTextureId, &EditorRequests::GetIconTextureIdFromEntityIconPath, m_entityIconPath); + m_entityIconTextureId = EditorViewportIconDisplay::Get()->GetOrLoadIconForPath(m_entityIconPath); } return m_entityIconTextureId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index 519b6d0b26..285d962b46 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -357,7 +357,7 @@ namespace AzToolsFramework AZ::Transform TransformComponent::GetLocalScaleTM() const { - return AZ::Transform::CreateScale(m_editorTransform.m_scale); + return AZ::Transform::CreateUniformScale(m_editorTransform.m_scale.GetMaxElement()); } const AZ::Transform& TransformComponent::GetLocalTM() @@ -520,91 +520,13 @@ namespace AzToolsFramework return m_editorTransform.m_translate.GetZ(); } - void TransformComponent::SetRotation(const AZ::Vector3& eulerAnglesRadians) + void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotation is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::ConvertEulerRadiansToQuaternion(eulerAnglesRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationQuaternion(const AZ::Quaternion& quaternion) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationQuaternion is deprecated, please use SetLocalRotation"); AZ::Transform newWorldTransform = GetWorldTM(); newWorldTransform.SetRotation(quaternion); SetWorldTM(newWorldTransform); } - void TransformComponent::SetRotationX(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationX is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationX(eulerAngleRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationY(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationY is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationY(eulerAngleRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationZ(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationZ is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationZ(eulerAngleRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::RotateByX(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByX is deprecated, please use RotateAroundLocalX"); - SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationX(eulerAngleRadians)); - } - - void TransformComponent::RotateByY(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByY is deprecated, please use RotateAroundLocalY"); - SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationY(eulerAngleRadians)); - } - - void TransformComponent::RotateByZ(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByZ is deprecated, please use RotateAroundLocalZ"); - SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationZ(eulerAngleRadians)); - } - - AZ::Vector3 TransformComponent::GetRotationEulerRadians() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetRotationEulerRadians is deprecated, please use GetWorldRotation"); - return GetWorldTM().GetRotation().GetEulerRadians(); - } - - AZ::Quaternion TransformComponent::GetRotationQuaternion() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetRotationQuaternion is deprecated, please use GetWorldRotationQuaternion"); - return GetWorldTM().GetRotation(); - } - - float TransformComponent::GetRotationX() - { - return GetRotationEulerRadians().GetX(); - } - - float TransformComponent::GetRotationY() - { - return GetRotationEulerRadians().GetY(); - } - - float TransformComponent::GetRotationZ() - { - return GetRotationEulerRadians().GetZ(); - } - AZ::Vector3 TransformComponent::GetWorldRotation() { return GetWorldTM().GetRotation().GetEulerRadians(); @@ -677,108 +599,36 @@ namespace AzToolsFramework return result; } - void TransformComponent::SetScale(const AZ::Vector3& newScale) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScale is deprecated, please use SetLocalScale"); - - AZ::Transform newWorldTransform = GetWorldTM(); - AZ::Vector3 prevScale = newWorldTransform.ExtractScale(); - if (!prevScale.IsClose(newScale)) - { - newWorldTransform.MultiplyByScale(newScale); - SetWorldTM(newWorldTransform); - } - } - - void TransformComponent::SetScaleX(float newScale) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScaleX is deprecated, please use SetLocalScaleX"); - - AZ::Transform newWorldTransform = GetWorldTM(); - AZ::Vector3 scale = newWorldTransform.ExtractScale(); - scale.SetX(newScale); - newWorldTransform.MultiplyByScale(scale); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetScaleY(float newScale) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScaleY is deprecated, please use SetLocalScaleY"); - - AZ::Transform newWorldTransform = GetWorldTM(); - AZ::Vector3 scale = newWorldTransform.ExtractScale(); - scale.SetY(newScale); - newWorldTransform.MultiplyByScale(scale); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetScaleZ(float newScale) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScaleZ is deprecated, please use SetLocalScaleZ"); - - AZ::Transform newWorldTransform = GetWorldTM(); - AZ::Vector3 scale = newWorldTransform.ExtractScale(); - scale.SetZ(newScale); - newWorldTransform.MultiplyByScale(scale); - SetWorldTM(newWorldTransform); - } - - AZ::Vector3 TransformComponent::GetScale() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScale is deprecated, please use GetLocalScale"); - return GetWorldTM().GetScale(); - } - - float TransformComponent::GetScaleX() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScaleX is deprecated, please use GetLocalScale"); - return GetWorldTM().GetScale().GetX(); - } - - float TransformComponent::GetScaleY() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScaleY is deprecated, please use GetLocalScale"); - return GetWorldTM().GetScale().GetY(); - } - - float TransformComponent::GetScaleZ() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScaleZ is deprecated, please use GetLocalScale"); - return GetWorldTM().GetScale().GetZ(); - } - void TransformComponent::SetLocalScale(const AZ::Vector3& scale) { m_editorTransform.m_scale = scale; TransformChanged(); } - void TransformComponent::SetLocalScaleX(float scaleX) + AZ::Vector3 TransformComponent::GetLocalScale() { - m_editorTransform.m_scale.SetX(scaleX); - TransformChanged(); + return m_editorTransform.m_scale; } - void TransformComponent::SetLocalScaleY(float scaleY) + AZ::Vector3 TransformComponent::GetWorldScale() { - m_editorTransform.m_scale.SetY(scaleY); - TransformChanged(); + return GetWorldTM().GetScale(); } - void TransformComponent::SetLocalScaleZ(float scaleZ) + void TransformComponent::SetLocalUniformScale(float scale) { - m_editorTransform.m_scale.SetZ(scaleZ); + m_editorTransform.m_scale = AZ::Vector3(scale); TransformChanged(); } - AZ::Vector3 TransformComponent::GetLocalScale() + float TransformComponent::GetLocalUniformScale() { - return m_editorTransform.m_scale; + return m_editorTransform.m_scale.GetMaxElement(); } - AZ::Vector3 TransformComponent::GetWorldScale() + float TransformComponent::GetWorldUniformScale() { - return GetWorldTM().GetScale(); + return GetWorldTM().GetUniformScale(); } const AZ::Transform& TransformComponent::GetParentWorldTM() const @@ -1183,12 +1033,6 @@ namespace AzToolsFramework ModifyEditorTransform(m_editorTransform.m_rotate, data, parent); } - void TransformComponent::ScaleBy(const AZ::Vector3& data) - { - //scale is always local - ModifyEditorTransform(m_editorTransform.m_scale, data, AZ::Transform::Identity()); - } - AZ::EntityId TransformComponent::GetSliceEntityParentId() { return GetParentId(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h index 3d1e1ed672..f772b608c1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h @@ -99,22 +99,7 @@ namespace AzToolsFramework float GetLocalZ() override; // Rotation modifiers - void SetRotation(const AZ::Vector3& eulerAnglesRadians) override; - void SetRotationQuaternion(const AZ::Quaternion& quaternion) override; - void SetRotationX(float eulerAngleRadians) override; - void SetRotationY(float eulerAngleRadians) override; - void SetRotationZ(float eulerAngleRadians) override; - - void RotateByX(float eulerAngleRadians) override; - void RotateByY(float eulerAngleRadians) override; - void RotateByZ(float eulerAngleRadians) override; - - AZ::Vector3 GetRotationEulerRadians() override; - AZ::Quaternion GetRotationQuaternion() override; - - float GetRotationX() override; - float GetRotationY() override; - float GetRotationZ() override; + void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override; AZ::Vector3 GetWorldRotation() override; AZ::Quaternion GetWorldRotationQuaternion() override; @@ -130,24 +115,14 @@ namespace AzToolsFramework AZ::Quaternion GetLocalRotationQuaternion() override; // Scale Modifiers - void SetScale(const AZ::Vector3& newScale) override; - void SetScaleX(float newScale) override; - void SetScaleY(float newScale) override; - void SetScaleZ(float newScale) override; - - AZ::Vector3 GetScale() override; - float GetScaleX() override; - float GetScaleY() override; - float GetScaleZ() override; - void SetLocalScale(const AZ::Vector3& scale) override; - void SetLocalScaleX(float scaleX) override; - void SetLocalScaleY(float scaleY) override; - void SetLocalScaleZ(float scaleZ) override; - AZ::Vector3 GetLocalScale() override; AZ::Vector3 GetWorldScale() override; + void SetLocalUniformScale(float scale) override; + float GetLocalUniformScale() override; + float GetWorldUniformScale() override; + AZ::EntityId GetParentId() override; AZ::TransformInterface* GetParent() override; void SetParent(AZ::EntityId parentId) override; @@ -161,7 +136,6 @@ namespace AzToolsFramework // TransformComponentMessages::Bus void TranslateBy(const AZ::Vector3&) override; void RotateBy(const AZ::Vector3&) override; // euler in degrees - void ScaleBy(const AZ::Vector3&) override; const EditorTransform& GetLocalEditorTransform() override; void SetLocalEditorTransform(const EditorTransform& dest) override; bool IsTransformLocked() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h index f1cb8459c4..437a39b1a0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h @@ -65,7 +65,6 @@ namespace AzToolsFramework virtual void TranslateBy(const AZ::Vector3&) = 0; virtual void RotateBy(const AZ::Vector3&) = 0; - virtual void ScaleBy(const AZ::Vector3&) = 0; virtual bool IsTransformLocked() = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h index 48b5148d3c..4cf6d077e7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h @@ -83,7 +83,7 @@ namespace AzToolsFramework protected: QWidget* GetFirstInTabOrder() override; QWidget* GetLastInTabOrder() override; - void UpdateTabOrder() override; + void UpdateTabOrder() override; void onChildComboBoxValueChange(int comboBoxIndex) override; @@ -93,7 +93,7 @@ namespace AzToolsFramework void addElementImpl(const AZStd::pair& genericValue); - QLabel* m_warningLabel = nullptr; + QLabel* m_warningLabel = nullptr; DHQComboBox* m_pComboBox; AZStd::vector> m_values; AZ::AttributeFunction * m_postChangeNotifyCB{}; @@ -131,6 +131,11 @@ namespace AzToolsFramework template AzToolsFramework::PropertyHandlerBase* RegisterGenericComboBoxHandler() { + if (!AzToolsFramework::PropertyTypeRegistrationMessages::Bus::FindFirstHandler()) + { + return nullptr; + } + auto propertyHandler = aznew GenericComboBoxHandler(); AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(&AzToolsFramework::PropertyTypeRegistrationMessages::RegisterPropertyType, propertyHandler); return propertyHandler; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index d69eb5559f..23f8378df5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -769,6 +769,14 @@ namespace AzToolsFramework // Request the AssetBrowser Dialog and set a type filter AssetSelectionModel selection = GetAssetSelectionModel(); selection.SetSelectedAssetId(m_selectedAssetID); + + AZStd::string defaultDirectory; + if (m_defaultDirectoryCallback) + { + m_defaultDirectoryCallback->Invoke(m_editNotifyTarget, defaultDirectory); + selection.SetDefaultDirectory(defaultDirectory); + } + AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, parentWidget()); if (selection.IsValid()) { @@ -1080,6 +1088,11 @@ namespace AzToolsFramework m_editNotifyCallback = editNotifyCallback; } + void PropertyAssetCtrl::SetDefaultDirectoryCallback(DefaultDirectoryCallbackType* callback) + { + m_defaultDirectoryCallback = callback; + } + void PropertyAssetCtrl::SetClearNotifyCallback(ClearCallbackType* clearNotifyCallback) { m_clearNotifyCallback = clearNotifyCallback; @@ -1214,6 +1227,11 @@ namespace AzToolsFramework GUI->SetTitle(title.c_str()); } } + else if (attrib == AZ_CRC_CE("DefaultStartingDirectoryCallback")) + { + // This is assumed to be an Asset Browser path to a specific folder to be used as a default by the asset picker if provided + GUI->SetDefaultDirectoryCallback(azdynamic_cast(attrValue->GetAttribute())); + } else if (attrib == AZ_CRC("EditCallback", 0xb74f2ee1)) { PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast(attrValue->GetAttribute()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index e845cdf4fb..37af3d0594 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -68,6 +68,7 @@ namespace AzToolsFramework // This is meant to be used with the "EditCallback" Attribute using EditCallbackType = AZ::Edit::AttributeFunction; using ClearCallbackType = AZ::Edit::AttributeFunction; + using DefaultDirectoryCallbackType = AZ::Edit::AttributeFunction; PropertyAssetCtrl(QWidget *pParent = NULL, QString optionalValidDragDropExtensions = QString()); virtual ~PropertyAssetCtrl(); @@ -119,6 +120,7 @@ namespace AzToolsFramework EditCallbackType* m_editNotifyCallback = nullptr; ClearCallbackType* m_clearNotifyCallback = nullptr; QString m_optionalValidDragDropExtensions; + DefaultDirectoryCallbackType* m_defaultDirectoryCallback = nullptr; //! The number of characters after which the autocompleter dropdown will be shown. // Prevents showing too many options. @@ -196,6 +198,7 @@ namespace AzToolsFramework void SetEditNotifyTarget(void* editNotifyTarget); void SetEditNotifyCallback(EditCallbackType* editNotifyCallback); // This is meant to be used with the "EditCallback" Attribute void SetClearNotifyCallback(ClearCallbackType* clearNotifyCallback); // This is meant to be used with the "ClearNotify" Attribute + void SetDefaultDirectoryCallback(DefaultDirectoryCallbackType* callback); // This is meant to be used with the "DefaultStartingDirectoryCallback" Attribute void SetEditButtonEnabled(bool enabled); void SetEditButtonVisible(bool visible); void SetEditButtonIcon(const QIcon& icon); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 8e91dc945d..91eee18cb7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -165,15 +165,18 @@ namespace AzToolsFramework virtual bool AngleSnappingEnabled() = 0; /// Return the angle snapping/step size. virtual float AngleStep() = 0; - /// Transform a point in world space to screen space coordinates. + /// Transform a point in world space to screen space coordinates in Qt Widget space. + /// Multiply by DeviceScalingFactor to get the position in viewport pixel space. virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0; - /// Transform a point in screen space coordinates to a vector in world space based on clip space depth. + /// Transform a point from Qt widget screen space to world space based on the given clip space depth. /// Depth specifies a relative camera depth to project in the range of [0.f, 1.f]. /// Returns the world space position if successful. virtual AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0; /// Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane. /// Returns a ray containing the ray's origin and a direction normal, if successful. virtual AZStd::optional ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0; + /// Gets the DPI scaling factor that translates Qt widget space into viewport pixel space. + virtual float DeviceScalingFactor() = 0; protected: ~ViewportInteractionRequests() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 5c62a2997b..31da01fadc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -21,6 +21,7 @@ #include #include #include +#include AZ_CVAR( bool, ed_visibility_showAggregateEntitySelectionBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null, @@ -232,10 +233,14 @@ namespace AzToolsFramework return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f); }(); - debugDisplay.SetColor(iconHighlight); - // debugDisplay.DrawTextureLabel( - // iconTextureId, entityPosition, iconSize, iconSize, - // /*DisplayContext::ETextureIconFlags::TEXICON_ON_TOP=*/ 0x0008); + EditorViewportIconDisplay::Get()->DrawIcon({ + viewportInfo.m_viewportId, + iconTextureId, + iconHighlight, + entityPosition, + EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace, + AZ::Vector2{iconSize, iconSize} + }); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 91644dc6ac..3507f532b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -435,7 +435,7 @@ namespace AzToolsFramework } } - static void DestroyTransformModeSelectionCluster(const ViewportUi::ClusterId clusterId) + static void DestroyCluster(const ViewportUi::ClusterId clusterId) { ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, @@ -483,6 +483,26 @@ namespace AzToolsFramework return worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, pivot)); } + void EditorTransformComponentSelection::UpdateSpaceCluster(const ReferenceFrame referenceFrame) + { + auto buttonIdFromFrameFn = [this](const ReferenceFrame referenceFrame) { + switch (referenceFrame) + { + case ReferenceFrame::Local: + return m_spaceCluster.m_localButtonId; + case ReferenceFrame::Parent: + return m_spaceCluster.m_parentButtonId; + case ReferenceFrame::World: + return m_spaceCluster.m_worldButtonId; + } + return m_spaceCluster.m_parentButtonId; + }; + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_spaceClusterId, + buttonIdFromFrameFn(referenceFrame)); + } + namespace ETCS { PivotOrientationResult CalculatePivotOrientation( @@ -789,14 +809,12 @@ namespace AzToolsFramework EntityIdManipulators& entityIdManipulators, OptionalFrame& pivotOverrideFrame, ViewportInteraction::KeyboardModifiers& prevModifiers, - bool& transformChangedInternally) + bool& transformChangedInternally, const AZStd::optional spaceLock) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); entityIdManipulators.m_manipulators->SetLocalPosition(action.LocalPosition()); - const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(action.m_modifiers); - if (action.m_modifiers.Ctrl()) { // moving with ctrl - setting override @@ -806,6 +824,8 @@ namespace AzToolsFramework } else { + const ReferenceFrame referenceFrame = spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers)); + // note: used for parent and world depending on the current reference frame const auto pivotOrientation = ETCS::CalculateSelectionPivotOrientation( @@ -1027,6 +1047,7 @@ namespace AzToolsFramework EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId); CreateTransformModeSelectionCluster(); + CreateSpaceSelectionCluster(); RegisterActions(); SetupBoxSelect(); RefreshSelectedEntityIdsAndRegenerateManipulators(); @@ -1037,7 +1058,9 @@ namespace AzToolsFramework m_selectedEntityIds.clear(); DestroyManipulators(m_entityIdManipulators); - DestroyTransformModeSelectionCluster(m_transformModeClusterId); + DestroyCluster(m_transformModeClusterId); + DestroyCluster(m_spaceCluster.m_spaceClusterId); + UnregisterActions(); m_pivotOverrideFrame.Reset(); @@ -1274,8 +1297,8 @@ namespace AzToolsFramework [this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void { UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, - m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally); + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + m_transformChangedInternally, m_spaceCluster.m_spaceLock); }); translationManipulators->InstallLinearManipulatorMouseUpCallback( @@ -1305,8 +1328,8 @@ namespace AzToolsFramework [this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void { UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, - m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally); + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + m_transformChangedInternally, m_spaceCluster.m_spaceLock); }); translationManipulators->InstallPlanarManipulatorMouseUpCallback( @@ -1335,8 +1358,8 @@ namespace AzToolsFramework [this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void { UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, - m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally); + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + m_transformChangedInternally, m_spaceCluster.m_spaceLock); }); translationManipulators->InstallSurfaceManipulatorMouseUpCallback( @@ -1414,8 +1437,7 @@ namespace AzToolsFramework [this, prevModifiers, sharedRotationState] (const AngularManipulator::Action& action) mutable -> void { - const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(action.m_modifiers); - + const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers)); const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; // store the pivot override frame when positioning the manipulator manually (ctrl) // so we don't lose the orientation when adding/removing entities from the selection @@ -1474,7 +1496,7 @@ namespace AzToolsFramework { const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized(); const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation(); - const AZ::Vector3 scale = entityIdLookupIt->second.m_initial.GetScale(); + const float scale = entityIdLookupIt->second.m_initial.GetUniformScale(); const AZ::Vector3 centerOffset = CalculateCenterOffset(entityId, m_pivotMode); @@ -1485,7 +1507,7 @@ namespace AzToolsFramework AZ::Transform::CreateFromQuaternion(rotation) * AZ::Transform::CreateTranslation(centerOffset) * offsetRotation * AZ::Transform::CreateTranslation(-centerOffset) * - AZ::Transform::CreateScale(scale)); + AZ::Transform::CreateUniformScale(scale)); } break; case ReferenceFrame::Parent: @@ -1597,16 +1619,15 @@ namespace AzToolsFramework } const AZ::Transform initial = entityIdLookupIt->second.m_initial; - const AZ::Vector3 initialScale = initial.GetScale(); + const float initialScale = initial.GetUniformScale(); const auto sumVectorElements = [](const AZ::Vector3& vec) { return vec.GetX() + vec.GetY() + vec.GetZ(); }; - const AZ::Vector3 uniformScale = AZ::Vector3(action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset())); - const AZ::Vector3 scale = (AZ::Vector3::CreateOne() + - (uniformScale / initialScale)).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)); - const AZ::Transform scaleTransform = AZ::Transform::CreateScale(scale); + const float uniformScale = action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset()); + const float scale = AZ::GetClamp(1.0f + uniformScale / initialScale, AZ::MinTransformScale, AZ::MaxTransformScale); + const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale); if (action.m_modifiers.Alt()) { @@ -1872,7 +1893,7 @@ namespace AzToolsFramework CopyOrientationToSelectedEntitiesGroup(QuaternionFromTransformNoScaling(worldFromLocal)); break; case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetScale()); + CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetUniformScale()); break; case Mode::Translation: CopyTranslationToSelectedEntitiesGroup(worldFromLocal.GetTranslation()); @@ -1901,7 +1922,7 @@ namespace AzToolsFramework CopyOrientationToSelectedEntitiesIndividual(QuaternionFromTransformNoScaling(worldFromLocal)); break; case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetScale()); + CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetUniformScale()); break; case Mode::Translation: CopyTranslationToSelectedEntitiesIndividual(worldFromLocal.GetTranslation()); @@ -2394,7 +2415,7 @@ namespace AzToolsFramework ResetOrientationForSelectedEntitiesLocal(); break; case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualLocal(AZ::Vector3::CreateOne()); + CopyScaleToSelectedEntitiesIndividualLocal(1.0f); break; case Mode::Translation: ResetTranslationForSelectedEntitiesLocal(); @@ -2420,7 +2441,7 @@ namespace AzToolsFramework ResetOrientationForSelectedEntitiesLocal(); break; case Mode::Scale: - CopyScaleToSelectedEntitiesIndividualWorld(AZ::Vector3::CreateOne()); + CopyScaleToSelectedEntitiesIndividualWorld(1.0f); break; case Mode::Translation: // do nothing @@ -2567,6 +2588,64 @@ namespace AzToolsFramework m_transformModeSelectionHandler); } + void EditorTransformComponentSelection::CreateSpaceSelectionCluster() + { + // create the cluster for switching spaces/reference frames + ViewportUi::ViewportUiRequestBus::EventResult( + m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + ViewportUi::Alignment::TopRight); + + // create and register the buttons (strings correspond to icons even if the values appear different) + m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World"); + m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent"); + m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local"); + + auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) { + if (buttonId == m_spaceCluster.m_localButtonId) + { + // Unlock + if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::Local) + { + m_spaceCluster.m_spaceLock = AZStd::nullopt; + } + else + { + m_spaceCluster.m_spaceLock = ReferenceFrame::Local; + } + } + else if (buttonId == m_spaceCluster.m_parentButtonId) + { + // Unlock + if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::Parent) + { + m_spaceCluster.m_spaceLock = AZStd::nullopt; + } + else + { + m_spaceCluster.m_spaceLock = ReferenceFrame::Parent; + } + } + else if (buttonId == m_spaceCluster.m_worldButtonId) + { + // Unlock + if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::World) + { + m_spaceCluster.m_spaceLock = AZStd::nullopt; + } + else + { + m_spaceCluster.m_spaceLock = ReferenceFrame::World; + } + } + }; + + m_spaceCluster.m_spaceSelectionHandler = AZ::Event::Handler(onButtonClicked); + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, + m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler); + } + EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode() { return m_mode; @@ -2940,7 +3019,7 @@ namespace AzToolsFramework } } - void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualWorld(const AZ::Vector3& scale) + void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualWorld(float scale) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -2955,7 +3034,7 @@ namespace AzToolsFramework const auto transformsBefore = RecordTransformsBefore(manipulatorEntityIds.m_entityIds); // update scale relative to initial - const AZ::Transform scaleTransform = AZ::Transform::CreateScale(scale); + const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale); for (AZ::EntityId entityId : manipulatorEntityIds.m_entityIds) { ScopedUndoBatch::MarkEntityDirty(entityId); @@ -2964,7 +3043,7 @@ namespace AzToolsFramework if (transformIt != transformsBefore.end()) { AZ::Transform transformBefore = transformIt->second; - transformBefore.ExtractScale(); + transformBefore.ExtractUniformScale(); AZ::Transform newWorldFromLocal = transformBefore * scaleTransform; SetEntityWorldTransform(entityId, newWorldFromLocal); @@ -2974,7 +3053,7 @@ namespace AzToolsFramework RefreshUiAfterChange(manipulatorEntityIds.m_entityIds); } - void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualLocal(const AZ::Vector3& scale) + void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualLocal(float scale) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3020,9 +3099,9 @@ namespace AzToolsFramework if (transformIt != transformsBefore.end()) { AZ::Transform newWorldFromLocal = transformIt->second; - const AZ::Vector3 scale = newWorldFromLocal.GetScale(); + const float scale = newWorldFromLocal.GetUniformScale(); newWorldFromLocal.SetRotation(orientation); - newWorldFromLocal *= AZ::Transform::CreateScale(scale); + newWorldFromLocal *= AZ::Transform::CreateUniformScale(scale); SetEntityWorldTransform(entityId, newWorldFromLocal); } @@ -3278,7 +3357,9 @@ namespace AzToolsFramework ViewportInteraction::BuildMouseButtons( QGuiApplication::mouseButtons()), m_boxSelect.Active()); - const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(modifiers); + const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(modifiers)); + + UpdateSpaceCluster(referenceFrame); bool refresh = false; if (referenceFrame != m_referenceFrame) @@ -3669,7 +3750,7 @@ namespace AzToolsFramework } void EditorTransformComponentSelection::SetEntityLocalScale( - const AZ::EntityId entityId, const AZ::Vector3& localScale) + const AZ::EntityId entityId, const float localScale) { ETCS::SetEntityLocalScale(entityId, localScale, m_transformChangedInternally); } @@ -3722,11 +3803,11 @@ namespace AzToolsFramework entityId, &AZ::TransformBus::Events::SetWorldTM, worldTransform); } - void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale, bool& internal) + void SetEntityLocalScale(AZ::EntityId entityId, float localScale, bool& internal) { ScopeSwitch sw(internal); AZ::TransformBus::Event( - entityId, &AZ::TransformBus::Events::SetLocalScale, localScale); + entityId, &AZ::TransformBus::Events::SetLocalUniformScale, localScale); } void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation, bool& internal) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 7514f5153b..2bc4d7cbf6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -106,6 +106,22 @@ namespace AzToolsFramework World, //!< World space (space aligned to world axes - identity). }; + //! Grouping of viewport ui related state for controlling the current reference space of the Editor. + struct SpaceCluster + { + SpaceCluster() = default; + // disable copying and moving (implicit) + SpaceCluster(const SpaceCluster&) = delete; + SpaceCluster& operator=(const SpaceCluster&) = delete; + + ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster. + ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id. + ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id. + ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id. + AZ::Event::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed. + AZStd::optional m_spaceLock; //!< Locked reference frame to use if set. + }; + //! Entity selection/interaction handling. //! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent. class EditorTransformComponentSelection @@ -160,6 +176,7 @@ namespace AzToolsFramework void RegenerateManipulators(); void CreateTransformModeSelectionCluster(); + void CreateSpaceSelectionCluster(); void ClearManipulatorTranslationOverride(); void ClearManipulatorOrientationOverride(); @@ -214,8 +231,8 @@ namespace AzToolsFramework void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation); void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation); void ResetOrientationForSelectedEntitiesLocal(); - void CopyScaleToSelectedEntitiesIndividualLocal(const AZ::Vector3& scale); - void CopyScaleToSelectedEntitiesIndividualWorld(const AZ::Vector3& scale); + void CopyScaleToSelectedEntitiesIndividualLocal(float scale); + void CopyScaleToSelectedEntitiesIndividualWorld(float scale); // EditorManipulatorCommandUndoRedoRequestBus ... void UndoRedoEntityManipulatorCommand( @@ -250,9 +267,12 @@ namespace AzToolsFramework void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation); void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation); void SetEntityWorldTransform(AZ::EntityId entityId, const AZ::Transform& worldTransform); - void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale); + void SetEntityLocalScale(AZ::EntityId entityId, float localScale); void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation); + // Responsible for keeping the space cluster in sync with the current reference frame. + void UpdateSpaceCluster(ReferenceFrame referenceFrame); + AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any). AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display. AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set. @@ -285,6 +305,7 @@ namespace AzToolsFramework AZ::Event::Handler m_transformModeSelectionHandler; //!< Event handler for the Viewport UI cluster. AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click. AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame. + SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space. }; //! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by @@ -320,7 +341,7 @@ namespace AzToolsFramework void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation, bool& internal); void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation, bool& internal); void SetEntityWorldTransform(AZ::EntityId entityId, const AZ::Transform& worldTransform, bool& internal); - void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale, bool& internal); + void SetEntityLocalScale(AZ::EntityId entityId, float localScale, bool& internal); void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation, bool& internal); } // namespace ETCS } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h index 59c250b8f7..9cd78f8c50 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h @@ -101,10 +101,10 @@ namespace AzToolsFramework virtual void ResetOrientationForSelectedEntitiesLocal() = 0; /// Copy scale to each individual entity in local space without moving position. - virtual void CopyScaleToSelectedEntitiesIndividualLocal(const AZ::Vector3& scale) = 0; + virtual void CopyScaleToSelectedEntitiesIndividualLocal(float scale) = 0; /// Copy scale to to each individual entity in world (absolute) space. - virtual void CopyScaleToSelectedEntitiesIndividualWorld(const AZ::Vector3& scale) = 0; + virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0; protected: ~EditorTransformComponentSelectionRequests() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 45f52704bf..aaf5c86d33 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -46,6 +46,7 @@ set(FILES API/EditorWindowRequestBus.h API/EntityCompositionRequestBus.h API/EntityCompositionNotificationBus.h + API/EditorViewportIconDisplayInterface.h API/ViewPaneOptions.h Application/Ticker.h Application/Ticker.cpp diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 5ccbd95f09..33009bc0da 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,12 @@ namespace UnitTest m_assetSeedManager = new AzToolsFramework::AssetSeedManager(); m_assetRegistry = new AzFramework::AssetRegistry(); + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start(AzFramework::Application::Descriptor()); for (int idx = 0; idx < s_totalAssets; idx++) @@ -75,7 +82,7 @@ namespace UnitTest } m_testPlatforms[0] = AzFramework::PlatformId::PC; - m_testPlatforms[1] = AzFramework::PlatformId::ES3; + m_testPlatforms[1] = AzFramework::PlatformId::ANDROID_ID; int platformCount = 0; for(auto thisPlatform : m_testPlatforms) @@ -163,20 +170,20 @@ namespace UnitTest AzFramework::AssetCatalog assetCatalog(useRequestBus); AZStd::string pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC); - AZStd::string es3CatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3); + AZStd::string androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); if (!assetCatalog.SaveCatalog(pcCatalogFile.c_str(), m_assetRegistry)) { GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (PC) file.\n").c_str()); } - if (!assetCatalog.SaveCatalog(es3CatalogFile.c_str(), m_assetRegistry)) + if (!assetCatalog.SaveCatalog(androidCatalogFile.c_str(), m_assetRegistry)) { - GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (ES3) file.\n").c_str()); + GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (ANDROID) file.\n").c_str()); } m_pcCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::PC); - m_es3Catalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::ES3); + m_androidCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::ANDROID_ID); const AZStd::string engroot = AZ::Test::GetEngineRootPath(); AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str()); @@ -220,21 +227,21 @@ namespace UnitTest } auto pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC); - auto es3CatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3); + auto androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); if (fileIO->Exists(pcCatalogFile.c_str())) { fileIO->Remove(pcCatalogFile.c_str()); } - if (fileIO->Exists(es3CatalogFile.c_str())) + if (fileIO->Exists(androidCatalogFile.c_str())) { - fileIO->Remove(es3CatalogFile.c_str()); + fileIO->Remove(androidCatalogFile.c_str()); } delete m_assetSeedManager; delete m_assetRegistry; delete m_pcCatalog; - delete m_es3Catalog; + delete m_androidCatalog; m_application->Stop(); delete m_application; } @@ -335,10 +342,10 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC); // Step we are testing - m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ES3); + m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID); // Verification - AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3; + AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList()) { EXPECT_EQ(seedInfo.m_platformFlags, expectedPlatformFlags); @@ -351,14 +358,14 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC); m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_PC); - m_es3Catalog->UnregisterAsset(assets[2]); + m_androidCatalog->UnregisterAsset(assets[2]); m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC); // Step we are testing - m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ES3); + m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID); // Verification - AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3; + AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList()) { if (seedInfo.m_assetId == assets[2]) @@ -376,14 +383,14 @@ namespace UnitTest { // Setup m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC); - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_ANDROID); m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_PC); - m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_ANDROID); m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC); - m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_ANDROID); // Step we are testing - m_assetSeedManager->RemovePlatformFromAllSeeds(AzFramework::PlatformId::ES3); + m_assetSeedManager->RemovePlatformFromAllSeeds(AzFramework::PlatformId::ANDROID_ID); // Verification for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList()) @@ -507,8 +514,8 @@ namespace UnitTest void DependencyValidation_MultipleAssetSeeds_MultiplePlatformFlags_ListValid() { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); - m_assetSeedManager->AddSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); + m_assetSeedManager->AddSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); AzToolsFramework::AssetFileInfoList assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -524,7 +531,7 @@ namespace UnitTest assetList.m_fileInfoList.clear(); - m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -540,7 +547,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[8])); assetList.m_fileInfoList.clear(); - m_assetSeedManager->RemoveSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->RemoveSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -555,7 +562,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[8])); // Removing the android flag from the asset should still produce the same result - m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -569,7 +576,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[7])); EXPECT_TRUE(Search(assetList, assets[8])); - assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ES3); + assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID); EXPECT_EQ(assetList.m_fileInfoList.size(), 5); EXPECT_TRUE(Search(assetList, assets[0])); @@ -579,8 +586,8 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[4])); // Adding the android flag again to the asset - m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ES3); - assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ES3); + m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID); + assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID); EXPECT_EQ(assetList.m_fileInfoList.size(), 8); EXPECT_TRUE(Search(assetList, assets[0])); @@ -766,7 +773,7 @@ namespace UnitTest AzFramework::AssetRegistry* m_assetRegistry; ToolsTestApplication* m_application; AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog; - AzToolsFramework::PlatformAddressedAssetCatalog* m_es3Catalog; + AzToolsFramework::PlatformAddressedAssetCatalog* m_androidCatalog; AZ::IO::FileIOStream m_fileStreams[s_totalTestPlatforms][s_totalAssets]; AzFramework::PlatformId m_testPlatforms[s_totalTestPlatforms]; AZStd::string m_assetsPath[s_totalAssets]; @@ -929,7 +936,7 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_AllPlatformsValid_SeedAddedForEveryInputPlatform) { using namespace AzFramework; - PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ES3; + PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ANDROID; AZStd::pair result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, validPlatforms); // Verify the function outputs @@ -946,8 +953,8 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_SomePlatformsValid_SeedAddedForEveryValidPlatform) { using namespace AzFramework; - PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ES3; - PlatformFlags inputPlatforms = validPlatforms | PlatformFlags::Platform_OSX; + PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ANDROID; + PlatformFlags inputPlatforms = validPlatforms | PlatformFlags::Platform_MAC; AZStd::pair result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, inputPlatforms); // Verify the function outputs @@ -964,7 +971,7 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_NoPlatformsValid_NoSeedAdded) { using namespace AzFramework; - PlatformFlags inputPlatforms = PlatformFlags::Platform_OSX; + PlatformFlags inputPlatforms = PlatformFlags::Platform_MAC; AZStd::pair result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, inputPlatforms); // Verify the function outputs @@ -978,30 +985,30 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, Valid_Seed_Remove_ForAllPlatform_OK) { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(seedList.size(), 0); - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 0); } TEST_F(AssetSeedManagerTest, Valid_Seed_Remove_ForSpecificPlatform_OK) { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(seedList.size(), 1); - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); @@ -1010,14 +1017,14 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, Invalid_NotRemove_SeedForAllPlatform_Ok) { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset(assets[1].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset(assets[1].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(seedList.size(), 1); - m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 1); } diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h b/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h index 1e01229d73..8a394d3ab5 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h +++ b/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h @@ -25,6 +25,8 @@ namespace UnitTests MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ()); MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ()); MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath)); + MOCK_METHOD3(GenerateRelativeSourcePath, + bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder)); MOCK_METHOD2(GetFullSourcePathFromRelativeProductPath, bool(const AZStd::string& relPath, AZStd::string& fullSourcePath)); MOCK_METHOD5(GetAssetInfoById, bool(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath)); MOCK_METHOD3(GetSourceInfoBySourcePath, bool(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)); diff --git a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp index 328bf5dea5..0b705338d9 100644 --- a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp @@ -10,6 +10,8 @@ * */ +#include +#include #include #include #include @@ -49,6 +51,14 @@ namespace UnitTest using namespace AZ::Data; m_application = new ToolsTestApplication("AddressedAssetCatalogManager"); // Shorter name because Setting Registry // specialization are 32 characters max. + + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash @@ -174,13 +184,13 @@ namespace UnitTest TEST_F(PlatformAddressedAssetCatalogManagerTest, PlatformAddressedAssetCatalogManager_CatalogExistsChecks_Success) { - EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ES3), true); - AZStd::string es3CatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3); - if (AZ::IO::FileIOBase::GetInstance()->Exists(es3CatalogPath.c_str())) + EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), true); + AZStd::string androidCatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); + if (AZ::IO::FileIOBase::GetInstance()->Exists(androidCatalogPath.c_str())) { - AZ::IO::FileIOBase::GetInstance()->Remove(es3CatalogPath.c_str()); + AZ::IO::FileIOBase::GetInstance()->Remove(androidCatalogPath.c_str()); } - EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ES3), false); + EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), false); } class PlatformAddressedAssetCatalogMessageTest : public AzToolsFramework::PlatformAddressedAssetCatalog @@ -241,7 +251,7 @@ namespace UnitTest AzFramework::AssetSystem::NetworkAssetUpdateInterface* notificationInterface = AZ::Interface::Get(); EXPECT_NE(notificationInterface, nullptr); - auto* mockCatalog = new ::testing::NiceMock(AzFramework::PlatformId::ES3); + auto* mockCatalog = new ::testing::NiceMock(AzFramework::PlatformId::ANDROID_ID); AZStd::unique_ptr< ::testing::NiceMock> catalogHolder; catalogHolder.reset(mockCatalog); @@ -249,7 +259,7 @@ namespace UnitTest EXPECT_CALL(*mockCatalog, AssetChanged(testing::_)).Times(0); notificationInterface->AssetChanged(testMessage); - testMessage.m_platform = "es3"; + testMessage.m_platform = "android"; EXPECT_CALL(*mockCatalog, AssetChanged(testing::_)).Times(1); notificationInterface->AssetChanged(testMessage); @@ -260,7 +270,7 @@ namespace UnitTest EXPECT_CALL(*mockCatalog, AssetRemoved(testing::_)).Times(0); notificationInterface->AssetRemoved(testMessage); - testMessage.m_platform = "es3"; + testMessage.m_platform = "android"; EXPECT_CALL(*mockCatalog, AssetRemoved(testing::_)).Times(1); notificationInterface->AssetRemoved(testMessage); } diff --git a/Code/Framework/AzToolsFramework/Tests/Slice.cpp b/Code/Framework/AzToolsFramework/Tests/Slice.cpp index 33160b9c20..7b67481684 100644 --- a/Code/Framework/AzToolsFramework/Tests/Slice.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Slice.cpp @@ -483,6 +483,9 @@ namespace UnitTest { AUTO_RESULT_IF_SETTING_TRUE(UnitTest::prefabSystemSetting, true) + // Swallow deprecation warnings from the Transform component as they are not relevant to this test + UnitTest::ErrorHandler errorHandler("GetScale is deprecated"); + // Create a parent entity with a transform component AZ::Entity* parentEntity = aznew AZ::Entity("TestParentEntity"); parentEntity->CreateComponent(); diff --git a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h index 57ac16673d..7d3d312cdb 100644 --- a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h +++ b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h @@ -149,6 +149,9 @@ namespace UnitTest const char* GetAbsoluteDevGameFolderPath() override { return ""; } const char* GetAbsoluteDevRootFolderPath() override { return ""; } bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return false; } + bool GenerateRelativeSourcePath( + [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, + [[maybe_unused]] AZStd::string& watchFolder) override { return false; } bool GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) override { return false; } bool GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetType& assetType, [[maybe_unused]] const AZStd::string& platformName, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& rootFilePath) override { return false; } bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorLayerComponentTests.cpp b/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorLayerComponentTests.cpp index 7b03b63dd0..2177b9e1b2 100644 --- a/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorLayerComponentTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorLayerComponentTests.cpp @@ -610,15 +610,15 @@ namespace AzToolsFramework m_layerEntity.m_layer->ClearUnsavedChanges(); // Change the scale of the child entity so it registers as an unsaved change on the layer. - AZ::Vector3 scale(-1.0f,0.0f,0.0f); + float scale = 0.0f; AZ::TransformBus::EventResult( scale, childEntity->GetId(), - &AZ::TransformBus::Events::GetLocalScale); - scale.SetX(scale.GetX() + 1.0f); + &AZ::TransformBus::Events::GetLocalUniformScale); + scale += 1.0f; AZ::TransformBus::Event( childEntity->GetId(), - &AZ::TransformBus::Events::SetLocalScale, + &AZ::TransformBus::Events::SetLocalUniformScale, scale); bool hasUnsavedChanges = false; diff --git a/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorTransformComponentTests.cpp b/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorTransformComponentTests.cpp index 62cf16f9a7..fcc4aa49e5 100644 --- a/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorTransformComponentTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ToolsComponents/EditorTransformComponentTests.cpp @@ -52,19 +52,19 @@ namespace AzToolsFramework TransformTestEntityHierarchy hierarchy = BuildTestHierarchy(); // Set scale to parent entity - const AZ::Vector3 parentScale(2.0f, 1.0f, 3.0f); - AZ::TransformBus::Event(hierarchy.m_parentId, &AZ::TransformInterface::SetLocalScale, parentScale); + const float parentScale = 2.0f; + AZ::TransformBus::Event(hierarchy.m_parentId, &AZ::TransformInterface::SetLocalUniformScale, parentScale); // Set scale to child entity - const AZ::Vector3 childScale(5.0f, 6.0f, 10.0f); - AZ::TransformBus::Event(hierarchy.m_childId, &AZ::TransformInterface::SetLocalScale, childScale); + const float childScale = 5.0f; + AZ::TransformBus::Event(hierarchy.m_childId, &AZ::TransformInterface::SetLocalUniformScale, childScale); - const AZ::Vector3 expectedScale = childScale * parentScale; + const float expectedScale = childScale * parentScale; - AZ::Vector3 childWorldScale = AZ::Vector3::CreateOne(); - AZ::TransformBus::EventResult(childWorldScale, hierarchy.m_childId, &AZ::TransformBus::Events::GetWorldScale); + float childWorldScale = 1.0f; + AZ::TransformBus::EventResult(childWorldScale, hierarchy.m_childId, &AZ::TransformBus::Events::GetWorldUniformScale); - EXPECT_THAT(childWorldScale, UnitTest::IsClose(expectedScale)); + EXPECT_NEAR(childWorldScale, expectedScale, AZ::Constants::Tolerance); } TEST_F(EditorTransformComponentTest, TransformTests_GetChildren_DirectChildrenMatchHierarchy) diff --git a/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp b/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp index 751e151ec6..1714ee5aa5 100644 --- a/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp +++ b/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp @@ -488,18 +488,17 @@ void TransformCompressor::Marshal(WriteBuffer& wb, const AZ::Transform& value) c { AZ::u8 flags = 0; auto flagsMarker = wb.InsertMarker(flags); - AZ::Matrix3x3 m33 = AZ::Matrix3x3::CreateFromTransform(value); - AZ::Vector3 scale = m33.ExtractScale(); - AZ::Quaternion rot = AZ::Quaternion::CreateFromMatrix3x3(m33.GetOrthogonalized()); + float scale = value.GetUniformScale(); + AZ::Quaternion rot = value.GetRotation(); if (!rot.IsIdentity()) { flags |= HAS_ROT; wb.Write(rot, QuatCompMarshaler()); } - if (!scale.IsClose(AZ::Vector3::CreateOne())) + if (!AZ::IsClose(scale, 1.0f, AZ::Constants::Tolerance)) { flags |= HAS_SCALE; - wb.Write(scale, Vec3CompMarshaler()); + wb.Write(scale, HalfMarshaler()); } AZ::Vector3 pos = value.GetTranslation(); if (!pos.IsZero()) @@ -527,9 +526,9 @@ void TransformCompressor::Unmarshal(AZ::Transform& value, ReadBuffer& rb) const } if (flags & HAS_SCALE) { - AZ::Vector3 scale; - rb.Read(scale, Vec3CompMarshaler()); - xform.MultiplyByScale(scale); + float scale; + rb.Read(scale, HalfMarshaler()); + xform.MultiplyByUniformScale(scale); } if (flags & HAS_POS) { diff --git a/Code/Framework/Tests/ArchiveCompressionTests.cpp b/Code/Framework/Tests/ArchiveCompressionTests.cpp index fb6beca0b5..c648262599 100644 --- a/Code/Framework/Tests/ArchiveCompressionTests.cpp +++ b/Code/Framework/Tests/ArchiveCompressionTests.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -40,6 +41,13 @@ namespace UnitTest void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start({}); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Code/Framework/Tests/ArchiveTests.cpp b/Code/Framework/Tests/ArchiveTests.cpp index aaca043c4b..6dc081ee72 100644 --- a/Code/Framework/Tests/ArchiveTests.cpp +++ b/Code/Framework/Tests/ArchiveTests.cpp @@ -16,6 +16,7 @@ #include #include // for max path decl +#include #include #include #include // for function<> in the find files callback. @@ -42,6 +43,14 @@ namespace UnitTest { AZ::ComponentApplication::Descriptor descriptor; descriptor.m_stackRecordLevels = 30; + + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start(descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Code/Framework/Tests/AssetCatalog.cpp b/Code/Framework/Tests/AssetCatalog.cpp index 8a89ba349c..1575b0a469 100644 --- a/Code/Framework/Tests/AssetCatalog.cpp +++ b/Code/Framework/Tests/AssetCatalog.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -304,6 +305,13 @@ namespace UnitTest m_app.reset(aznew AzFramework::Application()); AZ::ComponentApplication::Descriptor desc; desc.m_useExistingAllocator = true; + + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app->Start(desc); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Code/Framework/Tests/ComponentAddRemove.cpp b/Code/Framework/Tests/ComponentAddRemove.cpp index 4fd6db7dde..f635a5ee3b 100644 --- a/Code/Framework/Tests/ComponentAddRemove.cpp +++ b/Code/Framework/Tests/ComponentAddRemove.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -572,6 +573,12 @@ namespace UnitTest { AllocatorsTestFixture::SetUp(); + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + AzFramework::Application::Descriptor descriptor; descriptor.m_enableDrilling = false; m_app.Start(descriptor); diff --git a/Code/Framework/Tests/FileFunc.cpp b/Code/Framework/Tests/FileFunc.cpp index d703164582..8db77fd79b 100644 --- a/Code/Framework/Tests/FileFunc.cpp +++ b/Code/Framework/Tests/FileFunc.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -278,6 +279,12 @@ namespace UnitTest { FrameworkApplicationFixture::SetUp(); + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_serializeContext = AZStd::make_unique(); m_jsonRegistrationContext = AZStd::make_unique(); m_jsonSystemComponent = AZStd::make_unique(); diff --git a/Code/Framework/Tests/FileTagTests.cpp b/Code/Framework/Tests/FileTagTests.cpp index 989c811111..b94131afee 100644 --- a/Code/Framework/Tests/FileTagTests.cpp +++ b/Code/Framework/Tests/FileTagTests.cpp @@ -83,6 +83,7 @@ namespace UnitTest void SetUp() override { AllocatorsFixture::SetUp(); + m_data = AZStd::make_unique(); using namespace AzFramework::FileTag; AZ::ComponentApplication::Descriptor desc; diff --git a/Code/Framework/Tests/FrameworkApplicationFixture.h b/Code/Framework/Tests/FrameworkApplicationFixture.h index f3a90864e7..c2fea389e0 100644 --- a/Code/Framework/Tests/FrameworkApplicationFixture.h +++ b/Code/Framework/Tests/FrameworkApplicationFixture.h @@ -54,7 +54,7 @@ namespace UnitTest }; void SetUp() override - { + { m_appDescriptor.m_allocationRecords = true; m_appDescriptor.m_allocationRecordsSaveNames = true; m_appDescriptor.m_recordingMode = AZ::Debug::AllocationRecords::Mode::RECORD_FULL; diff --git a/Code/Framework/Tests/GenericComponentWrapperTest.cpp b/Code/Framework/Tests/GenericComponentWrapperTest.cpp index b3dac90777..25af10b339 100644 --- a/Code/Framework/Tests/GenericComponentWrapperTest.cpp +++ b/Code/Framework/Tests/GenericComponentWrapperTest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,12 @@ class WrappedEditorComponentTest protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is @@ -178,6 +185,12 @@ class FindWrappedComponentsTest public: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Code/Framework/Tests/PlatformHelper.cpp b/Code/Framework/Tests/PlatformHelper.cpp index 1ad7794c58..9a23fb8d25 100644 --- a/Code/Framework/Tests/PlatformHelper.cpp +++ b/Code/Framework/Tests/PlatformHelper.cpp @@ -30,11 +30,11 @@ TEST_F(PlatformHelperTest, SinglePlatformFlags_PlatformId_Valid) TEST_F(PlatformHelperTest, MultiplePlatformFlags_PlatformId_Valid) { - AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3; + AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; auto platforms = AzFramework::PlatformHelper::GetPlatforms(platformFlags); EXPECT_EQ(platforms.size(), 2); EXPECT_EQ(platforms[0], "pc"); - EXPECT_EQ(platforms[1], "es3"); + EXPECT_EQ(platforms[1], "android"); } TEST_F(PlatformHelperTest, SpecialAllFlag_PlatformId_Valid) @@ -42,7 +42,7 @@ TEST_F(PlatformHelperTest, SpecialAllFlag_PlatformId_Valid) AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_ALL; auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags); EXPECT_EQ(platforms.size(), AzFramework::NumPlatforms); - EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "es3", "ios", "osx_gl", "provo", "salem", "jasper", "server")); + EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "android", "ios", "mac", "provo", "salem", "jasper", "server")); } TEST_F(PlatformHelperTest, SpecialAllClientFlag_PlatformId_Valid) @@ -50,7 +50,7 @@ TEST_F(PlatformHelperTest, SpecialAllClientFlag_PlatformId_Valid) AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_ALL_CLIENT; auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags); EXPECT_EQ(platforms.size(), AzFramework::NumClientPlatforms); - EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "es3", "ios", "osx_gl", "provo", "salem", "jasper")); + EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "android", "ios", "mac", "provo", "salem", "jasper")); } TEST_F(PlatformHelperTest, InvalidPlatformFlags_PlatformId_Empty) diff --git a/Code/Framework/Tests/Slices.cpp b/Code/Framework/Tests/Slices.cpp index 6a9ce858c0..8a20c43fb5 100644 --- a/Code/Framework/Tests/Slices.cpp +++ b/Code/Framework/Tests/Slices.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1059,6 +1060,12 @@ namespace UnitTest void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp new file mode 100644 index 0000000000..1fa50fff52 --- /dev/null +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -0,0 +1,158 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class TestApplication : public AzFramework::Application + { + public: + // ComponentApplication + void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override + { + Application::SetSettingsRegistrySpecializations(specializations); + specializations.Append("test"); + specializations.Append("spawnable"); + } + }; + + class SpawnableEntitiesManagerTest : public AllocatorsFixture + { + public: + void SetUp() override + { + AllocatorsFixture::SetUp(); + + m_application = new TestApplication(); + AZ::ComponentApplication::Descriptor descriptor; + m_application->Start(descriptor); + + m_spawnable = aznew AzFramework::Spawnable( + AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready); + m_spawnableAsset = new AZ::Data::Asset(m_spawnable, AZ::Data::AssetLoadBehavior::Default); + m_ticket = new AzFramework::EntitySpawnTicket(*m_spawnableAsset); + + auto managerInterface = AzFramework::SpawnableEntitiesInterface::Get(); + m_manager = azrtti_cast(managerInterface); + } + + void TearDown() override + { + delete m_ticket; + m_ticket = nullptr; + // One more tick on the spawnable entities manager in order to delete the ticket fully. + m_manager->ProcessQueue(); + + delete m_spawnableAsset; + m_spawnableAsset = nullptr; + // This will also delete m_spawnable. + + delete m_application; + m_application = nullptr; + + AllocatorsFixture::TearDown(); + } + + void FillSpawnable(size_t numElements) + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + entities.reserve(numElements); + for (size_t i=0; i()); + } + } + + protected: + AZ::Data::Asset* m_spawnableAsset { nullptr }; + AzFramework::SpawnableEntitiesManager* m_manager { nullptr }; + AzFramework::EntitySpawnTicket* m_ticket { nullptr }; + AzFramework::Spawnable* m_spawnable { nullptr }; + TestApplication* m_application { nullptr }; + }; + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_Call_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket&, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + m_manager->SpawnAllEntities(*m_ticket, {}, AZStd::move(callback)); + m_manager->ProcessQueue(); + + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, ListEntities_Call_AllEntitiesAreReported) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + bool allValidEntityIds = true; + size_t spawnedEntitiesCount = 0; + auto callback = [&allValidEntityIds, &spawnedEntitiesCount] + (AzFramework::EntitySpawnTicket&, AzFramework::SpawnableConstEntityContainerView entities) + { + for (auto&& entity : entities) + { + allValidEntityIds = entity->GetId().IsValid() && allValidEntityIds; + } + spawnedEntitiesCount += entities.size(); + }; + + m_manager->SpawnAllEntities(*m_ticket); + m_manager->ListEntities(*m_ticket, AZStd::move(callback)); + m_manager->ProcessQueue(); + + EXPECT_TRUE(allValidEntityIds); + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, ListIndicesAndEntities_Call_AllEntitiesAreReportedAndIncrementByOne) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + bool allValidEntityIds = true; + size_t spawnedEntitiesCount = 0; + auto callback = [&allValidEntityIds, &spawnedEntitiesCount] + (AzFramework::EntitySpawnTicket&, AzFramework::SpawnableConstIndexEntityContainerView entities) + { + for (auto&& indexEntityPair : entities) + { + // Since all entities are spawned a single time, the indices should be 0..NumEntities. + if (indexEntityPair.GetIndex() == spawnedEntitiesCount) + { + spawnedEntitiesCount++; + } + allValidEntityIds = indexEntityPair.GetEntity()->GetId().IsValid() && allValidEntityIds; + } + }; + + m_manager->SpawnAllEntities(*m_ticket); + m_manager->ListIndicesAndEntities(*m_ticket, AZStd::move(callback)); + m_manager->ProcessQueue(); + + EXPECT_TRUE(allValidEntityIds); + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } +} // namespace UnitTest diff --git a/Code/Framework/Tests/TransformComponent.cpp b/Code/Framework/Tests/TransformComponent.cpp index c2615f2a30..707c4d66ec 100644 --- a/Code/Framework/Tests/TransformComponent.cpp +++ b/Code/Framework/Tests/TransformComponent.cpp @@ -24,6 +24,8 @@ #include #include +#include + using namespace AZ; using namespace AzFramework; @@ -362,8 +364,8 @@ namespace UnitTest TEST_F(TransformComponentTransformMatrixSetGet, SetLocalRotation_SimpleValues_Set) { // add some scale first - float sx = 1.03f, sy = 0.67f, sz = 1.23f; - Transform tm = Transform::CreateScale(Vector3(sx, sy, sz)); + float scale = 1.23f; + Transform tm = Transform::CreateUniformScale(scale); TransformBus::Event(m_childId, &TransformBus::Events::SetLocalTM, tm); float rx = 42.435f; @@ -379,13 +381,13 @@ namespace UnitTest Matrix3x3 finalRotate = rotateX * rotateY * rotateZ; Vector3 basisX = tm.GetBasisX(); - Vector3 expectedBasisX = finalRotate.GetBasisX() * sx; + Vector3 expectedBasisX = finalRotate.GetBasisX() * scale; EXPECT_TRUE(basisX.IsClose(expectedBasisX)); Vector3 basisY = tm.GetBasisY(); - Vector3 expectedBasisY = finalRotate.GetBasisY() * sy; + Vector3 expectedBasisY = finalRotate.GetBasisY() * scale; EXPECT_TRUE(basisY.IsClose(expectedBasisY)); Vector3 basisZ = tm.GetBasisZ(); - Vector3 expectedBasisZ = finalRotate.GetBasisZ() * sz; + Vector3 expectedBasisZ = finalRotate.GetBasisZ() * scale; EXPECT_TRUE(basisZ.IsClose(expectedBasisZ)); } @@ -476,18 +478,15 @@ namespace UnitTest { TransformBus::Event(m_childId, &TransformBus::Events::RotateAroundLocalX, rx); } - Vector3 localScale; - TransformBus::EventResult(localScale, m_childId, &TransformBus::Events::GetLocalScale); - EXPECT_TRUE(localScale.IsClose(Vector3(1.0f, 1.0f, 1.0f))); + float localScale = FLT_MAX; + TransformBus::EventResult(localScale, m_childId, &TransformBus::Events::GetLocalUniformScale); + EXPECT_NEAR(localScale, 1.0f, AZ::Constants::Tolerance); } TEST_F(TransformComponentTransformMatrixSetGet, RotateAroundLocalX_ScaleDoesNotSkewRotation) { - float sx = 42.564f; - float sy = 12.460f; - float sz = 28.692f; - Vector3 expectedScales(sx, sy, sz); - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScale, expectedScales); + float expectedScale = 42.564f; + TransformBus::Event(m_childId, &TransformBus::Events::SetLocalUniformScale, expectedScale); float rx = 1.43f; TransformBus::Event(m_childId, &TransformBus::Events::RotateAroundLocalX, rx); @@ -513,18 +512,15 @@ namespace UnitTest { TransformBus::Event(m_childId, &TransformBus::Events::RotateAroundLocalY, ry); } - Vector3 localScale; - TransformBus::EventResult(localScale, m_childId, &TransformBus::Events::GetLocalScale); - EXPECT_TRUE(localScale.IsClose(Vector3(1.0f, 1.0f, 1.0f))); + float localScale = FLT_MAX; + TransformBus::EventResult(localScale, m_childId, &TransformBus::Events::GetLocalUniformScale); + EXPECT_NEAR(localScale, 1.0f, AZ::Constants::Tolerance); } TEST_F(TransformComponentTransformMatrixSetGet, RotateAroundLocalY_ScaleDoesNotSkewRotation) { - float sx = 42.564f; - float sy = 12.460f; - float sz = 28.692f; - Vector3 expectedScales(sx, sy, sz); - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScale, expectedScales); + float expectedScale = 42.564f; + TransformBus::Event(m_childId, &TransformBus::Events::SetLocalUniformScale, expectedScale); float ry = 1.43f; TransformBus::Event(m_childId, &TransformBus::Events::RotateAroundLocalY, ry); @@ -550,18 +546,15 @@ namespace UnitTest { TransformBus::Event(m_childId, &TransformBus::Events::RotateAroundLocalZ, rz); } - Vector3 localScale; - TransformBus::EventResult(localScale, m_childId, &TransformBus::Events::GetLocalScale); - EXPECT_TRUE(localScale.IsClose(Vector3(1.0f, 1.0f, 1.0f))); + float localScale = FLT_MAX; + TransformBus::EventResult(localScale, m_childId, &TransformBus::Events::GetLocalUniformScale); + EXPECT_NEAR(localScale, 1.0f, AZ::Constants::Tolerance); } TEST_F(TransformComponentTransformMatrixSetGet, RotateAroundLocalZ_ScaleDoesNotSkewRotation) { - float sx = 42.564f; - float sy = 12.460f; - float sz = 28.692f; - Vector3 expectedScales(sx, sy, sz); - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScale, expectedScales); + float expectedScale = 42.564f; + TransformBus::Event(m_childId, &TransformBus::Events::SetLocalUniformScale, expectedScale); float rz = 1.43f; TransformBus::Event(m_childId, &TransformBus::Events::RotateAroundLocalZ, rz); @@ -572,110 +565,50 @@ namespace UnitTest TEST_F(TransformComponentTransformMatrixSetGet, SetLocalScale_SimpleValues_Set) { - float sx = 42.564f; - float sy = 12.460f; - float sz = 28.692f; - Vector3 expectedScales(sx, sy, sz); - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScale, expectedScales); - - Transform tm ; - TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 scales = tm.GetScale(); - EXPECT_TRUE(scales.IsClose(expectedScales)); - } - - TEST_F(TransformComponentTransformMatrixSetGet, SetLocalScaleX_SimpleValues_Set) - { - float sx = 64.336f; - Transform tm; - TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 expectedScales = tm.GetScale(); - expectedScales.SetX(sx); - - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScaleX, sx); - - TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 scales = tm.GetScale(); - EXPECT_TRUE(scales.IsClose(expectedScales)); - } - - TEST_F(TransformComponentTransformMatrixSetGet, SetLocalScaleY_SimpleValues_Set) - { - float sy = 23.754f; - Transform tm; - TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 expectedScales = tm.GetScale(); - expectedScales.SetY(sy); - - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScaleY, sy); - - TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 scales = tm.GetScale(); - EXPECT_TRUE(scales.IsClose(expectedScales)); - } + float expectedScale = 42.564f; + TransformBus::Event(m_childId, &TransformBus::Events::SetLocalUniformScale, expectedScale); - TEST_F(TransformComponentTransformMatrixSetGet, SetLocalScaleZ_SimpleValues_Set) - { - float sz = 65.140f; Transform tm; TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 expectedScales = tm.GetScale(); - expectedScales.SetZ(sz); - - TransformBus::Event(m_childId, &TransformBus::Events::SetLocalScaleZ, sz); - - TransformBus::EventResult(tm, m_childId, &TransformBus::Events::GetLocalTM); - Vector3 scales = tm.GetScale(); - EXPECT_TRUE(scales.IsClose(expectedScales)); + float scale = tm.GetUniformScale(); + EXPECT_NEAR(scale, expectedScale, AZ::Constants::Tolerance); } TEST_F(TransformComponentTransformMatrixSetGet, GetLocalScale_SimpleValues_Return) { - float sx = 43.463f; - float sy = 346.22f; - float sz = 863.32f; - Vector3 expectedScales(sx, sy, sz); - Transform scaleTM = Transform::CreateScale(expectedScales); + float expectedScale = 43.463f; + Transform scaleTM = Transform::CreateUniformScale(expectedScale); TransformBus::Event(m_childId, &TransformBus::Events::SetLocalTM, scaleTM); - Vector3 scales; - TransformBus::EventResult(scales, m_childId, &TransformBus::Events::GetLocalScale); - EXPECT_TRUE(scales.IsClose(expectedScales)); + float scale; + TransformBus::EventResult(scale, m_childId, &TransformBus::Events::GetLocalUniformScale); + EXPECT_NEAR(scale, expectedScale, AZ::Constants::Tolerance); } TEST_F(TransformComponentTransformMatrixSetGet, GetWorldScale_ChildHasNoScale_ReturnScaleSameAsParent) { - float sx = 43.463f; - float sy = 346.22f; - float sz = 863.32f; - Vector3 expectedScales(sx, sy, sz); - Transform scaleTM = Transform::CreateScale(expectedScales); + float expectedScale = 43.463f; + Transform scaleTM = Transform::CreateUniformScale(expectedScale); TransformBus::Event(m_parentId, &TransformBus::Events::SetLocalTM, scaleTM); - Vector3 scales; - TransformBus::EventResult(scales, m_childId, &TransformBus::Events::GetWorldScale); - EXPECT_TRUE(scales.IsClose(expectedScales)); + float scale = FLT_MAX; + TransformBus::EventResult(scale, m_childId, &TransformBus::Events::GetWorldUniformScale); + EXPECT_NEAR(scale, expectedScale, AZ::Constants::Tolerance); } TEST_F(TransformComponentTransformMatrixSetGet, GetWorldScale_ChildHasScale_ReturnCompoundScale) { - float sx = 4.463f; - float sy = 3.22f; - float sz = 8.32f; - Vector3 parentScales(sx, sy, sz); - Transform parentScaleTM = Transform::CreateScale(parentScales); + float parentScale = 4.463f; + Transform parentScaleTM = Transform::CreateUniformScale(parentScale); TransformBus::Event(m_parentId, &TransformBus::Events::SetLocalTM, parentScaleTM); - float csx = 1.64f; - float csy = 9.35f; - float csz = 1.57f; - Vector3 childScales(csx, csy, csz); - Transform childScaleTM = Transform::CreateScale(childScales); + float childScale = 1.64f; + Transform childScaleTM = Transform::CreateUniformScale(childScale); TransformBus::Event(m_childId, &TransformBus::Events::SetLocalTM, childScaleTM); - Vector3 scales; - TransformBus::EventResult(scales, m_childId, &TransformBus::Events::GetWorldScale); - EXPECT_TRUE(scales.IsClose(parentScales * childScales)); + float scale = FLT_MAX; + TransformBus::EventResult(scale, m_childId, &TransformBus::Events::GetWorldUniformScale); + EXPECT_NEAR(scale, parentScale * childScale, AZ::Constants::Tolerance); } class TransformComponentHierarchy diff --git a/Code/Framework/Tests/frameworktests_files.cmake b/Code/Framework/Tests/frameworktests_files.cmake index 197bcc9fce..3fc84eb788 100644 --- a/Code/Framework/Tests/frameworktests_files.cmake +++ b/Code/Framework/Tests/frameworktests_files.cmake @@ -11,6 +11,7 @@ set(FILES ../AzCore/Tests/Main.cpp + Spawnable/SpawnableEntitiesManagerTests.cpp ArchiveCompressionTests.cpp ArchiveTests.cpp BehaviorEntityTests.cpp diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 28429729e1..edb6655411 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -9,6 +9,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # + +set_property(GLOBAL PROPERTY LAUNCHER_UNIFIED_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # Launcher targets for a project need to be generated when configuring a project. # When building the engine source, this file will be included by LauncherUnified's CMakeLists.txt # When using an installed engine, this file will be included by the FindLauncherGenerator.cmake script @@ -40,28 +42,8 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC # In the monolithic case, we need to register the gem modules, to do so we will generate a StaticModules.inl # file from StaticModules.in - + set_property(GLOBAL APPEND PROPERTY LY_STATIC_MODULE_PROJECTS_NAME ${project_name}) get_property(game_gem_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${project_name}.GameLauncher) - - unset(extern_module_declarations) - unset(module_invocations) - - foreach(game_gem_dependency ${game_gem_dependencies}) - # To match the convention on how gems targets vs gem modules are named, we remove the "Gem::" from prefix - # and remove the ".Static" from the suffix - string(REGEX REPLACE "^Gem::" "Gem_" game_gem_dependency ${game_gem_dependency}) - string(REGEX REPLACE "^Project::" "Project_" game_gem_dependency ${game_gem_dependency}) - # Replace "." with "_" - string(REPLACE "." "_" game_gem_dependency ${game_gem_dependency}) - - string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_${game_gem_dependency}();\n") - string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_${game_gem_dependency}());\n") - - endforeach() - - configure_file(StaticModules.in - ${CMAKE_CURRENT_BINARY_DIR}/${project_name}.GameLauncher/Includes/StaticModules.inl - ) set(game_build_dependencies ${game_gem_dependencies} @@ -70,29 +52,9 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) get_property(server_gem_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${project_name}.ServerLauncher) - - unset(extern_module_declarations) - unset(module_invocations) - - foreach(server_gem_dependency ${server_gem_dependencies}) - # To match the convention on how gems targets vs gem modules are named, we remove the "Gem::" from prefix - # and remove the ".Static" from the suffix - string(REGEX REPLACE "^Gem::" "Gem_" server_gem_dependency ${server_gem_dependency}) - string(REGEX REPLACE "^Project::" "Project_" server_gem_dependency ${server_gem_dependency}) - # Replace "." with "_" - string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency}) - - string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_${server_gem_dependency}();\n") - string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_${server_gem_dependency}());\n") - - endforeach() - - configure_file(StaticModules.in - ${CMAKE_CURRENT_BINARY_DIR}/${project_name}.ServerLauncher/Includes/StaticModules.inl - ) set(server_build_dependencies - ${game_gem_dependencies} + ${server_gem_dependencies} Legacy::CrySystem ) endif() @@ -186,3 +148,63 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC endif() endforeach() + +#! Defer generation of the StaticModules.inl file needed in monolithic builds until after all the CMake targets are known +# This is that the GEM_MODULE target runtime dependencies can be parsed to discover the list of dependent modules +# to load +function(ly_delayed_generate_static_modules_inl) + if(LY_MONOLITHIC_GAME) + get_property(launcher_unified_binary_dir GLOBAL PROPERTY LAUNCHER_UNIFIED_BINARY_DIR) + get_property(project_names GLOBAL PROPERTY LY_STATIC_MODULE_PROJECTS_NAME) + foreach(project_name ${project_names}) + + unset(extern_module_declarations) + unset(module_invocations) + + unset(all_game_gem_dependencies) + ly_get_gem_load_dependencies(all_game_gem_dependencies ${project_name}.GameLauncher) + + foreach(game_gem_dependency ${all_game_gem_dependencies}) + # To match the convention on how gems targets vs gem modules are named, + # we remove the ".Static" from the suffix + # Replace "." with "_" + string(REPLACE "." "_" game_gem_dependency ${game_gem_dependency}) + + string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_Gem_${game_gem_dependency}();\n") + string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_Gem_${game_gem_dependency}());\n") + + endforeach() + + configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/StaticModules.in + ${launcher_unified_binary_dir}/${project_name}.GameLauncher/Includes/StaticModules.inl + ) + + if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) + get_property(server_gem_dependencies GLOBAL PROPERTY LY_STATIC_MODULE_PROJECTS_DEPENDENCIES_${project_name}.ServerLauncher) + + unset(extern_module_declarations) + unset(module_invocations) + + unset(all_server_gem_dependencies) + ly_get_gem_load_dependencies(all_server_gem_dependencies ${project_name}.ServerLauncher) + foreach(server_gem_dependency ${server_gem_dependencies}) + ly_get_gem_load_dependencies(server_gem_load_dependencies ${server_gem_dependency}) + list(APPEND all_server_gem_dependencies ${server_gem_load_dependencies} ${server_gem_dependency}) + endforeach() + foreach(server_gem_dependency ${all_server_gem_dependencies}) + # Replace "." with "_" + string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency}) + + string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_Gem_${server_gem_dependency}();\n") + string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_Gem_${server_gem_dependency}());\n") + + endforeach() + + configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/StaticModules.in + ${launcher_unified_binary_dir}/${project_name}.ServerLauncher/Includes/StaticModules.inl + ) + + endif() + endforeach() + endif() +endfunction() diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp index c8bd157b65..e0ba106875 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp @@ -137,8 +137,20 @@ namespace AzAssetBrowserRequestHandlerPrivate entityName = AZStd::string::format("Entity%d", GetIEditor()->GetObjectManager()->GetObjectCount()); } - AZ::Entity* newEntity = aznew AZ::Entity(entityName.c_str()); - EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequests::AddRequiredComponents, *newEntity); + AZ::EntityId targetEntityId; + EditorRequests::Bus::BroadcastResult(targetEntityId, &EditorRequests::CreateNewEntityAtPosition, worldTransform.GetTranslation(), AZ::EntityId()); + + AZ::Entity* newEntity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(newEntity, &AZ::ComponentApplicationRequests::FindEntity, targetEntityId); + + if (newEntity == nullptr) + { + return; + } + + newEntity->SetName(entityName); + + newEntity->Deactivate(); // Create component. AZ::Component* newComponent = newEntity->CreateComponent(componentTypeId); @@ -151,15 +163,7 @@ namespace AzAssetBrowserRequestHandlerPrivate newEntity->AddComponent(newComponent); } - // Set entity position. - auto* transformComponent = newEntity->FindComponent(); - if (transformComponent) - { - transformComponent->SetWorldTM(worldTransform); - } - - // Add the entity to the editor context, which activates it and creates the sandbox object. - EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequests::AddEditorEntity, newEntity); + newEntity->Activate(); // set asset after components have been activated in AddEditorEntity method if (newComponent) diff --git a/Code/Sandbox/Editor/CMakeLists.txt b/Code/Sandbox/Editor/CMakeLists.txt index c62e05f012..4706a61d08 100644 --- a/Code/Sandbox/Editor/CMakeLists.txt +++ b/Code/Sandbox/Editor/CMakeLists.txt @@ -173,6 +173,7 @@ ly_add_target( RUNTIME_DEPENDENCIES Legacy::CrySystem Legacy::EditorLib + ProjectManager ) ly_add_translations( TARGETS Editor diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 0bbc48d5f9..a0ff5d7eff 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -69,6 +69,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include // AzQtComponents @@ -3105,6 +3106,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam GetIEditor()->GetDocument()->SetPathName(fullyQualifiedLevelName); GetIEditor()->GetGameEngine()->SetLevelPath(levelPath); + if (usePrefabSystemForLevels) + { + auto* service = AZ::Interface::Get(); + if (service) + { + service->CreateNewLevelPrefab(fullyQualifiedLevelName.toUtf8().constData(), DefaultLevelTemplateName); + } + } + if (GetIEditor()->GetDocument()->Save()) { if (!usePrefabSystemForLevels) diff --git a/Code/Sandbox/Editor/CryEdit.h b/Code/Sandbox/Editor/CryEdit.h index 2e71ca6a58..d4c1304b6a 100644 --- a/Code/Sandbox/Editor/CryEdit.h +++ b/Code/Sandbox/Editor/CryEdit.h @@ -358,6 +358,8 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING private: + static inline constexpr const char* DefaultLevelTemplateName = "Prefabs/Default_Level.prefab"; + struct PythonOutputHandler; AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZStd::shared_ptr m_pythonOutputHandler; diff --git a/Code/Sandbox/Editor/CryEditDoc.cpp b/Code/Sandbox/Editor/CryEditDoc.cpp index a6c5f73c7f..747921d401 100644 --- a/Code/Sandbox/Editor/CryEditDoc.cpp +++ b/Code/Sandbox/Editor/CryEditDoc.cpp @@ -58,13 +58,11 @@ #include "LevelFileDialog.h" #include "StatObjBus.h" -// LmbrCentral -#include #include #include -#include // for LmbrCentral::EditorLightComponentRequestBus - +// LmbrCentral +#include // for LmbrCentral::EditorLightComponentRequestBus //#define PROFILE_LOADING_WITH_VTUNE diff --git a/Code/Sandbox/Editor/CryEditPy.cpp b/Code/Sandbox/Editor/CryEditPy.cpp index 135dd9878c..edbeccb04f 100644 --- a/Code/Sandbox/Editor/CryEditPy.cpp +++ b/Code/Sandbox/Editor/CryEditPy.cpp @@ -533,7 +533,7 @@ namespace AzToolsFramework ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); behaviorContext->EnumProperty("SystemConfigPlatform_Pc") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); - behaviorContext->EnumProperty("SystemConfigPlatform_OsxGl") + behaviorContext->EnumProperty("SystemConfigPlatform_Mac") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); behaviorContext->EnumProperty("SystemConfigPlatform_OsxMetal") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index f72801a4d3..ecd11da817 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -53,6 +53,7 @@ // AtomToolsFramework #include +#include // CryCommon #include @@ -75,7 +76,6 @@ #include "EditorPreferencesPageGeneral.h" #include "ViewportManipulatorController.h" #include "LegacyViewportCameraController.h" -#include "ModernViewportCameraController.h" #include "EditorViewportSettings.h" #include "ViewPane.h" @@ -1220,7 +1220,7 @@ void EditorViewportWidget::SetViewportId(int id) { AzFramework::ReloadCameraKeyBindings(); - auto controller = AZStd::make_shared(); + auto controller = AZStd::make_shared(); controller->SetCameraListBuilderCallback([](AzFramework::Cameras& cameras) { auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::CameraFreeLookButton); diff --git a/Code/Sandbox/Editor/Objects/SelectionGroup.cpp b/Code/Sandbox/Editor/Objects/SelectionGroup.cpp index a2c88c7302..aacf531203 100644 --- a/Code/Sandbox/Editor/Objects/SelectionGroup.cpp +++ b/Code/Sandbox/Editor/Objects/SelectionGroup.cpp @@ -482,28 +482,6 @@ void CSelectionGroup::StartScaling() } -void CSelectionGroup::FinishScaling(const Vec3& scale, [[maybe_unused]] int referenceCoordSys) -{ - if (fabs(scale.x - scale.y) < 0.001f && - fabs(scale.y - scale.z) < 0.001f && - fabs(scale.z - scale.x) < 0.001f) - { - return; - } - - for (int i = 0; i < GetFilteredCount(); ++i) - { - CBaseObject* obj = GetFilteredObject(i); - Vec3 OriginalScale; - if (obj->GetUntransformedScale(OriginalScale)) - { - obj->TransformScale(scale); - obj->SetScale(OriginalScale); - } - } -} - - ////////////////////////////////////////////////////////////////////////// void CSelectionGroup::Align() { diff --git a/Code/Sandbox/Editor/Objects/SelectionGroup.h b/Code/Sandbox/Editor/Objects/SelectionGroup.h index 277ff2a492..9f672f28b2 100644 --- a/Code/Sandbox/Editor/Objects/SelectionGroup.h +++ b/Code/Sandbox/Editor/Objects/SelectionGroup.h @@ -103,7 +103,6 @@ public: void StartScaling(); void Scale(const Vec3& scale, int referenceCoordSys); void SetScale(const Vec3& scale, int referenceCoordSys); - void FinishScaling(const Vec3& scale, int referenceCoordSys); //! Align objects in selection to surface normal void Align(); //! Very special method to move contents of a voxel. diff --git a/Code/Sandbox/Editor/RenderViewport.h b/Code/Sandbox/Editor/RenderViewport.h index d70dd59b98..b45c92b1c8 100644 --- a/Code/Sandbox/Editor/RenderViewport.h +++ b/Code/Sandbox/Editor/RenderViewport.h @@ -200,6 +200,7 @@ public: { return {}; } + float DeviceScalingFactor() override { return 1.0f; } // AzToolsFramework::ViewportFreezeRequestBus bool IsViewportInputFrozen() override; diff --git a/Code/Sandbox/Editor/editor_lib_files.cmake b/Code/Sandbox/Editor/editor_lib_files.cmake index 0646fb566e..e1cf18df55 100644 --- a/Code/Sandbox/Editor/editor_lib_files.cmake +++ b/Code/Sandbox/Editor/editor_lib_files.cmake @@ -823,9 +823,6 @@ set(FILES ViewportManipulatorController.h LegacyViewportCameraController.cpp LegacyViewportCameraController.h - ModernViewportCameraController.cpp - ModernViewportCameraController.h - ModernViewportCameraControllerRequestBus.h RenderViewport.cpp RenderViewport.h TopRendererWnd.cpp diff --git a/Code/Sandbox/Editor/res/o3de_editor.ico b/Code/Sandbox/Editor/res/o3de_editor.ico index 0680ceea19..e7b77c35bf 100644 --- a/Code/Sandbox/Editor/res/o3de_editor.ico +++ b/Code/Sandbox/Editor/res/o3de_editor.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a -size 107016 +oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266 +size 106449 diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt index 72d88b06b4..d02c656b8f 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt @@ -38,6 +38,7 @@ ly_add_target( Gem::LmbrCentral AZ::AtomCore Gem::Atom_RPI.Public + Gem::AtomToolsFramework.Static ) ly_add_dependencies(Editor ComponentEntityEditorPlugin) diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index 884e1f9e51..5ff2debe3d 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -66,8 +66,8 @@ #include #include +#include -#include #include "Objects/ComponentEntityObject.h" #include "ISourceControl.h" @@ -1736,9 +1736,9 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework:: const AZ::Transform nextCameraTransform = AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToTarget), aabb.GetCenter()); - SandboxEditor::ModernViewportCameraControllerRequestBus::Event( - viewportContext->GetId(), &SandboxEditor::ModernViewportCameraControllerRequestBus::Events::InterpolateToTransform, - nextCameraTransform); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + viewportContext->GetId(), + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform); } } } diff --git a/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp b/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp index c04f81f50c..ba705295ae 100644 --- a/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp +++ b/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,10 @@ AssetImporterPlugin::AssetImporterPlugin(IEditor* editor) opt.showInMenu = false; // this view pane is used to display scene settings, but the user never opens it directly through the Tools menu opt.saveKeyName = "Scene Settings (PREVIEW)"; // user settings for this pane were originally saved with PREVIEW, so ensure that's how they are loaded as well, even after the PREVIEW is removed from the name AzToolsFramework::RegisterViewPane(m_toolName.c_str(), LyViewPane::CategoryTools, opt); + + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequests::CreateAndAddEntityFromComponentTags, + AZStd::vector({ AZ::SceneAPI::Events::AssetImportRequest::GetAssetImportRequestComponentTag() }), "AssetImportersEntity"); } void AssetImporterPlugin::Release() diff --git a/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg index 81fcdbcf12..ac546d4f39 100644 --- a/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg @@ -3,7 +3,7 @@ "AssetProcessor": { "Settings": { "Platforms": { - "es3": "enabled" + "android": "enabled" } } } diff --git a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp index 4156a6790d..be6cf61c29 100644 --- a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp +++ b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,22 @@ namespace AssetBundler UnitTest::ScopedAllocatorSetupFixture::SetUp(); m_data = AZStd::make_unique(); + AZ::SettingsRegistryInterface* registry = nullptr; + if (!AZ::SettingsRegistry::Get()) + { + AZ::SettingsRegistry::Register(&m_registry); + registry = &m_registry; + } + else + { + registry = AZ::SettingsRegistry::Get(); + } + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + + m_data->m_applicationManager.reset(aznew MockApplicationManagerTest(0, 0)); m_data->m_applicationManager->Start(AzFramework::Application::Descriptor()); @@ -84,6 +101,12 @@ namespace AssetBundler delete m_data->m_localFileIO; AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if(settingsRegistry == &m_registry) + { + AZ::SettingsRegistry::Unregister(settingsRegistry); + } + m_data->m_applicationManager->Stop(); m_data->m_applicationManager.reset(); m_data.reset(); @@ -99,6 +122,7 @@ namespace AssetBundler }; AZStd::unique_ptr m_data; + AZ::SettingsRegistryImpl m_registry; }; TEST_F(ApplicationManagerTest, ValidatePlatformFlags_ReadConfigFiles_OK) @@ -126,7 +150,7 @@ namespace AssetBundler AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName); AzFramework::PlatformFlags hostPlatformFlag = AzFramework::PlatformHelper::GetPlatformFlag(AzToolsFramework::AssetSystem::GetHostAssetPlatform()); - AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ES3 | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag; + AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ANDROID | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag; ASSERT_EQ(platformFlags, expectedFlags); } diff --git a/Code/Tools/AssetBundler/tests/tests_main.cpp b/Code/Tools/AssetBundler/tests/tests_main.cpp index 9e12623b0d..71046a576a 100644 --- a/Code/Tools/AssetBundler/tests/tests_main.cpp +++ b/Code/Tools/AssetBundler/tests/tests_main.cpp @@ -40,14 +40,14 @@ namespace AssetBundler TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFile_OutputBaseNameAndPlatform) { - AZStd::string filePath = "assetInfoFile_osx_gl.xml"; + AZStd::string filePath = "assetInfoFile_mac.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile"); - ASSERT_EQ(platformIdentifier, "osx_gl"); + ASSERT_EQ(platformIdentifier, "mac"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFile_OutputBaseNameAndPlatform) @@ -64,14 +64,14 @@ namespace AssetBundler TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFileWithUnderScoreInFileName_OutputBaseNameAndPlatform) { - AZStd::string filePath = "assetInfoFile_test_osx_gl.xml"; + AZStd::string filePath = "assetInfoFile_test_mac.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile_test"); - ASSERT_EQ(platformIdentifier, "osx_gl"); + ASSERT_EQ(platformIdentifier, "mac"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFileWithUnderScoreInFileName_OutputBaseNameAndPlatform) @@ -97,21 +97,21 @@ namespace AssetBundler { public: void SetUp() override - { - m_data = AZStd::make_unique(); - m_data->m_application.reset(aznew AzToolsFramework::ToolsApplication()); - m_data->m_application.get()->Start(AzFramework::Application::Descriptor()); - - // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash - // in the unit tests. - AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); - + { + AZ::SettingsRegistryInterface* registry = nullptr; if (!AZ::SettingsRegistry::Get()) { - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(m_registry); AZ::SettingsRegistry::Register(&m_registry); + registry = &m_registry; } + else + { + registry = AZ::SettingsRegistry::Get(); + } + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); if (engineRoot.empty()) @@ -119,6 +119,14 @@ namespace AssetBundler GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to locate engine root.\n").c_str()); } + m_data = AZStd::make_unique(); + m_data->m_application.reset(aznew AzToolsFramework::ToolsApplication()); + m_data->m_application.get()->Start(AzFramework::Application::Descriptor()); + + // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // in the unit tests. + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); m_data->m_testEngineRoot = (engineRoot / RelativeTestFolder).LexicallyNormal().String(); @@ -144,14 +152,24 @@ namespace AssetBundler } void TearDown() override { - AZ::IO::FileIOBase::SetInstance(nullptr); - delete m_data->m_localFileIO; - AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); + if (m_data) + { + AZ::IO::FileIOBase::SetInstance(nullptr); + delete m_data->m_localFileIO; + AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); + + m_data->m_gemInfoList.set_capacity(0); + m_data->m_gemSeedFilePairList.set_capacity(0); + m_data->m_application.get()->Stop(); + m_data->m_application.reset(); + } + + if(auto settingsRegistry = AZ::SettingsRegistry::Get(); + settingsRegistry == &m_registry) + { + AZ::SettingsRegistry::Unregister(settingsRegistry); + } - m_data->m_gemInfoList.set_capacity(0); - m_data->m_gemSeedFilePairList.set_capacity(0); - m_data->m_application.get()->Stop(); - m_data->m_application.reset(); } void AddGemData(const char* engineRoot, const char* gemName, bool seedFileExists = true) diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp index c477ec2c9e..4f8b4b9144 100644 --- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp @@ -78,17 +78,17 @@ namespace AssetBuilderSDK { return AssetBuilderSDK::Platform_PC; } - if (azstricmp(newPlatformName, "es3") == 0) + if (azstricmp(newPlatformName, "android") == 0) { - return AssetBuilderSDK::Platform_ES3; + return AssetBuilderSDK::Platform_ANDROID; } if (azstricmp(newPlatformName, "ios") == 0) { return AssetBuilderSDK::Platform_IOS; } - if (azstricmp(newPlatformName, "osx_gl") == 0) + if (azstricmp(newPlatformName, "mac") == 0) { - return AssetBuilderSDK::Platform_OSX; + return AssetBuilderSDK::Platform_MAC; } if (azstricmp(newPlatformName, "provo") == 0) { @@ -115,12 +115,12 @@ namespace AssetBuilderSDK { case AssetBuilderSDK::Platform_PC: return "pc"; - case AssetBuilderSDK::Platform_ES3: - return "es3"; + case AssetBuilderSDK::Platform_ANDROID: + return "android"; case AssetBuilderSDK::Platform_IOS: return "ios"; - case AssetBuilderSDK::Platform_OSX: - return "osx_gl"; + case AssetBuilderSDK::Platform_MAC: + return "mac"; case AssetBuilderSDK::Platform_PROVO: return "provo"; case AssetBuilderSDK::Platform_SALEM: diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h index a11cc9a80d..126ecda2ba 100644 --- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h +++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h @@ -148,15 +148,15 @@ namespace AssetBuilderSDK { Platform_NONE = 0x00, Platform_PC = 0x01, - Platform_ES3 = 0x02, + Platform_ANDROID = 0x02, Platform_IOS = 0x04, - Platform_OSX = 0x08, + Platform_MAC = 0x08, Platform_PROVO = 0x20, Platform_SALEM = 0x40, Platform_JASPER = 0x80, //! if you add a new platform entry to this enum, you must add it to allplatforms as well otherwise that platform would not be considered valid. - AllPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER + AllPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER }; #endif // defined(ENABLE_LEGACY_PLATFORMFLAGS_SUPPORT) //! Map data structure to holder parameters that are passed into a job for ProcessJob requests. @@ -503,7 +503,7 @@ namespace AssetBuilderSDK AZ_CLASS_ALLOCATOR(PlatformInfo, AZ::SystemAllocator, 0); AZ_TYPE_INFO(PlatformInfo, "{F7DA39A5-C319-4552-954B-3479E2454D3F}"); - AZStd::string m_identifier; ///< like "pc" or "es3" or "ios"... + AZStd::string m_identifier; ///< like "pc" or "android" or "ios"... AZStd::unordered_set m_tags; ///< The tags like "console" or "tools" on that platform PlatformInfo() = default; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp index 196f6b0543..8bc6d0b6f7 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp @@ -655,6 +655,80 @@ namespace AssetProcessor return true; } + bool AssetCatalog::GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFolder) + { + QString normalizedSourcePath = AssetUtilities::NormalizeFilePath(sourcePath.c_str()); + QDir inputPath(normalizedSourcePath); + QString scanFolder; + QString relativeName; + + bool validResult = false; + + AZ_TracePrintf(AssetProcessor::DebugChannel, "ProcessGenerateRelativeSourcePathRequest: %s...\n", sourcePath.c_str()); + + if (sourcePath.empty()) + { + // For an empty input path, do nothing, we'll return an empty, invalid result. + // (We check fullPath instead of inputPath, because an empty fullPath actually produces "." for inputPath) + } + else if (inputPath.isAbsolute()) + { + // For an absolute path, try to convert it to a relative path, based on the existing scan folders. + // To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed. + validResult = m_platformConfig->ConvertToRelativePath(inputPath.absolutePath(), relativeName, scanFolder); + } + else if (inputPath.isRelative()) + { + // For a relative path, concatenate it with each scan folder, and see if a valid relative path emerges. + int scanFolders = m_platformConfig->GetScanFolderCount(); + for (int scanIdx = 0; scanIdx < scanFolders; scanIdx++) + { + auto& scanInfo = m_platformConfig->GetScanFolderAt(scanIdx); + QDir possibleRoot(scanInfo.ScanPath()); + QDir possibleAbsolutePath = possibleRoot.filePath(normalizedSourcePath); + // To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed. + if (m_platformConfig->ConvertToRelativePath(possibleAbsolutePath.absolutePath(), relativeName, scanFolder)) + { + validResult = true; + break; + } + } + } + + // The input has produced a valid relative path. However, the path might match multiple nested scan folders, + // so look to see if a higher-priority folder has a better match. + if (validResult) + { + QString overridingFile = m_platformConfig->GetOverridingFile(relativeName, scanFolder); + + if (!overridingFile.isEmpty()) + { + overridingFile = AssetUtilities::NormalizeFilePath(overridingFile); + validResult = m_platformConfig->ConvertToRelativePath(overridingFile, relativeName, scanFolder); + } + } + + if (!validResult) + { + // if we are here it means we have failed to determine the relativePath, so we will send back the original path + AZ_TracePrintf(AssetProcessor::DebugChannel, + "GenerateRelativeSourcePath found no valid result, returning original path: %s...\n", sourcePath.c_str()); + + rootFolder.clear(); + relativePath.clear(); + relativePath = sourcePath; + return false; + } + + relativePath = relativeName.toUtf8().data(); + rootFolder = scanFolder.toUtf8().data(); + + AZ_Assert(!relativePath.empty(), "ConvertToRelativePath returned true, but relativePath is empty"); + + return true; + } + bool AssetCatalog::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) { ProcessGetFullSourcePathFromRelativeProductPathRequest(relPath, fullSourcePath); diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h index f515fa3658..13dc7892b1 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h @@ -95,6 +95,12 @@ namespace AssetProcessor const char* GetAbsoluteDevGameFolderPath() override; const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override; + + //! Given a partial or full source file path, respond with its relative path and the watch folder it is relative to. + //! The input source path does not need to exist, so this can be used for new files that haven't been saved yet. + bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override; + bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) override; bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override; bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp index 97b28691dc..2d22d3d136 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp @@ -104,6 +104,27 @@ namespace return GetRelativeProductPathFromFullSourceOrProductPathResponse(relPathFound, relProductPath); } + GenerateRelativeSourcePathResponse HandleGenerateRelativeSourcePathRequest( + MessageData messageData) + { + bool relPathFound = false; + AZStd::string relPath; + AZStd::string watchFolder; + + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + relPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GenerateRelativeSourcePath, + messageData.m_message->m_sourcePath, relPath, watchFolder); + + if (!relPathFound) + { + AZ_TracePrintf( + AssetProcessor::ConsoleChannel, "Could not find relative source path for the source file (%s).", + messageData.m_message->m_sourcePath.c_str()); + } + + return GenerateRelativeSourcePathResponse(relPathFound, relPath, watchFolder); + } + SourceAssetInfoResponse HandleSourceAssetInfoRequest(MessageData messageData) { SourceAssetInfoResponse response; @@ -407,6 +428,7 @@ AssetRequestHandler::AssetRequestHandler() m_requestRouter.RegisterMessageHandler(&HandleGetFullSourcePathFromRelativeProductPathRequest); m_requestRouter.RegisterMessageHandler(&HandleGetRelativeProductPathFromFullSourceOrProductPathRequest); + m_requestRouter.RegisterMessageHandler(&HandleGenerateRelativeSourcePathRequest); m_requestRouter.RegisterMessageHandler(&HandleSourceAssetInfoRequest); m_requestRouter.RegisterMessageHandler(&HandleSourceAssetProductsInfoRequest); m_requestRouter.RegisterMessageHandler(&HandleGetScanFoldersRequest); diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h index 88f74c886b..3dc8bd7a00 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h @@ -57,6 +57,9 @@ namespace AzFramework class GetRelativeProductPathFromFullSourceOrProductPathRequest; class GetRelativeProductPathFromFullSourceOrProductPathResponse; + class GenerateRelativeSourcePathRequest; + class GenerateRelativeSourcePathResponse; + class GetFullSourcePathFromRelativeProductPathRequest; class GetFullSourcePathFromRelativeProductPathResponse; class AssetNotificationMessage; @@ -104,6 +107,8 @@ namespace AssetProcessor using GetAbsoluteAssetDatabaseLocationResponse = AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationResponse; using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest; using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse; + using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest; + using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse; using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest; using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse; diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index 523e39d622..3d7cc3b8b4 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -291,7 +291,6 @@ namespace AssetProcessor } } - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(registry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, platform, specialization, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, platform, specialization, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, platform, specialization, &scratchBuffer); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp index e92e9afba5..303f0ad0e8 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp @@ -130,6 +130,9 @@ namespace AssetProcessor auto cacheRootKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_cache_path"; settingsRegistry->Set(cacheRootKey, m_data->m_temporarySourceDir.absoluteFilePath("Cache").toUtf8().constData()); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + settingsRegistry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); AssetUtilities::ComputeProjectCacheRoot(m_data->m_cacheRootDir); QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(m_data->m_cacheRootDir.absolutePath()); @@ -221,20 +224,28 @@ namespace AssetProcessor dbConn->SetScanFolder(newScanFolder); } - // build some default configs. - void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config) + virtual void AddScanFolders( + const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config, + const AZStd::vector& platforms) { - config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true); - config.EnablePlatform({ "es3" ,{ "mobile", "renderer" } }, true); - config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false); - AZStd::vector platforms; - config.PopulatePlatformsForScanFolder(platforms); // PATH DisplayName PortKey root recurse platforms order AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder4"), "subfolder4", "subfolder4", false, false, platforms, -6), config, dbConn); // subfolder 4 overrides subfolder3 AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder3"), "subfolder3", "subfolder3", false, false, platforms, -5), config, dbConn); // subfolder 3 overrides subfolder2 AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder2"), "subfolder2", "subfolder2", false, true, platforms, -2), config, dbConn); // subfolder 2 overrides subfolder1 AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder1"), "subfolder1", "subfolder1", false, true, platforms, -1), config, dbConn); // subfolder1 overrides root AddScanFolder(ScanFolderInfo(tempPath.absolutePath(), "temp", "tempfolder", true, false, platforms, 0), config, dbConn); // add the root + } + + // build some default configs. + void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config) + { + config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true); + config.EnablePlatform({ "android" ,{ "mobile", "renderer" } }, true); + config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false); + AZStd::vector platforms; + config.PopulatePlatformsForScanFolder(platforms); + + AddScanFolders(tempPath, dbConn, config, platforms); config.AddMetaDataType("exportsettings", QString()); @@ -243,22 +254,22 @@ namespace AssetProcessor AssetRecognizer rec; AssetPlatformSpec specpc; - AssetPlatformSpec speces3; + AssetPlatformSpec specandroid; - speces3.m_extraRCParams = "somerandomparam"; + specandroid.m_extraRCParams = "somerandomparam"; rec.m_name = "random files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); config.AddRecognizer(rec); specpc.m_extraRCParams = ""; // blank must work - speces3.m_extraRCParams = "testextraparams"; + specandroid.m_extraRCParams = "testextraparams"; const char* builderTxt1Name = "txt files"; rec.m_name = builderTxt1Name; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); @@ -269,7 +280,7 @@ namespace AssetProcessor ignore_rec.m_name = "ignore files"; ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard); ignore_rec.m_platformSpecs.insert("pc", specpc); - ignore_rec.m_platformSpecs.insert("es3", ignore_spec); + ignore_rec.m_platformSpecs.insert("android", ignore_spec); config.AddRecognizer(ignore_rec); ExcludeAssetRecognizer excludeRecogniser; @@ -356,7 +367,8 @@ namespace AssetProcessor return false; } - // Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of the expected results + // Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of + // the expected results bool TestGetFullSourcePath(const QString& fileToCheck, const QDir& tempPath, bool expectToFind, const char* expectedPath) { bool fullPathfound = false; @@ -528,6 +540,177 @@ namespace AssetProcessor ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" })); } + class AssetCatalogTestRelativeSourcePath : public AssetCatalogTest + { + public: + QDir GetRoot() + { + // Return an OS-friendly absolute root directory for our tests ("C:/sourceRoot" or "/sourceRoot"). It doesn't + // need to exist, it just needs to be an absolute path. + return QDir::root().filePath("sourceRoot"); + } + + // Set up custom scan folders for the "relative source path" tests, so that we can try out specific combinations of watch folders + void AddScanFolders( + [[maybe_unused]] const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config, + const AZStd::vector& platforms) override + { + QDir root = GetRoot(); + + // This will set up the following watch folders, in highest to lowest priority: + + // /sourceRoot/recurseNested/nested (recurse) + // /sourceRoot/noRecurse (no recurse) + // /sourceRoot/recurseNotNested (recurse) + // /sourceRoot/recurseNested (recurse) + + AddScanFolder( + ScanFolderInfo(root.filePath("recurseNested/nested"), "nested", "nested", false, true, platforms, -4), config, dbConn); + AddScanFolder( + ScanFolderInfo(root.filePath("noRecurse"), "noRecurse", "noRecurse", false, false, platforms, -3), config, dbConn); + AddScanFolder( + ScanFolderInfo(root.filePath("recurseNotNested"), "recurseNotNested", "recurseNotNested", false, true, platforms, -2), + config, dbConn); + AddScanFolder( + ScanFolderInfo(root.filePath("recurseNested"), "recurseNested", "recurseNested", false, true, platforms, -1), + config, dbConn); + } + + // Calls the GenerateRelativeSourcePath function and validates that the results match the expected inputs. + void TestGetRelativeSourcePath( + const AZStd::string& sourcePath, bool expectedToFind, const AZStd::string& expectedPath, const AZStd::string& expectedRoot) + { + bool relPathFound = false; + AZStd::string relPath; + AZStd::string rootFolder; + + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + relPathFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GenerateRelativeSourcePath, sourcePath, + relPath, rootFolder); + + EXPECT_EQ(relPathFound, expectedToFind); + EXPECT_EQ(relPath, expectedPath); + EXPECT_EQ(rootFolder, expectedRoot); + } + }; + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_EmptySourcePath_ReturnsNoMatch) + { + // Test passes in an empty source path, which shouldn't produce a valid result. + // Input: empty source path + // Output: empty, not found result + TestGetRelativeSourcePath("", false, "", ""); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathOutsideWatchFolders_ReturnsNoMatch) + { + // Test passes in an invalid absolute source path, which shouldn't produce a valid result. + // Input: "/sourceRoot/noWatchFolder/test.txt" + // Output: not found result, which also returns the input as the relative file name + QDir watchFolder = GetRoot().filePath("noWatchFolder/"); + QString fileToCheck = watchFolder.filePath("test.txt"); + + TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), false, fileToCheck.toUtf8().constData(), ""); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathUnderWatchFolder_ReturnsRelativePath) + { + // Test passes in a valid absolute source path, which should produce a valid relative path + // Input: "/sourceRoot/noRecurse/test.txt" + // Output: "test.txt" in folder "/sourceRoot/noRecurse/" + QDir watchFolder = GetRoot().filePath("noRecurse/"); + QString fileToCheck = watchFolder.filePath("test.txt"); + + TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathUnderNestedWatchFolders_ReturnsRelativePath) + { + // Test passes in a valid absolute source path that matches a watch folder and a nested watch folder. + // The output relative path should match the nested folder, because the nested folder has a higher priority registered with the AP. + // Input: "/sourceRoot/recurseNested/nested/test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested/nested/" + QDir watchFolder = GetRoot().filePath("recurseNested/nested/"); + QString fileToCheck = watchFolder.filePath("test.txt"); + + TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_BareFileNameValidInWatchFolder_ReturnsHighestPriorityWatchFolder) + { + // Test passes in a simple file name. The output should be relative to the highest-priority watch folder. + // Input: "test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested/nested/" + QDir watchFolder = GetRoot().filePath("recurseNested/nested/"); + + TestGetRelativeSourcePath("test.txt", true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathValidInWatchFolder_ReturnsHighestPriorityWatchFolder) + { + // Test passes in a relative path. The output should preserve the relative path, but list it as relative to the highest-priority + // watch folder. + // Input: "a/b/c/test.txt" + // Output: "a/b/c/test.txt" in folder "/sourceRoot/recurseNested/nested/" + QDir watchFolder = GetRoot().filePath("recurseNested/nested/"); + + TestGetRelativeSourcePath("a/b/c/test.txt", true, "a/b/c/test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathNotInWatchFolder_ReturnsNoMatch) + { + // Test passes in a relative path that "backs up" two directories. This will be invalid, because no matter which watch directory + // we start at, the result will be outside of any watch directory. + // Input: "../../test.txt" + // Output: not found result, which also returns the input as the relative file name + TestGetRelativeSourcePath("../../test.txt", false, "../../test.txt", ""); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathValidFromNestedWatchFolder_ReturnsOuterFolder) + { + // Test passes in a relative path that "backs up" one directory. This will produce a valid result, because we can back up from + // the "recurseNested/nested/" watch folder to "recurseNested", which is also a valid watch folder. + // Input: "../test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested" + QDir watchFolder = GetRoot().filePath("recurseNested/"); + TestGetRelativeSourcePath("../test.txt", true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathMovesToParentWatchFolder_ReturnsOuterFolder) + { + // Test passes in a relative path that backs up one directory and then forward into a directory. This will produce a valid + // result, because it can validly start in the highest-priority watch folder (recurseNested/nested), move back one into the + // outer watch folder (recurseNested), and then have a subdirectory within it. + // Note that it would also be valid to move from recurseNested to recurseNotNested, but that won't be the result of this test + // because that's a lower-priority match. + // Input: "../recurseNotNested/test.txt" + // Output: "recurseNotNested/test.txt" in folder "/sourceRoot/recurseNested/" + QDir watchFolder = GetRoot().filePath("recurseNested/"); + + TestGetRelativeSourcePath("../recurseNotNested/test.txt", true, "recurseNotNested/test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathMovesToSiblingWatchFolder_ReturnsSiblingFolder) + { + // Test passes in a relative path that backs up two directories and then forward into a directory. This will produce a valid + // result, because it can validly start in the recurseNested/nested folder, move back two folders, then forward into the sibling + // recurseNotNested folder. The result will be a relative path to the sibling folder. + // Input: "../../recurseNotNested/test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNotNested/" + QDir watchFolder = GetRoot().filePath("recurseNotNested/"); + + TestGetRelativeSourcePath("../../recurseNotNested/test.txt", true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathBacksOutOfWatchFolder_ReturnsNoMatch) + { + // Test passes in a relative path that adds a directory, then "backs up" three directories. This will be invalid, because no + // matter which watch directory we start at, the result will be outside of any watch directory. + // Input: "../test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested" + TestGetRelativeSourcePath("a/../../../test.txt", false, "a/../../../test.txt", ""); + } + class AssetCatalogTest_GetFullSourcePath : public AssetCatalogTest { @@ -909,7 +1092,7 @@ namespace AssetProcessor { AssetCatalogTest::SetUp(); m_platforms.push_back("pc"); - m_platforms.push_back("es3"); + m_platforms.push_back("android"); // 4 products for one platform, 1 product for the other. m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefilez.arc2"); @@ -917,7 +1100,7 @@ namespace AssetProcessor m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefile.arc2"); m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefile.azm2"); - m_platformToProductsForSourceWithDifferentProducts["es3"].push_back("subfolder3/es3exclusivefile.azm2"); + m_platformToProductsForSourceWithDifferentProducts["android"].push_back("subfolder3/androidexclusivefile.azm2"); m_sourceFileWithDifferentProductsPerPlatform = AZ::Uuid::CreateString("{38032FC9-2838-4D6A-9DA0-79E5E4F20C1B}"); m_sourceFileWithDependency = AZ::Uuid::CreateString("{807C4174-1D19-42AD-B8BC-A59291D9388C}"); @@ -930,7 +1113,7 @@ namespace AssetProcessor // resulting in image processing jobs having different products per platform. Because of this, the material jobs will then have different // dependencies per platform, because each material will depend on a referenced texture and all of that texture's mipmaps. - // Add a source file with 4 products on pc, but 1 on es3 + // Add a source file with 4 products on pc, but 1 on android bool result = AddSourceAndJobForMultiplePlatforms( "subfolder3", "MultiplatformFile.txt", @@ -945,7 +1128,7 @@ namespace AssetProcessor result = AddSourceAndJobForMultiplePlatforms("subfolder3", "FileWithDependency.txt", &(m_data->m_dbConn), sourceFileWithSameProductsJobsPerPlatform, m_platforms, m_sourceFileWithDependency); EXPECT_TRUE(result); - const AZStd::string fileWithDependencyProductPath = "subfolder3/es3exclusivefile.azm2"; + const AZStd::string fileWithDependencyProductPath = "subfolder3/androidexclusivefile.azm2"; for (const AZStd::string& platform : m_platforms) { diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index c33943f9a7..04949c82a9 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -265,6 +265,9 @@ namespace AssetProcessorMessagesTests addPairFunc(new GetFullSourcePathFromRelativeProductPathRequest(), new GetFullSourcePathFromRelativeProductPathResponse()); addPairFunc(new GetRelativeProductPathFromFullSourceOrProductPathRequest(), new GetRelativeProductPathFromFullSourceOrProductPathResponse()); + addPairFunc( + new GenerateRelativeSourcePathRequest(), + new GenerateRelativeSourcePathResponse()); addPairFunc(new SourceAssetInfoRequest(), new SourceAssetInfoResponse()); addPairFunc(new SourceAssetProductsInfoRequest(), new SourceAssetProductsInfoResponse()); addPairFunc(new GetScanFoldersRequest(), new GetScanFoldersResponse()); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp index d04e13aef2..7b88321ad9 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp @@ -12,7 +12,7 @@ #include "AssetProcessorTest.h" - +#include #include #include "BaseAssetProcessorTest.h" @@ -67,6 +67,12 @@ namespace AssetProcessor static char processName[] = {"AssetProcessorBatch"}; static char* namePtr = &processName[0]; static char** paramStringArray = &namePtr; + + auto registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_application.reset(new UnitTestAppManager(&numParams, ¶mStringArray)); ASSERT_EQ(m_application->BeforeRun(), ApplicationManager::Status_Success); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h index c866a933dd..4ea0695f1c 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include // for the assert absorber. @@ -44,7 +45,18 @@ namespace AssetProcessor AZ::AllocatorInstance::Create(); } m_errorAbsorber = new UnitTestUtils::AssertAbsorber(); + m_application = AZStd::make_unique(); + + // Inject the AutomatedTesting project as a project path into test fixture + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); + } } void TearDown() override diff --git a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp index c2931c6a09..671d8d96a9 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp @@ -28,7 +28,7 @@ namespace AssetProcessor createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformsCount(), 2); @@ -48,19 +48,19 @@ namespace AssetProcessor ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { - { "es3", {} + { "android", {} } }; - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { @@ -72,24 +72,24 @@ namespace AssetProcessor createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} }, { "ios", {} - }, { "osx_gl", {} + }, { "mac", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_IOS); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(3), AssetBuilderSDK::Platform_OSX); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(3), AssetBuilderSDK::Platform_MAC); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(4), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_NONE); // using a deprecated API should have generated warnings. // but we can't test for it because these warnings are WarningOnce and some other unit test might have already triggered it @@ -106,23 +106,23 @@ namespace AssetProcessor } }; ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC)); - ASSERT_FALSE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3)); + ASSERT_FALSE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID)); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC)); - ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3)); + ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID)); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC)); - ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3)); + ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID)); // using a deprecated API should have generated warnings. // but we can't test for it because these warnings are WarningOnce and some other unit test might have already triggered it } @@ -133,9 +133,9 @@ namespace AssetProcessor UnitTestUtils::AssertAbsorber absorb; ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_PC)); - ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_ES3)); + ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_ANDROID)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_IOS)); - ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_OSX)); + ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_MAC)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_PROVO)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_SALEM)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_JASPER)); diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index 07d1e48229..bd99cf7a94 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -204,6 +204,9 @@ void AssetProcessorManagerTest::SetUp() auto cacheRootKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_cache_path"; registry->Set(cacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().constData()); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_data->m_databaseLocationListener.BusConnect(); @@ -4017,15 +4020,15 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor m_assetProcessorManager->m_allowModtimeSkippingFeature = true; AssetUtilities::SetUseFileHashOverride(true, true); - // Enable es3 platform after the initial SetUp has already processed the files for pc + // Enable android platform after the initial SetUp has already processed the files for pc QDir tempPath(m_tempDir.path()); - AssetBuilderSDK::PlatformInfo es3Platform("es3", { "host", "renderer" }); - m_config->EnablePlatform(es3Platform, true); + AssetBuilderSDK::PlatformInfo androidPlatform("android", { "host", "renderer" }); + m_config->EnablePlatform(androidPlatform, true); // There's no way to remove scanfolders and adding a new one after enabling the platform will cause the pc assets to build as well, which we don't want // Instead we'll just const cast the vector and modify the enabled platforms for the scanfolder auto& platforms = const_cast&>(m_config->GetScanFolderAt(0).GetPlatforms()); - platforms.push_back(es3Platform); + platforms.push_back(androidPlatform); // We need the builder fingerprints to be updated to reflect the newly enabled platform m_assetProcessorManager->ComputeBuilderDirty(); @@ -4033,10 +4036,10 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor QSet filePaths = BuildFileSet(); SimulateAssetScanner(filePaths); - ExpectWork(4, 2); // CreateJobs = 4, 2 files * 2 platforms. ProcessJobs = 2, just the es3 platform jobs (pc is already processed) + ExpectWork(4, 2); // CreateJobs = 4, 2 files * 2 platforms. ProcessJobs = 2, just the android platform jobs (pc is already processed) - ASSERT_TRUE(m_data->m_processResults[0].m_destinationPath.contains("es3")); - ASSERT_TRUE(m_data->m_processResults[1].m_destinationPath.contains("es3")); + ASSERT_TRUE(m_data->m_processResults[0].m_destinationPath.contains("android")); + ASSERT_TRUE(m_data->m_processResults[1].m_destinationPath.contains("android")); } TEST_F(ModtimeScanningTest, ModtimeSkipping_ModifyTimestamp) diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 31e4996b1e..9c80b267eb 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -39,7 +39,6 @@ void PlatformConfigurationUnitTests::SetUp() m_qApp = new QCoreApplication(m_argc, m_argv); AssetProcessorTest::SetUp(); AssetUtilities::ResetAssetRoot(); - } void PlatformConfigurationUnitTests::TearDown() @@ -121,14 +120,14 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Regular_Platforms) // verify the data. ASSERT_NE(config.GetPlatformByIdentifier(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), nullptr); - ASSERT_NE(config.GetPlatformByIdentifier("es3"), nullptr); + ASSERT_NE(config.GetPlatformByIdentifier("android"), nullptr); ASSERT_NE(config.GetPlatformByIdentifier("server"), nullptr); - ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("mobile")); - ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("renderer")); - ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("android")); + ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("mobile")); + ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("renderer")); + ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("android")); ASSERT_TRUE(config.GetPlatformByIdentifier("server")->HasTag("server")); - ASSERT_FALSE(config.GetPlatformByIdentifier("es3")->HasTag("server")); + ASSERT_FALSE(config.GetPlatformByIdentifier("android")->HasTag("server")); ASSERT_FALSE(config.GetPlatformByIdentifier("server")->HasTag("renderer")); } @@ -398,7 +397,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP AZStd::vector platforms = config.GetScanFolderAt(0).GetPlatforms(); ASSERT_EQ(platforms.size(), 4); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo(AzToolsFramework::AssetSystem::GetHostAssetPlatform(), AZStd::unordered_set{})) != platforms.end()); - ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set{})) != platforms.end()); + ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set{})) != platforms.end()); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("ios", AZStd::unordered_set{})) != platforms.end()); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("server", AZStd::unordered_set{})) != platforms.end()); @@ -406,12 +405,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP platforms = config.GetScanFolderAt(1).GetPlatforms(); ASSERT_EQ(platforms.size(), 2); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo(AzToolsFramework::AssetSystem::GetHostAssetPlatform(), AZStd::unordered_set{})) != platforms.end()); - ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set{})) != platforms.end()); + ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set{})) != platforms.end()); ASSERT_EQ(config.GetScanFolderAt(2).GetDisplayName(), QString("folder1output")); platforms = config.GetScanFolderAt(2).GetPlatforms(); ASSERT_EQ(platforms.size(), 1); - ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set{})) != platforms.end()); + ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set{})) != platforms.end()); ASSERT_EQ(config.GetScanFolderAt(3).GetDisplayName(), QString("folder2output")); platforms = config.GetScanFolderAt(3).GetPlatforms(); @@ -455,7 +454,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) using namespace AzToolsFramework::AssetSystem; using namespace AssetProcessor; #if defined(AZ_PLATFORM_WINDOWS) - const char* platformWhichIsNotCurrentPlatform = "osx_gl"; + const char* platformWhichIsNotCurrentPlatform = "mac"; #else const char* platformWhichIsNotCurrentPlatform = "pc"; #endif @@ -476,27 +475,27 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_pattern, "*.i_caf"); ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_type, AssetBuilderSDK::AssetBuilderPattern::Wildcard); ASSERT_EQ(recogs["i_caf"].m_platformSpecs.size(), 2); - ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_FALSE(recogs["i_caf"].m_platformSpecs.contains("server")); // server has been set to skip. - ASSERT_EQ(recogs["i_caf"].m_platformSpecs["es3"].m_extraRCParams, "mobile"); + ASSERT_EQ(recogs["i_caf"].m_platformSpecs["android"].m_extraRCParams, "mobile"); ASSERT_EQ(recogs["i_caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "defaultparams"); ASSERT_TRUE(recogs.contains("caf")); - ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("server")); ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_EQ(recogs["caf"].m_platformSpecs.size(), 3); - ASSERT_EQ(recogs["caf"].m_platformSpecs["es3"].m_extraRCParams, "rendererparams"); + ASSERT_EQ(recogs["caf"].m_platformSpecs["android"].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["caf"].m_platformSpecs["server"].m_extraRCParams, "copy"); ASSERT_TRUE(recogs.contains("mov")); - ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("server")); ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_EQ(recogs["mov"].m_platformSpecs.size(), 3); - ASSERT_EQ(recogs["mov"].m_platformSpecs["es3"].m_extraRCParams, "platformspecificoverride"); + ASSERT_EQ(recogs["mov"].m_platformSpecs["android"].m_extraRCParams, "platformspecificoverride"); ASSERT_EQ(recogs["mov"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["mov"].m_platformSpecs["server"].m_extraRCParams, "copy"); @@ -504,27 +503,27 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) // (but platforms can override it) ASSERT_TRUE(recogs.contains("rend")); ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("server")); ASSERT_FALSE(recogs["rend"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there. ASSERT_EQ(recogs["rend"].m_platformSpecs.size(), 3); ASSERT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); - ASSERT_EQ(recogs["rend"].m_platformSpecs["es3"].m_extraRCParams, "rendererparams"); + ASSERT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string ASSERT_TRUE(recogs.contains("alldefault")); ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("server")); ASSERT_FALSE(recogs["alldefault"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there. ASSERT_EQ(recogs["alldefault"].m_platformSpecs.size(), 3); ASSERT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, ""); - ASSERT_EQ(recogs["alldefault"].m_platformSpecs["es3"].m_extraRCParams, ""); + ASSERT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, ""); ASSERT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, ""); ASSERT_TRUE(recogs.contains("skipallbutone")); ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("es3")); + ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["skipallbutone"].m_platformSpecs.contains("server")); // server is only one enabled (set to copy) ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs.size(), 1); ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy"); @@ -550,7 +549,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides) // verify the data. ASSERT_NE(config.GetPlatformByIdentifier(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), nullptr); - ASSERT_NE(config.GetPlatformByIdentifier("es3"), nullptr); + ASSERT_NE(config.GetPlatformByIdentifier("android"), nullptr); ASSERT_NE(config.GetPlatformByIdentifier("provo"), nullptr); // this override swaps server with provo in that it turns ON provo, turns off server ASSERT_EQ(config.GetPlatformByIdentifier("server"), nullptr); // this should be off due to overrides @@ -567,11 +566,11 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides) ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_pattern, "*.i_caf"); ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_type, AssetBuilderSDK::AssetBuilderPattern::Wildcard); ASSERT_EQ(recogs["i_caf"].m_platformSpecs.size(), 3); - ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("provo")); ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_FALSE(recogs["i_caf"].m_platformSpecs.contains("server")); // server has been set to skip. - ASSERT_EQ(recogs["i_caf"].m_platformSpecs["es3"].m_extraRCParams, "mobile"); + ASSERT_EQ(recogs["i_caf"].m_platformSpecs["android"].m_extraRCParams, "mobile"); ASSERT_EQ(recogs["i_caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "defaultparams"); ASSERT_EQ(recogs["i_caf"].m_platformSpecs["provo"].m_extraRCParams, "copy"); diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h index fe669460a8..0fb67ab947 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h @@ -37,6 +37,5 @@ private: int m_argc; char** m_argv; QCoreApplication* m_qApp; - }; diff --git a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCControllerTest.cpp b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCControllerTest.cpp index b08fbc28d5..1da4703f3d 100644 --- a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCControllerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCControllerTest.cpp @@ -224,7 +224,7 @@ void RCcontrollerTest_Simple::SubmitJob() // This is a regresssion test to ensure the rccontroller can handle multiple jobs for the same file being completed before // the APM has a chance to send OnFinishedProcesssingJob events -TEST_F(RCcontrollerTest_Simple, SameJobIsCompletedMultipleTimes_CompletesWithoutError) +TEST_F(RCcontrollerTest_Simple, DISABLED_SameJobIsCompletedMultipleTimes_CompletesWithoutError) { using namespace AssetProcessor; diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp index 5940caadb7..079cdb7c66 100644 --- a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp @@ -51,6 +51,8 @@ namespace AssetProcessor public: using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest; using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse; + using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest; + using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse; using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest; using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse; }; @@ -88,34 +90,34 @@ namespace AssetProcessor //AZ_TracePrintf("test", "-------------------------\n"); } - void ComputeFingerprints(unsigned int& fingerprintForPC, unsigned int& fingerprintForES3, PlatformConfiguration& config, QString scanFolderPath, QString relPath) + void ComputeFingerprints(unsigned int& fingerprintForPC, unsigned int& fingerprintForANDROID, PlatformConfiguration& config, QString scanFolderPath, QString relPath) { QString extraInfoForPC; - QString extraInfoForES3; + QString extraInfoForANDROID; RecognizerPointerContainer output; QString filePath = scanFolderPath + "/" + relPath; config.GetMatchingRecognizers(filePath, output); for (const AssetRecognizer* assetRecogniser : output) { extraInfoForPC.append(assetRecogniser->m_platformSpecs["pc"].m_extraRCParams); - extraInfoForES3.append(assetRecogniser->m_platformSpecs["es3"].m_extraRCParams); + extraInfoForANDROID.append(assetRecogniser->m_platformSpecs["android"].m_extraRCParams); extraInfoForPC.append(assetRecogniser->m_version); - extraInfoForES3.append(assetRecogniser->m_version); + extraInfoForANDROID.append(assetRecogniser->m_version); } - //Calculating fingerprints for the file for pc and es3 platforms + //Calculating fingerprints for the file for pc and android platforms AZ::Uuid sourceId = AZ::Uuid("{2206A6E0-FDBC-45DE-B6FE-C2FC63020BD5}"); JobEntry jobEntryPC(scanFolderPath, relPath, relPath, 0, { "pc", {"desktop", "renderer"} }, "", 0, 1, sourceId); - JobEntry jobEntryES3(scanFolderPath, relPath, relPath, 0, { "es3", {"mobile", "renderer"} }, "", 0, 2, sourceId); + JobEntry jobEntryANDROID(scanFolderPath, relPath, relPath, 0, { "android", {"mobile", "renderer"} }, "", 0, 2, sourceId); JobDetails jobDetailsPC; jobDetailsPC.m_extraInformationForFingerprinting = extraInfoForPC.toUtf8().constData(); jobDetailsPC.m_jobEntry = jobEntryPC; - JobDetails jobDetailsES3; - jobDetailsES3.m_extraInformationForFingerprinting = extraInfoForES3.toUtf8().constData(); - jobDetailsES3.m_jobEntry = jobEntryES3; + JobDetails jobDetailsANDROID; + jobDetailsANDROID.m_extraInformationForFingerprinting = extraInfoForANDROID.toUtf8().constData(); + jobDetailsANDROID.m_jobEntry = jobEntryANDROID; fingerprintForPC = AssetUtilities::GenerateFingerprint(jobDetailsPC); - fingerprintForES3 = AssetUtilities::GenerateFingerprint(jobDetailsES3); + fingerprintForANDROID = AssetUtilities::GenerateFingerprint(jobDetailsANDROID); } } @@ -240,7 +242,7 @@ namespace AssetProcessor PlatformConfiguration config; config.EnablePlatform({ "pc",{ "desktop", "renderer" } }, true); - config.EnablePlatform({ "es3",{ "mobile", "renderer" } }, true); + config.EnablePlatform({ "android",{ "mobile", "renderer" } }, true); config.EnablePlatform({ "fandago",{ "console", "renderer" } }, false); AZStd::vector platforms; config.PopulatePlatformsForScanFolder(platforms); @@ -259,9 +261,9 @@ namespace AssetProcessor AssetRecognizer rec; AssetPlatformSpec specpc; - AssetPlatformSpec speces3; + AssetPlatformSpec specandroid; - speces3.m_extraRCParams = "somerandomparam"; + specandroid.m_extraRCParams = "somerandomparam"; rec.m_name = "random files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); @@ -269,13 +271,13 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(mockAppManager.RegisterAssetRecognizerAsBuilder(rec)); specpc.m_extraRCParams = ""; // blank must work - speces3.m_extraRCParams = "testextraparams"; + specandroid.m_extraRCParams = "testextraparams"; const char* builderTxt1Name = "txt files"; rec.m_name = builderTxt1Name; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); @@ -305,21 +307,21 @@ namespace AssetProcessor rec.m_testLockSource = false; specpc.m_extraRCParams = "pcparams"; - speces3.m_extraRCParams = "es3params"; + specandroid.m_extraRCParams = "androidparams"; rec.m_name = "xxx files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.xxx", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); mockAppManager.RegisterAssetRecognizerAsBuilder(rec); // two recognizers for the same pattern. rec.m_name = "xxx files 2 (builder2)"; specpc.m_extraRCParams = "pcparams2"; - speces3.m_extraRCParams = "es3params2"; + specandroid.m_extraRCParams = "androidparams2"; rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); mockAppManager.RegisterAssetRecognizerAsBuilder(rec); @@ -330,7 +332,7 @@ namespace AssetProcessor ignore_rec.m_name = "ignore files"; ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard); ignore_rec.m_platformSpecs.insert("pc", specpc); - ignore_rec.m_platformSpecs.insert("es3", ignore_spec); + ignore_rec.m_platformSpecs.insert("android", ignore_spec); config.AddRecognizer(ignore_rec); mockAppManager.RegisterAssetRecognizerAsBuilder(ignore_rec); @@ -432,7 +434,7 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 1); // 1, since we have one recognizer for .ignore, but the 'es3' platform is marked as skip + UNIT_TEST_EXPECT_TRUE(processResults.size() == 1); // 1, since we have one recognizer for .ignore, but the 'android' platform is marked as skip UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc")); @@ -455,16 +457,16 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); - QList es3JobsIndex; + QList androidJobsIndex; QList pcJobsIndex; for (int checkIdx = 0; checkIdx < 4; ++checkIdx) { @@ -662,19 +664,19 @@ namespace AssetProcessor // ---------- test successes ---------- - QStringList es3outs; - es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc1"))); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc2"))); + QStringList androidouts; + androidouts.push_back(cacheRoot.filePath(QString("android/basefile.arc1"))); + androidouts.push_back(cacheRoot.filePath(QString("android/basefile.arc2"))); // feed it the messages its waiting for (create the files) - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[1], "products.")) + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "products.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[1], "products.")) - //Invoke Asset Processed for es3 platform , txt files job description + //Invoke Asset Processed for android platform , txt files job description AssetBuilderSDK::ProcessJobResponse response; response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[1].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[1].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); // make sure legacy SubIds get stored in the DB and in asset response messages. // also make sure they don't get filed for the wrong asset. @@ -693,8 +695,8 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 1); // always RELATIVE, always with the product name. - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); - UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); + UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "android"); UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefile.arc1"); UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_data == "basefile.arc2"); UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged); @@ -793,14 +795,14 @@ namespace AssetProcessor changedInputResults.clear(); assetMessages.clear(); - es3outs.clear(); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.azm"))); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products.")); + androidouts.clear(); + androidouts.push_back(cacheRoot.filePath(QString("android/basefile.azm"))); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "products.")); - //Invoke Asset Processed for es3 platform , txt files2 job description + //Invoke Asset Processed for android platform , txt files2 job description response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); @@ -812,7 +814,7 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 1); // always RELATIVE, always with the product name. - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefile.azm"); changedInputResults.clear(); @@ -1002,11 +1004,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1025,25 +1027,25 @@ namespace AssetProcessor // this time make different products: - QStringList oldes3outs; + QStringList oldandroidouts; QStringList oldpcouts; - oldes3outs = es3outs; + oldandroidouts = androidouts; oldpcouts.append(pcouts); - QStringList es3outs2; + QStringList androidouts2; QStringList pcouts2; - es3outs.clear(); + androidouts.clear(); pcouts.clear(); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefilea.arc1"))); - es3outs2.push_back(cacheRoot.filePath(QString("es3/basefilea.azm"))); - // note that the ES3 outs have changed + androidouts.push_back(cacheRoot.filePath(QString("android/basefilea.arc1"))); + androidouts2.push_back(cacheRoot.filePath(QString("android/basefilea.azm"))); + // note that the android outs have changed // but the pc outs are still the same. pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc1"))); pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm"))); // feed it the messages its waiting for (create the files) - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile.")); QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 50); @@ -1055,12 +1057,12 @@ namespace AssetProcessor response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1083,12 +1085,12 @@ namespace AssetProcessor // The files removed should be the ones we did not emit this time // note that order isn't guarantee but an example output it this - // [0] Removed: ES3, basefile.arc1 - // [1] Removed: ES3, basefile.arc2 - // [2] Changed: ES3, basefilea.arc1 (added) + // [0] Removed: ANDROID, basefile.arc1 + // [1] Removed: ANDROID, basefile.arc2 + // [2] Changed: ANDROID, basefilea.arc1 (added) - // [3] Removed: ES3, basefile.azm - // [4] Changed: ES3, basefilea.azm (added) + // [3] Removed: ANDROID, basefile.azm + // [4] Changed: ANDROID, basefilea.azm (added) // [5] changed: PC, basefile.arc1 (changed) // [6] changed: PC, basefile.azm (changed) @@ -1110,18 +1112,18 @@ namespace AssetProcessor if (element.m_data == "basefilea.arc1") { UNIT_TEST_EXPECT_TRUE(element.m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged); - UNIT_TEST_EXPECT_TRUE(element.m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(element.m_platform == "android"); } if (element.m_data == "basefile.arc2") { UNIT_TEST_EXPECT_TRUE(element.m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetRemoved); - UNIT_TEST_EXPECT_TRUE(element.m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(element.m_platform == "android"); } } // original products must no longer exist since it should have found and deleted them! - for (QString outFile: oldes3outs) + for (QString outFile: oldandroidouts) { UNIT_TEST_EXPECT_FALSE(QFile::exists(outFile)); } @@ -1145,11 +1147,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // pc and es3 + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // pc and android UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1169,12 +1171,12 @@ namespace AssetProcessor response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1205,11 +1207,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1220,12 +1222,12 @@ namespace AssetProcessor response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1243,9 +1245,9 @@ namespace AssetProcessor // deleting the fingerprint file should not have erased the products UNIT_TEST_EXPECT_TRUE(QFile::exists(pcouts[0])); - UNIT_TEST_EXPECT_TRUE(QFile::exists(es3outs[0])); + UNIT_TEST_EXPECT_TRUE(QFile::exists(androidouts[0])); UNIT_TEST_EXPECT_TRUE(QFile::exists(pcouts2[0])); - UNIT_TEST_EXPECT_TRUE(QFile::exists(es3outs2[0])); + UNIT_TEST_EXPECT_TRUE(QFile::exists(androidouts2[0])); changedInputResults.clear(); assetMessages.clear(); @@ -1304,9 +1306,9 @@ namespace AssetProcessor } UNIT_TEST_EXPECT_FALSE(QFile::exists(pcouts[0])); - UNIT_TEST_EXPECT_FALSE(QFile::exists(es3outs[0])); + UNIT_TEST_EXPECT_FALSE(QFile::exists(androidouts[0])); UNIT_TEST_EXPECT_FALSE(QFile::exists(pcouts2[0])); - UNIT_TEST_EXPECT_FALSE(QFile::exists(es3outs2[0])); + UNIT_TEST_EXPECT_FALSE(QFile::exists(androidouts2[0])); changedInputResults.clear(); assetMessages.clear(); @@ -1321,28 +1323,28 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile.")); // send both done messages simultaneously! response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); // send one failure only for PC : @@ -1420,12 +1422,12 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 3); UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 3); - // which should be for the ES3: + // which should be for the ANDROID: UNIT_TEST_EXPECT_TRUE(AssetUtilities::NormalizeFilePath(changedInputResults[0].first) == absolutePath); // always RELATIVE, always with the product name. UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefilea.arc1" || assetMessages[0].m_data == "basefilea.azm"); - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); for (auto& payload : payloadList) { @@ -1526,28 +1528,28 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); - es3outs.clear(); - es3outs2.clear(); + androidouts.clear(); + androidouts2.clear(); pcouts.clear(); pcouts2.clear(); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefilez.arc2"))); - es3outs2.push_back(cacheRoot.filePath(QString("es3/basefileaz.azm2"))); - // note that the ES3 outs have changed + androidouts.push_back(cacheRoot.filePath(QString("android/basefilez.arc2"))); + androidouts2.push_back(cacheRoot.filePath(QString("android/basefileaz.azm2"))); + // note that the android outs have changed // but the pc outs are still the same. pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc2"))); pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm2"))); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile.")); changedInputResults.clear(); assetMessages.clear(); @@ -1555,12 +1557,12 @@ namespace AssetProcessor // send all the done messages simultaneously: response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1620,11 +1622,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1645,9 +1647,9 @@ namespace AssetProcessor absolutePath = watchFolderPath + "/" + relativePathFromWatchFolder; unsigned int fingerprintForPC = 0; - unsigned int fingerprintForES3 = 0; + unsigned int fingerprintForANDROID = 0; - ComputeFingerprints(fingerprintForPC, fingerprintForES3, config, watchFolderPath, relativePathFromWatchFolder); + ComputeFingerprints(fingerprintForPC, fingerprintForANDROID, config, watchFolderPath, relativePathFromWatchFolder); processResults.clear(); QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath)); @@ -1655,11 +1657,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and es3,since we have two recognizer for .xxx file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and android,since we have two recognizer for .xxx file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); @@ -1681,11 +1683,11 @@ namespace AssetProcessor // we never actually submitted any fingerprints or indicated success, so the same number of jobs should occur as before sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and es3,since we have two recognizer for .xxx file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and android,since we have two recognizer for .xxx file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); @@ -1705,7 +1707,7 @@ namespace AssetProcessor // now re-perform the same test, this time only the pc ones should re-appear. // this should happen because we're changing the extra params, which should be part of the fingerprint // if this unit test fails, check to make sure that the extra params are being ingested into the fingerprint computation functions - // and also make sure that the jobs that are for the remaining es3 platform don't change. + // and also make sure that the jobs that are for the remaining android platform don't change. // store the UUID so that we can insert the new one with the same UUID AZStd::shared_ptr builderTxt2Builder; @@ -1743,12 +1745,12 @@ namespace AssetProcessor // --------------------- unsigned int newfingerprintForPC = 0; - unsigned int newfingerprintForES3 = 0; + unsigned int newfingerprintForANDROID = 0; - ComputeFingerprints(newfingerprintForPC, newfingerprintForES3, config, watchFolderPath, relativePathFromWatchFolder); + ComputeFingerprints(newfingerprintForPC, newfingerprintForANDROID, config, watchFolderPath, relativePathFromWatchFolder); UNIT_TEST_EXPECT_TRUE(newfingerprintForPC != fingerprintForPC);//Fingerprints should be different - UNIT_TEST_EXPECT_TRUE(newfingerprintForES3 == fingerprintForES3);//Fingerprints are same + UNIT_TEST_EXPECT_TRUE(newfingerprintForANDROID == fingerprintForANDROID);//Fingerprints are same config.RemoveRecognizer("xxx files 2 (builder2)"); mockAppManager.UnRegisterAssetRecognizerAsBuilder("xxx files 2 (builder2)"); @@ -1763,18 +1765,18 @@ namespace AssetProcessor absolutePath = AssetUtilities::NormalizeFilePath(absolutePath); QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath)); UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000)); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // pc and es3 + UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // pc and android UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier != processResults[1].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); unsigned int newfingerprintForPCAfterVersionChange = 0; - unsigned int newfingerprintForES3AfterVersionChange = 0; + unsigned int newfingerprintForANDROIDAfterVersionChange = 0; - ComputeFingerprints(newfingerprintForPCAfterVersionChange, newfingerprintForES3AfterVersionChange, config, watchFolderPath, relativePathFromWatchFolder); + ComputeFingerprints(newfingerprintForPCAfterVersionChange, newfingerprintForANDROIDAfterVersionChange, config, watchFolderPath, relativePathFromWatchFolder); UNIT_TEST_EXPECT_TRUE((newfingerprintForPCAfterVersionChange != fingerprintForPC) || (newfingerprintForPCAfterVersionChange != newfingerprintForPC));//Fingerprints should be different - UNIT_TEST_EXPECT_TRUE((newfingerprintForES3AfterVersionChange != fingerprintForES3) || (newfingerprintForES3AfterVersionChange != newfingerprintForES3));//Fingerprints should be different + UNIT_TEST_EXPECT_TRUE((newfingerprintForANDROIDAfterVersionChange != fingerprintForANDROID) || (newfingerprintForANDROIDAfterVersionChange != newfingerprintForANDROID));//Fingerprints should be different //------Test for Files which are excluded processResults.clear(); @@ -1919,7 +1921,7 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(processResults.size() == 0); // nothing to process - // we are aware that 4 products went missing (es3 and pc versions of the 2 files since we renamed the SOURCE folder) + // we are aware that 4 products went missing (android and pc versions of the 2 files since we renamed the SOURCE folder) UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 4); for (auto element : assetMessages) { @@ -2178,8 +2180,8 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(assetMessages[2].m_assetId != AZ::Data::AssetId()); UNIT_TEST_EXPECT_TRUE(assetMessages[3].m_assetId != AZ::Data::AssetId()); - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); - UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); + UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "android"); UNIT_TEST_EXPECT_TRUE(assetMessages[2].m_platform == "pc"); UNIT_TEST_EXPECT_TRUE(assetMessages[3].m_platform == "pc"); @@ -2212,12 +2214,12 @@ namespace AssetProcessor mockAppManager.UnRegisterAllBuilders(); AssetRecognizer abt_rec1; - AssetPlatformSpec abt_speces3; + AssetPlatformSpec abt_specandroid; abt_rec1.m_name = "UnitTestTextBuilder1"; abt_rec1.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); //abt_rec1.m_regexp.setPatternSyntax(QRegExp::Wildcard); //abt_rec1.m_regexp.setPattern("*.txt"); - abt_rec1.m_platformSpecs.insert("es3", speces3); + abt_rec1.m_platformSpecs.insert("android", specandroid); mockAppManager.RegisterAssetRecognizerAsBuilder(abt_rec1); AssetRecognizer abt_rec2; @@ -2266,8 +2268,8 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // 1 for pc and es3 - UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"); + UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // 1 for pc and android + UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"); UNIT_TEST_EXPECT_TRUE(processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc"); UNIT_TEST_EXPECT_TRUE(QString::compare(processResults[0].m_jobEntry.GetAbsoluteSourcePath(), absolutePath, Qt::CaseInsensitive) == 0); UNIT_TEST_EXPECT_TRUE(QString::compare(processResults[1].m_jobEntry.GetAbsoluteSourcePath(), absolutePath, Qt::CaseInsensitive) == 0); diff --git a/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp index 29a0570d39..992cf09539 100644 --- a/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp @@ -17,16 +17,16 @@ void ConnectionUnitTest::StartTest() m_testConnection.SetAssetPlatformsString("pc"); AzFramework::AssetSystem::AssetNotificationMessage testMessage; EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0); - m_testConnection.SendPerPlatform(0, testMessage, "osx_gl"); + m_testConnection.SendPerPlatform(0, testMessage, "mac"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1); m_testConnection.SendPerPlatform(0, testMessage, "pc"); - m_testConnection.SetAssetPlatformsString("pc,es3"); + m_testConnection.SetAssetPlatformsString("pc,android"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1); m_testConnection.SendPerPlatform(0, testMessage, "pc"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0); - m_testConnection.SendPerPlatform(0, testMessage, "osx_gl"); + m_testConnection.SendPerPlatform(0, testMessage, "mac"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1); - m_testConnection.SendPerPlatform(0, testMessage, "es3"); + m_testConnection.SendPerPlatform(0, testMessage, "android"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0); // Intended partial string match test - shouldn't send m_testConnection.SendPerPlatform(0, testMessage, "es"); diff --git a/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h b/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h index f0553d5e84..75ffdfe218 100644 --- a/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h +++ b/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h @@ -63,7 +63,7 @@ namespace AssetProcessor size_t SendPerPlatform(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const QString& platform) override { - if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "es3", Qt::CaseInsensitive) == 0) + if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "android", Qt::CaseInsensitive) == 0) { return Send(serial, message); } @@ -72,7 +72,7 @@ namespace AssetProcessor size_t SendRawPerPlatform(unsigned int type, unsigned int serial, const QByteArray& data, const QString& platform) override { - if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "es3", Qt::CaseInsensitive) == 0) + if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "android", Qt::CaseInsensitive) == 0) { return SendRaw(type, serial, data); } diff --git a/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp index e09a6366a1..ff29cf2ca9 100644 --- a/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp @@ -64,7 +64,7 @@ void PlatformConfigurationTests::StartTest() PlatformConfiguration config; config.EnablePlatform({ "pc",{ "desktop", "host" } }, true); - config.EnablePlatform({ "es3",{ "mobile", "android" } }, true); + config.EnablePlatform({ "android",{ "mobile", "android" } }, true); config.EnablePlatform({ "fandago",{ "console" } }, false); AZStd::vector platforms; config.PopulatePlatformsForScanFolder(platforms); @@ -88,15 +88,15 @@ void PlatformConfigurationTests::StartTest() AssetRecognizer rec; AssetPlatformSpec specpc; - AssetPlatformSpec speces3; + AssetPlatformSpec specandroid; AssetPlatformSpec specfandago; specpc.m_extraRCParams = ""; // blank must work - speces3.m_extraRCParams = "testextraparams"; + specandroid.m_extraRCParams = "testextraparams"; rec.m_name = "txt files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); rec.m_platformSpecs.insert("fandago", specfandago); config.AddRecognizer(rec); @@ -111,7 +111,7 @@ void PlatformConfigurationTests::StartTest() UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms().size() == 2); UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[0].m_identifier == "pc"); - UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[1].m_identifier == "es3"); + UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[1].m_identifier == "android"); UNIT_TEST_EXPECT_TRUE(config.GetScanFolderCount() == 11); UNIT_TEST_EXPECT_FALSE(config.GetScanFolderAt(0).IsRoot()); diff --git a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp index 0e771f7fd1..02b98e5e33 100644 --- a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp @@ -239,14 +239,14 @@ void RCcontrollerUnitTests::RunRCControllerTests() createdJobs.push_back(job); } - // double them up for "es3" to make sure that platform is respected + // double them up for "android" to make sure that platform is respected for (QString name : tempJobNames) { AZ::Uuid uuidOfSource = AZ::Uuid::CreateName(name.toUtf8().constData()); RCJob* job0 = new RCJob(rcJobListModel); AssetProcessor::JobDetails jobDetails; jobDetails.m_jobEntry.m_databaseSourceName = jobDetails.m_jobEntry.m_pathRelativeToWatchFolder = name; - jobDetails.m_jobEntry.m_platformInfo = { "es3" ,{ "mobile", "renderer" } }; + jobDetails.m_jobEntry.m_platformInfo = { "android" ,{ "mobile", "renderer" } }; jobDetails.m_jobEntry.m_jobKey = "Compile Other Stuff"; jobDetails.m_jobEntry.m_sourceFileUUID = uuidOfSource; job0->Init(jobDetails); @@ -490,7 +490,7 @@ void RCcontrollerUnitTests::RunRCControllerTests() UNIT_TEST_EXPECT_FALSE(gotJobsInQueueCall); // submit same job but different platform: - details.m_jobEntry = JobEntry("d:/test", "test1.txt", "test1.txt", AZ::Uuid("{7954065D-CFD1-4666-9E4C-3F36F417C7AC}"), { "es3" ,{ "mobile", "renderer" } }, "Test Job", 1234, 3, sourceId); + details.m_jobEntry = JobEntry("d:/test", "test1.txt", "test1.txt", AZ::Uuid("{7954065D-CFD1-4666-9E4C-3F36F417C7AC}"), { "android" ,{ "mobile", "renderer" } }, "Test Job", 1234, 3, sourceId); m_rcController.JobSubmitted(details); QCoreApplication::processEvents(QEventLoop::AllEvents); diff --git a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp index 1e10002b53..650f3120dd 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp @@ -54,7 +54,7 @@ namespace UnitTestUtils { void SleepForMinimumFileSystemTime() { - // note that on OSX, the file system has a resolution of 1 second, and since we're using modtime for a bunch of things, + // note that on Mac, the file system has a resolution of 1 second, and since we're using modtime for a bunch of things, // not the actual hash files, we have to wait different amount depending on the OS. #ifdef AZ_PLATFORM_WINDOWS int milliseconds = 1; diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp index c85801a074..579c7f93d3 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp @@ -622,13 +622,14 @@ bool ApplicationManager::Activate() { if (!AssetUtilities::ComputeAssetRoot(m_systemRoot)) { + AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to compute the asset root for the project, this application cannot launch until this is fixed."); return false; } auto projectName = AssetUtilities::ComputeProjectName(); if (projectName.isEmpty()) { - AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Is bootstrap.cfg appropriately configured?"); + AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Configure your game project name to launch this application."); return false; } diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp index a17819bba2..7c4f429da4 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp @@ -1191,6 +1191,7 @@ bool ApplicationManagerBase::Activate() QDir projectCache; if (!AssetUtilities::ComputeProjectCacheRoot(projectCache)) { + AZ_Error("AssetProcessor", false, "Could not compute project cache root, please configure your project correctly to launch Asset Processor."); return false; } @@ -1200,22 +1201,27 @@ bool ApplicationManagerBase::Activate() // Shutdown if the disk has less than 128MB of free space if (!CheckSufficientDiskSpace(projectCache.absolutePath(), 128 * 1024 * 1024, true)) { + // CheckSufficientDiskSpace reports an error if disk space is low. return false; } bool appInited = InitApplicationServer(); if (!appInited) { + AZ_Error( + "AssetProcessor", false, "InitApplicationServer failed, something internal to Asset Processor has failed, please report this to support if you encounter this error."); return false; } if (!InitAssetDatabase()) { + // AssetDatabaseConnection::OpenDatabase reports any errors it encounters. return false; } if (!ApplicationManager::Activate()) { + // ApplicationManager::Activate() reports any errors it encounters. return false; } @@ -1230,6 +1236,7 @@ bool ApplicationManagerBase::Activate() m_isCurrentlyLoadingGems = true; if (!ActivateModules()) { + // ActivateModules reports any errors it encounters. m_isCurrentlyLoadingGems = false; return false; } @@ -1299,6 +1306,7 @@ bool ApplicationManagerBase::Activate() { if (!m_applicationServer->startListening()) { + // startListening reports any errors it encounters. return false; } } diff --git a/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg index 468ab68f5d..05ed19cb74 100644 --- a/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg @@ -5,11 +5,11 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "Platforms": { - "es3": "enabled" + "android": "enabled" }, "ScanFolder Game": { "watch": "@PROJECTROOT@", diff --git a/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg index 507fe4afb1..23f5725548 100644 --- a/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg @@ -5,7 +5,7 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "RC i_caf": { diff --git a/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg index 0e687062b2..32c0af0593 100644 --- a/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg @@ -5,7 +5,7 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "ScanFolder Game": { diff --git a/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg index 1c5c487a46..c43996f3d5 100644 --- a/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg @@ -5,17 +5,17 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform es3": { + "Platform android": { "tags": "android,mobile,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "Platform server": { "tags": "server" }, "Platforms": { - "es3": "enabled", + "android": "enabled", "server": "enabled" }, "Jobs": { @@ -56,7 +56,7 @@ "glob": "*.i_caf", "params": "defaultparams", "server": "skip", - "es3": "mobile", + "android": "mobile", "priority": 5, "checkServer": true }, @@ -68,7 +68,7 @@ "RC mov": { "glob": "*.mov", "params": "copy", - "es3": "platformspecificoverride", + "android": "platformspecificoverride", "renderer": "rendererparams" }, "RC rend": { diff --git a/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg index e1c2d6e8cc..5fe1071fd5 100644 --- a/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg @@ -5,13 +5,13 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform es3": { + "Platform android": { "tags": "android,mobile,renderer" }, "Platform ios": { "tags": "mobile,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "Platform server": { @@ -21,7 +21,7 @@ "tags": "console,renderer" }, "Platforms": { - "es3": "enabled", + "android": "enabled", "ios": "enabled", "server": "enabled" }, @@ -54,14 +54,14 @@ "display": "folder1output", "recursive": 1, "order": 50000, - "include": "es3" + "include": "android" }, "ScanFolder Folder2": { "watch": "@ENGINEROOT@/Folder2", "display": "folder2output", "recursive": 1, "order": 60000, - "exclude": "es3" + "exclude": "android" }, "ScanFolder Folder3": { "watch": "@ENGINEROOT@/Folder3", @@ -80,7 +80,7 @@ "glob": "*.i_caf", "params": "defaultparams", "server": "skip", - "es3": "mobile", + "android": "mobile", "test": "copy", "priority": 5 }, @@ -92,7 +92,7 @@ "RC mov": { "glob": "*.mov", "params": "copy", - "es3": "platformspecificoverride", + "android": "platformspecificoverride", "renderer": "rendererparams" }, "RC rend": { diff --git a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp index 67d6767a17..887889edcb 100644 --- a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp +++ b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,12 @@ public: protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + AZ::ComponentApplication::Descriptor desc; desc.m_useExistingAllocator = true; desc.m_enableDrilling = false; // we already created a memory driller for the test (AllocatorsFixture) diff --git a/Code/Tools/GridHub/GridHub/gridhub.cpp b/Code/Tools/GridHub/GridHub/gridhub.cpp index 7f85ade238..4b2d8e425b 100644 --- a/Code/Tools/GridHub/GridHub/gridhub.cpp +++ b/Code/Tools/GridHub/GridHub/gridhub.cpp @@ -552,7 +552,7 @@ GridHubComponent::OnMemberJoined([[maybe_unused]] GridMate::GridSession* session switch( member->GetPlatformId() ) { case AZ::PlatformID::PLATFORM_WINDOWS_64: - case AZ::PlatformID::PLATFORM_APPLE_OSX: + case AZ::PlatformID::PLATFORM_APPLE_MAC: { GridMate::string localMachineName = GridMate::Utils::GetMachineAddress(); if( member->GetMachineName() == localMachineName ) diff --git a/Code/Tools/ProjectManager/Resources/AddOffset.svg b/Code/Tools/ProjectManager/Resources/AddOffset.svg new file mode 100644 index 0000000000..4c62234070 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/AddOffset.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg b/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg new file mode 100644 index 0000000000..a0e2a07eda --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ArrowBack.svg b/Code/Tools/ProjectManager/Resources/ArrowBack.svg new file mode 100644 index 0000000000..749bb5a02e --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowBack.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png b/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png new file mode 100644 index 0000000000..cc1eda5bb8 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f82f22df64b93d4bec91e56b60efa3d5ce2915ce388a2dc627f1ab720678e3d5 +size 334987 diff --git a/Code/Tools/ProjectManager/Resources/FolderOffset.svg b/Code/Tools/ProjectManager/Resources/FolderOffset.svg new file mode 100644 index 0000000000..a048fbcc39 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FolderOffset.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg b/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg new file mode 100644 index 0000000000..fb13cd8558 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 1ffd7cf3e7..04d5e98a10 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -4,6 +4,12 @@ Add.svg + AddOffset.svg + AddOffset_Hover.svg + ArrowBack.svg + build.svg + FolderOffset.svg + FolderOffset_Hover.svg Select_Folder.svg o3de_editor.ico Windows.svg @@ -11,6 +17,14 @@ iOS.svg Linux.svg macOS.svg + DefaultProjectImage.png + ArrowDownLine.svg + ArrowUpLine.svg + o3de.svg + menu.svg + menu_hover.svg Backgrounds/FirstTimeBackgroundImage.jpg + ArrowDownLine.svg + ArrowUpLine.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 16ef48ee7c..5eb92964dd 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -1,29 +1,69 @@ /************** General (MainWindow) **************/ QMainWindow { - background-color: #333333; + background:#131313 url(:/o3de.svg) no-repeat top left; + /* position the logo using padding and background-origin, Qt does not support background-position pixels */ + background-origin:content; + padding:25px 16px; + margin:0; } - QPushButton:focus { outline: none; border:1px solid #1e70eb; } +QTabBar { + background-color: transparent; +} +QTabWidget::tab-bar +{ + left: 78px; /* make room for the logo */ +} +QTabBar::tab { + height:82px; + background-color: transparent; + font-size:24px; + min-width:100px; + margin-right:40px; + border-bottom: 3px solid transparent; +} +QTabBar::tab:text +{ + text-align:left; +} +QTabWidget::pane { + background-color: #333333; + border:0 none; +} +QTabBar::tab:selected +{ + border-bottom: 3px solid #1e70eb; + color: #1e70eb; +} +QTabBar::tab:hover +{ + color: #1e70eb; +} +QTabBar::tab:pressed +{ + color: #0e60eb; +} + /************** General (Forms) **************/ #formLineEditWidget, #formBrowseEditWidget { - max-width: 780px; + max-width: 890px; } #formFrame { - max-width: 720px; + max-width: 840px; background-color: #444444; border:1px solid #dddddd; border-radius: 4px; padding: 0px 10px 2px 6px; margin-top:10px; - margin-left:30px; + margin-left:50px; } #formFrame[Focus="true"] { @@ -59,15 +99,241 @@ QPushButton:focus { padding-top: -4px; } + #formErrorLabel { color: #ec3030; font-size: 14px; - margin-left: 40px; + margin-left: 50px; } #formTitleLabel { font-size:21px; color:#ffffff; - margin: 10px 0 10px 30px; + margin: 24px 0 10px 50px; +} + +/************** General (Modal windows) **************/ + +#header { + background-color:#111111; + min-height:80px; + max-height:80px; +} + +#header QPushButton { + /* settings min/max lets us use a fixed size */ + min-width: 24px; + max-width: 24px; + min-height: 24px; + max-height: 24px; + margin: 20px 10px 0px 10px; + background:transparent url(:/ArrowBack.svg) no-repeat center; + background-origin:content; + qproperty-flat: true; + qproperty-iconSize: 50px; +} + +#header QPushButton:focus { + border:none; +} +#header QPushButton:hover { + background:#333333 url(:/ArrowBack.svg) no-repeat center; +} +#header QPushButton:pressed { + background:#222222 url(:/ArrowBack.svg) no-repeat center; +} + +#headerTitle { + font-size:14px; + text-align:left; + margin:0; + padding-top:10px; + padding-bottom:-5px; + min-height:15px; + max-height:15px; +} +#headerSubTitle { + font-size:24px; + text-align:left; + margin:0; + min-height:42px; + max-height:42px; +} + +#body { + background-color:#333333; +} +#footer { + /* settings min/max lets us use a fixed size */ + min-width: 50px; + min-height:54px; + max-height:54px; +} + +#footer > QPushButton { + qproperty-flat: true; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0095f2, stop: 1.0 #1e70eb); + border-radius: 3px; + min-height: 28px; + max-height: 28px; + min-width: 150px; + margin-right:30px; +} +#footer > QPushButton:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #10A5f2, stop: 1.0 #2e80eb); +} +#footer > QPushButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0085e2, stop: 1.0 #0e60db); +} + +#footer > QPushButton[secondary="true"] { + margin-right: 10px; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); +} +#footer > QPushButton[secondary="true"]:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); +} +#footer > QPushButton[secondary="true"]:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); +} + +/************** Project Settings **************/ +#projectSettings { + margin-top:42px; +} + +#projectTemplate { + margin: 55px 0 0 50px; + max-width: 780px; + min-height:200px; + max-height:200px; +} +#projectTemplateLabel { + font-size:16px; + font-weight:100; +} + +#projectTemplateDetailsLabel { + font-size:14px; + min-height:40px; + margin-bottom:20px; +} + +#projectTemplateDetails { + background-color:#444444; + max-width:240px; + min-width:240px; + margin-left:30px; +} + +/************** Projects **************/ +#firstTimeContent > #titleLabel { + font-size:60px; + margin:73px 0px 0px 0px; + qproperty-indent: 0; +} + +#firstTimeContent > #introLabel { + font-size:14px; + margin:10px 0 60px 0; + qproperty-indent: 0; +} + +#firstTimeContent > QPushButton { + min-width: 210px; + max-width: 210px; + min-height: 276px; + max-height: 276px; + qproperty-flat: true; + background-origin:content; + font-size:14px; + border: 1px solid #ffffff; +} + +#firstTimeContent > QPushButton:hover { + border: 1px solid #1e70eb; + color: #1e70eb; +} + +#firstTimeContent > QPushButton:pressed { + border: 1px solid #0e60eb; + color: #0e60eb; +} + +#createProjectButton { + background:rgba(0,0,0,180) url(:/AddOffset.svg) no-repeat center center; +} +#createProjectButton:hover, +#createProjectButton:pressed { + background:rgba(0,0,0,180) url(:/AddOffset_Hover.svg) no-repeat center center; +} + +#addProjectButton { + background:rgba(0,0,0,180) url(:/FolderOffset.svg) no-repeat center center; +} +#addProjectButton:hover, +#addProjectButton:pressed { + background:rgba(0,0,0,180) url(:/FolderOffset_Hover.svg) no-repeat center center; +} + +#projectsContent > QFrame { + margin-top:60px; +} + +#projectsContent > QFrame > #titleLabel { + font-size:24px; + qproperty-indent: 0; +} + +#projectsContent > QScrollArea { + margin-top:40px; + margin-bottom:5px; +} + +#projectButton > #labelButton { + border:1px solid white; +} +#projectButton > #labelButton:hover, +#projectButton > #labelButton:pressed { + border:1px solid #1e70eb; +} + +#projectButton > QFrame { + margin-top:6px; +} + +#projectButton > QFrame > QLabel { + font-weight:bold; + font-size:14px; + qproperty-indent: 0; +} + +#projectMenuButton { + qproperty-flat: true; + background:transparent url(:/menu.svg) no-repeat center center; + max-width:30px; + min-width:30px; + max-height:14px; + min-height:14px; +} + +#projectsContent > QFrame > #newProjectButton { + min-width:150px; + max-width:150px; + min-height:26px; + max-height:26px; } +#labelButtonOverlay { + background-color: rgba(50,50,50,200); + min-width:210px; + max-width:210px;; + min-height:278px; + max-height:278px; +} \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Resources/build.svg b/Code/Tools/ProjectManager/Resources/build.svg new file mode 100644 index 0000000000..b6c3546443 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/build.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Code/Tools/ProjectManager/Resources/menu.svg b/Code/Tools/ProjectManager/Resources/menu.svg new file mode 100644 index 0000000000..a639c74ab4 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/menu_hover.svg b/Code/Tools/ProjectManager/Resources/menu_hover.svg new file mode 100644 index 0000000000..4eea63faca --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/menu_hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/o3de.svg b/Code/Tools/ProjectManager/Resources/o3de.svg new file mode 100644 index 0000000000..bb6e596a00 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/o3de.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/o3de_editor.ico b/Code/Tools/ProjectManager/Resources/o3de_editor.ico index 0680ceea19..e7b77c35bf 100644 --- a/Code/Tools/ProjectManager/Resources/o3de_editor.ico +++ b/Code/Tools/ProjectManager/Resources/o3de_editor.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a -size 107016 +oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266 +size 106449 diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp new file mode 100644 index 0000000000..69f0a3983d --- /dev/null +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -0,0 +1,140 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + CreateProjectCtrl::CreateProjectCtrl(QWidget* parent) + : ScreenWidget(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setContentsMargins(0,0,0,0); + + m_header = new ScreenHeader(this); + m_header->setTitle(tr("Create a New Project")); + m_header->setSubTitle(tr("Enter Project Details")); + connect(m_header->backButton(), &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); + vLayout->addWidget(m_header); + + m_stack = new QStackedWidget(this); + m_stack->setObjectName("body"); + m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding)); + m_stack->addWidget(new NewProjectSettingsScreen()); + m_stack->addWidget(new GemCatalogScreen()); + vLayout->addWidget(m_stack); + + QDialogButtonBox* backNextButtons = new QDialogButtonBox(); + backNextButtons->setObjectName("footer"); + vLayout->addWidget(backNextButtons); + + m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_backButton->setProperty("secondary", true); + m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); + + connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); + connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton); + + Update(); + setLayout(vLayout); + } + + ProjectManagerScreen CreateProjectCtrl::GetScreenEnum() + { + return ProjectManagerScreen::CreateProject; + } + + void CreateProjectCtrl::HandleBackButton() + { + if (m_stack->currentIndex() > 0) + { + m_stack->setCurrentIndex(m_stack->currentIndex() - 1); + Update(); + } + else + { + emit GotoPreviousScreenRequest(); + } + } + void CreateProjectCtrl::HandleNextButton() + { + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); + ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); + + if (screenEnum == ProjectManagerScreen::NewProjectSettings) + { + auto newProjectScreen = reinterpret_cast(currentScreen); + if (newProjectScreen) + { + if (!newProjectScreen->Validate()) + { + QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings")); + return; + } + + m_projectInfo = newProjectScreen->GetProjectInfo(); + m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath(); + } + } + + if (m_stack->currentIndex() != m_stack->count() - 1) + { + m_stack->setCurrentIndex(m_stack->currentIndex() + 1); + Update(); + } + else + { + auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo); + if (result.IsSuccess()) + { + // adding gems is not implemented yet because we don't know what targets to add or how to add them + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } + else + { + QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project.")); + } + } + } + + void CreateProjectCtrl::Update() + { + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); + if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog) + { + m_header->setTitle(tr("Create Project")); + m_header->setSubTitle(tr("Configure project with Gems")); + m_nextButton->setText(tr("Create Project")); + } + else + { + m_header->setTitle(tr("Create Project")); + m_header->setSubTitle(tr("Enter Project Details")); + m_nextButton->setText(tr("Next")); + } + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h similarity index 60% rename from Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h rename to Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index b6b57dc16b..01e3349b21 100644 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -13,30 +13,40 @@ #if !defined(Q_MOC_RUN) #include +#include #endif -QT_FORWARD_DECLARE_CLASS(QIcon) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QLabel) namespace O3DE::ProjectManager { - class FirstTimeUseScreen + QT_FORWARD_DECLARE_CLASS(ScreenHeader) + + class CreateProjectCtrl : public ScreenWidget { public: - explicit FirstTimeUseScreen(QWidget* parent = nullptr); - ~FirstTimeUseScreen() = default; + explicit CreateProjectCtrl(QWidget* parent = nullptr); + ~CreateProjectCtrl() = default; ProjectManagerScreen GetScreenEnum() override; protected slots: - void HandleNewProjectButton(); - void HandleAddProjectButton(); + void HandleBackButton(); + void HandleNextButton(); private: - QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); + void Update(); + + QStackedWidget* m_stack; + ScreenHeader* m_header; + + QPushButton* m_backButton; + QPushButton* m_nextButton; - QPushButton* m_createProjectButton; - QPushButton* m_addProjectButton; + QString m_projectTemplatePath; + ProjectInfo m_projectInfo; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index f51996bd65..6342041da4 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -82,6 +82,16 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::EngineSettings; } + QString EngineSettingsScreen::GetTabText() + { + return tr("Engine"); + } + + bool EngineSettingsScreen::IsTab() + { + return true; + } + void EngineSettingsScreen::OnTextChanged() { // save engine settings diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h index 0e91ec2d3b..36e329cdf6 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h @@ -26,7 +26,10 @@ namespace O3DE::ProjectManager public: explicit EngineSettingsScreen(QWidget* parent = nullptr); ~EngineSettingsScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + QString GetTabText() override; + bool IsTab() override; protected slots: void OnTextChanged(); diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp deleted file mode 100644 index a1be7e8ac9..0000000000 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include -#include -#include -#include -#include - -namespace O3DE::ProjectManager -{ - inline constexpr static int s_contentMargins = 80; - inline constexpr static int s_buttonSpacing = 30; - inline constexpr static int s_iconSize = 24; - inline constexpr static int s_spacerSize = 20; - inline constexpr static int s_boxButtonWidth = 210; - inline constexpr static int s_boxButtonHeight = 280; - - FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent) - : ScreenWidget(parent) - { - QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); - vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins); - - QLabel* titleLabel = new QLabel(this); - titleLabel->setText(tr("Ready. Set. Create!")); - titleLabel->setStyleSheet("font-size: 60px"); - vLayout->addWidget(titleLabel); - - QLabel* introLabel = new QLabel(this); - introLabel->setTextFormat(Qt::AutoText); - introLabel->setText(tr("

Welcome to O3DE! Start something new by creating a project. Not sure what to create?

Explore what\342\200\231s available by downloading our sample project.

")); - introLabel->setStyleSheet("font-size: 14px"); - vLayout->addWidget(introLabel); - - QHBoxLayout* buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(s_buttonSpacing); - - m_createProjectButton = CreateLargeBoxButton(QIcon(":/Add.svg"), tr("Create Project"), this); - m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); - buttonLayout->addWidget(m_createProjectButton); - - m_addProjectButton = CreateLargeBoxButton(QIcon(":/Select_Folder.svg"), tr("Add a Project"), this); - m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); - buttonLayout->addWidget(m_addProjectButton); - - QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum); - buttonLayout->addItem(buttonSpacer); - - vLayout->addItem(buttonLayout); - - QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding); - vLayout->addItem(verticalSpacer); - - // Using border-image allows for scaling options background-image does not support - setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }"); - - connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton); - connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton); - } - - ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum() - { - return ProjectManagerScreen::FirstTimeUse; - } - - void FirstTimeUseScreen::HandleNewProjectButton() - { - emit ResetScreenRequest(ProjectManagerScreen::NewProjectSettingsCore); - emit ChangeScreenRequest(ProjectManagerScreen::NewProjectSettingsCore); - } - void FirstTimeUseScreen::HandleAddProjectButton() - { - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); - } - - QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent) - { - QPushButton* largeBoxButton = new QPushButton(icon, text, parent); - - largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight); - largeBoxButton->setFlat(true); - largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); - largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }"); - - return largeBoxButton; - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp new file mode 100644 index 0000000000..6e9ad42017 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -0,0 +1,49 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) + : QFrame(parent) + { + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setAlignment(Qt::AlignLeft); + hLayout->setMargin(0); + setLayout(hLayout); + + setStyleSheet("background-color: #1E252F;"); + + QLabel* titleLabel = new QLabel(tr("Gem Catalog")); + titleLabel->setStyleSheet("font-size: 21px;"); + hLayout->addWidget(titleLabel); + + hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); + + AzQtComponents::SearchLineEdit* filterLineEdit = new AzQtComponents::SearchLineEdit(); + filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); + connect(filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) + { + filterProxyModel->SetSearchString(text); + }); + hLayout->addWidget(filterLineEdit); + + hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); + hLayout->addSpacerItem(new QSpacerItem(220, 0, QSizePolicy::Fixed)); + + setFixedHeight(60); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h new file mode 100644 index 0000000000..3e065edd8f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -0,0 +1,31 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemCatalogHeaderWidget + : public QFrame + { + Q_OBJECT // AUTOMOC + + public: + explicit GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); + ~GemCatalogHeaderWidget() = default; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index bbc6099f24..2d243e7f8b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include #include @@ -34,6 +36,9 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); + GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(proxyModel); + vLayout->addWidget(headerWidget); + QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); vLayout->addLayout(hLayout); @@ -64,9 +69,12 @@ namespace O3DE::ProjectManager GemFilterWidget* filterWidget = new GemFilterWidget(proxyModel); filterWidget->setFixedWidth(250); + GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(proxyModel); + QVBoxLayout* middleVLayout = new QVBoxLayout(); middleVLayout->setMargin(0); middleVLayout->setSpacing(0); + middleVLayout->addWidget(listHeaderWidget); middleVLayout->addWidget(m_gemListView); hLayout->addWidget(filterWidget); @@ -170,9 +178,4 @@ namespace O3DE::ProjectManager { return ProjectManagerScreen::GemCatalog; } - - QString GemCatalogScreen::GetNextButtonText() - { - return "Create Project"; - } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index bf4202499f..44e0727c7e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -28,7 +28,6 @@ namespace O3DE::ProjectManager explicit GemCatalogScreen(QWidget* parent = nullptr); ~GemCatalogScreen() = default; ProjectManagerScreen GetScreenEnum() override; - QString GetNextButtonText() override; private: QVector GenerateTestData(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index c6651b7295..3ece7760cf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -124,12 +124,12 @@ namespace O3DE::ProjectManager { if (m_collapseButton->isChecked()) { - m_collapseButton->setIcon(QIcon(":/Resources/ArrowDownLine.svg")); + m_collapseButton->setIcon(QIcon(":/ArrowDownLine.svg")); m_mainWidget->hide(); } else { - m_collapseButton->setIcon(QIcon(":/Resources/ArrowUpLine.svg")); + m_collapseButton->setIcon(QIcon(":/ArrowUpLine.svg")); m_mainWidget->show(); } } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index b96a1f242f..06b0adad32 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -62,20 +62,20 @@ namespace O3DE::ProjectManager bool IsValid() const; QString m_path; - QString m_name; - QString m_displayName; + QString m_name = "Unknown Gem Name"; + QString m_displayName = "Unknown Gem Name"; AZ::Uuid m_uuid; - QString m_creator; + QString m_creator = "Unknown Creator"; GemOrigin m_gemOrigin = Local; bool m_isAdded = false; //! Is the gem currently added and enabled in the project? - QString m_summary; + QString m_summary = "No summary provided."; Platforms m_platforms; Types m_types; //! Asset and/or Code and/or Tool QStringList m_features; QString m_directoryLink; QString m_documentationLink; - QString m_version; - QString m_lastUpdatedDate; + QString m_version = "Unknown Version"; + QString m_lastUpdatedDate = "Unknown Date"; int m_binarySizeInKB = 0; QStringList m_dependingGemUuids; QStringList m_conflictingGemUuids; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp new file mode 100644 index 0000000000..128fb93345 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -0,0 +1,78 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemListHeaderWidget::GemListHeaderWidget(GemSortFilterProxyModel* proxyModel, QWidget* parent) + : QFrame(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setMargin(0); + setLayout(vLayout); + + setStyleSheet("background-color: #333333;"); + + vLayout->addSpacing(20); + + // Top section + QHBoxLayout* topLayout = new QHBoxLayout(); + topLayout->setMargin(0); + topLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); + + QLabel* showCountLabel = new QLabel(); + showCountLabel->setStyleSheet("font-size: 11pt; font: italic;"); + topLayout->addWidget(showCountLabel); + connect(proxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] + { + const int numGemsShown = proxyModel->rowCount(); + showCountLabel->setText(QString(tr("showing %1 Gems")).arg(numGemsShown)); + }); + + topLayout->addSpacing(GemItemDelegate::s_contentMargins.right() + GemItemDelegate::s_borderWidth); + + vLayout->addLayout(topLayout); + + vLayout->addSpacing(20); + + // Separating line + QFrame* hLine = new QFrame(); + hLine->setFrameShape(QFrame::HLine); + hLine->setStyleSheet("color: #666666;"); + vLayout->addWidget(hLine); + + vLayout->addSpacing(GemItemDelegate::s_contentMargins.top()); + + // Bottom section + QHBoxLayout* columnHeaderLayout = new QHBoxLayout(); + columnHeaderLayout->setAlignment(Qt::AlignLeft); + + columnHeaderLayout->addSpacing(31); + + QLabel* gemNameLabel = new QLabel(tr("Gem Name")); + gemNameLabel->setStyleSheet("font-size: 11pt;"); + columnHeaderLayout->addWidget(gemNameLabel); + + columnHeaderLayout->addSpacing(111); + + QLabel* gemSummaryLabel = new QLabel(tr("Gem Summary")); + gemSummaryLabel->setStyleSheet("font-size: 11pt;"); + columnHeaderLayout->addWidget(gemSummaryLabel); + + vLayout->addLayout(columnHeaderLayout); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h new file mode 100644 index 0000000000..b16a654ad0 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h @@ -0,0 +1,33 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemListHeaderWidget + : public QFrame + { + Q_OBJECT // AUTOMOC + + public: + explicit GemListHeaderWidget(GemSortFilterProxyModel* proxyModel, QWidget* parent = nullptr); + ~GemListHeaderWidget() = default; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index 153c0964c7..b57a2b35b2 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -12,6 +12,9 @@ #include #include +#include +#include +#include #include #include @@ -23,6 +26,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -31,64 +35,81 @@ namespace O3DE::ProjectManager NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - QHBoxLayout* hLayout = new QHBoxLayout(); - this->setLayout(hLayout); - + QHBoxLayout* hLayout = new QHBoxLayout(this); + hLayout->setAlignment(Qt::AlignLeft); + hLayout->setContentsMargins(0,0,0,0); + + // if we don't provide a parent for this box layout the stylesheet doesn't take + // if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally + QFrame* projectSettingsFrame = new QFrame(this); + projectSettingsFrame->setObjectName("projectSettings"); QVBoxLayout* vLayout = new QVBoxLayout(this); - QLabel* projectNameLabel = new QLabel(tr("Project Name"), this); - vLayout->addWidget(projectNameLabel); - - m_projectNameLineEdit = new QLineEdit(tr("New Project"), this); - vLayout->addWidget(m_projectNameLineEdit); - - QLabel* projectPathLabel = new QLabel(tr("Project Location"), this); - vLayout->addWidget(projectPathLabel); - - { - QHBoxLayout* projectPathLayout = new QHBoxLayout(this); - - m_projectPathLineEdit = new QLineEdit(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this); - projectPathLayout->addWidget(m_projectPathLineEdit); - - QPushButton* browseButton = new QPushButton(tr("Browse"), this); - connect(browseButton, &QPushButton::pressed, this, &NewProjectSettingsScreen::HandleBrowseButton); - projectPathLayout->addWidget(browseButton); - - vLayout->addLayout(projectPathLayout); - } - - QLabel* projectTemplateLabel = new QLabel(this); - projectTemplateLabel->setText("Project Template"); - vLayout->addWidget(projectTemplateLabel); - - QHBoxLayout* templateLayout = new QHBoxLayout(this); - vLayout->addItem(templateLayout); - - m_projectTemplateButtonGroup = new QButtonGroup(this); - auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); - if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) + // you cannot remove content margins in qss + vLayout->setContentsMargins(0,0,0,0); + vLayout->setAlignment(Qt::AlignTop); { - for (auto projectTemplate : templatesResult.GetValue()) + m_projectName = new FormLineEditWidget(tr("Project name"), tr("New Project"), this); + m_projectName->setErrorLabelText( + tr("A project with this name already exists at this location. Please choose a new name or location.")); + vLayout->addWidget(m_projectName); + + m_projectPath = + new FormBrowseEditWidget(tr("Project Location"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this); + m_projectPath->lineEdit()->setReadOnly(true); + m_projectPath->setErrorLabelText(tr("Please provide a valid path to a folder that exists")); + m_projectPath->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this)); + vLayout->addWidget(m_projectPath); + + // if we don't use a QFrame we cannot "contain" the widgets inside and move them around + // as a group + QFrame* projectTemplateWidget = new QFrame(this); + projectTemplateWidget->setObjectName("projectTemplate"); + QVBoxLayout* containerLayout = new QVBoxLayout(); + containerLayout->setAlignment(Qt::AlignTop); { - QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); - radioButton->setProperty(k_pathProperty, projectTemplate.m_path); - m_projectTemplateButtonGroup->addButton(radioButton); - - templateLayout->addWidget(radioButton); + QLabel* projectTemplateLabel = new QLabel(tr("Select a Project Template")); + projectTemplateLabel->setObjectName("projectTemplateLabel"); + containerLayout->addWidget(projectTemplateLabel); + + QLabel* projectTemplateDetailsLabel = new QLabel(tr("Project templates are pre-configured with relevant Gems that provide " + "additional functionality and content to the project.")); + projectTemplateDetailsLabel->setWordWrap(true); + projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel"); + containerLayout->addWidget(projectTemplateDetailsLabel); + + QHBoxLayout* templateLayout = new QHBoxLayout(this); + containerLayout->addItem(templateLayout); + + m_projectTemplateButtonGroup = new QButtonGroup(this); + m_projectTemplateButtonGroup->setObjectName("templateButtonGroup"); + auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); + if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) + { + for (auto projectTemplate : templatesResult.GetValue()) + { + QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); + radioButton->setProperty(k_pathProperty, projectTemplate.m_path); + m_projectTemplateButtonGroup->addButton(radioButton); + + containerLayout->addWidget(radioButton); + } + + m_projectTemplateButtonGroup->buttons().first()->setChecked(true); + } } - - m_projectTemplateButtonGroup->buttons().first()->setChecked(true); + projectTemplateWidget->setLayout(containerLayout); + vLayout->addWidget(projectTemplateWidget); } + projectSettingsFrame->setLayout(vLayout); - QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - vLayout->addItem(verticalSpacer); + hLayout->addWidget(projectSettingsFrame); - hLayout->addItem(vLayout); + QWidget* projectTemplateDetails = new QWidget(this); + projectTemplateDetails->setObjectName("projectTemplateDetails"); + hLayout->addWidget(projectTemplateDetails); - QWidget* gemsListPlaceholder = new QWidget(this); - gemsListPlaceholder->setFixedWidth(250); - hLayout->addWidget(gemsListPlaceholder); + this->setLayout(hLayout); } ProjectManagerScreen NewProjectSettingsScreen::GetScreenEnum() @@ -96,31 +117,12 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::NewProjectSettings; } - QString NewProjectSettingsScreen::GetNextButtonText() - { - return tr("Next"); - } - - void NewProjectSettingsScreen::HandleBrowseButton() - { - QString defaultPath = m_projectPathLineEdit->text(); - if (defaultPath.isEmpty()) - { - defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - } - - QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("New project path"), defaultPath)); - if (!directory.isEmpty()) - { - m_projectPathLineEdit->setText(directory); - } - } ProjectInfo NewProjectSettingsScreen::GetProjectInfo() { ProjectInfo projectInfo; - projectInfo.m_projectName = m_projectNameLineEdit->text(); - projectInfo.m_path = QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + projectInfo.m_projectName); + projectInfo.m_projectName = m_projectName->lineEdit()->text(); + projectInfo.m_path = QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + projectInfo.m_projectName); return projectInfo; } @@ -132,18 +134,18 @@ namespace O3DE::ProjectManager bool NewProjectSettingsScreen::Validate() { bool projectNameIsValid = true; - if (m_projectNameLineEdit->text().isEmpty()) + if (m_projectName->lineEdit()->text().isEmpty()) { projectNameIsValid = false; } bool projectPathIsValid = true; - if (m_projectPathLineEdit->text().isEmpty()) + if (m_projectPath->lineEdit()->text().isEmpty()) { projectPathIsValid = false; } - QDir path(QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + m_projectNameLineEdit->text())); + QDir path(QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + m_projectName->lineEdit()->text())); if (path.exists() && !path.isEmpty()) { projectPathIsValid = false; diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index dbd4388668..f0e9609fdc 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -17,10 +17,12 @@ #endif QT_FORWARD_DECLARE_CLASS(QButtonGroup) -QT_FORWARD_DECLARE_CLASS(QLineEdit) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(FormLineEditWidget) + QT_FORWARD_DECLARE_CLASS(FormBrowseEditWidget) + class NewProjectSettingsScreen : public ScreenWidget { @@ -28,7 +30,6 @@ namespace O3DE::ProjectManager explicit NewProjectSettingsScreen(QWidget* parent = nullptr); ~NewProjectSettingsScreen() = default; ProjectManagerScreen GetScreenEnum() override; - QString GetNextButtonText() override; ProjectInfo GetProjectInfo(); QString GetProjectTemplatePath(); @@ -39,8 +40,8 @@ namespace O3DE::ProjectManager void HandleBrowseButton(); private: - QLineEdit* m_projectNameLineEdit; - QLineEdit* m_projectPathLineEdit; + FormLineEditWidget* m_projectName; + FormBrowseEditWidget* m_projectPath; QButtonGroup* m_projectTemplateButtonGroup; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp new file mode 100644 index 0000000000..4be876e79f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -0,0 +1,140 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define SHOW_ALL_PROJECT_ACTIONS + +namespace O3DE::ProjectManager +{ + inline constexpr static int s_projectImageWidth = 210; + inline constexpr static int s_projectImageHeight = 280; + + LabelButton::LabelButton(QWidget* parent) + : QLabel(parent) + { + setObjectName("labelButton"); + m_overlayLabel = new QLabel("", this); + m_overlayLabel->setObjectName("labelButtonOverlay"); + m_overlayLabel->setWordWrap(true); + m_overlayLabel->setAlignment(Qt::AlignCenter); + m_overlayLabel->setVisible(false); + } + + void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) + { + if(m_enabled) + { + emit triggered(); + } + } + + void LabelButton::SetEnabled(bool enabled) + { + m_enabled = enabled; + m_overlayLabel->setVisible(!enabled); + } + + void LabelButton::SetOverlayText(const QString& text) + { + m_overlayLabel->setText(text); + } + + ProjectButton::ProjectButton(const QString& projectName, QWidget* parent) + : QFrame(parent) + , m_projectName(projectName) + , m_projectImagePath(":/Resources/DefaultProjectImage.png") + { + Setup(); + } + + ProjectButton::ProjectButton(const QString& projectName, const QString& projectImage, QWidget* parent) + : QFrame(parent) + , m_projectName(projectName) + , m_projectImagePath(projectImage) + { + Setup(); + } + + void ProjectButton::Setup() + { + setObjectName("projectButton"); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setSpacing(0); + vLayout->setContentsMargins(0, 0, 0, 0); + setLayout(vLayout); + + m_projectImageLabel = new LabelButton(this); + m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight); + vLayout->addWidget(m_projectImageLabel); + + m_projectImageLabel->setPixmap(QPixmap(m_projectImagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); + + QMenu* newProjectMenu = new QMenu(this); + m_editProjectAction = newProjectMenu->addAction(tr("Edit Project Settings...")); + +#ifdef SHOW_ALL_PROJECT_ACTIONS + m_editProjectGemsAction = newProjectMenu->addAction(tr("Cutomize Gems...")); + newProjectMenu->addSeparator(); + m_copyProjectAction = newProjectMenu->addAction(tr("Duplicate")); + newProjectMenu->addSeparator(); + m_removeProjectAction = newProjectMenu->addAction(tr("Remove from O3DE")); + m_deleteProjectAction = newProjectMenu->addAction(tr("Delete the Project")); +#endif + + QFrame* footer = new QFrame(this); + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(0, 0, 0, 0); + footer->setLayout(hLayout); + { + QLabel* projectNameLabel = new QLabel(m_projectName, this); + hLayout->addWidget(projectNameLabel); + + QPushButton* projectMenuButton = new QPushButton(this); + projectMenuButton->setObjectName("projectMenuButton"); + projectMenuButton->setMenu(newProjectMenu); + hLayout->addWidget(projectMenuButton); + } + + vLayout->addWidget(footer); + + connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectName); }); + connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectName); }); + +#ifdef SHOW_ALL_PROJECT_ACTIONS + connect(m_editProjectGemsAction, &QAction::triggered, [this]() { emit EditProjectGems(m_projectName); }); + connect(m_copyProjectAction, &QAction::triggered, [this]() { emit CopyProject(m_projectName); }); + connect(m_removeProjectAction, &QAction::triggered, [this]() { emit RemoveProject(m_projectName); }); + connect(m_deleteProjectAction, &QAction::triggered, [this]() { emit DeleteProject(m_projectName); }); +#endif + } + + void ProjectButton::SetButtonEnabled(bool enabled) + { + m_projectImageLabel->SetEnabled(enabled); + } + + void ProjectButton::SetButtonOverlayText(const QString& text) + { + m_projectImageLabel->SetOverlayText(text); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h new file mode 100644 index 0000000000..671debf6d0 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -0,0 +1,82 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QPixmap) +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QAction) + +namespace O3DE::ProjectManager +{ + class LabelButton + : public QLabel + { + Q_OBJECT // AUTOMOC + + public: + explicit LabelButton(QWidget* parent = nullptr); + ~LabelButton() = default; + + void SetEnabled(bool enabled); + void SetOverlayText(const QString& text); + + signals: + void triggered(); + + public slots: + void mousePressEvent(QMouseEvent* event) override; + + private: + QLabel* m_overlayLabel; + bool m_enabled = true; + }; + + class ProjectButton + : public QFrame + { + Q_OBJECT // AUTOMOC + + public: + explicit ProjectButton(const QString& projectName, QWidget* parent = nullptr); + explicit ProjectButton(const QString& projectName, const QString& projectImage, QWidget* parent = nullptr); + ~ProjectButton() = default; + + void SetButtonEnabled(bool enabled); + void SetButtonOverlayText(const QString& text); + + signals: + void OpenProject(const QString& projectName); + void EditProject(const QString& projectName); + void EditProjectGems(const QString& projectName); + void CopyProject(const QString& projectName); + void RemoveProject(const QString& projectName); + void DeleteProject(const QString& projectName); + + private: + void Setup(); + + QString m_projectName; + QString m_projectImagePath; + LabelButton* m_projectImageLabel; + QAction* m_editProjectAction; + QAction* m_editProjectGemsAction; + QAction* m_copyProjectAction; + QAction* m_removeProjectAction; + QAction* m_deleteProjectAction; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index 4136b9eb8c..76bcc2eb99 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -11,52 +11,46 @@ */ #include -#include +#include #include #include #include -#include - namespace O3DE::ProjectManager { ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath) : QMainWindow(parent) - , m_ui(new Ui::ProjectManagerWindowClass()) { - m_ui->setupUi(this); - QLayout* layout = m_ui->centralWidget->layout(); - layout->setMargin(0); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - m_pythonBindings = AZStd::make_unique(engineRootPath); - m_screensCtrl = new ScreensCtrl(); - m_ui->verticalLayout->addWidget(m_screensCtrl); + setWindowTitle(tr("O3DE Project Manager")); + + ScreensCtrl* screensCtrl = new ScreensCtrl(); + + // currently the tab order on the home page is based on the order of this list + QVector screenEnums = + { + ProjectManagerScreen::Projects, + ProjectManagerScreen::EngineSettings, + ProjectManagerScreen::CreateProject, + ProjectManagerScreen::UpdateProject + }; + screensCtrl->BuildScreens(screenEnums); - connect(m_ui->projectsMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleProjectsMenu); - connect(m_ui->engineMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleEngineMenu); + setCentralWidget(screensCtrl); + // setup stylesheets and hot reloading QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast(engineRootPath.Native().size())); const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); const auto qrcPath = QStringLiteral(":/ProjectManager/style"); AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath); + // set stylesheet after creating the screens or their styles won't get updated AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss")); - QVector screenEnums = - { - ProjectManagerScreen::FirstTimeUse, - ProjectManagerScreen::NewProjectSettingsCore, - ProjectManagerScreen::ProjectsHome, - ProjectManagerScreen::ProjectSettings, - ProjectManagerScreen::EngineSettings - }; - m_screensCtrl->BuildScreens(screenEnums); - m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::FirstTimeUse, false); + screensCtrl->ForceChangeToScreen(ProjectManagerScreen::Projects, false); } ProjectManagerWindow::~ProjectManagerWindow() @@ -64,13 +58,4 @@ namespace O3DE::ProjectManager m_pythonBindings.reset(); } - void ProjectManagerWindow::HandleProjectsMenu() - { - m_screensCtrl->ChangeToScreen(ProjectManagerScreen::ProjectsHome); - } - void ProjectManagerWindow::HandleEngineMenu() - { - m_screensCtrl->ChangeToScreen(ProjectManagerScreen::EngineSettings); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h index d5c586e59b..74db3467c5 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h @@ -13,17 +13,9 @@ #if !defined(Q_MOC_RUN) #include - -#include - #include #endif -namespace Ui -{ - class ProjectManagerWindowClass; -} - namespace O3DE::ProjectManager { class ProjectManagerWindow @@ -35,13 +27,7 @@ namespace O3DE::ProjectManager explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath); ~ProjectManagerWindow(); - protected slots: - void HandleProjectsMenu(); - void HandleEngineMenu(); - private: - QScopedPointer m_ui; - ScreensCtrl* m_screensCtrl; AZStd::unique_ptr m_pythonBindings; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui deleted file mode 100644 index 633cd61182..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui +++ /dev/null @@ -1,67 +0,0 @@ - - - ProjectManagerWindowClass - - - - 0 - 0 - 1200 - 800 - - - - - 0 - 0 - - - - O3DE Project Manager - - - - - - - - 0 - 0 - 1200 - 36 - - - - - 16 - - - - - Icon - - - - :/o3de_editor.ico:/o3de_editor.ico - - - - - Projects - - - - - Engine - - - - - - - - - - - - diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp index 52fca439b4..76aa1d2897 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp @@ -30,6 +30,23 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::ProjectSettings; } + ProjectInfo ProjectSettingsScreen::GetProjectInfo() + { + // Impl pending next PR + return ProjectInfo(); + } + + void ProjectSettingsScreen::SetProjectInfo() + { + // Impl pending next PR + } + + bool ProjectSettingsScreen::Validate() + { + // Impl pending next PR + return true; + } + void ProjectSettingsScreen::HandleGemsButton() { emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h index 1ec1b46f44..a4cafcd93a 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h @@ -13,6 +13,7 @@ #if !defined(Q_MOC_RUN) #include +#include #endif namespace Ui @@ -30,6 +31,11 @@ namespace O3DE::ProjectManager ~ProjectSettingsScreen() = default; ProjectManagerScreen GetScreenEnum() override; + ProjectInfo GetProjectInfo(); + void SetProjectInfo(); + + bool Validate(); + protected slots: void HandleGemsButton(); diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp deleted file mode 100644 index 539f79b017..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include - -#include - -namespace O3DE::ProjectManager -{ - ProjectsHomeScreen::ProjectsHomeScreen(QWidget* parent) - : ScreenWidget(parent) - , m_ui(new Ui::ProjectsHomeClass()) - { - m_ui->setupUi(this); - - connect(m_ui->newProjectButton, &QPushButton::pressed, this, &ProjectsHomeScreen::HandleNewProjectButton); - connect(m_ui->addProjectButton, &QPushButton::pressed, this, &ProjectsHomeScreen::HandleAddProjectButton); - connect(m_ui->editProjectButton, &QPushButton::pressed, this, &ProjectsHomeScreen::HandleEditProjectButton); - } - - ProjectManagerScreen ProjectsHomeScreen::GetScreenEnum() - { - return ProjectManagerScreen::ProjectsHome; - } - - void ProjectsHomeScreen::HandleNewProjectButton() - { - emit ResetScreenRequest(ProjectManagerScreen::NewProjectSettingsCore); - emit ChangeScreenRequest(ProjectManagerScreen::NewProjectSettingsCore); - } - void ProjectsHomeScreen::HandleAddProjectButton() - { - // Do nothing for now - } - void ProjectsHomeScreen::HandleEditProjectButton() - { - emit ChangeScreenRequest(ProjectManagerScreen::ProjectSettings); - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h b/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h deleted file mode 100644 index 9fd5919d2d..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ -#pragma once - -#if !defined(Q_MOC_RUN) -#include -#endif - -namespace Ui -{ - class ProjectsHomeClass; -} - -namespace O3DE::ProjectManager -{ - class ProjectsHomeScreen - : public ScreenWidget - { - - public: - explicit ProjectsHomeScreen(QWidget* parent = nullptr); - ~ProjectsHomeScreen() = default; - ProjectManagerScreen GetScreenEnum() override; - - protected slots: - void HandleNewProjectButton(); - void HandleAddProjectButton(); - void HandleEditProjectButton(); - - private: - QScopedPointer m_ui; - }; - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.ui b/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.ui deleted file mode 100644 index 2ba93ccf90..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.ui +++ /dev/null @@ -1,137 +0,0 @@ - - - ProjectsHomeClass - - - - 0 - 0 - 826 - 585 - - - - Form - - - - - - My Projects - - - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - - :/Add.svg:/Add.svg - - - - - - - - 0 - 0 - - - - - - - - :/Select_Folder.svg:/Select_Folder.svg - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - - - Edit Project - - - - - - - Open a Project - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp new file mode 100644 index 0000000000..5f1c0e2b36 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -0,0 +1,347 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DISPLAY_PROJECT_DEV_DATA true + +namespace O3DE::ProjectManager +{ + ProjectsScreen::ProjectsScreen(QWidget* parent) + : ScreenWidget(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setAlignment(Qt::AlignTop); + vLayout->setContentsMargins(s_contentMargins, 0, s_contentMargins, 0); + setLayout(vLayout); + + m_background.load(":/Backgrounds/FirstTimeBackgroundImage.jpg"); + + m_stack = new QStackedWidget(this); + + m_firstTimeContent = CreateFirstTimeContent(); + m_stack->addWidget(m_firstTimeContent); + + m_projectsContent = CreateProjectsContent(); + m_stack->addWidget(m_projectsContent); + + vLayout->addWidget(m_stack); + + connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton); + connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton); + } + + QFrame* ProjectsScreen::CreateFirstTimeContent() + { + QFrame* frame = new QFrame(this); + frame->setObjectName("firstTimeContent"); + { + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignTop); + frame->setLayout(layout); + + QLabel* titleLabel = new QLabel(tr("Ready. Set. Create."), this); + titleLabel->setObjectName("titleLabel"); + layout->addWidget(titleLabel); + + QLabel* introLabel = new QLabel(this); + introLabel->setObjectName("introLabel"); + introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project. Not sure what to create? \nExplore what's " + "available by downloading our sample project.")); + layout->addWidget(introLabel); + + QHBoxLayout* buttonLayout = new QHBoxLayout(this); + buttonLayout->setAlignment(Qt::AlignLeft); + buttonLayout->setSpacing(s_spacerSize); + + // use a newline to force the text up + QPushButton* createProjectButton = new QPushButton(tr("Create a Project\n"), this); + createProjectButton->setObjectName("createProjectButton"); + buttonLayout->addWidget(createProjectButton); + + QPushButton* addProjectButton = new QPushButton(tr("Add a Project\n"), this); + addProjectButton->setObjectName("addProjectButton"); + buttonLayout->addWidget(addProjectButton); + + connect(createProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleNewProjectButton); + connect(addProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleAddProjectButton); + + layout->addLayout(buttonLayout); + } + + return frame; + } + + QFrame* ProjectsScreen::CreateProjectsContent() + { + QFrame* frame = new QFrame(this); + frame->setObjectName("projectsContent"); + { + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + layout->setContentsMargins(0, 0, 0, 0); + frame->setLayout(layout); + + QFrame* header = new QFrame(this); + QHBoxLayout* headerLayout = new QHBoxLayout(); + { + QLabel* titleLabel = new QLabel(tr("My Projects"), this); + titleLabel->setObjectName("titleLabel"); + headerLayout->addWidget(titleLabel); + + QMenu* newProjectMenu = new QMenu(this); + m_createNewProjectAction = newProjectMenu->addAction("Create New Project"); + m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project"); + + connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton); + connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton); + + QPushButton* newProjectMenuButton = new QPushButton(tr("New Project..."), this); + newProjectMenuButton->setObjectName("newProjectButton"); + newProjectMenuButton->setMenu(newProjectMenu); + newProjectMenuButton->setDefault(true); + headerLayout->addWidget(newProjectMenuButton); + } + header->setLayout(headerLayout); + + layout->addWidget(header); + + // Get all projects and create a horizontal scrolling list of them + auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); + if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty()) + { + QScrollArea* projectsScrollArea = new QScrollArea(this); + QWidget* scrollWidget = new QWidget(); + + FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(flowLayout); + + projectsScrollArea->setWidget(scrollWidget); + projectsScrollArea->setWidgetResizable(true); + +#ifndef DISPLAY_PROJECT_DEV_DATA + for (auto project : projectsResult.GetValue()) +#else + ProjectInfo project = projectsResult.GetValue().at(0); + for (int i = 0; i < 15; i++) +#endif + { + ProjectButton* projectButton; + QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; + QFileInfo doesPreviewExist(projectPreviewPath); + if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + { + projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this); + } + else + { + projectButton = new ProjectButton(project.m_projectName, this); + } + + flowLayout->addWidget(projectButton); + + connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); + connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); + + #ifdef DISPLAY_PROJECT_DEV_DATA + connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsScreen::HandleEditProjectGems); + connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); + connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); + connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); + #endif + } + + layout->addWidget(projectsScrollArea); + } + } + + return frame; + } + + ProjectManagerScreen ProjectsScreen::GetScreenEnum() + { + return ProjectManagerScreen::Projects; + } + + bool ProjectsScreen::IsTab() + { + return true; + } + + QString ProjectsScreen::GetTabText() + { + return tr("Projects"); + } + + void ProjectsScreen::paintEvent([[maybe_unused]] QPaintEvent* event) + { + // we paint the background here because qss does not support background cover scaling + QPainter painter(this); + + auto winSize = size(); + auto pixmapRatio = (float)m_background.width() / m_background.height(); + auto windowRatio = (float)winSize.width() / winSize.height(); + + if (pixmapRatio > windowRatio) + { + auto newWidth = (int)(winSize.height() * pixmapRatio); + auto offset = (newWidth - winSize.width()) / -2; + painter.drawPixmap(offset, 0, newWidth, winSize.height(), m_background); + } + else + { + auto newHeight = (int)(winSize.width() / pixmapRatio); + painter.drawPixmap(0, 0, winSize.width(), newHeight, m_background); + } + } + + void ProjectsScreen::HandleNewProjectButton() + { + emit ResetScreenRequest(ProjectManagerScreen::CreateProject); + emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); + } + void ProjectsScreen::HandleAddProjectButton() + { + // Do nothing for now + } + void ProjectsScreen::HandleOpenProject(const QString& projectPath) + { + if (!projectPath.isEmpty()) + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZStd::string executableFilename = "Editor"; + AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); + + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = cmdPath; + bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + if (!launchSucceeded) + { + AZ_Error("ProjectManager", false, "Failed to launch editor"); + QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); + } + else + { + // prevent the user from accidentally pressing the button while the editor is launching + // and let them know what's happening + ProjectButton* button = qobject_cast(sender()); + if (button) + { + button->SetButtonEnabled(false); + button->SetButtonOverlayText(tr("Opening Editor...")); + } + + // enable the button after 3 seconds + constexpr int waitTimeInMs = 3000; + QTimer::singleShot(waitTimeInMs, this, [this, button] { + if (button) + { + button->SetButtonEnabled(true); + } + }); + } + } + else + { + AZ_Error("ProjectManager", false, "Cannot open editor because an empty project path was provided"); + QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor because the project path is invalid.")); + } + + } + void ProjectsScreen::HandleEditProject(const QString& projectPath) + { + emit NotifyCurrentProject(projectPath); + emit ResetScreenRequest(ProjectManagerScreen::UpdateProject); + emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + } + void ProjectsScreen::HandleEditProjectGems(const QString& projectPath) + { + emit NotifyCurrentProject(projectPath); + emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); + } + void ProjectsScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath) + { + // Open file dialog and choose location for copied project then register copy with O3DE + } + void ProjectsScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath) + { + // Unregister Project from O3DE + } + void ProjectsScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath) + { + // Remove project from 03DE and delete from disk + ProjectsScreen::HandleRemoveProject(projectPath); + } + + void ProjectsScreen::NotifyCurrentScreen() + { + if (ShouldDisplayFirstTimeContent()) + { + m_stack->setCurrentWidget(m_firstTimeContent); + } + else + { + m_stack->setCurrentWidget(m_projectsContent); + } + } + + bool ProjectsScreen::ShouldDisplayFirstTimeContent() + { + auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); + if (!projectsResult.IsSuccess() || projectsResult.GetValue().isEmpty()) + { + return true; + } + + QSettings settings; + bool displayFirstTimeContent = settings.value("displayFirstTimeContent", true).toBool(); + if (displayFirstTimeContent) + { + settings.setValue("displayFirstTimeContent", false); + } + + return displayFirstTimeContent; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h new file mode 100644 index 0000000000..d88ba8398d --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -0,0 +1,69 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QPaintEvent) +QT_FORWARD_DECLARE_CLASS(QFrame) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) + +namespace O3DE::ProjectManager +{ + class ProjectsScreen + : public ScreenWidget + { + + public: + explicit ProjectsScreen(QWidget* parent = nullptr); + ~ProjectsScreen() = default; + + ProjectManagerScreen GetScreenEnum() override; + QString GetTabText() override; + bool IsTab() override; + + protected: + void NotifyCurrentScreen() override; + + protected slots: + void HandleNewProjectButton(); + void HandleAddProjectButton(); + void HandleOpenProject(const QString& projectPath); + void HandleEditProject(const QString& projectPath); + void HandleEditProjectGems(const QString& projectPath); + void HandleCopyProject(const QString& projectPath); + void HandleRemoveProject(const QString& projectPath); + void HandleDeleteProject(const QString& projectPath); + + void paintEvent(QPaintEvent* event) override; + + private: + QFrame* CreateFirstTimeContent(); + QFrame* CreateProjectsContent(); + bool ShouldDisplayFirstTimeContent(); + + QAction* m_createNewProjectAction; + QAction* m_addExistingProjectAction; + QPixmap m_background; + QFrame* m_firstTimeContent; + QFrame* m_projectsContent; + QStackedWidget* m_stack; + + const QString m_projectPreviewImagePath = "/preview.png"; + + inline constexpr static int s_contentMargins = 80; + inline constexpr static int s_spacerSize = 20; + }; + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 9a5e82dafb..8c79a153c8 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -540,7 +540,8 @@ namespace O3DE::ProjectManager ProjectInfo PythonBindings::ProjectInfoFromPath(pybind11::handle path) { ProjectInfo projectInfo; - projectInfo.m_path = Py_To_String(path); + projectInfo.m_path = Py_To_String(path); + projectInfo.m_isNew = false; auto projectData = m_registration.attr("get_project_data")(pybind11::none(), path); if (pybind11::isinstance(projectData)) diff --git a/Code/Tools/ProjectManager/Source/ScreenDefs.h b/Code/Tools/ProjectManager/Source/ScreenDefs.h index 658b8d88fd..46d243f677 100644 --- a/Code/Tools/ProjectManager/Source/ScreenDefs.h +++ b/Code/Tools/ProjectManager/Source/ScreenDefs.h @@ -17,11 +17,11 @@ namespace O3DE::ProjectManager { Invalid = -1, Empty, - FirstTimeUse, - NewProjectSettingsCore, + CreateProject, NewProjectSettings, GemCatalog, - ProjectsHome, + Projects, + UpdateProject, ProjectSettings, EngineSettings }; diff --git a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp index 1089f8ee94..b2b4376e14 100644 --- a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp +++ b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp @@ -11,11 +11,11 @@ */ #include -#include -#include +#include +#include #include #include -#include +#include #include #include @@ -27,11 +27,8 @@ namespace O3DE::ProjectManager switch(screen) { - case (ProjectManagerScreen::FirstTimeUse): - newScreen = new FirstTimeUseScreen(parent); - break; - case (ProjectManagerScreen::NewProjectSettingsCore): - newScreen = new ProjectSettingsCtrl(parent); + case (ProjectManagerScreen::CreateProject): + newScreen = new CreateProjectCtrl(parent); break; case (ProjectManagerScreen::NewProjectSettings): newScreen = new NewProjectSettingsScreen(parent); @@ -39,8 +36,11 @@ namespace O3DE::ProjectManager case (ProjectManagerScreen::GemCatalog): newScreen = new GemCatalogScreen(parent); break; - case (ProjectManagerScreen::ProjectsHome): - newScreen = new ProjectsHomeScreen(parent); + case (ProjectManagerScreen::Projects): + newScreen = new ProjectsScreen(parent); + break; + case (ProjectManagerScreen::UpdateProject): + newScreen = new UpdateProjectCtrl(parent); break; case (ProjectManagerScreen::ProjectSettings): newScreen = new ProjectSettingsScreen(parent); diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp new file mode 100644 index 0000000000..29b1eb6ff6 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp @@ -0,0 +1,62 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + ScreenHeader::ScreenHeader(QWidget* parent) + : QFrame(parent) + { + setObjectName("header"); + + QHBoxLayout* layout = new QHBoxLayout(); + layout->setAlignment(Qt::AlignLeft); + layout->setContentsMargins(0,0,0,0); + + m_backButton = new QPushButton(); + m_backButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + layout->addWidget(m_backButton); + + QVBoxLayout* titleLayout = new QVBoxLayout(); + m_title = new QLabel(); + m_title->setObjectName("headerTitle"); + titleLayout->addWidget(m_title); + + m_subTitle = new QLabel(); + m_subTitle->setObjectName("headerSubTitle"); + titleLayout->addWidget(m_subTitle); + + layout->addLayout(titleLayout); + + setLayout(layout); + } + + void ScreenHeader::setTitle(const QString& text) + { + m_title->setText(text); + } + + void ScreenHeader::setSubTitle(const QString& text) + { + m_subTitle->setText(text); + } + + QPushButton* ScreenHeader::backButton() + { + return m_backButton; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h new file mode 100644 index 0000000000..c5fdb56195 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h @@ -0,0 +1,42 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QPushButton) + +namespace O3DE::ProjectManager +{ + class ScreenHeader + : public QFrame + { + Q_OBJECT // AUTOMOC + + public: + ScreenHeader(QWidget* parent = nullptr); + + void setTitle(const QString& text); + void setSubTitle(const QString& text); + + QPushButton* backButton(); + + private: + QLabel* m_title; + QLabel* m_subTitle; + QPushButton* m_backButton; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index 483066e031..2ad6d30201 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -41,15 +41,27 @@ namespace O3DE::ProjectManager { return true; } - virtual QString GetNextButtonText() + virtual bool IsTab() { - return "Next"; + return false; + } + virtual QString GetTabText() + { + return tr("Missing"); + } + + //! Notify this screen it is the current screen + virtual void NotifyCurrentScreen() + { + } signals: void ChangeScreenRequest(ProjectManagerScreen screen); void GotoPreviousScreenRequest(); void ResetScreenRequest(ProjectManagerScreen screen); + void NotifyCurrentProject(const QString& projectPath); + }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index b8a38ed155..7d31d02f6c 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -21,17 +22,19 @@ namespace O3DE::ProjectManager ScreensCtrl::ScreensCtrl(QWidget* parent) : QWidget(parent) { + setObjectName("ScreensCtrl"); + QVBoxLayout* vLayout = new QVBoxLayout(); - vLayout->setMargin(0); - vLayout->setSpacing(0); vLayout->setContentsMargins(0, 0, 0, 0); setLayout(vLayout); m_screenStack = new QStackedWidget(); vLayout->addWidget(m_screenStack); - //Track the bottom of the stack - m_screenVisitOrder.push(ProjectManagerScreen::Invalid); + // add a tab widget at the bottom of the stack + m_tabWidget = new QTabWidget(); + m_screenStack->addWidget(m_tabWidget); + connect(m_tabWidget, &QTabWidget::currentChanged, this, &ScreensCtrl::TabChanged); } void ScreensCtrl::BuildScreens(QVector screens) @@ -57,7 +60,14 @@ namespace O3DE::ProjectManager ScreenWidget* ScreensCtrl::GetCurrentScreen() { - return reinterpret_cast(m_screenStack->currentWidget()); + if (m_screenStack->currentWidget() == m_tabWidget) + { + return reinterpret_cast(m_tabWidget->currentWidget()); + } + else + { + return reinterpret_cast(m_screenStack->currentWidget()); + } } bool ScreensCtrl::ChangeToScreen(ProjectManagerScreen screen) @@ -79,13 +89,28 @@ namespace O3DE::ProjectManager if (iterator != m_screenMap.end()) { ScreenWidget* currentScreen = GetCurrentScreen(); - if (currentScreen != iterator.value()) + ScreenWidget* newScreen = iterator.value(); + + if (currentScreen != newScreen) { if (addVisit) { - m_screenVisitOrder.push(currentScreen->GetScreenEnum()); + ProjectManagerScreen oldScreen = currentScreen->GetScreenEnum(); + m_screenVisitOrder.push(oldScreen); + } + + if (newScreen->IsTab()) + { + m_tabWidget->setCurrentWidget(newScreen); + m_screenStack->setCurrentWidget(m_tabWidget); } - m_screenStack->setCurrentWidget(iterator.value()); + else + { + m_screenStack->setCurrentWidget(newScreen); + } + + newScreen->NotifyCurrentScreen(); + return true; } } @@ -95,28 +120,52 @@ namespace O3DE::ProjectManager bool ScreensCtrl::GotoPreviousScreen() { - // Don't go back if we are on the first set screen - if (m_screenVisitOrder.top() != ProjectManagerScreen::Invalid) + if (!m_screenVisitOrder.isEmpty()) { // We do not check with screen if we can go back, we should always be able to go back - return ForceChangeToScreen(m_screenVisitOrder.pop(), false); + ProjectManagerScreen previousScreen = m_screenVisitOrder.pop(); + return ForceChangeToScreen(previousScreen, false); } return false; } void ScreensCtrl::ResetScreen(ProjectManagerScreen screen) { + bool shouldRestoreCurrentScreen = false; + if (GetCurrentScreen() && GetCurrentScreen()->GetScreenEnum() == screen) + { + shouldRestoreCurrentScreen = true; + } + // Delete old screen if it exists to start fresh DeleteScreen(screen); // Add new screen ScreenWidget* newScreen = BuildScreen(this, screen); - m_screenStack->addWidget(newScreen); + if (newScreen->IsTab()) + { + m_tabWidget->addTab(newScreen, newScreen->GetTabText()); + if (shouldRestoreCurrentScreen) + { + m_tabWidget->setCurrentWidget(newScreen); + m_screenStack->setCurrentWidget(m_tabWidget); + } + } + else + { + m_screenStack->addWidget(newScreen); + if (shouldRestoreCurrentScreen) + { + m_screenStack->setCurrentWidget(newScreen); + } + } + m_screenMap.insert(screen, newScreen); connect(newScreen, &ScreenWidget::ChangeScreenRequest, this, &ScreensCtrl::ChangeToScreen); connect(newScreen, &ScreenWidget::GotoPreviousScreenRequest, this, &ScreensCtrl::GotoPreviousScreen); connect(newScreen, &ScreenWidget::ResetScreenRequest, this, &ScreensCtrl::ResetScreen); + connect(newScreen, &ScreenWidget::NotifyCurrentProject, this, &ScreensCtrl::NotifyCurrentProject); } void ScreensCtrl::ResetAllScreens() @@ -133,8 +182,21 @@ namespace O3DE::ProjectManager const auto iter = m_screenMap.find(screen); if (iter != m_screenMap.end()) { - m_screenStack->removeWidget(iter.value()); - iter.value()->deleteLater(); + ScreenWidget* screenToDelete = iter.value(); + if (screenToDelete->IsTab()) + { + int tabIndex = m_tabWidget->indexOf(screenToDelete); + if (tabIndex > -1) + { + m_tabWidget->removeTab(tabIndex); + } + } + else + { + // if the screen we delete is the current widget, a new one will + // be selected automatically (randomly?) + m_screenStack->removeWidget(screenToDelete); + } // Erase does not cause a rehash so interators remain valid m_screenMap.erase(iter); @@ -149,4 +211,12 @@ namespace O3DE::ProjectManager } } + void ScreensCtrl::TabChanged([[maybe_unused]] int index) + { + ScreenWidget* screen = reinterpret_cast(m_tabWidget->currentWidget()); + if (screen) + { + screen->NotifyCurrentScreen(); + } + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.h b/Code/Tools/ProjectManager/Source/ScreensCtrl.h index 7912a314e3..935fc78e25 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.h +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.h @@ -18,6 +18,8 @@ #include #endif +QT_FORWARD_DECLARE_CLASS(QTabWidget) + namespace O3DE::ProjectManager { class ScreenWidget; @@ -35,6 +37,9 @@ namespace O3DE::ProjectManager ScreenWidget* FindScreen(ProjectManagerScreen screen); ScreenWidget* GetCurrentScreen(); + signals: + void NotifyCurrentProject(const QString& projectPath); + public slots: bool ChangeToScreen(ProjectManagerScreen screen); bool ForceChangeToScreen(ProjectManagerScreen screen, bool addVisit = true); @@ -43,11 +48,13 @@ namespace O3DE::ProjectManager void ResetAllScreens(); void DeleteScreen(ProjectManagerScreen screen); void DeleteAllScreens(); + void TabChanged(int index); private: QStackedWidget* m_screenStack; QHash m_screenMap; QStack m_screenVisitOrder; + QTabWidget* m_tabWidget; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp similarity index 55% rename from Code/Tools/ProjectManager/Source/ProjectSettingsCtrl.cpp rename to Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 95dcec3e18..b3180966ce 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -10,10 +10,10 @@ * */ -#include +#include #include #include -#include +#include #include #include @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager { - ProjectSettingsCtrl::ProjectSettingsCtrl(QWidget* parent) + UpdateProjectCtrl::UpdateProjectCtrl(QWidget* parent) : ScreenWidget(parent) { QVBoxLayout* vLayout = new QVBoxLayout(); @@ -34,28 +34,31 @@ namespace O3DE::ProjectManager QDialogButtonBox* backNextButtons = new QDialogButtonBox(); vLayout->addWidget(backNextButtons); - m_backButton = backNextButtons->addButton("Back", QDialogButtonBox::RejectRole); - m_nextButton = backNextButtons->addButton("Next", QDialogButtonBox::ApplyRole); + m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); - connect(m_backButton, &QPushButton::pressed, this, &ProjectSettingsCtrl::HandleBackButton); - connect(m_nextButton, &QPushButton::pressed, this, &ProjectSettingsCtrl::HandleNextButton); + connect(m_backButton, &QPushButton::pressed, this, &UpdateProjectCtrl::HandleBackButton); + connect(m_nextButton, &QPushButton::pressed, this, &UpdateProjectCtrl::HandleNextButton); + connect(reinterpret_cast(parent), &ScreensCtrl::NotifyCurrentProject, this, &UpdateProjectCtrl::UpdateCurrentProject); m_screensOrder = { - ProjectManagerScreen::NewProjectSettings, + ProjectManagerScreen::ProjectSettings, ProjectManagerScreen::GemCatalog }; m_screensCtrl->BuildScreens(m_screensOrder); - m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::NewProjectSettings, false); + m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::ProjectSettings, false); + UpdateNextButtonText(); + } - ProjectManagerScreen ProjectSettingsCtrl::GetScreenEnum() + ProjectManagerScreen UpdateProjectCtrl::GetScreenEnum() { - return ProjectManagerScreen::NewProjectSettingsCore; + return ProjectManagerScreen::UpdateProject; } - void ProjectSettingsCtrl::HandleBackButton() + void UpdateProjectCtrl::HandleBackButton() { if (!m_screensCtrl->GotoPreviousScreen()) { @@ -66,7 +69,7 @@ namespace O3DE::ProjectManager UpdateNextButtonText(); } } - void ProjectSettingsCtrl::HandleNextButton() + void UpdateProjectCtrl::HandleNextButton() { ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen(); ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); @@ -80,19 +83,18 @@ namespace O3DE::ProjectManager } } - if (screenEnum == ProjectManagerScreen::NewProjectSettings) + if (screenEnum == ProjectManagerScreen::ProjectSettings) { - auto newProjectScreen = reinterpret_cast(currentScreen); - if (newProjectScreen) + auto projectScreen = reinterpret_cast(currentScreen); + if (projectScreen) { - if (!newProjectScreen->Validate()) + if (!projectScreen->Validate()) { QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings")); return; } - m_projectInfo = newProjectScreen->GetProjectInfo(); - m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath(); + m_projectInfo = projectScreen->GetProjectInfo(); } } @@ -103,22 +105,35 @@ namespace O3DE::ProjectManager } else { - auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo); - if (result.IsSuccess()) + auto result = PythonBindingsInterface::Get()->UpdateProject(m_projectInfo); + if (result) { - // adding gems is not implemented yet because we don't know what targets to add or how to add them - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { - QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project.")); + QMessageBox::critical(this, tr("Project update failed"), tr("Failed to update project.")); } } } - void ProjectSettingsCtrl::UpdateNextButtonText() + void UpdateProjectCtrl::UpdateCurrentProject(const QString& projectPath) { - m_nextButton->setText(m_screensCtrl->GetCurrentScreen()->GetNextButtonText()); + auto projectResult = PythonBindingsInterface::Get()->GetProject(projectPath); + if (projectResult.IsSuccess()) + { + m_projectInfo = projectResult.GetValue(); + } + } + + void UpdateProjectCtrl::UpdateNextButtonText() + { + QString nextButtonText = tr("Continue"); + if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog) + { + nextButtonText = tr("Update Project"); + } + m_nextButton->setText(nextButtonText); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsCtrl.h b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h similarity index 83% rename from Code/Tools/ProjectManager/Source/ProjectSettingsCtrl.h rename to Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h index 42f1ce1978..ee871e7bb2 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsCtrl.h +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h @@ -21,17 +21,19 @@ namespace O3DE::ProjectManager { - class ProjectSettingsCtrl + class UpdateProjectCtrl : public ScreenWidget { public: - explicit ProjectSettingsCtrl(QWidget* parent = nullptr); - ~ProjectSettingsCtrl() = default; + explicit UpdateProjectCtrl(QWidget* parent = nullptr); + ~UpdateProjectCtrl() = default; ProjectManagerScreen GetScreenEnum() override; + protected slots: void HandleBackButton(); void HandleNextButton(); + void UpdateCurrentProject(const QString& projectPath); private: void UpdateNextButtonText(); @@ -41,8 +43,9 @@ namespace O3DE::ProjectManager QPushButton* m_nextButton; QVector m_screensOrder; - QString m_projectTemplatePath; ProjectInfo m_projectInfo; + + ProjectManagerScreen m_screenEnum; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/main.cpp b/Code/Tools/ProjectManager/Source/main.cpp index 3d8bb71a0c..cbeacbaf65 100644 --- a/Code/Tools/ProjectManager/Source/main.cpp +++ b/Code/Tools/ProjectManager/Source/main.cpp @@ -35,7 +35,6 @@ int main(int argc, char* argv[]) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - AZ::AllocatorInstance::Create(); int runSuccess = 0; { @@ -55,6 +54,12 @@ int main(int argc, char* argv[]) O3DE::ProjectManager::ProjectManagerWindow window(nullptr, engineRootPath); window.show(); + // somethings is preventing us from moving the window to the center of the + // primary screen - likely an Az style or component helper + constexpr int width = 1200; + constexpr int height = 800; + window.resize(width, height); + runSuccess = app.exec(); } AZ::AllocatorInstance::Destroy(); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 16bc8cf965..223465f3c8 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -21,8 +21,6 @@ set(FILES Source/ScreenWidget.h Source/EngineInfo.h Source/EngineInfo.cpp - Source/FirstTimeUseScreen.h - Source/FirstTimeUseScreen.cpp Source/FormLineEditWidget.h Source/FormLineEditWidget.cpp Source/FormBrowseEditWidget.h @@ -33,7 +31,6 @@ set(FILES Source/ProjectManagerWindow.cpp Source/ProjectTemplateInfo.h Source/ProjectTemplateInfo.cpp - Source/ProjectManagerWindow.ui Source/PythonBindings.h Source/PythonBindings.cpp Source/PythonBindingsInterface.h @@ -41,20 +38,27 @@ set(FILES Source/ProjectInfo.cpp Source/NewProjectSettingsScreen.h Source/NewProjectSettingsScreen.cpp - Source/ProjectSettingsCtrl.h - Source/ProjectSettingsCtrl.cpp - Source/ProjectsHomeScreen.h - Source/ProjectsHomeScreen.cpp - Source/ProjectsHomeScreen.ui + Source/CreateProjectCtrl.h + Source/CreateProjectCtrl.cpp + Source/UpdateProjectCtrl.h + Source/UpdateProjectCtrl.cpp + Source/ProjectsScreen.h + Source/ProjectsScreen.cpp Source/ProjectSettingsScreen.h Source/ProjectSettingsScreen.cpp Source/ProjectSettingsScreen.ui Source/EngineSettingsScreen.h Source/EngineSettingsScreen.cpp + Source/ProjectButtonWidget.h + Source/ProjectButtonWidget.cpp + Source/ScreenHeaderWidget.h + Source/ScreenHeaderWidget.cpp Source/LinkWidget.h Source/LinkWidget.cpp Source/TagWidget.h Source/TagWidget.cpp + Source/GemCatalog/GemCatalogHeaderWidget.h + Source/GemCatalog/GemCatalogHeaderWidget.cpp Source/GemCatalog/GemCatalogScreen.h Source/GemCatalog/GemCatalogScreen.cpp Source/GemCatalog/GemFilterWidget.h @@ -67,6 +71,8 @@ set(FILES Source/GemCatalog/GemItemDelegate.cpp Source/GemCatalog/GemListView.h Source/GemCatalog/GemListView.cpp + Source/GemCatalog/GemListHeaderWidget.h + Source/GemCatalog/GemListHeaderWidget.cpp Source/GemCatalog/GemModel.h Source/GemCatalog/GemModel.cpp Source/GemCatalog/GemSortFilterProxyModel.h diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp index 6fd664eee4..4ab41423fa 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp @@ -38,7 +38,6 @@ namespace AZ { namespace FbxSceneBuilder { - static AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler* g_fbxImporter = nullptr; static AZStd::vector g_componentDescriptors; void Reflect(AZ::SerializeContext* /*context*/) @@ -99,13 +98,6 @@ namespace AZ g_componentDescriptors.clear(); g_componentDescriptors.shrink_to_fit(); } - - if (g_fbxImporter) - { - g_fbxImporter->Deactivate(); - delete g_fbxImporter; - g_fbxImporter = nullptr; - } } } // namespace FbxSceneBuilder } // namespace SceneAPI @@ -114,11 +106,6 @@ namespace AZ extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env) { AZ::Environment::Attach(static_cast(env)); - if (!AZ::SceneAPI::FbxSceneBuilder::g_fbxImporter) - { - AZ::SceneAPI::FbxSceneBuilder::g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler(); - AZ::SceneAPI::FbxSceneBuilder::g_fbxImporter->Activate(); - } } extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context) { diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index ebdb57e452..a8b059304d 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -32,7 +32,7 @@ namespace AZ if (auto serializeContext = azrtti_cast(context); serializeContext) { serializeContext->Class() - ->Version(1) + ->Version(2) ->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions); } } @@ -61,22 +61,15 @@ namespace AZ { serializeContext->Class()->Version(1)->Attribute( AZ::Edit::Attributes::SystemComponentTags, - AZStd::vector({AssetBuilderSDK::ComponentTags::AssetBuilder})); + AZStd::vector( + {AssetBuilderSDK::ComponentTags::AssetBuilder, + AssetImportRequest::GetAssetImportRequestComponentTag()})); } } void FbxImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set& extensions) { - // It's unlikely an empty file extension list is intentional, - // so if it's empty, try reloading it from the registry. - if (m_settings.m_supportedFileTypeExtensions.empty()) - { - if (auto* settingsRegistry = AZ::SettingsRegistry::Get()) - { - settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter"); - } - } extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end()); } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp index 193a1f9fd5..c0d1fc3bd0 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp @@ -37,7 +37,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(2); } } diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/Utilities/SceneUtilities.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/Utilities/SceneUtilities.cpp index f5bb9d28a6..ac27ed54eb 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/Utilities/SceneUtilities.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/Utilities/SceneUtilities.cpp @@ -79,7 +79,7 @@ namespace AZ if (coordinateSystemRule->GetScale() != 1.0f) { float scale = coordinateSystemRule->GetScale(); - matrix.MultiplyByScale(Vector3(scale, scale, scale)); + matrix.MultiplyByScale(Vector3(scale)); } if (!coordinateSystemRule->GetOriginNodeName().empty()) { diff --git a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h index a2f9450bce..2deed280db 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h +++ b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h @@ -71,6 +71,11 @@ namespace AZ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; using MutexType = AZStd::recursive_mutex; + static AZ::Crc32 GetAssetImportRequestComponentTag() + { + return AZ_CRC_CE("AssetImportRequest"); + } + virtual ~AssetImportRequest() = 0; //! Fills the given list with all available file extensions, excluding the extension for the manifest. diff --git a/Code/Tools/SerializeContextTools/Application.cpp b/Code/Tools/SerializeContextTools/Application.cpp index da0cca5ca6..81cc314deb 100644 --- a/Code/Tools/SerializeContextTools/Application.cpp +++ b/Code/Tools/SerializeContextTools/Application.cpp @@ -97,6 +97,11 @@ namespace AZ return m_configFilePath.c_str(); } + void Application::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const + { + appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool; + } + void Application::SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) { AZ::ComponentApplication::SetSettingsRegistrySpecializations(specializations); diff --git a/Code/Tools/SerializeContextTools/Application.h b/Code/Tools/SerializeContextTools/Application.h index b1b818e27d..b4c02b87c2 100644 --- a/Code/Tools/SerializeContextTools/Application.h +++ b/Code/Tools/SerializeContextTools/Application.h @@ -28,6 +28,7 @@ namespace AZ const char* GetConfigFilePath() const; AZ::ComponentTypeList GetRequiredSystemComponents() const override; + void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override; protected: void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override; diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index a18a6ff3a3..3fbf7cb25c 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -25,8 +25,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -36,7 +39,6 @@ // SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data, // and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs. -// This converter is still in an early state. It can convert trivial slices, but it cannot handle nested slices yet. // // If the slice contains legacy data, it will print out warnings / errors about the data that couldn't be serialized. // The prefab will be generated without that data. @@ -70,6 +72,20 @@ namespace AZ AZ_Error("Convert-Slice", false, "No json registration context found."); return false; } + + // Connect to the Asset Processor so that we can get the correct source path to any nested slice references. + if (!ConnectToAssetProcessor()) + { + AZ_Error("Convert-Slice", false, " Failed to connect to the Asset Processor.\n"); + return false; + } + + // Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus + // so that the OnCatalogLoaded event gets processed now, instead of during application shutdown. + AZ::Data::AssetCatalogRequestBus::Broadcast( + &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); + application.Tick(); + AZStd::string logggingScratchBuffer; SetupLogging(logggingScratchBuffer, convertSettings.m_reporting, *commandLine); @@ -80,83 +96,90 @@ namespace AZ verifySettings.m_serializeContext = application.GetSerializeContext(); SetupLogging(logggingScratchBuffer, verifySettings.m_reporting, *commandLine); - auto archiveInterface = AZ::Interface::Get(); - - // Find the Prefab System Component for use in creating and saving the prefab - AZ::Entity* systemEntity = application.FindEntity(AZ::SystemEntityId); - AZ_Assert(systemEntity != nullptr, "System entity doesn't exist."); - auto prefabSystemComponent = systemEntity->FindComponent(); - AZ_Assert(prefabSystemComponent != nullptr, "Prefab System component doesn't exist"); - bool result = true; rapidjson::StringBuffer scratchBuffer; + // Loop through the list of requested files and convert them. AZStd::vector fileList = Utilities::ReadFileListFromCommandLine(application, "files"); for (AZStd::string& filePath : fileList) { - bool packOpened = false; + bool convertResult = ConvertSliceFile(convertSettings.m_serializeContext, filePath, isDryRun); + result = result && convertResult; + } - AZ::IO::Path outputPath = filePath; - outputPath.ReplaceExtension("prefab"); + DisconnectFromAssetProcessor(); + return result; + } - AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n"); - AZ_Printf("Convert-Slice", "Converting '%s' to '%s'\n", filePath.c_str(), outputPath.c_str()); + bool SliceConverter::ConvertSliceFile( + AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun) + { + bool result = true; + bool packOpened = false; - AZ::IO::Path inputPath = filePath; - auto fileExtension = inputPath.Extension(); - if (fileExtension == ".ly") - { - // Special case: for level files, we need to open the .ly zip file and convert the levelentities.editor_xml file - // inside of it. All the other files can be ignored as they are deprecated legacy system files that are no longer - // loaded with prefab-based levels. - packOpened = archiveInterface->OpenPack(filePath); - inputPath.ReplaceFilename("levelentities.editor_xml"); - AZ_Warning("Convert-Slice", packOpened, " '%s' could not be opened as a pack file.\n", filePath.c_str()); - } - else - { - AZ_Warning( - "Convert-Slice", (fileExtension == ".slice"), - " Warning: Only .ly and .slice files are supported, conversion of '%.*s' may not work.\n", - AZ_STRING_ARG(fileExtension.Native())); - } + auto archiveInterface = AZ::Interface::Get(); - auto callback = [prefabSystemComponent, &outputPath, isDryRun] - (void* classPtr, const Uuid& classId, [[maybe_unused]] SerializeContext* context) - { - if (classId != azrtti_typeid()) - { - AZ_Printf("Convert-Slice", " File not converted: Slice root is not an entity.\n"); - return false; - } + AZ::IO::Path outputPath = slicePath; + outputPath.ReplaceExtension("prefab"); - AZ::Entity* rootEntity = reinterpret_cast(classPtr); - return ConvertSliceFile(prefabSystemComponent, outputPath, isDryRun, rootEntity); - }; + AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n"); + AZ_Printf("Convert-Slice", "Converting '%s' to '%s'\n", slicePath.c_str(), outputPath.c_str()); - if (!Utilities::InspectSerializedFile(inputPath.c_str(), convertSettings.m_serializeContext, callback)) - { - AZ_Warning("Convert-Slice", false, "Failed to load '%s'. File may not contain an object stream.", inputPath.c_str()); - result = false; - } + AZ::IO::Path inputPath = slicePath; + auto fileExtension = inputPath.Extension(); + if (fileExtension == ".ly") + { + // Special case: for level files, we need to open the .ly zip file and convert the levelentities.editor_xml file + // inside of it. All the other files can be ignored as they are deprecated legacy system files that are no longer + // loaded with prefab-based levels. + packOpened = archiveInterface->OpenPack(slicePath); + inputPath.ReplaceFilename("levelentities.editor_xml"); + AZ_Warning("Convert-Slice", packOpened, " '%s' could not be opened as a pack file.\n", slicePath.c_str()); + } + else + { + AZ_Warning( + "Convert-Slice", (fileExtension == ".slice"), + " Warning: Only .ly and .slice files are supported, conversion of '%.*s' may not work.\n", + AZ_STRING_ARG(fileExtension.Native())); + } - if (packOpened) + auto callback = [&outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context) + { + if (classId != azrtti_typeid()) { - [[maybe_unused]] bool closeResult = archiveInterface->ClosePack(filePath); - AZ_Warning("Convert-Slice", closeResult, "Failed to close '%s'.", filePath.c_str()); + AZ_Printf("Convert-Slice", " File not converted: Slice root is not an entity.\n"); + return false; } - AZ_Printf("Convert-Slice", "Finished converting '%s' to '%s'\n", filePath.c_str(), outputPath.c_str()); - AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n"); + AZ::Entity* rootEntity = reinterpret_cast(classPtr); + return ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity); + }; + + // Read in the slice file and call the callback on completion to convert the read-in slice to a prefab. + if (!Utilities::InspectSerializedFile(inputPath.c_str(), serializeContext, callback)) + { + AZ_Warning("Convert-Slice", false, "Failed to load '%s'. File may not contain an object stream.", inputPath.c_str()); + result = false; + } + + if (packOpened) + { + [[maybe_unused]] bool closeResult = archiveInterface->ClosePack(slicePath); + AZ_Warning("Convert-Slice", closeResult, "Failed to close '%s'.", slicePath.c_str()); } + AZ_Printf("Convert-Slice", "Finished converting '%s' to '%s'\n", slicePath.c_str(), outputPath.c_str()); + AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n"); + return result; } - bool SliceConverter::ConvertSliceFile( - AzToolsFramework::Prefab::PrefabSystemComponent* prefabSystemComponent, AZ::IO::PathView outputPath, bool isDryRun, - AZ::Entity* rootEntity) + bool SliceConverter::ConvertSliceToPrefab( + AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity) { + auto prefabSystemComponentInterface = AZ::Interface::Get(); + // Find the slice from the root entity. SliceComponent* sliceComponent = AZ::EntityUtils::FindFirstDerivedComponent(rootEntity); if (sliceComponent == nullptr) @@ -167,44 +190,21 @@ namespace AZ // Get all of the entities from the slice. SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities(); - if (sliceEntities.empty()) - { - AZ_Printf("Convert-Slice", " File not converted: Slice entities could not be retrieved.\n"); - return false; - } - - AZ_Warning("Convert-Slice", sliceComponent->GetSlices().empty(), " Slice depends on other slices, this conversion will lose data.\n"); + AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size()); // Create the Prefab with the entities from the slice AZStd::unique_ptr sourceInstance( - prefabSystemComponent->CreatePrefab(sliceEntities, {}, outputPath)); + prefabSystemComponentInterface->CreatePrefab(sliceEntities, {}, outputPath)); // Dispatch events here, because prefab creation might trigger asset loads in rare circumstances. AZ::Data::AssetManager::Instance().DispatchEvents(); - // Set up the Prefab container entity to be a proper Editor entity. (This logic is normally triggered - // via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.) + // Fix up the container entity to have the proper components and fix up the slice entities to have the proper hierarchy + // with the container as the top-most parent. AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity(); - AzToolsFramework::EditorEntityContextRequestBus::Broadcast( - &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, container->get()); - container->get().AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent()); - - // Reparent any root-level slice entities to the container entity. - for (auto entity : sliceEntities) - { - AzToolsFramework::Components::TransformComponent* transformComponent = - entity->FindComponent(); - if (transformComponent) - { - if (!transformComponent->GetParentId().IsValid()) - { - transformComponent->SetParent(container->get().GetId()); - } - } - } + FixPrefabEntities(container->get(), sliceEntities); auto templateId = sourceInstance->GetTemplateId(); - if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) { AZ_Printf("Convert-Slice", " Path error. Path could be invalid, or the prefab may not be loaded in this level.\n"); @@ -219,14 +219,27 @@ namespace AZ AZ_Printf("Convert-Slice", " Failed to convert prefab instance data to a PrefabDom.\n"); return false; } - prefabSystemComponent->UpdatePrefabTemplate(templateId, prefabDom); + prefabSystemComponentInterface->UpdatePrefabTemplate(templateId, prefabDom); // Dispatch events here, because prefab serialization might trigger asset loads in rare circumstances. AZ::Data::AssetManager::Instance().DispatchEvents(); + // If this slice has nested slices, we need to loop through those, convert them to prefabs as well, and + // set up the new nesting relationships correctly. + const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices(); + AZ_Printf("Convert-Slice", " Slice contains %zu nested slices.\n", sliceList.size()); + if (!sliceList.empty()) + { + bool nestedSliceResult = ConvertNestedSlices(sliceComponent, sourceInstance.get(), serializeContext, isDryRun); + if (!nestedSliceResult) + { + return false; + } + } + if (isDryRun) { - PrintPrefab(prefabDom, sourceInstance->GetTemplateSourcePath()); + PrintPrefab(templateId); return true; } else @@ -235,8 +248,187 @@ namespace AZ } } - void SliceConverter::PrintPrefab(const AzToolsFramework::Prefab::PrefabDom& prefabDom, const AZ::IO::Path& templatePath) + void SliceConverter::FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities) { + // Set up the Prefab container entity to be a proper Editor entity. (This logic is normally triggered + // via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.) + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, containerEntity); + containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent()); + + // Reparent any root-level slice entities to the container entity. + for (auto entity : sliceEntities) + { + AzToolsFramework::Components::TransformComponent* transformComponent = + entity->FindComponent(); + if (transformComponent) + { + if (!transformComponent->GetParentId().IsValid()) + { + transformComponent->SetParent(containerEntity.GetId()); + transformComponent->UpdateCachedWorldTransform(); + } + } + } + } + + bool SliceConverter::ConvertNestedSlices( + SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance, + AZ::SerializeContext* serializeContext, bool isDryRun) + { + const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices(); + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + for (auto& slice : sliceList) + { + // Get the nested slice asset + auto sliceAsset = slice.GetSliceAsset(); + sliceAsset.QueueLoad(); + sliceAsset.BlockUntilLoadComplete(); + + // The slice list gives us asset IDs, and we need to get to the source path. So first we get the asset path from the ID, + // then we get the source path from the asset path. + + AZStd::string processedAssetPath; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + processedAssetPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, sliceAsset.GetId()); + + AZStd::string assetPath; + AzToolsFramework::AssetSystemRequestBus::Broadcast( + &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, + processedAssetPath, assetPath); + if (assetPath.empty()) + { + AZ_Warning("Convert-Slice", false, + " Source path for nested slice '%s' could not be found, slice not converted.", processedAssetPath.c_str()); + return false; + } + + // Now, convert the nested slice to a prefab. + bool nestedSliceResult = ConvertSliceFile(serializeContext, assetPath, isDryRun); + if (!nestedSliceResult) + { + AZ_Warning("Convert-Slice", nestedSliceResult, " Nested slice '%s' could not be converted.", assetPath.c_str()); + return false; + } + + // Load the prefab template for the newly-created nested prefab. + // To get the template, we need to take our absolute slice path and turn it into a project-relative prefab path. + AZ::IO::Path nestedPrefabPath = assetPath; + nestedPrefabPath.ReplaceExtension("prefab"); + + auto prefabLoaderInterface = AZ::Interface::Get(); + nestedPrefabPath = prefabLoaderInterface->GetRelativePathToProject(nestedPrefabPath); + + AzToolsFramework::Prefab::TemplateId nestedTemplateId = + prefabSystemComponentInterface->GetTemplateIdFromFilePath(nestedPrefabPath); + AzToolsFramework::Prefab::TemplateReference nestedTemplate = + prefabSystemComponentInterface->FindTemplate(nestedTemplateId); + + // For each slice instance of the nested slice, convert it to a nested prefab instance instead. + + auto instances = slice.GetInstances(); + AZ_Printf( + "Convert-Slice", " Attaching %zu instances of nested slice '%s'.\n", instances.size(), + nestedPrefabPath.Native().c_str()); + + for (auto& instance : instances) + { + bool instanceConvertResult = ConvertSliceInstance(instance, sliceAsset, nestedTemplate, sourceInstance); + if (!instanceConvertResult) + { + return false; + } + } + } + + return true; + } + + bool SliceConverter::ConvertSliceInstance( + [[maybe_unused]] AZ::SliceComponent::SliceInstance& instance, + [[maybe_unused]] AZ::Data::Asset& sliceAsset, + AzToolsFramework::Prefab::TemplateReference nestedTemplate, + AzToolsFramework::Prefab::Instance* topLevelInstance) + { + auto instanceToTemplateInterface = AZ::Interface::Get(); + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + // Create a new unmodified prefab Instance for the nested slice instance. + auto nestedInstance = AZStd::make_unique(); + AzToolsFramework::Prefab::Instance::EntityList newEntities; + if (!AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom( + *nestedInstance, newEntities, nestedTemplate->get().GetPrefabDom())) + { + AZ_Error( + "Convert-Slice", false, " Failed to load and instantiate nested Prefab Template '%s'.", + nestedTemplate->get().GetFilePath().c_str()); + return false; + } + + // Get the DOM for the unmodified nested instance. This will be used later below for generating the correct patch + // to the top-level template DOM. + AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom; + instanceToTemplateInterface->GenerateDomForInstance(unmodifiedNestedInstanceDom, *(nestedInstance.get())); + + // Currently, DataPatch conversions for nested slices aren't implemented, so all nested slice overrides will + // be lost. + AZ_Warning( + "Convert-Slice", false, " Nested slice instances will lose all of their override data during conversion.", + nestedTemplate->get().GetFilePath().c_str()); + + // Set the container entity of the nested prefab to have the top-level prefab as the parent. + // Once DataPatch conversions are supported, this will need to change to nest the prefab under the appropriate entity + // within the level. + auto containerEntity = nestedInstance->GetContainerEntity(); + AzToolsFramework::Components::TransformComponent* transformComponent = + containerEntity->get().FindComponent(); + if (transformComponent) + { + transformComponent->SetParent(topLevelInstance->GetContainerEntityId()); + transformComponent->UpdateCachedWorldTransform(); + } + + // Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance, + // create a patch out of it, and patch the top-level prefab template. + + AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore; + instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance); + + AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance)); + + AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter; + instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomAfter, *topLevelInstance); + + AzToolsFramework::Prefab::PrefabDom addedInstancePatch; + instanceToTemplateInterface->GeneratePatch(addedInstancePatch, topLevelInstanceDomBefore, topLevelInstanceDomAfter); + instanceToTemplateInterface->PatchTemplate(addedInstancePatch, topLevelInstance->GetTemplateId()); + + // Get the DOM for the modified nested instance. Now that the data has been fixed up, and the instance has been added + // to the top-level instance, we've got all the changes we need to generate the correct patch. + + AzToolsFramework::Prefab::PrefabDom modifiedNestedInstanceDom; + instanceToTemplateInterface->GenerateDomForInstance(modifiedNestedInstanceDom, addedInstance); + + AzToolsFramework::Prefab::PrefabDom linkPatch; + instanceToTemplateInterface->GeneratePatch(linkPatch, unmodifiedNestedInstanceDom, modifiedNestedInstanceDom); + + prefabSystemComponentInterface->CreateLink( + topLevelInstance->GetTemplateId(), addedInstance.GetTemplateId(), addedInstance.GetInstanceAlias(), linkPatch, + AzToolsFramework::Prefab::InvalidLinkId); + prefabSystemComponentInterface->PropagateTemplateChanges(topLevelInstance->GetTemplateId()); + + return true; + } + + void SliceConverter::PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId) + { + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + auto prefabTemplate = prefabSystemComponentInterface->FindTemplate(templateId); + auto& prefabDom = prefabTemplate->get().GetPrefabDom(); + const AZ::IO::Path& templatePath = prefabTemplate->get().GetFilePath(); + rapidjson::StringBuffer prefabBuffer; rapidjson::PrettyWriter writer(prefabBuffer); prefabDom.Accept(writer); @@ -260,5 +452,41 @@ namespace AZ return true; } + bool SliceConverter::ConnectToAssetProcessor() + { + AzFramework::AssetSystem::ConnectionSettings connectionSettings; + AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings); + + connectionSettings.m_launchAssetProcessorOnFailedConnection = true; + connectionSettings.m_connectionDirection = + AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor; + connectionSettings.m_connectionIdentifier = AzFramework::AssetSystem::ConnectionIdentifiers::Editor; + connectionSettings.m_loggingCallback = [](AZStd::string_view logData) + { + AZ_Printf("Convert-Slice", "%.*s\n", AZ_STRING_ARG(logData)); + }; + + bool connectedToAssetProcessor = false; + + AzFramework::AssetSystemRequestBus::BroadcastResult( + connectedToAssetProcessor, &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, + connectionSettings); + + return connectedToAssetProcessor; + } + + void SliceConverter::DisconnectFromAssetProcessor() + { + AzFramework::AssetSystemRequestBus::Broadcast( + &AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor); + + // Wait for the disconnect to finish. + bool disconnected = false; + AzFramework::AssetSystemRequestBus::BroadcastResult(disconnected, + &AzFramework::AssetSystem::AssetSystemRequests::WaitUntilAssetProcessorDisconnected, AZStd::chrono::seconds(30)); + + AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully."); + } + } // namespace SerializeContextTools } // namespace AZ diff --git a/Code/Tools/SerializeContextTools/SliceConverter.h b/Code/Tools/SerializeContextTools/SliceConverter.h index 90dfa0d50a..a977095f02 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.h +++ b/Code/Tools/SerializeContextTools/SliceConverter.h @@ -42,11 +42,20 @@ namespace AZ static bool ConvertSliceFiles(Application& application); private: - - static bool ConvertSliceFile(AzToolsFramework::Prefab::PrefabSystemComponent* prefabSystemComponent, - AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity); - - static void PrintPrefab(const AzToolsFramework::Prefab::PrefabDom& prefabDom, const AZ::IO::Path& templatePath); + static bool ConnectToAssetProcessor(); + static void DisconnectFromAssetProcessor(); + + static bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun); + static bool ConvertSliceToPrefab( + AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity); + static void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities); + static bool ConvertNestedSlices( + SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance, + AZ::SerializeContext* serializeContext, bool isDryRun); + static bool ConvertSliceInstance( + AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset& sliceAsset, + AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance); + static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); static bool SavePrefab(AzToolsFramework::Prefab::TemplateId templateId); }; } // namespace SerializeContextTools diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py index 7b55c118cd..fff5c4b59a 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from __future__ import annotations import logging -from PySide2.QtGui import QPixmap +from PySide2.QtGui import QIcon from PySide2.QtWidgets import (QMainWindow, QStackedWidget, QWidget) from model import (error_messages, view_size_constants) @@ -43,7 +43,7 @@ class ViewManager(object): def __init__(self) -> None: if ViewManager.__instance is None: self._main_window: QMainWindow = QMainWindow() - self._main_window.setWindowIcon(QPixmap(":/stylesheet/img/ly_application_icon.png")) + self._main_window.setWindowIcon(QIcon(":/Application/res/o3de_editor.ico")) self._main_window.setWindowTitle("Resource Mapping") self._main_window.setGeometry(0, 0, view_size_constants.TOOL_APPLICATION_MAIN_WINDOW_WIDTH, diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index 90e702b997..0ee7455e6c 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -50,6 +50,7 @@ if __name__ == "__main__": from manager.thread_manager import ThreadManager from manager.view_manager import ViewManager from style import azqtcomponents_resources + from style import editormainwindow_resources except ImportError as e: logger.error(f"Failed to import module [{e.name}] {e}") environment_utils.cleanup_qt_environment() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py new file mode 100644 index 0000000000..fb819cac34 --- /dev/null +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py @@ -0,0 +1,27147 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +from PySide2 import QtCore + +qt_resource_data = b"\ +\x00\x00\x01\x84\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01KIDATx\xdac`\x18\x05\xa3`\ +\x14\x0c&\x10\x91\x90\xe3\x18\x99\x98{\x08\x88\xf7\x810\ +\x90\xbf\x1f\xca\xd6\xa6\x8b\x03\x80\x165\x01-\xfd\x0f\xc3\ +@>\x8c\x1dK\xaf\x10h\x00Y\x0a\xc3@\xfe_(\ +{5\x10\x9b\x01\xf9V@\x1a\x8e\x89\xe4[\x03\xb1>\ +\xa5\x0e\x80\x87\x06\x05|\xf5\x81v\x80\x1e1i\xa0\x01\ +\x1a\xe7\x7f\xa1\x96\xff\x83\xf2o\x00\xf1\x19 \xff\x0c\x88\ +\x86a\x12\xf8\x97\x81X\x85\x9c\x10\x00\xd1\x13\xe8\x99\x0d\ +\xd1\x1d\xf0\x05Hs\x00\xb1\x1b\x10_\x07\xf2o\x02\xe9\ +\x1b0\x0c\x0a\x192\xf8\xb7\x80\xf80\x10\xb3\x12\xe3\x80\ +\x1ah\xd4<\xa4B\x1a@\xe6\xcf%\x94\x06@q\xff\ +\x1d\xea\xa8tX\xba\x80\xa6\x89\xbfhi\x84T>\xc8\ +,)b\xa2\xa0\x0c\xea\xa8gxr\x05\xa9|\xdc\xbe\ +Gs\xc0+ f\x06\xf23)\xb4\x10\x1b_\x94\x18\ +\x07\xe4A\xf9\xaf\xa9\xec\x80i\x84\xca\x81F\xa0\xa2W\ +@\xcc\x04\xc4i0C(\x8c\xf3\xbf\xd04\xf5\x03\xc8\ +\x97!\x94\x0d\xdb\x81\x8ar\xa0\x8ey\x8a%\x15S\x12\ +\x02\xd3\x88)\x07R\xa0\x96gQ1\xceA\xec_@\ +,A\x8c\x03\x98\x81\x0a9\xa1\x89\x90\x9a\xf9~\x1a)\ +\xa5\xa1\x0a\x10o\x07\xe2\xcd@\x8d\x9b\x81\xf4&\x18&\ +\x93\xbf\x14\x88yF\x9b{\xa3`\x14\x0cZ\x00\x00\xef\ +\xad\x00\xe1,\x84\xf5\xf4\x00\x00\x00\x00IEND\xae\ +B`\x82\ +\x00\x00\x02h\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x02/IDATx\xda\xed\x96\xcb+\xc4Q\ +\x14\xc7\xe7\xe11\xcd\x90\xc9L\x1a\x22e\xe4\x91YL\ +\x13\x8a\xa2lllX\xa1\x94WQ\xc6\xb3\xd8x\xd4\ +\x10\x0bE\x8d\x14\xfe\x03\x0b\x89\xb2\x96\xc4\xc2B\xf2(\ +\x22\x0by,lH\x16\x1e5\x8d\xef\xad\xab\x8e\x99{\ +~\xbfYZ\xfcn};\xbf\xee\xe7\x9e\xf3;\xfd\xee\ +\xb9\xe7wM\xa6\xff0Z;\x07J\xa0\xf5\x96\x8e\xfe\ +a\x8d5\xed\xe0\x1b\xb0\x95\x0c\xb7\x83\x87aW\xc4\xb3\ +j\x0dx\x00l\x13\xea\x8au>\x82\xa2X\x10\x85\xf5\ ++\x82g\x13~\xcf$0.\xb9P\x88I\xe0Jr\ +!/u\xde!/(P\x04\xcf\x80\x22\x92\x9f0\x09\ +t\x93\x04\x82L\x02\x07\x92G \x0f\x056\xa8\x07\x93\ +\x15\xdc\x16\x80\x8bm\x0a\xc2:5\xb6\xa9\x09\xbcY\x83\ +\xa7\x83\x8b\x18>\x931\x8c\xa1(\x90L\x14\x87Uk\ +\x0d\xb8[\x87\xdb\x11'M\xe7=.\xc8\x1c;9)\ +\x8f\xe1\x13\xacC\xe1d\x85\xce\xe51[e\x02W\x83\ +G\xe41\xaba\x12\x5c\x94\xfc\x02\xb2Q\xe7k\xd2\x07\ +j\x15\xc1\xbd\x84\xbf0\x09\xcc\x93>\x10Vp3\xf8\ +\x03iDe43\xd1f_1y\xc8m\x03\xe6\xb7\ +\xc0\xdf`\xc7\x18^\x0c~'^\x02\x952I\x0e\x81\ +\xbdC\xdb\x90%\x16Z\xf4\xeaD\xafF\x12\xac5\xab\ +q\xe2\x8c\xa1*\x8c\x5ch\x19E\xd6\xa6\xb1\xa6\x01\x5c\ +\x5c6\xb8\x0aO\x02\x17\xfdd\x1aJf\x8aX\x1c\xe7\ +5\xa81\xd6y\x9f\x9cs\x9f\x22x\x16\xe1\xb7L\x02\ +#\xa4\x0fL0\x09\x9c\x91>\x90O\xc1\xee\xaf3l\ +\x91\xc2\xd1E\xf8%\x13|\xf07Ih\x94I\xf2X\ +r\xa1\xbc?\xff\x01\xd1\x8e1Y\xaf\xb1\x05U\xe0!\ +\xd8\x1c\x8d>\xd1\xcb\xdd\x86$\xf7\x80\xcfp\xad\xda\x18\ +F/p\xc6\xfd\xa1\xe2\x8b(3\x81\x0b\x89C\xef\xe2\ +\xa3\x9a\xec\x83>\x10\xe0\x146\x85q\x14G\xf5\x0bv\ +\x96\xe1~\xf0gq_\x80\x02L\x82S`\x22\xc6\xde\ +\x9ff\x05pC\xcey\x9d\xc2\xb1\x90\xf0W&\xf8\x02\ +\xe9\x03K\x0an\x01\x7f$}\xa0\x8cf\x1f\x84>e\ +\xa7J\xd5\xf9\x02s\x09|\x81r&I\xd1\xaa\xbf\xe3\ +\xbe\x00\xb9\x94\xea\xd5\x80\xde\xa5\xd4\x91@\x0d\xb8\xffU\ +\xf1\xff\x00\xf0W\x81\xb2\xb1-\xb20\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\xa1\xcb\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0b\x22\x00\x00\x0b\x22\x01\x09\xe1\ +O\xa2\x00\x00\xa1`IDATx^]\xfdg{\ +cI\xb2\xa5\x89\xf2\x0fMF\x04\xb5\x04@\x90\x00\x08\ +j\xad\xb5\xd6Zk\x15\xd4:\x14\x19Z\xeb\x88\x8c\x94\ +\x91:\xab\xb2\xeaTU\xd7Q=\xdd}\xfav\xf7\xcc\ +<\xf7\xfe\x13\xbb\xef\xf2MFe\xcf\x07{\x00\x92 \ +\xf6\xden\xcb\xcc\x96\xb9\x9b\x9b\xc7\x84\xc7\x1f[\x04\xc9\ +Ar'\x9eZ\xf1\xec+\xabX\xfe`5\xeb\xdfX\ +\xc3\xd6\xf7\xd6\xb4\xf3\xa35\xec|o5\x1b_Y\xc5\ +\xea;+Y|n\x05\xb3\x0f-:qf\xe1\xa1c\ +\xcb\xea\xdd\xb2@\xfb\x92\xa57MYZ\xfd\x88\xa5\xd6\ +\x0dyR?li\x0dc\x96\xd68\x8eLx\xaf\xfc\ +\x9cZ7b)\xb5\xc3\x96\x5c3dI\xd5\xc3\xc8\xa8\ +%\xd5LXr\xdd\x8c\xa54,Xj\xd3\x8a\xa5\xb7\ +\xae\x9b\xafs\xdb\x02\xbd\x07\x16\x1c\xe0\x1a\x83\xd7,{\ +Hr\x82\x1c\xf2\xbb=\xcb\xec\xdf\xb6\xcc\xbe\x0dd\x1d\ +Y\xb3\xcc\xde\x15\xcb\xec^\xb2\xcc\xae\x05\xf3\xb7\xcdX\ +F\xd3\xa8\xa5\xd6\xf6ZRE\xab\xc5\x17\xd7ZlA\ +\xa5]\xce\xaf\xb0K\x055v\xa5\xb8\xd5\x12\xaa\x87,\ +\xade\xd1\xb2\x07\x0e\xadp\xe6\xa1U\xae\xbc\xb3\xba\xcd\ +o\xac~\xeb;\xabZ\xfb\xc2\x0a\xe7\x9e3.w,\ +\x93k\xfb\xfa\x0e\xcc\xd7\xb3o\xfen^\xbb\x90\xce]\ +^w-\xd8\x7f\xc8\xb8\xdd\xb4\xc2\xf9\xfbV\xb6\xfa\xcc\ +*\xd6_Y\xf9\xfaK+\xe5}\xf1\xf2c+Z~\ +\x80\xdc\xb3\xfc\xc5\xdb\x96;\x7fj\xd1\xb93\xe4\x8e\xe5\ +-<\xb0\xd2\xf5\x17V\xbb\xf3\xceZ\x0e\xbe\xb0\xee\xe3\ +\xaf\xad\xff\xda\xb76p\xed\xa3\x0d\xde\xf8\xd1F\xef\xfc\ +\xc9&\x1e\xff\xb3M\xbd\xf8\xaf6\xf7\xfa\xffc\xf3\xaf\ +\xff\x87-\xbc\xf9\x1f\xb6\xf2\xf6\x7f\xd9*\xb2\xfc\xfa\x7f\ +\xda\xc2\x8b\xffn\x93\x8f\xfe\xdd\xfaO\xffj]\xd7\xfe\ +h\xedG\xbf:\xe9:\xf9\xa3\xf5\xdd\xfc\x8b\x0d\xdf\xfe\ +\xbb\x8d\xdf\xffw\x9b~\xfc_m\xe6\xc9\x7f\xd8\xe4\xc3\ +\xffb\xa3w\xff\xcdzo\xfc\xcd\x9a\xf7\xff`5\x9b\ +\xdfs\xcf_[Ld\xec\xaeE\xc6\xeeYt\xfc\xbe\ +\xe5M>\xb2\xe2\xb9\x17\x0c\xc6{\xab\xbd\xfa\xb552\ +\x18\xcd\xbb?X\xf3\xcewV\xbf\xf9\x95U\xaf\xbd\xb3\ +\xf2\xa5\x17V2\xf7\xc8\x0a&y\xa8\x91k\x16\xe9\xdf\ +\xb5\xec\xee5\x0b\xb6\xcf[\xa0e\xca\xfc\xcd\xe3\xe6k\ +\x92L\x98\xafy\xd22.\x84\x9f\xd3\x01Az=\xa0\ +\xa8\x93r\xc6\x00\x82\x14?e\xc9\xf5s\x96\xd2\xb8h\ +\xa9\xcd\xab\x96\xd6v\xd52:\xb6\x18l\x94\xdcwd\ +A\xa7\xfc\xeb\x96=\x8c\x00\x80\xacA~7\xb0\xcf\xdf\ +\x04\x90\x0d\xf3\xf7\xac\xf1\xd9\x15d\xc9\xfc\x9d(\xbfc\ +\xce|\xadS\x5c\x0b0\xd6\xf6YRe\xbb%\x944\ +Z\x5cQ\xad])D\x8a\x1a-\xb6\xb4\xd3\x12kF\ +\x1d\x00\x82\xbd{\x967~f\xa5sO\xadr\xe9\x8d\ +U\xf1\xec\xe5\x0b\xaf\xacp\x1a\x90\x8f\x9d9\xe0I\xd1\ +\x99\xbd\xe7\xd2sd\x81\xee}\x0b\x00\x88`\xff\x91\x85\ +G\xaf[\xde\xd4\x19\x80\xb9o\xc5\x0b\x8c\xdf\x22\x8a_\ +xh\x85\x0b\xf7\x91\xbbV\xb8x\xd7\xf2\xe6Q\xfc\xec\ +-\xcb\x99\xb9e\x91\xe9S\xcb\x99\xbem\x05\x80\xa6\x14\ +\x90T\xaf=\xb3\xc6\x8d\x17\xd6\xba\xf5\xca\xdav\xdeX\ +\xc7\xfe\x07\xeb\xb9\xfe\xd1\x06n\xffj\xc3\x0f\xfebc\ +\x8f\xfe\xeed\xea\xc9?\xdb\xc2\xf3\x7f\xb5\x85g\xffb\ +s\x8f\xff\x93M?\xf8'\x1b\xbc\xf5\xb3\xb5\xee\x7fe\ +\x8d\xdb\x1f\x90/\xd0\xd5\xd7\xd6~\xf8\xbd\xf5\xdc\xf8\xd5\ +\x06\xce\xfe\xc9F\xee\xfd'\x1b\x7f\xf8o\x00\xe5?\xdb\ +\xf8\x83\x7f\xb3\xa1;\x7f\xb7\xee\xeb\x7f\xb6\xa6\xbd\x1f\xac\ +\xea\xea\x97\x5c\xff\x1d\x00\x18\xbd\x09\x8aO\xb1\xfe;V\ +0\x0d2\x17\x9ec\x01\xb2\x86/\xadi\xfb[k\xd9\ +\xfd\x0e\xf9\x160|iu\xeb\xef\x18\xa0\x17V6\xff\ +\xc8\x8a\xa6n[\xfe\xd8u\xcb\x1d:\xb0\x9c\xbeM\x0b\ +w\xafZ6\x0a\xc8F\x01Y\xed\xb3\x16l\x9b\xb5L\ +$\xd0:k\xfe\x96i\x80\x018\xf0\x12\x12_\xe3\x94\ +e4\xce\xa0\xa4yKk\x92\xe2W,\x0d\xabOo\ +\xdf\xb4\x8c\xce\x1d\xf3i\x805\xd8\xfd'\x0e\x00Y\x00\ + \x0bEd\x0d\xea\xe7c\x00p\xc0\xdfv\xf9\x0c@\ +\xe9\xb9\x8a\xf2W\xb1\xca%\xf3u,\x98O\xd6\xdf\x8c\ +\xc7i\x18\xb6\x94\x1a\x01\xa0\xc3\x12\xcaZ,\xbe\xb4\xd9\ +\xe2Jx-\xeb\xb4\x84\xaa\x01@7\x89\xa7Y\xb6`\ +\xcf\x8eE\x86\xafY>\xcf_4\xfd\xc8Jf\x9eX\ +\xd1\xe4\x03\xcbC\xf9\xfa\xbdW>\x7f\xd7\xaa\x16\xefY\xcd\xca\ +C\xab_\x7f\x8a\xc1\xbd\xb6\xb6\xc3\x0f\xd6\x81W\xe8\xbe\ +\xf1\xbd\x93\xbe\x9b\xdf\xdb\xe8\xd9\x8f6r\xfa\x1d^\xe2\ +k\xeb;\xfe`\xad\xdb/\xadj\xf9\xa1U,=@\ +/\x8f1\xda\x97\xd6\xb4\xfb\xde\xda\x8e\xbe\xb1\xae\xeb?\ +X\xef)@\xb8\xfd\x9b\x0d\xdc\xf9\xb3\xf5\x9d\xfe\x91\xdf\ +\xfd\x8c\xc7\xf9\x887\x7foe\xcb/\xf0\x5c\x8f-&\ +:\x0e\x82'o\xa2\xfc\xdbX\xff}+_~\x8a\xfb\ +\x7f\x8d\xc5\xbf\xf7P\xb5\xf3\xa55\xf3\xda\xc8?\xd5\xf1\ +\xfb\xea\x95\xe7V\x01\xd2Kf\xeeX\xe1\xc4M\xcb\x1f\ +9\xb6\xbc\xc1=\x8b\x0elY\xb4\xef*`X\xb3H\ +\xef*\x80X\x01\x10K\x96\x85R\x82ms\x16\x04\x08\ +\x9e\xcc\xf33\x96\xd7\xb6j\x99\xed(\x0fk\xf7u\xa0\ +\xf4\xae=\x14\x7f\x80B\xb1\xb0>\x06\xb9\x1f\xcb\x1b\x90\ +\xd2\xaf;\x10\x04\x9d\xf2/\xe4\x08\xd7\x8c\x12\x08\x05\x01\ +<\x81\xbfg\x83\xff\x07\x04\xed\x8b\x96\xd1\x0a\xb0\xf06\ +\x0aA\xc9\xb5\xfd\x96T\xd5\x0d\x08\xba\x10^\xab\xfa\xf1\ +:#\x80\x03\x00\xb6.\xe21\xd6Q\xe8\x0e\xa1\x0cW\ +>\x02\x98GO-o\xf4\xccr\x87Q\x10\xd7\x09\xf7\ +\x1fXv\xdf\xaee\xf5\xe0\xeee\xf1(>\xc8\xbdI\ +\xe4\x9d2\x09\x0d\x99x\x90L\xbeC\x12\xec\xc5\x1b\xe2\ +\x9dB\xc3\x87x\xd5#\xcb\x99<\xb1\xdc\x99\xeb\x16E\ +\x22\xd37,\xb12\xc0V\xbd\ +\x06\x88\xb6_X\xe3\xde[k9\xfc\xdcZ\x8f\xbe@\ +\xd0\xe1\xfe\xe7\xd6\xb0\xfd\xd6\xaa\x09=eK\x8f\xf0X\ +w-\x1f\xf0\xc5\xe4q\xd1\xfc\x99\x9bV\x04ZKp\ +Y\xe5 \xaaz\xed\x89\xd5\xae?C\xe1\xcf\x91\x97V\ +\xb7\xfa\xd2jAL\xf5\xd23\xab\x5cxles\x0f\ +\xacdZ\x00\xb8e\x05\xa3\xdc\xc4\xc8\xa1\xe5\x0fs#\ +\xc3\xbb\x967\xbc\x83W\xd8\xb2\x9c\xfe\x0d\x0b\xf7\xaeY\ +\xa8k\x19\xaf\x80g D\x84x\x0dw\xaeX\xa4{\ +\x03\x90\xec\x12>\x0e-\xa4\x18?p\x822O,\x80\ +\xf8Q|\x00q\x00\x90\x07\xe8g\xc0q\xb5\x99\xb2>\ +^\xb3\x1c'@\xc4?\x18\xbcL\x06<\xd0\xbb\x0d\x00\ +\xd6-\x03`\xa5\xb7\xccX*\xa1&E\x00\xa8\x1b\x04\ +\x04\x03\x96\xc2kj=._\xde\xa7m\xc1\x02\x9d\x84\ +\xac\xdeM\x94\xb5k\xa1\xa1}\x0b\x0f\x1f G(\xe4\ +\xc4B\x5c/\x9bke\xf7\xee[6\x8a\xcfF\xb1\xd9\ +(9\xc4\xefB\xdc_h\x10%\x02J\xdds\x10\x00\ +\x04\xba\x01o\x07a\x0b@K\x1c\xa8\xf0\x88!\xc6!\ +2q`Q\x14\xe3\x000u\xc3B\xe37\x9c'\x13\ +x\x5c\x18\xc1\xdbevm\x01\xae-\xae\xb9ea\x8c\ +(\x87\xb1\xcb\x1d\xde\x02\x14\xdb(v\xd7\x0a&\xf6\x90\ +}+\x9e>D\xe1'V1{he\xd3\xbbV<\ +\xb1i\xd1!\xf1\x1e\x7f\xe4\x00!\x9e\x8e\xec\xf2\x00;\x963@X\ +\xe8]w\x9e \xdc\xc5\xcd!9\xbc\xcf\xed\xbdj\xb9\ +\xb8\xef\x5c\x94\x97\x8b5Dq\x87\xe1\xb1\x9b\x96\x85\x0b\ +\xcd\xc4\x0a\x03X\xbb\x1f\xc5\xfb5HX\x9cbm\x00\ +>\x10\xe8\xd9s\x03\xee\xb9\x5c\x06\x94\xff\xd1\xff\x85\x00\ +`pP\x9ec\xcb2\xda\x09%-s\x00`\x12B\ +\x09\xc7\x80\x94\xa64\xc07D@\x09C\x19x\x88@\ +\xf7:\x83\xb5\x85\xe2\xf7,2\x8a\x95\x8e\x1fC\xf6\xf8\ +\xceQ\x11L)U\xca\xe1z\x90P\x7f\xc7&\xaf\x9b\ +\x16D\xc9\x02E\x88P\x14\x199\xe5\xfa\xa7\x8e\x97\xe8\ +~\xfc]\x9b\x96\xde\xb6L\x18\x9b\xb7\x94\xe6YB\xd9\ +\x19\xf7\xd1M\xc6h\x9b\xe7\xdb\ +a\x8cw!\xed{\x84\xa8}\xe4\xc0r'\x8f,:\ +y\x8c\x9cXL\xc1\x0cDe\xd6\x93\x22\x85\x81\xa9[\ +V\x02ZK&\xafY1R4y\x1d\x81\xe9\xe2\xee\ +\x0b\xe4\xf2Aq\x9eb?\x03\x16E\xf19\x83\xfb\x16\ +\x19\xdc\xe5\x15\xc4\x0fn#B2\x16@8\xc8\xeeY\ +\xb5\x10J\x8f 9\xbc\xcf\x05\x10\xf9\x03 \x1b\x8b\xcb\ +\xe7\xff\xf3\xf8\xbe\x5c\xae\x17\x01\x84\xa1\x09\x06\x94\xdf\x05\ +\xb0\xec\x00V\x1e`\xc0\xfdX\xa1\xc8\xa0\xbfk\x07\x81\ +\xf5c\x91\x22\x81\x22^\x11\xbcO\x0e\xe4+\x02\x10\xb3\ +\x01\x81<\x81\xbf\x1be8\x10\xcc\xa3\x10\xb2\x8a\xa6i\ +^\x91\x16\x14\xd36\x8f\xa5.;\x00\x04\xfb\xb9\xbf!\ +\x5c\xff(\xd6?\x8er\xc6\xb1\xf81<\x8d\xae\xad\xeb\ +b\x9d.,\xc1I\x1c\x08\xb8v\x90{\x91\xe2B\xc3\ +x\x80\xa1\x1b\x84\xa7#>\xb7\xcd\xf5V\xb9\xc6\x9c%\ +\x13V\x92\x1a&-\xb9i\xd2R[QL\x07\xdc\xa7\ +{\x01O\xb6\x0a@7p\xfbx\x9ba\x80\x06\x00\xc2\ +|G\x18/\x12\x22\x94\xb8\xf0\xd2\xb5A&\xb5l~\ +B\xa4\xafe\xc2e0\xe9MCp$\xa4i\xd8\xd2\ +\x9bG\x10~\xd7\x0c\x81n!\x93j\x01\xd4N\xf43\ +\xc4\xba\x15\x92\x0d\xf1\xcdh\x9b\xe6\xbeg\xe0Cs\x5c\ +{\x1e\xa3\xc0\xdb\xe1\x192\x07\x00\xe3\xe0\xba\x85F\xb6\ +\x00\xc5>`8f\xdc\xd1#\x844\xa6d\xe99\xee\ +\x82\xb4E\xc4N`\x90\x92\xb1\x8c\x5c,$g\x00\xa5\ +\x22a$$w\x89\x84%X\xb0'\xfc\x9e\x18\x1c\x92\ +E\xe1\xf6\xb2Qz\x16J\xce\x82\x99\x07Qz\x10\xab\ +\xcf&\x04\xc8\x0b8\x00\xf0\xf7<\xbe+\x0f\xb7\x9b;\ +\xc25\x88{\xd1\x09b-7\x94=\x86\x8b\x1f\xc1\xda\ +\xf9[\x00\x92\x17 \xfe\x06 _\x01\xdc\xaf\xdc\xac\x06\ +\xdb\x01\x00R&\xab\x8f@\xa6\xc4\xa6s \x5ca\xee\ +9\x8b\xc1\x0dpO>\xc2Kz\x87\xb2\x09\xac\xb2u\ +\xc9IZ\x9b'\x19\x00\xc0\xd7\xb5\x827A)\xfd\xdc\ +\xa7S\x0c\xeewd\x87Wb\xbcx\x85\xc2\x8d<\x0f\ +|$\x00/\xc9D\x82\xe7\xde\xc7\x0b?\xe2&\xf2N\ +\x5c\x0b\xcbM\x83K\xa44\xcd8\x00$\x03\x80\x14R\ +\xdeT8H\x1aDT\x8a\xf1\xb5\xcf\x00\xdeE\xee\x1d\ +\x83\xe8\xc7\xeb\x10\x02r\xe0\x18\xd1\x91[\xb8p\x80 \ +\x22\xd9\xb3i\x99\x1d\x90X\xb2\xa8t\xc8k*\xdc%\ +\xa5\xa6\xc7\x92\xab\xbbH\x97\xbb\x09c\xbc\xaf\xebC\xfa\ +!\xaf\x10\xd8\x06\xc9 \xd7\xe2\xb3\x8d\x0amx8]\ +\x13\xf0\xe9\xbai\x80(\x8dk\xa7\xb5O[z'@\ +\xe8\xc5\xb3\x0c\xe0\x8dF\xe1j\x18t\xfe\xccm+\x9a\ +\x7fH\xc6\xf2\xd4b*\xaf~n\xe5\xabo\x88\xff\xcf\ + \x82\xf7\xb1\xec\x1b(\x1dW\x0b\xc3\x0ev\xc95\xad\ +\xe3^H\xb5\x10\xc5\xceL\x5cU\x90\xd7\xac.\x09\x83\ +\xe8\x04\x85#\x99\x1a\x5c\xdcX@.\x90x\x1fh\xc7\ +\x9dA\x04\x9d\x17\xe8Y\xb7\x1c\xe2n\x94\xfc=\x0a\x88\ +\x22\x10\xb80 \x0b\x112\xb2P@p\x18\x05\x0f\x91\ +\xda\xe1I\x02\x90\xcaL<\x8b\xac:\xb3_ \xd8\x05\ +\x00\x02\x011S\x8c\x9c\xf0\x91=v\x0d\xaf\xc1\x00N\ +*\xb6B\x16\x01\x94\xf3\x02\xf0\x81\x0cy\x82N\x01\xe1\ +*\x16\xba\xce@\xac\x01\x00<\x03 H\x87'dp\ +_J\x19\x03\xa4\x8e\x01\xcd\x1fh\x1e\x01\x00\x07\x01P\ +\x96\x14\x22\x1e\x00\x07\x099\xc1\xeae\xf9(_\xe1G\ + \xd0=\xf8\xbb\x15r\xf8^R\xc9\xd4&\xd2X@\ + I\xd5|\x88\x14\xa1\xb0\x83b\x94\x02\xfb\xb0\xec\x00\ +\xdcG|\x22\xc2wDG\xb0\xbe\xd1[\x18\x01c\x0d\ +\xe8\xb2\x19\x97L\xc6\xcd\x87\xe7HG\xa1\x9aGI\x11\ +w\xa9A\xe1\xff/IrBv\x03H\xc4q\xc4u\ +R\x95Z7\xa0tB\x9f\xe6\x5c\x04\xc0T\xae\x9b\x06\ +!\xf6\xc2\x11\xe0#\xc2\ +\x00\xd6\xef\xc3\x12\x15\x16\x02()\xa0\xf9\x00\x88[\x90\ +0\x14$\x15\xd5\xe7\xfd|\xb7O\x84\x10o\x91\xc1\xfd\ +g\x10_3p\xe1\xe9m\xb2T\x00\xd0\xbc\x88\xcc\xe1\ +J\x19\x18\xc8\xa2\x14\xe3\xee\xb5\x83g\x121D\xa9\xd9\ +(7\x8c\x92#\x837 dX\xaa2\x02\x5c\xb6,\ +W$Qia\xa6\x00I\xec\xcf\xc0\xfd+\x95\x14\x08\ +\xd2\x9a\x17x%\xad\x15\x0f\x10\x08\xce'\xbf\xd2\x01\x83\ +\xafu\xce\x19N\x08\x8f\x19\xc5S\xe5\xc1w\xf2\xe0>\ +\xb9\x84\xd2\x1c\xee?\x84Qduc`\x18\x8dOJ\ +#e\xd6\xff\xa5\xa3P\x89\xfb\xae\xfaq\x801\x0a(\ +.&\xd1\x06\x01\xc2\x10\xdeA\x80\xe1\xef\x0d\x80\xa7i\ +\x96\xffE\xe0A\x19\x0ay\x18_\x00\xce\x95%oN\ +\x88-\x9c\xbf\x87\xb1C\xee\xb7\xde\x93\x0e~c\x9d\xc7\ +?ZL7\xf9b\xe3\xee\x97V\xbe\xf2\x12rp\x8f\ +\xd8\x08\x19S\xec\x85\xfc\x88\xd5\xa6\xc9\x85\xb6\xf0p\x90\ +\x9ct\xbe\xd8\x87\xf8y\xd0@+\xf1M\x22@\x00\x8c\ +\x00\xaf~~\xfe\xbd\xe8\xf7Y\xb8\xe3\x10n9\xdc\x03\ +?`\xe0\xc2H\x88\xf7A,4\xc0\xdf|\x9aE\x04\ +(\xe9\xdcp:\x8a\xc8\xc0\x8b\xf8\xb9i\x7f?\xe4\x08\ +O!\x00\xf8\x09\x07Np\xc3nVN\x82g\xf0c\ +\xb1~\x06\xd5\xafT\x10\x8f\xe5\xc3\xf2}R\x0c\xca\xd7\ +L\xa2f\xeb\xfc\x12\xcd-h\x8e\xa1m\x8d\xc1\x01\xcc\ +(,\x83g\xca\xe09|\xbc\xf7\x03\xf0L<]\x16\ +\xf7\x15\xe6{s\xb0\xd2\x1c\xb9j\xdc\xbdK\x07q\xf9\ +!\xae\x9f\xa5\x94O\x9e\xa8c\xc3}W\xba@\xc5\xf7\ +\xa5;Y!\x0e\xaf\xa0@\x0cE)\xa6\x9e\x9fW\x19\ +@\xb6\xb2\x1e\xdc\x7f\xee\xc8\x89\xe5+s\x9a\xba\xed\xd2\ +\xee|\xc2X.\x9e,B\xf8\x09\xf1\xbcAB\xa7<\ +\xa8\x9fp\xe0g\x5c\x9f\x89\xc7\x08:\x81x\ +t\x10&\x88\x93\xd9XV\xa8G\x96\x8f\xdbWJ\xc5\ +\xfb,\x00\x16\xe03RF\x1a\xc8Mm\xf4\x08[Z\ +\x1b\xe8\xd5\xa4\x0e\x83\xe1\xc3-K\xc9R\xb6\x0f\x05\xf8\ +\xb0|)?\x83\xef\xc8\xe0;2\xf8\xdet\x067\x83\ +P\x95\x81\x05+\xf6\xa7\xe3\x96\xd3\xdb5\x9b\x08(\x14\ +\xc3{4y\x83\xa7\xd0+\x1e$SdR\xd7&<\ +\x04\xf8\x5c&\xaf\x02c\x16^#\xa4\x18MF\x91C\ +\x88\xc9\x19\x22\x97'\x14\x86\x19\x8bl\xdd/\xd7\xf2\xfe\ +o\x03\x85\x5cE\x94v*\xfd\xe3\xbd#\x8bb\xf3\xfa\ +\xcc\x16!\x11OBV\x12\xc2#E\xf0bQ2\xa5\ +|M\x19\xa3\xf4\xa2\xd9{d[\xf7\x1c\xe9\xce\x87\x84\ +\xe5b\x999\xa4\xd1a\x88\xb4R\xc1`\xcf\x06\xf7,\ +\xc1C*\x0c+<\xa0L?^V\x80M\x85k\xa4\ +\xa0\xf4d\x94\xafi\xf4\xe4\x1aV.\ +k/D\xf1E\xf3\x0f\xdc$L\xf9\xf2#8\xd7}\ +\x97z\xe7C\xca\x22dE\xd9x2\xa5\xa7N\xe9<\ +o\x10\xd0k\xc63\x0b\x91N\x02x7\x97\xe6\x8ao\ +\xd4\x93m\xd4\x8c\x9d\xaf\xa3\x90\xee\xd6)L\xa0\x17\x9e\ +\xdf\x8f\xd1\xb9y\x05\x9e@\x00\x10@\x95e\x9c[\ +\xbb\x03\x80\xb2\x1d\x94\x0fA\x94\x08\x0c\x01\xcds\xa0\x17\ +\x19\xa4K9k\xc7\xddz\x86$\xa9\x8eq\x83\x03\xe8\ +o\x19\xa4\x93\x01t\x98\x05h\xc2\x84\x9c\xa8\xb2:X\ +\x7f\xc1\x9c@p\xd7\x8a\x97\xeeA\x00\x1f\x01\x82g\xd6\ +\xb6\xff\xcab\x0a\xa7oXt\x5c\xd3\x92\x9al\xc1\xda\ +x\xe8\x0c\x91>,S\x00\x90\xd2=!\x0e5+\xbe\ +\xad2\x10\x905,!\x93\xc1\xc8B\xe9!\x5ck\x18\ +\xeb\xce\xc1E\xe7\x10/\xf5z\xa1h)\xffB\x22<\ +\x98\x16\x8fr\xe4\x9ex\xe0l\x91@\xd8\xb1b\xa6\x08\ +Lj\xa3\xc8\x93\x08\x10y\xb4\xf8\x0610\x00\x00\xe4\ +\x0e3!wN\x18\x08Y\x83r~\x111'\x00\xc2\ +\x0f\x10}\xf2(\x88\xf8K&\x96\xa9\x19\xbb\x9c1\x1e\ +|\xf6\xb1[\xc4\xaaXye\x15\xab\x92\xd7(\xe0\x15\ +\x16\xf9\xc2\x8a\xe6\x9eY\xe1\xccc\x0c\xe0\x1e\x83u\x8b\ +AS\x06\xb0\x8b\xdb\xe5;]\x88\x02P\xcd\x00\xacE\ +\xe3\x22\xe3\xc0\xf2\xf1,R~\xd6 \x19\xc8\xf0)\xd7\ +\xb8\x8d\xe2\xef\xa2x\xb9uM\xb1\x22(>\x8f\xb8\xeb\ +\x04\x05\xe490\xdc\xc1\xd3b\x89N\xf9w\x00%\xe0\ +\x1c\x87_\x0c\xe8\xd9D\x88%\x1b.vg\xcb\x0b\x01\ +FI\x96V?\x19\x03)7\x95qJ\xaa\x9f\xb0D\ +\xc8_\x02\x5c \x01R\xa8\xf7JA\xf57e9~\ +B\xa2@\x14R(\x83hF\xf1\x02\xd1)\x84\xeb)\ +\x13\xc8\xe3\xb5p\xf6\xa6\x95\xcc\x9fZ\x8c\xdc\x8d\x06V\ +3i>\xc5}\xacR\x1e@\x0f/\xf4\xfb!6\x9e\ +\xe8\xe7s\xc5kb\x04+P\xb6\x10\x92\x95\xe1JE\ +\x98\xa2\xc3\x5cL2\x04\x8b>\x8f\x9f\x9e\x17\x80\x5c!\ +\x11\xae\x93\x03a\xcb\xe1\x01#\x90\x9e\x90\x18*n>\ +\x93\xf8\xe6Sz\xe6H\x19\xfc\xc2e\x10\xe4\xce\xca0\ +\x94f\xa2\xec,\xee/\x8b\xff\xd7\xf2sP\xaf\xdcw\ +\x16\xdf\xffI\x00\x98\x16v4\x1f\x9fE\x98\xc9\xe6~\ +\x22\x9a\xd7\x9f\xbc\xcf\x83>u$\xb7b\xed\xb5K\x7f\ +\xaa\xd6\xdf\xc2\x86\xdfZ\xd9\xca\x1b\xdc\xe2+\x97\x02\x17\ +\xce\xe2\x11&Q\xe6\xa8\x8ca\xdf}\x7fPs\x0f<\ +k\xa0\x13W\xdf\x85K\x86Gd\x91\x16z\x16\xcfg\ +\xc7Q.\xdf_\x80G)\x9c}\xe4\xbe\xa3\x00W/\ +eG\x89\xef\x11R\xd4\xc8\x04\x16>\xa6\x99FM`\ +\x1d\xe1uN\xf0>\xd7y=&\xe5\x16\xdf\xd0=\x03\ +\xdan<*\xbcGK\xdcY\x9a{!~\xe7\x11B\ +\xc4\x1d\xf24c\x8a'\x08IGx8\xf1\xb1d\x98\ +\xbf\x07\x82aK\xa8\x1d\xb1D\xb2\x84dx\x94f\x22\ +}\xf0\x00y\x0c\x01G\xcb\xe7!@\x1d\x22M\xce\x86\ +kd\x911\x051v-\xa9\x07\x09s1\x9a`\x91\ +\xc8\xb5z\x0cZ\x0b+\xc4=,@3TY\xfcN\ +\xf1:\x9b\xcf\xc8\xda\xb3dY\x9a\x16u\xb1\x93\xf8\xa5\ +T\x09\xf7\xa7\x85\x14OH\x9b\x86\xbc\x85\x94\x90\xe6\xd1\ +Q^\x88\xef\x09\xf3}.\x15\x84\xd8i\xe1\xc8M\x15\ +\xf3\xb0\xd9\x9a8\x12\x08\xf0\x04\x9a\x0d\x0b82\xe9\x89\ +G$5\xcf \x22\xc9}\xf0P\xfa\xce\x90<\x08\xe1\ +*\x0ca\xd3B\x8e\x13\x0d\x10\xa2\x1a\x85\x08\xf9\xb5\x96\ +r\x9dr\xb4\xc2\xb7\x00\x00\x96\x9f\x03\x02\xbc\xc0\xeaK\ +@\xf0\x1a\x00\xe0\x05\xf0\x08\xa5\xfc\xbeh\xfe\x11\x96\x8b\ +\xd2\xc65KG\x08\xe0\xbb\xb3\xf1`\xd9\xe7s\x02\xa1\ +\x01\x14>t\x06\xa0n\xf3\x19B\x06\xdf\x9bGH)\ +\x9c{\x845?\xc5\xb3<\xb7\xca\x15\x85\xd2\xc7.\xb6\ +\xe7\xcd\x9cZXs\x13\xc4\xf6\xe0\xa0\xe6.\xf0Z\x80\ +=\xd0\xb3\xee&\xcc\x22<\x7f\x98t\xd7{~1\x7f\ +<+\xa2e\xedL\xe5\xec\x9aY%~\x17A\xdeJ\ +\xf1&ex\x95\x92\x09\xc2\x85V'\x01\xa4O\xd9\x07\ +\xa19\x19\xf6\x9f\x88\xfbO$3Hl\x98\xb0d\xcd\ +|\x0a\x00\x18\xb0\x00\x90\x89\x17\xd0\xd2\xb9\xc2I\xa6x\ +\x04?\x07\x18?\x17N\xd1\xa9$&@\x8c\xd6<\xbb\ +\x1f\xcb\xb9\x10}0\x0bt\x88\x08i\xc2&\x8aE\xe4\ +2\xb8Q\xdc{\x14\x92\xa3\x19,O\xe9\x0c2V\x90\ +O,- \x85\xd4\x92j\xee(\xb1\x14\x00\x84\xb0J\ +)-\x0b4f#!,9\x8c\xcb\xcf\xe1\xc1s\x07\ +\xb7-wh\x9b\xef\xc2\x1b0\x18\x9e7\xe0\xb3\xb0\xdd\ +,\xa5c(=H|\xd7\x8a\xa1^\xf5\xb3H\xa5x\ +\x83f 5\xfd\xac\xfc9G,\x17\x89\xf0\xde\x13~\ +\x1e\xc1\x13\x11\xcf\xf3\xb8\x97\xfc)\x01\x80\x98\x8f\xa2J\ +\x16P\xce\x22\xb1w\x09Y~j\xc5\x8bOP\xfcc\ +\xac\x16\x85\x11\xb3\xa3.\x1d\x03DR>\x03\xa6\x95\xba\ +\xf0\xf9\xdc\x7ft\x0cw>\xf1\x00\x90\xc0\x1df\x1e\xb9\ +\x82\x98\xc2\xb9\x87V\xc6w\xd4\xc2\xaa[\xb6_#\xaf\ +\xacq\xe3\xa9U\xa9\x00dJ\xcb\xd7\x1aS\x19\x94\xb2\ +\x12\xc2\x08i\xa1K\xa3Q\x90\xe6J.\xc4M\x98u\ +\xe0\xf1:I\xa9{V\xf0<\x02\xc0\x0e$\xf2\xc4J\ +\x08\x1d\xe5\x10\xc7J\x00Z\xa1\x05\xb8I@\xaa\xb9\x88\ +ntD\x18N\x87\xf3\xb85\x08\x91hxT*\x1e\ +T\xd7\x12\x99\x8c`\xe9\xca>\xa2\xf0\x80\xc80\xe1\x0a\ +@\x8bC]L\xa8\x05\xf4\x1dH\x8c\x98\xabf\xb7T\ +y\x93\x89\x82\xb5\xcc\xaaU\xb6\x08.#*\x97\x85\x14\ +\xe0\xc2\x0a\xb5d\xc9\x97\x15\xe0\x8e\x0a\xc7\xb5^@^\ +\xc9\xe0\x951\xb8\xaa\x0f(e@\x0a\x88\x81Q<\x80\ +\x18\xb5f\xbdD\xf2\xbc\x89\x22ra,<\x8c\x8b\x13\ +\x00\xf2p{Z8\xca\x1f\xddw\x927B\xaa\xe4\xa6\ +\x9e\xc5\x0d\x142\xf08\xf2>\x22\x98\xf2\x00\xb0\xf0\x10\ +`\xd2\xdf\xa4|M#\xe7\x89\xe1\x8e!\xdc\x97\xd2(\ +1i'zh\xc7\xc0\x05\x02B\xc0\x04\xf1yR\xa1\ +\x00\xef\x84D\xf5:\xe5\xbd\x8a \xe5\xa0xY}H\ +\xee\x12pi\xba\xd6-\xd3*\x94\xe1E\x14\xdb\xf3\xa7\ +p\xed(\xbe\xd0)^\x96\x0f\xa1\xd3\xd2\xf9\xe2Ck\ +\xdaxf]{\xaf\xac{\xef\xa5\xb5m=\xb1\xba\x95\ +;V0\xa1\xd5K\xcdF*;\xd1<\x8a\xea\x1e\x94\ +F\xc3q p\xeeU5\x11Xl\xb0}\x8eg]\ +\xe0\xf9\x96\xe0Hk\x18\x1c^\x01\x83\x10\x18\x8b\x09#\ +\xe5\x90\xc7J-\xc6!Z\x85\xcd\x1f'\xf4\xc8\x13\xe0\ +a\x15\xf2\x02\x8c\x93\xe3m\x22\xee\xce\xf5\xe3i\x87\xf7\ +\xad\x90\xf4\xb2\x08b_D\xec\xd7\x1a\x8e<\xa3\x96\xaf\ +U\xd7\xe02\x16\xe7\xd9N,&\x17\x0b\x8e\x8e\x8b\x05\ +\xe3\xe2\xb0\x1c\xad\xb0ET\xdc\xa0\x14\x86/)\xc0\x95\ +\x15j\xad\x9a\xc1\x16\x08\x0aa\xca%\x90\xa5JHN\ +\x0d\x03P\x8b\xdb\xabY~b\x15\xaa\x82\x99\xe2{\x18\ +8\xb9O\xcd\xaaerc\x9a\x0c\xcal\xd7\x84\xc8\x12\ +\xc8]\xe5!\xaf:\xcb\xcf\x13\x00\xc6\x0e\xf8\xfeC^\ +\x11\xe2a.\x0a\x94\xb7\x11I\x8c`\xed\x0a\x1d\xd9\x9a\ +G\x90(\x8c\x88C`\xa1\xb9\x9aM\xe3>\xf2P\xde\ +\x05\x00\xc2C\x90&b\x9b\xf2i\xa5q\xb2\x00\x97\xd2\ +i\xda\x18P\xbb*\x22\xb9A\xd2-\x89J\xcaD\xb6\ +\xa4(\x0d\x9a\x98\xb3\x08\xa6\xf8\x84\xb7\xe2\x088\xc6\xf1\ +\x0c\x0e@x6\xc0\x9dG\x98\xc8\x83\xddk\x11%_\ +\x8bf\xb3\xa7(\xfc\x9e\xb5o>B\x1eZ\xcb:c\ +\xb2\xc0\xb8\x8d*m\xc3B\x9d\xf2\xc9\xa4\x1c\xc1\xc5=\ +\x93\xbe\xa5\xfdN\xd2\x01A\x16\x00\x08a\xfda\x01\xa0\ +\x97\xf1\xe9\x07\xf4x\x0e\x01R\x0bo\xaa\xbb(#{\ +\xa8 },c\xbc\x0b\x09\x09yd\x1a\xf2t\x0a{\ +Y\x8c\x95RG\xf7\x0cH\x10\x8e\x95\x8b\xd1\x16O\xdf\ +\xc2@o#0\x7f\xee[\x9eY\xa48{P\xe1\x0c\ +/=r\xc6w\xdc\xb6\x98\xe2\xf9g\xa0\xf9\x89#1\ +\xf9Z\x0b\xe0A\x9d\xc5(\x9d\x11\x08\x88\x8b\xf9(\xbf\ +@\x1e\x00\x17+)f\xe0+g\xcex\xd8{V\xbb\ +\xf4\x00 <\xb0r\x15\x18hiW\xf3\xe5\xa4|A\ +,6S\x88T\x5c\x07\x00\x99\x1d\x80@\xb5\x01=\xab\ +\xb8\xf1\x0d,^ \xd8\xe5\xbb\xf7\xf10\x87N\xf2G\ +\xb5\xac|hy\xc4\xf6(\xca\xca\xc1\x8b\x84\x01\x92\xac\ +_\xf3\x09aBS\x0e\xee\xd9\x03\xc0\x89\x93(\xde@\ +\x96\x1f\xd2D\x0a\x96\x93%\x12E\xa8\xf1D\xef5\xe5\ +\xec)\xd9O\x88\xc9\xe8\x22m\xec\xc4%\x03\xc8t\xee\ ++\x0d7\xac\x05\x1d\xa5\x92Z\xdc\xd14\xafV\x1c\xb5\ +l\x1b\x1a\x85\xe5;\x81\x1b \x11\x18uD\xa1\x82\xeb\ +\xcaC\xe6\x8d\x1dY\xc5\xecukX\xbae\x8d\xcb7\ +\xad~Q\xe3rh\xb9C\xcaL4/\xb1\xe0V\x0a\ +E\xda4\x81#\x00x\x82\x07p\xb3w3\x00`\xd1\ +\xc2\xe7\x0bf\x11 \xc9\xc7{\xe52\xce9\x18\ +\x80\xc0\x1dR\xba(\x10\x88\xb4\xa2\xfcl\x80S\x80\xe1\ +\x96\x93eT\xa2\x97*\xc2^\x05\xa1\xae\x80p\x18\x19\ +\xbd\x83\xdc\x05\xd8\xe8y\xf2!\xfa~l1e\x8b\xcf\ +A\x16\x04f\xe9\x19\xaf\xc4I\xad\x12\xc1de\xcd\x02\ +\x80bH\x94\x0bE\xa5\x14Y\x1fR\x88{*\x87\xc9\ +Vc\x015\x0bw\xac\x1a)S\x09\x94\x06\x08Tf\ +\x8b\x95\xe3\x9e4\xf8YZ~e\xd03yH\x89V\ +\x09E|<\x10\x10\x0a\x00@\x11.\xb3x\x92\x98\xa7\ +%hX\xb3\xbcM\x81f\xcf\xb0\xe0\x5c\xacY\xd7u\ +\xd7\xe6\x1er\x01\xa0\xabA@\x14\x0arP~\x18\xcb\ +\x97\xf2\xc5\xa63\xf9~\x91\xaa\x80\xe6\xf7Q\xb2[g\ +g@\x03\x9aM\xe35\x83\x90\xa4T\xc9\xd5\x0d(n\ +6\xcd\x91?\x93\x81(\xcf\xef\x12q\xd2b\x13 \xe6\ +\xb9%\x99<{&\xd7U\xcdA\x90{q\xec\x19\x0f\ +\xa2\x9c]YL\xfe\xe0\xa6\x95\x8en[\xf9\xd8\xb6\x95\ +\x8emZ\xe1\x90\xc2\x15it+\xdf\xaf\xc5!Y>\ +9z\x0a\x0a\x97\xe2\xf5\xdeM\xaa\x9dO\xa8es\x7f\ +\xdeB\x19\x9e\x91\xf0\x18\xc1#iZZ\xeb0\x01\xa5\ +s\xf2J(X\x99\x89\xe39\x0e\xf0\xc8'\xf2\x0b\xf8\ +\x19\x1b\x85.)_\x9f)\xc4\xf5W`\x9c\xd5x\x8d\ +\x1a\xf8N\xd5\xd2S\xbc\xd5#G\x8as5\xff1\x85\ +\xf2I}\x0bg\x9f\x08\x00OP\xfe\x13\xc8\x8b\x08\x0c\ +\xae\xdc\xc5\x1a\xf2T\x01\x00\xf4\xe70\x18\x1e\xa3\xf7\xe6\ +\xf0e\x91Q\x90VD\xdc.#\x95)\x9f\xbaf\xe5\ +\xd3\xd7Q\x1c\xf1\x98\x8bk.=\x84\xa5\xaa\x94*\x1b\ +T\xba\xd94\xac1\x88\xfb\xff\x04\x82.@ >@\ +&P\x08\x00\xca\xa6N\x08)7\xacj\xee\x96U\xcd\ +\xde\xe2\xe7\x1bVL\xe8q@\x10\x07A\x0a\xb1\xb6\xa2\ +\xf1\xe3OR\xc0\xcfQb\x9d\x9bBu\xca\x87ek\ +\x1d\x01\xc5k\x81\xc7-\xf2\xa0h\xb7\x82\xc9 \xaa\xf4\ +\xcb\xa5v\x9al\x01\x0c\x0e\x18.\xdde\xa05i\xd4\ +\xad\xd4\xef\xc8-\xf7f\x11c\x83R>\x03\xad\xa5i\ +?\x80\xf6\xf3<~\xc7\xa0a\xcf(\xc6\x07\x90\xfc\x9a\ +\xeb\xe7:!H\x9cD\xf5\x90*\x7f\xf35K\xf1S\ +\x96\xa2i[Y>JOE\xe9\xa9\x9aQm\x12\x1f\ +\x80\x10jB\x8dLGs!\x9a\x13\xc9\x19\x80\x14\x0f\ +\x89\xe0\xe2\x81t\x8f\x5cC\x00\xd0\xab\xcb\xc8dL\x1a\ +K\xc6\xfeB\xb2?\x09\xe3\x0c(\xc5a\xc2\x84\xbe<\ +\xbc\x94b\x7f\xc9\xec\x1d\xc7!*0j\xf1\x87\xe2\x99\ +{V$\x91\x81;\xb9O\x08 F\x94\xe2\xbe+H\ +_*\x17!6\xf3\xf7H=nc\x95\xb7\x18`/\ +\x9dSi\x94\xe6\xb75\xeb\x17\x14#gPs\xb8\xf1\ +<\x10\x9b?\xb8Ez\xa22\xb0\x1d\xe2\x97j\x03\xfe\ +w\xc9\x06\xd1\xaa\x11\x90efv\xc9\x22=\xe6\x1b\xec\ +\x5ct1\xafhd\xc7*\xa7\x8e\xacf\xf6\x9a\xd5\xcd\ +\xdf\xc0\xab\x5cw\xc0*\x84\x13\xe4\x0f\x13\x07\x87w\xf1\ +\x06{\x00\xe2\x00`\x1cy2y\x08\x08\xf6\xb8&\xe1\ +\x01N\xa1\xfa\x03-E;\xe5\xb7{\xcaw\xa5_(\ +X\xae4\x0b\xab\x0e)\xae\xf3<.S\x00\xa89\x0c\ +\x94\xe6\xfb\x95\xb2j\xd5/\x87\x98\x18\x1d\xbb\x83{\xbc\ +M\x0a\xa7\x22\x93\xeb\x16\xe4\xf3\x81\x81C\x94\xaf\x15I\ +\x84\xd0\xe6RfB\x85&\xcb4\xff\x9e\xaeU\xb8\x06\ +b\xfa\xb9\xa4jF\x137\xef\xc9\xb9\xf2\xa5x\xd26\ +7\xb1\x06s\xcf \x97w\xe5c\xe2HR.\x8a\xcd\ +V\xcd\x858\x8c\x96\xc85\x0d\xcd\xd8\xb9\xf0\xa5\xbf\xe3\ +\x11\xe4\x15\x5cH\xd5\xff\x08|\xe7\xe2y8\x9e_\x9e\ +\xb6W\x04r\xdb\x85Dq\xa3\xfc\x09\x0c\x09\xbeR\x06\ +\x17(W\xb5\xd7\xec\x19\x9c\xe2\x94\xf4\xf2\x96\xe5O\xaa\ +\x88\xf5\x86\xc5\xe4\x8b\xe8\xe1\xceKfn\xa2x\x88\x03\ +\xaf\xaa\x08\x12\xf1\x8b2HZ\xd0P:'\xc5k\xde\ +_\x8b?\x01\x88\x8d\x8a;\xb3\x18\xecl\x06]u\x7f\ +!,<\xa4<\x17`(\xa5\x0b\xf1P*\x14\xb9\x00\ +\x80\xf3\x00\xce5k\x92g\x8e\xef\x82\xfc\x00\x88\xc2\xc1\ +\xab\xb8\xcf\x1d\xab\x9a\xdc\xb7\xea\xa9\x03\xab\x98\xd8\xb3\xa2\ +\xe1-\xcb\xed\xc7-\xc2\x8as\xfa\xd7\xc8\x1a6\xe0\x1d\ +\xb8\xd9\xa9=\xab\x98\xde\xe3u\xd7J\xc6\xf9\xcc `\ +\xecUX\x81h\x9e\xd7 \xf8\xb5\xd6\xaf\xd54\x06&\ +\x13o\xa5\x8a^Y\xb5H]\x14\xc5j\xb2Gq\xb4\ +P\x0b$\x9a#\x9fy\x80\x90\x12\xce>\xb3b\xcd\x0a\ +\xce>\x86\xe4\xdd'K\x80\xd0\x92I\x84\xc8jB\xe2\ +\x03C0o\xf1\x1b\xbeO\xdf\xebf\x09\xa5P7M\ +.\xb7~\xe1\xe2/\xc4\xb3z\xa7x\x80\xa2\x99UM\ +\xe2\xb8\x096)R\x96-\x05\xa3\xe8 \xca\xcfR\x18\ +\x13\x91\x05\x00\x9a\x07p\xeb\x02x\xaaLy)\xc0\xa6\ +5\x13w=\xe7A\xe4I\xf8nD\xd3\xe8.\xbdT\ +Z\x09\xef\xd0\x8a\xa2V\x16#x\x93\xe8\x88H\xf6\xb1\ +\x95\xe2\xa1+\xf0\xb0\xe52\xae\x19\xbc5^[3\x90\ +\xca\xf2b\x22Xn\x8e\x8a9q\xc5\x85\x13X\x16\xf1\ +\xb8\x08\x0b\x14)SUPXV\xcc\x8d\x04eM\xb0\ +\xda\x80f\xea\x9a\xbd2\xef@\xf3\xa4e\xb6L\xe1\xf6\ +fI\xd7\x16\xf0\x0c\xaa\x00Z\xe5\xf3\x02\x02y;\xca\ +\xbf\xa8\x16\x0a\x09\x9d\xddZ\x1d\xd4J\xa1\x5c'\xa9\x0f\ +\x9f/\x80\xf5\x96\xa1\xf0\x0a\xe2h%J.\x1b\xde\xb0\ +\xfc>\xe2b7\xe4\xa8\x1bv\xdc\xb3Hl\x5c\xc1S\ +\x00\x94I>7\xb5\xe5^K\xc6\xae\xc2G\xb4\xd4:\ +\x8f\x85\xcc\x03\x809\x0f\x5c\xca\xab5\xa9\xa4\x01&l\ +\xb9B\xd2\xa1\x1b\x907\x88-\xa9k\x81X1n\xb1\ +d\x11\xb7Hl,_~i\x15+\xef\x90\xf7ns\ +H\xf9\xd2+x\x10\xa4xF\xeb#\xca\x904\x01\x04\ +\xb9\x85uk\xa2IdK\xdf-\xf7\xad\x18\x9e\xeeV\ +I=\x00\xfcC\xbc8\x9f\x0e\xbf\xf0a,n\xb9\x99\ +\xf1\xd3\xac\xa6j d\xa9Y2\x0c\xdc~\x96S>\ +\x0aGa\x9a;\xd0\xfb \x0a\xd4\x8a\xa0@\xac\x19\xd2\ +\xf4s\x22\xa9p\x92\x5c\xaf\xbd\x14\xe3\xc8\x98\x93\x94\xfa\ +1\x806\x0e\x18 \x95*<\x01\xfcA\xc69\x1b2\ +\xac\x22S\xd5g\x16\xe1=K\xa7\x0e\x9d\x94\xe0=\x0b\ +\xc75\x8f\xa2*\xafm\x8b\x09\xc2\xca\xe5B\xe5JU\ +\xcf\xa78\xa4\x14-\x97\xf8\xaa\xca\x9d\xdf\x03@)\x9d\ +_h#\x7f\xf55N c\xc88`\x98\xc43L\ +;\xab\x0e\x8a\xed\xa3\x00\x91?y\x82\xb0\x5c\x92\x9bP\ +\x22D\xf0]\x02F6\xee,\x9b\xef\x8b0 \x05<\ +l)\x00\xac\x18\xdd\xb5\xca\xd1\x1d\x00\xb0i\x05}+\ +\x96#\xe5w\xc9K\xccA\x92\x16\x085\xcbV<\xba\ +\x86\xe2\x91\xd1U\x88\xe82\x1e\x02\x0f\xd45\xcb\xf5f\ +\x19`\x00\x80G\xf2\x0aR\x96\x08\x05\xb24\xa5F\x9a\ +\x0d;B\x81\xd7\x01\x806p\xdcE\xf9\x0f\xacl\xe5\ +\xb1U\xae\xbf\xb0\xea\x0d\xed\x81\xf8\x02\xf9\xca\xea7\xbe\ +\xb4\xda5\xc0\x001.Q\xce\xaf\x8c\x08/\x10q\xca\ +W\xfeL\xfa(\xe5\x93\xd9h\xdaZK\xe3N\xf9.\ +\xbf\xbf\x08\x03S\x80b\x9a\x1c_S\xda\x0b\xa4\xc0\xe2\ +<\xde\x18g\xf7\xf1\xdcx\xb6\xec\x01\xd2Z\xf2\xfd,\ +8P\x10\xc9$\x94\x06\x00D\x00\xa5y\xfb\x1c\xb4\x9a\ +\xa90\xa1:\x8cYK\xe1\xfb\x93\xeb\xc7\xdd\xd4o\xa2\ +\xa6~\xab\x07\x91\x01KD\x92j\x06-\xa5n\x84{\ +\x98\xe0\x9e\xce\x01\x80'\x16\x00B\x22\x95\xe84\x8f0\ +[0F(e|\xf3y\x1f\xe5w\x9a\x81\xcd\xc4+\ +\xc7x\xb1\x12\xa5\x11\xa3\xc3\x179:\xcaW\xa5\xaf\xdb\ +\xf4!R'\x02\xe5\xd2:\x08\x93sG\xb84-\xd8\ +\xe8\xa2\xa0/\xa3q\x12\xe2\x83W\xd0\x1c\xbe*\x85p\ +q\xaasS\xcd\xa0\x9b\xb8\xe1\xbb\x1cs\x87Q\xe7\x12\ +S\xa3\x90\xaa\x1c\x14\x93\x8b\x85\x16B\xb2J`\xb6e\ +\x90\xba\xf21\x90\xcaM\x16\xe2\xdasU\x05\x8bug\ +w\xce8\x09w\xcf\xc1\x94\xe7\xf9\xdf\x05\xcbE\xf4>\ +\xa4\xaaY,_\x1e (\xcb\x97\xfbg\xd0\xddB\x96\ +\x8b\xb5+\x96\x01\xc9\xf3w\xc9\xd5\x02f\xae\x13U\xc8\ +S,\x5c\x82\x1c\xad=\xb5\x9a\x8d\xd7V\xbf\xf5\xb95\ +l}a\x8d\x9b\x1f\xacn\xed\x8dU.\xc0\x9a\xa7\xef\ +\x91N\x89\x07\x1d\xbb\xa9g\xa5\x94\xae\x80E3z\xca\ +\xebQt*\xd6\x98\x825\xaaH#\x15\x05\xa9,+\ +\x03e\xf9P\x9a\xd62\x82\xb8d\xcd}d\xc3u\xb2\ +\x01uv\xff2\x8c~\x09\x01\x14x\xb5 \x9e.\xc0\ +\xdf\xb4\x0e\xa0B\x18\xa5\xa6i\x0d\x0f\x81\xe4\x15\x11\x0ar\x07\x95\x13\ +k\x105\x98\x80\x0b\xd1k\xa0\x1dO\xd3>\xc5=\xf3\ +\xbec\x06\xd1\xfd{ V\xf5L\x06\x0f\xa5\xb2/\xad\ +\x8cI\x5c\x09\x18\xca\xf03\xc0A\xb9E\x88e.\xcf\ +\xa7\xda\xf82@P\xb5\xfa\xd4\xed\xa8\xa9\xbb\xfa\x0a\xe5\ +\xe3\x114]\xccXs\x02\x11\ +\x94\xab\xc9\x197g\xaf\xf9zrhW\x17@\xa6 \ +\x90\xa8\xd2U\xa5T\xaa\xa7\xcbG\xc1ES\xf7\x5c\x1e\ +Z6G\xca9\xff\x94\xb4\x04\xd1^<\xa4z\xe1\x99\ +\xd5,=?\xcfWq\xcd\xa4\x82\x85\x10\xc2(\xf9\xb4\ +\xea\xe1\xfd\xed\x22:\xb8V\x15:\xe2qT\xe3\xafJ\ +\xd8\x14m\xfel\xc6\xeaTIK\xecK\x03\xc8\xca\xed\ +U\x0a\x9eD\x5cL\xa8\x1bf \x87p\x9d\xc3\x00a\ +\x0cw\x0ap \x8b!\x5co\x14\x90\x17LA|a\ +\xc6\xe5d>\x95\x5cW\x93&\xe5\xe2\x07\x137\x9d\xf7\ +\x0bCb\xb5\x1a\xa9ei\xa5t\x8a\xbbRzb\xf5\ +\x08\x83\x8fEV\xfeCT\x98\x91R\x87\xf5\x036U\ +D\x85\x18\xec\x08\xde/B\x8c\x95W\xcdRyv'\ +\xe0h\x1b\xc63\x0d\x00\xca\x01\xee\x1d\xd0\xf0\x0c\xc9<\ +K\x12\xa14\xb1~\xd4Sz\xcd\x90\xc5U\x0dX,\ +J\x8fE\xe9W\xca{\xecJY\xb7\xc5:\x01\x00\xe5\ +\xb2|mv\x19u\xe9f6\xc0V\xf5\xb6\xdb]\xe4\ +vx\x9d\x91\xe7\x9f\x02\xf2\x9b\x84.M\xf3k)y\ +\x8b\x90\xa2\x0a&-k\xab~\x91\xb1\xd2\xc2\x11\x9e;\ +F\xec9\x0c\x11+P>>w\x13e\xdc\xb5\x86\xb5\ +G\xd6\xba\xf1\xd4:6\x9f[\xfb\xc6sk\xbd\xfa\xcc\ +\x1aV\x89\x99\x90\xa7b\xb1h\x01A\xf3\xe50bW\ +\xf0!\xe9\xe5\xbd\xea\x00T\x11\xe3V\x08!]\xa3\x9a\ +\x17\xe7\x7ff\x1e[\xd9\xc2s\x06\xfa\x95U\xad\xbc\xb6\ +\xea\xd5\xd7V\xc3k\x0d\xaf\xb5\xb8\xdc\xba\xabo\xacv\ +\x9d\xbf\xad>\xb1\xd2\x05\xd5\xcaifO\xeb\xfa\xaa\xef\ +W\xb95\x83\x7f\xb1\xea\xc5\x83'\x12\xf3\x12\xa5\xe0\xfa\ +\x11g1Z\x05K\xc6:\x93\x88\xbdZ\x16\x8d'.\ +\xc6Uuc9\xda\x07\xd8\x85\xc5\xf6\xf2\xc0\xc3\xcec\ +d\xe3vs4\x039y\x02\xfb'\xeb\x01\x04\x92\x12\ +\xd2\xa5\x22\xbc^\x9e&W\xa4|\x14\xa9\x02K\xb9\xf9\ +$\x17{\xa5|\x09\xf7P\xcd\xef\x9c\xe0\xa2k\x95\xee\ +\x09\xa4\xcaB\x94\x95\x90\xcb\xe315\x81\xa6I\xaa\x10\ +^G\x045\xa3e\x84\xb0\xd1k\xc95\x9d\xfc\xbf6\ +\xa7v\xe3\xd6q\xe5N\xfa\x9c\xc4U\xc9\xbdK\xfa-\ +\xb6\x1c\x00H\xca$x\x81\xf2\x01,\x7f\x10\xe5\x8f\xa0\ +|\xc2\x0c\x8aT\xb5\x92\xf6fH\xf1.\xa3\x99#l\ +\x01b\xadu\xe4\xe0\x054y\xa4\x14\xd3\x15\xf7\x92\x81\ +x\x05>\xe2-\xe2,\x18\x12\xa1#F7\x98\x87E\ +\x94\xe2\x12kV\x1fZ\xf3\xd6s\xeb>xkc7\ +\xbe\xb0\xf9;\xdf\xda\xe2\xddom\xf6\xeck\x1b9y\ +o\xad\x00BS\xbe\x8a\x8d\xb9\x90\xaa(9tt\x80\ +\x87\x1d\xe0u\x90\x9f\xb1\xf8\x5c\xa7\xf8\xbbd\x15\x0f\xc8\ +C\x1f\x93s>\xb5\xd2\xf9\x97V\xb9\xfc\xc6\xaa\xd7\xdf\ +Y\xcd\xd5wV\xbb\xf1\x16\xb7\xfb\x86\xf7\x80\x00\xa9\xc6\ +\x05W\xad?\xb3\xf2\x15\xad\xdc\x9d\xc2\xbc\x0f\x88\x91\xeb\ +\x96\x8ekK\x22\xd6&\xd4N\xa0\xd4\x09^'\xb1\xec\ +I\x94?n\x09(Z\x92\xc8\xdf\x93\xc8\xb3\x93H\xb7\ +\x12a\xde\x09Xb\x1c\xae3\xb6\x12\x8b)\xef`\xd0\ +:\x18\xb4n\x06m\x80A\x98\xf0\xbc\xc0\xd0\xa6\xbbF\ +>\xe9P\x01)o\xc1\x946\xbc@\x125\xcb\xa6x\ +\x0f9\x15\x97\x10\xc1\xd3\xba\xbbW}#\x12&\x85\xe3\ +\xaa\xeb\xe7a\xe4*\x07W\x15\x92\xea\x10E\xdc\xe0I\ +\x18@xD\xeb(\x8c\x81\x16m\x00\x81*\x81\x15\x9e\ +\xd2\xb1ro\xb3j\x97%Ttr_]\x08 \x95\ +K\xaf\xecq1=\x1e\xe5\xc7c\xdd\xf1UC\x08\xde\ +\x00e'T\xe1\xcd\x00[b\x8d\xbc\x90\xd6\x144k\ +\xa9l\x02\x0f\xc3\xf8\xe7\xe3\x9d\xdd\xe2\xd4\xfc\x03Wy\ +\xa4\xfa\xbf\xc8\x98\xf6\x1e\xee\xb9I/\xcd\x13\xa8\xeaJ\ +\xd9\x88\x0f\xcbW\x88\x92\xe7\xf0\xb5@\xe4\x91\x98\x5c\xcd\ +\xac\xcd\x9e\x92\x06=\x84\x0c\xbd\xb4\xd6\x83\xf76x\xf3\ +k[z\xfc\x93\x1d\xbe\xfd\x93]{\xff';x\xf5\ +\x07[\xbe\xff\xd1z\xf7_\xe3\xbe\xb9\xc8\x98,\x1c\x91\ +\xc2\x87\x15\x1an\xe3A\xee\x92F\xde\xc7}\x8b=?\ +E\xf1/q\xb1op\xb1\xef\xdd~\xfbZ1l\x88\ +V\xed\xd6{\xab\xd9|mUW\x9f[\x05\xf1\xb7\x5c\ +\xe5Q\xb8\xe0\xe2E\x95N\x9d\x91\x7f\x93o\x93\x85\x88\ +\x11gp\xf3\xa9 7\xb9y\x19R\xb4\xf2;Y\xe4\ +w\xf3\x96DN\x9c\xd4\x04H\xf8\xd9\x09?'\x82\xf0\ +\x04<\x85\xe2g\x02\x16\x95\xc8\xc0&U\xf7C\xd4\x88\ +\x97\xcaVD\x18\xf1\x02\xa1\xa1-\x94\xb5\x87\x9b\xd4\x0a\ +\xa2\xa6W\xbd\xa5`\x15\x9eh\x11K\x03&k\xd1\x9a\ +{\x12\x16\x97T\xa7\xd02\x87\xcb\xd6\x12\xec\x1a\xbcb\ +\x93\xf8\xad\x125\xdd+\xe9\xe1 \xf7\xad\xa5\xf0\x09Y\ +\xa0\x96\x98\xef\xb9ei-\xd8h\x7f\x85\x06?M\xe9\ +\x1b\xcaM\xa8\xe8\xff$\xf1z\xad\x84\xd1W\xc9\xba\x87\ +\xbc\x10s\x0e\xb6\xc4:\x11A-\xf7jQI\xcb\xca\ +\xaaz\xde5\xed-\xd4\x0e\xa5\xa8\x96\xbcU\x84\xa2z\ +\x86\xb9\x07\x16\x9d\xbama,_\xd3\xd5\x9a\xfa\xf6\xb2\ +\x15Ur\xcd\xbb\xeb\xfb\x91\x00\x5c\xe6b]F\xc4?\ +&\x1f7X\xa28\xb8\xfe\xd4\xeav\xb4\xa3\xf4\x0b\x1b\ +8\xfdh+\xcf\xffh\xd7\xbf\xfa\xbb\x9d}\xf3\xcfv\ +\xfd\xc3_m\xe3\xc9\xcf6pD\xae\xbc\xf0\xc0\xb4\x9f\ +>\x8a\xd2\xf3\xc6\xb4A\x94\x9cz\x1a\x17?\xfb\xcc\xca\ +\xe6_\xe0\xea_\xa2\xf4\xb7\xb8\xf3/\xadq\xeb\xa3\xb5\ +\xed\xffh\x1dG?Y\xc7\xf1\x0f\xd6r\xf05\x00\x00\ +\x14\xb0o\xb1\xf0B\x5c\xaf\xae\xafmJ\xd1\x89k \ +\xf7\x18\xe5\x1fX&1\xcd+\xf5f\x80U\xab\xd0\xa3\ +\xbd\x7fG\xa4tG\x0c\xf8!\x8cy\xcf\xd2:\xb6-\ +\xb5}\xc3R\xb0\x86\x148Hr\xeb*\x02Xp\x8d\ +\x02B\xb2\xcb\x9b5\x19\xe3\xe5\xe5J\xd9D\xe4\xb4\x83\ +\xd8\xafy\x02\xd22\xf5\x17P\x9f\x01\x15]\xba\x1a<\ +2\x1e\xada\xa8\x08T\x936\x9a\xb2MQ\xe1E\xc3\ +\x1c\x1eF`[\xb2\x14\x14\x91\xda\xae\xba\xc4=\xee\xed\ +\x00\xe5k\xad@\x1bSnY\x08\x82\x1bA\xf9\xd1\xe9\ +\x87\x96+\x99\xbc\x07!\xc3\x13\xa8@\x86\x10\x99\xa5\x82\ +Rm\x85\x97R\xa5\xdcZ\xac\xba\x86\x10\x82w\x93$\ +\xab_\x02\x22\xc0%\xd6\xcf\x12\xd6\xb8>\x1e&M%\ +\xed*o\xd7~B\xbc\xad\xae\x15\x1e\xbfc9\xba\xd6\ +\xccC\xe4\x81E&\xef\x00\xc0\x1b\xfc\x9d\xb1\x82\x9f]\ +\xd4uj\xf2\xc8\xc7\xf7\xf8y\x0d06JK\x95\xa2\ +\xbb4Q\xf3\x00\x05\xf3*\x14|\x88R\x9eY5\xca\ +i\xd8\xfb\xdczn|\xb4\x85\xa7\xbf\xd9\xd1W\xffb\ +7\xbf\xfd7;\xf9\xe2\x9fm\xfd\xd9\x1fm\xe0\xe4+\ +\xe2\xf8S\xcb\xd3\x83\xa1\xf8\xfc\xa9GV<\xfb\xc4)\ +\xbdr\xe9\xb5U\xae\x90B\xad\xa0\xfc\xb5\x0f\xd6\xb2\xf7\ +\x9d\x0d\xde\xfa\xa3M?\xf8\xab\xcd>\xfa\x9bM=\xf8\ +\x93\xf5\xdd\xfc\x88\x97y\xe5\xea\xe2\xb4)5\x07W\xa5\ +\xd2\xa7l\xd2M\x95*]\x886{\xb8-\xd4\x9a\x85\ +\x1b\xd3\xd4\xec=,\xeb\xa1E\xc6\x1fYh\xf4\x01\x7f\ +\xbb\xcb\xa0\xdf\x02$'\x96\xd1\xb3oi]\x80\xa1\x03\ +0\x90\xf2\xa5\xe2\x8e\xd3T\xba\xad\x12n\x95\xaeu)\ +\x85\xf5D\x9b<55\xec\xe6\xf2\xc9VTN\xae\xbd\ +\x04\xday\x14\xec\xe7\xda\x02\x01\x04W\x9f\xf1\xa9\xe0\x94\ +TR\xde\xc6y\x19\x94\x9f\xd4\xb2b\xc9mW\xb9\xd6\ +\xb6\xa5w\xed\x03\x80C\xf3\xf7\x03LxO&\x16\x19\ +$\xbb\xc9\xd6T2^ Gc\x84\x17\xc8W:\xa9\ +Wy\x06\xc2\xa3v\x1be\xaa\xcbH\x1b\xdf\xd1r\x15\ +`\x12B\x1a\x89\xcf\x0dX9\xc0H\x05l\xc9\x02\x9d\ +S\xfe\x8a\x03y\xba\xf65\x08l\xfd\xc7\x969\x04\xd8\ +\xb4\xb4\x0b\x00B\x5c#\xbe\ +G%}\xae\x88W\x15^J\x89\x19\xe7\x08\xc41\xa6\ +@\xe5\xc90\xe0b\xad7\xe3\x92+\xaf\xbe\xb0\xa6\xfd\ +\xf76t\xfb;[z\xf1'\xbb\xfa\xf6\xaf\xb6\xf6\xea\ +\x9fl\xe6\xc1/\xd6u\x0c\x00V^@4p9\xb3\ +\x8f\xad\x106\xaf\x961\xe5\xcb\xaf\xac\x12B\xa7r+\ +I\xf5\xfa[\xac\xfe#\xff\xf3'\xdbx\xfd\xcf\xb6\xf5\ +\xe6\x9fm\xed\xc5\x9fm\xf4\xec\x1b\xb7\xd7=\x9ft\xcf\ +\xd5\xdeA\xb8T}sQ\x01\x9b\x0dk\xd5\x0e\xda\x88\ +\xd6\xbb\x19\xb8<\xcd\xc8\xa9xs\xf9\x9d\x95\xac|\xb0\ +\xe2\xe5/\xf8\xf9\xbd\xe5\xcd\xbe\x06\xfd\xcf\xb0\xb8\xfb\x0c\ +\xfc-\xf310\xe9x\x0a\x01!\x0d\x85\xab\x9d\x8bW\ +\xb4\xa9%S\xae\xc5g\xa2\xaa\xe1\x93\xe5\xc8U\xf7\xe1\ +e\xb0t\x95D\x09\x00\xf24\xaa\x15pB\xca$\x0b\ +\xca\x808i7Q\x0a\x83\x98\xec\x04\xe5\xb7\xae\x01\xb2\ +MKE!\xe9j\x17\x03\x00|\xdaL*\x10`\x99\ +\x99\xda1Lh\x94\x82\xb4\x9e\x90\x8bK.$.\xab\ +\x12\xa9\x8cL\xa7t\xe1\x85\x15\xc2\x89r\x00q\xf6\xc0\ +\x19q\xfc:`\xc0\x83tr?\xed\xbb\xa4\xb9\x00\x97\ +k\xa4\xa3\xacT\x89v4u\x02\x94\xee}\x80~\x01\ +6\x81@\xd7\xb9\xc5un;\x09\x02\xac\xc0\xd0\x0dw\ +/\x19\xdd\x02\x80X?\xdf%n\x02\x80\x02*\x91g\ +\x5c\xb4/Ben\xe1\xe1k\xa6\xd66Q\xf8CL\ +>\xcc>\x9f\x9bT\xc5\xaa\xa4@3ex\x84\xba\xed\ +\x97\xd6q\xed\x83\xf5\x9e~k}\x84\x84\xee\xeb\xdfX\ +\xd3\xde\x07\xd7\x07\xa7xY=p\x9e\xa3\x14\xdc\xbd\x94\ +N\xfe\x5c\xb1*\x12\xc7\x83\xaa\xf6\x1c \xb5\x01\xa2\xe9\ +{?\xd8:\xa1\xe4\xea\xf3?\xd8\xd2\xa3\xefl\xe0\x18\ +\xa0,\x9e\x12o\xb5|\x89U*?\xd5\xdc\x82\x16\x9c\ +\xb0\xa2\x10\xc4R\x03\x17A\xf9\xb9(\xbf\x88L\xa1l\ +\xf3K\xab\xda\xf9\xcej\xf7\x7fB~\xb6\xea\x9d\x1f\xad\ +l\xe3#\x7f\xfb\x02\x80\xbc\xc6\x02\x1ey\x030p\xcd\ +|\xb8HI\xa0\x1f7\x89\xe2C\xfc>\x07\xef\x91\x07\ +/Qm\xa0\xaa\x7f\x0b\xb4$\x8a\xa5\xaa~_\xeda\ +\x9c\xbb\x94\x90\xba\xb9\xf2s\xbd\xc73dh\xc1\x87\xd4\ +\xd6\xdb[\xe8y\x95t\x066\x1d/\x92\x0e\xc0|\xbd\ +\x84#Dn\xd9/\x10\x10\x0ad\xa1\xb2\xc2L2\xa4\ +,-\xd9\xe2\xe1\x04v\xcd\x9e\xc4\x89\ +,_\xca\x17\xf0\x18h\x1f.8\x83g\xf2i\xfa\x1c\ +\x8e\x11!\xe5,\x22\xc3\xaa#\x9dn?\xfc\x0a.\xf4\ +\x9d\xb5\x1d\xaa\xf9\xd6\xb7VM\xa8,\x9b\x7f\x05\x7fz\ +ne\xf0\xa8\xd2\xf1\xbbV\x84\x82\xf2\xb9/eV\xda\ +\x98\x9a\xd5\x07\xa9C2\x7f\xdf\x9a\x86\xebi?\x82@\ +\x90\x09\xd03y\x95\x07\xf2\x9aY\x09\xd4\xaa\xf7\x83\x08\ +\xba\xf2uO\xf1Ay\x0dH{\xf6\x18a\x08\xf2\x18\ +\x11I\x9d\x06\x00\xa1\x11\x5c\x22\xaeX\xabR\x81\x8by\ +h\x08\x92\x0f\x92\x10\x18\x86\x1cM\x9cX\x04\xa2\x96\x07\ +W(\x80,\x16-#\x22pK\xc46\xe5\xec\xb3d\ +\x02\x93\x00fTK\x99\xaabY\xc7\xba6\xadd\xea\ +\xc0\x9aWnY\xcf\xd6]\xeb\xdd\xbcm\x1dk7\xac\ +zz\xcf\xad\xf0\xb9\x05\x0e\x11\x14M+\x13k\xb5\xba\ +\xe6v\xe5j\xdf<1?2\x05\xb1\x81[\xe4/\xa2\ +4\xc2I\xe5\xce\x17Vw\xfc\xd1\x9an\xfch\xcd7\ +~\xb6\x86\x93\x9f\x00\xc1\xf7V\xbe\xf1\x0d\xf7\xf3\x01\xaf\ +\xf5\x1aK{\x0e/y\x049\x85\x18\x8d\xc0\x19F\xee\ +\x12\xe3\xee\x01\xee\x87\x96\x07\x00\x0a\xe1)%\xa4\xa2\xa5\ +H1\xdf\x9b\x0fyR\x93\x09\xb9\xfd\x0c\x14\x9d\x069\ +Lm#\xcdj\x9d\xc6\xf5k\x7f\xff<\x1cb\xc5-\ +\xca\xb8Z\x02\x11D\x94\x1f\xc4\x05\x07QPP1_\ +B\xfa+\xeb\xd2\x0e!Y\x5c\x86\xbc\x856\xa8\xc0\xb8\ +57\xaf\x86\x0fy#\xdb\xd6\xb4v\xcf\x86\xae\x7f\xb0\ +\x89\xdb\xdf\xdb\xe4\xdd_l\xe4\xf4gk\xdf\xfb\xda\xcd\ +\x89h\xed\xa1B\x93d(\xa5\x14\xeb,&\x84\xe4\xe3\ +\xe2s\x86\x14\xb2T\xca\xa5M;\xd7\xb9\x07\xae\xad\x8c\ +\xc3\x09\x80@\x02x!\x81\xcf/\xaf$\xf7\xcf=\xb8\ +\xfd\x90\x0e\x00Z\xc6\xf6\x88\xa3\xc2F\xa6\xc2\x06\x06\x16\ +\x9a\xbcka\xae%\x12\x19\xe3j\xe54Y\x00\xfa\xb5\ +\x1b'\x95\xdc0\xa5\x19ie0:\x97,];v\ +\x07\x19\x80\xd1\x03\x90s\x8crN\xf8G\x18\xfb\xe41\ +\xeeZJ\x13\x81\xd2\xd2\xa6V\xe0\xc4\xb0\x17x\xe8%\ +\x0b\xa3\xe8\xc2\xe1-+\x1d\xdbA\xb6@\xf6\x86E\xfb\ +\xb1z\xe5\xc4\xca\xb1[\x17\x11m\x0c]\xe6\x7f\xb0\x16\ +\xc8\x97:r\x85A\xa8c\xb7\xa03W\x0b2\x8bO\ +\xac\x04\xafS\xb9\xfd\xc6j\x0f>\xe0\x09\xbe\xb6\x06\xf8\ +E\xdd\xc1\xf7V\xb3\xf3\x83Um~OJ\xf9\x11\xe2\ +\x09?Y\xc6\xc5.\xa0\xe4\xd9\x97V\x04G\xc8\x9f\x84\ +\xb0\x92\x92\x16\xcc\xbe@\xe9*\x03\x7f\x0b\xcf\x11\x9fx\ +E\xea\xf4\x18\xaeq\x8b\xc1\xd9\xc5\xb5+\xd6\x93\xf3k\ +V\xee|\xdf}z\xcb\x04\xf1s\x0e\x0f\xa5\x85\x1c\xd5\ +\x14\x10\xaa\xe0*\x1e\x00P\x86b\xbd\x06\xd4\x81@^\ +@\xec\x1b\x97\x0d\xf9J%\x0bq\xb5\x00\xa4\xa3\x19\x8d\ +\xe3\x16\xe9Z\xb0\x86\xf9c\x1b9|j\xd3\xa7\xefm\ +\xee\xee76y\xfb[\xeb\xd8\x03\xdc\xcbxXrx\ +\x95\x7f\x97\xc0}\x8aa\xf2\x85x\xc2\x06C\xa2\xcd\x15a^s&\x89\x8b\xb3\xb7\ +\xc9T\x1eX\x05|\xa4\x9eT\xb5\xed\xe4{\xeb\xbc\xf9\ +\xab\xf5\x9c\x92^\x22\xbd\xbc\x1f:\xfd\xc5\xc6o\xffj\ +\xa3\xbc\x8e\xde\xfa\xc5\xa6\xef\xfcf\xe3g\xa4\xaf\xd7\x7f\ +F~\xb2\x9ek\xda\x0a\xff\x05<\x86\x10\xa0\xcd\xb0|\ +\xb7\xbf\x7f\x13\x00h\xbf\xdd\x88%VkF\xae\xcd\x12\ +*[-\x09 \x08\x04\xea8\xe6*\x8c\xc8\xab\x95>\ +jS\x85\xfa\x15\xb8\x12z\x14\xef\x94\x8fb\x02\xdc\xbb\ +\xebe@\xb8\x10o\xf0\xb9\xa9\xe4yR\xbb)\x0b4\ +MZq\xff\xb25\xcf\xeeZ\xe7\xca\x91\xf5\x5c\xbda\ +]WO\xadzNet\xbbn\xb5N\xcb\xe6\xd9\x84\ +\x1b\xcdB\x06\x01Q\xa6\x96\xb2!r\xda\xb3!\xee\xe1\ +\xb6\xc7\xf3\xfd>@\x98\x01\x18\xdd\xe6X\xa5\xbd-j\ +QC\xca\xda\x8c\xf2[\x18\xd3V\x94\xdf\xa6P\x84\xd2\ +\x01\x80\x9b/\xc1;\xa4\xc2KR\xf9\xbf4B}\x06\ +\xd7\xf2i\xaeep\x17\x00\x10\xe7\xd4E\x22\x9dAH\ +S\x87\xad&\xdc\x7f\xe34\xaep\xda\x12\x9d\xcc8I\ +\x16\xa2\xf5@\x9a!k!^:\xc5\x03\x02D\xe9\x92\ +\xbaeh\x7f\x9e\x1a\x1e\xb9:6-\x1d\xab\xf2\xc5\x11\ +=\x5c<\x03\xa78\xea\xedC\x00}\x221\x9aD\x11\ +C\xc5E\x05A\xbd\x98\xb3z\xf5\x84\xc6!\xa4\xe2\x1e\ +\x13\xa4+\x93Z\xc7\xc7J\xe6\xeeX)\xd9I\x8d\xc8\ +\xd4\xf1\x176r\xf7'\x9b~\xf2'\x9b\x7f\xfeO6\ +\xf7\x04E\xdf\xfd\x1e\xe5\x7fkSw\xbe\xb5\xf1\xd3\xaf\ +m\x82\xece\xe9\xe1\xcf6\x7f\xef'\x1b\xbd\xf9\xd1F\ +n~k\x83\xd7\xbf\xb6\xc6m\x88\xe0\xf2\x03x\x01\x84\ +hD\x939\x022\xc0\xaf\xd3L\x5c\x97\xc5\x97\xb78\ +I\xacl\xb7d\x00\x91\xa2\xae\xa7n\xc3\xaa\xf6\xdd\x01\ +d=\x93f\x0b\xe1\x0e^\xbb:)\x9fl@\x1e\x00\ +\x0b\x0bh\xf1E\xd3\xbf\xbdd\x14\x10\xc9\x8c\xd6y\xf3\ +3\xae\xd1\xaey+\x1d\x5c\xb1\x8a\x91\x15\xbc\xc1\x9a\x95\ +\x8d\xaa\x10\x94\xef#\xd4\xaaa\x85V\x1a]\xc1G\x9d\ +\x96\x805\x0b\xa8\x95A-\x0b3\xce\x18T*\xe3\x9a\ +J\x86\x92\x0a\xb8R\x19\xd3T\xa5\xa9\x00 \x9dT1\ +]\x16\xdf\xa6\xf0#\xc5\x13\xd2\x9c\xa0|B\x81&\xad\ +4G\x92\xc6\xff\xa7AL\xd3{%\x18}\x1fY\x0e\ +\x12\xe3\xebY\x858\xe0\x86\x89\xdb>\xe2\xb3\xeb\xb5\xc7\ +\x83\xca\xc2SPr\xf2\xf9DHr\xa3\xa6$\xb58\ +\xa3\xc6\x8eR\xfe\xcay\xae\xea\xddH\x86,\x1b\x94i\ +\xe3\xa6\x8a\x18/\xc4U\xb5\x92\xee\xa9\x9a&\x82{\xd7\ +\x0eZ\xd5\xdbGx\xd5\xbc\xf9\xa7\xee\x9f\xaa\xba\x1d\x82\ +h\xa1\x94,\x08eh\x0cR9\xb6\xcf\xe7\x0eH\x97\ +\x8eL\xbbm\xd4\xe2\xac\xee\xea#\x1b\xb8\xf1\xc1V\x9e\ +\xffjG_\xfd\xddn|\xfcW;\xfa\xe2/\xb6\xf2\ +\xe4[\x9b\xb9\xf3\xd6\xc6o<\xb7\xc1\xc3\xc76L\xbc\ +\x9d=}g\xd37\xdf\xda\xd0\xe1K\x1b:zi}\ +\x07x\x8f5\x98\xf6\xec\x09\xdf\xa9\xf4\x93\xf0\xd5>\x0d\ +\xe8\xd5h\xa1\x0f\x00tZB\x05\x1e\xa0\xbc\x95\xd7v\ +@\xa0p\xa0\xde<\x02\xc18\x166\x03\xc8\x09i=\ +\xeb\xfc/\x80\xd6b\x0b\xcf\xa5\xb4\xcf/\x0f\xc0\xcf^\ +{\x1b\x00\x80g\x11\x99\xd6zF:\x9cJ]S\x03\ +\x84\x14\x7f\xd3\xa8\xf9\x1bG\xcc\xa7N\xa6|o\x92\x9b\ +\x02\x1e\xc4\xeb\x0cZ|\xc5\x80\xc5],\x02\x95k\x19\ +x\xc8\x12j\xf0\xc8\xf5\x18\xa2\xa6\xbd1\xb6$\x8cM\ +\xb3\x9e\x9a\x01M#\xc7\xcf\x10\x07\xe8\xda\x87\xc8z\x92\ +\xde\xa9Y\xd2\x1d\xf4\xb2\xe9\xe6,RP\xbe\x00\x93\xa6\ +0\xdf\xbdJ\x0a\x0b\xd7\x03\xf4\x92\x0c\xde\xc7\xb8V\xab\ +\x17B8P\x91\xa2\x10\xae\xc5\x0d}\xb9\xa6!SZ\ +\xd7\xdd\x1c|j\xb3\x17\xb7\xd3P~\x1a7!\xc5{\ +92\xa8r\x00P\xca\xc4\xff\x7fR>.\x0d\x8e\xa1\ +\x9c_\xe5\xcbZ\x9e\xd4nU\x89\xdb\x8a\x8d\xc5\xab\x16\ +N\x9b4\xfc\xa0\xd1\x07q\xf4C\x14%\x81\x81U\x06\ +w\x1d\x80h\x0d\x7f\x8b\xb4m\xcf\x8ag\x8e\xaca\xfd\ +\xb6M\x9e\xbd\xb5\xa3\xcf\x7f\xb5g\x7f\xf87{\xfb\x97\ +\xff\xd3\x1e\xff\xf4\x17\xdb}\xfe\x85\xcd\xdd|h\x03;\ +\xd7\xac}e\xcf:V\x0elx\xe7\xa6\x0dn\xdf\xb4\ +\xce\xd5\x13\xeb\x5c\xe3\xf7H\xe5\xcc\x8e\xe5\x0d\xafBR\ +\xc5K\xa6\xb0<-\x19\xf7\xa1h\xad\xb1w!\x1d(\ +\xa3\x03\xe5w\xba\x9f\x93\xf0\x02j8\x99R7L\x5c\ +\x1f\xc7bg\xf0n\x8b<\x1b\xbc\xa0\x0f\xb0+{\x92\ +\xdb\xefE\x11\xb2z\x9e\xc3\xafLG\x03\x0c\xf9Ko\ +S5\xb0\xa6x\x15b\x06\x00V/\x00\xebF\xba<\ +)\xeb\xb6\xc42~\x87\xc4KJ{,N\xe2\x96~\ +\xfb\x09I\x1e\x08\x12\xea\x00A#\x86\x88\x0e\x92[\xd6\ +\xd0\x09\xe3\x8eu+\xc6gh\xa2\x08\xb2\xa7I\xa0t\ +\xb8T:Y\x95\xf3\x14\xe8 \x0d\x02\x9b\xaeu\x15g\ +\xf1\x00\xbe\x17\xaf\xa4{S\xe9:\x82\x07\xe0\x86\xc9W\ +\x03}rk\xb0}\xdcX61M\xb3hJ\x1f\xfc\ +\xa4\x19n\xee]\x17s\xeeFn\xc7\x13\x17\x83\x1c\x08\ +@\x17.\xc9\xa7\x18)\xf7\xffI\xf9W\x11^\x19(\ +\xcd;\x87\x86v]AF\x88\xb8\x97\xad]\xb1\x03\xba\ +.\x83\xd5\x0d1\xec\x9c\xc3M\xcd\x22\xb8\xc3\xae\x19n\ +n\x96kC\xc0PTv\xff\x12\x8cx\xd5\x0a\xc77\ +\xac~\xf9\xd0\xa6n>\xb6\x9b\x9f\x7f\xb4\xf7\x7f\xfa\x9b\ +}\xfb\xb7\x7f\xb6\xd7?\xfdj\x07O^\xd8\xc4\xde\x89\ +\xb5\xcc.[\xf9\xe0\xa4\x95\x0dLZ\xfd\xf8\xbc\xd5\x8d\ +\xcd[\xc5\xd0\xb4U\x0c\xcf\xe2~\xe7,\xafw\xc2\xb2\ +\xda\x87-\xd0:\x08C\xef\xb3\x94\xdan\x94\x8f\xe2\x89\ +\xf9\xff\x9b\x08\x0c\xbc&U\xf3\xf7\xea^\x148`\xa9\ +\xf0\x04\xb7\xa0\xe4\xb2\x03<\x01\x16\x15P\xda\xac\xcd&\ +p\x1c\x91I5\xb7Hk\xc3\xab\xb4N`4c\x84\ +S\xbcK=\x96^\xa3E\x1f8\x06J\x8f/\xebD\ +\xd1\x1dN\x12\xd5\xb7\xb8\x14 \xf1%\x9e\xc4\x95v\ +\x9f\x83\xa0\x0f\xaf\xa0E-\x15\x88(,/\xf2\x9d\xea\ +\x89\x80.p\xf3\xbeN\xf5s \xec\x90\xef\xfbd\xb0\ +x\xdft\x8c8\x95\xac&\x19o\x9e\x02P\xd3Pr\ +:J\xf7\x8c\x0b\x03G<\x80\xf2;\x95\x84\xa5\xa30\ +\xcd\x82\xa9\xe5\xaa6>\x84 a\x11\xcd\xc6iN{\ +\xf46i\x1e\xa9R\xff5<\x04\x17Q\x9e\xdb)\x86\ +\xb9\xc1\x0d\x00\x86s\xe5K4u\xfaI\xf9R\xba\x13\ +M\x86\x88A#\xbcfq\xe1 n7\xd8G~M\ +\xfcS/\xbb\x8cN\xf2\xee\xb6q\x1el\x98P3h\ +I\x8d\x03p\x8e~\xf7\x9a\xc2\xcfi-C\xc4Q\xad\ +\xe5\x8fZ\xb8g\xc2\xca\xc6\x97lp\xe7\xc4\xf6\x9e\xbc\ +\xb4g\x1f\x7f\xb0\xf7?\xffj\x8f\xbf\xfc\xda6\xce\xee\ +Y\xf7\xd2\xba\x15v\x0dY\xa0\xb6\xcd2\xaa\x9a,P\ +\xd3l~\xc4W\xddd>\xbd\xafk\xb5\x8c\x9a\x16K\ +\xadF\xaaZ-\x19\xc2\x97\x88\xcbO\xc4\xdd;\xabw\ +\x1e\x00\x8bD\xe9\x89Ux\x04\xac_\xcaO\xaa&\x0c\ +\xd4\xaa\xdb\xe8\x08\xa4P^`\x0a\x82;C\xb8$\xd7\ +oS!\x8a\xac\x5c\x04\x9a4\x92\xcf%\x126\x12\xf8\ +\x0e\xd5\x22$\xc8\x93\xa8_1\xd7\x88/kC\xe9\xad\ +(\x19\x9eQ\xdc\xec^\x13J\x087%\xfc\xbe\xb8\xdd\ +\xe2\x90\xd8\xe2\x0e\x8b-\xe9\xb4X\x81\x80P\xa0e\xe1\ +\xc4\x1a\xbe[\xeb\x04\xeaQ\x80{\x0ft\x8b;\xc1\xa3\ +\xfa$\x10Q\xcdDb\xf5\xda\xf5\x94J\xb6\x95\xdc6\ +e\x89-c\x84\x8cQB\x05\xfc\x05\xa3\xca\xe8&\x0b\ +\xeb\x85\x98\x93\xd6\xfb\xc5\x01\x00\xae&\xbfbD0\xd2\ +5\xe3\x85\xdbWK\x12\x15O\xe6\x90#F'\xef\xf3\ +\x0aS\x1e\xbb\x039\xf3@\xa0\x99.\xbf\x9bi\xf2\xa6\ +\x1a\xddvr\xdc\xbfS>$\xc3+u\xf6f\xc0\x9c\ +\xf2Q|&\x08\xd4\xfc\x80\x96a}XxF\xc74\ +\xc0\xe1\xa6\xda\xb0\x8e\x16\x94.\x85\xd7\xe3\xfajA\x7f\ +u\x87\xc5U1\x10U\x0c\x88\xd88\xaf\x92\xc4\xeav\ +,\xa8\xc3\xd2\xea\xbb,\xd29l\x8d3\xab6up\ +\xd3\xf6\x1f\xbe\xb0\x9b/\xde\xda\xfe\x83\xa76\xb9sl\ +\xf5c3\x96\xdd\xd0a\xc9\xa55\x96PX\x89TX\ +BQ\x85%\x16WZbI\x95%\x95T[Ri\ +-\x16W\x8f4\xe2v\x9b\x11\x14P.\x00\xa0,)\ +\xbf\xaa\x97\xeb\x89\x0f\xf4\xf3\xca\xbd\xd5H\xa1X\xb0Z\ +\xdc\xd7\x8d\x90\xdf_\x08\x83\x8b\xe8w\xea\xd6\x95X}\ +a\xe1<\x03J\x8e+iB\x99\x0d(\xb5\xde\x93\xa2\ +Z\x8b-\xfc\xbd\xd4\xf1\xbb\x06@\xd0\xe4\x01\x02\x10\xc4\ +\x95\xf0\xec%\x00\x00o\x10K8\x88\x85\x13\xc4\xab\x00\ +\xa5N\xbb\x7f!\xda0z\x11\xe70\xfa\xd0V\xf5\x1c\ +$b}?9v\ +\xcf\x88E[{p\xf7M\x96\x5c&%\xcb\xea+\xb0\ +xI%V\x8f\xf5\xe3\x15\x12KQ>\xd6\x1f_\x8a\ +\xf2\x1d\xb8\x00\x9d,\x16\x00\xc4\xf1\xbd\xf1\x0e\x00\x03X\ +\xbb,\x9bA\xc2\xed&7\x92\x05A\xbe\x12Uqt\ +^r\xa6\x92\xb4\x04<\x83\x0aNd\xf5\x22u\xbf\x07\ +\xc0\x95b\x0f\x00\x97\x0b\xaa\xecr^9R\x8a\xe2\x8b\ +\x9d\x5c\x91\xf0sl~\x19\x00\xa8\xc4;\xd4\xf2?\x8d\ +\x0e\xe4q\xfc\x7f,\x1c!\xb6\x12\xf2'\xf05`\xf9\ +\x90m\x91;\x15\x9c\x84\xd1Kt\x1a\xe5\xcf=r\x92\ +;\x03\x00 \xd4\xda\xe5\x9c\x81\xebOi\x1cu\xe3\x19\ +[\xd1lWx\xd6+x=I,^/\xae\x82g\ +\x16\xc1%\xa4%\xd4\x00\x12\x95\xcd\x01\xe2\x98\xd8rb\ +]\x8d\x0a'U\xa5JzC>\x1b\x1eU\xaav\xd7\ +\xa2\x84\x00\xb7\x99p\xf2\x11i\xd8\x13+\x98~\xea$\ +\x7fJ\xa7\x8b\ + (\x00\x04\x85x\x81b@PnW\x8a\xf0\x14\x84\ +\x89+\x80\xf2\x0a\xdf\x15+`\x01\xb4\x94\xd6\x05G\xec\ +4\x03\x18\x998\xb3\xe8\x0c\xfa\x98G\x1fH\xee\xac\x0a\ +BH\xa3!\xd5\x01\xd29\xb5\x86M\xaeW\xd6\xd0\x86\ +\xc2\x1b\xf8\xbej\xbbL\x08\xbc\x5c\x00\x00y\xd55b\ +\xb9F,\xdeF\xcf\x1d\xab\xe7\xe6~cbE@\x18\ +\x5cU\xbdj\x0aS)\x8d\xb6FG\xc6@\x9b\xbah\ +M=\xb4\x02m\x9d\x9aWO\xe1W\xc8k+\xd6\x89\ +\x1a\xb3\xcf\x1c8\xd4A#t\xbe\xc6\xeez\xf4ha\ +\xa7[\x8a\xd6\x19\x02\xc7\xae\xfc\xbb|\xf2\xaeU\x81\xd6\ +\xca\x99\xdbV:qby\x83x\x1aBCF\x8b\xa6\ +\x9e\xe5f\xe5n\x89\xa3\xff\x9b\xc8\x0d+\xb6\xca\xd5\xe2\ +-\xb0\x8a\x84\x0a@\x82\x8bt\x0c\x9a\x81\x8e\xc7\xd5\xc6\ +\x13O\xe3p\xb3\xb1XZ\x5cA\x19q\xb5\x9c\xbfa\ +\xf5R|e\x1dd\xaf\x1e\xd2Wo)\xd5\x80\x04b\ +\x98XI8q\xa1\xa6\xcd.3\x10\x97$\xb8\xdf\xcf\ +\x90\xcbR$\xd6\xa1vu*\xa7rM\xa9\x00\xb0\xda\ +\xd3\xa5\x91V%\xb5.\xe2\x05T\xa38\x82\xd7\xd2`\ +\xff\x8e\xd5\x0b\x00r\xe1|OlI\xb3]\xc6\xb5_\ +f\xd0/\xa1\x00\x07\x80\xdcB@\x90\x87\xe4\x02\x82(\ +\xde\x80\xd7\xbc|\xbb\x9c\x8fg\x90\xa2t\xa2\x09\xca\x11\ +\x00\xe2\xc4=\x00Z\xaa\xa6\xd6\x95F+}V\xa3\xa7\ +i<\xb3j\xfeP|h\xfc\x10`\x90\xae\xf7h\xce\ +F\xc6$#\xc2\x03a\x0cWJt2\x0a\xd6\x8f\x87\ +\xb9\x5c \x10\x10\x86\x0a\x01\x99;1\x05\xa0)D!\ +\x97\x8b\x9a\x00@\xb1\xe2\xa0R!\xf2\x5c\x15\x0d\x12\xcb\ +\xb5\xe0\xa0\xa5Ym\x94\xcc#\xde\x14\xcc=\xb6\xe2\xc5\ +\xe7V\xba\xa2\x05\x15\x00\xb0\x04\x00\xe6_\x10\x8b\x1e\xe1\ +-\xce\x08\x07\xd7a\xa3\x87X\xfd\x9ee#\x11\xde\xe7\ +\xf1\xbb\x12\xf8C\xf5\xec\x13k]{m}\xbb\x9f\xdb\ +\xe0\xfe{\xeb\xdd~n-\xeac?\xbek!\x11\x92\ +&n\xde\x91)\x89\xb7\xdd\xc9\x131j\x84\xfbr@\ +\xa8\x04\x04b\xc5b\xc7\x90\xa5\xd8\x22\x90\x5c\x00\xa2\xf3\ +x\xb0\x5c\x01\xa0\x02\x00T\xe1R\x89\xabeX{e\ +\x03Jo\xb2\x94\x9aFK\xadmB\x9a-Y\xf1\x10\ +\x8f!\xbe\x11\x8b\xd5_Fi\x97\xce\xe53\xbd\xa2\xcc\ +81\xfe\x86Q\x88\xea\x1c\x83\x8f\xb7\xea\xdf\x22\x85\xda\ +v\xf9tr\xfb\x0a^@\xc5\xa7\xe7^\x00\x0e\x10\x8f\ +\xa7\x8a/\xfb\x07\x08\x14Vbq\xe7Wp\xebR\xea\ +%\x94\xe0\xbc\x00\xae\xff\x12\x1e\xc0\x03A\xd4\x13@p\ +\x09O\xe0\xfe^P\x03h\x00\x00\xdf\xa5\xb2\xf0\xc4z\ +\x11e\x98\xbb&\x9d\xb4\xc1cT\x9eY\xdd[4o\ +\x02\xf1&uN\x85\xed'\x13\xd7\x13\xe0E\xf1z\xae\ +\xf2\x06\xc0Ws\xae|\x89\x94.\xd1\xef\xea\x183)\ +\x1f\xef\xe08\x8a'1n\x22\xa2B\x8d\x88\xd5u\x1a\ +\x96\xc8C\x0a\xf9\xd9\x83*h\xb8\xe1\xaaF\xf2\xb5\x95\ +X\xe7\xe1,=\xb3\x22\xa4p\xe1\x19\xbf{\xc2\xdf\x1e\ +pS\xe2\x04j\x0c\x01w\x18<\xb1\x5c8D\x11D\ +\xa5\x1c\x82R;\xff\xd4Z\xd6\xde\xda\xf0\xe1G[\xba\ +\xfb\xabm>\xf9\xcd\xd6\x1f\xfc`3\xd7\xdfX\xc7\xea\ +-+\x1a\x22\x05\xc5\x0bh\x7f\x9b\xd6 R\xe4\xf6\xb4\ +\xdb\xc6\xc9\xf9\xfe7\xac\xcd\xb1l\xcd\x96\xc9\x03\x08\x00\ +J\x97\x0a\x01@\xbe\x00\x00\xa3F\xe2\x0a\xea<\x8f\x80\ +\x15%h*\xb7\xaa\x85\xffk\xe1\xff\x9b-\xa5\x0e\xa9\ +\xe7}\x1d\x19E\x0d\x8c\x1f\xbe\x11G\x18\xb9\x82\xfb\xbe\ +LH\xb9Lh\xb9\xa4Wm\xbc\xc0\xfd&\xd5+\xfd\ +$\xdd\xeb\xc2 4i\x02\x10\xd2PDJ\xa7j\x0f\ +U\xa4:\x0f\xa9\x22\xd5\x02\xa0\x09\xdc\x97<\xa8\xf3L\ +\x02@)V\x08\xb9t\xae\x16\xb2y\xc5\x9dU\x04\x17\ +\x90+\xce\x87\x0b\xe4\x15a\xfd\x00!\x17 \xe4\xe6;\ +`|\x06x?\x03\xcc\x97\x8aZ\xed2\xcf'\xf6\x9f\ +\xc0w\xa7@\xd6\x14\xdb\x03\xdc\x83\x0aY3yU\xa9\ +|:d:\x19\xa6\x9fX\xcbu\xaby\x16b~l\ +\x99\x94\xcfX(\x9cp=\x11Le\x1a\xb1(<\x96\ +\x90$n\x22\xef\xe4\xdd\x9f\x84{%\xe3\x88I\x22\xcf\ +T\xd7ImgV\x17J\xf5\xa2S'\x0d5A\xc8\ +V\xf3\x85\xb1\x13\xdc\xcf\x0d\x08\xc7-\xcb\x9b\xc5#\x10\ +{rU{>\x0dA\x9c\x84\x14j\xf9\x16\x85\xe7\x8d\ +y\xbb\x7f\xca\xa6\xb1\xfa\xf9\x87\xd6\xb0\xfc\xdc\x9a\xd7P\ +\xf4\xd6\x07\x9b\xb8\xf9\xa3m?\xff\x8b\xdd\xf8\xfc\xefv\ +\xf2\xf6\xcf\xb6\xf9\xf0\x1b\x1b\xde}`\x15c\xdb\xee\xa0\ +)\xedm\x93\xa2SP\xb8\x07\x82\x7f\x88@\x90\x04Q\ +U\xc5l\x82\xd2\xa22,\x8e\x1b\x8fc\xb0\xe2\x00A\ +\x1c(\x8e\xc7\x95%`=\x09\x0c|\x02\x16\xe8\xa6s\ ++[\xcfA\xe0)?\xb5\xb1\xdd\xd2\x1a\xbbyOn\ +\xef\x06\xae\xef|\xf3\x85^\xfb=\xe1\xbd\xb6])\x87\ +O\xe2s)-\xe4\xfe\xed\xe3n\x82*\x15\x12+\x00\ +\xa4\x90\xf6\xa6\xb4\x03\x04M\xcb\x8a\x18\xd6Nb}\xc3\ +X_/\xd6\xaf\xfc]\x83\xab\x81\xd6\x80\x0b\x04M\xc4\ +c\x85\x03H!\xd6x\x09w\xec\xf1\x82R\x84W\xc2\ +\xd7g\x80\xf7\xb3B\x94_\xdcM\x18\xd0\x9e\x80!\xc2\ +\x0b)\xb2\xc6D'\xaf\xa9\xeb8\x80\xcc\xd0\xb9\x01d\ +P\xa9dOIdN\x09\xca\x9c\x5c\xc6D\xca\x09\xd8\ +b\xc9pb\x8b\xa4\xf8\x1a\xc6Ei&\xe4R\x1e^\ +!S\xb3\x8e\xf2V<\xa3\x17N5+9`1)\ +0\xcd\xd4\xc6y\xdc\xbf\xb6Uii\xd6\xab\x84QK\ +\x15mZ\x0c\x0e\x10\xd3\x87\xf0\x08#\xb8\xecQ\x01\x82\ +Td\x1ck\x9f\xb8I\xa6 \xb2\xa8](\xda\x83~\ +\xd7\xea\x96\x1fY\xd3\xda3k\xdf|e\x9d\xdb\xef\xac\ +m\xf3\x9d\xb5\x22C\xd7\xbe\xb1\xd5G\xbf\xd8\xe1\x9b?\ +\xdb\xc1\xab_m\xe3\xe1W6\xbc'\x00l\xb9\x1d\xc6\ +)Xx2qO]\xb0u\xa6\xcf?\x80 O\xa0\ +\x85\x9a\x09\x14\x09\x10\xaa\x00\x82f\xc6\xdc\x94)\xae\xd2\ +\xe5\xcdp\x18$\x01R\xa3,\xc1\x09\xac^S\xbaI\ +\xc4\xc4\xe4Z)\xbe\x93\x01\xecE\x06\x18\xccA\xbc\x0d\ +\xbc\xa2\x16\xcb\x85_\xc4\x13^\x12H\xf9$\xaa\xc9\xd7\ +\xf6+\x97\x8a\xc2\x9ac\x95F\x91B%4\x0dYR\ +\xdb\xac%u\xaeZ2ioJ\xd76`\xd8\xb2\xe4\ +\xb6uR\xa9E\xacu\x12\xe0\x0c\xa1\x08\xfe\xd7\x85\x01\ +\xee\x8b\x10\x13/!\xabQ\xa6\xa1\x90s\x05p^\x01\ +\x18\x97\x1dI\x14(p\xc3\xc5\x0a\x17\x9d\xfcM\x9b?\ +\xe0\x16\x15\xe3\xe7\x82w!\x15T\xe8K\x82\x1c'C\ +zS\xaa\xda\xe15\xad\x90Y\x00\x8f\xc5\xc7\x97\xa1\xe0\ +2Y?\x02\xd8\x048\xcd?\xb8I&\x1d\x8d\x87\xa1\ +$pO*?\xffG\xc99\x9c\xcb\x95\x9dk\xbf\xc3\ +\xa4\xc5\xe8\x8c>\xd7\xf0\xb8C\xdb\x91\xc9\xe9\x95\xd7\x93\ +\xcai]@\xd5AN\xb4N\xa0\xdd\xa4}\x9b\xc4\xa3\ +\x1d\xf8\xc1!\xa9\xe2uB\x00\x96\xaf\xc2\xc7\x99\xbb\xae\ +QT+\x8a\xef\xd8~k\xdd{\x9f;i\xdexm\ +\xb5+\xcf\xacc\xe7\xb5M\xde\xfc\xc2\xd6\x1e~D\xbe\ +\xb6\x85\xb37\xaeJ\xa8l\x84\xeb\x80\xee\x14\x08U\x12\ +\x84*\x09\x8bt@8\x07\x81\x9a-\xb8\xce\x1aHJ\ +\xfd4\xe0 \xe6q\xf3\xf2Z\x0e\xc1\xb8nG\xc2P\ +zBE\x977PX\xb66`\xe8\xcc\xc0\xd4\xfa^\ +Kk\xe8%\xcc\xf4\x9b\xbfu\xc8|\xad:yc\x04\ +\xc0kzV\x16\x06\xbfP\x88Q\xf3e\xf1\x0d\x00!\ +V\xef\x91\xb8\x1a\xbbT\x02\x93&\xae^\x91\x9b%\xc5\ +J &'unXJ\xcf\xae\xa5@\x0cS!\xbb\ +)\xedW!\x86\x0b\x16\xc7}\xc52\xd0.\x8d\xe3~\ +\xe2\x091n6\x91\x0c\xc7\x11Zq\x06\xf8\x8c\x00\x17\ +\xa7\xb0\x81b\xe3+\x95J\x0a|J-g\xf0:\x84\ +\x96z\xd2\xce\xbay~7\x8d\xa5\x8e\xf1\x8c<'\xbc\ +@\xde-\x11v\x9f\x88\x9bO\xe0\xbe\xe2\xc5uJ\xb0\ +r)\x1dB+\x90\xb9\xcd&.%U\xb6\xd4\x8b!\ +\xc0%\x04\x22\x19N5\x06t^r\x9eT\xaf\xfd\x06\ +s\x90\xddE\x8b\xc9h\xdf\xf0\xd2\x1dP\xad\x22\x83\x80\ +\x13\x01A\x877\xae!\xaa\x8f\xf3j\xe4\xb4\xd9\xc05\ +\x22\x22\xad\xd3\xd6\xa3<-\xd3b\xf9e\x8b\x8f\xac\x11\ +\xe5w\x1d|\xb0\x9e\xc3/\x9dt\xee\xbd\xb7\xda\xd5g\ +\xaeKU\xd5\xc2]k]\x7f`}{Ol`\xff\ +\xb1\xf5l\xdf\xb5\x86\x85#\xcb\xef\xc7\xe30\xb0\xc9(\ +\xdf\xb3^\x06\xcdm\xe4\xd0aO\x9aiS\xa3\x05<\ +\x94+\x97>\x97zrc\xe5\xe8n\x83\xa6\x08\x22\x0f\ +\x89\xd25\xd7\xee\x1d\x0e5d\xe9\x0dC\x96A\x8c\xcc\ +\xc0]f`\xf1\xbe\x96!\x000j\xfe6\xed\xa2\x1d\ +\x83x\x0a\x5c\xe7 P\xf8\x81p\xba0$E\x89\xd9\ +c\xa5r\xa7b\xf1\x97\x19\xe4\xcb\x15XiM\xaf]\ +i\x1c\xb782\x81\x04\xd2\xe5$\xc6(\xa5\x8b\xf4\x10\ +@h\x95.\x81A\x8d\xc7\xc2D\xe0\x94!hJX\ +\xe7\x15\xb9\xc3#Zf\x11M\xe5\xaai\xd4<\x03\xcf\ +38Pk\x09x\xce\xad\xae\xa6\x91fju/M\ +k\xf7\xadW!w\xcb\x80b\x16\xe5\x01<\x94\xa9\xd0\ +\x96\x08\xb1L\x90\xe2I\xf1$q%\x9a\xd3\xd0\xf2\xb5\ +\x94\xcf}kb\xca\xad,\xe2\xd5\x90\xc4J\x14/\xe1\ +;\x1c\x00jd\xf5\xbf\x03\x00\xde+\xe6\xe2\xa0FW\ +K\x06\xb2\xd5\x06EG\xa0y \xd0\xe2\x8e\xa6y\x09\ +\x09n\x96O\xa5Q\xde\xd2\xae\x00\x90?}\xea:c\ +V\xae>\xb5\xd6\xbdw\xd6w\xedk\xe4\x1b\xeb>\xfa\ +\xd2\xdav\xdeZ\xe5\xe2C\xb8\xc1\x0d\xcb\xc3c\x14\x8d\ +\x1dZ\xd9\x94\xe4\xc0J&v-\x7f\x08v\xdb\xc1\xc0\ +\xd4\xa1\x04\xe5\xdf\xb8NM\xf6(\xdf\x17\xe1\x93\xcb\xd7\ +d\x8c\xea\xe4\xb5\x1d*\xadq\x09\xc5-c\xc1H\x93\ +v\xed\xe8\xf7x\x85\x06\x85\x8aQ>\xa7\xc1\x1eC\xe1\ +(\xb9y\xcc\xfc(\xda\x93Q\x0b\xb4\x8e}\x12\x0f\x00\ +\xfa\xbc\xa6s\xe1?\x88^S\x00\x8eV\xfd\xb4\x16\xa0\ +\xe5\xe0\x0b\x02wY@(m\xb4K\x15\xedv\x09\x90\ +]\x86\xac\xc6B\x96\xe3\x95\x12\xb6i\x9f\x00a\x01\x00\ +\xb8\x89\x22\x00+\xf6\xaem\xddZ\x1f\xf0\xb5\xc3\xe21\ +\x1e\xd7\xe8\x01cr\x87o\xa8Z\xa8\x19 \xf0y\xd7\ +=\xa4e\xd1\x85]\xad\xc0\xaa\x06\xaa\x9c\xd2\xd6mw|\x1dc\xaa2\ +\xb5\xec\xb1\x9b\xa6\xcac\xedo\xd0\xb17\xa9\xad\xf2\x1c\ +p\xa5F\x8c\xa5^\xde\x050h\xf9\x1a\xaf\x94\xc8\xf8\ +%\xd5b\xedx0\xafm\xec$\x0a\xf6$\xa9\x86\xd7\ +\xaa\x09\x84\xdf\x0b\x00n\x9f\xa1@\xa0-\xe8\x02\xeb\x8c\ +\xc5\x08\xbd)\xa08\x95\x0b\xa5\x13\xff}(Zk\xdb\ +:\x8bO\x00p=l\x9cxu\xfcjC\xaen\x19\ +\xaeO\xde\xe4u\xd7\xf2\x5c\xed\xe6\xab\xd7\x1f[\x8dZ\ +\xa5\xae\xa9\xa1!i\xe3\xf4M\xac\x1f\xae\x00\x89\xd4\x16\ +f\x1d-\xab\x93D\xb5\x9c\xea\xce\xf5\xc3b\xd5\xdc \ +Q\x1b\x22\x15\xb7\x94F\xc9\xd5\x11\x17\x93\xb8Im\x9b\ +J\xa9\xd76f,_\xab`(\xdd\xf5\xe8\xef\x06\xa0\ +*\x88\x94\xb7\x12hU~u\x1e\xaa\xfc\x00\xcc-<\ +ui;\xb6Z\xc6\xa8\xaf\x80\xf6\xec\xa3x\xc2\x81\x18\ +\xb4\xc2C\x1a\x03\x99F\xe8I\xd5\x86M\x85 \x07\x00\ +@W\xa7\xc9\x141e\x119M\xe9\x9e\xb3w\xe43\ +\x09@\xb8\x04\x104\x81\x14+\xd0*\xfc(Um\x84\ +\xa7\xa0L\xb5\xaf\x17\x7frar@-Xd0\x90\ +h\x80\xa0\xbaBw\xe8\x83\x03\xacd\x01#\xe0^\x01\ +\x85\xba\x82\xa9\xd1\xa5\xebA8\xa1z\xc8S\x0b\x8e\x02\ +\x02Rj\xef\xa4\x14<\xb4+\xdcY\xe0\x1axE2\ +\x02o\x99Y\x9eK\xa1R\xe3\xc9xi;\x1c^E\ +\xee=Y[\xcb\xc4+d\xed\x02\x02J\x17\xd7H\xc0\ +\xb0\xb4\xd9V\x9bl\x13\xe0U1\x09\xa06\x91\x9bO\ +\x22\x03p+\x83\xb8{\xd7\xa3\x9fx\xef2\x01!\xf8\ +w\xa2\x22\x08\xf5\xb0\x09\xa9\xeb\x84Z\xbdN\x9dX\xf1\ +\xfcM\x94N\x0a\xb8t\xe6\xde\xebw*\x13\x8fh\x03\ +f\x9fz\x05/\xba>B\x81\x96i\x94\xc0\xcd\x12s\ +]~\xff\x89\xcc\x117\xc5\xeeI}\xe4\xa2d\xf9)\ +\x8a\xf7\xb8}\xb7\xfbV;f\xa4\xfc\x9e\x03\xae\x7f\xc4\ +\xf5\x8f\x11^\xfb\x0e\xb8'B\x97\xc2\x95H+^\xc6\ +\xef*\x9b\xb4\x07p\x06\x0b\x9bp\xee>\x0d\xa5\xab\xbe\ +/\xadA \xd0\x92.a@\xa7k\x09\x8c\x90PM\ +\xfcx|\x80{\x80@\xc6kk9._\xcc\xfa2\ +J\xbf\x5c\xa8\x09\x9djR\xb5*\x84TN\xa0\x00\x08\ +n\xf71\x16\x98\x8c'H\x93\xd5\xab\xec\xcdU>\xe9\ +\xf9O\xcc5\xb1\x1cR\xc7/\xc6\xad[u\x92\x9e\xb7\ +R\x09\x98'\xf2Z\x8b\x84V\xc6H\xbd\x04\xd4\xf1\x14\ +\x10h\x8f_p\xe4:\x008q\xe5f\xae\xd2\x88\xbf\ +\x07\xfa\xd0M\xf7\x12!\x9b\xefP\xf9:\xf7\x9f\xae\xb9\ +\x02\xbe\xcb\xb5\xd7\x07P:\xb2\xc6kq\xcf\xd8\xe9X\ +^\x08\xaa\xc6\xd2\xedo\xc4\xe2U]$\xc5\xab\xd63\ +\xa1\x01\x00$\xa1t\x89f\xb9R@c*\xa22i\ +oc\x84\x88\xdf\xb9\x80\xc0\x0b\xc9\xec\x05\xb5\xfd\xebn\ +\x1f@tL{\xed\x0f\x08\x07\x90\xba\xe9CRB\xed\ +\xb8\x95\x05\x5c\xb5\x90v\xe1\xf6,\xe3\x01\x16\x88\xf7s\ +x\x00\xe2b\x13\x83Ej\x97\x8a\x8bJ\xd6\x1e\xfbJ\ +\xdc\x93D\xae\xaa\x06\xb4j\xe9S\x07I7\xad\xa0|\ +\x15\x9el\x9a\xce\x13\xd6Y~\x17\x9b\x1c\x5c/?\xc9\ +\x80\x80p\xe8V1\xe5\xb1\x04ZGX;\x18\xe86\ +\x0d4\x83\x84\x82\xc5\x0f\xe4\xf2\xc5\x11\xd2[t\xc6\xde\ +\xb4\x0b\x19\xe9\x88\x0e\x94\xd4!\x15\xc9\xe4\xdb\x17\x8b<\ +n\xaf>\xae^\x0a\xd6\xf4\xac@\xe0\x00P\xa0\x09\x9b\ +r@@.\xaf\x09\x1e\xbc\x84>\xab\xb3\x02\xb5\x93Y\ +1\xdc\xb5\xd9U%3\x19R\xae\xd2\xe5\xe1=\x00\xa0\ +\x95R\x15\xcdr=\x14\xa6\xeakw\xba\x98\x14\xa7\x16\ +=\x8co\x16F\xe5\xba\x84\xa9\xa9\x03\x00r-\xea\x08\ +\xb5\xae\xd8\x14\x8f\x1b\x18\xc4\xe3\x91\x96\x07\xc8\xc6\xd4\xed\ +D-\xf1\xd4\x11\xcd\xed\xf4\xe5\x99ur\x9bz9\xb9\ +\x1e\xcb:/\x01b\x1f\xd0nb\x15\xde\x22:\x9b9\ +U[\xdb\xb4\xddMee\x84\x22IL:n=\x8d\ +\xc1K\xc3\xda\xd3\x88Si\xb8\xd3t\xb9\xaa\x0e\xcd\x09\ +\x9c\xd7\x09v\xe0\xae\xdcz>(\xeb\x9a\x83,\xe2\xce\ +{\x97P\xc0\x8a\x85\x86\xd7-\xa2S*G\xb1\xf6a\ +b\xfd\xe0:\x96\xb9\xc2C\x81\xec\xaeyn\x0c\xe1\x7f\ +\xdd\xc1\xd2\xf2\x04\xbaa\xcd\xb3C\xa0\xb4S6\xe3|\ +cdj\xfd\x12H\xc5\x0b5\x01\xc2\x16\xeeCU/\ +\x1d*@Q\x97k\x06\xa1\xef\xc4\xd5$\x04\x07\xaeC\ +\x94T*}\x0e\x02m\xf1R\x99\xf6\x80\x8aKw\xf9\ +\xac\x16p\x08\x19\x8a\xb50o\x0d\xb2:h9kA\ +\xd9\x19\xe2\x1fz\x1e\x95\xbac}:\xac\xca;d\x92\ +\xcc\xa2\x11W\x89;M\x00\x08^\x93\x09\xad\xc9\x03\x02\ +\xf2\xec\xcb0\xeeK0\xef\xcf\x8a*\x91*B\x01\x00\ +\x10\x09S\xb9\x18|FUUR\x9evZ\xe9\xc0\x08\ +\xd7\xa9c\xe2\xba\xe5\x8c\xa0T5\x83\xd2\x22X\xe7\xc5\ +\x98\xea\x15\xa0*,\x88\x1f\xa0\x03\xd7\x81\x14e\xebH\ +\xdcO\x95\xc6\xe7\x00p\x9dJ\xf5w\x85b\xc2\x85k\ +\x83+\xaf\xd1\x83\x11\xe2\x91\xa3\x84\x9a\x5c\xc0\x93?L\ +X\x068\xb9\x8cI\x0e^2\xd4{\xc4g\x19\x17\x9d\ +\xc9\xac\xcd\xac\xdaf\xe6\xb6\x9a\xa9\xb2\x18\x81\xf3\xc5\xa8\ +\x98Q\xe5\xc6:}\xcb\xd7K\xac\x01Ejq\xee\xd5\ +\xfak\x80\xd4cGk\xf9S\xbc\xe2R\xdb\x89\xa9\x9d\ +\x93X\x1bD\xabw\x0e\x0b\x5cD\x09 \x10\x09\x92\xd6\ +\xb9J\x1f\x94\xee\x83\xe1\xeb$o\xf5\xf6\xc9\xd4i\xe2\ +\x02\xc0y#*\xb54Q\xeb\xd7\x1c\x5cx\x84\x1b\x0c\ +u\xf1\x80(:\xd0\xb9\xc7\xff\xeeq\x0f\xda\xe2\xa4}\ +~\xc7\xc4?\x9d%\x8c\xd2\x07\xb41B\xa2\xf7\x02\x02\ +\x22\x00\x0cy\xc7\xb1\xba#Y5p\x03\xda%#\xe0\ +lp\xbf\xf2\x22\xaa`&c\xc0\xf5\xea\xf4P\xef\x18\ +\x1a\xf1\x05\x06\x9fP\xe7\xea\x17\x90\x8b\xa5\xeb\x00^O\ +V\xa9V3\xda\x1b!\x17\xafn#\xb1\x95m\xa4\x83\ +\x00\xa1\xa2\xd1.W6\xdb\x95*ro\x1d\xe6H\x98\ +\xc9\x90\x05Cv\xa3S\xa7\x96?w\xdf\x0a\xe7\xd5Q\ +\xfc\x1e\x1eQ=\x97oXt\xf4\xd8\xc5xo?$\ +a\xe2\x9cS\xe9\x9cB\x15\xc3\xaa;\xbbS<\xca\x93\ +\xa8S\xa9vG\x07\xd5\xe6]\x95\xd2\xfc]\xa9\xb7\x0e\ +\xb5R\xbb|\xb5\x9f\xd1\x18\xaa\x8d\x9f\xba\xa8\x16\x8d_\ +\xb7\x12<\x8ez\x1b\x95N\xdf\xe3\xfd]+\x1c\xbb\x0d\ +\x18N\xf9\xdc\x0d<1c\xd3\xab\xede*\xeaQ\x99\ +\x9f\x8e\xe6el\x09\xa31*kV\xea\xe1\xaarU\ +\xd2\xcc\x8d\xb9\xa3c\xe4\x09\xda\x15\x83\xc9\xc3\x9b&\x18\ +\x10\xdch\x13\xb1\xb4\x99<\xbbu\x98\x81$\xaf\xee\x9a\ +\xe0\xb3\xd3<\x0cV>\x00\x10\x06\x96x\xaf\x86H\x9e\ +\xf5\xfbQ\xbc\x18\xff\x85\xf2\xd5\x09;\x97\x98\xa8\xf6\xf3\ +E\xe37I\x0dO\xadp\x14\x8b\x19a\xa0\x86\xb5\x15\ +\x8a\x9b\xd5\xee\x15D\xdb\xad\xb5\xe350\xa8\xbdo\xc8\ +\xc0M\x06\x0c\x00\xe0\x05\xdcY=\x88;\xbd\x03\x10\x84\ +!L9\x13g\x10\xa8\xdb\xe6\xba|\xf2\xbf:lZ\ +s\x1b^\xd9\x9a\x0aXI\xc1D\x16\xa5p\x80\xae\xfe\ +\xbb\xea\xb5\xab\x86Jj\xb1\xa2\xee\x99\xae\x83\xa6\xf6\x22\ +\x0c\x12R\x00\x87\xfa\xefk\xe1'\x1e\xb2\x1aG\x86\x10\ +[\xd3mWj\x01\x83\xea\x18\xe0\x13I\xe2\x18\xb8v\ +\x1d\x0a\xa1\x89\xb1<,\xbfp\xfe\x91\x15-<6\x1d\ +B\xe1N\x10\xd1\x112\x93\xb7\x09\x07\xb7\x00\x82<\x02\ +\xd6\x89r%j?\x17Q\x13j5\xd9\xd69\xc8\x88\ +2\x00q\x80lm\xd9\xd3\x1e\x09\x09@Pc\xe8\x08\ +zR\x8f\xa6<~\xa7\x8d$\xc5R\xfa\xccm+\x03\ +l:W@M\xa1+\xe6\x9fX\xf9\xecc+\x99z\ +\x08\x08\xee\x02\x823@\xc0w3vY\xbd\x8c\xab\x04\ +@\x04$\x8ce\x8cw\x22\xb7\x10v\xe8P\xa6\x83\xa3\ +Ta\xf2\xe9\x94*\x88\x8a\xe6\xa3\xbd\x93\xb8\xb5l\xdc\ +\xc3\xcf\xbd\xfc\xbe\x1f\x0b\x1b\xc2\xd2\xc7`\xe5\xd3\xb8\xb9\ +\x05\x08\xcf2\xb2j9\xea\xf0\xd5\xbf\x86\xfbQ\xbf@\ +\xaf/\xf0?\x9aC\x1fZ\xd1\xa4\xd7\xc2\xb4\x04\xc4\xaa\ +\x1d[\xf1\x14\x037\xa5\x03\x1en;+\x0aO\xdd\xb2\ +l\x1e.\x8bA\xcb\x1c9\xc5\x1d\xf2\xcaCd\xe2\x09\ +2\x15\x0apm\x17'ui)Z\xd3\xd1yj\xec\ +\xc8\xa0\xe71\x08\xb93\x0f]\xf1\x84\xbc\xc5\xc5\x86M\ +\x9f\xb6\x83\xf3\x5c\xae\xec\x8d\xc1\x97B\x8af\xefZ\x85\ +\xda\xddk\xb3\xc9\xe6\x0b\xe4\xb9U\xaf>\xb2b\xd2W\ +u\x0cQ\x86\x91\xd2\x02I&\xafO\x80G\xc4\x93-\ +\xc4\xc3%\x12E\x1c\x95\xc6*\xad\xed\xdfF\x89\xc7\xa6\ +C'\x0aP\xb8N\x11)\x9c{\xcc{dF\x1d\xd8\ +\x1f\xf2\x5c\xaa\xa9\xb8OHP\xab\xf7\xdb\x96\x0fX/\ +\xc4;\xd3@\xbb\x98yn\xed\x88\x02\xcc\xd9*\x97\x07\ +\x00\xda\xb7\xa9\x06\x95\xaa\xa0vg \xc0/\x0a&\xd4\ +\xea\x15K\x9f{`\xe5\x8b\x8f\xadR\xa7\x80.\xbf@\ +^Z\xc5\xd2K+_xae\xb3O\x19S\xeec\ +\xec\x1e\x06w\x8a\xe1\xe1!\xb1\xfa`7a\xa5\x1bn\ +\x81\xf8{T\xe7\x09\x004\x90\x22RZ\xfdS~\x1f\ +\xc2\x15\x09\x0c\xea\xb2\xa9-[\x9a\xc1R\x93\xc2$\xe5\ +\xeb\xaef\xae\x83\x5c\xb9\x9d\xf4\xa2\x03 t; \xf8\ +\xf1\x06\xa1\xbe\x19\x14\xbf\x84%\xafa\xd1\x9b o\x8b\ +\x14\x90\xf4\x06r\xa3nY9XU.,W\x16\xa6\ +\x83\x0c\x8ag\x00\x00\xe8U?\xfcb\x1d\xab\xa6n\xe3\ +3X\xc9\xcc\x0d\x0bOs\xc3:u\x03\xf7\xa9\xbdl\ +\xda\x0b\x1f\x1c\xc2\x03h\x7f\x9c<\x80\x00\xa0\x8c\x00\xe5\ +f\xb92\xa9\x9b\x96;\x8b\xcbe@\x0a\x96\x9eY\xc1\ +\xe23\xd79C\xcd%\xb4\x87/\x88K\xd5.\x1e\xb9\ +V\xb5\x7f-\xd0a\x17\x0b\x0fI]\x9fY\xc3\xcek\ +k>xk\xadGo\xad\xed\xf0\x8d5\xed\xbc\xb0\xaa\ +\x15\x14\x897P\xba\x9bNV\xa4\ +m\xce\x08a\x19\xae%\xaa\xe2O\xbfv\xbb\x8a=.\ +\x02\x00\xe5\x90J\xd54\xd7\xdc\x86\xb4\xb8\x19\xa9DU\ +\xd6\xd6v\x92\x86\xf4C\xf6&@\xda\x02\x8a_\x07y\ +;V8~\xe0f\x00\xdd!\xd1\xb8\xac\x5cP\xac\xa3\ +R\xddAH\x13:\x89\x5c\xfb\xe1\xce\xf7\xc4\x8d\xab'\ +\xfe1\xec\xf9\xc8r&\x0f-DV\x91\xcdk\xd68\ +n\x9e\xbfg9\xb7\x88G\xd0\x86L\xed\x93\xd3\x8e\x22\ +Gn4kIlU\xc5\x0c^#w\x1e\x0b[\xc4\ +\xe2\x16\x1fY.\xb18\x82\xeb\x0dc1!\x06P\xaf\ +Q,G'~\x94-\xe9\x00\xc5gV\xbf\xf3\xd2\x9a\ +\x0e^Y\xf3\xe1\x0bk9xf\xad\xfbO\xadq\xfb\ +\xa1U\xad\xdeu\xcd\xaa\xc2X\xb6\xe6ER\xb5\xe1S\ +\x9ba\xce\xa7lu4\xad\xb8F\x18\xe5\xe8\x940\x1d\ +GS\xbe\xf4\x1cy\xe9\x9a@\xe8$\xb2\x02\x94^0\ +\xf3\x1c\xe5\xbf\x04\x1c:\x9bY\x0az\xcb\xdf_[\xc9\ +\xfcs\x00\x80\x85\x12\xab\xf3\x08\x83\xaa\xebsG\xd7\xab\ +\xb7\x80\x13\x85d\xb5\xcb\xd1\xf68\x8cB\xde\x11\xefR\ +\x04\xc0t\xc6\x91\x0e\xbf\xaa\xdax\xe7\xce\xfe\xad\xba\xaa\ +C?\xdf[\xf1\xc2\x1b\xae\xf7\x02O\xf3\x94q\xe6\xf9\ +\x87\xefat<\x03\x00 \xce\x03\x94l\ +\xe2\xa4\xe2\xa3\xeeS\xe9\xa0#\x8bn\x03\xa4\xaab \ +T\x9aq\xd4v2\xbcK\xee\xac\xca\xa7p\xab\xa4c\ +9j\x99\x86\xa7\xc9\x9d#\xcc\x00\x8e\xb2\xd5'N\xf9\ +\x9a\xb4\xaa\xddzl\xb5\x9b\xf7\xb0\xa4\x9bX\xfd\xb1U\ +.\x1d\x10C\xb5\x01\x05F=\xb9\x8f1\xa8/\x8fJ\ +\xe0\xe1\x10-\x9a\x90R*\x07\x9bW\xd5\x93R>\x9e\ +A\x8d\xa7u\x12h\xe9\x82w\x80\x94\xe7\x01\x1e\xe3\xcd\ +$z\xff\x82\xdf\xbd\xb6\xd2\xa5\xb7\x00\x0f\x00,\x0a\x10\ +x\x86\xc9{\xae\xd1\x96\x0e\xa5V1M@E\xb6\xee\ +\x15\xe6\xaf}\x87\xd2\x09\xc0\xd0\xa4P\xe4\xbc\x99C\x01\ +\xf1\xbd\x18\xa0\x95\xb9\x13\xd0\xdeX\x85N>[z\x83\ +W\xd1\xd6\xf8\xc7\x84\xb6\xfbx\xad;\x90MB(\xb1\ +>K\xe5\xe3\x9d<\x83\xf6sh\x17\x97\xdbQ\x04\x98\ +I\x11\xd3\xbat\x00\xe7\x01\x00\xe0B\xae\xef\x8c.\x86\ +\x1b\x0a\x83H\x81@\xbc@\xb5\xe6\xda\xc5\x9b\xdc0\xed\ +\xe6\x98]\x0b3W\x9fwQ\x97\xd7m\xe9\x90\xa3@\ +\xd3\xb0\x85;\xa7\x01\xc0\xb2\x15\xa1\xb8\xb2\xe9k\x0c\xe4\ +\x1db\x94\xac\xed\x89\x95-?u'V\x97\xea\xe0\x02\ +\x91#,Q\x1eA-\xd4t\xea\xb8;\x1f\xd7\xa5g\ +d\x0e\xca \xcek\xd8u\xe4yP=\x0a`\xbaY\ +c\x0c\xc8(\xf7\x0a!\xd2\xe1\xd1\xda\xf6\xac\xc9!\xb7\ +5J\x0bX\xfa.u\xdav+\x95\x90,\xa5`p\ +\x8d(\xae2\x0c\x85R5\xa1\xd0\xb9Cj\x1b\ +\x97\x87\xcbw\xad\xe3\xe0Z\xae=\x8dKU\xb5\xd3G\ +\x82\x95\xe2\xcd\x94\xa3\x0b\x04\x1e\x000Du\xf4\xc0[\ +\xe4@&s\x09\x1b*\x07\xd7!\x97y\xaa\x10V%\ +\x962\x22\xf5\x01\xd0V1\xcd\xa1\xb89\x95yK\xad\ +\x83\xabhR\xadn\x06\x99&\x8c\xc3a4M, \ +\xb4j\xe5\x11\x92\x0f\x08b\x5c'\x09\x11A\xf2\xea\x10\ +d\xcbm\xda\x14\x13%\xae\x8a\x08j\xe3\x87z\xf5i\ ++\xb8k\xe6\xd0A\xbe\xdf\xb9\xe2\xce\x08\x08\x93\x02E\ +\xba\x16\x9d\x84\x11\x9d|\x15\xee[\xc7\xb5o\xc3\xb2\x19\ +d\xc8\x5c\xc1j>Hlje\xa3%\x95TY|A\xa9\xc5\ +\xe7\x17[B\x01R\x5cn\x89e\xd5\x96\xc8\xdf\x12\xab\ +[-Q\xdd=I\x01\x93j\xb5OP\x99\x10\xa2\xf7\ +\xbc\xa64\xa8\xc8d\x84\xfb?\xdf2\xa6\xc5.\xadI\ +tk\xc6\x14\x0f\x86g\xd2Q.ZA\x0dq\x7fj\ +4\xa1\xd2y-d)\xbcj\xc3\xad\xb7\x90\xc4\x18k\ +\xcb\x9dVf\x05\x02\xc2\x81\x1aQ\xa8\xa5K\x04\x008\ +\xe5\xe3\x01r\x05\x22\xf4\xa3\x83\x8f\xa2\x0b\x067-\ +_\xfdwQ|\x18ku\xcd\x91y\xaf\xa9\xe0\xf0\xf0\ +&\x80\xda\xc6\x02\xf8\xec\xcc\x11D\xe8&\xd6\x7f\x9b\x94\ +\xe5\x1e\xf1\x96\xb89{\x8a%\x92\xe3\xe2Ju\xbe\xbe\ +\xeb\xcb\x8b\x87\xd1Z\xbf@\x90\x5c\xafU;\xcd?\x8c\ +a\x89X_\xdb\x14\xe4e\xc1\xedr\xf5i/\xe3\xa0\ +v\x13\xc3\x96I\x91\xdc\xe1N\xb8\xd5\x90\xd8\xbe\xa6\x87\ +\xdd\x926\xf7\xcdgu \xa3\xdfmPQ\x0b\xf7q\ +\xcb\x90\xd2\xaa\xda-Y;\x84\x0a\xab,!\xbf\xdc\xe2\ +sK-.Z\x8c\x14Z\x5c.\x92Wdq\x05%\ +\x16W\x5c\x01\xe7Q\xa1i\xbd%\x947\x10\xfe\x1a\xbd\ +W~\x8e\xd7\x1e\x832\xfd\xdc\x02X:\xe0C\xbdn\ +\xc7\xaf\xb74-O\xe1M\xf5\xfa\xdaum\xad\x05\xac\ +\x98\x16\x82\xdc\x01\xd0\x8d*\x00\xd1\xd4\xf7$\xafX\xa8\ +\x96\xbdQL\x1a\x8a\xd1\xbeKu\xfa\x129\xd7\xbc\x88\ +vj\xe9@/w\xa8\x17\xfcI\xe72)t\xaac\ +{J\xed\x90%\xb9\x92y-\xa6IT$\xd3\xebB\ +u|\xa5*\x97\x09\xdb*\xa7\x03\x10\xdae\xa4\xc5 \ +\xaf\x09\xa5\x1a_\xceXLB\xc5\xb0[7N\xe5\x17\ +>\xd0\xa4\xae\x96!\x14\xad\x16\xe9\x9ayr\x07@!\ +\xda\xdd\xab\x83\x08\x0b\xc6\xbc\x83\x9dJ'O\xact\x02\ +W?\xbeg\x85XWTs\xff\xe4\xfdA7\xd0\xe7\ +3\x81\x9d3\xc4\xb7\x19B\xc9,\x80X\x02\xb9W\x89\ +Y\xbb\xa4~'V\x86g(\x83i\x17\xb9UC\xb2\ +\x0fMBqm-\x93\xaa\x10\xc4k\xcc\xac\x92p-\ +\x0fk\xbd\xbb\x97\x9b\x1e\x00\xbd\xdck\xdb,\xaer\x85\ +\xd0\xb0\x09)\xc4\xdd\xaa\xe5\x9c\x96R\xc92\x94;\x8b\ +\xbf\x04\x14[Ic\xbdC\xa5q\x89\x0cV\xaaV\xce\ +\xb4\xd1\x93L&\xa9X\xca\xaf\xb1$IA\x8d%\xe6\ +W\x03\x84J\x80Pf\xb19Ev%\xa7\xd0\xae\x00\ +\x86+\xb9\xbc\xcf+\xb1X{\x98\xd16\x8a\x82\xa7\ +\xf1 \xcb\x90\xaem\xc8\x17\x00\x02\x00e\x0b\xa4Q\xa4\ +o:\xc2E\x93\x1d\x02\x9b_[\xce\xe5\x165Pd\ +\x1f\x89B3)\xa86bh>B\xed\x5c\xd4\xcc)\ +\x83A\xd0n\xd7 \x9e(8\x827\x80Sh>=\ +\x80'Sv\xa0r\xb74H\x90\x0aJRA{*\ +\x03\x9e\xc2\xa0$\x97\x01\x80\x92FK.i\xb0\x94\xd2\ +&\xa4\x05i\x06\x14XsA\xad\xc5\xe7U\xe2\x05P\ +4^!\x16\xafp%\x0a\x08\x04\x86h\xc1'\x89u\ +\xaf\x85\xee\xef\xb1\xb9\xe5x\x8cZK(l\xc1\xab\xf4\ +\xe0]\x08\x09\x18\x94\xbf\x11\x0e\xc3sd4\xe2\xb5\xea\ +\x15\x93\xb1tU\xe5hM^\x16\xe9J\xc3\xb4\x0dL\ +[\xc1\xcf\x97\xc4U\xc3W\xa6\x9e\xc2C\xeeh95\ +\xad\x8e`\x88\x1a\xff\x0a\xc6\xa9z\xe9\xaeU\xcci\x16\ +\x900;L(\xeb\xd6fU\xc6\xb7E}\x8d\xf0\x96\ +\xcd\x00\x88\xb1O\x82K\xa9c\xba\xf6\x18\xa8\xf8\xf5\x0a\ +\xdf\x7f\xa9\xb4\xd3.\x93\xc5].iCZ\xed\x0a\x02\ +\x00T\xb9\xea\xed\x91SM]:V\xa2\x93?t~\ +]\x1e\xa9\x95:h\x97J\xe9\xb3w\xacR\x87K\xe9\ +\x00\xa2\x19\x08\xde\xd45w\x14\x89\x94\x9f\xab=\xfc}\ +\x9a\xf5\xf3\x8a=\xdc\x9a\x7f\x83J\xadT\xac\xd0\x8b\xd2\ +\xbcx\x99\x0e\x18\x02\xedS\xee\xb3\xf9c{\x84\x80\x9b\ +\x84\x83\x07V\xbd\xf6\x14rH\x96@\x9e+\x82\xa3\xb3\ +\xfeu0\xb5\x80\x90\xa1\xa6\x14\xae\xc7\xae\xca\xb5T\xb1\ +\xd3\xef\x81@\xe1AS\xb1\xf24*\x97\xee\xc7\xdd\x13\ +\xf3\xfd\xc4~\xd7-K+\x88\xdaI\xdbA\xcc%\xef\ +\xcd\x04\x0c\x01\xbe+\x83\xefJ\xab\x06\xecd0i\xd5\ +d15\xdcWM\x1f\xef\xb9G5\x96\xd6F\xcd\xa2\ +\x06Wf\x1e\x87W\x88\x05\x0c\xb1\x02C\x14/\x80g\ +\x88uJ\xf7\x00\x10\x0b0\xe2\xf8}\x5cn\x05\xde\xa3\ +\xce\x92\x8a\xda-\xad\x82g\x84|e\xb7@\x0c!Z\ +\xd90\xee\xcc\x16\xc2\x1b \xf8\xdf\x01\xa0\xfa\xc0\x81s\ ++\xedv\x15\xc5\xaa\xdeM(iw\xc5\x9c\x89\x00!\ +\xa5z\xc8|\x8d\xd3\x10\xd55\xbc\xec\x915\x10:\xbb\ +w\x9eX\xcf\xeeC\xeb\xdc>\xb3\xe6u\xa5\xdb\xea\x18\ +\xbeLX\x80\x97\x0dh\xe7\xb5j\x12\x08#dV\xc9\ +\xcdZ\xfe\x1d\x03\x04C\xe7 \xe8B:\xcc\xdb\x19\xd4\ +\xea\xd2\xf9\x18\xb7\xf9\x92|>^\xdb\xa4\x19\x10\x15K\ +\x04\x19\xd4\x1c\xe2\xba\x8eg\xd3IS\x95\xa4PU0\ +\xfaJ\xac\xb5\x1c\xab\xd5!D:[ \x0fr%\xcb\ +\x0f\xf7.[V\x17\x04\x88L\xc0\xafE#\xe2\x9f\x03\ +@\xadJ\xbb@\xb3\xb6Y\x03\x82\x94\xba>R\x93Q\ +\xd39?y#[^{\xfa\xf5'\xd6\xb0\xf3\xc6\x1a\ +w?X\xdd\xd6\xe7\x10\xc5\xb7p\x85\xe7\xb8\xba\x07\xb0\ +j\x80\xa0\xe2\x0f\x91#\xb9PGt\x86\x01\xc2 !\ +\xc1+\xb4LV}\x01\xb16\xad\x03\x97H\x5c\xd4!\ +\xd8j\x8f\x96\x05;\x0e\x0f\xdc\xc4Jn\x03\xd2;V\ +0|\xcb\xf2\x06\x8f,\xa7\x17\xa5t\x12\xea \x97A\ +\xee5\x13\xf2\xa6\x8c&C\xc5\x15\xd5\x84\x1b)B;\ +u\x8b\x18\x97\xc2&\x80\x00\x18\xf2\xb5\xf7\x007\x9fW\ +\x81\x94#\x00\x02\xf7\xaf\xd78~\x8e\x07(\x89|6\ +\x05\xeb\xf7\xd5L:\xe5G\xbb\xf7-\x8f{\x88\xf6\x1e\ +Y\x88T5\xa0\xba\x86\x06u\x0f\x13+?\xaft\x86\ +\xf0z\x1bM\x07x&B\x1c\x9e \x09KM*\xeb\ +r\xaf)\x80=\xbd~\x1c\xf0.\xc0\xb3v\xdc\x86\x9a\ +\xe1\x83\xe76v\xf2\xdcF\xaf=\xb1\xbe\x83{V\xb7\ +\xa2\xb5\x8c=\xb2\x02B\xf4\xd8!\x19\x06\xe4PF\x00\ +g\x13qW\xf6\xa6\x8d\xad:\x87\xc0\xab\x19$\x9c\x02\ +~\x85\x0b5\xc1\x88QW,m\xceL\xac\xed\x86\x88\ +\xf4\xe3\xaa\xc7,\x9b\xfcWn\xbaD\xf9\xfc\xc2m\xab\ +\x82\xb0U.\xea\x0c\xbaS\x5c\xb7N\x12\xd5qk\xf0\ +\x04\xed\xf7W\xf5M;\xfcA\xe4\xaaY5\xec\xaa\xb4\ +Q\x95\x0d,T\xf5~\xb5<\x9c\x06V\x16\x867\xc8\ +\xc0\x0bduN\x03\x80\xabV\xbep\xc3\x1a\xb6\x9eZ\ +\xdb\xd1\x07\xeb\xbc\xfe\xd1:\xae\xfd`-G\xdf\x03\x88\ +o\xacj\xeds\xd7f\xbe`\x82J\xd6\xb0\x88\x87\xd9\xb5\ +\xda\xb5\xdb\xd6\xb2\xff\xc2\xba\xae\x7f\xb0\xbe\xb3\x8f\xd6\x0f\ +\x08\xfa\xef\xfc\xc1\xfan\xfff=\xb7~\xb3\xae\x93?\ +Z\xfb\xfeO\xd6\xbc\xf1\xad\xd5-\xbf\xb7\xca\xd9\x17\x90\ +\xcf\xc7\x84\xa7\x07V4\x86\xf0Z2\xf1\xc8\x01\xa4b\ +^\xd6\xfe\xb9\xd5\x01\x9a\xc6\x9d\xef\xacy\xffGk=\ +\xfc\x09\xf9\xc1\x9a\xf6\x00\xc0\xc6[w\x08s\xa1\xe6\x1f\ +H=\xb3T>&\xbe\x22O\x85B\xbd\x9d3(\xdf\ +\xf1\x0c\xd5\xdb\xc1\x17\xc8\x1etB\x98J\xadt\xd6\x8e\ +6\x8d*\xadS1\xaa\x8f1\xc8P\xaa\xa7\xcf\x10\xab\ +\xfdp\x8c`\xeb*\x00%+B\xe1\xda\x1f\x99\xeb\x1a\ +>\xde \x8b\x22\xa7w\xfd\x15\x8e\xed\xa2\xd9\xa3\xa6\x94\ +?e*X\xaa\xcb\x14T.\xcew\xb9\xfd\xfb\x18W\ +\xb2t\x03\xe1\xd5\x81\x9b\x85p\xad\xda\xa9\x03\xabW\xaf\ +\xa4\xd9\x13\xab\xc5C\x17\xaa\xe1v7Y\x01\x86\xa0z\ +\x0au!Ok^\xe7;VP8\xd6\xaf\xcc\x03/\ +)q\xe1\xe7\x5cRI=\x95\xa1\xc4\xa4\xaa\x22F-\ +\xe2:\x17]\xd7\xa8\x001=kx\xcb\xc2\xa3\xbb\x16\ +\x81\xe1\xabS\x97v\x05\x05\x07A.nH\x07/\xa7\ +\x92\xdb\xba#Z@S\x22\xb1\xcc\xabF%>\xcb\xcd\ +\x90\xc7'\xe2\xd6<\xe1\xbd\x03\x81j\xfd4\xa8\xbd\xa0\ +\xbb\x97A\x1f\xe4\xa6\xf1\x02#+\x10\xc1\x03,\xf5\x0c\ ++}l\xedGo\xac\xfb\xc6\x97\xd6{\xeb;\xeb?\ +\xfb\xd9\x06\xce\xfeh\xfd\xa7\xbfY\xdf\x8d?Z\xf7\xf1\ +/\xd6\xb1\xf7\x835c\xd1\x0dWQ\xa6\x9a?\x22\x8d\ +W\xbf\xb2\xf6\x9do\xad\xfb\xe0{\xeb=\x06<|\xb6\ +\xff\xd6\x9f\xac\xeb\xe6o\xd6q\xfdWk;A\xf9\xfb\ +j\x12\xf9\xc6*Vt\x82\xc9\xa9\xe9\xa8\xfa`\xd72\ +!\x0b\xf0_X\xbeS\xbe\xe2#\xf7\x09hS\x9a\x08\ +\x0b\xad\xa4b<\xafkx\xa1B\x0e\xa5\xc5*\xcc \ +\xdb\x08A\x90\xb3U\xef7\xa8\xe2\x8e\xf3\xbe\x08Xv\ +\xb0\x93\xd4\x14\x00\x84\xe0!\x11\x94\x9d3\x08\xd8T\x98\ +1z\xc7r\x08K\x91\xb1;\x16\xc6+\xa8\x8eA \ +Pk\xf7tM\xcfb\xa9\xea\xc4\xe6\xa4\x19\x01tI\ +\x00,\x19IQA\x0e\xa9of\xe7\xbcE\xdc\xe4\xdb\ +\xb2\x855\x19\xd7\xb9j\x01\x00\x97\x8e\xb2\xd3\x90\xd4\x06\ +\x94^\xb7\x88\xc1\xcd\x91\xefk\xfa~\xd2\xcb\xfb\xc5\x9d\ +\x5c\xea\xa9\x13O\x14:\xbd\x99A\x81-&\xa5m\x89\ +\x0bx\xedB\x5c-`\xf7\x1aV\xae\xa5N\xed\x02\xe2\ +\xe1\x10m\x11\xd3yv\x17\xe7\xe1k\x8d<\xd9\xcd`\ +q\x83nzQ\x02\xc1Qy\xb4\xfeF\x8cs\xafz\ +\x10\x80\xa2R+o\x070\xee\xb3\x0e\x06\xde\xd8\x8fE\ +\x0d\x935La\x1d\xcbX\xe46\xe9\xe0\x09@\xb8\x03\ +\x10\xe0\x04\x87o\xad\xfb\xdaW\xd6w\xeb{\x1b8\x05\ +\x08\xa7\xbf\xa2\xd4_\xad\xe7\xfa/x\x84\x9f\xad\x030\ +\xb4\x0b\x10'x\x8a\x9b\x7f\xb2\xa9\xfb\x7f\xb3\x85'\xff\ +l\x8b\xcf\xfe\xd5\x16\x9f\xff\x9bM?\xfd;\xe1\xe47\ +k\xbd\xf6\x9d\xd5\xef\xe15 \x9aE\xf3g\x96\x0bw\ +\xc9&\xbb\xd1Ii\x8a\xad\xae\x22Y\xbbq%\x9a<\ +\x11\xb1\xe4\xf7\xda\x94\xa9\xbe\x89j\x9f\xa7\x1a\xbc \xde\ +Bg\x1ahuPk\x0c\xd1\xa9\x9bn\x8d!\xaa\xb6\ +\xf7S\xeao\x8cu\xab\xa2\xc7\xb9\xf9}g\x8dY\xbc\ +\x86 \x81r\xcb:\x926wR\xd3\xb8O\xdc\x8e\xea\ +\xc8\xf8]>\x7f\x1d\xb2\xba\x8b']uqZ3t\ +*\xd3v\xfb\x0b\xb4\xf9T\x00he\xfcZ\xb1`\xb5\ +\xb1\xc5S\x09\x94\xee\xe0,\x8c\xccY0\xd6\x9dJz\ +\x99R\xc7\xe7Pz2\xdc(I\x8a\xaf\x82\xf4\x91\x8a\ +\xbacgH'\xf5\x9a\xa0\x0d&\xae*X\x15\xc1\x18\ +'\xd7\xd2Q;\xaeGPJ\x9b\x0a\x05\xbd\xe9^)\ +Xu\xed\xae\xb8R\xacZ3N(^u\x82^w\ +\xd0\x7f\x88\x88W\x1a\x0f\xe0\x1aE\xa9v\xa0\x83\xfc\xfd\ +BT\x89\xa3\xbf\xe9\xb3\x8e\xf0\x88\xacM\xe0\x9a\xb45\ +\x8b\x1c\xb7i\x10\xf79B\x9a6I\xca\x07\xb2\xe1\x1d\ +\xf9\x13\xa4\x86:?x\xed\x1e1\xfb\x99\xb5\x1d\xbc\xb1\ +\xce\xe3\x0f\xd6}\xf2\x15\x80\xf8\xda:\xaf}c\xed\xd7\ +>Z\xfb\x8d\x1f\xad\xe3\xd6/\xd6M\x98\x18~\xf0W\ +[|\xf5\xaf\xb6\xfe\xee?\xdb\xda\xdb\x7f\xb7\xe5\xd7\xff\ +bSO\xfel\x1d7\xbe\xb5\x9a\x9dWVJ\xea\x94\ +\x07w\xc9\x06\xd0\xfe\x0eU\xc82\x08Xy\x1c9q\ +,\xe9\x96\xfa\xf1hBD{\x03\xe5\xbdR\xb4\x8b\x07\ +\x17\xaf2/uP\xd3\xd15\x1e\x00\xf0\x86\x22\x8f\x93\ +\x84\xc4\x19\xb2\x0b\xb2\xa1\x5c\x1dqCZ\x1c\x99\xba\x0d\ +\x08\xce\xe0N\xb7\xc8>\xae[\xb0\x070\xf4\x1c\xb9x\ +,B\xaac\xdb\xd5\xb5m2^\xe9g\x17c\xdd\x03\ +\xdc{\x00@\x17\xe3\x8fB\x9bb\x83v\x8d|\x02\x00\ +\x17S\xbe\xaa\x1e\xb9\xaa\x0f\xcc`0\xd4G\xc8u\x02\ +\xedSQ\xc4\x0enOK\xaf\xca\x10\xd4N]\xc0\xc0\ ++h\xb5K\xcaw^\x83\xc1\x13\xdb\x96\xf2\xbb\x17x\ +\xd5\xba\xff4\xa9\x0d\xa9\xa3\xbaV5\xc0\x0b\xeaQ\x84\ +\x8a,\x95B*%#3\xd1*\xa0\x8aJ3;f\ +\x89\xa9\xb0k\xfe7S\xc5\xa6\xfd\xa4n\xc3X\xe3\xf8\ +\xb6\x85\xa7\xf7\xac`\xf1\xd0\xaa\xd6\xafY\xdd\xe6u\xab\ +Y?\x86\xe5\xef[\xc1\xb4\xb6\xb5\xcb\x9a\xb1\x8c&\xa5\ +\xa2\xc4\xf6j\xe2=\xd6\xefu\xe1\x82\xf0a\xf9\xca\x8b\ +\x05rw@\x04\x0aw\xc0\x05\xe0\xda\x8e\xe5\x8aI\x09\ +\x8d\xb2Pg\x08\x840-\xee\xb8Jg\x81\x14@\x07\ +\xe1\x07\xd9:\x17\x99\xdc[\x07P\x14\x12\xdfK\x94\xa5\ +\xa8\x00D\x95?\x0b:\x1f\xe9\x85\x15\xce=s\x05\x22\ +\x02IX-\xe6U\xf0\x01\x00\xdc\xa9 (\xdc)\x05\ +\xde\xa0n\x9f*\xd4Hi\xdf@'KX?\x06\x89\ +\xeb\x96E\x0b\x00J\xdb\xdc\xa1\x8f.\x04\x13\xb6\xe55\ +\x94\xde\xc9C\xe0\xee\x93D`\x11\xb5\xd4Q\xdf'\x1d\ +w'\xb2\x97\xaa\xddUR\xbe\xeb1\xa8]U\x07x\ +l\xd5J\xee[\x8c\xeb\x80\x85\xeb\xf7\x94\xafmCr\ +7r\x19X\x06\xee\xc6-\xa2\xc8B $j)\xef\ +C\x99Z\xea\x0cB\x14\xb3q\x89\xe1!-\xb3\xc2z\ +y\xd56(u\x08\xd5\xf1\xe8\xae[6\xdeCLZ\ +e\xe5\xea\xc9\xab\x1e\x81\xb2~\x9d\xdf\x97\x06\x17\xd0Y\ +~\xaeEku\x17\x0f\xaa\x06M\xb0\xf0Zn^=\ +t\x9dh\xb2BG\xa4\xea\xd8\xb7\x01\xdc\x9dz\xf9\x03\ +\x12\xbcEr\x1b\xf7\xd6\xc9\xf7\xf5N[\xe6\xc0\x0c^\ +\x88\x10\xd2?\xc5\x83\xa9t\x9d\xfb\xc7\xb3$\xaa\x13\x99\ +k\xda\xc8\xf7j\xd7\xef'\xa6\xaf)j\xad2\xea\xbe\ +T\xc6\xae\x9dP\xdc\xb7\xac\x1f\xc5\x8b\x95{\x87>\xa8\ +N\x81\x01\xe7s\xe9J\xf5 \x87\xfefR2\xb5\x8b\ +%{\xd2\xb2x6\x80\xd7A\x91\xf9\xe3\xb7\xcd; \ +\xf3\xa5\x95/\xbf\xb5\xf2\x95wV\xb6\xf2\x16\x0e\xf2\xda\ +\x8a\x00A\xc1\x9c\x08\xe0}\xf8\x802\x80\x13\xc0s\xc0\ +8j^@\xa2\x94\xd0\xab\xdd\xd7^\x08\xb5\x81Mj\ +\x5c\xf4t\x22\x97\x8e25Q$\xf2\x97\x06/\x10\xf7\ +J\x15y\xe4\x1e]6\x86\xbe\xdc\x16t\x94\xef:\x97\ +\xf1|\xee\xffd\xc8\x18\xb6\xdc\xbeZ\x00\xf8;\xb6I\ +\x155=\xae\x1215\x98\xde\xb0\x18\xa5\x09Z&\xd4\ +\xd6\xe6x\x5c\x88\xa6\x0c5\xc5\xaaT\xc8\xc5\x1c5+\ +r\x1b+\x18\x04\x18\xa9\x5c\xb4v\xb9\x04q\x85\xda\x1d\ +\xa4I!m|\x10\xc9\xca\xd6\xb61\x11'H\xa3\xda\ +\xc4kyW[\x97t\xf8B\x9av\xe5J\x9aG\xdd\ +t\xb3\x94\x9a\xa4\xd5+\xd5\x18~\x9a\x8e\xee\xc02\xbb\ +\x11Y(\xaf\xea\xdf\xc7\xdf]3\xc72\xbd\x22\xe7\xfb\ +\xe0\xdd\xc9\xa0j1W\xc7w\xd4w\x02bO\x12\xd4\ +\xd0AM'\xf9\x9b>\x13\xcf5\xbc\xef\x04`\x88V\ +\x175g\xe1:\x81\xe9\x189\xac_\x80\xfd\x04Z,\ +^g\xf4\xa7\xe36\xd3\xc9\x93\xd3\xf1\x84\xe9\xa4\xb4\xda\ +\xd1\xe4\x94\x0f#\xcf\x84(\xea\xf8{\x9d\x0f\xac\xd3\xd5\ +sGN\xacH\x85\xa6*\xcb^yi\x95:\x80z\ +\xfd\x9d\x13\x95n\x95\xae\xc8\x13\xe0\x05\xa6\x1f\xc0\x13N\ +\xdd\x92\xb5\xdb\xd6&\x00\xb85\x0b<\x806\xc2\xb4\x12\ +\x06\x9aW\xf1\x84R>\xee_\x06y\x0e\x00q\x00\xdd\ +\xb3\xbc\x91k\xd3\x0f!O\xe15\x99\x9f\x93\xc8\x0c4\ +\xe9\x96HJ\x9b\xe0\x84\xf7\x18\xb0\xba\xbd\xab\xd6@\x8d\ +\xbd\xb5\xe4\x9f\xd1J\x18\xd7>K\x1dx\xd9@\xf8 \ +\x83\x88\x89\xab\x80\x11K\xb4*\x05\x82\xb4@\xa1\xedQ\ +\x1e\x08.\x88\x87@ \xf4\xc9z\xa7\x19\xa8Yn\x5c\ +\xb1z\x09\xd6\xbb\x86\xe5\x0b\x04[\x16\xea\xc7\x0b\xa8\xe6\ +^!\x02\xf7\xefN\xf2f\xd0\xd4^US\xc4\xb2z\ +7[\xa85\x02\x94\xac\xae\x1eq%\xcd\x16[\xdc\x08\ +1k\x22.\xab\xdd\x09\xe4\xacL[\xb2\xb4CW\xfb\ +\xf4\x1b\xbd\x8e\x9aH\x82V\xeb\xf8\xbc\xa6e\xe3\xf4w\ +\x00\xa1\xedY\x97\xb5s\xa7\xbc\xc9\xc9\x95r\xbeO]\ +\xc0\xb4\xc1\xb3R\x80R\xb3\x06\xafa\x83\x00\x90\xa4Y\ +\ +\xb9\x7f\xbeX\xb5{:\xf3G\xe7\xd4k\xb7\xabw\xfa\ +$i\xd1\xb0\xb7]\xdc\x89\xab\xf4\xdd\x01\x04\x1b\x0c\x12\ +\x5cA\x9fo\x9b\x03\x08\x0c`# \xd02.\x00H\ +V\xd3\x22Y2\x00P\xef\xdc+\x05\xea\xa6U\x07\x10\ +\xb4\x1b\xb7\xfe\x5c\xf9\xb5\xe7\x00\xb8P\xbe\x07\x80+\x80\ +\xe32\x00\xb8T\xdc\x8a\xd2\xdb\xce\xa5\xf5\xd3\x1aw\xac\ +z\xf5\xaa\x0d\xec'9\xef!\x04\xe84\x8f\x7f\xd1~\ +FG\xc3\xa6K\x18\xcct\x065\x1d\xc5g\x90+g\ +\x10\x163PH\xba\x04\x10\xb8\xbf\xf1\x99\x0c,Mg\ +\xee\xeb8y\xed\xf9\xcfF\x99\xda\x09\x1c\x1d\xbfa:\ +\xad\xdbm\x03\xd3!\x17d\x06\xd1\x09\xc9\x91;\xeb \ +G\xbd\x95D\x92\xe1H\xfa?\x85\x1b\xed]\x14\xc7\x10\ +\x89S+Z\x85_\xb5\x8e\x91b$\xcaP.\x00\xa0\ +\xc6\x0f\x0a\x03^\x9bZ\xbc\x81\x13\xbc\xb3\x08\xbb\xe6\x03\ +x\x06\x85\x02\xcd\x1e&\xab\x8c_\x93u\x22\xb0J3\ +?q\x05\xfe\xce3+\xe5M\xd0\xca\xa0\xf6>2\x16\ +1\xf1M \x0f\xcb\x8c\xaf\x1f&\xee\xfc.\xf6\xa3|\ +m\x0b\x13\xf1s\x9d\xc3 <\xae\xe9A7)\x906\ +B\xf20a2\x80\x9cQ=$\x83\xc0\x83G\x95\x12\ +\xb9\x87\xc5\x1b\xf4\xc3\x09\xc4\x92U&\xa6\xddA\x9dX\ +\x8d\xbc\x86\x80 O\x00\xd8\x92d\x99\xb8~\x0f\x04u\ +\xe7\x00\xf0\xe4\x12\xca\xbe\x8c\xd2/\xe7\xab\xe7\xae\xac^\ +\x9f!T\xe0\x1d.\x94\xffYI\xbb}V\xaa>\x7f\ +\x9d\xae\xe7\x9f:_\xc6^t\xe9\x02\x14\xde\x0a\xde\xb9\ +\xf0\xb3W\xad\xa3>DjFE\x18`\xe0\x9d\xa0\xe8\ +4\xd8\xb6\x13\x5c\xaf\x13\xde\xa7\xe2&S\x01C*\x03\ +\x9dJH\x10`Dl\xb5mNg-eiZ\x18\ +\x00\xb8\xdd;\xc3\x9a\xf5;\xc4\xc2\xb5X\x06)\xd6\xe4\ +\x91z#\xa8.QS\xc5\x9aO\xd0\xc4\x1a\x99\x94\xc6\ +Ss,\x8a\xe7\xca\xb6t\x91\x14P\xd5O^\xda\xa4\ +\xc1\x04\xf4\xbc&Wz\x92REv\x02\x00RP~\ +\x0a\x03,Qc*\x15r\xa8TMi\xb1fF\xd5\ +\xb4!K\x0a\x1f\xf5\xb6o\x85\xb5\xb1\xa6o\x1f\xc0\xef\ +`$(\x9c\x8cB%n:B\xc7\x1d\xdf\x8eBd\ +\xf5N\x5cvD\x86\xe1@ \xc5\xe8Tt\x8c\x10\xe3\ +\x8b\x93`\xe9\xf1b\xf6\x80\xd3\xb5tq\xa9 @\xf8\ +\xa4|\xadJ\x02\x00\xdd\x97&\x81\x00\x808D\x8a\x8a\ +N\x00\x81\x97\xbaJ\x88\xf9\x9aY\x04\xb8:e=\xb1\ +^M!&P>\xafx\xf6\x04\xf8YL\x22\x16\xae\ +9g\x89;\x18\x8a\x9bR\x1f\x1b1b\xb9*-r\ +H\xf1nkr?\xb1]\xee]\x9d+\xc9\xfde\xed\ +\xd1\xf1C\xa7x\x1d\xc7\x1e\x1e\xe5oC\x9b|F[\ +\xc4E\x0eQ\xf8\xe0U\xcb'M,\xe4oE#\xbc\ +\x0eoZ~\xff\x8aE\xbaf-K={\xea\xff\x01\ +\x02\xf1\x01)\xfd3b\xfeg\xe7\x00\xb8\x5c\x80\xe5K\ +\xf9X\xb1\xd7\xe0P-\xd5\xa4\xf4.\x94\xdf\x83\xf4\xda\ +%\xd5\xbb92\xab\xd8I*\xa4\x936\xca\x08m\xfc\ +\xdd\xe5\xfdj.\xa9\x89\x1f\x95[\xa9\x0b\x89\xa6~\xf9\ +\x9c\xbah]\x9c\xe2\x9d\xc8\xff\xa8\x05]R\x85\x06\x19\ +\x17\xcb\xa0{5z\xe7B\xecu$Li\xad\xa6\x89\ +\xb1n\xb7\x19EE\xa9\xc3\xc8\xc01\x1e\xef\x00\xe5\xc3\ +\xec;\xb6\xe0H\xe4\xf7\xa4Z\xee\x087\xf7\xba\xe1D\ +\xe7*\xb9\xc3\xb34\xa7\x02Q\x96\x91\xc9\xd8\xd4\xb0\xdb\ +\xedADI\xf1R\x94\xe2\xb5\xc2\x03\xfc\xc0u\xf6r\ +`\xe0\x9ep\xff\xc9\x10\xf4d\xcd^\xf2;\x15\x97\xb8\ +\x0ej\x02\x81\xd6\x06H\xe9U\x83\xa8\xdfy'\x9f\xab\ +\xef\x82\xdc\xbe\xc2\xc79\xa8\xf8\xfeD\xae\x97\x00\xf8b\ +R\x94N\xb8x\xa1\x19/M\xf5\xe2\xa6:u\xd0\x13\ +\xae^q\x0e\xe5\x8b\xdd\xab#\xc8\xc5\x89\x1f\x91\x91=\ +\x8b\x8e\x1dX\x1e\xca\xcfG\xf9y\x13\x00at\x97\xbf\ +\x83|\x95$\xf5\x00\x9enx\x02\xee_^@\x07'\ +\x16\x8d\xefY\xe9\xc4\xbek\x10\xa53\x04s\xfb\x97,\ +\xd49c\x01\xd2\xc2Truu\x09\x8b\x13\xa9\xc3\xda\ +/\xa9k&\x22\xe5_QGP\xe7\xc2\xd5C\x08\x92\ +\xaa\x82F\x14v\x05\xe5]\xc6b/c\xc1W\xb0\xd8\ +8\xd7\xff\x86\x87u\x82\x9b\x13\xcbE\x89\xde\xa2\x88\x1a\ +/j&P+\x7f\x84:\xf7\x9e\xf8\x8a\x88l\x094\ +\xf1\x02\x8dZ\xb2\xa9S\x09 P\xa3F\xb7\x98\x82\xeb\ +U\xd7N\xa5Ub\xda:\x90!\xbd\x97g\x1c\x14\x00\ +\xb4R\xeaUQ\x87\x01@X{\xf2\xfb\x04\x84#\x04\ +P\xf4\xdd\xe0w\xa7\xde\xb4\xf0\xe0)!\xe2\x16\xef\xaf\ +a\x18\x87\x84Q\xaf\xc3\xba\x1ap\xc9\xe0\xd4=L\x8b\ +3\xee\xf8{\xbdb\xb9Z\x14rk\x03\xfcN^\x22\ +I\xf7\x80\xfbV\x88Vog)W\xe0p\xab{Z\ +\x0f\xa8U\x95\xb0\xb7&\xe0X\xbe\xd2z\xc7+Tz\ +\xa6\xb5\x01=\x0f\xbf\xe7\xef^\x97\x10\x01@\xeeB\x84\ +D\xcaG\xe9\xda>\xadrj5!\xc8F\xd4\xe0I\ +;{\xc3\xb8\xf1\xb0&~\x90\x88\xce\xf0\x19\xd9\xb1<\ +u\x07A\xf2\xb0\xee\xa8&\x83\xf8\x8c\x0eTRg\x11\ +\xd7V\x8e\x98'\xa2\x98;\xec\xed\x15,\x99:B\x0e\ +\xadhB{\xdb\xd7I\xa3\x16\x5c\x09\x99N\xe2\xd0$\ +\x86,\xf6JI\x97])\xd6\x5c=RDL/\x16\ +0\xe4\xd6{\x9c\x82\xdcC\x80\xe48\x1e\x22\x0e\xeb\x88\ +#\x97\x8d\xaf\x9f\xe7\x814\xa1\xb5\xf8;Y` \xf9\ +\xbd\xe6\xd3yPgI\xce\x9aD\xbap\x7f\xb8t\x0d\ +F\x1c\x00\x89\x05\x10q(]\xe2V\xcf\xf43\xa0Q\ +-]\x1c\x96\x13\xcf`ky;\x19\x22\x9c\x06\x01\xce\ +\xe0\x99\x02Z\x0a\x86\xffD \x80Q\xedNV_\xe5\ +q\xed\xf5\xd3\xa6\x8d\x07\x18\x86\x96\x9e\x9f[\xc9\xec\x1b\ ++\x9b\x7fg\xe5\x0bo\xadl\xe15\xe9\xa2\xa6\x88\xef\ +Y\xee\xd8-\xc6\xf1\x84\x90\xaa\x89 -\x09{L\xdd\ +\xad\x0a^\x00\x00\x8f,q\xcc^i\x1daZi\x9e\ +\xab \x92\xf25m\x8f\xd2=\xe5{\xab\x81z\x9f\xa4\ +T\xb2j\x1c\xa5C,\xdd\xb3\x09\x04\xe7\x00\xc0P\xe2\ +\xf5\xec\x8cA<\xff\x1b\xe3\x8e\x82\xc3\x0d\xb9\xa5`\x94\ +\xa7]8Y(22\xb8iQ$\xd7\x89\xe2\xf8\x06\ +\xf1\x5c1\xfd\xaas\xeb\xb9r\xe5#[\xc8\xb6\xb3\xf0\ +\x5c\x00\xa0]\xc0jx\xa4\xb0\xa1\x87rm\xe5\xf0 \ +\x11\x88Q\xde\xe8\xa1\x15\xc0\x88\x0b\xe51\x00MD\xfb\ +\x00\xd5L\x02b\x98\xaa\xbcU\xae\x09\x85\xe8\x06]\x83\ +Du\xca,\x1f\x81\xb8q\xd3\x92\x0a\xdec\xd9j\xa8\ +\x98\x88b\x13\x9b\x97\xce\x17NV\x19$O\x92Z\xd6\ +\xf9\xf9*\xb2aIm\x88\xa6T\x11\x1d\xf9\x9e\xcc\xef\ +5\xc3\x96\xc2g\x92\x9b\xf8l\xe3\x12\xa0\xd0\xfa\xc7,\ +\x03\xa14\x0c\x8b\xd0\xc0\x9d\xaf\xa1{ \x1b\xb5X\x94\ +\x1f'\xf2\x84B\x925C\xa8\x850m3\x1fF\xf9\ +0\xff\xfc\x99{V2\xff\xc4\xca\x17\xc9\xf9\x97\xdfX\ +\xe5\xca;\xd3\xf1\xf9\xb5\x1b_[\xc3\xf6\xf7\xd6\xbc\xfb\ +\xb3\xb5\xec\xfdb-\xbc6\xf2s\xed\xd5\xaf\xacrU\ +{\x04\x9f\x93\x1e>p\xdb\xd4\xc5\x1f2\xbb\xb6\xe0\x06\ +xb\xe5\xea<\x9f\xeb<&\xe5\xb7\xf1\x9e1Jn\ +;_\x16\xc6\x0biB\xc8\xb9~\xac_n_\x93:\ +i\xea\xaer.)\xf5\xaa\x09\x98=\x7f\x1em\xec\x95\ +7\xd0l\xa1\xc2\x08\x1e\x12\xe5{\x02\x00\xd2\x1d\xd9\x83\ +\xe9\xc3\xd2}\xbd\xcb\x969\xb0\x86\x8b\xd7\xf6.\xdc6\ +\x96Z\xac\xd7Q,\x1e\xa5\x87\xfbp\xe9\xbd\xc4\xef\xbe\ +\x15\x00\xb1fy(1\x0f H\xa2\x10\xbf\x08\x8c7\ +\x1b\x00x$\x88\x94\xa7[\x80 l\x0c\x1cX\x8ev\ +\x09+c\x807D\xf0\x08Y}\xea\xc6\xa19j\x11\ +\x17\x11\x18\x89&+\x18\xe4\xa65O\x1aW-\xad\x1e\ +\x16[G\x1aS\x8f4.3\x08kN\xa1Z4Q\ +\xbb\xd6\x0bI&\xee\xa6t\xee {\x96\xda\xb5oi\ +\xddG\x96\xd6s\xfcI\xd2\xf99\xbd\xeb\x10\xa0\x1f\xe0\ +\xed\xf8\x8c\xce\xd4k\xd39\x00\x1b\x1e0TE\x030\ +t\x0fI\x0ch\x22\x03\x1b\x8f\xc7\x88\x97\x07\xd1D\x0d\ +1=]S\xb6jJ1r\xcbr&\xeeX\xc1\xec\ +C+[zaU\xeb\xef\xacn\xebKk\xdc\xfd\xd6\ +Z\x0e\x7f\xb0\xee\xeb\xbf\xda\xd0\xed\x7f\xb2\xb1\xfb\x7f\xb7\ +\xa9\x87\xff\x86\xfc\xbb\x8d\xdc\xfd\x17\xeb\xbb\xf57\xeb8\ +\xf9\xa3\xb5\x1c\xfch\xf5;\xdf\xb8\xad\xddej\xbe\xad\ +\xbe\xcb#\xd7\xf1\xba\xbb\x8e#\x880:n\xc0\xd8H\ +\xf9)\xed\xc4\xf4V\xa4E\xf3\xfa3.\x1bI\xd3L\ +%c\xe6\xc3\x10\xfc\xad\x8c%<\xc3\x9b\xe2\xdd\x01H\ +\x9bn\xfc\xf4,\xee\xd0\x0b\x9eC\xab\x81nE\xd0\x89\ +W\x15\xa4mc1iX_\x9a\x9a)\xb5M38\ +\xb3\xa47j\xf4\xb0\x82\xcb\xde ^o\x13\xb7\xb7\xad\ +x\x0c\x00@\xecB=K\xae\xe1S\xa8\x9b\xcf`\xbd\ +\xd1\x01\x800\xb8\x86\xac[\x0e\x9eA\xed_\xdc\xe9\x97\ +d\x0cY=R\xfe\x9e\x8bw\xe1AR$\xeduW\ +\xca\xa4\xfd\x80\x10\xc9\x00\xdeF\x0bEnZ\xd3mS\ +\xd2\xea!\xee\xb0\xe7\x08ph\xe3\xa4:Z =\xd7\ +\x01\xca1!E\xfb\xd9\x0f\x9c\x12\xd2\xba\xd5\xa6u\xd7\ +\xbd\xa6u\xeb\xe7}~F\xb1\xdd(\xba\xf7\x1a.\xfa\ +\xa6\xf9\x07\xce,s\xe8\xaee\x0e\xdf\xe3\x15\x19\xbc\xeb\ +~\xe7\xeb\xbf\x89\x05_G\xae\xc1\xe4\xbd\xf63\xea\x98\ +\xe1\xda\xa7\x00\x12_\xc7>\x83\x0f8\xf0\x22)\x02\x9a\ +S\xfc\x9e\xfbl\xf6\xd0mr|\x9d\x9f\xf0\xd4\x8a\x17\ +\x9e[\xc5\xca+\xab\xd9xg\x0d;_\xa0\xd4o\xac\ +\xfd\xe4{\x1b8\xfb\x83\xcd>\xf9\x9bm\xbe\xff?\xed\ +\xe0\xeb\xffa\xc7\x1f\xff\x1f^\xff\x1f[{\xf7?m\ +\xea\xe9\x7f\xb3\xa1{\xffj\xbdg\x00\xe1\xc6o\xd6t\ +\xf8\xbd\xd5l~ne\xcb\xcfM\xcd1\x9c'pe\ +b\x22\x87b\xf3(\xae\x05KGt\xa6\xb3\xeb*\x06\ +I\x0f\x02\x90l2\x8b0\x86\x16\xc5\x1b\xe5\xf2\x7f\xd1\ +!\xd5D\xaa\xf4\x8c1\xeb;a\xcc~_\x17\xb8t\ +n`\xca\x16P<\x04S\xe2J\xc2RT\x0e\xed\x8a\ +\x0f\x87\xb9\xc8\x08\x17\x9f\x80\xf4\xcdY\xc1\xc8*\x8a\xdf\ +p\xca/\x1c\xd9\xb4\x1cu\xfc\xc0Kdv\xcc\xb9\xb2\ +\xf1\xac\xce9\xcb\x06\x08\x02E\x08\xcf\x11\xc2;dk\ +[\x18\x8a\x15w\xc8\xee\x917\x808\x92\x1a\x85\x05\x82\ +\x01\xd2$\ +\x22\xad|Bz\xf9\x84\xf8\xfa\x98l\xe3\x01)\x17\xca\ +\x84H\xf9PXz\xdf\xa1\xa5\xf5\xa2\xf8\x9e=\x14\xce\ +\x83\xaa\xb8\xa2\x1f\x05\x0e\xde\xb2\xac\xe1;\x16\x1a{`\ +\xe1\x89'\x963\xf9\x1cy\x81\xab~\xce\xef\x9eX\xd6\ +\xc8\x03\xc0p\x87\xf8-p\xa8y\xc4]~\x7f\xdf\x22\ +c\x0f\xb9\xc6C\xc0y\x8f\xdc\xfe\xcc\xd4>\xc5\xdf\xcb\ +w\xc2\xea%\xb2z\x1dI\xaf\x13\xc9uRJ\xe1\xf4\ +c\xcf\xedc\xbd\xd5k/\xacn\xe35\xee\xfd\xad\xb5\ +\xec\x7fn\xfd7?\xda\xf2\xf3?\xd9\xb5o\xfe\xb3\xdd\ +\xfb\xe5\x7f\xda\xa3\xdf\xfe\x7fv\xe7\x97\xff\xaf\xed~\xf5\ +\xbfl\xf6\xe5\x7f\xb5\x91G\xfff\xfdx\x86\xae\xdb\x7f\ +\xb1\xd6\xeb\xbfX\xdd\xeeWV\xbe\xfa\x92Pr\x97\xf4\ +Y\x00P\xb6\x80g\xd4^I\xc5\xfb\x06H\xb2\x0aC\ +q\xfd>\xb8@\x10p\x14\x10R\xab&\xaf[\xed\xec\ +M\xab_<\xb3\xfaem\x16\xb9m\x05\xae3\x99\xb8\ +\x05 \xe8\xd7\xeenU\x1c\xe9\xccGyOo\xceA\ +\xbd\x1eD \xd5\x85]\xfb/c\xbcs\xf14E\xda\ +\xcd/\xbd\xcd\x1b\xd9]S\x967\xb8\x04[_\xc7\xfd\ +oZ\x01\xec>\x82r\x03\xed\x0b\xa6\xa3N} \xd3\ +\xdf\x0a\x83\xd7\x9a\x80\x96Fubx\xd7\xa2i[\x98\ +j\xed\xb4\xd9SS\xc1\xd9 4D^\x1c\xc6\x13\x84\ +\x05\x04\xe5\xc9\xbc\xcf\xe2w\x0a\x0f\xfe.\xc4mNU\ +C\x04\x94?\xae-\xd0\xb8D\x9dJ\xb2\xfc\x1e\xf9\x9c\ +\xf7\x9f[\xde\xdc[\xcb\x99za\xd9X\x9f\x94\x97\xce\ +\xc3\xa5\xe2.S\xb1\x964\xbeG;\x85\xb5\x9f>{\ +D\xdb\xb2\x89\xabSO\x19\xd0W\x96\x0f\x01\xcb\x9fy\ +c\xd1\xa9W\x00\xe2\x99e\x8f=\xb2\xe0\xe8}\xe4.\ +\xdf\xa5\x06\x12j'\x83\xf5\xcd\xea\xb3/\xf9\xbfgX\ +\xf8#R\xbb\xbb\x00\x0e0aQjH\x91\xcdkh\ +\xf8&\x008%\xfb\x81\xec\xe1\xfe\xd5\xd2F\xf3\xffe\ +\x0b\xf7\xbc\xcd-\xab\x0f\xadv\xfd\x91u\x1e\xbc\xb2\xb9\ +\xfb\xdf\xd8\xe1\xfb?\xd9\xe9\xc7\x7f\xb1{?\xfd7\xbb\ +\xfd\xe3\x7f\xb7\xdd/\xff\x8b\xcd\xbe\xf8W\x1b{\xfcw\ +\x1bz\xf87\x00\xf0'k\xbe\xfe\x93\xd5\xee}ie\ +k\xdc\xc3\xccmWk\x18\xe8\xc63j\x8e\x00\xcf\xac\ +\xfe\xc5\xaaYL\xab\x1buk\x11\x99\xad\x8b\x16\xc1\xbb\ +V\xa0\xfc\x8e\xf5\x07\xd6\xbd\xf5\xd0zv\x1eZ\xf7\xee\ +CkX\xbd\x0d\xe9\xbc\x06@\xd5R\x06\xc0\xaa\xbf\x03\ +^X\xfd\x91\xdcz\x01)\xa1\xb2\x19\xafHD\xa5\xfa\ +\xfd\xe8\xbc\xcfb\xb4\x1a\xe7\x8eM+\xef0\xf5\xecM\ +\xab\x1b\xb4`\xeb\xa4Eq\xf1\x05\xc4\xfd\x02b{\x1e\ +9oX\x04\x91\xb8\xe4\xe6\xcd\x9b\xd4\xf1s\x06 \x00\ +\x06\x15Nj\xaa\x17\xb2r!\x81vZyEb4e\xaaY3\xf5\xdf\xf7\ +\xf6\x07\x8eZ\x00\xc5\x86\xb1\xe2(\xa4.wp\x97\xf8\ +N\x8e\xaf\xc9 \x15\x83\xb4\xaf\x00\x84e'R\xb2$\ +\xa05u5y\x80\xb4\xf8\xb4\xc2F\x9c\xf2\x93^f\ +\x92\xda\x04A\xa0\xe2U\x08\xf6\x1cBY!\xc0\x10\xc2\ +u\xabN^9\xb1*du<]t\xea\xb1\xe5\xa3\ +\x8c\xe2\xd5w\x0c\xcaWV\xb3\xff\x9d\xd5\x1f\xff\x8c\xfc\ +j5\x07?\x03\x88\xef\x00\xc1\x07\xcb\xc1Cd\xa1h\ +?nY\x0d\x0eTD\xa1FP\xb2T\x1db%\x17\ +\xed\xf5\xe6y\xeeZ\xb4\x14L?\xb3<\x94\xaa\xb2\xac\ +\xf0\xe8\x1d\xbc\xc4\xa9\x93\xd0\xe8\x99+\xe3\xca\x9b\xd2\xe7\ +\xb96\xd7\xcfS\xd9\xd6\xd8]\x94\x8d\x1b\x1d\xe2\xfb\xe0\ +.9\x83\x87\x08$V2\x84\x0c\x8b\xd0\x1e\x02\x06~\ +\xcfk\x0eV\x9b3\xb4O\x06\xb4\x03\x09\xde\xb4B\xb2\ +\xa1\xfa\xd9#\xeb]\xbfe#\xbb\xf7l\xea\xf8\xa9\xcd\ +\x9f\xbe\xb5\xa9\xd3\xcf\xdd\x99\xc7=7\xbe\xb4\xce\x1b_\ +Y\xdd\xde;\x80\xfe\xcc\x0a\xf0 \x11,:\xc88{\ +\x0aSY\x9e7g\x7fQ\x09\xa4\x92\xf3,\x5c\x7f\x8e\ +\xf6\x08\x8e\x01\xb2\xa5\xfb(\xfe\x09 \xb8kmk\xc7\ +\xd6\xb4\xb4c\xa5c\xf2\xbe:\xebh\x12\x12I|o\ +\xc1\xddkvWS\xbex\x91X\xd2\xec+(\xff\xb2\ +\xa6\xcdK\xdb\x916\xa4\xd5b\xdcVb7\x0b\xa6\x8b\ +\x8d\xc3,\xb5\xda\xa5m\xde\xdb\x10\x0b\xf5\xf7\xc1\xad\x90\ +\xaf\xe6\xf0>\xacF\xd1}Z\xe8!\xbe+\xc6#\x22\ +|\xdaQ\xac\x93\xc2u\x5c\xbc\xda\x95f\x00\x00\x1fi\ +\x8c\xbfy\x110\x01\x12\xd2\x9b`\x1b\x1c\xa2}\x1d\xee\ +\x00B\x01\x81<\x80\xce\xc7W\x85\x8c\x00\x903\xa5\xbe\ +>x\x80\xe5\xd7V\xb6\xf1\xc1\xaa\xf7\xbe\xb1\x86\x93\x1f\ +\xac\xf1\xba@\xf03?\x7fo\xa5\xa4P\xf9\x0bo\x88\ +\xebOP \xf1zH\xe5U\xb8h\x80\x14Baa\ +b_\x0e\xdf\x17E\xb9\x12)2\xcc\xef\xdd\x89\xe5\x00\ +N\xdb\xdf\x83d%j\xce\x90\xa5p\xe4\x08\xea\x89S\ +v\x0e.^$J\xc7\xde8\xce\xd2\xb7G\xd8#|\ +\xe1F\xc3<\xa3\x96~\x95\xe1h^?[\x13cX\ +bXmb\xfa\x0f\xf0j<\x0b\xe1,\x0b>\x13j\ +_\xb3|\xdcx\x19VXIz\x5c;y`m\xcb\ +7\xadu\xed\x8e5\xae\xdf\xb3\x86\x8d\x87p\x06\x1d\xbf\ +\x03\xd7\x99VS\x8ec\x94\x0f\xc9\xe4\xfb\xd5\xd7\xd0M\ +\xdf*^\x9f3wu\x15\xd7\xb6r\xf1\xa9(\xba(\ +\x9c\xd3\xfeC\xb7C\x18\xb9\xa2\xb9\x16'\xdd\x16\ +\x8b\xc4x\x95\xa5b\x87\xc4\x9d\xe6U\xd2\x10\xcd\xf9\x13\ +\xafq{\xb9c\xa7\xae\x9dI\xde\xf8\xa9s}9Z\ +\xf0\x18\xe4ox\x04\xad\xf8\xa9\xc5K\xb6\xa6\x88\x19 \ +\xcd\x1c\xba\xe9M\x1e\xc2\x87\xe5\xfb`\xf6~r\xf4L\ +HH\xb0\xc3k_\x9aE\xba\xa6\x13\xc54_\xaec\ +\xe6B\x1al\x07\x80;X\x81\x5c2\x96\xb8\xf8\xc2J\ +\xd6\xdfZ\xe5\xee\x17(\xfe#\x00\xf8\xd1\x1a\xaf\xfdd\ +u\xa4W\x95\xdb\x1f\xadd\xf5K+\x5cx\x87\xbb~\ +\x89\xc5B\x14\xc7\x94B\x9dy\x8a\xee\x97\xa2Q2\x1e\ +&S\xfc\x82\xeb\xfa\x00\x9e\xb7\x1d\x0bbE\xc6!\xd1\ +|\xbc\xee\xd3\x8f\xb2\xbc\xe3\xee\x95\xba\xa2\xc0s\xc9\x86\ +[8o\xc5\xefC\xdcw\xf6\xb9d\xf1{\xcd\xe0e\ +\x13_\xdd*\xe0\xd8M\xcbgl\x0a\xc6\xce \xcd7\ +\x09\x95x\x0cy7=\xaf@\x8f\x01dq\xad\x02<\ +C\xe9\xd8\xbe\x95M\x1d[\xf9\xec\x0d\xb7'Rk(\ +Yx\x0e\xb5\xb6\xf1\x03,\xd5#\xaa\x0c\xed\xf7g3\ +\xebg\x1f\xd7\xcd\x04\xb0*\x22\x89\x8e\x9fY\xfe\xe4\x1d\ +\xc2\x8e\x1aB\x12~F\xf7\x08\xd5\x10\xf0\xf6)\xf3\xab\ +\xd95!\x5c\x07g\x5clp\x89G\xf1\x09*}?\ +_]\xd4\x8c\xa8&\xbe\xe2\xabF\xdd$X|\xf58\ +i 9dz\xfb\x06\xeeG\x95\xbd\x07\x16\xc4\xb5\x86\ +\xd4\xb4Q\xb3Z\xd3\xeao\x83\x90\xa7\xe6\x81<\xb5\x88\ +\x8f\x0c\x03\x00M\x0bk\xb5K\x0b\x22\xbd\x9a\xf0\xb9\xf0\ +\x00\x02\x80D\xef\x19Xr\xf3\xac\xae]\x14\x8e\xb2q\ +\xd3!Y\x8a,\xaf\x8fl@Y\x01 \xd3I\x98\x11\ +\x1d'\x0f\xb1\xcaQ\xf7\x8b\x99\xfb\xb8\xc5GV\xa2\xa2\ +\x0a\x98u\xed\xc1\x97V\x7f\xf4-\xf1\xf2{\xab\xdd\xfd\ +\xc1\xaa\xb7~\xb0\xaa\xab\xdfY\xd5\xda7V\xb5\x02W\ +\xc0#\x94(\x96\x03\xa2( \xd05\x82(O\x1bJ\ +}*\x84\xc4\x82TC\xef\x8e\x9e\x11\x03Vl\xe5\xd5\ +m\x8f\x82\x15\xa7\xab\x00D\xde\x0a\x90\x04\xb8\x7f\x85\xb9\ +,@!\x85\x87\x00\xac6vfK\xbaP<\xcau\ +^\x83\xfb\x8e\xc2\xb6\xf3\xa6t\x14\xfd\x03+\x9d{\x84\ +R\x91\x99\x07V\xacf\x0ej\xea\xa0\xb0\x841\x05Z\ +\xe1A\xe4\xf69x\xcd(\x9eC!5\x8a\xe2u\xfc\ +\xbc\xca\xd0\xd4\xbcR\x85\xa7Z\xbau\xc5\xa1j\xdd\xe2\ +j\x04w\xd1\x87\x00BZ\xcc\xf7\xa9uO\xf4\xd3\x89\ +\xa1\xea\x12\xa2if\x9e\x15^\xe0\x87\xe4i\xc9:\x19\ +\xe5&Vj\xba[\xa2\x121-x\x0d\xbb\x95C\xd7\ +oA\x93m\x9a&\xc6\xbbx\x15\xe0\x0b\x16\x93\xc1\x85\ +|*\x14\x84\x90\xb9\x1e\xbc(?4\xa6C\x09E\xca\ +\xb0\xc89d\xf6!\x17Vy\xb3\xdc\xacn^^\x02\ +>\xa0\xee\x1b\x9dX\x92\x8a*qS>\x1eB\x13\x19\ +\x99\xce\xd2\x15\xe7\x09\x1b\x903\xb9\xd6\x1c\xb9gD\x84\ +*\x0c\x9a=\x11\x07\xe0A\x5cw2\x91A\x84\xf7\xaa\ +\xb2\xd5\xe1T\xc5\xcb\x8f\xacb\xe3\xa5\xd5\xec~\xb0\xfa\ +\xfd\x8f\xd6p\xf0\x935\x1e\xfcjM\x07\x7f\xb0\xe6\xfd\ +_\xacy\x17\xef\x00Y\xac]\x837\xcc\x93\x9bc\x1d\ +\xea\x95\x1b\xc1}\x87 \x9fA\x94\xea\x17aE\xe1\xa9\ +\xc4T\xad\xa2\xb9\x83\x15T3\xc7{W\x17H\xbc\xcd\ +`\x00\x03\x84/\x852\x85\xb5\x10\xb9\xb52\x96\x08\xf7\ +\x1f\xd1\xe6\x0e\xed4&\xb7\x0e\xcb8D6\xa5`\xac\ +_\xcd!\x0b\xb9\xcf\xd2\x85\x07\xae\xe1d\xd5\xca\x132\ +\x02\xd2\xc3y\x5c\xbc\xa6{\x19\xcb\xec\xdec\xc6\x83\xf1\ +=\xaf\xc3S\xbb6\xe7\x99\x5c\x0a\x8c\xcb\xe7\x1e]\xc1\ +\x86f\x01\x05\x00\x8cF\xfd|\x95~\xfaU<\xaa\xbe\ +\x81\x18\x89k\x14\x05\x00\xa2\x02\x80\x80\xa0\xfe\x82xh\ +\xe5\xffa\x0c*\xc8\xff\xa9\xdcK\x07k\xa8G\x90&\ +yRk\x09!\xb5d\x13Hr\x1d\x06\x80\xb7O\xaa\ +\x9f5\xd5\x81z\xc7\xd0\xaf\xea\xf8\xf8C\xf3\xf3\xa0j\ +\x17\x17DQ\xd9\xa3\x10$\xac\xc9\x03\xc0\x13R0\x04\ +\x00\xe4\xaa\x9cI\xad\xcc5\x89\xa3\xd50m\xc7\x86\xf0\ +\xb9\xeeYn\x05Qnu\xcd)_\xf1=\xc2@\xe5\ +\xe2\xde\xf3\xf9\xae\xfc\xc9{^c#\x90\xab\xa6\x88j\ +\x94\xa4\xd2(\x1dT\x1d\xd6r\xaa\x13\x06\x17Q\xbbt\ +\xcd\xaf\xe7N\x9d\xc2\xce\xef\xbb\x06S\xd5[o\x01\xc0\ +\xd7\xd6D\x18h9\xfe\xc5\xda\xae\xfd\xc1\xdaO~\xb1\ +\xf6\xa3\x1f\xadu\xff\x1bk\xdc|o5+\xcf\xac|\ +\x0e\x8b\x04\xa4y\x90\xb5(\xa1)BX\x0aAP\x83\ +d*\x01W\x9a\x86\xe5\xbbM\xab\x12\xf2`\x94\xaf\xc9\ +\x15q\x17-|\xa9\x0f\xaf\xb8\x8ebm..=\x17\ +\xb2(\x89\xc25\xd4s/\xa2T\x15#\x11o\x08\x13\ +\x0eU\xe1\xa3\xa5\xf0\xa2\xf9[V\xb1z\xdfj6\x9e\ +X\xfd\xf6K\xab\xe3~\xab\xd7\xc9f\xdc6\xf7G\x80\ +\x1e\xf2\xd9\xa7.^\x8c\xb3\xfa!\xbb\x1a@\x00\xc0\x98\ +\xb9\xf2n\x09\x5c\xe9\x1f=\x824\xf1t\xe0\xda\xc5\xa9\ +e|\xa6v\xfcb(\xd9j0%O\x80\xf2\xd5\x9f\ +\xb0\x10oY\x04 \x0a\x09\x9f\xb9\x10`\xb5\x83\xcd\xec\ +:\xc4\x10w\xe0a\x80\xac\x89\xf0\xd7\xb8\x82\x97#\xf4\ +\xa1t\xb7\xff\xb0\x19\x80\x9c\x83-\x15o\x13\xe3\x07\xa1\ +\x01\x10\xae\x06\x85\xbf\xef\x16\xa6\xc6D\xd1iX1!\ + \x8a\xabS?[m\x8f\x0a\xf4\x90^\xc0\xf8\xbd\x1e\ +\xc2\x9a\x9aT\xcd\xf9\x02qU\xde@\xb3\x80\x22G\xc4\ ++)\x1f\x96]\xc8\x0d\x16\xcd>\x22%{\xec\xda\xa8\ +\x16p\xd3r\x9d\xea~)@\x85G\x09\x05\xa3\x84\x87\ +QB\x0b\xccZ-\xde\xa2\x137`\xef:{@]\ +\xc6\x1e[5^\xa0~\xf7=\xd6\xff\x855\x1f~m\ +-G_[3\xef\x9bv\xdfY\xc3\xd6K<\x00\xd6\ +\x07\xa9*\x9b%\x1d\x9b<\xb6\x02\xbe+oh\xc7r\ +\xf1TQ\xd2\xa1\x88Z\xd5j+\xf8\xf9I\xe6:\x8b\ +/\xbd]\xa2z\x87\x157\xf9\x92\x8d\xf2#R>\xc0\ +\xcc\xbdX\xdcQ\x96\x00A\xcd\xd5\xe4\xd4\x08F\x01\x10\ +\xb4\x0bY\x00wK\xe4|\xb7\xba\xa3G'w!u\ +\xd7\xadr\xe3\x1e\xec\xfe\x855\x1e~\x0e`\xbft\x8d\ +\x1cK\x97IE\xd5\xc0Q\x13ZC\xa4\x97\x03\xe7^\ +A-lTz\xee\xaau\xe6=q\x0bsZ\x95\xbd\ +\xeaJ\xd4\xb5\xc7\xc2\x8f7\x0b\xe0\xea\xd51\x5c\x8d\xa3\ +U\x7f\xa0\xc3\xba\x0aT\x83\xb8\x84\xb7YQ\x152a\ +p\xde\xe3D\xe1\xa1{\xdc\xd7-\x80\xc05:\x0e \ +\xf4\xf0!\xb5\x8dk\x13\x1f\x02`\x18\xa9\xe3\x1ax\xa1\ +4\x80\x18\xa3\xce\xdc\xea\xc5\xaf\xa6\x0ajJ\xa4n\x95\ +j\xb6\xac\xadN\x8a\xcb\x12u\xab\xcc\x06\xf1\x99X\x95\ +V\xf9>mi\x96(\xfe\x80d\x95SkbG\xe5\ +\xce\xae\x7f/\x00\xc8\xe5\x7f\x0b\xa6\xef\xe3&q\x89\xf3\ +\x8f\xadx\x01\x99\x7f\xc8\xcf\x0c\xec\xa4\xb6L\x1d\x02\xaa\ +\x1dR8\xed\xba\xb9\x0a)\x22\xee\x8e\x90N\x01\x88\xbc\ +\x89\x13\xc0s\x93\x07;3\xf5\xf5\xab\xbe\xfa\xd8j6\ +\xd5\xdc\xf1)\x16\xf6\xc4j\xd6\xef\xe3r\xf9\xdb\xe2u\ +\xe2\xef\xb1\x95L\x1f\xa0|\xb5RQ3jdt\xd7\ +\xf2G\xbcfV\xf9\xc3;\xae\x99\x85\xd6+\xc2\x5c+\ +\xab_\x0d\xa3\xe1-X\xbd\x0a]\xdc9H\x03\x80O\ +\xd9\xce(\x04M\xaeUm\xd8\xf0^\xb9\x84\xc3\xe8\xa8\ +Z\xb2\xe1\x01\xc88\xc4e\x82\x90C1sm\x8f\xf7\ +w\x91\x9f\x0f\xadX\xde\xcc\xae\x95\xae\xdc\xb0\xea\x9d\xc7\ +\xd6x\xf4\xce\x9a\x8e\xbe\xb2\x06\xbcV\xed\xf6Wx\x83\ +/Q\xd2;+\x99{\x89\xc5\x12R\xe5a\xb1j\x11\ +JM\xfbj\xeb\x5c2\xa9\x9e*v\xdc\x16\xeeFm\ +\xfeP\xfd\xbf\xc0\xcau\xba \xab\xdc\xab:\x8e\x85\xe4\ +\xdd\xc6\xafaH\xb7\x01\xc0c<\xcf+\xab$\x04\x96\ +\x09h\xb3\xaf\xf8\x9b\x07\x82P\x1f\x9e\xbc\x87\xd0\xda}\ +\x02\x7f\x01\xb0\xf0\x8a\x00\xbcLaZ\xba\xca\xd0\x0c,\ +\xde&&\xb5\x05\x14\x8a\x08\xb6ijV\xb5\xf1|P\ +\x08\x17\xa3\xd6\xec\x1a\xa2\x03\xa5]/~\xe2\xaak \ +\x0dR]\xcd<\xaf\xaa\xa1\xd7\x97\xa9\xab\x95f\xf5\xb4\ +ARiYD\x00`\x10\x05\x80b,\xbf\x14\xe5\x97\ +.A\xee\xd4\x1eN\xfb\xea&D\x02a\xb8}\xc4\xe9\ +\x9ey\xf3!\xfe\xdeE\x00H\x0a:\xb4\xc1\xa0o\xe3\ +AP\xe8\x14\x8a%\xaf.\x9dG\xc9\xf3G\x0c\x22?\ +3\xd8\x85\x13\x1b\x967\xba\x82R\xd5\xe4q\x9etl\ +\x8eM\xb0u*T\ +\xads/\x90\xc9\xb1#+\x9a9\xb32\x08s\xc5\xd2\ +\x0b+[|\xc58\xbdp\xcb\xd0\xd1\xe1{x*\xc2\ +E/\xe9q7\xf7\xdc\xb9cA\xb8G@\x04]\x99\ +\x10^'M!\xa7y\x854\xb0^\x85\x89\xdaf\xa4\ +\xc5\x07m\x1fRQ\x08\x08\x87\xf5\xea|\x1e\xb7y\x81\ +\x87\x96\xf2\x1dq\x91\x8b:\x97\x7f(\x9e\xcf\x00\x1am\ +x\x14p4\x80\xf2\x00J!\x8bp\xf9%\x0bZ5\ +\x83-\xabJv\xfe\x14>\x009\x1c\x96\xf5\xcds\xad\ +I\xd8/\x0f\x8a\xa4wL\xf0]\xda#8\xcfC.\ +\xe1rQ\xf0\xf0\x0a\x0a]\xe6u\x09E\xcc\xa3\x04\x06\ +\xbc{\x02\xf76\x02s\x1f \xce\xf5\x11\xe7\xd4y\xc4\ +;\x1f0\xd0>\xc2\xdf\xa7\xb0\xd6E\xcb\xd3Z\x06\x9e\ +\xa1l\xde\xdbgX\xb1$\xc2\x86W\x9a\x91\x85\xe3\xca\ +\x07\xbd\xa6\xd8^\xd3\x06\x89\x9e\x93\xe7\xd6^{\xbcb\ +\xa6\x04\xcf\x18D\x14\xda4\x85\x9d\x85k\xf6\x8e\xc3_\ +\xc2\xcd\xce\xf1\xf7Y\x9ew\x09\x00\x008\x00Z\xbe~\ +\xc7jv\x9e[\xdd>\xbc\xe5\xe0s\xb2\x97\x0fV\xbb\ +\xf7\xde\xaa\xb4=}\xfd9!\x01\x0f8\xa7\x19K\x19\ +\x17\xf1\x9e\xefImV\xe5\xce\x08\xe4\xd4\xebQ\xa0\x96\ +o\x92$\xde'\x93\xc7\xa7\xd6\x0c\xc2\xf2\x87]Q\xad\ +\x8ak\x83x\x1e5\xb7\x88\x02\xa0|xS\x91:\x88\ +\xc3\x0b\x94\x09\xe5\xcaS\x11\xa6Bx\xf6,\x9e'\x88\ +a\x070RM\xcc\xb9\xb2\xf7\xa69/3Bt\x22\ +Y\x8c+(lPQ!\xbf\xe0\x03\xaa\x1fO\xd3\x9e\ +s\xe5\xa0\x8e\xb1\x92\xca\x10\xdb\x03\xae\x94\x89\xdf!\xf2\ +\x14\x19\x9a\x82\xd5\xf2\xa568\xa0t\x1d\xe5\xa2\xe9X\ +\x1d\xe0\x10\x86G(gU_`\xf5\xd1-Y\x90\x00\ +\x86\xb9\x1b\xc4\x7f-\x07\xaf3\xa8s\xe4\xe7\xda\xec\xd0\ +\xcf\xc3w\x83\xfe.\x88Y\x0f\xee\xaf\x0f\xef\x82b[\ +\x87@\xec\x88Sh\xa0\x8d\x87o\x1d\x84\xad\xf7[Z\ +\x83z\x0duZ\xb2:\x96W\xb5Z\xa2\x9aV#I\ +\x95-\x96R\xd3\xce\xdf\x01\x02\xff\x17\xec\x99\xe1:\xab\ +\x90\xb0\x1d\xee\xe1\x18\x00B\xd4\x96\xe1\x09\x80\xb0H\xc7\ +\xdf\xc2A\x94[\x0b\xe0\xeeH\x15\x18tj\xf3\x85\xe8\ +\xac\x80\xab\x8c\x83J\xb77\xf9>\xb9|o\x1cT\xe7\ +\x97\xa5R\xf8\xdeU\xbc\x0698 \xcd\x19\xbbJ\x96\ +\x84WZ8\xb1\xd2\xd5;V\xb1\xf9\xc4*\xb6^X\ +\x05\x84\xb0|\xeb\xb9\x95m<%\xad}HVs\x0f\ +\x90\x90\xcbO\xebtvyY\xf1)\x18\xbb\xac\x9f\xec\ +D\x15O\xae\x96Q5\x8c\xc5:\xf9\xb3\xcd\x92J;\ +\x5c\x05uje\x8f\xa9\xb9Uz\x1d\xe3\xd0\xc0\x984\ +Ox@\xe8Z\xb1\x08\xa9x\x0e\xe19G\x13W\xdd\ +\x90pt\xa4\x0c(\x80>\xfd\x84\x93\x0c\xa5\xbb\xa4\xbd\ +idCi\xa4\x8b*p\xf5\xfa\x14MY\x8c\x8a\x11\ +\xd5IJ\x1d=\xdc6\xe4\xa6iWb\xec\xd5\x06\xc2\ +\xec?M\x96(\xdf\x07\x10X\x80\x8fTI\xadY\xb5\ +\x08\xa3<\xd5\xb5\x9b\x95\xe0\xfa\xb5\xef=B\x1c\x15\xdb\ +wm\xd4g\xd5J\x1dF?s\x0d\x8f\x00At\xca\ +'5\x93\xf2\xeb\xd5;H=\xeeZMG\xbaK\xf4\ +>\xa1\xaa\x8d\x1c\x96\x87\x97\x92\xcf%\xb1\x0a\xa9l\xc3\ +B\xf8{y\x0b\x83\xd5d\xf1\xa5\x8d\x16_\xd2\xe0$\ +\x91\x9f\x93\xf9L:\xdf\x19h\x1b'V/\xc2\x8c\xb7\ +\xdc\xa6R\xb5\xba)\xc1\xf3\x94\xcc{\x80,\x98Q6\ +\xa2~\xbd\x10[\xc0\x1b\xe8\xdc\xc5-n\x00\xbe5\x8c\ +a\x85X\xacs\x81\xb5cZu\xfb\x22j\x1e\x10d\ +\x0cZ\xaeU\xd1\x8b\xaa\x9f4\xef\x1e\xd1J\xe98\xfc\ +bj\x8f\x8c\x09\x02\xba\xc4\xb3\xaeB~\xd7\x1eX\xc1\ +\xda}\xcb_\x06l\x0b\xb7,:\x07y\x9bQ7t\ +\x91\xddm\xc2\x86\x88\x1e^\xb7U\xc5\x9a#\xa6vq\ +WT\xefX\xdcnW\x0a[\xbcR8$\xbe\xa8\xc5\ +\x12\x00D\x22@H*\x03\xf8\x15^\x8f\xe6Tu[\ +\xa9\xed'\xc5\x1dB\xc9c\x16 \xc3\x094M:\xf1\ +7jC\xcb\xb8[DJQ\xfd\xa0\xf6a \xae\xef\ +\xa2\x0b1^\x95\xb1\x9aG\xc6\xa8s\x94\x8eEI\xa8\ +\x1d\xb4\x04\x95\x86\xabq\x82Z\xb6\x02\x04U\xc0\xea|\ +\x9b\xcc\xf3R\xf0\xa0\xca\x9b\xfbq\x95}\x00@\x84P\ +=\xfd\x1c\x08p\xa5\x00A\x04R\x05\x92\xea\xa1/\x92\ +\xa7\x9e\xfbE3zU;\xf8=\xcf\xf2q\xef\x19(\ +H-V\xd5~5\xa1R\x1b7\xb4\x89\xe3w\xe2j\ +\xf9\xd5\x91\x5c\x070#:\xf9\xda\x89\x8aC\x9b\x90\x06\ +\x8b+\xaaGj-\xae\x10\xe15Q=\xff\x00\x80\xbf\ +q\xc8B\x9ds\x96;\xb8I\xfc?\x81 \x9eY\xf9\ +\xe2]\xe4\xbe\xcb\xd7\xd5\xd7\xbfp\x16\xc5\x90\x89hU\ +/J\xda\x1b\xd614\xc4J_\xdb.\xdeg\x8bp\ +x\x15R\xa6\xca!y\x82M\xc2\x9d6r\x88\xf9\xc3\ +\x11x\xc6\xc8\x90\xea\x1bD\x1a\x05\xe8m\x9ey\x0bv\ +\xbema\x94\x9b3K\x16\xb3p\xc3r\x90\xc8<\x06\ +1E\x18\x81\x8f\xf8\x87\xc8\x92\x08k\xbe>\x9e\xbf\x9b\ +\xcc\xa9C%fcn\xcc\xe3\xb0\xecX\x5c\xbe;C\ +X\x00\xd0)\xe2(^@\x88E\xe2\x0a\x19\x13^\x05\ +\x06w.p\x89\xc0\xdf\x04\xe8\x9b\x01D\x1b\xde\xa1\xc3\ +\xd2\xaa\xba\x10y\x89^KE\xd4\xf1,I\xfb<\xdc\ +^\xc1^\x070'\x15\x12t\x8e\xc4\xf1>F\x9b\x09\ +]\x17/\xd0\xa4N\x9c\x89 *\xd1\xed\xd2\xc5]h\ +\x07P'\xc4\xcc\x9d\x12\x86\xdb\x1bX\xc7\xda\x89)\xb8\ +.\xd7\xb3\x97T\xc8k\xd2\xbcK\xfc\x17\xa9\x22\x03\x18\ +:t{\x05r\xc7\x8f\x19\xe0#H\xc9>\x84\x85\xc1\ +\x19T\x87m\x1e\xbe\x1d\xf7\xd34lI\x5cO]-\ +\x13\xb4e\xab\x02/\xa0\xf3v\xcf\xc5\x9d z.\xee\ +\xe4kU\x03\x970\x08\xeaiX\xd4\xc8\xa0\xd4Yl\ +A\x0dRmW\x0a\xd4\xa5\xb3\x0awYg\xa9U\x1d\ +\x96\xd9\xa5T}\x10\xc3\xc1\ +[x\x0d\xf8\x03\x1e.gD\xab\x84\xaaoPg2\ +\xed\x13P\xe7\xd2U\x0b\x0c3>\xda\x96>\xca\xf8@\ +F\xd3\xfb\x16,\xa5\x0b\x86\x0f\xe8\x93ZG,\xa9\x99\ +go\xc2\x12\x09}\xf1\xb5\xbd\x16\xc7\x18\xc4a\xd5n\ +;\x5c\x09\xcaw\xd2\x06\x10$\xaa\x89$$\xa86\xb2\ +\xa8\x0d\x01\x14<\xff\x15\xc0\x7f\xa5\xb8\x8e\xf1\xa8\xf7\xbc\ +\x1f`H\xc2P\x92\xca\xf0\x9a\xe5xL\xb7\x0b\xaa\x07\ +\xe5kM\x80q\xbe\x98\xfc\x92\xa8\x18\xd6UCK\xfa\ +-\xc6\xf5\xa7\xe3&\xd4\xc6\xcd\x9dD) \xd4\x0f\xe2\ +\x0aGp\x7f\xe3(l\x1ab4G\x1a\x02\x0b\xedU\ +\x1bw\xc8D\xcf\x92\xeb'\x94\x0e\x19r\x0d\x14H\xa5\ +\xdcA\x93\xa4S^\xd9\xb8\xd2\x15/\xe5\x0a1(j\ +;\x17\xe8\xd4\xa6\x0a\xe2P\xb3&`\x04\x00\xb9#\xae\ +\xa5\x9bts\xd6\xbf\x13\x1dq\xae]\xbc\xda\xc6\xed\xca\ +\xba\x19\x00\xed\x09\xc0\xfac\x0b\xeb=\xe5\xe7W\xd9\x95\ +\xbc\x0a\xa4\x9c\xf7\xe5\x00\xa0\xc6\xd2\xe1\x00\xd9m\x13V\ +\xc0u\xcbI!k\xc8:\xea\xd6\x9e[-\xe4\xabZ\ +\x1d\xcbI\x9bJ\x01@1,\x5c\x9dIu\xf2\xa9\xe6\ +\x1b\x0a\xd4\xe6\x1d\xce\xa2^>\xca\xd3\xb3\xfan\x00z\ +\xbc\x82\xaa\x92\x06\xcfH\x81\xd5\xd7\x87\x94Xs\x01\x00\ +@\x8bG!\xb2\x06\xad\x0dhu\xd4\xf58\x86\xc8\xa5\ +\xb4LXJ\x1b\xaf\x1d\xa4r\xb2p\x94\x9e\x88\xc2\xe3\ +\xeb\xb1\xf0\xda\x1e\x8b\xad\xe9\xc2\xdaQ(!-\x8ep\ +\xa6S\xcau\xa2xl\xa9\x94\xad\xe3\xe3\xbd\x9dM\x92\ +\xcb\x02B\xa9\xceg\x00$\xa5}\xbc\xaa`V\xbd\x0d\ +\xf47m\x81\x03\x08\x88N\x0bw\x07K\xab\xd1'\x06\ +#\xe2\xa8-\xe2\xaet\xfc|\xb2\xcbkE\xa7\x02R\ +@\xa8=\x06U\x84\x80s \xc4\xa8\xf7\xbf6M\xba\ +\xe6\x81\x17[\xb4\xd5\xd0I[\xb3\xf1\x06\xa9M#\xc4\ +\xc7Q\xdc\xf6\x18\xe4O\x9d9!\x14(3\xbd\x93\xfc\ +_\x8d\xa5.N\xe0\x22LHt\xea\x96\xd2\x15\xb5c\ +\xf1\xb7\xcd\xc2\xd4\xb5\xab\x96\x98\xd44\x06\xfb\x1c5w\ +b\xa7<\x0c\xae/Y[\xc0\xb5\x07Q\xcd\x0c\xb4+\ +Y\xe2b\x94\xe6\xb1\x85P\xdc\x15 p\x00`p\xdc\ +\xce\xa0\x025m\xac\xb6\xd8\xdcJ\x8b\x8d\xa2\xfc\xdc2\ +\x8b\x03\x00I%\xb5\xe6\xab\xed\xb4(\xd6V6\xb6g\ +u\xb8\xfc\xe6\xab\xaf\xadu\xfb\x0b'M\x9b\xef\xadv\ +\xed\xb5\xe9\x8c\x1d\x81\xa0\x88<\xba\x80\x10\xe5dJ\x8b\ +^ZA\x94ruT\xccM'\xe1\x11-\x19k\xea\ +\xf5\x09$\x0f\xd1\x8a\xe5\xf8]\x97\x16fA\x1e\xfd\xb0\ +k\xb5sMR\xf95\xc0\xf5\xf6V\x22\x0am\xb5\x12\ +\xbc\x18\xa0\x8c\x85\xa0^\xa9\xd0\xc6\xd5\x06\xbb\x5cV\xef\ +\xe4J)\xafX\xef\x15\x81\xbaX\x96\x8d\xe0\xe1\xae\x10\ +\xea.c\xcdZ\xb2\xbdR\xa6\x15\xbc\x01\xaf\xaa\x97W\ +\xb7\x9a\x87\xb7P\xb7\xcf+Xz\x1c\x96\xee6\xbcJ\ +\xe1\x8c\x9d:\x84\x88\xdc\xb9\x13\xd7!\x80iJ\xf9\x5c\ +}\xa1j\x0c\xe0\x1bM\xda/\x00\xf9\xd3\xfe\x82*\xc6\ +\xb9R}\x02\x89\x0bj\x11\x9b\xe4\xbc\x00\x1e@5\xfa\ +\xe7\xa2\x8a\x91d\x00\xa1\x06\x8f)\xda\xc0\xd10\xe8\x00\ +q\xd1\xcd#\xbd\x1d2\x87\xa2}j\x00!\xe9\x00\x10\ +\xe4\xab*\x16I\x87P\xa6\x83\xc0t\xd2\x9b\x0c\x09D\ +'\xa3\x01 \xa9\xbc\x09Ik\x10:E\x04G]\xbe\ ++q\x1d-.r\xe1\x0b\x008f\xcc \x16\xe1\xfe\ +\xb5K(\x0f\xeb\xcf\xad\xb28\x00\xe0$\xaf\xd2\xe2\x09\ +\x05\xc9e\x8d\x16h\xe8\xb3<\xbcT\xd5\xf4\xb1\xb5\x5c\ +}f\xdd\x07_Y\xff\xb5\x9fl\xe0\xc6/\xd6\x7f\xfd\ +'\xeb:\xfc\xceZw\xbe\xc0+\xbc\xb2rMH\xc9\ +\xf2U\xd9\xab\xb5\x07\xc4\xcd\xad\xbb\xa5d\x08\xe2\xa8W\ +\xfe\x95\xaf\x0a\xa5\xf9\xd7V\xbc\xf8\xc6\x8a\x17^Af\ +\x9fZ.\x9fQM\x83\x96\xbbS Rj,\xe9\xdc\ +\xb3\x00\xeav:\xc3Q\xca$\xb8\xe9\xb2\x1a\x94]\x85\ +\xb2+\xedRq\x85'E\x95\xc4\xf8j\x040\xe3\xd2\ +\xb5\x1fB\xa2c\xebuZ\xa9\xb7\xe3\x19\x00`\xcd*\ +\xe2p\xe5\xe9H,\xe9`,\xc6)\x89\x97\x07\x15\xc9\ +\x83\xe1\xabg\x80*\x8a\xd5\x03\xc0G\xb6\xa2\xce\xef.\ +=\xd7\x8a\xa8\x13\x11X\xad\x8a\x02\x08M\xdei1\x8c\ +\xb1VG\x91\x98\x14\xbeT\x1d)u\xfc\xfa\x85\xb83\ +\xef\xcf\xdd\xb3v\xd4&\xba\xb3\xf0\x11\xd7\xf2\x15\xb4\x91\ +\x93\xea\xfc_50T'\x0f\x9f:j!R\xbej\ +\xd9\xd4L!]\x8b,u\xe3\xe6\xab\x1bs\xe2\x87\x95\ +\xfa\xb5\xcbV\x0c\x15I'\xeb\x10\x08R\xe5\xa6\xceE\ +uj\x0e\x08nO\x9c6n\xf0\xa0\xe7\xa9\x91\x068\ +V\xbb\x84\xf2\x19\xd4\xbcZ\x8b\xcf\xab\xb3x\xde\xc7\x17\ +\xeaL\xfdF\xe2\x7f\xbb\x05[\x86-\x7fp\xc5j\x16\ +nZ\xc7\xeek\x14\xff\xbd\x8d\xde\xf9\x93M\xde\xff\x0b\ +\xf2O6v\xfb7\x1b\xbc\xfe3\x7f\xfb\xda\xeaV^\ +[\xa9V:\xc7\xceP\xa8\x94O\xe6\xa2\xe5or\xe9\ +\x82I\xd5\xfd=\x810\xbe\xb4\xd2\xa5\xf7V\xbe\xfa\x95\ +\x95\xaf!+\x9f[\xf1\xfc\x0b\xcb\x1b\xbb\x0d\x00v\xcd\ +G\xea\x9c\xaceV\xc5\xe7|\xac\x99\xfb\xba\x22\x80\xe6\ +\xe3\xa1\xc4M\x0a+\x01E\xb9]*(\xb5\xcf\x0aJ\ +\xec\xff\xc8/\xb6\xcf\xf2Kx_\xc6\xef\x00\x81B\x19\ +\xf7\xef\xb6\xbd\xb9\xddO\x00\xe0|w\xf3\x15m\x93\xc7\ +8\xa5\xf8x\x94\xa5\xbd\x09\xf1\x8ca\x1cc\x96\xc0\xf8\ +%5O[\xaa\xb6\xb4\x93\x96\xaa\xdd\x5c\x96Rp\xbc\ +\x96j\x17\xb3\xdc\x11{\xea\x18Jz\xde\xab\xad\xe8{\ +xf\xa5\xb2\xab\xaeXG\xfaIS\xdf\x07\xc6<\xc6\ +\x1d\x09'+\xc4%\xbb\xd6\xae\x88\xb6\x86\xab\xe3D\x12\ +\xe8s\x1cAa\x02Q\xd7n5l\xd6\xe6\xcaT\x14\ +\x95&\x104k\xcb\xb8\xb6Z\xc3l% L;n\ +3\x1a\xa6\xcd\x87{\xf4#\x01\x09H\xf5K\x88G>\ +\xcdnq\xf14\xae\xab\xefQgL\xed\xd8ueP\ +\x02\x01\x0f\xecR\x15\xc5)\x15\xach\xbf\x1f\xec\xf8S\ +\xff^$\xbe\x88\xd8\xc7\x80%\x12\x13\x93\xcb\xdb,\x0d\ +\xb7\x1bl\x1d\xb3\xbc\xa15\xab^\xbci\x9d\xfbol\ +\xe8\xf4G\x9bz\xf8\x17\x9b\x7f\xaa\x16r\xffl\xf3\x8f\ +\xff\x93M\xdd\xfb\x8b\x0d]\xff\xd5\xda\xb7\xbf\xb1\x1aY\ +\xf5\xc4#\xcb\x1b\xc1\xe2q\xfd*\x0a\x91\x07P\x9fb\ +\x9d\xc3S\xba\xf8\xce*\xd6\xbf\xb1\xaa\xad\xef\xadz\xfb\ +{\xab\xda\xfc\x06\x10\xbc\xb5B\xb5zQ\x06\x84\xe5\xa5\ +\x08\x00\x10\xb6\xd8|brn\x1d\x82\xc5\xe7b\xddx\ +\xa7\xcb\xf0\x93Kye\xf6Y\x1e\xca\xcf+F\xf4\x0a\ +\x18\x08Y\x97\x00\xc8\xe5\x02\xc8,^\xc3c\xf7\xda\xcc\ +\xaa\x0d\xac\x10b\xad\xe9\xe3\xd6]U\x10c\xa6\x1dC\ +I\xda4\xc2\xf5\xd4>.\xa5U\xedo\x08\xbb\xf0.\ +\x9d2\x1a\x06\xb4Q\xf5!\x9ay\xce\xeb3R\xcd\xc7\ +\x16\x19\xbbOv\x02\x7fqU\xc2\x00A \xe8\xd4v\ +\xb5U<\x01 \xc0K\xeb\x94\x96\x18\x1dh\xa0\xe3S\ +\xd3Z=\x05j\x81\xc7k'J*\xe8\xbaN\x11\x9f\ +a\x91\x02\xc3\xc5\x86JmLt\x00\xc0\x8au\x18t\ +:\xb9\xa7+-\x97\xebo&,`\x19\xb2\x0eu\xe7\ +\xcel\xbc\x10~\xe6A\xfc\xc4'\x1fq(]\xff\x0f\ +QQ\x8bV\xe5\xaa\x0e\x04\x00@\xd7U\x9f}\xed{\ +s\x84E[\xb9\xcaa\xb2e\x10B\xb7\xe9\x13 \xb8\ +\xadbJ\x15\xc9\x8f\x89\x85\xc9\x95\x9d|W\x8f\x05Z\ +\xd5\x81t\xd9\xaa\x16\xae[\xcf\xd1\x1b\x9b\xba\xff\xb3-\ +\xbf\xfcO\xb6\xf9\xee\xdfl\x0bY{\xf5/\xb6\xf0\xc4\ +\x03\xc1\xe0\xf5?Z\xfb\xce\xf7V\xb7\xfc\xc1\xcag^\ +X\xd1\xc4C,\x1b\xb7\x8f\xe4M>\xb4\x82\xd9\x17V\ +\xb2\xf4\xb9\x95_\xfdhU\xdb?Z\xf5.\xb2\xf3\xd1\ +*7>w\xd3\xday\xea\x0d\xd8\xb5\x0e\xd0\x89\xa9J\ +\xab\xc4\xd8\x0b\xb0^\x01!\x8f\xf8\x8e\x87\xd2\xb6\xf6\xcb\ +(Z\xcavR(\xe1wE\x00E\x8d/\xd4\x99\x9c\ +X\x9fX\xa6\xe6\xd0=\xa4\xb1\xfd\x96\xceX\xc8K\xfa\ +[\xe7,\xb3c\xd5\xed\xb1P\x15\x93\x8aB$\x01\x85\ +\x1eD\xb3\xae*V\x8dL\xdc\xb3\xfc\xb9\xe7V\xac\xb5\ +\x86\xb5/\xadd\xf5\x0b+Zz\xc7\xef^\x98\xaa\xab\ +\xd5\xa2.\xa4\xa6\xdb\x9a\xacS}\x81&\xf0:5\xdb\ +\xab)\xfc%\x8bq\xdd/5\x1f\xde\xef\x1dk\xaa\xc9\ +\x1eW\x97\x8e\xabH\xc5\x92\xbdmH\xda\x90\x88\x97\x10\ +\xc9@1\xeaJ\xe5\x1a\x16a\xc9N\xb4\x95\x5cn\x1f\ +\xe5\xeb\xbc^\x95\x85\xf9\x897\x01$\xb3\x05i\xf6$\ +\xd0\x041T\x01\x86\x00@\xacW\x9f\x80\x14\xc0\xe5\xfa\ +\xef\xd6\x9cw\xc1\xc2[\x88\xc4x\x1b\x17`\xd3\xb5\x0c\ +\xb0\x03\xc2\x10\x96\xc1@\xab\x97\xaf\xd2\xc3r\x08\x90\xd2\ +\x1dBS2\xe45\xb5\xae\x17\xee1d\xd9=sV\ +1{h\x03\xc7/m\xf9\xe9\xcf\xb6\xf3\xfeov\xfc\ +\xf5\xbf\xd8\xe1\x97\x7f\xb7\xf5W\xffds\x8f\x7f\x03\x18\ +\x7f\xb2\xd1\xdb\x7f\x86\x1f\xfcf\x1d{?[\xe3U\xbc\ +\xc1\xf2{+\x9b\xf7\xe6\xd3\x8bt\xf6\xde\xe2{+]\ +\xf9\xda\xca\xae~o\xe5\x9b?X\xd9\xe6\xb7\xbc\xff`\ +%+/\xadp\xee>dP\x99\x80V\xd9\x96\x1c\x11\ +L\x86Y'\xc0\xd6\xe3\x8aa\xeeE\xb0\xf5\x22b\xb8\ +B\x83\xbc\x03\x96-\xa6\xaf\x8e&jX\xf1\x0f\xf2\xd6\ +\xe7\x9e_\x86\x90\x81\xe7\x0b0\x86\xd9(%\xa7g\xc3\ +\xf2\x06\x0f\xac\x88\xb0\xa4C5K\xa6\x1fX\xf1\xf4#\ ++\x82\x84\xea\x90\xa8\xbc\xf1\xfbp\x16\x9dJ\xaa\xc3\xa3\ +^[\xd9\xea\x07\xab\xe4\xfe\xaa\xf0R\x95\xdb\xdfY\xd9\ +\xc6\xd7V\xb4\xf2\xfe\x13\x08\x94\xc1d\x13\x1eT\xec\x93\ +\xd9{@\xc6\x06\x90H\xdf\xd5\xe9-FK\x8b\x92\x5c\ +\xbdB\x88t\x14Z\x16)\x8e\x90\xa2\xf9~\xafF\xcd\ +[\xf9K\xc3\xa2\xd3\x9b\xd4C\xe7\xbc\x8f\x8e\xdc=\x1e\ +\xc3\xc5|Y\xbe\x0aBu\xaaV\x87\xb2\x00\xf2b\x80\ +\x94\xc9wh.Z\xb5\x81\x22M>\xbeK\xd5+\xa9\ +R8^%\xd9\x91P\xb8\x07a\xc5\x01Lupn\ +\x87\xeb<\xa2-O\xbc\x0a\x0c\x80\xce\xb1W\x97!\x88\ +\xa3\x88\xa8B^k\x19\xc4\xba>>\xd3\xc7\xfd\x0dr\ +\xcdI+\x1e\xdf\xb0\x9e\xbd\xfb\xb6\xf0\xe0\x83m\xbc\xfc\ +\xde\xf6\xde\xfdd[\xaf\x7f\xb0\xb9\x07_\xd9\xf0\xe9\x17\ +\x84\x86\x8f6t\xf6\x8b\x0d\xdc\xfa\xcdz\x00A\xfb\xc1\ +/\xd6\xc4\xc0\xd5`A\xe5\x00\xa1\x84\xb8_\xb2\xf2\x85\ +\x95\xae\x7fk\xa5\x1b\xdfY\xc9\xc6\xb7V\xb4\xf6\xc1\x0a\ +\x96_X\xee\x9c\x8a8U\xc4\xa2\x85\xa3],t\xc3\ +2\xdb\xd7\xf0x\xb0\xecZ\xf2\xfd\xea\x09\x94\xab\xedm\ +J\xb7\xc4gd8\xda\xc8y!c\xa6^\xca:\x80\ +J\x9e\xd3\xa7\x9eCx\xe0l\xc6L\x8aW\xef\xdf\xb2\ +\xc9S\xab\xc6\xcb\xd4\xaf\xbd!{\xf9\xc2\x9a\xb7\xbe\xb6\ +\xa6-\xd5=|c\xf5\xeb_Y\x0d\x0a\xafZA\xb0\ +\xf6\xaa\xf5\xaf\xadz\xf3\xa3\xd5\xec\xfc\xe0\xbcT\x15\xaf\ +\xe5[\x1f\xadd\xfdK+\x5c|ky\x00:g\xe2\ +\xb1\xa9\x862\x9bP\x97\xad\x22\x95\xe1\x1b\x08\x1el\xe4\ +\xba\xc5\xe8(2\x15|\xe4\xcf=\xb2\xfcY\xefT*\ +\xd5\xbe\xab\xc6N\x85\x94Z\xb7\x16\x18$\xaa\xb3\xcbt\ +\x8b%0L\xadY+\x07V\x9e\ +B'\x89\xe7\xe9g\x17W\xdb\ +\xa7\xb9\xcb]\xc8DP\x04* 0\x85\x1c\x05E\xa0\ +\xc4\x04\xde\x03!\x00\xe7%8\x9fB\xaa~\xccn\x05\ +h\xc2x\xdaF\x04\xd49$7\xd1\xdf\x06\xf6\x9d\x12\ +h\xf2\xec\xbe \xe5e\x08\xe0}\x04\xfd\x1d\xb2\x03|\ +\x00q'\x04\xd2\x80\xc6\x0e\xc6\xa4[\x8c\xa9r\xbff\ +\xd1\xb7\x01\xbcr?\xa1\xbe\xcc\x1a\xcb\xe4\x9c\x8c\x00\x9a\ +y9\xa0o\xa1?\x06\x197\x87\x1d\xe8\xea\xce!\x1b\ +n\x12\xed7\x12<\x8b!\xf4\x880\xcb\x04\xf4c\xc8\ +$\x10\x12\xde+<\x9e\x9c+^$$\x91\x18\x02G\ +\xacg\x81\x1e;\xd0C<\x02j\xd0\xc3\x90\xab\xc03\ +\x11\xf1\xb2*\x22\x1c\x91\x9a>;&\xf0m\xd2\x89X\ +\x14X\xf3\xa3\xee\xb3\xecdB\x8e\xd9\xb9\xd1\xd6\xc9@\ +>\xfa\xb5\xf3\xf5\x84\x90\xcf\x8a\xc0\x84\xdb\x87\x8e\x0f\x16\ +\xde\xdbiw3\x00\xbd\x0aI\xd9\xbe3\xc3\xe1,\xfd\ +\x8e\xa3\x96G\xbb,\xd8#y\x03d\xcc\x04\xf6LI\ +5\x15\xf4\x18\xcd\xc8!\x81&\x13x\xb2\xb0G\xa8\x98\ +\xd7\x97J\xea\x93v5\xda\xdbu\xf0f\x14\x07\x92\xfc\ +\xb1y\xcd\xadt\xa3\xf6\xa1\xb7\xd0\x1e\x08\xf6HR9\ +\x17\x9d.Q\x88JMX\xe3E\x11\xc8\xf9\x91\x84\xf7\ +6\x92\x90d\xdf\x972\x84\xd10\xd7\xf9\x22\x02'^\ +\xd7?\xed\xed\x0f\xbc\xcbE!\xdb\xbc\xce\x86SF_\ +w\x8ev\x01Hy\xe1\x9cN\xb5.\xcf\xf0\x0d\xfa:\ +G\x83\xaa\xe2\x16:\x1d\xdb\x0d\xe0\x5c\x0f;d\x81\xc7\ +k\xf3\xee\x13\xb6\xb6_\x18x\x11B\xac\xd8\xfc.\xa5\ +\x1f`t\x8aZ\x1d\xe5v\x8c\xd0M\xa6#$\x9db\ +1&\xcfu\xde\xb2q\x9f\x90\x9d\x86\xbe\xbd\xe8<\xc7\ +\xfb2\xf7\x0f\xe6\x1c\xe5c!G\x1c\xdd\x88\x0e\xe8\x1a\ +\x85\x8f\xea.\xef\x842G\xf0z\x99e\x88\xdb\xbc\x96\ +\xabn\xecx\xf1_\xa0JA\x0d\x0a\x0d\x0a Icons / Noti\ +fication / Error\ + - black\ +\x0d\x0a \x0d\x0a \x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\x0d\ +\x0a\ +\x00\x00\x01u\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01\xd0\xa2K@\xfc\x1f\ +\x84\x81\xfc\xff06\x10\xbf\x02\xf2\xe7\x00i9Z\xf8\ +\xd8\x02\x88\x9f\xa3Y\x08r\xc0_4>\x8c\xddHM\ +_G\x00\xf1?\xa8\xe1 \xfa/\x0c\xe3\xe0\x83\xd5\x02\ +\xf1\x1a \x9f\x91R\xcbA>\xff\x87\xc7\xc7\x84\xf83\ +(u\xc03\x12-\xc4\x16%n\xe4\xc6{>\x11q\ +N\x8c\x03\xae\x93\xeb\xfb\x8b0C\x09\xc49!>\xc8\ +\x0c}r\xf29H\xe3?\x0aC\x00\xc6\xaf!\xd5\x01\ +\x0e\x14Z\x88\xce_C\xaa\x03B\xa8\xec\x80\x03\xa4\xc6\ +\xbf\x0f\x09\xf9\x9e\x984\xb0\x95\xd4\x10P\xa7r\x08L\ +\x22\xd5\x01l\xd0\xb2\xfd?\x95\x1c\x10@N90\x9b\ +\x0a\x0e\x00\xe5\xa2\x9f@\xccDN9 \x0d3\x84\x82\ +4\x00\xd2\xdfBIQ\x5cOa\x08\xdc\x01\xd2\xcc\x94\ +V\xc5k\xc8t\xc0\x1b _\x99\x1am\x01F \x9e\ +\x81\xa5\x01\x82+\xceAl\x90\xcf\x95\xa9\xdd\x1ar\x07\ +\x1az\x13\x9a\xaf\xe1\x8eA\xe3\xff\x04\xc59\x10\xb3\xd0\ +\xb2=\xa8\x0f\xc4\xb5@\xbc\x16h\xd1! \xbd\x1d\x88\ +\xa7\x00q \x90\xcf\xcc0\x0aF\xc1( \x11\x00\x00\ +\x90\xbf\xd0E\xdf\x04S\xa8\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x02\xaa\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x02qIDATx\xda\xedWMHTQ\ +\x14\xd6q,\x0c\x1db\xd0d\xa86\x814!\x11A\ +\x90\x92H\x14\xc4@\xaef\x11\xb5\x10l'\xe4l\xc4\ +\x85\xcb\x1aA\x88@\xc2E0\xad\x1c1h!\xd3\xa6\ +\x7f\x9a\x84\x90hg$$\xb6\xa84]\x88\x039\x8b\ +\x18)g\xf2;\xf0=8\x5c\xdfLo\x86\xb9\xd2b\ +\x1e||s\xcf\xb9\xef\xdes\xbew\xce}o\xea\xea\ +jW\xed*\xe3\xba>0\x14\xbbq3\xf6\x07w\ +\xe9q\x8d\x8fN\x00\x15+\x80\x88E\xbe\x0f\xe0\x97\xe0\ +\xa8\x11\x80\x1f\xf6\xcfF\x9f;\x9c\x01\xb7x\xad\x01\xd8\ +\xafJ{\x82g\xc0>\xbd\xc9)#\xab\x84\x11D\x08\ +\xf8i\xcc\xd9\x06\xcex\xad\x01U?\x82M\xa0IG\ +\x16fF\x1b|\xdb\xc9\xa4Qc\x81n\xd8w\xd4I\ +\x17\xf5\xda\x05\xac\x15\xb9oM6\xe7>M\xfa\xe60\ +#{\x0c\xf4\x02\x05\x8e{\x8cM\xae\xd1>\xec\x92\xa1\ +S\x039\xad\x00~\x1f\x06\xb6\xe8\x93\xb5\xbf\x01\x1b\xc5\ +\x14Hq<\xa0\x14\x09\x19\x1b\x9d.QK\x17E)\ +c\xfe\x0b*s\x8f\xe3\x1f\xa5\x14H)\xdb]\xda\xa4\ +\x00\x0fU\xd8A\x0f\xb8\xc6;\x8e}\x12\xc0?\x15P\ +\xf6I\xda\xbfK\xa1\x96\xd1UG0\xff53\x9f\x07\ +\xfbio\xf0\xac\x80\xf2\xddR5\x91\x04\xce;\xad\xa7\ +\xe6\xc8\xc2\xb2\xe9%9%\xd5\xf7\xc1}\xa0\xde\x98\xe7\ +]\x01\xe5\xef\x90\xd7\xadz\xcfK\xab}\x05/\x81\x97\ +\xc1\xeb\xf2\x1aV\xfeW\xe0\xb3.\xeb\x94\xaf\x80\x91\xe9\ +1`\x10\x90#y\x1e\xf8\x04,\x00r4'\xe4[\ +\x10h/q\xbf\xd4\xc0\xaa\x9b\x02'\x9d\xc8-\x7fU\ +\x1f\xe49\xb1G\x01\xe9\xe1\x1d\xf6\xf0\x09Ko\xd2\x03\ +\xc0C*\xbd\xe06a\x92\xcf\xf0\x17x\x0e\x9c\x06\xa7\ +\xab\xc4o\xc1\x19U#}\xc5\xa2\x1c\x01\x16Y\xa9\xab\ +U\xc6\x0a\xf0\x1e\x88xyV\x8d\x98\xd8Xe\xf6\xff\ +\x97\x7f\xf7v\x01&\xc7\xb3\xa0\x90\xee\xe83\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x02\xae\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a Icons / Noti\ +fication / Warni\ +ng\x0d\x0a \ +\x0d\x0a \ +\x0d\x0a <\ +polygon id=\x22Tria\ +ngle\x22 points=\x2212\ + 2 22 22 2 22\x22><\ +/polygon>\x0d\x0a \ + \x0d\x0a <\ +/g>\x0d\x0a\x0d\x0a\ +\x00\x00\x06\x8c\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a Icons / Icon\ + Grid\x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \x0d\x0a \ + \ +\x0d\x0a \x0d\x0a \ + <\ +use xlink:href=\x22\ +#path-1\x22>\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x01\x12\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\xd9IDATx\xdac`\x18\x05\xa3`\ +\x14\x8c\x82\xa1\x06\x22\x13s\xd9\x818\x15\x887G$\ +\xe4\x5c\x00\xd2\x87\x80\xb8\x1b\x88Uhn9\xd0BG\ +\xa0Eo\x80\xf8?\x08\x03\xf9\xffal(\xbf\x87\x96\ +>w\xc2b\xe1_4>\x88^F\x0b\x9f\xb3\x03\xf1\ +k\x98\xa50\x0c\xe4\xff\xc3\xc2\x07\xa9\x09\xa2\xb6\xefS\ +q\xf8\x18\x17\xff4\xb5\x1d\xb0\x91D\x07\x800\x0f5\ +\x1dp\x9e\x0c\x07(R3\x0d\x1c\x84&\xb0\x7fD\xa4\ +\x81\xbf\xd0t N\xcd\x10\xe8\x221\x04^\x011#\ +5\x1d\xa0\x04\xb5\xe0\x1f\x91\x0e\xe8\xa0E9\xd0Cd\ +9\xf0\x0c\x889hU\x18-\x83\xc6/,4\x90\xe3\ +\x1cl9\x90V\xa1u]\x10\x04\xca\xe7hE1(\ +\xce;\x81|\x0e\x06z\x01P>\x07Z\xa8\x08\xa4\xc5\ +\xa9\x9a\xe0F\xc1(\x18\x05\xa3\x80\xde\x00\x00*\x106\ +\x97\x13c\xdc\xaf\x00\x00\x00\x00IEND\xaeB`\ +\x82\ +\x00\x00\x01\xf5\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01\xbcIDATx\xdac`\x18\x05\xa3\x80\ +D\x10\x91\x90#\x06\xc4\xd7\x80\xf8jdb\xeeU\x10\ +\x0d\xc47\x81\xb8\x84.\x0e\x00Z\xaa\x02\xc4\xffA\x18\ +h\xe9\x7f\x18\x1b\x88\x17\xd1\xcb\x01\xcaH\x0e\xf8\x8b\xe4\ +\x80\x05#\xc3\x01@KU@A\x0f\xb5\xfc\x1f\x88\x86\ +\xf2\x17\x8c\x98(P\xc4\xe1\x80\xf9\xb4\xb4T\x0e\x88u\ +\x81\x16j\x01\xe9(\x1c\x0e\xd8\x0b\xe4k\x00i\x1d(\ +f\xa1V\x9c3\x02\xf1mX\x96\x83\xc5?Z\x1a\xf8\ +\x8b&\x0f\xc2\xce\xd4\x0c\x01\x17\xa8\xe1\xff\x90|\x8c\x1e\ +\x02\xc8\xf2\x1bi\x11\x0d\xe9h\x05\x0f6\x07\x80\xe8\xab\ +\xe4Z\xc0NDtL'\xe0\x80W@Z\x92\x80=\ +L\xd8\x0cV\x06\xe27@\xc9\x07@\xba\x06\x14\xefx\ +\x1c\xb1\x17K9\x00\xa2\xff\x00\xf9\x16\xf8\xea\x10\xa0\xfc\ +J \xfd\x1c\x88\xeb\xd1]\xa5\x87V\xb6o\xc7\xe3\x03\ +\x1e \xbe\x8f%\x04\xa2\xf0\xe8\x11\x02\xe2gH\xe6\xcf\ +CW\xa0\x8b\xc5\xc0\xe9x\x0c\x94\x05\xe2\x1fH\x06\xb6\ +\x11\x08\xf6#h\xe6\xcf%\xc6\x01 ~\x00\x1eC\x9d\ +\xa1q\xbe\x94\x80\xe5yX\xd2\xcc\x5c\xf4\xf8\xd1\x85\xfa\ +\x069_\x83\xb2\xd47 \xadFA\xce1FJ#\ +\xb04\x03r\x08\xd1!\x00\xa2_\x03\xb1\x12\x99\x96\x7f\ +\xc0\x91k\x88v\x00\x8c\xff\x05\x88#\x88\xb4\x98\x0b\x88\ +\xab\x81\xf8/\x9er\x83d\x07\xc0\xf8\x17\x81t\x19\x10\ +\x9b\x03\xb1(\x90\xcf\x0b\xa2\xa1\xb9(\x06T%\x03\xe9\ +7x\xf4\x93\x94\x06\xfebI\x13\xc8e\xfd\x7f<|\ +l\xfa\xc9J\x03\xb4\xe2c8@\x9b\xce\x0e\x98\x8e\xad\ +t{KDeC-~\x18\xb6\xb2:\x84\x848\xa6\ +\x84\xbf\x1e_m\xa7\x04TT\x07\xc4\x13\x81x\x02\x90\ +?\x01D\xc30\x85\xfc. \xdfo\xb4k\x87\x0e\x00\ +\x0bqL\xd4T\x13W`\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x00\xd5\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00jIDAT8Oc\xfc\xff\xff\ +?\x03%\x80\x09Jc\x80\xc522\xffA\x18\xca\xc5\ +\x09\xb0\x1a@\x8cF\x18\x00{\x01\x9f\x86\xd8'O\x18\ +\xa1L\xac\x00\xa7\x17`\x80\x90k\xc0\x06\x10\xb2\x05\x9f\ +!\x04]\x00\x03 C\xb0\x19D0\x0c`\x00\x97+\ +\x09\xba\x00\xa4\x11\x9f\x17Q\x0c \xa4\x18\x1b\xc0\x99\x12\ +a\xde\x22d \xed\x922\xb1`\xc8\x1b\xc0\xc0\x00\x00\ +\x1d[2\xc1\xc0t\xc2\x0d\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x00\xeb\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\xb2IDATx\xdac`\x18\x05\xa3`\ +\x14\x8c\x82Q0\x14Adb\xae:\x10O\x8eH\xc8\ +\xd9\x0f\xa4\xd7\x00q8\xdd,\x07Z\x1a\x0f\xc4\x7f\x81\ +\xf8?\xd0\xe2\xff \x1a\x8a\xb7\x00\xf9\xcc\xb4\xb6\x1c\xe4\ +\xf3\xbf \x8b\xa1\x96\xc3\xd9P~\x1b\xad\x1d0\x19\xcd\ +Bt\x07\xbc\x05\xd2l\xb4t\xc0~\x02\x0e\xf8\x0d\xa4\ +\x15i\x99\xf8VA\xe3\xfb/\xd4\xf2\x7f06\x10\xff\ +\x03\xf2\xbf\x02i\x11Z\x86@\x18\x81\x108H\x8f,\ +\xb8\x19\xc9\xc2\x7fH\x0e\x00\xf9^\x95\x1e\x0e`\x06\xe2\ +6 ~\x0b\xb4\xf0\x0f\xc8b >\x04\xc4\xaat-\ +\x8c\x80\x96\xb3\x81\x12\x1c\x90\x16\x1d-\x9aG\xc1(\x18\ +\x05\xa3`H\x03\x00\xac\xe7\x98*\x92\x10\x95\xa4\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x03\x0b\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x02\xd2IDATx\xda\xed\x97MH\x94Q\ +\x18\x85\xd5i\x0a-\x17\xba0\xcc1!\x88\xc2(\x0b\ +\x92\x5cLFn*\x88Z\x05J(\xd3&*4B\ +\xfbq\xd5\xa2\xc2 \xa4\xb2_Z\xb4\x88\x8aP\x82)\ +\x22\xab\x85\x14Q\x8b\x16\x81\xf4\xb7h!\x16\x95\x05-\ +\x8a\x8a\xd2t\xea\x1c8\x1f\xbc\xdc\xee7)\xe9H\xe0\ +\xc0\xe1r\xee\xf3\xcd\xbd\xef\xf7\xce\xfdy'+k\xea\ +\x83O\xdd\x96\xa6\x1d\xd0\xc1\x10\xb6\x11\xba\x09\x1d\x0e\xe1\ +\xab\xa0\x1b\xd0\xb1\xdaDc\xae\x87/\x86\x92\xd09\xf0\ +\xc2\xb0\x00NC\xbf\xf0@\x9b\xed\x87\xaff\x7f \xf8\ +\xf3\x0e_\x84\xfe\x94\xe1\xb7\x1c>\x1b\xfd?\x0c\x7f\xe2\ +\x0d\x00\xa0\x03\xe2C?\xed[\xc0?b\xbf\x94\xd2 \ +s\x0dOzx\xa5\xe1\x1d\x86\x07A\xac\xf7e\xa0Z\ +\x90\x0f\xd5\xaa\xaf0\xf8\x12t\xd9\xf0f\xf1\x5c\xf8\xef\ +\xe2]\xd0\x90x\xbbx\x04\xfe\xb5x\x0f4 \xde\xe9\ +\x0b`:\xf4N\x0f\x5cT\xdff\x13\x00S\xfdL\xbe\ +G\xbc\xc6\xf0\x95\xd0]\xf9\x97\xe2\xe5\x86\xd7A\x17\xe4\ +?\x86\xad\x83.=\xd0'\x7fI\xfe\x8d\xfcQ\xf9\xcf\ +P\x0etD\x9eY\x88@\xbb\xcd\x84\xb3\xa0\x9d\xc6\xe7\ +C\x9b\x8c/\xf7\xad\x83D\x90fh!\xf4J\xbeS\ +|\xad\xe1\xcb\xb9\xa0\xe4\x1f\x88/3|\x03tG\xbe\ +O\xbc\x18~D\xbc\xd5\x97\x81\x22\x13a\xb7\xf9\xcd\xeb\ +\xc5\xa3\xf0#\x1e\xbeO<\x1b\xfe\xbd\xf8}\xe8\x9b\xf8\ +I\xf3\x92\xbd\xe2\x8f\xc3v\xc3Cg\xdbq\x0b\xe5\x1b\ +\x9et8\x03*3\xfc\x84\xc3\xd9V\x1a\xbe\xc7\xf0\x12\ +_\x00+\x9c\x01\xf6:|\x01\xfa\x07\x0d?\xee\xd9\xf7\ +\x9f\x0c\xbf\xea\xf0(\xfa?\x88\xc7\xc2\xb2\x10\x87\xcep\ +M\x84\xf0\xa5\xd0)h;\xd3\xee\xe1\xf3\x98\x09\xa8\x85\ +\xbb\xcb\xc3K!\x9e\x9a\x05\xff\xfd\xfd\x11,Z\xae\x93\ +\xc8d\x04P\xaaE\xc8\x00\xf22\x1e\x00&\x8fa\xe2\ +a\xee\x14\xdf-\x98\x89\x0c\xc40\xf1\xb020)\x01\ +\x14+\x00\x1e\xc5\xd1L\xa5\xbd\x0c\x93q\xfbm\x85Z\ +\xb5\x06\x86\xa0&\xf6\xc1oC[0\x91o]\xe5\xdc\ +\xef)\xd6\x01\xc6\x0frmLt\xea\x1b\x9c\xa3\xd8\x9e\ +\x9c\xf1L\xfd\xfe\x87<\x01$2\xbd\x08\xaf\x99\xdb\xb3\ +}<\x16X\xceX\x9f\xc7\xc4/\xd0v\xff\xf3\x5c\x18\ +\xe8\xac\xb9>\xbfB\xcfY\xbb\xc17\xa2]\x92f\xa0\ +\xec4\x19\xca\x03\xafA{\x00\xba\x0d\xf5k\xdb\x06Y\ +\x8b\x87\x05\xe0\xde\xe7\xd4\x80J6\x06\xc4A\xe7Cs\ +T\xe5P%\xac\xffY\x09\xa1\xdd\x0f\xdd\x83\xbe\xa4\x19\ +\xef\x8f\x00\xaa\xa0]\xbc\xffY\xfbA\xd7\xa1\xb7\x7f\x19\ +`\xb4\x9e\xe5\xfaS\xd5\x98mh[tn\x14\x8df\ +\xa1\xf1-\x1b\xa0+P\xbf\xa9\xf9l\x9d\xefz\x9e\x0b\ +\xbd,V\xe0\xd7\xa0\x9d1\x9e'\xe1L\x0cZ\x01\xad\ +\xe3\xff\x07\xf8z\xd6\x8c\xacx\xa1\xd5,F\xd0N\x9b\ +\xfa\xb3;\x96\xcfo\xe5\xb5\xdc\x8eP\x05\x84\xb2\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xe0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00uIDAT8O\xd5R\xed\x0e\ +\x80 \x08\xd4\xd6{\x83ON\x9d\x03\xe7,\xd0\xd5\xfa\ +\xd1m|(p\x0a\x9aE$\xbd\xc1\xa6\xf6\x82R\x8a\ +@t\xe9\xe2\x96\x00\x85D\x94u\x19b\x87\x1aOZ\ +-\xae\xc0\x0c\x98\xf94R\xfdQ\xa2\x18\xa49OI\ +\xdc!\xf6@K\xeeP\xc12;=\x8aWe\x09c\ +rTh\xd2Z\xb0\xa7\xb3\xeb\xea\xf6\x14\xeeO4\x92\ +\xd9\x93~\xf7\x95W\xf1{\x82\x94\x0e\xd4\x89\xe2k\x0c\ +\xdb\xee*\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00mIDATx\xdac`\x18\x05\xa3`\ +\x14\x8c\x82Q0\x0a\x86*\x88L\xcc\xad\x89H\xc89\ +\x07\xa4\xd7\x00\xb1\x14\xbd-\xaf\x05\xe2\xff@\x07\xfc\x07\ +\xd1@\xfc\x98\xde\x0e8\x03u\xc0_\xa8\x03@X\x85\ +n\x0e\x00Z\xbc\x1a\xea\xfb\x7f@\x1a\x84A\x8e\xe1\xa2\ +g\x08H\x82\x82\x1d)\x0aR\x07$!\x02\x1d\xa0\x02\ +\xb4\x9ck4K\x8e\x82Q0\x0aF\xc1(\x18\xd2\x00\ +\x00e^3\xba\x9ez\xe49\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x00\xef\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\xb6IDATx\xdac`\x18\x05H \ +21\x97\x11\x88C\x81\xb8\x18\x88\x0b\xa9\x88\x8b\xa0\xd8\ +\x05\x9f\xe5\xdc@|\x19\x88\xff\xd3\x18o\xc0\xe5\x80i\ +d\x18\xf6\x1b)\xe4\xf0a\x16 \xf6\x01\xe27P}\ +)\xd8\x1cp\x85\x0c\x07\xfc!1\x8aM\xa1\xfaVb\ +\x93\xbcFk\x07@\xed\xf9\x00\xc4\x9b\x06\xd2\x01\xcf\x06\ +\xda\x01\xcfG\x1d0\xea\x80Q\x07\x8c:`\xd4\x01\xa3\ +\x0e\x18u\xc0\xa8\x03\x06\xad\x03\xae\xd3\xa1Q\xca\x8e\xb3\ +i\x0e\x14\xdcK\x07\x07\xac\x83\xea\xeb\xc6&iEf\ +Gc\x11\x10/#\x80W\x01\xf1{\xa8\xfa\xaf@,\ +\x8e\xcb\x85\xd6@|\x08\x88\xef\x92\x80\x1f\x10\x89\xef\x01\ +\xf1\x16 V\x1c\xed\x04#\x03\x00u\xbb\x07\x04\xef\x85\ +8\x87\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xfe\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00\x93IDAT8O\xa5\x93[\x12\ +\xc0\x10\x0cE\xb1\x0d\xb6fs\xb55]G;!\x94\ +4\x8f\x0fg\xa6\xa3&rs\xe3\xe1\xaf\x18\x1fw@\ +\xc8w\xf5\xf8\xbf\x91k\xf5\xf0\xe1T$\x94\x98~\x0e\ +\xb4D*\xdcZ\xd0\x12J\xfa\x0a\x80[Z0\xe0(\ +\xb2\x8asn\x9b\xc0Z\x85Csh:\x18\xd0\xde\x07\ +\xe6\x1e\x0c$\x97\xa6\x03H\xd4Z\xdc\x04\xac\xc5\x1c\xe2\ +M\x1cmi\x82p\xacS\x80;\xe3\x15)\xdeZ\xe0\ +\x82tc!\x0e\xebp:\xe9\xf7@\xa9\xbc\xc2\xad;\ +\x7f\x8d8\xaap\xd6;\xce\xbd\xfa\x8fW\xbc\x9c\xc2\xae\ +\xed\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\x8b\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00RIDATx\xdac`\x18\x05\xa3`\ +\x14\x8c\x82Q0\x0aF\x01\x99 21\xd77\x22!\ +\xe7.\x90\x9e7P\x0e\xb8\x0bt\xc0\x7f \x0d\xc2\xa6\ +tw\x00\xd0\xf29 \xcb\x81\xf4+ \x16\x1c\x90P\ +\x00Zl\x0at\x84\xe0h\x82\x1c\x05\xa3`\x14\x8c\x82\ +Q0\x0a(\x01\x00\x9bf\x16\x9e7\xad\x98\xae\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x02\x02\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01\xc9IDATx\xda\xedW\xbbJ\x03A\ +\x14MLc)XJTD\x91\xd8\xf9\x016V\x82\ +A4I\x13AH\xc4\x80\x8d\x96\x96NZ\xbf\xc4\xca\ +GH\x14T,\xf4\x17\xfc\x89<\xf0E\xea\xac\x9e\x0b\ +we\x18fwgv\xb2\x09B\x16\x0e7\xb3\x99\xdd\ +{\xee\x9c3\x8fM\xa5&\x97\xe3\xb5_=M\x8f+\ +\xf1N\xb9z\xd2F\xec\x03g#M\x8e\xc4\x05\xe0G\ +\x81\x18U\xe5%\xc0\x03(\xe9\x80\xa2\x8f\xc4I \xc1\ +\xae\x92P%@Q$Uy\x81\x13\x84\x11\xf0\xf8w\ +}\xd8\x95\x17Yg\x8f\x92J\xd0\xb5\xbd\xa1z\x02\xd5\ +\x14C4\x8fj\x0bg\xb7[&\xd4y\xa2\x1e\xb7\xf2\ +=\x03\xcd\x07\x86\x9e\x10\xb6\x95\x97\x14=eDy\xc0\ +\xcd\x13\xe4vIsb\xff\x02l\x02e\xb4?\x1cG\ +DDU\xbe\xae<\xd0dR\xb3\xc0\x14\xdaY\xc4\xae\ +\xa3'*a\x04\x84\xf4\xc0\x1d'\xaf\x01\xf4\xa2\x0e\xfe\ +\x9fG$\xb4\x1d\x084\xc3\x08\xe4Y\xaf\x16\xb7\x0f\xa5\ +\xf9O\xb1\x0b,\x02s\xc0{\x0cOP\xff\xf3(\x0f\ +T8\x1ehf\x01\xc5\x1e\x8fB\x96HX\x8e@\xcb\ +\xd4\x88G\xca4R_\xf8M$h$|9\x0c\x08\ +\x5c\x99&\xaf\x19\xbe\xb0Gr .\xf8\xc6\x0c\xe9\x7f\ +mtp\x91\xf6y\xcf@S\xeaG\xc6\x9c\x016\x02\ +\xf6\x0a\xbawo\xb3\x00]\xc6p\xf5\x160\x1d Y\ +\xc3v\x05\x5c\xc1\x83_\x16\x04\x1eX\xb6\xe3\xd8\x9ak\ +<\xb0\x06|\x1a\x10x\xe6\xfey\xcdn\xd9p\xdd\x05\ +s@?`\x9e\xd3\xbdW\xee\xb7-\xad\xf7\xf6\x9aG\ +\x8c\xc4\x12\x91\xd0h\xfe$\x9d\x8a\xd5u\xe2f\xd8'\ +\xa1\x9c/\x07'xD\xcc\x00\xcb\x12\xa1?\xcd\xd1N\ +'q\x16$O\xbcq\x82\x8ct\xffB\x22p;\x96\ +\x8f\x13\xda\x1d\x81\xd5\xc9\xf7\xe1\xbf\xbe~\x01}\x06\x5c\ +\x02\xc1U\xc6\xe7\x00\x00\x00\x00IEND\xaeB`\ +\x82\ +\x00\x00\x00\xd0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00eIDAT8O\xd5\x91Q\x0e\ +\xc0 \x08Cq\xd9\xbd\x99'g\xe9\x06\x06q&\xea\ +\xcf\xb6\xf7SBLmC\x12\x11\x029\xe7{\xe8\xc0\ +\xcc\x09o\xa0\xba\xba\xd8T\x87\x89\x1fM\x1b\x00o2\ +UA\xc7\x8aa\x83\x88\x19.U\xf0\xec\xaa\x85^T\ +#&}\xbfBc\x00gs\xe7\xa3\xd6'~p\x05\ +$\xf3\xbb\xef]\xa1$X\x83\xe8\x04\x88\xd4H\x07\xd8\ +\xd9\x12\x13\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01/\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00\xc4IDAT8O\xa5\x92\xd1\x0d\ +\xc3 \x0cD\x01E\xcd\x10\xcd\x0e\xedB\x99.\x0b\xa5\ +;\xa4CD\x8d\x94\xf6\x5c\x83\x8ck\xc3G\x9fD\x04\ +\x0e\x1cg\x9b\xf8Z/g\xf8\x834\xdc\xf7\xc8\xf3\x8a\ +\xe1\xb6G\x0c^\xba\xa4c\x1d\x7f\x1c@t\x99&\xd3\ +\x99\x16N\xf8\xe4`\x1e\xcb\xd59\xfc\x11>\x1e\xe3\x89\ +\xc1\xa1@5\xa0C\xce\x8d\xf3s\x8b\x96\xcb\x0c9\x80\ +\xe2\xbcmf\xbep\xe3\xd5\x09\x90@\x0f\x12\xe1\xf48\ +T\xe8\xa7\xc0\xced\xde\x92\xca\x016\xcbT0\xd7E\ +\xd3\x14\x01\xb9\x19\x85\xe3p\x17\xf7%\xe6|[\xb7\xa3\ +\xb8\xc5\x81\xae\xb4\xb6nu\x02\xed%\x01z \xaa\xd7\ +\xba\xe2\xf8o\x89\x90@\xeb\xa1H\xac}U\x17$\xad\ +\xdc%\xae\x80\xc4\xb2\xfe%\x847o\x1ay\x8cB\x12\ +\xd5\x15\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xe6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01\xadIDATx\xda\xedW\xcdJ\xc3@\ +\x10\xeeK\x88 ^D\x11\x11\xd1\xda\x1f\x22^l\xd2\ +J\x9e\xa1\xde\x0az\xb3\x82\x17\x05\x1f@(\xf8\xb0\x05\ +\x1blS\xd38\x1f\xec\xc2\x12v6\xbb\x9b\x04`\xb8]\x8e\xe6\xb0\xd8\xa2\x0cO\xb8\xcf\x02\x12\ +\xde\x97\xf4\xab\xc9y\xcaq;\xc8\x0a\x8c\xc9\xd4\x04\x1c\ +9\xb1u\xe0\xd4\xc0\xfd\xa1E\xcdp\xb3\xe3\xc6%\xff\ +\xcfJNe\xce#\xdb\x01&\xe6\xbf\xaa\xbf\xf0\xa9\x83\ +'\x91\xd3\xad\xeb<'\xf9+DB\xe8/\xaat\xc2\ +\x19]p\xe0\xc9\x1f\xf8eu\xd1\xfe\xb7h\xd7\xbf_\ +\x7f\xb3\xb1^\xeamg8\x00\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x00\xd0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00eIDAT8O\xd5\x91Q\x0e\ +\xc0 \x08Cq\xd9\xbd\x99'g\xe9\x06\x06q&\xea\ +\xcf\xb6\xf7SBLmC\x12\x11\x029\xe7{\xe8\xc0\ +\xcc\x09o\xa0\xba\xba\xd8T\x87\x89\x1fM\x1b\x00o2\ +UA\xc7\x8aa\x83\x88\x19.U\xf0\xec\xaa\x85^T\ +#&}\xbfBc\x00gs\xe7\xa3\xd6'~p\x05\ +$\xf3\xbb\xef]\xa1$X\x83\xe8\x04\x88\xd4H\x07\xd8\ +\xd9\x12\x13\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\x05\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00\x9aIDAT8O\xa5Sm\x0a\ +\x80 \x0c\x9d\x12\x05]\xa6\xdf\x9d\xa6\xb3u\x9a\xfe\xd6\ +e\x82(\xb0&j*\xfb\x08z0\xd6\xb4\xbd\xf7&\ +j\x9cs\xf0\x07\xf6\xda:\x92a\x99z\x87\x11J\x16\ +\xb6\x19\x0e\x13\xbe\x13\xa4F\x14\xccE\xfd\x08R\xc38\ +\xefI\x00\x1bkA\x1b2\x8b\x9c\x9cr\xeb\x09r\x15\ +\x0a\x92C\xd5A\x04\x92PD\xea\x19Dp.U\x07\ +\xd8(\x8dX\x10h?S`ob\x1cK\x22\xf4\xf7\ +\x01\x090\xce\xb5}\xd2[\xd7\xc1\xed\xb3\x9b_\xd7\x8a\ +\x22\x0f\xcdQ\x8c\xff\xaf1d\x11\xdc\x8b\x05\x00\xb8\x01\ +\xef\x94\xd6@\xd4\x8c1\xa7\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x01\x88\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01OIDATx\xdac`\x18\x05t\x00\ +\xd1I\xb9<\x03iy\x03\x10\x7f\x04b\xc9\x81\xb0<\ +\x0c\x88\xffC\xf1m f\xa7\xa7\xe5\xe6@\xfc\x17\xc9\ +\x01 \xbc\x9b^\x96K\x03\xf1k4\xcba\xb8\x9f\xd6\ +\x963\x02\xf1\x1d\x1c\x96\xc3p<\xad\x1d\xe1\x01\xc4\xbe\ +@\xec\x0e\xc4\xb7\xa0\x966\x01\xb1\x17\x10\xfb\x03\xb1\x12\ +=\xd3\xc2\x09\xa8\x03<\x07*\x1b\x9e\x84:\xc0\x87X\ +\x0d\xea@\xcc\x0f\xc5\x1aP>>,\x0f\xc4,@,\ +F\xb1\x03\x80\x8av@\x15\x17B\xf1\x7f\x22\xf0U \ +V\x80\xb2\xb7\x90\xed\x00\xa0\x02\x0e \xfe\x0aU\x9c\x0b\ +\xc5\xc48\xe0\x1c4\x14`|\xb5\x81v\x80\xfa\xa8\x03\ +\xd0\xcc=\x0b\x15\xf7\x1d(\x07\x14\x03\xf1Jtq\xba\ +9\x80\x94\x02c\xd09\xa0\x80H\x07\x5c\xa6\x85\x03\xf2\ +\x80X\x18\x88\x83\x818\x10\x0f\x0e\x82\xd6\xfb\x92\xd4v\ +\xc0cP\xe3\x01\x88w\x11\x89OQ\xdb\x01\xc4\xe0\xdf\ +@\xfc\x0b\x88\xff\xa0\x89S\xc5\x01_\x09\xb5d\xa1\xd1\ +S\x01\xc4\x86Hu\x08\xd5\x1c\xf0\x05\x88E\x08\xa8?\ +\x04U\xdbF+\x07\x08\x10P\xbf\x1e\xaa\x0e\x14\x0a\xdb\ +\xa9\xed\x00P\xfc\xd6\x03q\x11\x01\x5c\x0c\xcd\xb2wp\ +\xd5\x86\xa4:\xa2\x05\xea\xabo\xd0\x04\xf6\x9b\x00\x86\xa9\ +\xf9\x0eu\xfcvj4\xa1\xd8\xa1\xa1A2\x1e\xed\x84\ +\x92\x03\x00\xf9}\xdcv\x87\xa5\xe0^\xe3\x07\xa0\xd1\xc6\x9f\xc8\x81\xcf\xc5T\ +\x81;\xb0\xdb^0\x12\x9ac\x5c\x0f%\xa0N\xf1\x07\ +\x9a\xf3\xaa\xbd`\x18\xbf\xca\xb8\x06\x1a\x0c>+n\x93\ +\x03o\x8a;\xa1\xa1\x7f;\x80?\xe4@\xb7c\xb0\xdc\ +\xc6\xae\x07\xcf6\x1e\x07^\x02;PL\xa7\x16\xc3\x96\ +\xd8\x87\xcc\x04\xdfd\x9c\x04/\x83\xfa\x8asq\xcdg\ +\x14\xc5)`\xce\xe7\xe0\x0fW\xd8\xd85t\xc2\x98!\ +\x9d\xcf\x87X\xce\xe7\xc2^nNN\x05O3\x1e\x0b\ +\xbe\xd9\xd6DB\x5c\x10;\xf0\xac\x85\xfc\x12h\x96q\ +\xad\x1c<#~[s^\x123\xccC\xa0[-\x05\ +\x8b\xa0A\xe0\xd3\xe2\xbdr\xe05\xf1o\xd0\xc5\xee\xc0\ +A{a\x09Tg\xbc\x81\xf94\xee\xd0\x9c\xc3\xf6\xc2\ +\x22\xa8\xc9x%4\xc6\xf8\xb0\x1c\xf8\xd2\x16\xe1X_\ +\x03\xdcR/b\xb0A\xdc\x07\xda\x04\xde\x0e\x9b\xa7\xb1\ +%\xe0\x97aS\xe2\x09\x5c\xbc\xb0\xf5\xe2~\xe0-\xb0\ +\xdbx-'\x97\xe3\x9as&\x8a+q\xbd\x07\xaa\xf9\ +\xa7Z\x90\x888\x0b7\xf6\xf61pn\xc4\x89\x88{\ +b^v\xf4\x9c~\xff\xc5a\xb0I!\xee\xe0\x82\x81\ +J\xa1?\x15\xc2i\xd1\xbe\xdf$^-\xfeZk\xa4\ +\x1a\xdc\xa9\xf0\xa6\xe8\x08\xf8S\xf1:9\xb8>\xd4\x05\ +(\xc7\x1d\xf8\xc2r\x5c\x0d5\x1a?\x05\x0d7>\xa9\ +9G-\xc7\xe5\xd0\xc3\xc6\x5c7E\xc6\xed\x8a\xe87\ +\xb6\x06\x92\x1e:\x96\xd9S\x18\xdc\x0f\x9b\x09]\x0a\xf1\ +\xcb\xbe\x87-\xd5=\xcf\x83\x7f\x86\xbdG\xbc\x14\xfc\x13\ +,\x0bR\x0f\xd5\x09:\xf5-T\xc81\xed\x94_B\ +\xce\xc1w\x89_\xe0{2\xba\xcd\x0f\x9e]\x0b}\x05\ +\xafv\x88Y\xfb\xdb\xc0\x1f\xb2\x00il\x03\xf8\x08\xf7\ +\xbbU\xbd4\xec\xd3\xe2a\xe0\xfd\xb0\xef1e\x8aR\ +\x0b\xa3\xc2\xba\x22\x9e-n\x89\x1d\xf8\xccr\x5c\xc9\x83\ +\xc5x\xa3\x8aS\xe0\x13\x9a\x93\xb6\x1cs\xd1\xae2^\ +\xc7\x0aiLG\x99\xda\xa3\xb6\x06\x0a}\x0d<\xa1\x9b\ +\x99\x9f\x81\xd0\xd5a2\xec<(K;\x84\xdc\xaa9\ +[\xc5'\xa0\xfe\xd0\x8c\xe0$4Su\xe1\x07\xf16\ +9\xbdK\xdc\x01\x0d\x88\xa3P\x1c\x0e\x1a\xf1\x08\xf0\x18\ +c\x1eXe\xd1\xbe/\x09\xfd\x82\xf82\xaf\xf3r\xa2\ +\xb0\xdc)\x8f\ +\xc3\xce\x15\xf7\x047\xaa\x17\xe8%\x07nT\x0aG\x8b\ +\x0b\xc47\xc4\x0e\xb4Y\x8e\xd9\x5c,6^\x05]d\ +\x9c\xd6\x9c\x03\x96c\x9ezu\xc6\x0d\xd0(\xe3\x83r\ +\xe0#[\x03\xa3<\x94\xaf[\xcey\xec.4nV\ +\x8e\x03\x87\x83\xe5-\xcb9_v\xa7\xf1\xdd\xd0p\xe3\ +w\xe5\xf4\xfbbj\x84G\x80_\xd8\xc0\xf3\xdc\xc6\x16\ +\xe8\xa1=\xc4U\xe0\xfb\xb9\xdd\xc2VU\xc8'\xd9\x87\ +\xb0\x07\x5cj|\x15\xb8\x99\xf7\x8a\x87\x88Sq\x0a\x98\ +\xf3I\xb8a\xa4\x8d\x95\x80\xab\x8c\x07\xab\xe1\xc8\xb1\xc3\ +j2\xb7\xa8\xbd\x90'd\x85\xf1P\xf0\x14\xae\x0fq\ +6\xae\xa7\x9e\xd7\x8e\xe9aaK\x9dU\x11\xba\xcer\ +\xce\xe6\x82\x13O\x8a_\xd1\x9c\x9d\xe2_\xd9|\xb0\xd8\ +X\xce\xd99q;\xff(n\xb5r~\xee<\x81\xf2\ +|\x0d\x1c\xb2\x1cO\x84V\x18\xb7\xf0K\x8c\x8fkN\ +\xdar\x5c\xaa\x1d\x13x-4\xde8\xad\xb3\xa1\xdd\xd6\ +\xc0x\x8f@\x15[-\xf6\xf5\xa17\xa0\xd7\xe0\xbdl\ +\xb74\xc65r\x00v\xba8\x05f;\xbfF<\x80\ +\x87\x0c\xec\x1e\xae)9\xf9 w\x0b4Yc\xdfy\ +\x8e\xb2Y\xb4\x1c\xa7\xb4\xf7\x03o\xe6\xee0>\xa59\ +\xc7,\xc7\x15\xd0\x1a\xe3G\xa0\xa4q\xbb\xa2\xf3\x9d\xad\ +\x81\xa4G\x80\xfb\xfd4\x06?\xa6gQ\x04RQ\x04\ +V\xffK\x04\x8a-\x02\x15Q\x04\x1a\xe5t\x93x_\ +\x9c*>0\xcf\xbb\x14\x9e\x01\xa1\xbb\xb5H\xe5G<\ +(\x14*\xfb\xc7#a\x9c\x19\x8a\x90=7?\xa3;\ +\xfd\xfe\x02\x18\xfc\x86\x85\xa5`T\xfb\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x00\xfc\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00\x91IDAT8O\xa5S\x81\x0d\ +\x80 \x0c\x03b\xf4\x1c\xff\xffB\xcf1\x9a\xa0%T\ +q\xbaa\xa4I\x1dts\x14P\x1fct-\x08\xdb\ +<\xbcv\x80\xae\xe5n\x80\x03\xc9u\xea\x8fp\xc5\x92\ +\xd0J=mA\xae\xd4\x8d\x8bG\x84\xce1 \xe7\x09\ +\xec\xca\x8e\x92V\x0e<\x07\x7f\x9b\x84l\xc4\x04l\xab\ +\x87\x8a.\xb5\xd5\xad|z\xb0@\x16[/\x92\xe7\x16\ +x\xc2\xb4\x9b\xe5*\xd4/\x91M\x1e\xd7V \xd5\xd0\ +J\xcd\xae\x96W\x93_\xb5\xdb\xa4d\xcd\x11\xd9\xfe7\ +\xe6hB\xbf\x15\xe7v\xf1\xcd\xb3\xd1w\xeb$X\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x02\x14\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x01\xdbIDATx\xda\xed\x97\xbfKBQ\ +\x14\xc7E\xa1!\x10,tk\x08\xda\x02\x1bj\x8eR\ +hj\xd1!\xe8\xd7\x90\x8deCS\xd1\xd2\xd8\xd2\x16\ +iC\xb4e\xf8'\xb4i:YTCC\x8bA\x16\ +M\x85IF\x93\xda\xf7\xbc\x8er\xb9\xdcgO\xbb*\ +\xc1{\xf0\xe1\xf0\xe5\x1d\xef\xfdz\xcey\xf7\xa9\xc3a\ +_\x1d\xbe\x96V7\x92\xe0\x0e\x0cu}\xf3\xc5\x955\ +\x176~\x0650\xd6}\x03\x91\xa8\x0b&\x0a\xa0\x06\ +\xfc\xbd\xaa@\x81+`\x1b\xf0\xdb3\xd0\xabs\xe0\x91\ +\x0d\x8cZq\xbc\x8f\xc4\x0b\xe0\x01G\xd0i\xc4T\x1d\ +\xe8\x94\xa43\x88\x9b`\x02\x9c@\x0f#\xd2\xb7^\x06\ +\x0b\xd0\x14_\xd9\xc0\x16\xf4<\xdf\x1bQm>\x83\x9e\ +\xd5\xb8_>p%\xe8\x1f\xd4\x9a\x8c\x06YS\xbf=\ +\xa0,\xdc\xaf\x82\x8a\xf4\xf9YU\xa9Bt\x93\xddz\ +AN\xd0\x06\xb4\x90\xa4)\xc6@\x80\xf5;b?\x18\ +\x07\xb4qU\x91\xbfk\xd6+\x1d\x06\x8a\x88n^o\ +N\xf1\xf9D\xb3a\xd1j\x80\xdb\xba#\xe4g\xa1\x9d\ +\xcd\x060$\xf4\xc8\x0br\xac+\x0d\x22\xd1\xaa\xa4\xe9\ +~\x0c\x04\xb8\xc7ED\xb7\xb4\xee)x\x02\x03\xbf=\ +.\xda+\xc0\xeb:\xe9\xa9\xb2\xf2\xbcv\xc4@+\x07\ +\x86l\xe0Ra@5\xd5q-\x06\xa4\x19\x18\x04\xe7\ +\xd0\x1f\x88\xa5\x06\x91hI\xd2\x9f\x88{`\xdal\x06\ +\xda\xad\xc0\x14\xa0\xc3\xc8\xcb\xd500\xd1>\xb0\xae\xbb\ +\x05b\x89[\xd1\xda\x0c\xbc\x80\x03\xe88\xf7\xd8@\xd0\ +1I\x97u\xcf@\xdaB\xfe-\xf2\xef\x11'A^\ +\xf7\x0cd,\xe4\xd7[\x10\x06y\x1d-\x08\x0b\x06\xd2\ +\x16\xf2\x0f\x91\x7fL\xefy\xf0 \xbc\x8c\xda6\x10\x14\ +\x0c|\x81\x1b*3b\x03I\xd3A\x95C\xbc\xa67\ +\x1f\x1bxC\xec\xfb\xcbo\xb8m,r\xc6$\xa1\x93\ +F\xacc\xae)?\x01\x1d\xb0\xff\x0b\xfe\xbb\xeb\x1b\xc9\ +\xfb\xd7\x13\xad&L\xa9\x00\x00\x00\x00IEND\xae\ +B`\x82\ +\x00\x00\x00\xd0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00eIDAT8O\xdd\x93\xd1\x0a\ +\x800\x08E]?.~\xb9atc\xc55\xad\x97\ +\xa0\xf32\x86\xf3p\x057\xdc]\x18f\xc6\x0b;\xaa\ +:\xe2<\x04s\x03\x8aA&\xc2\x9be\xbb]\x88&\ +4\xce2\x06\x15\x80\x8e\xe4V\x10d#\x80RP\xf1\ +Z\x80d\xdf%\x00\x7f\x12T\x1b\x97qJ\x10\x92\xa7\ +\x22:BG\x84z\xfa\x9d{\x88\xac\x1d\xf5-\x8f\xc3\ +r\xe1\x95\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x04M\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x04\x14IDATx\xda\xcdW}L\xceQ\ +\x14~U$\x94\xbc\xd2\x87,\x9f\xf3\xe6\xcd|d-\ +LS\x93lY6\x9f+[MM\x85B\xc3l6\ +b\xfc!B\xd6\x1f\xac\x86\x0cM\x96\xcfe\xb3\xd9d\ +\xf3\xfdG#\xf9\x1c\xcd\xb0\xcc\x98\xaf\xc9V!y\xce\ +\x9c\xcb\xd3\xf5\xf2\xf7\xdb\xf6\xec\xbe\xe7\xf9\x9ds~\xe7\ +w\xefs\xef=9\x1c\xde\xf0\x97\x91\xbd2\x1a8\x9e\ +\xbe\xa4\xb0\x88\xb8`\xd8\x071\x96\x00=\x85\x83\xed\x87\ +\xdf\xdb\x81*\xc0I\xbe\x85@\x0d\x9e\xbb\x89\x9b\x0b\xfb\ +$\xc6\x14\xc3\xc1\x8e\x85}\x0a\xc8\xb1\x0b\xb8\x05t\xc1\ +\xa1\x0b\xe3\x04\xe5\xf6\xa9-\xc8\xd3\x049j\x0b*\xd4\ +o\x9c\xe1\xf0\xfc\x8er}\x80o\x1a\xdfI\x05<\xa2\ +\xf8\x91\x5c@\x1d\x150B\xb9b*`\x9e&\x98C\ +\x09\xb6\xa9\xdf0*\xe0\x82r>\xc0'\x8d\x7fC\x05\ +\x5cU\xdfN \xdcA\x0fz\x03y \xe3\xac\x99\xc9\ +\x02\x9f\xca\x1c\xecY@\x96\xc5\xc5\x02\xcb\xe0\x1f@\xdc\ +P\xd8\x05\x18#(_ l\xe1\xc6:\xbc\xea\x0f\x95\ +\x0d\x14q\xa1\xb24\xe2z\xc0\xde\x841\xdf\xf2]\x0a\ +\x08\xefK\xdcl\x8d\x0f!Nfe\x17F7\xcdJ\ +\x04\xecR`\x86]\xc0\x15\xd2\x80[\xb92\xd2@\xa6\ +&\xc8 \x0d\x94\xd3\x0e2\x1a\xb8\xa9\x5c/\xa0]\xe3\ +\xdb\xa8\x80\xbb\x14\x1f\xc5kXo^f\xb6\x12\xc6R\ +S\x14\x90\xa9\x893\xd4\x16\x94\xa9_\xb4\xe1\xf0\xfc\x9a\ +r\xfe\xc0\x17\x8do\xa5\x0f\xbdM\xf1Q<\x0325\ +;@.$N\x92l\xd6=\xde\x83\xf8\x02`\x0b\x0b\ +Nv\x09\xb0\x13\xfe\x83\x89\x9b\x02{\x0f\xc6\xf1\x960\ +\x85K\xf5:\x11\xf6\x05\xb2Qa\x9c\xb5\xbd\xd2\xf9$\ +S\xdf\x99@\xba\xc5\xc9\x09\x97\x03\xff~\xc4\x0d\xd7\xad\ +\x1dI\xf9\xfa\xc3\xce\x05b\x1c\xd6\x8bN\x93\x06\x86*\ +\xb7\x814\x90f\xd4NkX\xac~Q\xa4\x81:\xe5\ +|\x81\xb7\x1a\xdfBE\xd5\xabo\x1b\x10\xc6_\xd0@\ +\xbb`\x92r\x15\xb4\x0b\x96k\xe2\x5cR\xf1!\xf5\x1b\ +O\xbb\xa0\x89\x8e\xe2\x1f&\x9e>\xf4\x09\xc5\x8f\xb2/\ +\xa3Z8\xac#.\x08\xf6\x11\x8c\xbb\x01?\xf3e\x22\ +6\xe0\xa8\x5cV\xe4[\x04\x9c\xc4\xf31\xc4\xcd\x87}\ +\xce\xba\x8c&\xc0\x16.\xd7\xbbD\x88\xca\xe4\xd4\x9b.\ +\xdb\xd1\x12W\x1f\xd8\xc9|b\x9a\ +\x07\x95\xba\x86\xdf1\x86*\x97O\x1aH\xd4\xa4\xd3h\ +\x0d\x0b\xd5o\x10\xf0U\xe3\xab(g\xb3\xc67QQ\ +g4\xf6#0\x80\x0b\xb8G\x22\x9c\xac\xdca*`\ +\xb5&(\xa0\x02\xaa\xd5/\x8eD\xf8\xd8\xe8\x87\xf2\xb1\ +\x08\x9fQ\xbc\x8b\x0bH\x00\x1a\xe1\xb0\x9f8\xd9^\xd7\ +1\x9e\x95\x844\xad\xd2\xd1\xdc\x90}N\xbe\xe5\xc0]\ +Y\x06\x16&\xec\x07ryQ\x01ri\xdd\x97\x8b\xcb\ +\xe1u\x7f\xd2\x09\xa1\xc2\x00K\x5c\x91rU[~N\ +`\x88\xc5\xf5\xee\xd6b\xfd\xb9\xce]\x1e\xde\xe32\xdb\ +\x9a\xc9\x12]\xb3wf\x7fc\x5c\xa0k\xd8\x0eL\xd4\ +\x82\xe4\xd0i\xd35\x5cL\xeb\xfdZ\xe3K\xb9\xcf\xd4\ +\xf8K\xf4A\x15\x1a\xfbT\x0e+.\xe0!\x89&A\ +\xb9c$\xc2\xb5\x9a`\x15\x89\xa8F\xfd\xa6\x92\x08\x9b\ +MGm\x8bPg\xe4\x05\xc5\xbby\xaa\x17\x01\xefA\ +^\x943A9\xe9v_I\xa7\x0b\x84k\x92P\xfc\ +\x96;]x3+\xb2\xb7\xcf\x03\x1f\xa4_\xa0\x9c{\ +a\x7f\xc6\xb8\x95\xbb)\xd8\xc2UK\x9c\xd7\x890\xd8\ +\xaeJ\xaeWiL,?\x7f\xe9n-\xce\x87\xef\x06\ +\x8awzx\x8f\xd3\xd3\xcbW\x88\xb8\x10\xd0(\xfd\x9c\ +r\x892]\x18[\xcc\xcd\x05{8~\xbf\x04Z\xe5\ +\x98\xa5\x82\x1a\xb4\x07\x5cM9O\xc0\x96\x13\xb2\x92\x0a\ +\x92f\xb6\x03\xb8l\xfe\xdb\xfa}MR?\x90\xa4\x5c\ +\x0d\xf5\x03\xeb5\xe9\x1a\xea\x07j\xcd\xf1L\xfd\xc0s\ +\xf3\xe5\x94\xaf\xcbhE>\x86\xe2c\x1cV\x9f\xd7\xae\ +]\xab?\xcd@\xeb\x7ff \x85\xce\x00\x99\x81\x0e\xd9\ +%\x94\xb3Vg\xe0\x00}\xe8F\xbd7\xba\xcf\x80Y\ +\x1b\x0f\x1a\x08\xfa\x87\x06\x82\xecC\xc7\xd3\xda\xfeu\xeb\ +\xfd\xf2\x0d\xf1*\xf1\xff\x04\x8f\x07^\xb5\xbe\xa8\x80\xde\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xd0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00eIDAT8O\xdd\x93\xd1\x0a\ +\x800\x08E]?.~\xb9atc\xc55\xad\x97\ +\xa0\xf32\x86\xf3p\x057\xdc]\x18f\xc6\x0b;\xaa\ +:\xe2<\x04s\x03\x8aA&\xc2\x9be\xbb]\x88&\ +4\xce2\x06\x15\x80\x8e\xe4V\x10d#\x80RP\xf1\ +Z\x80d\xdf%\x00\x7f\x12T\x1b\x97qJ\x10\x92\xa7\ +\x22:BG\x84z\xfa\x9d{\x88\xac\x1d\xf5-\x8f\xc3\ +r\xe1\x95\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00;\xfb\ +\x00\ +\x01\xa2\x08x\x9c\xed]\x09\x5cL\xdf\x17\x7f-\xb4 \ +\xc9\xde\xa2\xc5RD%\x7f\x89HB\x96(;E\x14\ +\xd1\x0fQ\xb4\x92(B\xd6V*d/E*[Q\ +hAE\x08\xadZ\x94\x92\xd4\xb4\xafS\xcd\xf4\xfe\xf7\ +\xbey\xed\x13M\xcd\xd4\xc4\xdc\xcf\xe7+\xaff\xde;\ +\xf7\x9e\xfb\xce=\xf7l\x17A\xd8\x90\xfe\x08ll\x88\ +\x18rI\x18Av\x80\xff\xdb\xd8P\xae\xa5\xb8\xd9\x10\ +D\x04ATT\xf0kU\x04\x91\x1a\xcf\x86\xc8\xc9Q\ +\xae}\xc6#\xc8\x0a\x03\xf0?1\xfc\x9a\x1fA\x88g\ +\xd9\x10~~\xca\xf5\x7f\x9c\x08r\xdd\x93\x0d9\xb5B\ +c\xd1 ^A^p\xebAK\x16/X\x05\xff\x0a\ +\xc1\x0d\x1f\xbdi\xbf\x0fx\xa6\xb0\xde\x92\x05\xf3\xd6X\ +\xa6\x16e\x1c\xb8\xe2\x9a\xe8YR\xa69E\xe7E\xc0\ +\x0a[\xb5\xc9*Zk9\xfb\xb1\x9f\x97\xf2\xd9\xf6\xfa\ +\xbf\xb3[\xcfN\x10/\x1eU5)w\x94\xe4\x0c\x0f\ +\xb9%\x13.]~\x9b%\xad]p\xc1naN\x22\ +\xff\xca[Q*\xe3.}\x9e\xa72o\xdeW\xd7\xbd\ +*\xb6\xec;\x9f\x5c\xcf\xb7\xf6\xf5\xcfmX\x99\xb8A\ +\xc5(qm\xdeL\xcd\xc4\x0a\xab\xb0\xcd\xa4\xb1\xab\xcc\ +\xae\xde)8D0\x8c\x9b\xa90\xf9u\xce\xa0\x1f\x1c\ +96\xabm\x87\x84\x1bg\xe9\xeb\xaaD\x8a\x8e\x5c\xe1\ +\x17\xea\xa7zJM\x8c[X\x83\xf8H4e@!\ +\x9br\x7fs\xa2u\xf1\xba\x8c~\xd3O/\xb7`[\ +Sf\xc9\xf1,|\x9c\xee,\xb9\xd1\xee\xc8Y\xce\xc2\ +\x90\xd8\x09\xcb\xa6\xb0\x17\xf6\x0fB\x84l\x9f\x84O\x08\ +\x8b\xcb\xf0\x0f\xd5\x9d\xa4\xbf\x1c\xb1|\xce&\xf4`\xe9\ +\xe1\x89r\x03\xdcy\x7f\x14\xc8\xb8\xc5\xf0\x16\x85\xf7\xd7\ +\x1d^:)\xf0M\x89\xc9\x22\xb3\x91\x93D\xe4g!\ +\xb3\xbf\xd9\x88(\x8e|:\xc2}\xd0\x9a\x02\x19\x97@\ +;\xc1\xac\x85\xba3\x03?\x82\x0f\x98\x8e\xf4\x09\x93\x97\ +\x07\x1f\x08\xff/k\x9d.\xdf\xaa\xfc\xe3\x9b\xa7\x87O\ +\x0b\x17\x08\x8b{\xb1\xdaB\xcf\xf9\xc2T\xb6\xac\xb2\xfe\ +W\xc2\x13\xc3E\xc2\x07f\xe4\x0e\xb99\xe0\x9e\xed\xf1\ +\xf0kde\x8b\xc5\x16zu\x91\x95\xf0\xaf\xa7m\x9e\ +\xd9\x8c\x1c\xe5\x1d:[\xb52B9\x9ckru}\ +\xfa\xf2\x90\x01\xf1C\xe3\xfb\xab<\xcf\xce\xecG\x92^\ +q\x03\xfem\xfe\x94@\xc4\xf2\xbd\xdf<\xdd\x10\xa4\xd4\ +\x92Cf\xeb\x07Rm\xb8\x0a\xb8\xa5\xc4 >\xb6\xc1\ +\xc8\x00p\xcb\x15\x16z\x02\x1e\x93 -\xb9\xe1F\xb6\ ++Cb%\xec\xd8Cm\xac\xc8\xca5\xbc\x19\xfd\xf2\ +\xf5O\xc3\xef\x8d\xb5\x89>\xf5!s\x80S\xd1:6\ +\x8b:\xeb\xe29!\x03nN\x87\xdd\xe3p_\xe4w\ +`\xcd\xf5\xfa\x1d\xdcz\xd7Gl\xcdH_DR\xb6\ +\xd8\xb0^\xfcq?\x9bh!\xc5\x0b)\xa4\xfc\xff\xe9\ +l\xe7\x8f\xe4L\xe20!)\xd7D\xcf\xfa\xb6)6\ +\xc4\xd2a\x86\xf1\xd8\xc0\xd1\xbe$\xd5e\x1c\x0a\xd1\x04\ +\xbb\xef\x03\xf7\xb0Y>\xb7U\xd5\x1dR\xaa#2R\ +\xda\xee\xed\x14\x8e\xbd\x15\xf5\xe9\x1f\xd4^\xdc\xd0\x83\xfd\ +T\x13\xd88w\xce7\x9f\x9aa\x12\x1c{8v\xd4\ +[\x17g\xa8=\xf7\x10\xd7A\xc0m\xa6\x96\x8eYU\ +{\xfc\xd7\x07\xcb\xac\x17\x96\x9e+5\x0e\x00\xae\xd8^\ +\xb6\xbd\xa0\xe21d\xe4\xb8\x05#\xa48\xd6E\x11\xce\ +\xafZ\xca\x11\x9e3\x88\xc4\x158,f\xa5\xb5\xb8\xaa\ +\xbe\x87\xb0\xaeo\xb4\x10\xd1\xeb\xdd\x8c\x95\xf2\xfd\xf93\ +\xaf\xe4\xe8m5!\xa9\xda'\xccfO\xbf\xb5u\xa8\ +\xf3c.\x95C\xfb\xe2J\xec\xe7\x04\xc8\xda-v\x18\ +\x1f^\x94\xbf\xf9\x7f\xaeI\x88=Z.\xab:)\x8c\ +\x7f\xd2dq\x85\xe5\xeeA\x17UB\xa2\x911j\xe0\ +\x17w\xd3\x97\xfa\x9d\xf7Z\x9e\xca.\xa6\xbcA\xdc\xfa\ +\xb0x\xc2%\xb7\x8f\xb6\x11\x8fM\xd9\xb2\xe3\x87\xab?\ +\xc9\x10\x10\xf7\xcb\x1b\x11\x16G^pW\xd6y?\xf2\ +z\xe4d\xbf\x05\xdeuvr\xab\xb8\x8c=2\xf3\x0e\ +\xcf\xb90r\xc9D$;\xd6*s\xfe\x88Z\xee\x1b\ +\xf3E\xe4\xc6_\xd8\xfe\x8a<\x86\xc3~nJ\x85\x04\ +\xe8\xda'\xaf\x81\x96S7\x8f\xac\x1f\x7fa^\xf8\xc2\ +\x98\xd4\xc3\xe2\xa6B\x9a~2\xd5\xeaa\xecb\x0d\x85\ +\x0fg\x7f\x91\xb6\x9by(8\xac\x9eg\xdc\x1c\xd0\x95\ +\xb7\x06\xd3V\xdc\xae\xbf{\x9c\x7f\xd1t\xb6\xa1.\x19\ +ZR\xbe#T\x0ey\x94L[\x9c;2\xd2\xf0\xb4\ +\x8dA\xb6\xd9\xe38\xe7\x22\xf6\xf7\xf3\x83\xca\x04\x22\xfd\ +\xbc\x9d~l}\xael\xc1\x96\xbd\xd1\xc2h\x91y\xc9\ +\xf0\xc8\xf7\xda6\xd3\xeaBr-\xe5\xa7\xd8D;\xbd\ +\x93\xb5\xfb:\x9c\xff\xde=\xeb/w\x10\xfbk)c\ +O\x9as;\x8d\xde\xf1\x86`\x18p\xb0\x88\xfdu\x5c\ +A\xday\xe1\xe4\xf1\x0b&|]\xe8'\xfd)G\xcb\ +\xcez'[\xba\xed\x9a\xc3\xe2\xf2\x1f\xa4\xb6\x9a\x05/\ +\x9a\x13\x84d/2-\x99\xa7\x91?\x5cb\xa8\x08\xbb\ +Z\xa5\xe9\xff\x0e\x18M\xb1\x09u|'\xbb\xc0[p\ +\xb1\x1f\x9f\xc5\xbd\xb4\x11*\xa4\xe4#\xa2+n\xd4O\ +\xb2\xe3'\x9bs\x96\x9a&\xf3\xfe\xf2\xed'7\x12\xcc\ +\x0b\xfd\x90\xfd[\xcd\x9e\xa9\xeb\x04\xc1\x196\x948\xaa\ +\xf4\x97\xcc\x0b~\x89\xed\x87\xb3\xd6\x84\xc5\x89\xccj\x90\ +\x0d\xd9\xcd\xb6\xe9\x1cx\xda\xc3\xe3'7x*\xcf\xb3\ +`\xfbD\xa8\xb4\xe5\x97\xb8e\xaauG\xf3\x17;\x98\ +\x03SU.\x14\x7f\xe1\x15\xff\xb2\xd7/3\xcf_\x83\ +4X\xe7\x09\xb2\xf7\xb3\xceRq;\x01\xc9\x84\xa2m\ +\xda\xc8\x18\xa3p>ad\x92\x99\xdb\x1an'\xf3\xe9\ +\xe1+\xc0m\xf3I\x83\xa3\x83\x91\x8f\x0b\x01K\xbf\xde\ +u\xde 2\xb7\x80\x88<\xd5\xb0\xe09\xce\xbfTd\ +u\xb4\x839[6\x97\xd5\xe1\xd3\x8f\x11\xbb\x98\x1d\xd3\ +\xc3\x97xr\x9d\x9d\x10\x84L\x10\xbb\xb0\xc5\xdep\xfd\ +\x12?\x7f\x8e\xace\xc43\xeafn~\xd2^\xfd\x14\ +\x9e\xbb\x8d\x0b\x1b\xf2\xd9h\xdd\xe3O\xdf\x9c\x82V\xbb\ +\x1e\x8f\x99\xe0&\x90\xf6\xb5\xde\x1d\x91\xcb=?w\xe1\ +\x01\x85\xbd\x1eN\xd7*\xc3\xac~%\x8eHU2u\ +\xdb\xcc\xbd\xfaR?\x92`6\x9c\xd0\x8e\x03\x96%\xea\ +L\x12\xb7\x13\xf7\x8e\x22\x90>O\xb5Y\xa2\xe2_\xbc\ +\x8aG|\xa0\x93\xba\xdfG\xd1A6\xf9!\xea\xe3\xec\ +\xe4\x12L\x89\xf2\xc8\x04\x82\xcc9\xfe\xc8\x15.\xdfc\ +\x11w\xc7\xc8\x01<\xe2\xf2q\x9cHh\xb4\x90\x14\xa2\ +V\x80\xfde\xec\x9co*\xb6\x22\xa3\xd4\xe7:\xf0\x8c\ +T\xd5\x9f\xac$\x97t\x8aT\xb71\xcaq\xbf\x92\xe4\ +\x05%\x9f\xc9\x8f\xa6=}\xff1k\x95\xd2\xb1\xa5\x85\ +?\x14\xf7\x0c\xcb\xfbj\x9crCQ6h\xd6la\ +\xdb\x9f\xd5R\xdf\xb9\x85w\x9e_\x91&\xec\xe5?3\ +\xd2\xa0j\x08\xe9\xe4+a\x9fs\x81a\x0a\xcb\xa4\xfc\ +\xea\xef.\xe0U\x9be\xfb=D]\xfc\x8b\x91\xa1\x97\ +\xcb\xa3E\x8a\xa3g\xbc~\x9b\x22\xce\x17\xb5n\xd6\xc4\ +\xb7%\x15Z\x81\xd6\x02\xbc\xaf\xa2\xb7\xd8\xf9\xce\x1a\x95\ +}\xeb\x98\xb8B\x11\xcf\x837\xa2\x12\xa3\x9c\x87\xbb\x8f\ +\xde\xfat\xe8\xa8\x1b\xa1\xc3$\xcc8\xce\x14\x1d\xb2>\ +z\xc6D-W\xae*A\xb8\xff0\xdf\x92\xc4\x99\x22\ +r\xde_\x22\xdc\x97re^\x19\xb6Q\xed\xcb\x90\x13\ +{\xea\xe7\xfb\xf9\xf2\x95\xff\xf6{\ +\x83\xd1\xf0|\x99\xfe3\xf9\x14\xad\x9e\xba<:\xa9T\ +\xb6yU\xb8\xd3O\xa9\x80=\xbb\x0dr\x7f\x8a\x11\xcc\ +\xfb\xf3,\x5cud\xb0\x93\xc2|\xcfp$\xc3\xeb\xd7\ +\x12u+\xd7\xbc\xcc=\xeau\x99f\x95\xb7\x8cBH\ +\xfe\xab\xbc\xcf\xbd\xffl~\xdf\xc4M\xa145H\xb7\ +@V_\x22\xf7\xb8\x82\xbf\xe7\xc5\xe8'\xf1\xe5>\xbb\ +O\x95\x97G)\x5c|\x11\xaffvn\xe0\x18\xf7p\ +$\x9e\x0b\xde\xbcf\xca\xee\xa5\xe4\xe3v\xe6\xdb\xfb)\ +\xec\x9d=\xf7\x9aP\x5c\x9c\xc1\xa3\xb9\xaf]\xeem\xd4\ +*\x9b\x17\xfa\x98\xb7r\xb4n\xf4\x0f6\xb1\x87\xf1\x0b\ +\x1c\x86f\xd6\x9c\xd1\xaax\xc4\xbb7T\xee\x1a\xfb\xfa\ +\x83\x5cR\x0b\xaf\x0d\x0c\xe4\x90\xc9\xf0\xe2O\x9a~\xfb\ +\xfdG\xcd\x00)\xfd\x8c\xb8\xd8\xda\x88\xe3\xb5?~.\ +\xe6\x1f\x15\xb3\x9d\xeb\xb4\x99\x9b\xaa\xfdiU%\xd4|\ +\xa1\xf8n\xf4\xc5\x95\xddwr\xf5\x5c\x84\xe5T\x1f\xf8\ +!\xc3\xbc\xcb/\x08(\xa1f:C\xa4\x0fo\x88\xfa\ +1\xa4\xae\xbf\xfe}Uv\x15\xf7/\xa3\xe7{>\x97\ +\x0bpE\x9f\x07\xfd\xfaU\xb0\xc0\xe9\xb0\x00\x12\xf9\xd8\ +P\x81\xf8q\xe6\x01R\x8ed.\xa9\x22_~\x8d\xd7\ +QN\x1b\xb9\x17\x11^\xe6\x17?N\xf9\xbe>y\x98\ +\x93\xe41\xbb\x9d\xe3\xd9\xc4\xe4\xa3\xae\x98Tl8\xb0\ +e\xf4\xb6I\x22!g\x9c\xa7\x8fC\x16\xa8\x8d\x9e\x19\ +\x13\x11\x8b\xae\x8a\xca5x;\xe2b\xbe\x18\xf8\x90\x86\ +ml\xd4\xb6\xe4\x91\x15\xf1\xd2\xbc\x0f\xd4\x1d\xc4\xc6\x0b\ + \x12c\xbf\x1a\x1a\xa7\x1d\xaf\x9d\xfc\xdcwAT\xbf\ +U\xf0C\xa9~\xd3t&\x87>\xb2;\xadZ;\xa9\ +\xe6d\xfat\xa7\xb2\x09\xe7b\xbekZ\x5c\x90\xf3Y\ +\xf3\xf2\xe3[g\xa5\x09\x0f#S\x96J\xc6e\xe8\xbc\ +\xd3\xe5\xb1\xbd\x1d\x93!\xa0\xb5\x8d\xa8\xc2\x15;\x9e\xd3\ +Y\xc2b\xa9\xfe\x85\xef\x1c\x22\xb3\xad\x17\xf9\x9e\xe5\xa9\ +tJ\x1f\xefd\xa66\xd8\xbc\xf4\xa4\xa8k\xcc\x18\x0e\ +\xeb\xace\xe4\xf1\xabf\xecw<&lP\xa7\xec\xb7\ +x\xdc\xa4\x8d\x1aU\xd7\x83fF\x16\xc7\xbd*\x1cd\ +W[\xb9\xd8Ne\xbe\xdd\xab\xe5CR\xe7\x7f\x12<\ +[\x94\xb5\xf3\xd1\x9b n\xd9\xb7\xc1+W\xea\x1b,\ +\xf6\xf0\xbc1\x9cOm\x90y\xd6\xe8,92\xa9\x9e\ +\xd3\xc1%z\xaf\xd4\xcf\xa4\xf1\x83\x1eN\xdc9x\xd9\ +\x10D\x93|\xdd\xee\xc6\xf0\xfc\x8d\xdf\xbe~\x12\xdcN\ +\xd2\xe4#\xbc\x9c\x1c\xef\xe3\xcf\xf3h\xaf\x81\xe3 Q\ +\x8b\xc0D\xd1l]\x9d]V\xc6\xb9\xa1\x86gE\x0b\ +\xf4\xbfpK\xe6\x9d7/K\x5c}\xc5;=&\xe7\ +f\xbc\xce\xfb\xb0\x87\x8e[^&\xf8\x85Jyo?\ +\x8d\xf0\xe7\x07\xef\xd4\xe4\x92V\xe0\xd2z\xe25'\x97\ +\xf7\xc8\xf3$]W\x8b\xb9\xf9Y\x19\x9a\xb36\x103\ +\xef\xff\xf8\x99\xf2Tw\x13\x9f\xdb\xf2\xf7\xb9\xd9\xd36\ +\xfd\xb8\xec\x93o\x1d{\xf6\xd0]\xbe\x13\x07\xf6\xce/\ +\x17\x8a\xd5\xb6\xe5T\x19\xe1kt\xaa\xbc\xb0N\xa1\xa1\ +RC\xc8\x8a=\xca\xcc6U\xe9\xa6_\xaa\xa0~b\ +\x92\xd8\xf1g\x0a\xfeW\x5cc\x07\xe5\xe5^\xfey\xc3\ +\xb3>lmL\x10\x87\xb8\xf9!\xc1/:\xc3\xe2\x9d\ +\x90\x18\x81\xddw\x95\xdc\xeb\xce\xa4\xde\xb2\xb6\xd4\xb9\x94\ +v\x8bTV\xa4\xd0@4\x17\x9a\x16\x15R\xba\xd99\ +n\xdc\x07\xa5\xfa\x1f\xb9)D\xf2\xf27\x87\xc8\x11\xb3\ +.\xa5\xea\xab\x889\xbdg\xd7?\x15G\x18\x9b*\x8d\ +\x96\xad[\xdf\xcf\xa3\xe8^\x9e\x95C\xf2\xa3\x9c\xeb\xdf\ +\xb6d\xeaq\x88\xa4\x1fy\xd2pl\xf5{5v\x15\ +\xa9`s\x03.7\x19\x9f\x9c;q3>\x7f\xba+\ +{{\x87\xe6\xdb\xdc\x87\xa7\xcaG\xed\x93\xe0A\xf4\xec\ +\xff\x1bvU+9\xaa\x1f\x9ag\x9f\x14\xefp\xd8l\ +\xb9\x93x\xde\x84\x1d\xbc\xcf\x86F\xae8)8\xf6\xbd\ +Nj\xb8\xc3\xd5=_\xbel\x9f(\xfb\xb8\xac\xe2\x1e\ +\xafYf\xffxG\x84_{\xf2\x96\xcc\xa8\x19\xa2w\ +\xa7\x99\x06\xdd\x8c\x9f!\x99+o\xfd\xd8p&\x9f\x00\ +\xa2z\xab\xe0\x92\xdczR\xd0\xf9`\x0b\xb3Q\x0d\xdf\ +j\xb6\xac\xb6[\xfd\x92\x0c~\x1fp\x22I*\x13\xd5\ +\x99#k\xb4kF\xa5\xb5\xe6>%\xf7is\xb6\xd4\ +\xe4\xee\x96d\x13{<\xe0xD\xbd\xe9X\xcd\x80i\ +\xe6O\xbe\xcdq\x1b\xb6\xa1\xe1\xd3\x10-n\xd7,\xef\ +\x85\x83\x14\xff\xa7v\xf3\xeb\xcf\xb2s\x0ahm\xca\xd4\ +\xd2\xcc\xb93\xe2\x1ck\xa4\x01!\xc7\x06T\x1c(5\ +\x9a\x12\x22\xab#\xc4c\xc39eD\x80\xcd\x0e9\xb4\ +v\xc2\xe9\x8a\xad\xb1\xa7\xce\x19\xbf:PKj8\xb7\ +NpJ\x88\xea\x8a\x10$\xd2\xf4\xf0S\xbf\xcf\x81\xb6\ +\x11\xce\x87\x84\x82\xdf?.\xde`\x03\xde%\xcf\x15\xa3\ +\xf6\x1ey\xc8\x86\xeam\xf3\xe66\x9a-\x9b\xf9p\xe3\ +\x19u\x84{\xf5]\xa3S\xf2\x16Kn\x89\xe7`b\ +\xe4\x92\xe1\x87\xf1\x91\xf0m\xbc\xbbK5\x1d}8\xf5\ +\xf2\x81\xf4\xd9\xaf.e `\xd4v\x1d9\x14\xfa\xc1\ +\xd1\x80\x0bLs9\xad\x8am\xb1\xb3\x16#\xdc\xf1\x97\ +\xd1\xa3S5\x87l\x9e\x96x0\x03Y\xbd\x8cC&\ +\xf3\x93\x86\xd6\xbc\xba\xfez\xa9g\xb7hM\x1aV\xf7\ +\x5c9\xf2\xc8\xfd\xff~\xda\xeeNP\x8eC\xcf=\xda\ +_\xfb\xe6\xcc\x87\x8b\xda\x0ez\x15\x96\x1cI\xcf\xb4\x9d\ +\x1e\x08\x9b\xc379\xe7\x84\x0f\xf9CH\xc5\xe0s~\ +\xe9\x86\x85\x0f\xf5=o\x0f\x10\x93WUd\x0f;\xbc\ +\x90|\xfa\x85\x96A\x88\xe9\x96\xbb7\xc0k\x1e\xb5A\ +g\xf8\x8f\x0c\xcd\x84\xea\x12\x99Y\x19\x0b\xcf\xcf\xac\x96\ +\x9fn\xc7i\x13#\xe5\xf0\x22\xe9ne\xd5\x00\x93=\ +\xcb\x84vz~\x05\xa4:\xad\x04\x93\xe3\x82\x91\xdc\xc1\ +\x86Q\x04S\x0e\xf8\xcd\x03\xbb\xce\xa5F\x9f\xb8\xe3\xf0\ +@c@\xf8\xb0x\x9b\x1d\xe5\xaf\xd3\x05xM\x12\xfd\ +\x068\xa8}\x0f\x92\x14|\x5c|\xf6\xf2\xa0\xcd\x07\xc2\ +<\x8f\x84=\x5c\xf15\xef\x7f{\xed]\x9c\x0f]\x1e\ +\xaf,\x09Fz\xfe\xbb\xa2M\xa6!!?3\xa3\xe6\ +\x88\x5c$\xae\xf2MJ\x1f\x93[\xb2\xf1\xd8\xfb\xab7\ +w|?\xff\xa1\xbf\xc1\xa0X=\xb3\x0b/3\xce\x9c\ +\x93\xd0\xb1\xe1\x1c:g\xd7\xe5\xc3\x01\xcf\x8d\xde\xe6f\xa2\x16;\ +_\xa6\xefX\xfb\x8e7\xb1\x9f\xcdps#g\xa3\x1d\ +\x9b\xceD\x0e\xda\xf1n\xb8\x8c\xfc\xc3\x10[\xded\xa3\ +\xffI\x93V\xca\x0d\xe0\xb1yz\xf0\xb8I\xd5\xfc\xd8\ +\xed\xa7\xe7]\x10\xcf}h#=\x5c\x00\xd9w-Q\ +z 2`\xf6\xba\xbbg\x0clB\x8c\xef\xef\xb98\ +\xc2f{\x82T\xba\xff\xeeJ\xdd\xdd\x85\xb2w^\xf9\ +D\x14\xb0\xab\xe4:\x1c\xd5\x02\x14\xf8\x92\x02\xd5\x07\x18\ +\xae\xd1\xce\xfa\xbc\xeaV\xae\xa2\xee\x0f1\xb3\xef\x07\xc3\ +\xd95\xf7\xbd\x0b\x96\xd7\xc8\xdd\xe6\x85\x18h\xeb\xbbi\ +\x8d\xf5<\xec\xbc:\x7f\xde\xd5\xc3<\x08\xe1?\xc33\ +\x93\xf6\xf4[6Z\xcf3\xc7\xfb\xac|\xed0\xb3\x9a\ +\xff\x09\x83\xd71\x9f}\xc3;\x99\xe3J5\xa4x)\ +\x1e\x1b\xd3\xcb\xca\xda\x19s2,\x14\xfdL\xf7\xb9\x83\ +\xa7\x8c\xf3\xf1_\x19\x1c\x96\xbeSr\xe6\x85\xbdJ;\ +\xa6?\x9b\xb9\xcb\xa1\xcc\x7f\xd5!\xf6\x07Z13\xe4\ +#N^\xbb[\xffi\x8f\xc2S\xfd\xe7f\xe7=\xd7\ +\x15=\xff\xcb\xc9\xb1\x13\xee\xce|\xff\xe5\xb4\xcc\ +\x5c\xf5\xa2\x17\xe9O\xd7gxp\xec\x90`s\xd4\xcd\ +\xbc!g\xdb\xdf\x8ccn\xbc\xda\xcf\x18S]\xc1a\ +?S\xdd\xcd\xbcC\x92\xd6H\xb1_\xaeY22\xdc\ +\xf9\xdbZ\xcb\xd4\xb3\xaf\xf82\xebv]-V\x7fo\ +\xb9^\xe3\xdd\xdaT\x12Z\xaa\x12#\xefpR\x1d\x11\ +6 \xef}\xb2<\xac\x84\xbcz\x8c\xc6\x81\xff\x89\x8d\ +\x9f\xe1y-\xa0\xb2*\xf3n\xd0\xc7\x99\xe7\x05\x90\xe8\ +\xda\x12\x87\x84\xc3\xc4\x0b\xdb\xcde\xdc\x1e\xd8\x1c\xbe<\ +\xf3\xa3\x97i\xadl\xf8F%\xbf\xcf(\x17\x8f\xcd\xd0\ +C\xdaBz'\xb3'^\x0f\x95\x91Yq\x90OO\ +!4\xcf\x99\xdd+ \x13,g.\x0b\xfb#\xf3\xd8\ +\xaf\xab\xa3\xc4\x15{\xa4u\x13t\xb9\xd6_\x99r\xdf\ +F_\xc7\xf8\xd2\x99G'\xee\xe9\xac\x22;\x14\xcd\x8b\ +\xfc\x1a\x86\x94\x82\xa1\x92y\x1e\xb4\xd6Z\xb4t\x91\xdb\ +p\xc7\xdd\xb6\x15y\xf1o\xf4N\x1e\x09\xbd\xb9\xa2\xb6\ +\xde/\xc5\xcf\xd8`\xd4Pg\xe4Wd\x12Y\xdc+\ +\xf6(\xdb\x89u\x8aO\x8b\xd8\x9f\x1c\xd3\xfa\xfa\xa3.\ +\xbf\xf6\xec\xba\xf4\x1a#\xc33\xe5\xeb.\x98aO\xdb\ +(\xfb<\xd9\x9b4\xa7\xf8\xa5\xe2\xb5\xd5\xe9\xf2\xe3?\ +]~'\xafTc\xad\xb3'S4\xcb\xeb\x5c\x91\xe9\ +\x86mlY+\x05\x90\x0c\xcb\x9d\xdbB\xcb_\xea\xee\ +=\x7f.\xf3\xd0\x1d\xee\xd0J\xce\x83\x95\xd7\x92G\xac\ +\xbcm\x5c\x93\xa9\x11\xe7\xbf^\xb4n\xc7X\xb6O\xe7\ +\xc0\xeaD\x8a\xe3\xab\xfa\x10\x11p\xd5\xf9\x95\xc8\xc8%\ +\xeb\xaf+%k\xc6m\xfb\xa9X\x15\xb7\xc20M4\ +\xfb\x15G\xd2\x06\xb2\xdbb\xf6\xeb\x5c\xea\xeb\x83\x05\xcb\ +\x95k6k\xc7\xe8\x7f\x95\xcd\xe2\xbc3\xd8L\x1e\xbd\ +\x5c\xc0\xd5\x804\x04F\xb9\xc7\xa5\x1a\x8eC\x0e\x06\xee\ +5\xe0\x12\x9dxH5\xd0%\x9e-,\xd4\xd7}\x9e\ +W\xf9>\xd3I\xb2\x8f\x1f\xfc\xfa\xb9u\xfe\x8e\xa5\xca\ +\x01\x0e\xa5\xe5\x13v\x9fk\xb0p6\xda$;\xe2\xc5\ +\xce\xf7\xb2\xc7B_\x7f\x1cj\xa2.\xe58\x8d\xf7\xed\ +\xbe\xf0\xec\x8dKGi\xeb\xbdG\xf2W\xef\xd6\xe6\xd1\ +2,\x0dp\xe1T\x91m\xa8\x9b\xa7\xc4\xb5\xeb\x5c\xad\ +\xf9\x95\xa9\x09o#/\x9f\x9eB\xf6\xb3\xc9\xe8?\xf8\ +\xc3\xc4\x1d\xa7#\x8b^\x0a$*,\xb2\xdcu\xbf\xaa\ +?:k\x09R)1\xecD\xe6\xcb\xd4\xad)\xf5\x1f\ +\x84\xf3\xb2\xcb\x84\x96NV\x9aVz\x99p\x9ax\x22\ +\xdb\x80}\x9a\ +\xb4\xc5\xcaq\x84\xca\x17\xa0\x13\xa5W,\xbb\x15\xb4|\ +,\xd40K\xe6N\xca\xb9\xaa\xe5\xf1\xe4\x9a\xcd+\xbe\ +\x19q1y\xa2se\x92\xd7\xd5\x8a'\x9dsy\x87\ +\x8e_\x94\xffX\x94\xacj!.\xbf|\xb9\x0eg\xe8\ +\xa0=\xe4\xfd'3\xa2f\x8e5yzp\xe9\x98\x93\ +\x97%\xd6\x08).\xfc*:-\xfa\xd9\x86\xfd\xbby\ +\x1eI\xe6e\xef/\x18p\x7f\xbbQp\x99\xd5\xb1u\ +\xe6;x\xf3\xd9+]^\xf1\xc9\xbe\x97\xce1|\xb3\ +qM\xd5\x1b\xff\xb8\xc8\xd9u\xda\x03\xa4\x15\xee\x1d\xb1\ +\x9b9z\xe4\xd5\x9f[\xbf%\xf3\xba\x1a\xe5$\x8e\xbc\ +\xc6\x19\xb9)\xe8\xfa\xc7\xf2\xf8\xc3\xd1`\xad\x89x\xcb\ +!\xecG\xd2\x1b\xed\xd5\xef\xe7\x7f\xa7\x93\x13\x16Z\x1e\ +\xdc#\x1d\xf7\x22\xcd\xbf\xd2\xc6\xe5*\xd8b\xb9\xcf\x19\ +\xef&\xe4\x9dgR f\xe2\xa7\xbf\x8bG\xedY\xc1\ +\x8d\x83w\x87]%\xbb\xfb\x8eCV\xfd\xcf\xe4\xca\x19\ +I\xa3i\xc9_2Vp~\xbb\xcb[\x1e\xfca\xde\ +\xe6\xb4o\x1a\xd6gR\x1d\x143\xb7o\x8bJ\x11\x96\ +\xb9\xec\x1e;>G\xc7[\xe8\xb2{\x0d*X\x1b[\ +\x9ft(5`\x86\x00\xb2\xd38za-\x89(\x14\ +5;\xf8\xaa\x9f1\xa7B\x09A\xb7\xe8#\x9fBq\ +~\xd6\xb1\x17\xe2\x82\xab7\xd5_-y8\xf5\xb0\x8f\ +\xcb\xdb;\x8a\x1e\xca\xa9o\x5cW_\xd1\xae\xaf\x9e\xa3\ +E\xda!=\x98\xf4&e\xed\x15\x8f\x05\xecb\xcb\xcf\ +n\x8e\xbezZs\xf7\xd8B\xd2d;\xd4\xd8N\xd3\ +\xc1\xae\xf6\xa6\xb8\xaa\x8e\xba+Pe\x17s\xaa:I\ +\xc9\xdc9\xb7\xe9\xce\xb9\x87\xaaB\xc5[\x95\xc0j>\ +\xf0\xbe\xb1\xdc\xc1\xc0c@_\xf4\xdeUf\x9a\x07\xd6\ +\xf2\xf4\x06\xfdc\x93\x89\xf9\xb6\x0d\x9f=&\x97\xb9\xe4\ +]\x22.=\x95\xe1Xto\x95\xd9\xaa\xe3\x9e.\x93\ +\xdeo\xf9\xb8\xe6P\xa0\xec\xeb\xe7w\x84\xbc\xcakT\ +\x93\xd8n/\x91\x1a\x18<\xf7k\x0c\xd7\x88\x99k\x93\ +\xdeCE\xe9\xb9\x8fm\x10\x9f/\xf9\x85\x13\xb2\xf8\x16\ +x\xea\xcc\x01\x84\xaf{#>\x8c\xff\xb0\xb5\xe4\x86\x9c\ +\xc9\x08\x85\x15i\x867kW\x0c\x05\xc3\xfc\xc8;H\ +*\xd3\xfa\xf9fmv\xc1\xc29@\xe6.\x22\ +f\xb7\x0b\xbc\x94q\xf8Z\xa8\x97I\xf1\x7fg\x8a\xbf\ +p\x8b\x0f|\xd2\xefe:\xbf\xfe-\xca\xff\x87\xbc\x1c\ +h\xf3$D}\xac\x9d\x5c\x826Q\x1eQ`{\xf8\ +\xf6\x12\x7f\xd2'\x0d\xbf\x8f\xd1\xec\xe1%&\xc1\x1cB\ +\xa6nK\xb9\xf5\xec\x07\x95\xcd:\xf2\xde\xfb\xfa=\xca\ +\xd5-\xb39S\x91\xcd\x14#\xd5\xf9\xa4\xaa\xd7H\x0a\ +{\xc2/\x19\x0f~U_A\x01o\xb2C\x7f\x9b\xcc\ +\xbc\xf3\xec\xb2\xf92\x9e\xe07n\xd9f\x22\x8ae\xba\ +\x8e\x06fn\x1a\xdc7\x12\xa6\x9a\x91\x88\xc8\x86\x93\xa6\ +\x1cv1\xeb\xa6\xbf. \xae\x8c\xe6\x84\x9fF\xe2~\ +\xc9x\xf1\xab\x9e2\xc86#\xf9\xf8W;i\xdf\x0d\ +uU\xe5\x1e\xaf\xe1\x93&\x09\xff\xee\xdf\x7f\x1dA\xc6\ +\x97_\xe2\xfe\xbahB\xf5\xd4\x90\xb4%\xfbo`\xa6\ +\xbd\xbb\xb1\xb32t\x10\xd2\xe8R\x82\xcc\x03~\x89)\ +\xa4\xd7\x05\x95\x8c\xb38\xd1z\xba\xaa\xfd\ +\xedt\xa1\x80\xd09\x05\x15z\xfb\xc0\xbc\x5c\xab{\xb1\ +\xf8\x9e\x80j\xe5\xae\x0b\x99y/\x1f\xcb\xa2\xdb\xe4\x0d\ +\x96f\xf2<\xfe\xf5R^|\x81\x83\xb6}b\xa2r\ +\xbe\x09B\xb4.\x9e\x13~(c\xbd\xa4\xdd\xaf\xcf$\ +K\x9d\x22\x17}m\x04q@\x08f[n\xf0\xdfT\ +'\x9cJ\xfc\xac\xac^\x17\x0em\xe5\x93\x02GK}\ +\xb1~\xa2j\x7f\xeb\x92\xcd\xa9\xc3\xd7\xeas\xf4\x1bV\ +>\x9c\x82d\x1b\x19\x07\x17\xdf\x1b\xae*\x9cS\xac/\ +=\xb7\xf2V\xbfOos\x06\x91\x86\x10\xfd\x8b\xef\x8d\ +P\x0d|_br$`.*\x97?B\xe5h\x9a\ +\xcd\x9e\x90\x80M\xdcg\xd2\xd40\xd3\xf1F\x04\x19c\ +\xa4;\xaet\xcc\x0a\xefz\x19n\xbdyf\xe1\xc3\xc2\ +\xe2^\x84\x86\xc9:.\x1d`C\x14:\x22\xf4\xeb\x93\ +\xb5\xa4]\xa0;_V\x9a\xef\x9a\xc3\x8b\xa7\x22\x08I\ +\xb9\x86\x10^\x14Rr\x82\x7f\x84\x89\xd5\xf3b\xafq\ +\xb9\x08\x1cDM$,\xc1z\x91]\xa0}}\x8e\x99\ +\xcc\xf6\x89\xe7\xd8\x84\xe3r\x06\x11m\x0eh\xef=\xc5\ +\x9fTj\x9f\x99W\xedMx\xf3\xe5\x0b\xfbb\xe3\xc1\ +\x81\xaf?\xa5\x89\xdb\x99M\x16_\x17\xfde\xc2}6\ +\xfe\xb9\xa6\xa5\xe6\xe38\xed\x16\x07\x8f\x7f]G\xfe\xec\ +\xbae\xe2\xbc\x90\x8dpH\xe4\x1e\x1cx{;i\xdc\ +\x00\xf1\x90*n\xe3\x8d\xcf\xf7z\xad\xd5\x8eF\xce\xe7\ +\x95\xf5o\xe0 z\xbc\xab\xe1\x97\x88%\x7f)1A\ +7%\x9e\x91\x1e\x84@\x0f\xc0\xb4R%\xb1EUV\ +;r^F\xe8\xf0\x90QF\ +\x09V\xbcN\xb6Al\x8fV\xeb|O^\xbfS\x94\ +\xb3P\x83t\xf4#\xdb\xc5\xd1UWU\xf5\xdd\xb7\x87\ +\xcfT[{X.\xb0\x9fcL\xce\xa0t\xee\xd9\xa7\ +\x0d'Ip\xbeV\xa8x\x9a\x06\x1e\x1c\xf1kF\x92\ +\x00bljp\xc3\xc0fg\xc8;1;\x1e?\xae\ +\x9f\x95\x06\xd3\x1cL\xe5\xfc\x06\x18?'\xf6\xd3\x944\ +\xffiS\xa80\xd9\xcf\xfbx\x8a\xc5\x91\x10\xffw\x83\ +\x057\x22\xa7\x80\xac\xe4\xce\xda\xf7#\x9dMo\xd0=\ +\x1b%\x97\xaf/\xd6\x0f_\xcd\xa9\x22\x09\xe8\x9fY\xba\ +Q\xd7\xb5\xf8!\xf7\x8dp\x99\xad\x92\xd5~\x9ft\x1c\ +5RC\x10\x9dS\x99\xee\xd3\xec\xe7\x94\x8a\xdd\x118\ +c\xb8\xcf>\xc5\xa2n\xe04@\x93\xd6\x001\xa7h\ +!)\xf6b\xf6\x86\xe1>\x9ft\xc6\x88\xd7\xdd\x18\xb1\ +\x01\x99\x9d\x946\xf1E\x88\xbb\x022\xeb\xcd1]\x99\ +\xac~'\xf2e\x1c\xf8\xad\x03\x07\x97\xb2\x9d\xf9\xa0]\ +\xa6\xe4\xa1\x8c\xcc:\xf0\x9cM\xd2x\xba1\x97\xdc|\ +\x8b\x03v\xbf\x22wm]\xb4\x1f\xfdZbr\xd2t\ +\xa4\xb7\xac\xfc\x04D\xe0\xc0s\xdb\xcf\xe1\xf7\xc3\x85\xaf\ +\xaf\x02\x7f\xf7J\xe4\xdf\x18\xf7\xf0u\x89II\x82\x8e\ +\xbb\xfev}\xc4\xb4\xdc\x92\xc3$\ +\xa3_A\xc0Y>\x9b\x05c\x8c\xc2\xfb/Cd\xc6\ +\x07]\xd5\x9d\xe4x/t\x93\x84\xf4c\x0e!\xfb\x0f\ +\x99a\xbc\x86Kj>\xce\x0e\xd1_\x8e\xc0\xb9\xf1\x83\ +\xdd\xc5\xfe\xbe\xc2\x95e\x93\x85\xd5\xb6>\xf4\xd5\xd4\x9b\ +5DwF`\xbf\x14n\xbf~\x0a\xb5\xd6\xc5\xe6\x19\ +\xfdV\x8b\xef\xe6\xb8.=\xc6HLt\x14\xd7\x15\xde\ +\xb3\xc8\x09\x0eI\x0e\x0e\x81\x86\xa76[3\xb6\x0f_\ +\xfa?\xf6yY\xc6\x91\xd7\xc8\xe1\x9bu\x07\x97N\x0d\ +|\x05\x84T\x81\x8c\xcf\xca\x86\xb9b:\xff\x138\xa9\ +\xb8}3\xf4v.Y\xa8\xb1\xc0_U\xf7\xa8\x14\xf4\ +vR\xdc\x9f\x98\xc7\x12\xc1}\xa1=\xd1\x08Fc\xa8\ +a\x08\x80\x0c\xc0f\x00{\x00?\x80w\x00\xd9\x00\xe5\ +\x00$\x00\xf4\x1f\x01\x09\xefs6>\x06~\xf8\x98l\ +\xc6\xc7h\x08\xb51d\xe6\xd6\x01\xcf\x87\x01,\x008\ +\x06\x10\x0a\x90\x03P\xc9\x04\xe3\xcf\xac\xa8\xc4\xc7(\x14\ +\x1f\xb3\x05\xf8\x182\xed\x5c\xa0B\x1b'>\x87\x0f\x02\ +D\x01\x941\xc1\xb8\xf6U\x94\xe1cx\x10\x1fSN\ +f\x9a\x07T\xf8>\x03\xc0\x15\xe0;\x13\x8c\xdd\xdf\x86\ +\xef\xf8\xd8\xceh;\x0fz\x99\xef\x10\x13\x01\x1c\x00r\ +\x99`\x9c\xfev\xe4\xe2c=\xb1-\x1fz\x81\xf7|\ +\x00;\x01\x92\x99`\x5c\xfe5$\xe3c\xcf\xd7Ss\ +\xa0\x0d\xef\xa7\x00\xf8\x00\x10\x99`,\xfeU\x10q\x1e\ +La\xf4\x1chq\x7f\x0e\x80\x95\x00\xf1L\xd0\x7f\x16\ +(\x88\xc7y\xc2A\xef9@h\xfd\xces\x03\x18\x01\ +\x140A\x9fYh\x8d\x02\x9c7\xdc\x04:\xca\x82\x16\ +\xf7\x1a\x08p\x14\xa0\x8a\x09\xfa\xca\x02uT\xe1<\x1a\ +H\x0f\xfe\xb7\xe1\xfd)\x02k\xad\xef\x0b \xe2\xbc\xea\ +\xd6\x1c \xb4\x96\xf9GY\xbc\xefS\x80\xbc:Fh\ +\xb1\x16t\x91\xf7P\x9f\x80k\x0aK\xe6\xf7=T\xe1\ +\xbc\xa3I'$\xb4\xd6\xf7\xa0N\xc9\xd2\xf5\xfa.\x0a\ +p\x1evZ\x1fl\xf1Y\xb8\xa7d\xed\xf1\xfa>\xe2\ +\x09-\xec\x03\x9d\xe4=\xb4)\xf90\x01\xed,\xd0\x07\ +>\x84\x16v\xc2N\xc8}hWd\xe9{\x7f\x0f\x88\ +8O;\x5c\x07Z\xfc\x0d\xfa\x15X\xf6\xfc\xbf\x0f\xc9\ +\x84\x16>\xa3\x0ex\x0f\xfd\x8a\x0eL@+\x0b\x8c\x81\ +\x03\xa1\x85\xef\x98\x0a\xff\xa1o\x99\xe5\xc3\xfd{\x91\x8b\ +\xf3\x98\x1a\xff\xe1\xbcpe\x02\x1aY`,\x5cq^\ +\xb7}\xf7a|\x11+n\xe7\xef\xc7w\x9c\xd7mu\ +~+&\xa0\x8d\x85\x9e\x81U\x1b\xde\xc3\x18\xd3(&\ +\xa0\x8b\x85\x9eA\x14\xa1u\x5c\xf1B\x02+N\xf7_\ +B\x19\xce\xf3F\xfe\x1fc\x02\x9aX\xe8Y\x1c#4\ +\xe7\xe5\x842\x01=,\xf4,B\x09\xcd9Y9L\ +@\x0f\x0b=\x8b\x1cBs>\x1e+'\xeb\xdfC%\ +\xa19\x17\xb3\xb7i\xe9!\x88\xa0\x84\xbd\x10\xc2(a\ +\x0f\x84\x10\x0e\xc1\x16\x10j\x06\xfc\x1c\xfc<\xfc^\xaf\ +\xd3\xce\x10@\xde\xdfg\x02:\x18\x07\xc8\xbfF~\xee\ +\x97@\x0b\x0f\xc8\xa0\xc5Gg\xa3\xa5\x8e\xab\xd0\xb2+\ +\xdb\xd1\x0a/\x13\xb4\xe2\xeeA\xb4\xc2\xef0Zq\xcf\ +\x0a\xad\xf06A\xcb\xae\x1a\xa0\xa5\xcek\xd1b;e\ +\xb4\xe8\xa0,Zh2\xae\xc5\x9c\xf8\xab\xe6\x02\xe4\xfd\ +;&\xa0\x831<\x07\xff/\x04\xfc+uZ\x85V\ +\xfa\xdb\xa251\xbeh]\xe6\x07\x94\x5c\xf2\x13m\xa8\ +*E\x1bj\xabQ\x94T\x87\xa2\x0dd\x80\x06\xcaO\ +p\x0d\x7f\xdfP]\x8a\x92K\xf3\xd0\xfa\xef\x9fPb\ +\xec}\xb4\xf2\xc1q\xb4\xd4u=Zd5\x15%\x18\ +\x8b\xfe-s\xa11\xff\xbe\xb7\xe9\xa0+\xdf\x0b\xcd\xa5\ +\x00\xcfW\xa3U\xcf\x5c\xd0z\xc0\xef\x86\xaa\x12\x140\ +\x17\xednk\xa8.C\xeb\xb3\xbf\xa0\xd5/=\xd0\xd2\ +\x0bZh\xa1\x854e\x1d\xe9\xbb\xf3\xa0\xb1\xf6Bo\ +\xd3A\x07\xbe\x0bc\xb2\xbd\xfc\x86!Z\x1b\x1f\x82\x92\ ++\x8b\xbb\xcd\xef?\xcd\x85\xda\xe4\x08\xb4\x1c\xac\x1fE\ +V\xf2-t\x05&\x18\x8f\xce\xa3\xef\xd7\xdd\x00\xe3^\ +h>\x11-\xbf\xbe\x0b\xad\xfb\xfa\x1am\xa8\xaba(\ +\xdf\xdb\xcd\x83\xfaZ\xb4.\xe3\x1d\x98\x07\xfb\xd1B\xcb\ +\xc9\x94y\xd0\xdbc\xd2y\xf4]\xdec\xef\x9a(Z\ +r^\x13%~|\x04\xd6\xec\xaa\x1e\xe5{\xbbyP\ +G\x04r'\x14-uY\x07\xf4\x03\xb1\xbe(\x0b\xfa\ +\x0e\xe0;\x0f\xd6\xde\xca\x80\xa3(\xb9\xf8G\xaf\xf2\xbd\ +m#\x97\xe5\xa3UO\xce`kQ\x1f\x93\x05}\x03\ +`L\xe1\xbe\x8c\xf8>\x00E\xeb\xebz\x9b\xdd\xd4\x1b\ +\x99\x84\x12?\x07\xa3\xc5'\x17\xb0\xe6\x00]y/\x02\ +\xf6\xed+\x81N\xff\xbe\xb79\xdc\xa9V\x9f\x13\x0f\xf6\ +\x8c\x1bXk\x01] \x82\x96\xb9oFI\xf9\xe9\xbd\ +\xcdV\x9a\x1a\xa9\xf0;fg\xea\xfd\xf1\xeb\xc3\x00\xef\ +O\xd9%=\x94T\x94\xdd\xdb\xec\xecR\x83v\xa4\xb2\ +\xab\xff\xf5\xfe8\xf6E\x80\xf5\xb3\xf4\xa26\xf6\x1e\xd1\ +\xbd\x81\xbd[CM9\xdaPY\x8c\x92+\x8b\xb0\x9f\ +\xd85\xf8=\xbd\x1b\xb98\x17-\xf3\xd0e\xe9\x034\ +\xf2\xbe\xe4\xccR\xb4>7\x99.Vz\ +qS\xef\xc9R8\x07\x5c\xd7\xa3$\x02\xed6\x8a\x9a\ +W7)\xbaio\x8f}o\x03\xfauN\xcc\x03c\ +\x98E\xd3\xf8A\x1d\x91\xa2K\xf5\xb2.\x0d\x9e\x0fc\ +O`\xdc\x18-\x0d\xc6\xa2\x95\x9c^\xcc\xd2\x03@\xff\ +a\x8c\x1dM\xeb>\xf8,\x94\x17\x84\xfdc\xd1\xde\x8f\ +\xd1\x15\xc1\xde\xe3\xaaPWJ\x1c!\x0d\xad*\xf8|\ +\xef\x8f\x7f\xaf\xf2^\x04-<0\x05\xb3\x8d\xd0\xd2\xea\ +\xb3>\x82\xbd\x82\x22\xf3\xbc;\x98\xcdJ\x01\xad\xcb\x88\ +\xa5\xad\x1f`\xafStH\xbe\xf7eXo\x01\xec\xd3\ +`\xdc\x0c-\xb2\x13\xc6yA\xfb-\xd3\xf0\xbe\xa9/\ +\xc2X,\x1a-6\xe3\x06b%\xb6\x8f`\xd8~\x95\ +\xd9\x01x\x08\xed\xf1\xb4\xb4\xba\x94H\xb4\xd0r\x0a\xf3\ +\xbd3P\x96YLBk\x13_\xd0\xd4\x9f\xea\xe7n\ +\xbdO{o\x8d\x97\xf9D0^/;?X\xa4:\ +\xb4\xc2\xdb\x94y\xdf\x17@W\xf9\xcd=hC}\xe7\ +\xed\x83u\xe91x\xfc(\x93\xcdg\x86\xf3_\x18\xcb\ +\xc7\xa1\xc5\xa6\x0e\xed-L\xedC\x01<,\xb2\x9e\x8e\ +\xd6\xffH\xe8t\x9f`\xdc`\xb1\xfd?\x18/\x06\xde\ +\x15\x18\xd7\x03\xd7\xc0\xce\xb6\x9a7^\xcco73\x16\ +E\xab\xc3\xaft\xbaO\x98>sm'\xf3\xca4\x86\ +\xf1_\x10\xad\xf4;\xdc\xf9}\x1f\x90\xfd0\xd6\x9e\xe9\ +\xc7\x09\xae\x01\xd0\x87EC>B\xd5\xa3S\xcc\xdf/\ +\xbaC\x84&\x9b\x1f\xb9\xa2\x08\x8b\x09az9\x89\xd9\ +\xb3Ti\xf2\x0f\xc1\x18\x03\xc2>\xb1\xde\xa7\xbd\x07y\ +\x0fc,`~eg\x1b\x8c\xc7):4\x8d\xf9\xf5\ +$\xa8\xd7\x1e\x94\xa5\xc9\x8fY\xfb\xe5)Zh&\xc9\ +\xfc}\xa3\xe7\x18\xd1\xa8\xfbc\xfb>\xb0\xbfb\xfe1\ +\x12\xc1xY\xfb9\xb8\xf3}\xc3\xf6\x00L\xb8\xa7e\ +$\xff\xc1\x9e\x07\xf6\xbb\xb3\x8d\xf8\xf1!Zh:\x1e\ +\xed}{o'\xb0O\x02\x8b;\xebl\x83\xf9\x02E\ +Vr\xff\x16\xff\xa1\x8c\xcc\xfa\xd8\xe91\xaay{\x97\ +I\xec\xfd\x9d\x00\xd8\x03\xd4D^\xeb<\xff\xb1\xb5\xed\ +\x1f\xb2\x03cv\x7f\x19\x9a\xf2y\xfej\xfe\xffL\xf9\ +\xf7\xf8\x0f\xd6;\x96\xfc\xc7\xf9\xff/\xca\x7f\xa8\xff%\ +t\xdeVN\xd1\xff\xa4\xfb\xc0\x18uE\xff{\x8b\xf9\ +A\x99\xbfot\x1c#\x9a\xf7\x7f\xa9}l\xff\x17\xd7\ +\xe9\xbe\xd5~y\x06\xe6\x8c\x14\xf3\xf7\x8d\xce\xe3T\xfd\ +\xc2\xad\xd3c\x04\xf3\xb4`\xfcu\x9f\xb0\xff\x1c\x9fG\ +\x9b\xfd\xe7\xcdm\xe6\xb7k\xd3\x1b\x98\xfd\xd7\x9a\x06\xfb\ +o=s\xfb\xfe\x9a\xfa\xd5\x05\xfb\xef\xe3\xd3\xcc\xdf/\ +\x06\x8cS\x99\x9b\x0eM\xf1\x12\xb0\x8e\x1b\xd3\xc7LB\ +\xffO\xd8\xe5N\xf7\x09\xe6\x92\x94_\xdf\xfd\xef\xf1\x1f\ +\xfaJ\x8f(b6@X[\xad\xfe\xfb\xe7\xdf\x03|\ +\x06\xe6o\xc2\xb5\x95i\xd7I\xcc\xff\xfb?\xa0\xcf\xd3\ +\xe0\xff-/@KN\xa91\xff\xba\xc6\x08\x80w\x05\ +\xee\x03a\xad\xcd\xce\x00\xab\xad\xc3\xcc\xeb$\x94\xfd0\ +\x16\x98\x86\xfc0XC\xac\xcf\xea\xfe{E:\x07z\ +\xdc\xa33\xf7\xea\xe5\xb1\xc0\xe2\xbf\x12\x9ew\x9a\xf7\xb0\ +aq\xccF\xa2\xbdO?\xad\xd8?\x16\xeb\xef\x1fa\ +.E\xc9\xcb\xeamz\x19\x8d=x\xee*\x91\x86\xf8\ +O\xa0\xfb\x94]\xda\xda\xf7\xd6~\xa8\xbb]5@k\ +\x93\xc3\x7f\x8f\x94\x08\x94\xf8\xf6.^\x13\x8d\x89\xdf\xdd\ +\xee\xa2\xb1fA\xc6[\x9a\xde}\xa8\xd3\x14Y\xf7\x01\ +\x9bF[\x18\x0aRr6:\xd1`|\x1f\x1c\x9b\xbf\ +W\xbf\x11\xc1\xec\xbdU!.\xb4\xe7\x7f\xc8\xd4v\x8c\x9e\xe2\xbf\xef\x01\ +\xacfnW[\x8f\xc7\x84\xc0\xfa/fR\xd8y1\ +\xb0\xc6TW\x1a\xe9g\x0aZ|\x5c\xa5\xf7\xd7\xae\x7f\ +\x81\xff\xf4:\xc3\x09\xa3]\x14\xe8kK\x80\xcc\xb9\xdf\ +\xe5\xb3\x06\xe0\xf92\xf0\xdc\xa1>\xb7\xdf\xeb\x8b\xfc7\ +\x16\xc3\xce\xec\xc1b\x86\xbaT\xffM\xa4\xb9\xfe\xdb\x89\ +\xf9\xd8^\xad\xbbu\x00a\xfeR\xa1\xe9_\x10\xe7M\ +\xcb\xfe\xbf\xf8G/\xf0\x9f\x12_\x02\xd7Y\xe83\x82\ +6\xf9b[%\xbc\xf6\x9ep\x8b\xf3\xde:Q\xff\xf1\ +\xd5M\xba\xd4\xa0\x85\xb1nE\xb6\xb3\xfa\xb6\xdco\x04\ +V\xf7f\x03\x16\xe3\xf8{\x5cG\xab\x82\xceQ\x8f\xdb\ +\xea\x01\xfeC\xde\xc3\x06\xf3q\xe1\x1e\x8d\xf8)\x08\xab\ +1V\x0e\xcfzs\xdf\x8c\xd5\x94\x87{\x10\xa8\x8b\xc3\ +z\xb0\xad\xea\xbf\xfe\xfcJ\x93-\xf7w\x0d\xd6.\xc7\ +\xf4\xfd\xbf\x81\xf7-\x01\xed\xfa\x9d\x01\xb5\xef\xf6\x04\xff\ +\xe1Y\x11T\x19R\x87\xe5\x9d\xc2\xf3\xc1\xe0yQ\xf0\ +'\xc3\xea?\x97\xe4br\xa4\xcf\xcb|z\xa37\xf9\ +\xdfC\x0d\xd6\x7f/g\xd5\x7f\xff'\xf9\x8f\x9d\xff\xe0\ +\xc9:\xff\xe1_\xe4?\xf4\xeb\xb1\xce\x7f\xf9\x07\xf9O\ +\xae\xc7t\xcc\xe2\x93\xf3\xff>]\x8f\xc5\xff\xdf6\xe8\ +\xcf\x83{\x07\xd6\xf9o\xff\x16\xff)\xe7?\x86`\xe7\ +\x86\xf7\xb1\xf3\x1f\xe9|\x06(\x8d1{}\x9c\xffM\ +\xe7\xbf\xde\xde\x87\x16ZJ\xf7\xb5w\x1e\xf2\x9e\xae\xe7\ +?C;+\xcc\xe9o\x07X\xb3\xc1\x98\x8a\x8f\x96.\ +\xfe\xbf\xfb\xbf\xe1\xffx\x94\xf8!\x90N\xdcnn\x94\ +\xf3\x9f\xc3\xb1ZD\xd8\x99\xf0}\xf7\xfcg:\x9e\xff\ +.\x82\xf1\x12\xda8\xebR\xdf4#-\x0a%\xc6=\ +\xc6b\xe2\xdb\x8d\x11\xf4\xff\x9fU\xc7\xec\x83U\xc1\x0e\ +\xb4\xe3\xa9#\xa5\xe6?U\xff?\xe5\x1c\x19Zrp\ +\xff\xc4\xf3\xfa\xec\xcfX\xdd~\xa8\xd7\xff%\xe7\xbf\xbf\ +\xa3'\xff;\xcay\x81\xe7r\xc0:\x7f\xd4\xe3\x7f\x84\ +1_B\x97\xf1\xbb\xf8\x09cQ\xb4\xd8~!Z\xe1\ +g\x8d\xcd\x03(\xab\xa1/\x02\xb3\xf5\xc1\x9c#R\x1d\ +e\xed\x81\xf9g0n\x0f\xda\x04k\xab\xb1Z\xb4\xd0\ +n\x03\xeb\xf7\xc0\xfcT\xe8\xe7\x801>\xd8\xbb\x0e\xe3\ +\xb41_R\x9f\xe5{# \xef\xef\xd3\x95\xff\x1d\xd4\ +\xf2\x82zq\x87\xfcg4\x9a\xcep\x92\xc0r,\x8a\ +\x8f*av\xf8\xb2\xcb\xdb\xc0\xba\xbd\x1f\xd3?\xe0Y\ +.\x15w\xad\xb0\xdc\xc22O\x03\xa0\xc7\xadA\x8b\x8f\ +)S\xf2M\x80\x0ci\x8e5\xeb\xf3\xd2\xea\ +\xeb\xd8\x0dJ[^\x0fF\xd1\xac\x96\xd7\x5c\xadn\x8f\ +=\xa0-9m\xae\x1bZ_\x1f!\xb5\xbe\x9eKl\ +}-Z\xda\xfazp\xdb\xeb\xac\xd6\xd7\x5c\xe1\xad\xaf\ +9\xda^\xdb\xb4\xbef\xfb\xd35\xc2j\xac\xc6j\xac\ +\xc6j\x0ci6\xad/\xff(\x8f\xc3[_\xb7\x93\xef\ +Y\xad\xaf\xb9\xfe\xb4~\xb4]o\xda\xaeGm\xd7\xab\ +?\xado\xed\xd6\xc3V\x04q\xb5_O\xdb\xae\xb7m\ +\xd7\xe3\xb6\xebu\xbb\xf5\xbc\xf5z/\x05~\xa8 \x94\ +qeC\xc4(\xbfWAhj\x1d\xe8zj\xb8N\ +\x13\x86\xeb9\xd5\x00\x0d\xbd\xa0k5\xe0\xcf\xce\xc6i\ +\xb1\xc7ik\xa7c\xd1\xda\xa8\xf4[\x08\xc0\x10 \x02\ +\xa0\x94\x09\xf4\xcc\x8eP\x8a\xd3h\x88\xd3L\xf38\xb4\ +\xf9\x0e/\x80.@\x5c/\xf1\xb8;s#\x0e\xa7\x9d\ +\xb7\xb3c\xd0\xa6\xef\xe2\x007\x01j\x99\xa0?]E\ +-\xde\x07\xf1?\x8dA\x9b\xbe\xcb\x13\xfe\xae\xbc\xe1(\ +\xbcOT\xc7\x80J\xdf?1\x01\xcd\xf4\xc6\xa7\x8e\xc6\ +\x80\xd0z\xce\xffM|\xa76\x0f\x9a\xde\x856}\x87\ +r\xe2&\x13\xd0\xc8h\xdc$\xb4\x91\x898\xa0\xac\xec\ +\xcb\xb2\xae\xb3\xa8\xc5\xfb\xdav}\x8fc\x02\xdaz\x0a\ +q\x84\xd6\xfa\x01\xd4\x17\xfa\xd2\xfa\xde]4\xe0}n\ +\xd4i#\x98\x80\xa6\x9eF\x04\xa1Y\x9fgf\x9d\x96\ +Q(%4\xefez\x9b\x96\xdeB\xe3>\xae\xe7\x9e\ +\xb9\xb7E}Fx\x0dkG\xc1\xb3R\xe19R-\ +\xcfn\xeb\x99\xbaB\xb0\xeft\xac\x93\xdcQ\x9f[\x9c\ +5xT\x09\xabOZ\xf9\xc8\x1e\xady}\x0b%~\ +x\x88\x12?=\xc1\xce\xba\x85\xf5\x87+|-\xd1R\ +\x87\x95X=SJ\x9dJ\x86\xd6\xd5k\xdc\xbf3\xee\ +\x19\xf0\xbc=\xf3\x89X\x9f\xb1s\xd7\x8a\xb2)\xb5y\ +\x7f\xd3\xe0Y]\xf5YqX\xed\xe8\xe2\x93\x0b\x9a\xe7\ +\x0d\xfd\xe9c\x9c\xed\x02\xf2\x1c\xcc\xed\xd2\x8b\x9b\xd0\xda\ +\xa4\xb0.\x9f)J.\xceE\xabB\x5c\xd0\xa2#3\ +\x18q\x9e4\x83\xfa.\x8c\x9d\x93\x01\xcf\xf1\x86\xf5\x96\ +\xe9\xd1`\xbdfx\x86\x1f\xe5\x19L\x5cs\x0c\xf4\xbd\ +\xc8F\x11;'\x86\xde\x0d\xd6\xda\x84u\x8c)\xf5\xf6\ +\x98p\x0c`\xdf\xc1<\x85\xf3\xbd3\x0d\xd6\xe3&W\ +\x14\x82~\x15\xa0\x0dU`\x9e\x90\xea\xff\xfc\x1d \x1b\ +*|,z\xbf\xaf\xed\xfa.\x82\xd5Cl<\x93\xb2\ +C\xfa\xabJ\xd0\xda/\xcf\xd0\xca\xfb\x87\xd1R\xd7\xf5\ +h\x89\xbd\x1aZ|B\x15-9\xbb\x0c-\xf3\xdc\x81\ +\xad\x01\xf5?\x12\x01\xb3\xc9\x1d\xdf\xa3\xb2\x18\x93\xa7\xcc\ +Vs\xb5\xf2\xe1IJ\xaduj\x0d\xc8|b\xdc#\ +\xb4\xd4iu\xeb\xba\xdbMg\xb4\xe2\xf5\x9a\xc18\xc2\ +z\xa2\xb0\xae;\xa9\xe0[\x87c\x00\xcffg\x9as\ +\xb6\x01\xed\xf0\x9cQrY~\x87<\xaf\xf4\xb7E\x0b\ +\xcd$;'\xc31}A\x18\xabo\xff\xbbw\xa9&\ +\xca\x0b%\xec\x97\xe8\xfd\xfe\x03\x1a\xe0Y-T\xfb^\ +]\x86\x9d\xa5\xd1\xd4/\x1a\xc7\x15\x9e\xd3[\x9b\x10\xda\ +\xe1\xbda\xed~\x06\xac\x8b\xb4\xf1\x1e\xbc\xbb\xe4\x8a\xa2\ +\xf6\x04\x92I\xd8\x99Q\xd8\xb9\x02]\x95\xd7\xe0\xfe\xc5\ +v\xca\x14\x99@m\x0e\xc4\xf8R\xf4\xe7\xde\xea?x\ +\xff\xaa\x82\xcfS\xa5\x0d\xd6\x0b.\xb4\x9c\xd2}]\x1e\ +\x8cA\xf9\xf5]Tu(\xa8\x1f\x15\xdb\xcd\xed%9\ + \x82\xe9\xb6\xf0\x5c\x94vs\xb3\xbe\x96r6(=\ +\xe6&\x5c[\xcc\xa4\xa8\xcb\x02 o\xcbo\x1b\xf7\xce\ +;\x00\xc6\x1c\xae]\xd4\xe4^}N\x02Zd%G\ +\xbf}\x1c\xe8\x1f\x5c\xf7\xb1\xf3<\xda\xbe\x03\x91\xd7\xd1\ +^\xd1\x87\x00Me\xee:T\xcf\x15\xaeys\x9b\xce\ +\xcf\x12FKN\xa9a\xfaR\xbb\xf7,%\x02;\x9f\ +\xa9\xe7\xfb/\x08\xf4Q\x13\xaa<\x81\xeb\x1d]\xe7$\ +\xd4\x0b\x0e\xca\xa1\xf5\xb9I\xed\xe7\xda\x8f\x04\xb4\xf0\xa0\ +l\xcf\xd7\x22\x06\xfd\xaf\xbc\x7f\x84\xfa;y\xcb\x98\xa2\ +\xcf\xd0\xedy\x14\x19@M\xd6\x90\xf23\xa8\x9f\xc1\xc4\ +\xea?\xc3\xfb\xdf\xe1\xfc\x0f\xe8\xd9\xf9_\xd4+\xf3\x1f\ +\xc8?7\x1d\xaa\xf6\x1cx&\x13E\xef\xa1\xd7\xb3\x84\ +\xb1\xbd\x12u\xf9\x17\xd9;\xf2\x0f\xae\x7f@7\xab\xcb\ +\x88\x05s0\x1d%\xfdJ\xa3\xa0 \x03;\x17\x0d;\ +G\x8c\x9e\xeb\xdf\x1ds\xea\xeb\xdf\xab\x1b\x0c\xe4q\xa3\ +M\xb6\x05Z\xeaZ\xc6b\xd8:\x0f\xf7lE\x87p\ +\xc0\xff\xc3\xf3\xb4\xe8\xa5\x97\xfeQ\xff\xd9\xcf\x18\xfd\xc7\ +X\x14\x9b\xdf\xd8\xf9Q\xf0,)\x88\xbb\x07\xd1\x923\ +K[\xf3\x95\xd1u\xf1\xa1\xfe{m'u\xfd\xb7$\ +\x97q\xfb`\xc0?h\xabh\xdb\xaa\x9e\x9ci\x1eo\ +l\xaf*\xf8\x1bt\x93/p\xffsL\x19\x93q\xd4\ +\x1a\x11\x9e\x11\xca\xa8\xfdO\x87\xfd?\xdb\xe4\xa3\x80v\ +\xce2\xf7\xcdh\xd9%=*\xd8\x8a\x96:\xae\xec:\ +}p\xff\x0b\xd65x\xfe,\xb5\x86\xed\x7f]\xd73\ +N\xf7\xff\x13\xff\x01J\x9d\xd6`t`g\xedA\xfb\ +]K\x00Y\x05\xd7k\xec\x8c\x90&\xfd\xbc\x13\xe7n\ +\xe1\xfe\x92b\xfb\x05\xe0\x9d\x7fI\xb5\xef\xb0\xd5D\xdd\ +a\xac\xfd\x03\xf6\x9f\x8a\xed\x01\xfa%\x9a\xfa\xef\xbc\x96\ +r\xde`\x07\xad\xee[l\xab\xfeC\x1bP\xa1\xe9\x84\ +\xf6\xe7\x91\xb5\xb4\x7f\x01\x19Zq\xef\x10\xa6\xd7t\xd4\ +Hy\xa9\xe0\xbd\x9f\xc7\xd8}/\xe8\x7f\xd53'\xfc\ +\xdc\xcf(\x0a\xc0\xff)\xba]'\xfb\x9f\xd1\xa2\xffP\ +_\xf0\xdc\x0e\xe6s(\xb6?(\xbd\xa0\x05\xf64\x8b\ +\xb0\xf3\xd4K\xce5\xda?\xddp\xfbg\xc7\xe7\x06c\ +\xf6Ox6h\x0f\xec\xf9\xb1sop\x9ea\x806\ +\xbc\xc69Gs\xff\x05\xb1\xf3\x84\x9b\xfa\x01\xe49\xb9\ +\xb2\x08\xb3\xebc~\x92N\xdb\xbf-\x19\xde\xef\xd6\x10\ +i\x83f\xf9D{\xff\x8d\xa9\xea0\x9diL\xe7\xff\ +\xe8\xc1\xfeC\x7f(\x83\xfc_\x9d\xf3\x01\xb6\xfa\xcc\x1ea\xccw\x00i\ +\x86\xf1\x81\xe4\x92<\xea>\x7fR\x1dJ.\xfa\x81\xe5\ +\xcb\xc2\xf83,_\xaa{\xfc\xe8>\xed\xe0\xf9%\xa7\ +\x97\xa0\xb5\x9f\x82\xd0\x86:b\xa7\xfd\xb7\x0d\xc4J\xcc\ +\xb7\xd7\xbb\xb9Z\x22\xd8\x98\x93\x08Y\x9d\xa6\xbbm\x83\ +~/,\xd6\xaa\xa7\xfb\x00\x9e\x07\xe3b`\xec\x03\xd5\ +\xf1\xad\xa9\xc0b\x81\xb0\x18\x9d\x8c\xb7X\x1cjG\xf1\ +\x080\xf7\x9a\x92/\xd5C}\x801\xbbg\xd5QR\ +av;Z`\xceHu\xd8%,\x1f\xb0\xe8\x90<\ +%\xc6\xc6L\x0a\x8bw+u\xdd\x80\xe5\xc6\xc3\xbc\xb9\ +v|\xf8\x91\x88\xc5\xdc2\x9c\x0f{)\xb9=\xd4b\ +\xef\xea\xd2\xdfb>jJ\x1f\x85Z\xf8\xc4D\x9a\xfd\ +\xbbX\xee\xf7F\xaaq\x8b5\xd1>\x98\xfcb\xec\xd8\ +\xc3\x98\xd1]\xedb3\xea\xd2\xa2\xb1\x1a\x04\x9d\xf2_\ +\xc3\xdc\xb6Sj\xedr\xcf`|\x02\xe4\x11#s\x8e\ +\x0aM\xc6\xb7\x1b{(/a\x9e%M\xcf\xc5rt\ +6\xb7\xcbC\xc7\xe2\xd3\xb18\x22F\xcc\x1da\xacn\ +\x01\xcckn\xd9\xaaC]\xbb\xe6\xd7\x07\xeb\x18\xcc\xf3\ +k\xf5.\x17|\xa3\xd4\x04`\xc4{\x00\xe3tn\xec\ +n\x95\x83\x0c\xf3\xb3i\x1e\xfb\x16\xf7+s\xdf\xd2j\ +\xdd\x80100\xfe\x8f!s\x08\xdc\x13\xe6]\xb6\x92\ +\x1b9\x09X\xecI\x97\xc6\x1f\xcb\xe9\x9f\xd9N\x8eU\ +\xf8\x98\xd39\xc7\xa5\xf9y\xd5//\xb5z\x16\x8c%\ +\xc2\xf4\x80.\xdd\x0f\xc82\xcb\xc9h}\xf6\xe7V\xf7\ +\xac|x\x821\xe3\xdf!\xfd]\xcc\xd1\xe8i\xfa\xa9\ +\xcd\x9f\xdc\xa4\xae\xe7\xd8t8\x7f,\xe84\x7f\xda\xc7\ +e\xc2x5,'\x05\xc8\x09\x08\x18?\x08s5\xbb\ +$/:|\x7f\xb5\xbb=\xfeX|mcl-\x8c\ +\x93\xc3e2\xfc=\xd4\x05Z\xa2\xcb\xf3\x07\x93\x9f>\ +\xad\xe5'!\x13\xc8O\xc5\xee\xc9O@#\xd4a\x1a\ +\xe3\xa3\xe1zU|lNs\xdc\x9a\xe1\xe8\xd6\xe8\xca\ +\xbe\x0a\xcf\xb1j\xbf~\xf9t{\xfd\x82\xe3\xdd2\x9f\ +\x0e\xea\x92P\xc7\x87t\x16YO\xc7t\xc5\xd6X\x0f\ +t/\x15\x9ah\xa7\xae?T\xd0E\xf6c\xf4\x83q\ +o\xbaou\x19F\x7f\xc1\xeeQX\xae\x14\x9c\xa3-\ +\x01\xf7U\xadr\xa5\x1a\xe3\xee\xa9\xeao\xa2\xd8\xfc\x86\ +\xebF\xdb\x86\xe5\xdc\xd3A\x7f\xc3t\x9c/O1\xba\ +\xa1\xae\x0b\xf7'0\x9e\x14\xa3\xdf\xf7@\xbb\xe7b\xcf\ +\x8e\xf2\xc6i\x17\xc1tP\xb8'\x81\xf9\x02Pw.\ +4o\xa3?W\x16\xb7\xfb>\x94ct\xcb\x97\x07c\ +\x04\xf7\xd90\x86\x1f\xf2\x19\xc6\xaab\xef\xb0\xa1\xe0\x9f\ +\xe9\x872\x16\xec\xd7!_`\xce\x1c\xdc\xbb\xd4e\xbc\ +\xeb\xc4\xfee=}\xf7/-\xeb\x915\xdewOg\ +\xe9w\xa4\xfa\x19j\x8d\xa1\xfb\xc7\x969Xx\x9c5\ +\xccS\xa3J?\x94\x1b4\xd0\x8f\xed\xdf\xdf\xde\xa3\xc7\ +\xfe\x9d\xba\x0d\x02\xe6\xbb\x80\xb5\x11\xe6\xb7\xc1=:\x16\ +\xf7\x0b\xd7\x1b\xb0~\xc1\xf9@\x99\x1782\xdf\xa3\x95\ +\x0fN4\xd1\x0fk\xa8Q\xad7\x83\xd9Or\xe8m\ +?\xa1n\xbf\x82\xba\xf2\xcd=\x18\x1d\x98\x8e\x0c\xf6\xbb\ +\x94Zo\x12\xf8\x9e\xb65\x9a\xe5\x86\x08\xf6\xaeR\xec\ +W\xb6=a\xbf\x0a\xeb\x90~\x18\x93\x0e\xf8L.\xfd\ +\x85\x96\x9cY\xd2B.\xb7\xcd\xfbhCG\xd3\xbck\ +c;d\x8c\xfd\x90\xba\xfd\x16\xce\x1f+9,\xcf\x0e\ +\xc6\xe5C\x19\xc8d\xf1\xa9\x8d\xf6\xdb\x8e\xed\xe7\xadb\ +\xc2\x99\x8av\x94\xd0l?\xef\xeb\xfe\x8b\xbe\xee?\xfa\ +\x1b\xfcw}\xd6\x7f\xda\xd7\xfd\xd7\x7fC\xfc@_\x8f\ +\xdf\xe8\xcb\xf13X\xfc\xd0`\x04\x09\x87?9\xf0x\ +#\xb6\xe6\x9f\xac\xd6\xbdf\x03\xffi1\x9e\xe1\xf0'\ +G\xf3\xb8\xc38-1\x84r\x84OS\x9c\x16?\xf5\ +{\xb5\x99[C\x01\xb6\x01<\x05( t\xef4\xces\xda\xbf\xdf\x14\x0b\xd1\xa2n\x09\xed\xb6\xde\ +\xc6w\xac\xf3\xdf\x81\xf5\xb1\xc1\xfe\x14\xdaN`\xbd\x10\ +X[\x01\xd6\x0d\x84\xf5\x10\xb0\xfaA\xb4\xd1\xd1\xf9\xf7\ +\x1b\xdes\xbf\x04\xb6\xd7\x845>0_\x15\x16\x8b\xd0\ +\x80\xd5^\x806)X\xbb\x02\xda\x11\xa0\xed\xbd\x93\xfb\ +\xb9N?\xbb\xd0R\x1a\xcb\x8dn v\x9c;O1\ +@\x90\xb1z\x11t\xb3Ya\xb5\x84$1\xfbX+\ +\x93A\xc17\x94\x18{\x1f\xad~~\x91\x92\xeb\xfa\xfd\ +S\xab\xbaV\xd0\xfePd;\xab\xfb4\x80\xefc\xb6\ +\x192%\xee\x03\xda\xba\xaaC/`6\x10,\x07\x18\ +\xab\x87K\xc9\x93\x866LX\xdf\xa7\xc9v\xd3]\xbb\ +#\xf4C\x1cV@I?\xbf\xe2\x9d\xae\xc7\xf2\xb01\ +;r\xdb~\xe1{\xd22\x8f-M5\xb7\xa0\x8d\xa0\ +[v\xdbF[\x09\xeeW\xacM\x0e\xa7\xd4\x06\xf8\xdd\ +\xfc\x06\x7f\x83\xf9\xd6Mc\x80\xd5z\xec:\xef\xab\xc3\ +=\x9b\xee\xd5)\xff\x0b\xb4a\x9fY\xda\x14\x8b@\xa9\ +g0\x09\xed\xd2>}\x9f\x18\x16\xf7C\xe1{5V\ +;\xe2\x8fc\x89\xd97\xe41\x1b)e\x9efb5\ +q\xbb\xe4\x87\x02|\x86g\x0d`\xcf\xaf'\xe2\xb6T\ +\x06?\xbfe\x8d\x1b\xc8\xcb\xe0\xf3\x98m\x0e\xde\xa7\xcc\ +\xd3\xe0\xcf\xcf\xa76\xfe\x16\x9d\x1c\xff}\xe2\xd8w\xa1\ +\xdd\x15\xda\xd5\xe1\xfbE\x891\xa1\xd4(\xc3\xecq\x7f\ +z\x9f!\xcd\xcf\x9c\x9b\xe7_\xa7k\xadQ\xea\x81C\ +\xb9\x01\xe5i\xed\xe7`L\xdeB\x9f=\xa4\x09\xc3Y\ +uJ\x9d4j9\xd5\x8d\xef\x9f\xfb\xe6\xa6\xd8\x1f\xca\ +\xfb\xd7I\x9f\x1bV\x8f\x5c\x1a\xad\xcf\xfeBy\xd7\x92\ +^b\xe3\x81\xc9\x5c s\x1a\xaa\xcb1?\x0cVK\ +\x06;\xa3A\x8a\x22\x07\x9a\xe4\xcf\x14\x8a\xfc)n-\ +\x7fh\xf2A\x03YU~\xd5\x00\xab\xe5\x0d\xe5\x08\xcc\ +\xa3\x87\xe3\xd7\xb2\xc1z\x1aX\x0d\xe1\x10W\x94\xf8\xf1\ +!\x90\xbfn\x1d\xca\xdf\xe2\xae\xc8_\xd0\xb7\x82\xdd#\ +)\xfe7\xd0\xaf\xb6r\x1f\xd6a\x87\xf7\x84\xcf\xa6\xda\ +h_\x7fZ\xaf\x81\x80\xe70n\xa2\xe4\xf4\x22\xec\x1a\ +\xca[\xe8\x83\xa9\xcf\x89\xc7b\x83\xca\xaf\xef\xc6\xee\x0b\ +\xfd\x0aX]\x94\x86n\xaf\xbf\xcd\xfa\x07\xceG\xf8\xac\ +\xda\xc4\x97\x18?0?+\xb8_\x13p~\x16\xdb*\ +\xd1K\xffh\xa5\x7f\xc1\xda\x19\xe5^&\x98}\x1f\xca\ +\xbf&\x1f[\xdb\x1ay\xf4\xd3\xbf\xda\xeb\x9f-\xebm\ +0\x0e\x8d\xfago\xeb\xdf\xbd\xbd\xff`\x86\xfdW\xaf\ +\xed?{{\xffm\xd3\x8b\xd6\x18\xf8lh\xa7\x80f\ +\x091\xa4\x85\x9d\x82\xb3\xfdgq\x9a\xc7\x00\xb8\xe1}\ +\xfa\x9d\xed\xae\x01\xff\x8c\x1b\xfe\x9d\xc6\xef\x06ua\x0c\ +\x83Z<\x97\xca\xdf)g/4\xe9,\xd4\xef\xd1H\ +s\xeb\xdf\xc3so\x80|\x85\xb1\x7f%\x0e\x9a\xcd:\ +}{9\xd6\xbe\xbf0\xbe\xfa\xec2\xac\xde l\x18\x9b\ +\x02\xfd\x96\xd8\xd9\x22\x8dgl\x18R|\xe1\xf0\xb9\x94\ +x\xd56\xf47\xeeA#\xaea\xeb\x1e<\xaf\x07>\ +\xa7\xd4U\x0b\xd3\xb9\xe0zU\x1d\xe1\xf9\xbb\xf1kh\ +\xf4\xe1\xc28\x22\xe2\xbb\xfb\xd8\x99$\xf0\xfb\xb5\x89/\ +(\xfe\xd4\xdf\xf3\xaf\x00\x8b\xedv\xd3\xc1\xc6\x16\xae\xb5\ +X\x1c4\xb4\x05\x00]\x03\xc6v\xfca\xfeP\xe6/\ +\xd4\xd9\xb0\xfd[\x8b\xf5to\xa7\xe6ow\xdf\x9fn\ +\xbd\xbf\xddm\xff\x07:J\xaf{\ +\x00\x00_\x84\ +I\ +I*\x00\x08\x00\x00\x00\x17\x00\xfe\x00\x04\x00\x01\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x01\x01\x03\x00\x01\x00\x00\x00`\x00\x00\x00\x02\x01\x03\ +\x00\x04\x00\x00\x00\x22\x01\x00\x00\x03\x01\x03\x00\x01\x00\x00\ +\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00\x11\x01\x04\x00\x01\x00\x00\x00@T\x00\x00\x12\x01\x03\ +\x00\x01\x00\x00\x00\x01\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\ +\x00\x04\x00\x00\x00\x16\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x17\x01\x04\x00\x01\x00\x00\x00\x17\x0b\x00\x00\x1a\x01\x05\ +\x00\x01\x00\x00\x00*\x01\x00\x00\x1b\x01\x05\x00\x01\x00\x00\ +\x002\x01\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\ +\x00(\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\ +\x00$\x00\x00\x00:\x01\x00\x002\x01\x02\x00\x14\x00\x00\ +\x00^\x01\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00R\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\xbc\x02\x01\ +\x00\xfa8\x00\x00r\x01\x00\x00I\x86\x01\x00\x8c\x0d\x00\ +\x00l:\x00\x00i\x87\x04\x00\x01\x00\x00\x00X_\x00\ +\x00s\x87\x07\x00H\x0c\x00\x00\xf8G\x00\x00\x00\x00\x00\ +\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\xf9\x15\x00\x10'\x00\ +\x00\x00\xf9\x15\x00\x10'\x00\x00Adobe P\ +hotoshop CC 2015\ +.5 (Windows)\x00201\ +7:03:08 11:38:26\ +\x00\x0a\x0a \x0a \ +\x0a pain\ +t.net 4.0.9\x0a \ + 2017-03-0\ +7T11:32:29-08:00\ +\x0a 2017-\ +03-08T11:38:26-0\ +8:00\x0a <\ +xmp:MetadataDate\ +>2017-03-08T11:3\ +8:26-08:00\x0a \ + image/tiff\x0a \ + 3\x0a \ + sR\ +GB IEC61966-2.1<\ +/photoshop:ICCPr\ +ofile>\x0a \ +\x0a \ + \x0a \ + adobe\ +:docid:photoshop\ +:94a27cdb-0433-1\ +1e7-b02d-9f84d9f\ +5a326\x0a \ + \x0a <\ +/photoshop:Docum\ +entAncestors>\x0a \ + xmp.iid\ +:d3f831c7-1693-8\ +248-a9a0-2c4f91d\ +81b49\x0a \ + adobe:docid:\ +photoshop:c7bad1\ +dd-0436-11e7-b02\ +d-9f84d9f5a326\x0a xmp.did:a36\ +4ea4e-2280-ea40-\ +ae73-10beb7a78ae\ +8\x0a \ + \x0a \ + \x0a \ + \x0a \ + <\ +stEvt:action>cre\ +ated\x0a \ + xmp.iid:\ +a364ea4e-2280-ea\ +40-ae73-10beb7a7\ +8ae8\x0a \ + 2017-03-07\ +T11:32:29-08:00<\ +/stEvt:when>\x0a \ + <\ +stEvt:softwareAg\ +ent>Adobe Photos\ +hop CC 2015.5 (W\ +indows)\x0a \ + \x0a \ + \x0a \ + saved\x0a \ + <\ +stEvt:instanceID\ +>xmp.iid:d3f831c\ +7-1693-8248-a9a0\ +-2c4f91d81b49\ +\x0a \ + 2\ +017-03-08T11:38:\ +26-08:00\x0a \ + Ado\ +be Photoshop CC \ +2015.5 (Windows)\ +\x0a \ + /\x0a \ + \x0a <\ +/rdf:Seq>\x0a \ + \x0a \x0a \ +\x0a\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \x0a8BIM\x04\ +%\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x008BIM\x04:\x00\x00\x00\ +\x00\x00\xe5\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x0bprintOutput\x00\x00\x00\x05\ +\x00\x00\x00\x00PstSbool\x01\x00\x00\x00\ +\x00Inteenum\x00\x00\x00\x00Int\ +e\x00\x00\x00\x00Clrm\x00\x00\x00\x0fpri\ +ntSixteenBitbool\ +\x00\x00\x00\x00\x0bprinterName\ +TEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0fpr\ +intProofSetupObj\ +c\x00\x00\x00\x0c\x00P\x00r\x00o\x00o\x00f\x00\ + \x00S\x00e\x00t\x00u\x00p\x00\x00\x00\x00\x00\ +\x0aproofSetup\x00\x00\x00\x01\x00\ +\x00\x00\x00Bltnenum\x00\x00\x00\x0cb\ +uiltinProof\x00\x00\x00\x09p\ +roofCMYK\x008BIM\x04;\x00\ +\x00\x00\x00\x02-\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x12printOutputOp\ +tions\x00\x00\x00\x17\x00\x00\x00\x00Cpt\ +nbool\x00\x00\x00\x00\x00Clbrbo\ +ol\x00\x00\x00\x00\x00RgsMbool\x00\ +\x00\x00\x00\x00CrnCbool\x00\x00\x00\x00\ +\x00CntCbool\x00\x00\x00\x00\x00Lb\ +lsbool\x00\x00\x00\x00\x00Ngtvb\ +ool\x00\x00\x00\x00\x00EmlDbool\ +\x00\x00\x00\x00\x00Intrbool\x00\x00\x00\ +\x00\x00BckgObjc\x00\x00\x00\x01\x00\x00\ +\x00\x00\x00\x00RGBC\x00\x00\x00\x03\x00\x00\x00\x00\ +Rd doub@o\xe0\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00Grn doub@o\xe0\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00Bl doub\ +@o\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00BrdT\ +UntF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00Bld UntF#Rlt\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Rslt\ +UntF#Pxl@b\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x0avectorDatabo\ +ol\x01\x00\x00\x00\x00PgPsenum\x00\ +\x00\x00\x00PgPs\x00\x00\x00\x00PgPC\x00\ +\x00\x00\x00LeftUntF#Rlt\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Top U\ +ntF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00Scl UntF#Prc@\ +Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10cropW\ +henPrintingbool\x00\ +\x00\x00\x00\x0ecropRectBott\ +omlong\x00\x00\x00\x00\x00\x00\x00\x0ccr\ +opRectLeftlong\x00\x00\ +\x00\x00\x00\x00\x00\x0dcropRectRi\ +ghtlong\x00\x00\x00\x00\x00\x00\x00\x0bc\ +ropRectToplong\x00\x00\ +\x00\x00\x008BIM\x03\xed\x00\x00\x00\x00\x00\x10\x00\ +\x90\x00\x00\x00\x01\x00\x01\x00\x90\x00\x00\x00\x01\x00\x018\ +BIM\x04&\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00?\x80\x00\x008BIM\x03\xee\x00\ +\x00\x00\x00\x00\x0d\x0cTransparen\ +cy\x008BIM\x04\x15\x00\x00\x00\x00\x00\x1e\x00\ +\x00\x00\x0d\x00T\x00r\x00a\x00n\x00s\x00p\x00\ +a\x00r\x00e\x00n\x00c\x00y\x00\x008BI\ +M\x045\x00\x00\x00\x00\x00\x11\x00\x00\x00\x01\x00\x00\xff\ +\xff\x00\x00\x00\x00\x00\x00\x00d\x01\x008BIM\x04\ +\x1d\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008BIM\x04\ +\x0d\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e8BIM\x04\ +\x19\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e8BIM\x03\ +\xf3\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\ +\x008BIM'\x10\x00\x00\x00\x00\x00\x0a\x00\x01\x00\ +\x00\x00\x00\x00\x00\x00\x018BIM\x03\xf5\x00\x00\x00\ +\x00\x00H\x00/ff\x00\x01\x00lff\x00\x06\x00\ +\x00\x00\x00\x00\x01\x00/ff\x00\x01\x00\xa1\x99\x9a\x00\ +\x06\x00\x00\x00\x00\x00\x01\x002\x00\x00\x00\x01\x00Z\x00\ +\x00\x00\x06\x00\x00\x00\x00\x00\x01\x005\x00\x00\x00\x01\x00\ +-\x00\x00\x00\x06\x00\x00\x00\x00\x00\x018BIM\x03\ +\xf8\x00\x00\x00\x00\x00p\x00\x00\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\ +\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\ +\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\x03\xe8\x00\x008BIM\x04\x00\x00\x00\x00\ +\x00\x00\x02\x00\x008BIM\x04\x02\x00\x00\x00\x00\x00\ +\x02\x00\x008BIM\x040\x00\x00\x00\x00\x00\x01\x01\ +\x008BIM\x04-\x00\x00\x00\x00\x00\x06\x00\x01\x00\ +\x00\x00\x0a8BIM\x04\x08\x00\x00\x00\x00\x00\x10\x00\ +\x00\x00\x01\x00\x00\x02@\x00\x00\x02@\x00\x00\x00\x008\ +BIM\x04\x1e\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008\ +BIM\x04\x1a\x00\x00\x00\x00\x035\x00\x00\x00\x06\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\ +\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00null\x00\x00\ +\x00\x02\x00\x00\x00\x06boundsObjc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Rct1\x00\x00\ +\x00\x04\x00\x00\x00\x00Top long\x00\x00\ +\x00\x00\x00\x00\x00\x00Leftlong\x00\x00\ +\x00\x00\x00\x00\x00\x00Btomlong\x00\x00\ +\x00`\x00\x00\x00\x00Rghtlong\x00\x00\ +\x00`\x00\x00\x00\x06slicesVlLs\ +\x00\x00\x00\x01Objc\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x05slice\x00\x00\x00\x12\x00\x00\x00\x07s\ +liceIDlong\x00\x00\x00\x00\x00\x00\ +\x00\x07groupIDlong\x00\x00\x00\ +\x00\x00\x00\x00\x06originenum\x00\ +\x00\x00\x0cESliceOrigin\x00\ +\x00\x00\x0dautoGenerated\ +\x00\x00\x00\x00Typeenum\x00\x00\x00\x0a\ +ESliceType\x00\x00\x00\x00Im\ +g \x00\x00\x00\x06boundsObjc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Rct1\x00\x00\ +\x00\x04\x00\x00\x00\x00Top long\x00\x00\ +\x00\x00\x00\x00\x00\x00Leftlong\x00\x00\ +\x00\x00\x00\x00\x00\x00Btomlong\x00\x00\ +\x00`\x00\x00\x00\x00Rghtlong\x00\x00\ +\x00`\x00\x00\x00\x03urlTEXT\x00\x00\x00\ +\x01\x00\x00\x00\x00\x00\x00nullTEXT\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00MsgeTEX\ +T\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06altTa\ +gTEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0ec\ +ellTextIsHTMLboo\ +l\x01\x00\x00\x00\x08cellTextTE\ +XT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x09horz\ +Alignenum\x00\x00\x00\x0fESl\ +iceHorzAlign\x00\x00\x00\x07\ +default\x00\x00\x00\x09vertA\ +lignenum\x00\x00\x00\x0fESli\ +ceVertAlign\x00\x00\x00\x07d\ +efault\x00\x00\x00\x0bbgColo\ +rTypeenum\x00\x00\x00\x11ESl\ +iceBGColorType\x00\x00\ +\x00\x00None\x00\x00\x00\x09topOut\ +setlong\x00\x00\x00\x00\x00\x00\x00\x0al\ +eftOutsetlong\x00\x00\x00\ +\x00\x00\x00\x00\x0cbottomOutse\ +tlong\x00\x00\x00\x00\x00\x00\x00\x0brig\ +htOutsetlong\x00\x00\x00\x00\ +\x008BIM\x04(\x00\x00\x00\x00\x00\x0c\x00\x00\x00\ +\x02?\xf0\x00\x00\x00\x00\x00\x008BIM\x04\x14\x00\ +\x00\x00\x00\x00\x04\x00\x00\x00\x0d8BIM\x04\x0c\x00\ +\x00\x00\x00\x043\x00\x00\x00\x01\x00\x00\x000\x00\x00\x00\ +0\x00\x00\x00\x90\x00\x00\x1b\x00\x00\x00\x04\x17\x00\x18\x00\ +\x01\xff\xd8\xff\xed\x00\x0cAdobe_CM\x00\ +\x01\xff\xee\x00\x0eAdobe\x00d\x80\x00\x00\x00\ +\x01\xff\xdb\x00\x84\x00\x0c\x08\x08\x08\x09\x08\x0c\x09\x09\x0c\ +\x11\x0b\x0a\x0b\x11\x15\x0f\x0c\x0c\x0f\x15\x18\x13\x13\x15\x13\ +\x13\x18\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x01\x0d\x0b\x0b\x0d\x0e\x0d\x10\x0e\x0e\ +\x10\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\ +\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11\x08\x000\x000\ +\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01\xff\xdd\x00\x04\x00\x03\ +\xff\xc4\x01?\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\ +\x00\x00\x00\x00\x00\x03\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\ +\x0b\x01\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x10\x00\ +\x01\x04\x01\x03\x02\x04\x02\x05\x07\x06\x08\x05\x03\x0c3\x01\ +\x00\x02\x11\x03\x04!\x121\x05AQa\x13\x22q\x81\ +2\x06\x14\x91\xa1\xb1B#$\x15R\xc1b34r\ +\x82\xd1C\x07%\x92S\xf0\xe1\xf1cs5\x16\xa2\xb2\ +\x83&D\x93TdE\xc2\xa3t6\x17\xd2U\xe2e\ +\xf2\xb3\x84\xc3\xd3u\xe3\xf3F'\x94\xa4\x85\xb4\x95\xc4\ +\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\ +\xc6\xd6\xe6\xf67GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\ +\xf7\x11\x00\x02\x02\x01\x02\x04\x04\x03\x04\x05\x06\x07\x07\x06\ +\x055\x01\x00\x02\x11\x03!1\x12\x04AQaq\x22\ +\x13\x052\x81\x91\x14\xa1\xb1B#\xc1R\xd1\xf03$\ +b\xe1r\x82\x92CS\x15cs4\xf1%\x06\x16\xa2\ +\xb2\x83\x07&5\xc2\xd2D\x93T\xa3\x17dEU6\ +te\xe2\xf2\xb3\x84\xc3\xd3u\xe3\xf3F\x94\xa4\x85\xb4\ +\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\ +\xa6\xb6\xc6\xd6\xe6\xf6'7GWgw\x87\x97\xa7\xb7\ +\xc7\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xf5\ +T\x92@\xce\xbe\xdcl+\xef\xa6\xa7d[S\x1c\xea\ +\xe9f\xae{\x80\xf6\xb0\x7fY\xc9)\x8e_Q\xe9\xf8\ +Q\xf6\xcc\x9a\xb1\xf7}\x1fU\xedd\xff\x00Wy\xf7\ +\x22\xd1\x91\x8f\x93X\xb7\x1e\xd6]S\xb8}n\x0ei\ +\xfe\xd3%\xab\x98\xe8?Uq\xf31\x7fju\xfa\x9d\ +\x95\xd4\xb3\x7fI`\xbbp\xd8\xd3\xfc\xdd~\x94\xb7g\ +\xb3\xf3\x1d\xfc\xc7\xf3?\xe0\xd0rzs~\xab\xf5\xcc\ +\x0c\xae\x98\xe73\x03\xa9\x5c\xdc\x5c\xacB\xe2[\xb9\xda\ +Wcw\xeew\xb7\xe9\xfe\xfd\x7f\xcd\xff\x005v\xc4\ +\x94\xf6)$\x92J\x7f\xff\xd0\xf5U\x9b\xd6\xfa\xf6\x17\ +E\xaa\xb7\xe4\x8b,}\xee-\xaa\x9a\x9b\xb9\xee#W\ +}\x22\xc6\xedo\xf5\x96\x92\xe5\xfe\xbd\xfe\x87\x1f\xa6\xf5\ +\x01\xa3\xb0\xf3kt\xf84\xcb\x9d\xff\x00\x9e\x98\x92\x94\ +>\xb7\xf5[\xc4\xe1t\x0c\xab\x01\xfa.\xb6k\x1f\xf9\ +\xed\xcd\xff\x00\xa6\xb3\xba\x9d\x1f\x5cz\x8d\xf4u,\x9c\ +*q\x9b\xd3w_UN\xb09\xbb\x84Y\xea=\x8c\ +s\x9fe\x8d\xf4\xfd\x9f\xcd\xad\xff\x00\xad\xcf\xeau\xf4\ +Km\xe9\xaf5\xbe\xb2\x1f{\xd8@x\xa5\xb2\xeb\x9d\ +S\x8f\xd1s\x7f\xcf\xf4\xfdOO\xf4\x89\xfe\xaa]\x99\ +\x97\xf5~\x8bs\xec\x17\xbe\xdd\xfb^L\xb8\xd7\xb9\xcd\ +\xaf\xd5#\xfc&\xcf\xa7\xff\x00\x82~\x91%6>\xaf\ +\xf5Gun\x8f\x8d\x9fcZ\xcb.i\xf5\x1a\xd9\xda\ +\x1c\xd7:\xb7\xed\xdd\xee\xda\xed\x9b\x96\x8a\xe6?\xc5\xf9\ +,\xe9\x19\x18\x8e>\xecL\xabj\x8f\x01\xedw\xfdV\ +\xf5\xd3\xa4\xa7\xff\xd1\xf5U\x87\xf5\xd3\x0d\xf9\x7fV\xf2\ +\xd9[\x0d\x9606\xc65\xa2O\xb1\xcds\xf6\xb4\x7f\ +\xc1\xef[\x89$\xa7\x92\xa3\xeb\xadY8\x8c\xa2\x9e\x97\ +\x97\xd4\x1ek\x0c\xb86\xb0kq\xdb\xb6\xc1\xfe\x13u\ +n\xfeS\x13\xe3u_\xad\x1e\x8b1\xfa_\xd5\xe6a\ +\xe3\xd66\xd6\xcbl\x0ckG\x95_\xab\xae\xb1$\x94\ +\xe0}U\xe9\x1dK\x01\xd9\xf9]G\xd3\xae\xec\xfb\xbd\ +oB\xa2KX}\xc5\xee\xdc\x7f}\xcf\xfa\x1e\xff\x00\ +\xf8\xc5\xbe\x92I)\xff\xd9\x008BIM\x04!\x00\ +\x00\x00\x00\x00a\x00\x00\x00\x01\x01\x00\x00\x00\x0f\x00A\ +\x00d\x00o\x00b\x00e\x00 \x00P\x00h\x00o\ +\x00t\x00o\x00s\x00h\x00o\x00p\x00\x00\x00\x19\ +\x00A\x00d\x00o\x00b\x00e\x00 \x00P\x00h\ +\x00o\x00t\x00o\x00s\x00h\x00o\x00p\x00 \ +\x00C\x00C\x00 \x002\x000\x001\x005\x00.\ +\x005\x00\x00\x00\x01\x00\x00\x00\x0cHLino\x02\ +\x10\x00\x00mntrRGB XYZ \x07\ +\xce\x00\x02\x00\x09\x00\x06\x001\x00\x00acspM\ +SFT\x00\x00\x00\x00IEC sRGB\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xd6\x00\ +\x01\x00\x00\x00\x00\xd3-HP \x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11cprt\x00\ +\x00\x01P\x00\x00\x003desc\x00\x00\x01\x84\x00\ +\x00\x00lwtpt\x00\x00\x01\xf0\x00\x00\x00\x14b\ +kpt\x00\x00\x02\x04\x00\x00\x00\x14rXYZ\x00\ +\x00\x02\x18\x00\x00\x00\x14gXYZ\x00\x00\x02,\x00\ +\x00\x00\x14bXYZ\x00\x00\x02@\x00\x00\x00\x14d\ +mnd\x00\x00\x02T\x00\x00\x00pdmdd\x00\ +\x00\x02\xc4\x00\x00\x00\x88vued\x00\x00\x03L\x00\ +\x00\x00\x86view\x00\x00\x03\xd4\x00\x00\x00$l\ +umi\x00\x00\x03\xf8\x00\x00\x00\x14meas\x00\ +\x00\x04\x0c\x00\x00\x00$tech\x00\x00\x040\x00\ +\x00\x00\x0crTRC\x00\x00\x04<\x00\x00\x08\x0cg\ +TRC\x00\x00\x04<\x00\x00\x08\x0cbTRC\x00\ +\x00\x04<\x00\x00\x08\x0ctext\x00\x00\x00\x00C\ +opyright (c) 199\ +8 Hewlett-Packar\ +d Company\x00\x00desc\x00\ +\x00\x00\x00\x00\x00\x00\x12sRGB IEC6\ +1966-2.1\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x12sRGB IEC6196\ +6-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00XYZ \x00\x00\x00\x00\x00\ +\x00\xf3Q\x00\x01\x00\x00\x00\x01\x16\xccXYZ \x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\ +YZ \x00\x00\x00\x00\x00\x00o\xa2\x00\x008\xf5\x00\ +\x00\x03\x90XYZ \x00\x00\x00\x00\x00\x00b\x99\x00\ +\x00\xb7\x85\x00\x00\x18\xdaXYZ \x00\x00\x00\x00\x00\ +\x00$\xa0\x00\x00\x0f\x84\x00\x00\xb6\xcfdesc\x00\ +\x00\x00\x00\x00\x00\x00\x16IEC http:\ +//www.iec.ch\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x16IEC http\ +://www.iec.ch\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00desc\x00\ +\x00\x00\x00\x00\x00\x00.IEC 61966\ +-2.1 Default RGB\ + colour space - \ +sRGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\ +IEC 61966-2.1 De\ +fault RGB colour\ + space - sRGB\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00desc\x00\x00\x00\x00\x00\x00\x00,R\ +eference Viewing\ + Condition in IE\ +C61966-2.1\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00,Reference \ +Viewing Conditio\ +n in IEC61966-2.\ +1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00view\x00\ +\x00\x00\x00\x00\x13\xa4\xfe\x00\x14_.\x00\x10\xcf\x14\x00\ +\x03\xed\xcc\x00\x04\x13\x0b\x00\x03\x5c\x9e\x00\x00\x00\x01X\ +YZ \x00\x00\x00\x00\x00L\x09V\x00P\x00\x00\x00\ +W\x1f\xe7meas\x00\x00\x00\x00\x00\x00\x00\x01\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x02\x8f\x00\x00\x00\x02sig \x00\x00\x00\x00C\ +RT curv\x00\x00\x00\x00\x00\x00\x04\x00\x00\ +\x00\x00\x05\x00\x0a\x00\x0f\x00\x14\x00\x19\x00\x1e\x00#\x00\ +(\x00-\x002\x007\x00;\x00@\x00E\x00J\x00\ +O\x00T\x00Y\x00^\x00c\x00h\x00m\x00r\x00\ +w\x00|\x00\x81\x00\x86\x00\x8b\x00\x90\x00\x95\x00\x9a\x00\ +\x9f\x00\xa4\x00\xa9\x00\xae\x00\xb2\x00\xb7\x00\xbc\x00\xc1\x00\ +\xc6\x00\xcb\x00\xd0\x00\xd5\x00\xdb\x00\xe0\x00\xe5\x00\xeb\x00\ +\xf0\x00\xf6\x00\xfb\x01\x01\x01\x07\x01\x0d\x01\x13\x01\x19\x01\ +\x1f\x01%\x01+\x012\x018\x01>\x01E\x01L\x01\ +R\x01Y\x01`\x01g\x01n\x01u\x01|\x01\x83\x01\ +\x8b\x01\x92\x01\x9a\x01\xa1\x01\xa9\x01\xb1\x01\xb9\x01\xc1\x01\ +\xc9\x01\xd1\x01\xd9\x01\xe1\x01\xe9\x01\xf2\x01\xfa\x02\x03\x02\ +\x0c\x02\x14\x02\x1d\x02&\x02/\x028\x02A\x02K\x02\ +T\x02]\x02g\x02q\x02z\x02\x84\x02\x8e\x02\x98\x02\ +\xa2\x02\xac\x02\xb6\x02\xc1\x02\xcb\x02\xd5\x02\xe0\x02\xeb\x02\ +\xf5\x03\x00\x03\x0b\x03\x16\x03!\x03-\x038\x03C\x03\ +O\x03Z\x03f\x03r\x03~\x03\x8a\x03\x96\x03\xa2\x03\ +\xae\x03\xba\x03\xc7\x03\xd3\x03\xe0\x03\xec\x03\xf9\x04\x06\x04\ +\x13\x04 \x04-\x04;\x04H\x04U\x04c\x04q\x04\ +~\x04\x8c\x04\x9a\x04\xa8\x04\xb6\x04\xc4\x04\xd3\x04\xe1\x04\ +\xf0\x04\xfe\x05\x0d\x05\x1c\x05+\x05:\x05I\x05X\x05\ +g\x05w\x05\x86\x05\x96\x05\xa6\x05\xb5\x05\xc5\x05\xd5\x05\ +\xe5\x05\xf6\x06\x06\x06\x16\x06'\x067\x06H\x06Y\x06\ +j\x06{\x06\x8c\x06\x9d\x06\xaf\x06\xc0\x06\xd1\x06\xe3\x06\ +\xf5\x07\x07\x07\x19\x07+\x07=\x07O\x07a\x07t\x07\ +\x86\x07\x99\x07\xac\x07\xbf\x07\xd2\x07\xe5\x07\xf8\x08\x0b\x08\ +\x1f\x082\x08F\x08Z\x08n\x08\x82\x08\x96\x08\xaa\x08\ +\xbe\x08\xd2\x08\xe7\x08\xfb\x09\x10\x09%\x09:\x09O\x09\ +d\x09y\x09\x8f\x09\xa4\x09\xba\x09\xcf\x09\xe5\x09\xfb\x0a\ +\x11\x0a'\x0a=\x0aT\x0aj\x0a\x81\x0a\x98\x0a\xae\x0a\ +\xc5\x0a\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b9\x0bQ\x0bi\x0b\ +\x80\x0b\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\x12\x0c*\x0c\ +C\x0c\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\xc0\x0c\xd9\x0c\xf3\x0d\ +\x0d\x0d&\x0d@\x0dZ\x0dt\x0d\x8e\x0d\xa9\x0d\xc3\x0d\ +\xde\x0d\xf8\x0e\x13\x0e.\x0eI\x0ed\x0e\x7f\x0e\x9b\x0e\ +\xb6\x0e\xd2\x0e\xee\x0f\x09\x0f%\x0fA\x0f^\x0fz\x0f\ +\x96\x0f\xb3\x0f\xcf\x0f\xec\x10\x09\x10&\x10C\x10a\x10\ +~\x10\x9b\x10\xb9\x10\xd7\x10\xf5\x11\x13\x111\x11O\x11\ +m\x11\x8c\x11\xaa\x11\xc9\x11\xe8\x12\x07\x12&\x12E\x12\ +d\x12\x84\x12\xa3\x12\xc3\x12\xe3\x13\x03\x13#\x13C\x13\ +c\x13\x83\x13\xa4\x13\xc5\x13\xe5\x14\x06\x14'\x14I\x14\ +j\x14\x8b\x14\xad\x14\xce\x14\xf0\x15\x12\x154\x15V\x15\ +x\x15\x9b\x15\xbd\x15\xe0\x16\x03\x16&\x16I\x16l\x16\ +\x8f\x16\xb2\x16\xd6\x16\xfa\x17\x1d\x17A\x17e\x17\x89\x17\ +\xae\x17\xd2\x17\xf7\x18\x1b\x18@\x18e\x18\x8a\x18\xaf\x18\ +\xd5\x18\xfa\x19 \x19E\x19k\x19\x91\x19\xb7\x19\xdd\x1a\ +\x04\x1a*\x1aQ\x1aw\x1a\x9e\x1a\xc5\x1a\xec\x1b\x14\x1b\ +;\x1bc\x1b\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c*\x1cR\x1c\ +{\x1c\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1dG\x1dp\x1d\x99\x1d\ +\xc3\x1d\xec\x1e\x16\x1e@\x1ej\x1e\x94\x1e\xbe\x1e\xe9\x1f\ +\x13\x1f>\x1fi\x1f\x94\x1f\xbf\x1f\xea \x15 A \ +l \x98 \xc4 \xf0!\x1c!H!u!\xa1!\ +\xce!\xfb\x22'\x22U\x22\x82\x22\xaf\x22\xdd#\x0a#\ +8#f#\x94#\xc2#\xf0$\x1f$M$|$\ +\xab$\xda%\x09%8%h%\x97%\xc7%\xf7&\ +'&W&\x87&\xb7&\xe8'\x18'I'z'\ +\xab'\xdc(\x0d(?(q(\xa2(\xd4)\x06)\ +8)k)\x9d)\xd0*\x02*5*h*\x9b*\ +\xcf+\x02+6+i+\x9d+\xd1,\x05,9,\ +n,\xa2,\xd7-\x0c-A-v-\xab-\xe1.\ +\x16.L.\x82.\xb7.\xee/$/Z/\x91/\ +\xc7/\xfe050l0\xa40\xdb1\x121J1\ +\x821\xba1\xf22*2c2\x9b2\xd43\x0d3\ +F3\x7f3\xb83\xf14+4e4\x9e4\xd85\ +\x135M5\x875\xc25\xfd676r6\xae6\ +\xe97$7`7\x9c7\xd78\x148P8\x8c8\ +\xc89\x059B9\x7f9\xbc9\xf9:6:t:\ +\xb2:\xef;-;k;\xaa;\xe8<' >`>\ +\xa0>\xe0?!?a?\xa2?\xe2@#@d@\ +\xa6@\xe7A)AjA\xacA\xeeB0BrB\ +\xb5B\xf7C:C}C\xc0D\x03DGD\x8aD\ +\xceE\x12EUE\x9aE\xdeF\x22FgF\xabF\ +\xf0G5G{G\xc0H\x05HKH\x91H\xd7I\ +\x1dIcI\xa9I\xf0J7J}J\xc4K\x0cK\ +SK\x9aK\xe2L*LrL\xbaM\x02MJM\ +\x93M\xdcN%NnN\xb7O\x00OIO\x93O\ +\xddP'PqP\xbbQ\x06QPQ\x9bQ\xe6R\ +1R|R\xc7S\x13S_S\xaaS\xf6TBT\ +\x8fT\xdbU(UuU\xc2V\x0fV\x5cV\xa9V\ +\xf7WDW\x92W\xe0X/X}X\xcbY\x1aY\ +iY\xb8Z\x07ZVZ\xa6Z\xf5[E[\x95[\ +\xe5\x5c5\x5c\x86\x5c\xd6]']x]\xc9^\x1a^\ +l^\xbd_\x0f_a_\xb3`\x05`W`\xaa`\ +\xfcaOa\xa2a\xf5bIb\x9cb\xf0cCc\ +\x97c\xebd@d\x94d\xe9e=e\x92e\xe7f\ +=f\x92f\xe8g=g\x93g\xe9h?h\x96h\ +\xeciCi\x9ai\xf1jHj\x9fj\xf7kOk\ +\xa7k\xfflWl\xafm\x08m`m\xb9n\x12n\ +kn\xc4o\x1eoxo\xd1p+p\x86p\xe0q\ +:q\x95q\xf0rKr\xa6s\x01s]s\xb8t\ +\x14tpt\xccu(u\x85u\xe1v>v\x9bv\ +\xf8wVw\xb3x\x11xnx\xccy*y\x89y\ +\xe7zFz\xa5{\x04{c{\xc2|!|\x81|\ +\xe1}A}\xa1~\x01~b~\xc2\x7f#\x7f\x84\x7f\ +\xe5\x80G\x80\xa8\x81\x0a\x81k\x81\xcd\x820\x82\x92\x82\ +\xf4\x83W\x83\xba\x84\x1d\x84\x80\x84\xe3\x85G\x85\xab\x86\ +\x0e\x86r\x86\xd7\x87;\x87\x9f\x88\x04\x88i\x88\xce\x89\ +3\x89\x99\x89\xfe\x8ad\x8a\xca\x8b0\x8b\x96\x8b\xfc\x8c\ +c\x8c\xca\x8d1\x8d\x98\x8d\xff\x8ef\x8e\xce\x8f6\x8f\ +\x9e\x90\x06\x90n\x90\xd6\x91?\x91\xa8\x92\x11\x92z\x92\ +\xe3\x93M\x93\xb6\x94 \x94\x8a\x94\xf4\x95_\x95\xc9\x96\ +4\x96\x9f\x97\x0a\x97u\x97\xe0\x98L\x98\xb8\x99$\x99\ +\x90\x99\xfc\x9ah\x9a\xd5\x9bB\x9b\xaf\x9c\x1c\x9c\x89\x9c\ +\xf7\x9dd\x9d\xd2\x9e@\x9e\xae\x9f\x1d\x9f\x8b\x9f\xfa\xa0\ +i\xa0\xd8\xa1G\xa1\xb6\xa2&\xa2\x96\xa3\x06\xa3v\xa3\ +\xe6\xa4V\xa4\xc7\xa58\xa5\xa9\xa6\x1a\xa6\x8b\xa6\xfd\xa7\ +n\xa7\xe0\xa8R\xa8\xc4\xa97\xa9\xa9\xaa\x1c\xaa\x8f\xab\ +\x02\xabu\xab\xe9\xac\x5c\xac\xd0\xadD\xad\xb8\xae-\xae\ +\xa1\xaf\x16\xaf\x8b\xb0\x00\xb0u\xb0\xea\xb1`\xb1\xd6\xb2\ +K\xb2\xc2\xb38\xb3\xae\xb4%\xb4\x9c\xb5\x13\xb5\x8a\xb6\ +\x01\xb6y\xb6\xf0\xb7h\xb7\xe0\xb8Y\xb8\xd1\xb9J\xb9\ +\xc2\xba;\xba\xb5\xbb.\xbb\xa7\xbc!\xbc\x9b\xbd\x15\xbd\ +\x8f\xbe\x0a\xbe\x84\xbe\xff\xbfz\xbf\xf5\xc0p\xc0\xec\xc1\ +g\xc1\xe3\xc2_\xc2\xdb\xc3X\xc3\xd4\xc4Q\xc4\xce\xc5\ +K\xc5\xc8\xc6F\xc6\xc3\xc7A\xc7\xbf\xc8=\xc8\xbc\xc9\ +:\xc9\xb9\xca8\xca\xb7\xcb6\xcb\xb6\xcc5\xcc\xb5\xcd\ +5\xcd\xb5\xce6\xce\xb6\xcf7\xcf\xb8\xd09\xd0\xba\xd1\ +<\xd1\xbe\xd2?\xd2\xc1\xd3D\xd3\xc6\xd4I\xd4\xcb\xd5\ +N\xd5\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\xe0\xd8d\xd8\xe8\xd9\ +l\xd9\xf1\xdav\xda\xfb\xdb\x80\xdc\x05\xdc\x8a\xdd\x10\xdd\ +\x96\xde\x1c\xde\xa2\xdf)\xdf\xaf\xe06\xe0\xbd\xe1D\xe1\ +\xcc\xe2S\xe2\xdb\xe3c\xe3\xeb\xe4s\xe4\xfc\xe5\x84\xe6\ +\x0d\xe6\x96\xe7\x1f\xe7\xa9\xe82\xe8\xbc\xe9F\xe9\xd0\xea\ +[\xea\xe5\xebp\xeb\xfb\xec\x86\xed\x11\xed\x9c\xee(\xee\ +\xb4\xef@\xef\xcc\xf0X\xf0\xe5\xf1r\xf1\xff\xf2\x8c\xf3\ +\x19\xf3\xa7\xf44\xf4\xc2\xf5P\xf5\xde\xf6m\xf6\xfb\xf7\ +\x8a\xf8\x19\xf8\xa8\xf98\xf9\xc7\xfaW\xfa\xe7\xfbw\xfc\ +\x07\xfc\x98\xfd)\xfd\xba\xfeK\xfe\xdc\xffm\xff\xff\x80\ +\x00 P8$\x16\x0d\x07\x84BaP\xb8d6\x1d\ +\x0f\x88DbQ8\xa4V-\x17\x8cFcQ\xb8\xe4\ +v=\x1f\x90HdR9$\x96M'\x94JeR\ +\xb9d\xb6]/\x98LfS9\xa4\xd6m7\x9cN\ +gS\xb9\xe4\xf6}?\xa0PhT:%\x16\x8dG\ +\xa4RiT\xbae6\x9dO\xa8TjU:\xa5V\ +\xadW\xacVkU\xba\xe5v\xbd_\xb0XlV;\ +%\x96\x10\x01\x07ZDA\x1b`\xac\x1bo\x0f\x81\xae\ +A\x10\x0d\xd4\x02\xf9\xbc<\x1eW\xb6\xe3\xc6\xfc\xda\xbf\ +<[vl&\x166\x0a\xc4\x06\x84x\xb2\xb6,F\ +Y\xb6\x04E@\x5c\xa02(\xfc\xcc=\xdd\x99\xb6c\ +\x83<\xb2n\xe8U\x0f]#\x93\x0d\xa7\xc3\x04\xf5C\ +\x01n\xb4\xde\x1e\xd8\x12\xc1\x1b0\xac\xb1\xf1\xb7v7\ +\xf7J\xe6\xce\xf5:\xea\xe02\xb5\x1c:\xc8\xbf\x8cu\ +\x19\xf2O\xe0N`.x\xfd\xe8>\x18\xbd3cc\ +\xac\x9d\xe2vi ~\xe0K\x923?\x8b\x10*b\x1eA\x04\xa8U\x05\x8dH\ +\xc1\xdd\x07\x9aN\x01\xd4e\x9c\xf0\xa9\x82\xd2\x1e\xa7)\ +\xfd\x0d\x9f,\xa0\x0a\xb7\xad\xe1\x105\x11\x88\x00\x94L\ +\x16\x02\x11HP\x81\x80(\xc1\xad\x17\x93\x064d6\ +\xc3g\xf1\xf9\x03G\x09(]\x1d\x8eA\xc4|F\xa2\ +\x90\x91\x94fH\x83\xe1\xcb#\x97\xb0\x11\xff\x02\x22\xe0\ +\x14\x9c\x02\x832\x88|\x18\xca\x83\xe4F\x0d\x08\x08\xc1\ +\xa1-\x90\xe6T\xbc=G3\x0a;\x13\x02Ah\xa1\ +3\x99\x0e`\x08\x05\x22\x06\xdc\xdcR\x18s\x88\xcc\xcc\ +\x1f\x87\xbaP\xbb\x00MhZ8\x86\xf3\xe9\x12\xbb\x00\ +h\x82\xf0|\x9d\xe5U\x0c\x14=\x87\xb9\xd51Q\x88\ +\xa8\x93G\x96\xc0\xed$$\xa2\x0d\x09\xbaT\x97\xf4\xc8\ +\xbd\x1a\x9fI\xa07O\x88b\x0dDR1\x00P0\ +\x88\x1bUIB`U\x83\x05\x1bW\xa1\xa0]d\x0d\ +\x8a\xf5\xa9\xb359\xc8\x5c0r\x15\xb5\xe8`\xdb\x9f\ +\x07jx\x0b\xd8\x81\xd0\x9bc\x98\x12p\x04\x02\xa1\x94\ +\xe1Wg\x85\x8c\x0b\x07XZ\x88 ik\x90A\x95\ +\xb4>\xa2\x13\x89\x864\x1a\xf7\x092\xa2\x077)\x1f\ +=\x0e\x08\x84\xb6h\x11\x12\xf1\x94<\xda\xb5\x83\xb8\x03\ +\x82U\xa8\xael6`Cj\x85\xd1'QQ\x7f\x84\ +\x87\xde\x04y\xa8\x80\x1e\x0c\x04\x0axI\xa1\x14\x82\x01\ +:\x18y\xe2\x07\x05\x9eU\x85s\xa1\xedx\xcc/\x10\ +X7\x07X\xe9\x22\x887F\xf9\x5c]\xe4\x82\x9a\x99\ +\x8d\x0d\xf8\xe8tH!\x92Q\xfcWf\x01\x99\xdb\x99\ +\x9a\x18\xc3\xe0\xf9\x01\xe2\xb6tkT\xa0\xd2\x1e\x7f\x96\ +Z\x08zth\x86*\x98\x04\xe9\x00\xbet+\x1a\xf7\ +\x98\x22\x86\x17\x9a\x88\xaaoj\x85fl\xf7\xe1\x81D\ +\xce(\x18\xef\x90 \x86h\x87A\x86Y\xec\x82\x06\x5c\ +\xa8\x0a;I\x96\x0a\xed\x81\xa2\x18an\x03#\xcc\xec\ +j\xef\x80\x03Y\x01`\xd6\xd8\x0a\x86\xb7D\xa2\x0c\x87\ +\xe8#\xfe[\x89G\x1f\x0c[\xaa\x827\x14X\x04\x1c\ +h\x9e\x86FF0\xdcjr\x84\x9e\xebx\xd3\xe0\xd8\ +\x87\xcc\x88\x86o<@:\x07\xe9\xf2\xaaQ\xe2M#\ +I\xf2\x11\x97'\xca\xf2\xfdj\xb35\x018H\xa6h\ +\x81\xfd\xa8J\x86\x18=\xc8\xc6\xde\x9b$\xf7]\xdf\xaa\ +\xaf\xc0,\x1ckf4X\x86\x17^H\xa4\xcf\x1c\x05\ +\x7f\x81\xe7\xaa!\xf7\xa4M\x85\x1e\xa8\xc9\x96\xc0G\xe9\ +_\xed\x86\xcc\xd9\xd8gz\x1f\x0a\x92\xc8\x85B\x97\xcc\ +g`\xc0\x18\x0e\x86\x1d\xffi\xad^\x95\xa1\x8d9\xf1\ +~\x8a-\xca\x1c\x91\xd3\xd0\xe2\x88s\xc6i\x02\xffD\ +\x03\xf5\x80E\x05\xf4\x80v\x966\x11\x00 !\x83\xda\ +\x06\x0ef`+\x81\xa4\x0c\x1e\xc3\x9e\x01\xc1Rz\x95\ +\xc2\x10L\x83B\xf1\xe3\x90\xb1\xf5\x07\xc7\x84!\x1e\x03\ +e<\x0fHL8\xc7\x0c)\x16*\xa4m\x0a(-\ +\x0b\xc8\xaa\x1e\x01\x8d \x04\x81d@\x08KH\x0e\x04\ +\x802\x1e\x01\xd8h\x06\x1b\xf8>v\xa0=\xdb\x923\ +\xf89\x05\xd3\x91\x0d\xf0\x88lC\x07\x5c\x87\x80kx\ +\x03p&!\x82f\x18\x0ab\x18$\x87 \x8e\x1a\x01\ +epO\x10x\xee\x1aB\xc22\x03\x96-\x13\x8a\xe2\ +x\x86L\xe1\xbcC\xe6\x90\x05\xa2\xec?\x87\x800\x0e\ +\xb0\xc0O\x1bU(\x19\x80\xa5a\xe4\x8b\xa7\x96g\x9e\ +th(\xcb(\x02\xb7\xb0i\x0eA!\x91\x05q\xda\ +\x1c\x82%\xe6\x04\x95\xc3NQ\x90\xb0P*\xc1\x80\x18\ +d\x11>k\xc0\xaeN\x06\xb0E'\xc2\xa9\xaa\x02`\ +\xbd\x1c#Q\xf6\xc5\x96\x00\xeci\xd2<\x86!Q\xce\ +0\x9b \xb3p2d\x9cH\x80\x8f-\xc5\x84\x8a,\ +\x08\xd4~,\x01\xd5\x09\x87\xa0\xe2\x89\x90\x88\xc0\x17\xe1\ +\xb2\x86\x074\x1f\x1fC\xc6e\x0e\xf5\x06<\x10@<\ +\x12\xa0\x9ej\x06\x22\x190\x07\x18\xac\x9b@\xb6e\x0f\ +\x19hL$ N\x9cC\x0d\xe1\x03r\x98\xe8G\xc2\ +\x89\x1dl@y\x8d\xe3\x027!\x10\xd6\x89\x93\xb0p\ +\xcc\x01\xc4\xc0\x87\xdb\x04#i\xf4\x1b\x88\xa3\x8c\x0b\xc3\ +\xa9\x0ct#\xe6m\x0a\xc0\x5c`F\xd4\xdf%\xe0\x9a\ +\x86\x06\x00\x81C\xc4\xf90e\xca\x0cv\xcd\x8a\x10\xb4\ +\xa0\x90\xe8Pc\xb9`N\xb6\x22\xa2GL\xf8\x1e\x84\ +\xd2N\x02\xb0\xd6\x0e\xe9@\x94\x22\x02\xe2\x96\x04\xb1\xc5\ +K\xc5\xb5\x0a%\xce(#\x0a\xf7\x1a\x08\x02\x81\x18\x97\ +\x94vv\x0e\x02\xf6<\x86\xeb\xed\x1d\xe3QiS\xf1\ +\xbb=\x94\x18\xef*\x8ep%\xd4\xd1vD\x06=Q\ +\x0e#N\xaa2\xcadK\x168M\x181\x04\x86Q\ +\xd1\xa5W\xc4ta\x1aS\xd1]\xcd\xd3\x87\x16\xc2\xad\ +i\x1a\xb1\xec\x85\xa2\xf1\xac&\x06%q\x0d5\x5c\x96\ +P\xf0\x80(h`&\x0b\xd0.\x06\x8aZ\xfc\x07\x92\ +P\xfdQ\x8f\xa4\x044\xb1\xaf\x02H`\xe6\xb1B\xfc\ +ZX\xd0\x85]\x09X0\xb2A\xe4\x1bYQ\x0c\xf6\ +\x07\xf8\xfd~\x00\xca\xb1-J\xb20\x1b\xfc\xb3!T\ +\xfcoPP[\x19\xec\x81%\x03\xf6\xac&\xcbp\x8e\ +,\x88\x83Q\x17\x8dM\xaa\x93KBc\x82\xb47\x95\ +-\x84b&\xe1\xb6)S\xa0\xf5%\x93DK \xb0\ +U\x5c\xc8\x5c\xe8\xa0\xa0\xbe\x84Z\x92J\x99\x01s\xb2\ +\x19\xe9\xe0\x86.\xe0\xf0\xba\xc4I,\xb0\x8f\xdcG\x82\ +\x9b\xbc\x19\xae\xa1\x0b\x1d7\x8cc)\x91~\x17\xaa1\ +(G`\xb9\x1e\xa3\xf2 -o\x80DH\xe3\x94^\ +\xdc\xe2H\xd7\x97\xb0\xd8\x86\x80\x5c\x86[\xe1Gy\xab\ +\xd9*\x004\xd0WSzrE\xe7\xb4}\x0a\x0fx\ +g\x92E$\x07BC\xa5q\x04>\xb8\x8c@\xd5[\ +\x84\xbd\xf6$\xed\xa4(\xb6\xb6\xdaC\x10\x90\xc8\x8c\x82\ +\xc0\x1c\x92\x8bCg\xc9\x03\x86\x1cb\xe1\xc1\xa9BG\ +\x1d\x82\xa61\x1ar\x10\x86.\xb5\xda\x97\x97\x86\x1a$\ +\xa1\x0f\x1e\x0as\x1c\x16\x08b\xbbP\xc2\xa8\x15O\x89\ +\xf4H\xab\xb0\xa0\xaf!|\x90A!\xce\xbf\xc5@%\ +\xb8$\x82\x19@x\xa4C\x1d\xe0\x9ew#\x041\xe3\ +\xa2J\xb6\x81\x90|Z\xe0\xd0A\x90\xc6-\x90\xc1T\ +\xc0\x1c$\x93\x0ea\xe0*\xdb\x88\xfc\xe8b`\xae\xd2\ +\x12I\xc4\x13\x86\x18\x18\xcf@\xf0\x86R\xf1\xc4-)\ +`\xb8\x09\xb9x\x92\x18\xe0\xaf\x8f\x02\x18\xa8\xb3\x16i\ +^\xd9\xc4 I,\xfd\xa1$\x14\x8b9\xcfbI\x92\ +rY\x0c\x1dzlf=\xb1^\x0d\x88\x18\xff\xd0\x84\ +~Q\x03\x07\xcc\x14\x86m\xe1!V4Z\x04+\x14\ +9\x85\xf9$\xcckem\x92\x0a\x8d\x90\xc1L\xa6$\ +\x97\xac:#\xe0p\x22\xc8e b`\xb2T\xea2\ +<\xbe@\xa2\xf6\x1bRH\x85\xc9`\xbf\x0b!q#\ +\x91\x0d,j\xac\xa0\x0cG\x1c\xa0\xd4\x12.Et\x92\ +{V\x07\xc2u\xae\x16+5\x0d\x8f\xc8\x1f\xa3\x87p\ +\xd3\xd8\xc4u@\x1cH#\x82\xc4\ +i\xcd\x07\x02(\x8a\xef\x08Y@\x08!\x83\xd2\x19`\ +\xf6\x19\xf0\xc4\xb2\xe2nP\x0b\xf6\xfbH\xb6\x91H\xb6\ +k(\xf2\x8a\x07`&\x0e\xc8\x11\x8e\xc2\x19\x0a\x03\x0b\ +\x02&g\x05\xec\x1b+\xf6\xf5g*r#\xc8#e\ +\x00M@\x15\x08\xe8\xe6\x03\xe8\xaa\x8a\xa7\xc8\x81/\xb4\ +k\xc2\x90\x99A\xe0\xb3i\xd8\x1b\xf0\xec\x22\xc0\x02x\ +\xa7\x84?B\x16\xf4a\xb8\x14\xcf@\x17\xc0\xb6 \x8b\ +\xf6\x8f0\x9cEH\xb2D\x00E\x0aF\x9ck\xc8d\ ++\x8a@\x171\x5c\x0a\x0cC\x12b4\x08Qh\x14\ +\xa0I\x16\xe0\xb4\x9a\xe8L\x1c.\xde\x18\xe8\xab\x10\xa6\ +\x9c\xd5C\x88\x99A\xe4\x95\xc1\x80H\x81\x98\x0f\xe6f\ +\x1d\xa6k\x16B8\xb2@`\x0f\x0e8\x10\xe9J\xdc\ +\xa9|\x9e\xc9\x88\xa7\xe1\xb6X\x01\xda\xa3\xa9R\xa8\xc9\ +\xb1\x19\xc2H\xebn\xba\x9cN\xbe+i\xd1\x1b\xe2\xf6\ +\x9d\xc2\xfc\x1bi\x86\x84!\xac\x9e\x89\xec\xc8\xd1\xc4'\ +\xe7`\xb5\xc1ds\x82v\x1f\xe9R\x9b\x040\x1c\xaa\ +(\x9dQ\xfe0!\xb2\x9d\xeaA\x1e\x91\xea+0\xa4\ +\xae\xc1D\xa6\xe7\x1e#jv7\x11\x22\x9d\x81\xbc\x8c\ +!\xaa\x9ef \x1b\xec\xd4\xa92\x12Z\xa4\xf0\xeam\ +\xbe\x09\xa9\x1e\x97\x91\x1c\xa2\xe2\xfc\x1b\x8a\x85\x1e& \ +\x1b\xd1\xfe\xf2\xf2;%\xf2a&2e&ri&\ +\xb2m&\xf2q'2u'ry'\xb2}'\xf2\ +\x81(2\x85(r\x89(\xb2\x8d(\xf2\x91)2\x95\ +)r\x98(\x22\x02\x00\x03\x00\x01\xa0\x03\x00\x01\x00\x00\ +\x00\x01\x00\x00\x00\x02\xa0\x04\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x03\xa0\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\ +\x00\x00\x02\x9e\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a <\ +/g>\x0d\x0a\x0d\x0a\ +\x00\x00\x02\xa4\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Icons / \ +System / Carat /\ + White / Default\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a <\ +polygon id=\x22Tria\ +ngle\x22 fill=\x22#FFF\ +FFF\x22 transform=\x22\ +translate(8.0000\ +00, 8.000000) sc\ +ale(1, -1) trans\ +late(-8.000000, \ +-8.000000) \x22 poi\ +nts=\x228 6 12 10 4\ + 10\x22>\x0d\ +\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x05!\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / slice \ +/ standard copy \ +2\x0d\x0a <\ +desc>Created wit\ +h Sketch.\ +\x0d\x0a \x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\ +\x0a \x0d\x0a\ + \x0d\x0a \ + \x0d\x0a\x0d\x0a\ +\ +\x00\x00\x035\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / layer \ +/ default\x0d\x0a Cre\ +ated with Sketch\ +.\x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \ +\x0d\x0a \ + \x0d\x0a \ +\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x04\xa0\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Not active - \ +Default\x0d\ +\x0a Creat\ +ed with Sketch.<\ +/desc>\x0d\x0a \x0d\x0a <\ +g id=\x22icon-/-out\ +liner-/-entity-/\ +-Not-active---De\ +fault\x22 stroke=\x22n\ +one\x22 stroke-widt\ +h=\x221\x22 fill=\x22none\ +\x22 fill-rule=\x22eve\ +nodd\x22>\x0d\x0a \ +\x0d\x0a \ + \x0d\x0a\x0d\x0a\ +\x00\x00\x09\x94\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a <\ +g id=\x22Sky-Icon-/\ +-System-/-View\x22 \ +transform=\x22trans\ +late(-1.000000, \ +-1.000000)\x22>\x0d\x0a \ + \x0d\x0a \ + \x0d\ +\x0a \x0d\x0a <\ +path d=\x22M7.06342\ +091,12.9074237 L\ +7.88176629,12.08\ +90784 C8.2054235\ +6,12.1935613 8.5\ +5066739,12.25000\ +46 8.90909425,12\ +.2500046 C10.754\ +2281,12.2500046 \ +12.2500046,10.75\ +42281 12.2500046\ +,8.90909425 C12.\ +2500046,8.550667\ +39 12.1935613,8.\ +20542356 12.0890\ +784,7.88176629 L\ +13.4371592,6.533\ +68547 C14.319022\ +1,7.17645386 15.\ +2844155,7.968256\ +79 16.3333395,8.\ +90909425 C13.022\ +4727,11.8787923 \ +10.5438307,13.36\ +36414 8.89741345\ +,13.3636414 C8.3\ +7051833,13.36364\ +14 7.75918749,13\ +.2115688 7.06342\ +091,12.9074237 Z\ + M5.69216773,12.\ +1787833 C4.48306\ +461,11.4370945 3\ +.08062505,10.347\ +1982 1.48484904,\ +8.90909425 C4.78\ +014139,5.9393961\ +7 7.25099619,4.4\ +5454713 8.897413\ +45,4.45454713 C9\ +.76312087,4.4545\ +4713 10.8589211,\ +4.865077 12.1848\ +142,5.68613676 L\ +11.2977095,6.573\ +24152 C10.691167\ +6,5.95308542 9.8\ +450802,5.5681839\ +1 8.90909425,5.5\ +6818391 C7.06396\ +042,5.56818391 5\ +.56818391,7.0639\ +6042 5.56818391,\ +8.90909425 C5.56\ +818391,9.8450802\ + 5.95308542,10.6\ +911676 6.5732415\ +2,11.2977095 L5.\ +69216773,12.1787\ +833 Z M8.8358430\ +8,11.1350016 L11\ +.1461965,8.82464\ +811 C11.1472437,\ +8.85266758 11.14\ +77719,8.88081938\ + 11.1477719,8.90\ +909425 C11.14777\ +19,10.1391835 10\ +.1480347,11.1363\ +678 8.91479629,1\ +1.1363678 C8.888\ +36881,11.1363678\ + 8.86204856,11.1\ +359099 8.8358430\ +8,11.1350016 Z M\ +7.3616286,10.509\ +3224 C6.94241377\ +,10.1044297 6.68\ +182069,9.5371166\ +3 6.68182069,8.9\ +0909425 C6.68182\ +069,7.67900503 7\ +.68155792,6.6818\ +2069 8.91479629,\ +6.68182069 C9.54\ +237304,6.6818206\ +9 10.1094814,6.9\ +4005568 10.51514\ +44,7.35580661 L7\ +.3616286,10.5093\ +224 Z\x22 id=\x22Combi\ +ned-Shape\x22 fill=\ +\x22#E9E9E9\x22>\x0d\x0a <\ +polygon id=\x22Rect\ +angle-25\x22 fill=\x22\ +#E9E9E9\x22 transfo\ +rm=\x22translate(8.\ +582044, 8.280986\ +) rotate(45.0000\ +00) translate(-8\ +.582044, -8.2809\ +86) \x22 points=\x227.\ +7813425 0.916636\ +365 9.38274632 0\ +.913653421 9.358\ +23763 15.6483178\ + 7.7813425 15.61\ +58919\x22>\x0d\x0a \x0d\ +\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x05\x1f\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / slice \ +/ Editor only -\ + Saved\x0d\x0a\ + Create\ +d with Sketch.\x0d\x0a \x0d\x0a \x0d\x0a \x0d\x0a \ +\x0d\x0a\x0d\x0a\ +\x00\x00\x02\x9e\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a <\ +/g>\x0d\x0a\x0d\x0a\ +\x00\x00\x05#\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / slice \ +/ Editor only -\ + Updated\ +\x0d\x0a Crea\ +ted with Sketch.\ +\x0d\x0a \x0d\x0a \ +\x0d\x0a \ + \x0d\x0a\ + \x0d\x0a\ +\x0d\x0a\ +\x00\x00\x06\x09\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / lock /\ + on\x0d\x0a \ + Created w\ +ith Sketch.\x0d\x0a \x0d\x0a \ + \x0d\x0a\ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\ + \ +\x0d\x0a \ + \x0d\x0a \ +\x0d\x0a \x0d\x0a\ +\x0d\x0a\ +\x00\x00\x15\xa4\ +I\ +I*\x00B\x08\x00\x00\x80?\xe0@\x08$\x16\x0d\x07\ +\x84BaP\xb8d6\x1d\x0f\x88DbQ8\xa4V\ +-\x17\x8cFcQ\xb8\xe4v\x0b\x02\x7f\xc7\xa4R9\ +$\x96M'\x94JeR\xb8\xa4\x82Y/\x98Lf\ +S9\xa4\xd6a.\x9bNgS\xb9\xe4\xf6}#\x9c\ +O\xe8T:%\x16\x8d/\xa0\xd1\xe9T\xbae6\x9d\ +\x1f\x81\xd3\xeaU:\xa5V\x91Q\xabVkU\xba\xe4\ +B\x93]\xb0XlU*\xfd\x8e\xcdg\xb4N\xec\xb6\ +\x9be\xb6\xdd&\xb5\xdb\xeeW;\xa4J\xe3u\xbc^\ +o7{\xd5\xf6\xfdi\xbe_\xf0X:\xde\x07\x09\x87\ +\xc4Sp\xd8\x9cf6\x7f\x8b\xc7drS<\x86O\ +-\x97\x93\xe5s\x19\xbc\xe4k5\x9d\xd0hk\xd5\x8d\ +\x16\x97M-\xd2Q\x00:\xbb\xf1\xaf\x5c\x1a}\xecG\ +pQ\x94\x14c\x05\x0e\xc1B;\xa8K\xbe\x0a\xf0\x82\ +\xb8\xf5`\x16|\x15\x9c\x04\xe41\x12\xbc\xb75\xf7?\ +\x94\xd4\xd0\xf8v\xc3?T7 .AJ\x10Q\xbc\ +\x14\x036\x90\xc1\x19pUx\x0f\xcc\xa5K\xfa\x5cx\ +\x0e\x8d\x0b\x9f2\xe9\xd7M\x1f1\xc4\x80\xe1 )\xc1\ +@\x95g\xe7\x93\xcc\x01\x92\x0fI.c+\xafzc\ +\x03&\x0f\x8a\xa44\xc1\x80\xe9\xfb\x07\x91\xa8(\xaa\xbd\ +\x15\xa8(\xe4MC\x07\x12\xa7\x04*\xef\x0a\x8d\x05(\ +\xa3\xfcD\x03\x1d\x11(\xe6\x90\x0fh(\x16\xc4\x1e\xc8\ +)\x0a\x03\xc6\x04i'\x19\x9f*<8\x96F\xe9\x5c\ +@\x9f\x0c\xd1\xe8\x8c\x82\x92h(L\xd1\x1bh(\xdd\ +\x0c\x13E\xc2\x87\x1c\xa5RbS\x1d\xa6q\x10\xfe\x01\ +D\xa7A\x06\x90\x0fM:\x1eD\x032\xe8\xf6@\xcc\ +\x07\xf2s'%\x13\x22O(%\x83|\xd4\x08\x1e\xf3\ +iL\x82\x892\xd2&\xe1\x97\x00D\xec,\x923\xcb\ +\x80\x99L\xcb\x83\xda\x9f\xcd\x09;\xaa3\x85I\x01`\ +\x82\x84\xb3\x92:n9\x00 \xa0KR\x06\xac:\xa5\ +O\xa9-\x02\x91G\xa30\x86\xf2 \xa0m\x14\x93\x9e\ +\x8f\xf8\xa3\x01\x17\x89M*\x92T\xe9\x1d.\x8dPb\ +*@X\xa0\xa0M>\x98\x1f\x0e\x18\x9cL\xd7\x05\xda\ +KT\xa4U\xe2=U\xa2\xb4\xcc~\x82V\x08 \x10\ +\xa99\xa8!X\x82\x96\xef\xf9\xc2\x06Z\x07!\xebi\ +\x9f\xee\x1b`\xd8\x97\xa8(8\xae\x1f\x08(\x9f$\x17\ +H\xf5|\x8e\xdch\xe5\x80\x88>c@\x8e\x7f]\x94\ +=\x8c\xa6\x1c\x00\x15\xe4:\x02\xf7\xa9_0\x103\x15\ +\x83\x1e\x9b\x88(F\xb1\x1f\x17\x90\x04(\x13\x18)r\ +\xcfO\xe9\xf5\xca\x8d\xdc\xe8\x5c\x184\x86\xb0y\xfa`\ +\xd6*a\x18\x05\xe3\x03\xf1\x1f\x8d\x9e\xe8\xf53~\xa0\ +\x97\xfa\xcf\x16\xa0\x81\xfc\x90f\x22\xd8^\x11\x0f(\xb8\ +j\x0dL\x83\xc8)\x92\x82\x83\x0a5\xf4\x00\x0d\x92A\ +.\x94\xe3\xf7\xf2\xdcs\xbf\xe1\xbc\x04\xf5\xa29R3\ +\xa3#\x18h\xdb\xa5\x81\xc7\xce\x9cb\xa0\xa1b\x8e\xe1\ +\x8c\x95\xc12N\xa5\xf9\xeeB\xba\x1a@~\xbc\x1e\x11\ +{\x09\xe6\x87i\x08\xbe\xca\x8bR\xe9\x00\x03A\x96h\ +(\x96\xa6\x14\x12@\xc2\x99\xeb@\x06D\xbc\x16Z\xb0\ +\xa0\xe1\xe5\x886\xce\x8a\xef\xe8\xa5/L\x8f\x88)\x07\ +xc\x00XU\x8d\x91\xf8\xeae\xba\xee\xeb\xc3\x86;\ +\xea\xc4R\x15\xc0\xa2|\xc2%\x1d\xdd!\xed\xd8\x7f\x17\ +\xe8(\x06\xa6\x0c\xb2A8\x9c\xf2\x0c\x13\xfa\x82\x07\x92\ +Ff\xa8o\xb2^\x12\x9e\xbazX\xdb\xa6\xe9\xc6\x92\ +\x0a\x0f\xa9\x87\x0d\x11$\x1f}E\xf9\x9f0\xf2*\x08\ +\x18I\x07\xb75\xa2\xf6i\xe3\xa7A\x91\xa9\x00\xe4\xa6\ +\xb8dN\xac<'\xbdK\x10\xe1\x90:\xb1\x01\xe5\xb4\ +}\x8a\x848|\x93d\xdb\xa2\x00\x00b\x9a\xff\x88\x10\ +\x16(\x9e{La\xdfF\x83\xceY*zF\xdej\ +wt\x8e\xc9\x01\x12T\x9cp\x00\x01\xef\x01\xec\xbc6\ +\xb6c\x8e\x18r`\xa2`G\xbf\x97\xc4O\xd4\x18\xb5\ + \xa1(\xa9\x0d\xd4\x90\x09\x0a\x1b\x0f\x04D\x80\x02\x92\ +\x97>\xdbH\x22\x89)\x87\x0cY\xc0\xb0\x9d\x03\x8aR\ +\x83\x1b)\x08\xa9\x0cT\x90\x0f\x0c\x1a\x99t$\x10 \ +\x15!\xb0\xd5\x81L((\xea\x0d\x92\x00\x05dS\xc5\ +\xa2H\x09\xb0\xc5\x1e\x8a\xf3\xb4T\x87\xb3VEe\x19\ +\xf0\x10\xf5\x067\xc8( *C\x19$\x1b3\x04\xa6\ +F\x11\x05\x07\xa5Ho5g\x22\xec\xa0y>Pj\ +\xe8\x82\x04H\xb8\x92\x22\xf9zS#iD\x15!r\ +\xd5\x82D:(\xcb\xa4D\x12\x00\xeeT\x87\xc2]\x03\ + =|\x0f\xa2\xea\xa6`\xe9\x04O`\x00\x05=C\ +V! X}\x8eE\x14\xd7\x06\xb0>?$\x80\xdd\ +tE5\x81\x048\x16/\x8b\xaa\x83\x08\x04\x82\x19\x94\ +\xe1\xf4\x01e\x00!\x12\x92\x8dd\x94X\x9aC\x8e\x9a\ +\x99\x15$\x14+\x15\x22\x00\x8cM@\xce\xa0\x084\x1e\ +\x11\x09\x85B\xe1\x90\xd8t>!\x11\x00\x19\xa2\x88\x98\ +9\xda%\x19\x8dF\xe1)\xe8\x1ah\xc6\xff\x91G$\ +\x92XD\x89\xff&\x95G\x002\xd0\x01\xa6`\x22~\ +\xcc\xda\x10pl\xaeq\x1aq\x86g\x82D\x0c\xfd\xf5\ +9\xa1P\xe1G\xfa0\x19\xcfInA\xc3\xb4Jt\ +)\xdf-\x00\x8bS5W,\xa2\x9fY\xacVi\xd5\ +(I\x9e\xc0^\x94(k\x96X1\xa6>\x98\xb3Z\ +\xe31C1\xa2\x0e\x97\xb6Q*Ez\xaaeU'\ +\x91\xdc\xe5u\xbb\xe4\x9a\xbd\x0c\xb7'\xe0\xe6\x0b\xfc\xe1\ +\xc4\x0d\xc5\x0a\x91\xb8\xd7\xae\x1e\xd8j\xc9\x03\x1f\x99V\ +\xb53!$\xa9&\xae\xe6xe\xfb3\x1a\xd0hb\ +X\x18]\x18\xfe\x08\xa4\xb9\xd8\xd0q\x8e\x92H\xa5\x8f\ +\x976\x15\x9br\x9a\x0eY\xda\xe9e\xac\xc06\xfcx\ +\x93\xe1>s\xf7\xbd\xdcCG\xc7\x86i\xa1\xc6\xdep\ +q\xf3\xd1d\xc1\xc3\x5c\xa8}H\xd7wKu\xa3\x96\ +\xe3l\x1d%\xdc\x889*C\x8b\xbb\x97\x91\xc6\xf1B\ +\xb9>\xb872#\x925\x0c2\xaf\xc6\x1c\x1c\x19\xee\ +\x84\xca`\xc7H\xf9\x1c\xfd!+r\x0a\x83\x22\xcf|\ +\x02\x83\x9e`\x1c\x16\x1e\x12\xf0q\xa4\xd1=PC\xda\ +\xf7>\x08\xd0\xd1\x0c\x09'\xf46Y \xe0$\x10\x85\ +\x12\xe0|F;\x91q1\xe6\xd29\xc3h\x1c}E\ +\xa4ZP3D\x08I\xf8\x01F\xa2a1\x1c\x17)\ +,(\xf5\xc7\x8f\x14,\x92-\xcd\xa2\x0cP \xe0\x1c\ +d\x84\x1d\x088\xf0\x83\x94\xc8\xf9\xf6\xa25\x0aB\x92\ +-\xa0\xe4:\x0e\x0b\xc9\x089\xfa\xc2\xa3\xe5\x22q\x1f\ +;\x93\x0b\xad %P\xc0\xd0,Cg\xf1G\x0fK\ +HQ\xe0\x83\x96\x089q\x1a\x80G\x08\x0b<\x1c\x88\ +A\xf7>\x03\x93P>\x83\x89\x088\xa0\x83\x82\x13r\ +\x12~\xce\xa2\xe4pL\x15\x0a$\xc6\xe5R\x0e<\xca\ +\x9c\xac\x038\xa8\x947\x080\x0bC\xd3\xac9\xf8\xa9\ +\x0b+\xb9X\xaeRM\xddL\xda\xd2\x8a\x22\xddB \ +\xc5:\x0e\x04S\xd5\x92\x88|7(\xfc\xe4\xb5\xd5\x0d\ +\x85t\xd2UJ\xca`4\x86i\x99\xfaW \xe0\xf5\ +gd#G\x12\x0e)\xa3\xe6c\x0f^46\x8b3\ +_,\xcbp(\xa9\x15\x09@\x87d\xdb\xa8A~\x03\ +\xdc\x22\xbb\x84I\x9dm%\xa6\xc8]\x0c=\xaa\xb9\x95\ +Wp\x06^^$B\x0e:[\xd2\xd2\xa4G\x08w\ +\xd0\xec+_\xb2\xe5O\x09@7R\xffv4\x93<\ +5\x0d\x92(8K{7f\xe2\xa47\xae\xe5\xb6\x05\ +\x80\xbfX\x1a\xf9\x82\xb7r\x93V9 \xe3\xdb\xf1\x86\ +\xa9\xecz\x0cC\x5c 9\x1br8\x91\x96.\xb9\xe5\ +\x8bf2\xf7R\xc0\xdaPE \xe2\xd6B\x93\x15\x19\ +0\xebrOU\x9e]\x5c\xe2\xb0\xaa]\x9c\x22h\xa0\ +d\x83\x8d\xc88\xb0\x83\x80\xf6\xf6T\x00\x15PX\x06\ +H\xc1\xc4\xb9\x9b\xa2\xe8\x0b6\xb4\xb2\xe6\x16LT\x0a\ +\xc5\xa7\xd0\xafA\xa5\x01\xfc\xda\xda\x9f\x889\x858\xce\ +\xa5M\x18uh\xae+\xf9\x95\xe8O^\xbd\xb9 \xe3\ +~\xf6\x08\x1e\xfb\xf0p\xa9\x06)C^\x83)\xa85\ +\x0c\x83\x02(\x5c\xe0\x83\x1d\xe89\xc6\x83\x9a\x13\xa9\x9e\ +\xa9\x19\x1a\xb7\x1d\xbc\xa3z\xe5K\xbbs<\xf7?\xd0\ +:\xdc\xdfC\xd2t\xbd2#\xd1\xf4\xfdWW\xd3u\ +=g_\xd8k<\xefc\xdav\xbd_]\xdbw=\ +\xd4'\xd9\xf7}\xf7\x7fn\xf7\x1e\x07\x87\xe2/\x9e\x17\ +\x8b\xe4y4\x7f{\xe5y\xbes3\xe3\xf9\xfe\x97\xa6\ +\x86\xfa>\xa7\xaf\xeb\xfa\xde\xc7\xb7\xe7{^\xe7\xbf\xe2\ +\xfb\xdf\x07\xc7\xdf|_'\xcf\xda\xfc\xdfG\xd7\xd6}\ +_g\xdf\xd2\xfd\xdf\x87\xe7\xcf~_\xa7\xef\x9c~\xdf\ +\xc7\xf7\xe0\xf9\x9f\xe3\xffwo\xea\x00@4\xb4@@\ +\x00\x13\x00\xfe\x00\x04\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\ +\x01\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x01\x01\x04\x00\x01\ +\x00\x00\x00`\x00\x00\x00\x02\x01\x03\x00\x04\x00\x00\x00,\ +\x09\x00\x00\x03\x01\x03\x00\x01\x00\x00\x00\x05\x00\x00\x00\x06\ +\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x00\x11\x01\x04\x00\x01\ +\x00\x00\x00\x08\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\x00\x04\ +\x00\x00\x00\x16\x01\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x17\ +\x01\x04\x00\x01\x00\x00\x009\x08\x00\x00\x1a\x01\x05\x00\x01\ +\x00\x00\x004\x09\x00\x00\x1b\x01\x05\x00\x01\x00\x00\x00<\ +\x09\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00(\ +\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\x00\x10\ +\x00\x00\x00D\x09\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00R\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x00S\ +\x01\x03\x00\x04\x00\x00\x00T\x09\x00\x00s\x87\x07\x00H\ +\x0c\x00\x00\x5c\x09\x00\x00\x00\x00\x00\x00\x08\x00\x08\x00\x08\ +\x00\x08\x00\x802\x02\x00\xe8\x03\x00\x00\x802\x02\x00\xe8\ +\x03\x00\x00paint.net 4.0\ +.9\x00\x01\x00\x01\x00\x01\x00\x01\x00\x00\x00\x0cHL\ +ino\x02\x10\x00\x00mntrRGB X\ +YZ \x07\xce\x00\x02\x00\x09\x00\x06\x001\x00\x00a\ +cspMSFT\x00\x00\x00\x00IEC s\ +RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xf6\xd6\x00\x01\x00\x00\x00\x00\xd3-HP \x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11c\ +prt\x00\x00\x01P\x00\x00\x003desc\x00\ +\x00\x01\x84\x00\x00\x00lwtpt\x00\x00\x01\xf0\x00\ +\x00\x00\x14bkpt\x00\x00\x02\x04\x00\x00\x00\x14r\ +XYZ\x00\x00\x02\x18\x00\x00\x00\x14gXYZ\x00\ +\x00\x02,\x00\x00\x00\x14bXYZ\x00\x00\x02@\x00\ +\x00\x00\x14dmnd\x00\x00\x02T\x00\x00\x00pd\ +mdd\x00\x00\x02\xc4\x00\x00\x00\x88vued\x00\ +\x00\x03L\x00\x00\x00\x86view\x00\x00\x03\xd4\x00\ +\x00\x00$lumi\x00\x00\x03\xf8\x00\x00\x00\x14m\ +eas\x00\x00\x04\x0c\x00\x00\x00$tech\x00\ +\x00\x040\x00\x00\x00\x0crTRC\x00\x00\x04<\x00\ +\x00\x08\x0cgTRC\x00\x00\x04<\x00\x00\x08\x0cb\ +TRC\x00\x00\x04<\x00\x00\x08\x0ctext\x00\ +\x00\x00\x00Copyright (c)\ + 1998 Hewlett-Pa\ +ckard Company\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00\x12sRGB \ +IEC61966-2.1\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x12sRGB IEC\ +61966-2.1\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\ +\x00\x00\x00\x00\x00\xf3Q\x00\x01\x00\x00\x00\x01\x16\xccX\ +YZ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00o\xa2\x00\ +\x008\xf5\x00\x00\x03\x90XYZ \x00\x00\x00\x00\x00\ +\x00b\x99\x00\x00\xb7\x85\x00\x00\x18\xdaXYZ \x00\ +\x00\x00\x00\x00\x00$\xa0\x00\x00\x0f\x84\x00\x00\xb6\xcfd\ +esc\x00\x00\x00\x00\x00\x00\x00\x16IEC h\ +ttp://www.iec.ch\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16IEC \ +http://www.iec.c\ +h\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00.IEC 6\ +1966-2.1 Default\ + RGB colour spac\ +e - sRGB\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00.IEC 61966-2.\ +1 Default RGB co\ +lour space - sRG\ +B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00,Reference Vie\ +wing Condition i\ +n IEC61966-2.1\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00,Refere\ +nce Viewing Cond\ +ition in IEC6196\ +6-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00v\ +iew\x00\x00\x00\x00\x00\x13\xa4\xfe\x00\x14_.\x00\ +\x10\xcf\x14\x00\x03\xed\xcc\x00\x04\x13\x0b\x00\x03\x5c\x9e\x00\ +\x00\x00\x01XYZ \x00\x00\x00\x00\x00L\x09V\x00\ +P\x00\x00\x00W\x1f\xe7meas\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x8f\x00\x00\x00\x02sig \x00\ +\x00\x00\x00CRT curv\x00\x00\x00\x00\x00\ +\x00\x04\x00\x00\x00\x00\x05\x00\x0a\x00\x0f\x00\x14\x00\x19\x00\ +\x1e\x00#\x00(\x00-\x002\x007\x00;\x00@\x00\ +E\x00J\x00O\x00T\x00Y\x00^\x00c\x00h\x00\ +m\x00r\x00w\x00|\x00\x81\x00\x86\x00\x8b\x00\x90\x00\ +\x95\x00\x9a\x00\x9f\x00\xa4\x00\xa9\x00\xae\x00\xb2\x00\xb7\x00\ +\xbc\x00\xc1\x00\xc6\x00\xcb\x00\xd0\x00\xd5\x00\xdb\x00\xe0\x00\ +\xe5\x00\xeb\x00\xf0\x00\xf6\x00\xfb\x01\x01\x01\x07\x01\x0d\x01\ +\x13\x01\x19\x01\x1f\x01%\x01+\x012\x018\x01>\x01\ +E\x01L\x01R\x01Y\x01`\x01g\x01n\x01u\x01\ +|\x01\x83\x01\x8b\x01\x92\x01\x9a\x01\xa1\x01\xa9\x01\xb1\x01\ +\xb9\x01\xc1\x01\xc9\x01\xd1\x01\xd9\x01\xe1\x01\xe9\x01\xf2\x01\ +\xfa\x02\x03\x02\x0c\x02\x14\x02\x1d\x02&\x02/\x028\x02\ +A\x02K\x02T\x02]\x02g\x02q\x02z\x02\x84\x02\ +\x8e\x02\x98\x02\xa2\x02\xac\x02\xb6\x02\xc1\x02\xcb\x02\xd5\x02\ +\xe0\x02\xeb\x02\xf5\x03\x00\x03\x0b\x03\x16\x03!\x03-\x03\ +8\x03C\x03O\x03Z\x03f\x03r\x03~\x03\x8a\x03\ +\x96\x03\xa2\x03\xae\x03\xba\x03\xc7\x03\xd3\x03\xe0\x03\xec\x03\ +\xf9\x04\x06\x04\x13\x04 \x04-\x04;\x04H\x04U\x04\ +c\x04q\x04~\x04\x8c\x04\x9a\x04\xa8\x04\xb6\x04\xc4\x04\ +\xd3\x04\xe1\x04\xf0\x04\xfe\x05\x0d\x05\x1c\x05+\x05:\x05\ +I\x05X\x05g\x05w\x05\x86\x05\x96\x05\xa6\x05\xb5\x05\ +\xc5\x05\xd5\x05\xe5\x05\xf6\x06\x06\x06\x16\x06'\x067\x06\ +H\x06Y\x06j\x06{\x06\x8c\x06\x9d\x06\xaf\x06\xc0\x06\ +\xd1\x06\xe3\x06\xf5\x07\x07\x07\x19\x07+\x07=\x07O\x07\ +a\x07t\x07\x86\x07\x99\x07\xac\x07\xbf\x07\xd2\x07\xe5\x07\ +\xf8\x08\x0b\x08\x1f\x082\x08F\x08Z\x08n\x08\x82\x08\ +\x96\x08\xaa\x08\xbe\x08\xd2\x08\xe7\x08\xfb\x09\x10\x09%\x09\ +:\x09O\x09d\x09y\x09\x8f\x09\xa4\x09\xba\x09\xcf\x09\ +\xe5\x09\xfb\x0a\x11\x0a'\x0a=\x0aT\x0aj\x0a\x81\x0a\ +\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b9\x0b\ +Q\x0bi\x0b\x80\x0b\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\ +\x12\x0c*\x0cC\x0c\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\xc0\x0c\ +\xd9\x0c\xf3\x0d\x0d\x0d&\x0d@\x0dZ\x0dt\x0d\x8e\x0d\ +\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\x13\x0e.\x0eI\x0ed\x0e\ +\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\xee\x0f\x09\x0f%\x0fA\x0f\ +^\x0fz\x0f\x96\x0f\xb3\x0f\xcf\x0f\xec\x10\x09\x10&\x10\ +C\x10a\x10~\x10\x9b\x10\xb9\x10\xd7\x10\xf5\x11\x13\x11\ +1\x11O\x11m\x11\x8c\x11\xaa\x11\xc9\x11\xe8\x12\x07\x12\ +&\x12E\x12d\x12\x84\x12\xa3\x12\xc3\x12\xe3\x13\x03\x13\ +#\x13C\x13c\x13\x83\x13\xa4\x13\xc5\x13\xe5\x14\x06\x14\ +'\x14I\x14j\x14\x8b\x14\xad\x14\xce\x14\xf0\x15\x12\x15\ +4\x15V\x15x\x15\x9b\x15\xbd\x15\xe0\x16\x03\x16&\x16\ +I\x16l\x16\x8f\x16\xb2\x16\xd6\x16\xfa\x17\x1d\x17A\x17\ +e\x17\x89\x17\xae\x17\xd2\x17\xf7\x18\x1b\x18@\x18e\x18\ +\x8a\x18\xaf\x18\xd5\x18\xfa\x19 \x19E\x19k\x19\x91\x19\ +\xb7\x19\xdd\x1a\x04\x1a*\x1aQ\x1aw\x1a\x9e\x1a\xc5\x1a\ +\xec\x1b\x14\x1b;\x1bc\x1b\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c\ +*\x1cR\x1c{\x1c\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1dG\x1d\ +p\x1d\x99\x1d\xc3\x1d\xec\x1e\x16\x1e@\x1ej\x1e\x94\x1e\ +\xbe\x1e\xe9\x1f\x13\x1f>\x1fi\x1f\x94\x1f\xbf\x1f\xea \ +\x15 A l \x98 \xc4 \xf0!\x1c!H!\ +u!\xa1!\xce!\xfb\x22'\x22U\x22\x82\x22\xaf\x22\ +\xdd#\x0a#8#f#\x94#\xc2#\xf0$\x1f$\ +M$|$\xab$\xda%\x09%8%h%\x97%\ +\xc7%\xf7&'&W&\x87&\xb7&\xe8'\x18'\ +I'z'\xab'\xdc(\x0d(?(q(\xa2(\ +\xd4)\x06)8)k)\x9d)\xd0*\x02*5*\ +h*\x9b*\xcf+\x02+6+i+\x9d+\xd1,\ +\x05,9,n,\xa2,\xd7-\x0c-A-v-\ +\xab-\xe1.\x16.L.\x82.\xb7.\xee/$/\ +Z/\x91/\xc7/\xfe050l0\xa40\xdb1\ +\x121J1\x821\xba1\xf22*2c2\x9b2\ +\xd43\x0d3F3\x7f3\xb83\xf14+4e4\ +\x9e4\xd85\x135M5\x875\xc25\xfd676\ +r6\xae6\xe97$7`7\x9c7\xd78\x148\ +P8\x8c8\xc89\x059B9\x7f9\xbc9\xf9:\ +6:t:\xb2:\xef;-;k;\xaa;\xe8<\ +'\ + >`>\xa0>\xe0?!?a?\xa2?\xe2@\ +#@d@\xa6@\xe7A)AjA\xacA\xeeB\ +0BrB\xb5B\xf7C:C}C\xc0D\x03D\ +GD\x8aD\xceE\x12EUE\x9aE\xdeF\x22F\ +gF\xabF\xf0G5G{G\xc0H\x05HKH\ +\x91H\xd7I\x1dIcI\xa9I\xf0J7J}J\ +\xc4K\x0cKSK\x9aK\xe2L*LrL\xbaM\ +\x02MJM\x93M\xdcN%NnN\xb7O\x00O\ +IO\x93O\xddP'PqP\xbbQ\x06QPQ\ +\x9bQ\xe6R1R|R\xc7S\x13S_S\xaaS\ +\xf6TBT\x8fT\xdbU(UuU\xc2V\x0fV\ +\x5cV\xa9V\xf7WDW\x92W\xe0X/X}X\ +\xcbY\x1aYiY\xb8Z\x07ZVZ\xa6Z\xf5[\ +E[\x95[\xe5\x5c5\x5c\x86\x5c\xd6]']x]\ +\xc9^\x1a^l^\xbd_\x0f_a_\xb3`\x05`\ +W`\xaa`\xfcaOa\xa2a\xf5bIb\x9cb\ +\xf0cCc\x97c\xebd@d\x94d\xe9e=e\ +\x92e\xe7f=f\x92f\xe8g=g\x93g\xe9h\ +?h\x96h\xeciCi\x9ai\xf1jHj\x9fj\ +\xf7kOk\xa7k\xfflWl\xafm\x08m`m\ +\xb9n\x12nkn\xc4o\x1eoxo\xd1p+p\ +\x86p\xe0q:q\x95q\xf0rKr\xa6s\x01s\ +]s\xb8t\x14tpt\xccu(u\x85u\xe1v\ +>v\x9bv\xf8wVw\xb3x\x11xnx\xccy\ +*y\x89y\xe7zFz\xa5{\x04{c{\xc2|\ +!|\x81|\xe1}A}\xa1~\x01~b~\xc2\x7f\ +#\x7f\x84\x7f\xe5\x80G\x80\xa8\x81\x0a\x81k\x81\xcd\x82\ +0\x82\x92\x82\xf4\x83W\x83\xba\x84\x1d\x84\x80\x84\xe3\x85\ +G\x85\xab\x86\x0e\x86r\x86\xd7\x87;\x87\x9f\x88\x04\x88\ +i\x88\xce\x893\x89\x99\x89\xfe\x8ad\x8a\xca\x8b0\x8b\ +\x96\x8b\xfc\x8cc\x8c\xca\x8d1\x8d\x98\x8d\xff\x8ef\x8e\ +\xce\x8f6\x8f\x9e\x90\x06\x90n\x90\xd6\x91?\x91\xa8\x92\ +\x11\x92z\x92\xe3\x93M\x93\xb6\x94 \x94\x8a\x94\xf4\x95\ +_\x95\xc9\x964\x96\x9f\x97\x0a\x97u\x97\xe0\x98L\x98\ +\xb8\x99$\x99\x90\x99\xfc\x9ah\x9a\xd5\x9bB\x9b\xaf\x9c\ +\x1c\x9c\x89\x9c\xf7\x9dd\x9d\xd2\x9e@\x9e\xae\x9f\x1d\x9f\ +\x8b\x9f\xfa\xa0i\xa0\xd8\xa1G\xa1\xb6\xa2&\xa2\x96\xa3\ +\x06\xa3v\xa3\xe6\xa4V\xa4\xc7\xa58\xa5\xa9\xa6\x1a\xa6\ +\x8b\xa6\xfd\xa7n\xa7\xe0\xa8R\xa8\xc4\xa97\xa9\xa9\xaa\ +\x1c\xaa\x8f\xab\x02\xabu\xab\xe9\xac\x5c\xac\xd0\xadD\xad\ +\xb8\xae-\xae\xa1\xaf\x16\xaf\x8b\xb0\x00\xb0u\xb0\xea\xb1\ +`\xb1\xd6\xb2K\xb2\xc2\xb38\xb3\xae\xb4%\xb4\x9c\xb5\ +\x13\xb5\x8a\xb6\x01\xb6y\xb6\xf0\xb7h\xb7\xe0\xb8Y\xb8\ +\xd1\xb9J\xb9\xc2\xba;\xba\xb5\xbb.\xbb\xa7\xbc!\xbc\ +\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\x84\xbe\xff\xbfz\xbf\xf5\xc0\ +p\xc0\xec\xc1g\xc1\xe3\xc2_\xc2\xdb\xc3X\xc3\xd4\xc4\ +Q\xc4\xce\xc5K\xc5\xc8\xc6F\xc6\xc3\xc7A\xc7\xbf\xc8\ +=\xc8\xbc\xc9:\xc9\xb9\xca8\xca\xb7\xcb6\xcb\xb6\xcc\ +5\xcc\xb5\xcd5\xcd\xb5\xce6\xce\xb6\xcf7\xcf\xb8\xd0\ +9\xd0\xba\xd1<\xd1\xbe\xd2?\xd2\xc1\xd3D\xd3\xc6\xd4\ +I\xd4\xcb\xd5N\xd5\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\xe0\xd8\ +d\xd8\xe8\xd9l\xd9\xf1\xdav\xda\xfb\xdb\x80\xdc\x05\xdc\ +\x8a\xdd\x10\xdd\x96\xde\x1c\xde\xa2\xdf)\xdf\xaf\xe06\xe0\ +\xbd\xe1D\xe1\xcc\xe2S\xe2\xdb\xe3c\xe3\xeb\xe4s\xe4\ +\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\x1f\xe7\xa9\xe82\xe8\xbc\xe9\ +F\xe9\xd0\xea[\xea\xe5\xebp\xeb\xfb\xec\x86\xed\x11\xed\ +\x9c\xee(\xee\xb4\xef@\xef\xcc\xf0X\xf0\xe5\xf1r\xf1\ +\xff\xf2\x8c\xf3\x19\xf3\xa7\xf44\xf4\xc2\xf5P\xf5\xde\xf6\ +m\xf6\xfb\xf7\x8a\xf8\x19\xf8\xa8\xf98\xf9\xc7\xfaW\xfa\ +\xe7\xfbw\xfc\x07\xfc\x98\xfd)\xfd\xba\xfeK\xfe\xdc\xff\ +m\xff\xff\ +\x00\x00\x89\xf8\ +I\ +I*\x00\x08\x00\x00\x00\x18\x00\xfe\x00\x04\x00\x01\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x01\x01\x03\x00\x01\x00\x00\x00`\x00\x00\x00\x02\x01\x03\ +\x00\x04\x00\x00\x00.\x01\x00\x00\x03\x01\x03\x00\x01\x00\x00\ +\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00\x11\x01\x04\x00\x01\x00\x00\x00\xfcS\x00\x00\x12\x01\x03\ +\x00\x01\x00\x00\x00\x01\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\ +\x00\x04\x00\x00\x00\x16\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x17\x01\x04\x00\x01\x00\x00\x00\xf9\x12\x00\x00\x1a\x01\x05\ +\x00\x01\x00\x00\x006\x01\x00\x00\x1b\x01\x05\x00\x01\x00\x00\ +\x00>\x01\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\ +\x00(\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\ +\x00\x22\x00\x00\x00F\x01\x00\x002\x01\x02\x00\x14\x00\x00\ +\x00h\x01\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00R\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\xbc\x02\x01\ +\x00\xf68\x00\x00|\x01\x00\x00I\x86\x01\x00B\x0d\x00\ +\x00r:\x00\x00i\x87\x04\x00\x01\x00\x00\x00\xf8f\x00\ +\x00s\x87\x07\x00H\x0c\x00\x00\xb4G\x00\x00\x5c\x93\x07\ +\x00\xd0\x22\x00\x00$g\x00\x00\x00\x00\x00\x00\x08\x00\x08\ +\x00\x08\x00\x08\x00\x00\xf9\x15\x00\x10'\x00\x00\x00\xf9\x15\ +\x00\x10'\x00\x00Adobe Photo\ +shop CC 2017 (Wi\ +ndows)\x002017:04:0\ +4 11:01:55\x00\ +\x0a\x0a \ + \x0a \x0a \ + paint.net \ +4.0.9\x0a \ + 2017-03-01T11:2\ +0:20-08:00\x0a \ + 2017-04-04T\ +11:01:55-07:00\x0a\ + 2017-\ +04-04T11:01:55-0\ +7:00\x0a \ + imag\ +e/tiff\x0a 3\x0a \ + sRGB IEC\ +61966-2.1\ +\x0a \x0a \ + \x0a \ + adobe:docid\ +:photoshop:6e5b1\ +c59-1960-11e7-ba\ +e7-e6e7a5cd2814<\ +/rdf:li>\x0a \ + \x0a\ + \x0a \ + xmp.iid:db7b2\ +8e8-30e9-e04f-9f\ +78-916e5c5110e6<\ +/xmpMM:InstanceI\ +D>\x0a ad\ +obe:docid:photos\ +hop:bf3203a1-196\ +0-11e7-bae7-e6e7\ +a5cd2814\x0a \ + x\ +mp.did:8f625742-\ +0048-e64f-ab3c-d\ +4b1a5a27a24\x0a \ +\x0a\ + \x0a \ + \x0a \ + created\x0a \ + \ +xmp.iid:8f6257\ +42-0048-e64f-ab3\ +c-d4b1a5a27a24\x0a \ + \ +2017-03-01T11:20\ +:20-08:00\x0a \ + Ad\ +obe Photoshop CC\ + 2017 (Windows)<\ +/stEvt:softwareA\ +gent>\x0a \ + \x0a \ + \x0a\ + \ + \ +saved\x0a \ + xmp.iid\ +:db7b28e8-30e9-e\ +04f-9f78-916e5c5\ +110e6\x0a \ + 2017-04-0\ +4T11:01:55-07:00\ +\x0a \ + \ +Adobe Photo\ +shop CC 2017 (Wi\ +ndows)\x0a \ + <\ +stEvt:changed>/<\ +/stEvt:changed>\x0a\ + <\ +/rdf:li>\x0a \ + \x0a\ + \x0a \ +\x0a \ +\x0a\x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \x0a8BIM\x04%\x00\x00\x00\x00\x00\x10\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008BI\ +M\x04:\x00\x00\x00\x00\x00\xe5\x00\x00\x00\x10\x00\x00\x00\ +\x01\x00\x00\x00\x00\x00\x0bprintOutp\ +ut\x00\x00\x00\x05\x00\x00\x00\x00PstSbo\ +ol\x01\x00\x00\x00\x00Inteenum\x00\ +\x00\x00\x00Inte\x00\x00\x00\x00Clrm\x00\ +\x00\x00\x0fprintSixteenB\ +itbool\x00\x00\x00\x00\x0bprint\ +erNameTEXT\x00\x00\x00\x01\x00\x00\ +\x00\x00\x00\x0fprintProofSe\ +tupObjc\x00\x00\x00\x0c\x00P\x00r\x00\ +o\x00o\x00f\x00 \x00S\x00e\x00t\x00u\x00\ +p\x00\x00\x00\x00\x00\x0aproofSetu\ +p\x00\x00\x00\x01\x00\x00\x00\x00Bltnenu\ +m\x00\x00\x00\x0cbuiltinProo\ +f\x00\x00\x00\x09proofCMYK\x008\ +BIM\x04;\x00\x00\x00\x00\x02-\x00\x00\x00\x10\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x12printOu\ +tputOptions\x00\x00\x00\x17\x00\ +\x00\x00\x00Cptnbool\x00\x00\x00\x00\x00\ +Clbrbool\x00\x00\x00\x00\x00Rgs\ +Mbool\x00\x00\x00\x00\x00CrnCbo\ +ol\x00\x00\x00\x00\x00CntCbool\x00\ +\x00\x00\x00\x00Lblsbool\x00\x00\x00\x00\ +\x00Ngtvbool\x00\x00\x00\x00\x00Em\ +lDbool\x00\x00\x00\x00\x00Intrb\ +ool\x00\x00\x00\x00\x00BckgObjc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00RGBC\x00\x00\ +\x00\x03\x00\x00\x00\x00Rd doub@o\ +\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00Grn do\ +ub@o\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00Bl\ + doub@o\xe0\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00BrdTUntF#Rlt\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Bld Un\ +tF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00RsltUntF#Pxl@b\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0avector\ +Databool\x01\x00\x00\x00\x00PgP\ +senum\x00\x00\x00\x00PgPs\x00\x00\x00\ +\x00PgPC\x00\x00\x00\x00LeftUnt\ +F#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00Top UntF#Rlt\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00Scl Unt\ +F#Prc@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x10cropWhenPrintin\ +gbool\x00\x00\x00\x00\x0ecropRe\ +ctBottomlong\x00\x00\x00\x00\ +\x00\x00\x00\x0ccropRectLeft\ +long\x00\x00\x00\x00\x00\x00\x00\x0dcrop\ +RectRightlong\x00\x00\x00\ +\x00\x00\x00\x00\x0bcropRectTop\ +long\x00\x00\x00\x00\x008BIM\x03\xed\x00\ +\x00\x00\x00\x00\x10\x00\x90\x00\x00\x00\x01\x00\x01\x00\x90\x00\ +\x00\x00\x01\x00\x018BIM\x04&\x00\x00\x00\x00\x00\ +\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\x80\x00\x008\ +BIM\x03\xee\x00\x00\x00\x00\x00\x0d\x0cTran\ +sparency\x008BIM\x04\x15\x00\ +\x00\x00\x00\x00\x1e\x00\x00\x00\x0d\x00T\x00r\x00a\x00\ +n\x00s\x00p\x00a\x00r\x00e\x00n\x00c\x00\ +y\x00\x008BIM\x045\x00\x00\x00\x00\x00\x11\x00\ +\x00\x00\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00d\x01\ +\x008BIM\x04\x1d\x00\x00\x00\x00\x00\x04\x00\x00\x00\ +\x008BIM\x04\x0d\x00\x00\x00\x00\x00\x04\x00\x00\x00\ +\x1e8BIM\x04\x19\x00\x00\x00\x00\x00\x04\x00\x00\x00\ +\x1e8BIM\x03\xf3\x00\x00\x00\x00\x00\x09\x00\x00\x00\ +\x00\x00\x00\x00\x00\x01\x008BIM'\x10\x00\x00\x00\ +\x00\x00\x0a\x00\x01\x00\x00\x00\x00\x00\x00\x00\x018BI\ +M\x03\xf5\x00\x00\x00\x00\x00H\x00/ff\x00\x01\x00\ +lff\x00\x06\x00\x00\x00\x00\x00\x01\x00/ff\x00\ +\x01\x00\xa1\x99\x9a\x00\x06\x00\x00\x00\x00\x00\x01\x002\x00\ +\x00\x00\x01\x00Z\x00\x00\x00\x06\x00\x00\x00\x00\x00\x01\x00\ +5\x00\x00\x00\x01\x00-\x00\x00\x00\x06\x00\x00\x00\x00\x00\ +\x018BIM\x03\xf8\x00\x00\x00\x00\x00p\x00\x00\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\ +\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x008BI\ +M\x04\x00\x00\x00\x00\x00\x00\x02\x00\x018BIM\x04\ +\x02\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008BIM\x04\ +0\x00\x00\x00\x00\x00\x02\x01\x018BIM\x04-\x00\ +\x00\x00\x00\x00\x06\x00\x01\x00\x00\x00\x048BIM\x04\ +\x08\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x02@\x00\ +\x00\x02@\x00\x00\x00\x008BIM\x04\x1e\x00\x00\x00\ +\x00\x00\x04\x00\x00\x00\x008BIM\x04\x1a\x00\x00\x00\ +\x00\x035\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00`\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x01\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\ +\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00null\x00\x00\x00\x02\x00\x00\x00\x06bo\ +undsObjc\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00Rct1\x00\x00\x00\x04\x00\x00\x00\x00To\ +p long\x00\x00\x00\x00\x00\x00\x00\x00Le\ +ftlong\x00\x00\x00\x00\x00\x00\x00\x00Bt\ +omlong\x00\x00\x00`\x00\x00\x00\x00Rg\ +htlong\x00\x00\x00`\x00\x00\x00\x06sl\ +icesVlLs\x00\x00\x00\x01Objc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05slice\x00\ +\x00\x00\x12\x00\x00\x00\x07sliceIDlo\ +ng\x00\x00\x00\x00\x00\x00\x00\x07groupI\ +Dlong\x00\x00\x00\x00\x00\x00\x00\x06ori\ +ginenum\x00\x00\x00\x0cESlic\ +eOrigin\x00\x00\x00\x0dautoG\ +enerated\x00\x00\x00\x00Type\ +enum\x00\x00\x00\x0aESliceTy\ +pe\x00\x00\x00\x00Img \x00\x00\x00\x06bo\ +undsObjc\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00Rct1\x00\x00\x00\x04\x00\x00\x00\x00To\ +p long\x00\x00\x00\x00\x00\x00\x00\x00Le\ +ftlong\x00\x00\x00\x00\x00\x00\x00\x00Bt\ +omlong\x00\x00\x00`\x00\x00\x00\x00Rg\ +htlong\x00\x00\x00`\x00\x00\x00\x03ur\ +lTEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00n\ +ullTEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00MsgeTEXT\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x06altTagTEXT\x00\x00\x00\ +\x01\x00\x00\x00\x00\x00\x0ecellTextI\ +sHTMLbool\x01\x00\x00\x00\x08ce\ +llTextTEXT\x00\x00\x00\x01\x00\x00\ +\x00\x00\x00\x09horzAlignenu\ +m\x00\x00\x00\x0fESliceHorzA\ +lign\x00\x00\x00\x07default\x00\ +\x00\x00\x09vertAlignenum\ +\x00\x00\x00\x0fESliceVertAl\ +ign\x00\x00\x00\x07default\x00\x00\ +\x00\x0bbgColorTypeenu\ +m\x00\x00\x00\x11ESliceBGCol\ +orType\x00\x00\x00\x00None\x00\x00\ +\x00\x09topOutsetlong\x00\ +\x00\x00\x00\x00\x00\x00\x0aleftOutse\ +tlong\x00\x00\x00\x00\x00\x00\x00\x0cbot\ +tomOutsetlong\x00\x00\x00\ +\x00\x00\x00\x00\x0brightOutset\ +long\x00\x00\x00\x00\x008BIM\x04(\x00\ +\x00\x00\x00\x00\x0c\x00\x00\x00\x02?\xf0\x00\x00\x00\x00\x00\ +\x008BIM\x04\x14\x00\x00\x00\x00\x00\x04\x00\x00\x00\ +\x048BIM\x04\x0c\x00\x00\x00\x00\x03\xeb\x00\x00\x00\ +\x01\x00\x00\x000\x00\x00\x000\x00\x00\x00\x90\x00\x00\x1b\ +\x00\x00\x00\x03\xcf\x00\x18\x00\x01\xff\xd8\xff\xed\x00\x0cA\ +dobe_CM\x00\x01\xff\xee\x00\x0eAdo\ +be\x00d\x80\x00\x00\x00\x01\xff\xdb\x00\x84\x00\x0c\x08\ +\x08\x08\x09\x08\x0c\x09\x09\x0c\x11\x0b\x0a\x0b\x11\x15\x0f\x0c\ +\x0c\x0f\x15\x18\x13\x13\x15\x13\x13\x18\x11\x0c\x0c\x0c\x0c\x0c\ +\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\x0d\ +\x0b\x0b\x0d\x0e\x0d\x10\x0e\x0e\x10\x14\x0e\x0e\x0e\x14\x14\x0e\ +\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\ +\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\ +\xc0\x00\x11\x08\x000\x000\x03\x01\x22\x00\x02\x11\x01\x03\ +\x11\x01\xff\xdd\x00\x04\x00\x03\xff\xc4\x01?\x00\x00\x01\x05\ +\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x01\ +\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x01\x00\x01\x05\x01\x01\x01\ +\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x05\ +\x06\x07\x08\x09\x0a\x0b\x10\x00\x01\x04\x01\x03\x02\x04\x02\x05\ +\x07\x06\x08\x05\x03\x0c3\x01\x00\x02\x11\x03\x04!\x121\ +\x05AQa\x13\x22q\x812\x06\x14\x91\xa1\xb1B#\ +$\x15R\xc1b34r\x82\xd1C\x07%\x92S\xf0\ +\xe1\xf1cs5\x16\xa2\xb2\x83&D\x93TdE\xc2\ +\xa3t6\x17\xd2U\xe2e\xf2\xb3\x84\xc3\xd3u\xe3\xf3\ +F'\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\ +\xf5Vfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf67GWg\ +w\x87\x97\xa7\xb7\xc7\xd7\xe7\xf7\x11\x00\x02\x02\x01\x02\x04\ +\x04\x03\x04\x05\x06\x07\x07\x06\x055\x01\x00\x02\x11\x03!\ +1\x12\x04AQaq\x22\x13\x052\x81\x91\x14\xa1\xb1\ +B#\xc1R\xd1\xf03$b\xe1r\x82\x92CS\x15\ +cs4\xf1%\x06\x16\xa2\xb2\x83\x07&5\xc2\xd2D\ +\x93T\xa3\x17dEU6te\xe2\xf2\xb3\x84\xc3\xd3\ +u\xe3\xf3F\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\ +\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf6'7\ +GWgw\x87\x97\xa7\xb7\xc7\xff\xda\x00\x0c\x03\x01\x00\ +\x02\x11\x03\x11\x00?\x00\xf4<\xdc\xcc\x96d\x9a\xa9;\ +@\x80\x00\x00\x92O\xc6P\xfd~\xab\xe0\xff\x00\xfbl\ +\x7f\xe4R\xc9\xff\x00\x95\x1b\xfdz\xff\x00\xef\xabU\xce\ +\x0dis\xb4\x00I>A%9^\xbfU\xf0\x7f\xfd\ +\xb6?\xf2)z\xfdW\xc1\xff\x00\xf6\xd8\xff\x00\xc8\xa2\ +UU\xf9\xe0\xdde\x8e\xaa\x92}\x95\xb7\xc0x\xa7{\ +o\xe9\xeem\x82\xc3n9 =\xae\xd4\x89\xee\x12R\ +/_\xaa\xf8?\xfc\xc1\xff\x00\x91D\xc0\xcc\xc8\xb3#\ +\xd2\xb4\xee\x04\x1e@\x04\x11\xfdU\xa4\xb20\x7f\xe5\x03\ +\xff\x00\x5c\xfc\xa9)\xff\xd0\xef\xf2\x7f\xe5F\xff\x00^\ +\xbf\xfb\xea\xb9\x9f\x7f\xa7W\xa6\x1a\x5c\xfb\x81c@\xf3\ +\x11\xfcU<\x9f\xf9Q\xbf\xd7\xaf\xfe\xfa\xacu\x0f\xe9\ +\x18\x9f\xf1\x9f\xc5\x89)\x1e>NU\x14\xb6\xaf\xb2\xbd\ +\xdbg\xdd\xa8\xe4\xcf\xee\xa8\xe5]\x95\x93I\xa8\xe2\xbd\ +\x92A\x9dO\x1f\xd9\x0a\xdfP\xb6\xea\xb1\xf7\xd3\xa1\x90\ +\x1c\xee`x\xa9aYu\x98\xed}\xdfH\xcc\x1e$\ +~k\x92R\xd8y\x02\xfa\xa7ik\x98v\xb8\x1f\x10\ +\xa8`\xff\x00\xca\x07\xfe\xb9\xf9U\x9e\x97\xf4.\xff\x00\ +\x8d*\xb6\x0f\xfc\xa0\x7f\xeb\x9f\x95%?\xff\xd1\xef\xf2\ +\x7f\xe5F\xff\x00^\xbf\xfb\xea?R;,\xc6\xb4\x83\ +\xb1\x8f\x97\x11\xf1i\xff\x00\xbe\xa8f\xe1d\xbf$\xdb\ +P\x90b\x0c\xc1\x04!\xfd\x9b\xaa~\xf3\xff\x00\xed\xcf\ +\xfc\xc9%'\xb7\xa8\xe1[[\xabxyk\x84\x1d\x08\ +N\xce\xa7\x86\xc65\x8d\x0f\x0dh\x00\x0d\xa7\x80\xab\xfd\ +\x9b\xaa~\xf3\xff\x00\xed\xcf\xf6\xa5\xf6n\xa9\xfb\xcf\xff\ +\x00\xb7?\xda\x92\x9b\x1d(\x1fJ\xd7A\x0du\x84\xb4\ +\x9e\xe1V\xc1\xff\x00\x94\x0f\xc6\xcf\xca\x9f\xec\xddS\xf7\ +\x9f\xff\x00n\x7f\xe6H\xb88Y\x15\xdf\xea\xda\x03@\ +\x04s$\x92\x92\x9f\xff\xd9\x008BIM\x04!\x00\ +\x00\x00\x00\x00]\x00\x00\x00\x01\x01\x00\x00\x00\x0f\x00A\ +\x00d\x00o\x00b\x00e\x00 \x00P\x00h\x00o\ +\x00t\x00o\x00s\x00h\x00o\x00p\x00\x00\x00\x17\ +\x00A\x00d\x00o\x00b\x00e\x00 \x00P\x00h\ +\x00o\x00t\x00o\x00s\x00h\x00o\x00p\x00 \ +\x00C\x00C\x00 \x002\x000\x001\x007\x00\x00\ +\x00\x01\x00\x00\x00\x0cHLino\x02\x10\x00\x00m\ +ntrRGB XYZ \x07\xce\x00\x02\x00\ +\x09\x00\x06\x001\x00\x00acspMSFT\x00\ +\x00\x00\x00IEC sRGB\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xd6\x00\x01\x00\x00\x00\ +\x00\xd3-HP \x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x11cprt\x00\x00\x01P\x00\ +\x00\x003desc\x00\x00\x01\x84\x00\x00\x00lw\ +tpt\x00\x00\x01\xf0\x00\x00\x00\x14bkpt\x00\ +\x00\x02\x04\x00\x00\x00\x14rXYZ\x00\x00\x02\x18\x00\ +\x00\x00\x14gXYZ\x00\x00\x02,\x00\x00\x00\x14b\ +XYZ\x00\x00\x02@\x00\x00\x00\x14dmnd\x00\ +\x00\x02T\x00\x00\x00pdmdd\x00\x00\x02\xc4\x00\ +\x00\x00\x88vued\x00\x00\x03L\x00\x00\x00\x86v\ +iew\x00\x00\x03\xd4\x00\x00\x00$lumi\x00\ +\x00\x03\xf8\x00\x00\x00\x14meas\x00\x00\x04\x0c\x00\ +\x00\x00$tech\x00\x00\x040\x00\x00\x00\x0cr\ +TRC\x00\x00\x04<\x00\x00\x08\x0cgTRC\x00\ +\x00\x04<\x00\x00\x08\x0cbTRC\x00\x00\x04<\x00\ +\x00\x08\x0ctext\x00\x00\x00\x00Copyr\ +ight (c) 1998 He\ +wlett-Packard Co\ +mpany\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00\x12sRGB IEC61966\ +-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\ +sRGB IEC61966-2.\ +1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00\xf3Q\x00\ +\x01\x00\x00\x00\x01\x16\xccXYZ \x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\ +\x00\x00\x00\x00\x00o\xa2\x00\x008\xf5\x00\x00\x03\x90X\ +YZ \x00\x00\x00\x00\x00\x00b\x99\x00\x00\xb7\x85\x00\ +\x00\x18\xdaXYZ \x00\x00\x00\x00\x00\x00$\xa0\x00\ +\x00\x0f\x84\x00\x00\xb6\xcfdesc\x00\x00\x00\x00\x00\ +\x00\x00\x16IEC http://ww\ +w.iec.ch\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x16IEC http://w\ +ww.iec.ch\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00.IEC 61966-2.1\ + Default RGB col\ +our space - sRGB\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.IEC \ +61966-2.1 Defaul\ +t RGB colour spa\ +ce - sRGB\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00,Refer\ +ence Viewing Con\ +dition in IEC619\ +66-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00,Reference View\ +ing Condition in\ + IEC61966-2.1\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00view\x00\x00\x00\x00\x00\ +\x13\xa4\xfe\x00\x14_.\x00\x10\xcf\x14\x00\x03\xed\xcc\x00\ +\x04\x13\x0b\x00\x03\x5c\x9e\x00\x00\x00\x01XYZ \x00\ +\x00\x00\x00\x00L\x09V\x00P\x00\x00\x00W\x1f\xe7m\ +eas\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x8f\x00\ +\x00\x00\x02sig \x00\x00\x00\x00CRT c\ +urv\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x05\x00\ +\x0a\x00\x0f\x00\x14\x00\x19\x00\x1e\x00#\x00(\x00-\x00\ +2\x007\x00;\x00@\x00E\x00J\x00O\x00T\x00\ +Y\x00^\x00c\x00h\x00m\x00r\x00w\x00|\x00\ +\x81\x00\x86\x00\x8b\x00\x90\x00\x95\x00\x9a\x00\x9f\x00\xa4\x00\ +\xa9\x00\xae\x00\xb2\x00\xb7\x00\xbc\x00\xc1\x00\xc6\x00\xcb\x00\ +\xd0\x00\xd5\x00\xdb\x00\xe0\x00\xe5\x00\xeb\x00\xf0\x00\xf6\x00\ +\xfb\x01\x01\x01\x07\x01\x0d\x01\x13\x01\x19\x01\x1f\x01%\x01\ ++\x012\x018\x01>\x01E\x01L\x01R\x01Y\x01\ +`\x01g\x01n\x01u\x01|\x01\x83\x01\x8b\x01\x92\x01\ +\x9a\x01\xa1\x01\xa9\x01\xb1\x01\xb9\x01\xc1\x01\xc9\x01\xd1\x01\ +\xd9\x01\xe1\x01\xe9\x01\xf2\x01\xfa\x02\x03\x02\x0c\x02\x14\x02\ +\x1d\x02&\x02/\x028\x02A\x02K\x02T\x02]\x02\ +g\x02q\x02z\x02\x84\x02\x8e\x02\x98\x02\xa2\x02\xac\x02\ +\xb6\x02\xc1\x02\xcb\x02\xd5\x02\xe0\x02\xeb\x02\xf5\x03\x00\x03\ +\x0b\x03\x16\x03!\x03-\x038\x03C\x03O\x03Z\x03\ +f\x03r\x03~\x03\x8a\x03\x96\x03\xa2\x03\xae\x03\xba\x03\ +\xc7\x03\xd3\x03\xe0\x03\xec\x03\xf9\x04\x06\x04\x13\x04 \x04\ +-\x04;\x04H\x04U\x04c\x04q\x04~\x04\x8c\x04\ +\x9a\x04\xa8\x04\xb6\x04\xc4\x04\xd3\x04\xe1\x04\xf0\x04\xfe\x05\ +\x0d\x05\x1c\x05+\x05:\x05I\x05X\x05g\x05w\x05\ +\x86\x05\x96\x05\xa6\x05\xb5\x05\xc5\x05\xd5\x05\xe5\x05\xf6\x06\ +\x06\x06\x16\x06'\x067\x06H\x06Y\x06j\x06{\x06\ +\x8c\x06\x9d\x06\xaf\x06\xc0\x06\xd1\x06\xe3\x06\xf5\x07\x07\x07\ +\x19\x07+\x07=\x07O\x07a\x07t\x07\x86\x07\x99\x07\ +\xac\x07\xbf\x07\xd2\x07\xe5\x07\xf8\x08\x0b\x08\x1f\x082\x08\ +F\x08Z\x08n\x08\x82\x08\x96\x08\xaa\x08\xbe\x08\xd2\x08\ +\xe7\x08\xfb\x09\x10\x09%\x09:\x09O\x09d\x09y\x09\ +\x8f\x09\xa4\x09\xba\x09\xcf\x09\xe5\x09\xfb\x0a\x11\x0a'\x0a\ +=\x0aT\x0aj\x0a\x81\x0a\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\ +\xf3\x0b\x0b\x0b\x22\x0b9\x0bQ\x0bi\x0b\x80\x0b\x98\x0b\ +\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\x12\x0c*\x0cC\x0c\x5c\x0c\ +u\x0c\x8e\x0c\xa7\x0c\xc0\x0c\xd9\x0c\xf3\x0d\x0d\x0d&\x0d\ +@\x0dZ\x0dt\x0d\x8e\x0d\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\ +\x13\x0e.\x0eI\x0ed\x0e\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\ +\xee\x0f\x09\x0f%\x0fA\x0f^\x0fz\x0f\x96\x0f\xb3\x0f\ +\xcf\x0f\xec\x10\x09\x10&\x10C\x10a\x10~\x10\x9b\x10\ +\xb9\x10\xd7\x10\xf5\x11\x13\x111\x11O\x11m\x11\x8c\x11\ +\xaa\x11\xc9\x11\xe8\x12\x07\x12&\x12E\x12d\x12\x84\x12\ +\xa3\x12\xc3\x12\xe3\x13\x03\x13#\x13C\x13c\x13\x83\x13\ +\xa4\x13\xc5\x13\xe5\x14\x06\x14'\x14I\x14j\x14\x8b\x14\ +\xad\x14\xce\x14\xf0\x15\x12\x154\x15V\x15x\x15\x9b\x15\ +\xbd\x15\xe0\x16\x03\x16&\x16I\x16l\x16\x8f\x16\xb2\x16\ +\xd6\x16\xfa\x17\x1d\x17A\x17e\x17\x89\x17\xae\x17\xd2\x17\ +\xf7\x18\x1b\x18@\x18e\x18\x8a\x18\xaf\x18\xd5\x18\xfa\x19\ + \x19E\x19k\x19\x91\x19\xb7\x19\xdd\x1a\x04\x1a*\x1a\ +Q\x1aw\x1a\x9e\x1a\xc5\x1a\xec\x1b\x14\x1b;\x1bc\x1b\ +\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c*\x1cR\x1c{\x1c\xa3\x1c\ +\xcc\x1c\xf5\x1d\x1e\x1dG\x1dp\x1d\x99\x1d\xc3\x1d\xec\x1e\ +\x16\x1e@\x1ej\x1e\x94\x1e\xbe\x1e\xe9\x1f\x13\x1f>\x1f\ +i\x1f\x94\x1f\xbf\x1f\xea \x15 A l \x98 \ +\xc4 \xf0!\x1c!H!u!\xa1!\xce!\xfb\x22\ +'\x22U\x22\x82\x22\xaf\x22\xdd#\x0a#8#f#\ +\x94#\xc2#\xf0$\x1f$M$|$\xab$\xda%\ +\x09%8%h%\x97%\xc7%\xf7&'&W&\ +\x87&\xb7&\xe8'\x18'I'z'\xab'\xdc(\ +\x0d(?(q(\xa2(\xd4)\x06)8)k)\ +\x9d)\xd0*\x02*5*h*\x9b*\xcf+\x02+\ +6+i+\x9d+\xd1,\x05,9,n,\xa2,\ +\xd7-\x0c-A-v-\xab-\xe1.\x16.L.\ +\x82.\xb7.\xee/$/Z/\x91/\xc7/\xfe0\ +50l0\xa40\xdb1\x121J1\x821\xba1\ +\xf22*2c2\x9b2\xd43\x0d3F3\x7f3\ +\xb83\xf14+4e4\x9e4\xd85\x135M5\ +\x875\xc25\xfd676r6\xae6\xe97$7\ +`7\x9c7\xd78\x148P8\x8c8\xc89\x059\ +B9\x7f9\xbc9\xf9:6:t:\xb2:\xef;\ +-;k;\xaa;\xe8<' >`>\xa0>\xe0?\ +!?a?\xa2?\xe2@#@d@\xa6@\xe7A\ +)AjA\xacA\xeeB0BrB\xb5B\xf7C\ +:C}C\xc0D\x03DGD\x8aD\xceE\x12E\ +UE\x9aE\xdeF\x22FgF\xabF\xf0G5G\ +{G\xc0H\x05HKH\x91H\xd7I\x1dIcI\ +\xa9I\xf0J7J}J\xc4K\x0cKSK\x9aK\ +\xe2L*LrL\xbaM\x02MJM\x93M\xdcN\ +%NnN\xb7O\x00OIO\x93O\xddP'P\ +qP\xbbQ\x06QPQ\x9bQ\xe6R1R|R\ +\xc7S\x13S_S\xaaS\xf6TBT\x8fT\xdbU\ +(UuU\xc2V\x0fV\x5cV\xa9V\xf7WDW\ +\x92W\xe0X/X}X\xcbY\x1aYiY\xb8Z\ +\x07ZVZ\xa6Z\xf5[E[\x95[\xe5\x5c5\x5c\ +\x86\x5c\xd6]']x]\xc9^\x1a^l^\xbd_\ +\x0f_a_\xb3`\x05`W`\xaa`\xfcaOa\ +\xa2a\xf5bIb\x9cb\xf0cCc\x97c\xebd\ +@d\x94d\xe9e=e\x92e\xe7f=f\x92f\ +\xe8g=g\x93g\xe9h?h\x96h\xeciCi\ +\x9ai\xf1jHj\x9fj\xf7kOk\xa7k\xffl\ +Wl\xafm\x08m`m\xb9n\x12nkn\xc4o\ +\x1eoxo\xd1p+p\x86p\xe0q:q\x95q\ +\xf0rKr\xa6s\x01s]s\xb8t\x14tpt\ +\xccu(u\x85u\xe1v>v\x9bv\xf8wVw\ +\xb3x\x11xnx\xccy*y\x89y\xe7zFz\ +\xa5{\x04{c{\xc2|!|\x81|\xe1}A}\ +\xa1~\x01~b~\xc2\x7f#\x7f\x84\x7f\xe5\x80G\x80\ +\xa8\x81\x0a\x81k\x81\xcd\x820\x82\x92\x82\xf4\x83W\x83\ +\xba\x84\x1d\x84\x80\x84\xe3\x85G\x85\xab\x86\x0e\x86r\x86\ +\xd7\x87;\x87\x9f\x88\x04\x88i\x88\xce\x893\x89\x99\x89\ +\xfe\x8ad\x8a\xca\x8b0\x8b\x96\x8b\xfc\x8cc\x8c\xca\x8d\ +1\x8d\x98\x8d\xff\x8ef\x8e\xce\x8f6\x8f\x9e\x90\x06\x90\ +n\x90\xd6\x91?\x91\xa8\x92\x11\x92z\x92\xe3\x93M\x93\ +\xb6\x94 \x94\x8a\x94\xf4\x95_\x95\xc9\x964\x96\x9f\x97\ +\x0a\x97u\x97\xe0\x98L\x98\xb8\x99$\x99\x90\x99\xfc\x9a\ +h\x9a\xd5\x9bB\x9b\xaf\x9c\x1c\x9c\x89\x9c\xf7\x9dd\x9d\ +\xd2\x9e@\x9e\xae\x9f\x1d\x9f\x8b\x9f\xfa\xa0i\xa0\xd8\xa1\ +G\xa1\xb6\xa2&\xa2\x96\xa3\x06\xa3v\xa3\xe6\xa4V\xa4\ +\xc7\xa58\xa5\xa9\xa6\x1a\xa6\x8b\xa6\xfd\xa7n\xa7\xe0\xa8\ +R\xa8\xc4\xa97\xa9\xa9\xaa\x1c\xaa\x8f\xab\x02\xabu\xab\ +\xe9\xac\x5c\xac\xd0\xadD\xad\xb8\xae-\xae\xa1\xaf\x16\xaf\ +\x8b\xb0\x00\xb0u\xb0\xea\xb1`\xb1\xd6\xb2K\xb2\xc2\xb3\ +8\xb3\xae\xb4%\xb4\x9c\xb5\x13\xb5\x8a\xb6\x01\xb6y\xb6\ +\xf0\xb7h\xb7\xe0\xb8Y\xb8\xd1\xb9J\xb9\xc2\xba;\xba\ +\xb5\xbb.\xbb\xa7\xbc!\xbc\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\ +\x84\xbe\xff\xbfz\xbf\xf5\xc0p\xc0\xec\xc1g\xc1\xe3\xc2\ +_\xc2\xdb\xc3X\xc3\xd4\xc4Q\xc4\xce\xc5K\xc5\xc8\xc6\ +F\xc6\xc3\xc7A\xc7\xbf\xc8=\xc8\xbc\xc9:\xc9\xb9\xca\ +8\xca\xb7\xcb6\xcb\xb6\xcc5\xcc\xb5\xcd5\xcd\xb5\xce\ +6\xce\xb6\xcf7\xcf\xb8\xd09\xd0\xba\xd1<\xd1\xbe\xd2\ +?\xd2\xc1\xd3D\xd3\xc6\xd4I\xd4\xcb\xd5N\xd5\xd1\xd6\ +U\xd6\xd8\xd7\x5c\xd7\xe0\xd8d\xd8\xe8\xd9l\xd9\xf1\xda\ +v\xda\xfb\xdb\x80\xdc\x05\xdc\x8a\xdd\x10\xdd\x96\xde\x1c\xde\ +\xa2\xdf)\xdf\xaf\xe06\xe0\xbd\xe1D\xe1\xcc\xe2S\xe2\ +\xdb\xe3c\xe3\xeb\xe4s\xe4\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\ +\x1f\xe7\xa9\xe82\xe8\xbc\xe9F\xe9\xd0\xea[\xea\xe5\xeb\ +p\xeb\xfb\xec\x86\xed\x11\xed\x9c\xee(\xee\xb4\xef@\xef\ +\xcc\xf0X\xf0\xe5\xf1r\xf1\xff\xf2\x8c\xf3\x19\xf3\xa7\xf4\ +4\xf4\xc2\xf5P\xf5\xde\xf6m\xf6\xfb\xf7\x8a\xf8\x19\xf8\ +\xa8\xf98\xf9\xc7\xfaW\xfa\xe7\xfbw\xfc\x07\xfc\x98\xfd\ +)\xfd\xba\xfeK\xfe\xdc\xffm\xff\xff\x80\x00 P8\ +$\x16\x0d\x07\x84BaP\xb8d6\x1d\x0f\x88Db\ +Q8\xa4V-\x17\x8cFcQ\xb8\xe4v=\x1f\x90\ +HdR9$\x96M'\x94JeR\xb9d\xb6]\ +/\x98A\x00\x930(*l\x0c\x9c\x03\x02\x13`P\ +0\x07?\x02?\xe8O\xe8X\x06 \x01\xa4\x00hO\ +\xfa$\x1a\x93H\x82R\xdf\xf0J}J\x06\xff\xa4\x80\ +\xa9t\xd0\x05\x1a\x07U\xa5\xd5\xeb\xf4\xfa\x8d\x86\x05S\ +\x81S\xe9VhK\xfc\x05o\x01\xd5\xaa\xf6\xca\x8d~\ +\x0fh\xb4\xd2\x00P\x8a\x95\x92\xefK\xb0P\xacW\x9a\ +M\xca\x9dI\xbe]05\xca\xf0\x02\x98\xfe\xc8>\xf2\ +O\xa7\xa6U\xe2\xf3\xcc<2O\xb7\xce\x1aSO\x07\ +\xe8Bcm!\x10\x87\xa7)\x89\xb5C\x00v\xb4#\ +?\x01\x81*\x98\x88]\xf7i\x87\xa4g\xb18+\x1e\ +\xe7\x01\xb7\x8e\xed\xaa\x17:\x15\xfa\x0d\xba\xde\xda\xf7\x98\ +Ln;\x7f\xbe\xe2\xf0!\x99\xec\x0d\xe2\xbbj\xe8T\ +\xab\x18^\x7f6\x9f\x90\xc8uz\xbb>\xcf\x81\xfc\xf8\ +\xf4=\x9c\xbe\xb6\xf3#\xdc\xbaa|VN\x0f\xa3c\ +6\xfa\x94\x04\xbfAb\xcf\xf4\xe0)\xc0\x03:r\x08\ +-\xebzc\x03\xc1\x10L\x15\x05>\xe6\x94\x1cc\x13\ +p\x89\x04h\xc2\x86+\xee\x90\x00\xd0\xc8\x10(C\x83\ + \xdb\x0f\x91\x00LD\x05\xc1q,M\x13\xc5\x09B\ +\xa4fE\x85\xf9\x17\x17\x8d\xa6\xfcdk\xb9\x08\xb0;\ +\x1b\x84\x84LtV5A0_\x14\xc8\x12\x0c\x85!\ +\xa2\xe7\xd4\x8c|\x93RI\x00SI\x84|\x8c}\x1f\ +(\xc4\x0a\x01\x07\xb2\xa8\x9aCK\x058\x0f-\x812\ +$\xbd/\xcc\x13\x0a\x04gL\x86\x08\xed3\x8a\x87\x84\ +\xd4v#\x09\x98\x08\x02\x8a\xf3\x88\xda7\xce\x84\x5c\xa7\ +1O\x13\xcc\xf5\x03\x9d\x13\xe9\xc44P\x02\x11\xc9A\ +\x9b\xa8\xc42\x03\x01\x03-\x14?\x0c\x14h\xf0\xe3O\ +t\x8d%I\xa3\xe7\x95,w\x0c\x94\xc8|oS\x86\ +\xad\x0d\x0d\x0d\x95\x09\x0e\xfe\x8b#}!JU\x15M\ +T\x873\x07\x99\xe1E\x0c\xa1\xf1\xb9Y\x9ah\xc4\xb6\ +\x03\x810\xf8\xdaDN\x22\xb8\xdbS\xc4\xae\x13\x9bU\ +\xc8q\xab\xae\xe1\xa1V\x15UV\x9e\x14\xc8\xc8\x1e\x9b\ +\xb6\x89\xa9O\xd1\x03u\xacD\xd7\xb5\xfb\xa4\x89\xaaF\ +\xdd\xbch\x9e\xb7\x09\xe6\xea\xcav\x13\xa8\xe0\x1f\xb7I\ +\xfb;\xb8\x8e\xdb\xb2\xf29Wr\xd4\x82<\xc8E\x80\ +\xe3\xbb\xab+\xa3d9\xd7\xdd\xe3}-\xd0-\xec\xe0\ +X\xc8\xa3\xc6\xe4\xbc\xce\xc5\xde\xbf\xb9`\x04\xdc\x02\x84\ +X\x88T\x05\xe2\x80r8\xca\x9e\x87\x8d\x9c\x1e\xd6f\ +\xe5j\x8b\xd6\xe0MB6\x10\xe2\xc6L7^\xe8\x89\ +\xf9\x95\x9fc\xa6\x5c(\x9a\xf9\x89\x9be!\xf72\x87\ +\x02\x80xN\x15\x80:\xb6\x15 \xed.\x92\x9d\xea\x81\ +\xe8O\x06R\xe9\xad\x97r\xb5\x9b\xae\x17\xa6\x8c\xe0`\ +\xeec\xcdr\xbb\xb9\xa5\x93|\xdd\xb4\x83\xcc\x08\xeb\x80\ +\xa8\xf9\xaf\x93an\xc4\x1cb\xec\xb0\xc7\xb3\x87\x96\x89\ +\xbbi\xe4\x12\xe6GQ\xbf\xa3z8\xfb\x8c\xdb\xa8~\ +i\xef\x06E\x89\xbd\xef\x88>\xb8\x08\x82\xb1\xd1\x12U\ +\x86\x5c(~\x8eY\x9b\xa8\xcc\x1f\xdb\xc6\xd9\xa5[K\ +\x95\xd5y9h\xe8{\xefX\x07\xc6\xa74d\xef\xa8\ +\xbcD\x04\x81a\x0fD\x14\x82\x9d(2\x0a\xf5\x00\xd3\ +Z\x07\x02G\x7f\x5cu\x9d\x9d\x89\xccu\xf6\x8758\ +o\x1a\xd0\xbf:\x8a\x82\x1d\xe8)\x17\x91epa\xe1\ +\x87\x9c\xaa\x1d\x8ccT\xce9Zr\x15\xc6\xdf\x93\x0b\ +\x19E\xb6\x89I\xe7\xc8\xcf\xeb\x88\x1b\xc1\xa7\xbdwh\ +Gz\x08\x02\x82\x07\xc4(|B\x00\x9e\xd2\x06\xc2%\ +\x0e\x04#\x07\xb7\xdcy\x98\xdf\x89n`~\x85\x8b\xe2\ +a\x16_q\xecz{\xbe\xf7|\xf0\x1e\x13\xc4x\xc4\ +5f=p\xce\x10\x06\xd4\x09\x1a+P\x049%H\ +\xdc\x88\xd9\xf7\x80\xc1\x01\x07\x0d!\x8e\xf7I\xe0\x0c\x0b\ +\x90l9\xc1\xb0\xb8\x1d \xc90MC\xc0v\x09\xc8\ +L \xc5l)\x13\x0e\xe9\xce\xbd\xf0(\x22\xa1\x80\xad\ +p\xa0\xc8\x1f6V2\xe2\x81\xfc\x09\x1bP-\xb6\xbc\ +\xe5D\xf4\x1e\x92\xfc\x22\x8e]E9\x976\xdf\x02<\ +I\x0b\x01\xce&\x09\x03\xf4\x04\x80\xbaD=c\x94o\ +\x08X\xac\x19\x86TY\x17\xad\xf1\xd5\x81!\x19\x17\xc5\ +t3p\xe4mf9\x86:\xc7\xc8\xb3!rKf\ +\x01\x90\xc6\xe8\xdd\x9e\xd3\xdcR)L5GQ\x0a\xa3\ +C\x00xRk\xa8~\x08\xe8\xfc\x1cEL\x81\x12\x8a\ +\xaa\x17@\x00c!\xc1\xe9\x1c\x5c#\xd4y6p\xc6\ +\x0f#;\xcddP\xfd\x93\xc6\xd2\x16\xca\xc7\xe0\xfbs\ +\x11\xc5=\x00Y<\x01\x9c\x10\xab\x07\xd2\x8c'=\xd1\ +Y)\xc4\xb0\x88\x95A\xadI\xb7\xf7\x02\x8e\xe1\x9c5\ +\x8c\x86f3<\xc8z\x02V\xb0n[\x09\xc9\xb9\x99\ +70\xe6\x86\xa3\x9cO!\xf6b\x09\xc0\x9f1\xc3\x19\ + \x1dS,r\x8d\x89\x9c3\x9dp\xef\x1dO \xd0\ +\x80\xf8\x9f\x14\x01T\xd9\x06\xb0\xb8\x90\x09y\xbc\x1f\x04\ +\xec\xe1\x10\xa9\xeaWC\x01\x14+d81\x912\xcd\ +W1\xb6\xd4\xdb#K\x91D\x11\xb1\xe9\x91\x18\xde\xe2\ +\xe4\xe2`\x81\xf109\x89\x020\xa5\x87\x90\xee\x15T\ +\x0cJ\x8b\x8a\x0c)\x8f\xa0\xe0\x1b\x0c\x18\xa4\x82z\x1c\ +\x0c\x02e\x11\x0c\x13\x1c'\x867>\x89\x16\xe1K\x0e\ +\xb4l)?A\x80,\x13\x0c\x85E\xe2\xbat\xce\xb2\ +4\xf2!\xc4:\x87\x93\xc6\x1f2H\x1eG$\xc0\xfb\ +cs\x02a$ IM\xc1jL\x14\xc3=\x9c\x11\ +\x02\xa4)j\x00\x8e\x135\x0c?\x8fz\x8c=IC\ +\xdf\x02a\xca\xa6\x09\x00\x95S\xc2\xe1\x14\x7fC\xcd\x0e\ +\x05\x00H;\xaa\xc0\xeaH\x93\x96\x18\xd2W\x11-\x22\ +,\x91\x96\xeeJ \x11\xc7\xab\x0e)\xa2D\x125\xac\ +Z\x83\xba\xdc\x12\x88\x85F\x1e\xe3\xd6\x8d\x87P\xa2{\ +\x86@\xbbD\xd4D&\x05\xe0\xfd_\xc4\xf5=!\xe2\ +\xae\xc2\x09W\x04\x1b\x12$.\x9c\xd3\xa2DIb\x15\ +\x01^\xc5*\x81\x95\x92J\xcfR!=\xdb\xbbyH\ +\x00\xd2\xce\x04\x011g\xc5\xf9\x10I\xe3\xe2\x5c\x84\xa4\ +X3-\x0aC\x096\xac-\x88\x1b\x5c(\x97a\x0a\ +\xa6!V\xda\x02\xa1\xc7m\xc6\xe2@\xab\x93\x9e\xaf\x11\ +\xb7\x90\xc6\xeb\x15,W3\xcd\xcaYg-/\xa2-\ +iE\x22B\xe6\x0b@ys\xc2Y\x10\xb9\x82@:\ +\x0aK\xac#S\xd0w\xbbBR\xda\x05YXC\xe8\ +\x18\xaa\x12\x93\x986\xa4\x08]\x17\xc4`\xafx`\xc0\ +\x1eCa\xe3J`T\x92\x8dw\x16!\x11:cL\ +\xe2:%\x83\x22\xf2\xfe\x0e\xc5\x0e\x01\xc8dg\x0bx\ +\x0c\x18\xae\xa1\xfa\x9e\x98\xa0\x0b\x01\xa2\xbf\x06\x0d\xc8\x9e\ +\x05\x88`\xe9\xc2C\x8c%\xe1P=n\x9a\xeb\x82\x15\ +\x92\xc6E.\x19\x1a\xda.\x09\x15d2\xe4D\x85l\ +L\x1b,q\x09z\xb0N|\xa0\xb3N\x10\xc2\x9e\x1a\ +\x22\x17\xa07\x0a\x8cl$\xd5UL\x0eB8-c\ +\xd0\xe2D \xf03\x99\xc3`g\x22kwc'U\ +\xees\x0e5\xc7\xcbv\xdfK\xe0\x89\x93\x87\x18\xb5\x05\ +\x07\xbc\xac&B\x8eY\x0c\xc4@$e\xd04\xecG\ +`\xe7U@\xbf2\x03\xa9\xc2'F)\x10\x12\x99\xac\ +<\x0a\x0c\xdc\x22r3]\xb1v\xf6\x93\x99l\x96\xb7\ +\xb2m\xc2y\xf6V\xfa\x91($\xf6 \xac\x17D\xa2\ +KB\x0b`u\xa1\xc2I\x0c\xaeC\xd5*\x83\xd0\x18\ +\xdf%p\xbb\xd2Uh\x87\xcaqY*e^qp\ +\x10\x032\x02\xf0w\x8aHDe\xac2\xd8\x8b>\xbc\ +IYr\x88\xfb\x1fR\xfe\xfc\xa0\xbat4\x11\xea?\ +!c\x8bZ\x0d\xb0\xa5\xad\xc13\xdd\x19:\xec}\x9b\ +\x03dB\xe8\xf0\xb0e\xc1\xd0(\xa2jD\xf0o[\ +\xc5\xb8\xcf\x1c\xcb_\x08w|\xae\x22\xbe\xd4\x04\x1e\xcc\ +eD\x13\xa4\x85\xd8\xea\x95\xc42\x84\x8d\x80\xa9\xb7\xc1\ +K\xdd~#\x18|_\xf2\x19M#\xc3dD\xb6*\ +\x18\xe1\xcb}\x9d\xa2.L\xda\x0a\xeezg\xd9\xed\x94\ +\xa3\x85\x9aD\xa3\x0f~\x0fJ/\xb7\x0f\xae\xdf\x0a\x9b\ +\x85\xdd\xee=\xca\x86p\x01\x0b\x9d\xea\xf4\x16\xecg}\ +b\xf7vuc9\xdd\xc7o7'\xb4\xb6Y\x0d\xcf\ +\xf0\x1f@\xa2m\xf80\xf7\xf2#\xe0\x14+\x81pG\ +;\xc1\xb77\x0aZ\x5c3\x87>\x09\x0d\x220\xec\x8c\ +c{\xca[\xe2LL\x15\xb1G\x19\x8d\xc6N\x09\xf1\ +\xdd\xf7\xbfw\xf9\x0b\xdb\xbc\x97q?\x1e\x0e\x01\xb8I\ +\x0a\xe1i\xc7\x86\xa2Y]\x86\xb8\x89\x19\xa5\x0d\xda\xc9\ +\x11w\xd6\x1c:\xc0\x8b\xbb\xa1\xafi\x90m\xab\xbe\x90\ +_\x1f\xe4.\x83\x91\xed\xed\xc1\xd1w')\xe9\x5c\xaf\ +\xa6i\xa9^\x22Ee%\xeb\xa4\x15\xc4\xb7nip\ +\xaf\x9f\x18\xde\xd6_|O\x8e\xc0\x82\xbb\x17A!]\ +\x0f\xb3\xf0^\x8d\xda\x88OK\x0a\xfd5\x05\xee\xc9\xcf\ +\xd4H\xc6\xa2V8\x84\x8a>\xb6\xdf\x8fB\xd0p\x97\ +\xba\xab\x9e\xa0\xed\x04\x81\xef\xf8\xbe\xf4C\xb8\x04zP\ +\x14C5\xa0\xe2\x1br8\x1d\xbd\xd1m\xeb\xc7$\x9e\ +\x00\xa0\x18\x86#!\xbe5\xd5 /\xa6(&WJ\ +\xa1\x10*\xa3\x17s \x94\x00w\xc1>\xef\x88\x92\xe7\ +6\xc4\xff\x08\x81\xbdU\x00\x1a\x02\x0f>$\xc5?\x9b\ +\x86\xben\x1b\x00\xff\xd9\x04\xef\xf5\x04-\xd5\xbe(\x7f\ +\x00\x89\x17?\x8cT\x12\xcd\x8fI9\x84\xec\x1e\x10\xe3\ +\xe3\x91H\xd4\x885A\x1a\xeb\xefl\x92MP%\x00\ +#\x17\xdcO\x1e\x88_\x0a\xdc\xac\x0f`\xb4\x85\x82B\ +\xf1\xc8dp\xa9d\xe2G\x92Y\xef(\x22fB\xcf\ +g\xa2\xf9\x82\x04\xfeh\xe4$(\xac\x10\xa1L\x89 \ +\x8e\x0b/\xf4R\x8c\xdc\x14\x01\x0e\xcda(\x0fBI\ +\x00\x8f \x22\xe5\x98\x9d\xc5\xa4\x92P\x1c\x88%\x86\x22\ +h\x88V+\x94#\xc0q\x06`\x8b\x04\x01u\x03e\ +T<\x01\xfa\xe1\x8fl\x1a\xe2@\xc8\xec\xe8#)\x16\ +\x1eNf\xcf\x0e,\xfe\x223\x02B@\x0fP\x98\x13\ +\x0dn\x0a@\xcf\x07\x06\xf6\x12\xb0\xa8\x0fA?\x0a\xe1\ +\x0e$\x0b\xce\x8b\xeb\xd4x\x8ec\x08\x87\x94\xfd\xb0\x18\ +\xf9%\xae\xfa\xf0 \x00\x0e6\x82\x8f<$\x10@\x17\ +0f\x07\x00\x8c\x22\x08\xb2\x19Ay\x07P\xa4$G\ +>\x01\x8d:\xf5\xa2\x1e\x16\x10\xfc\x13a\x09\x10,\xb6\ +#\xe9\xaa\x02k\xd0\x15\xf0\x82#\x07\x90\xe2\x8c\xf2\xf9\ +\x05p\xef%\xb4\xefk\x8e\xd5MX\x98\x22@I!\ +4\x18\x0f\xf2!\xed\x1a\x01\x8d\x17\x0e\xe2B\xa6\xe0H\ +\x05\x8cl\x15\x08\xd0!\xa1k\x15!D\x0f\xf1X\x0b\ +\xf0~\xceHb\xd90\xcf\x11m\xe3\x08\xca\xc6\xda1\ +#\x05\xac\xfc\xef\xab2\xfe\x82?\x13\x114p\xc2!\ +\x13\xb1?\x14\x02?\x14QH\xc6\xd1N!\x91R\x16\ +\xb1W\x15\xb0\xb4\xe1\xed\xdb\x00\xca\xbeU\xcf\xd9\x16\xce\ +\xf1\x17\x10\xce\xc5g\xb0\xda\xc27\x18\x116!\xd1\x88\ +\xa8\xf1\x8d\x18\xeao\x191L\x22\x11\x9b\x19\xe0\xff\x15\ +\xc2>\xe9\xe9a\x1a\x8f\xd4\x96\xa6<\xe2\xcd\xeb\x17M\ +\xef\x12\x8b\x92\xd5\xa2;\x1c\x11\x85\x13\x84\xab\x13\xd1\xc9\ +\x1c\xa2;\x19\x11K\x19b\x17\x1dqY\x1d\xb1\xa2\xe5\ +\xcaG\x16Nt!p\x87\x08\xae+\x16\xed\xe8\xbe\x91\ +\xf0\xef\x8dT\xcan\xfe#\x91\xfc\x06H\xc7\x1cR\x03\ +\x18\xb2\x08#r\x0d\x19Q\xd5\x15R\x17\x1d\xc2<\xc8\ +\xf2!\x12EX31\xaf\x22\xac\xf4\x92\x90\x1f\x22\x22\ +\x15\x09Q~I1\x83$\x11\x87$r\x06@\xe07\ +(`B\x06r\x8c\x08\x08\x5c\xcc\x01\xcf\x0eax\xcc\ +\x01\xd0A2O\x1d\x22\x1f!Q\xa0#\xf0I\x1eB\ +4\xee\xa7\x17\x0cB$\xc4p\xca\xf9rp!0^\ +\x88\xd1-'q3\x1c\x22\x1b\x1cj\xe6%\x8fJ\x01\ +\x00\x14rP\xcc\x9e\xaab\x84\xc18\x10P:\x11\x09\ +0\x1f\x82Y*2\x10!R\xa9!\x91\xdf\x16\x0bx\ +\xfd0\x11\x11\x90\x8ed\xef6\x1fR9\x17\xc2=#\ +\xf2C-2\x81-bN\xbf\xf0:\x19\x04z\x06\x02\ +0\x18\xb34\x16\xc4\xe8\x0d\xeb\xa2%r\xf9%1\x9d\ +%q^p\x0e\xa1+\x023+G\x19\x1b\x11\x1c\xb8\ +ew\x09\x021,pc\x1f\xb2y-\x02\x19-J\ +\x90$\xe6\xde\x8f\x08\xf4#\xe1\x0f7\xe0\xd0\x85!Z\ +\x13\x22Q42\xa7%R\xaa#\xcf\x1c\xc3q\xff+\ +*\xc0\xf2mI5\xb1!\x0c\xf2u1\xb3k9\xb3\ + \xd1\xd2H$\x22r\x01\xeb\xf8\x17\x81\xd6a\xe2@\ +\xab\x01\xdc\x1d,\xba\x09\x004hbE8\xc2\x1d/\ +\xf2X#\xb1\x0ax\x10\x0b'\xcd\xde\xe2qk&\x93\ +\xa5\x1bR\xc2!\x13e\x1f\x92=:\xf3\xe9$S\xb5\ +(\x22C\x0d\xf0j\xcdpn$\xe4\x00\x0a`N\x1c\ +4\x1c\x1bBI=\x91Q9\x13\x019Q\xa4\xf1\xf3\ +P#\x12'\x0c3X\xfd\xc9\xe55\xf3\x10#jb\ +\x93r;\x1b\xf4\x011\xf3o23r$@\x83E\ +\xa0\xa2\x80\x02P\xc8,\x86\xc8\xa2GBQ\x99B\x93\ +\xde#\x91\xe0\xee\x10J\x22\xcf$VS\xa3C\xd1\x1f\ +?R`\xe3K\x91\x06\x13\xfdD\xd2\xcf;\x14S@\ +s$$J\x1c\x04\xe0`\xa8\x01J\x19\xe2P\xc5\xe0\ +&\x1e4\xb4\x1d\xd4#\x1c\xf2\x0f4Q\xd9G\x227\ +\x10\xb1\x0f\x11\x22.\xb7\xe7\x95\x01r\xbbC\xee/\x17\ +3b\xe7\x8d\x01\x0dr\xcd'\xb4P!sp$\x84\ +\xa6\x15\xd4\xf4\x1b@9O\xa0F$\x0bN\x17\xef\x9e\ +\x08S\x8bK\xd2Q8\xf3G9\x22;%\xc8\x05?\ +b\x0fCe\x9f+\x82#+\xc9t[3\x131p\ +'?\xf4\x97@3\xb3 T\x9e$\xa0\x8bT \xad\ +7\xe1\x0e\x15\x228\x8f\x88&\x1a\x15T\xcd\x22OF\ +\xd2\x13G\x13J\x02\xac\xe70\x8e\xa42\xcb\x81H0\ +\xc7Hr.\xefR2!\xe7\xaas\x01\xabX!\x95\ +\x12\xf4O'\xf4\x9dEbR\x0cU\x94\x0f@\xd3Y\ +\xa1\x08\xc5#\xef\x02\xa0\xcc\x16\x95\xa8\x142\xf7P\xd2\ +\xa5=\xb5a*\xd4.\xc9\x00{\x16umM5q\ +MuuM\xb3\xa9\x17\x91\xbc#S\x1dX\xd5=Y\ +\x02Xda\x0c\x8f\x00\xf2\x22\x11\x02\x10\x80\xcb\x0f\xc1\ +`\x13\x82aU\xd2\xfd[t-!\xc7\x83L\xc2-\ +\x16\x85cRB!R\x8cK,\x14\x8a!\x95~\x88\ +\xb5\x82\x1a\xb5\x87NsmN\xd4T&\x00\xbfb\xe0\ +\xecWL\xe0!\xe0\xf1c\xa0\xad;\xe1W_u\xb1\ +/\xa2\x13=\xd2\x1a\x85\xf1\xa6\x86\x8b\xdc\xd9\xcaW?\ +4@z5.\xdf3\x196\x957N\xa2\x15N\xe2\ +_b\xe0\xbfc$?cb\x1dc\xa0\xf1c\xeb\xf9\ +d\x22__\x96K_\xd5\x16\xc3$v\xeeU\x1c \ +\xd6\x08\x07\xd6\x0c!\xef\xdfWt\xdcH\xa4\x8e\xc5\x94\ +K]U\x8b \x15\x8fb\xd61cB!h\x16\x84\ +\x17\x96\x88%\xd6\x8c!\x16M0'\x01Vl\x92#\ +p\x86\x91\xc9!\x5cu'M\x93`\x22\xf4F\x88\xb5\ +\xd2#5\xd7k\x95\xda&\x0a\x9e\x09@\xb8\x10W\x04\ +\x14b Y\xc0yUA\xa1U\x96\xcddt\xc14\ +\x91\x084T\xcbV\x91\x14\xde\x16\x0bC\xa2&}h\ +\x1cn6b\xef\xd6gST\xe9]\x93\xb6%\x8f\xec\ +\xb0\x81V\x1a\xc8\x9e\x8a\x22\x0ev\xe1\xaa\xc0`\xb6\x06\ +P\x05Z\xf1GK\xf5\x11L6N\x80\x12_W\xad\ +\x99>\xd7+?\x14\x855\xc1\x11n\xa2-:\xb6i\ +s\xf6\xf9t\x22^\x04\x17\x90\x05\x07$t@B\x05\ +!\xady\xe1\x96\xd0\x81$\x0e\xe9\x96\x1dA\xcb*\x17\ +\x19vw\x1c#\xd4\xc9\x0b\x96\x04\x22\xb6\x9fj\x22\x1c\ +\xd4\xc5\xaf\x1e\xf4\xdf\x1fT\x91,\xb3\xadf\xb7A@\ +\x92J#\x16\xce \xf6\xd3%\xb6\x94pi\xd2\x07\xd0\ +\xcfG\xf4\xd5nu\xcb|\xd6\xacJ\x0f\x9e\xfa4\xe5\ +}w\x89@V\xfb}\xe25~\x22\x0d~b:\xfc\ +\xf7\xbe\x22\x97\xf5nV\x0em\xc5D\xca\x0f\xe42f\ +7a\xf6#\x80\x96'f\xf6+\x81\x023\x81B\x0b\ +\x81\x8299x:\x22\xb0\x87Y@\xc4\x07i\xdf\x05\ +X+sMR\xd5q\xf7}W\x87\x84\xe2\x11g\x18\ +@\x22\xf8D \x98H#r\xafeO\xd4\x82n\xab\ +&\xa6H\x88\x15\xcf#veS4\x95\x80\xb5;x\ +\xd8r\x22Xv xz#Tw9\x98\x81\x01\x15\ +o\x1e\xb2-\x5c\xd6\x9a \xb7\x84#\x8b>\x13\x01|\ +\xb3\x80h\x08\x22 \xbb@\xee\x0a\x81\xf3\x8da\xf1\x89\ +\xe2.\x03X\xe0\x04\x04\xce\x0e\xc9\x06!\xf5\xa8\x16\x81\ +@\x10\x18\xf4\x0c7j\xa4x\x1e\x22sU|B\x1b\ +jx\xb9ab\x17\x8b\xe26\x94,^\x0a\x98\xdcO\ +\x0b\xac\x14\x81\x1a\xba`\xe9d\xee!C4\xcf\x5c0\ +\x15\x82V\xa5n\x94C\x82\xf7\xd1,\x8aj#\xa9\xf6\ +\x89\xb9\x1aLM\x86\x0a-\x83\x92\x96S\x00\xf3S&\ +N\xedr\xd5\xc8\x92x\x8a\xcf\x97qH\xcf9N(\ +,$\x0c\x12\x01\xb2\x0e\x039|\x03\xf9JH\x17\x9e\ +\x1a\xc1\x97\x85@u/5b\xc3V\x99\x90\xc2\x15M\ +\x193\x8bMJCMO\x93\xa23n\xe5co\x22\ +8\x06\xb9\xb4\x08A'\x9b\xa1qC\x88)\xc2\x0aS\x82\x81\ +*\xcf\xcf\x18\x0b\xfcH\x06P\x09\x8c\xae\xbd\xc9\xbb\x9e\ +\xa1>\x0a\x91\xa3\x05\x83\x87\xdc\x1cG \xa2\xaa\xf4V\ +\xa0\xa3\x90i\x0b\x9cJ\x9c\x0a\xab\xbc\x0a4\x12\xa2\x9a\ +\x91\x08\x0c|D\x83\x92\x0a> \xa0[\x10{ \xa4\ +(\x1d\x17\x91\xa14d|\xa8\xf0\xdaY\x1b%p\xfa\ +|\xf9\x19\x822\x0aI\xa0\xa13Dn6\xa3k\x92\ +\x19\x97\x0a\x1cp\x95IiLt\xe7\x1f\xe0\x11\x9d)\ +\x90i\x00\xf4\xd3\xa1\xe4L\x8e=6\xa7\xf2s&\xa5\ +\x13\x02O'\xa5\x8e\x99\x9e\x08\x1f\xb3IJ\x82\x89R\ +\xc2,\x5c\x00s\x88\xb2\x18\xce\x8e\x03\xdb\x03\xb1\xf3\xc2\ +}2$\xf2\x99\x9c\x15:\xc7\xf1`\x82\x84\xb3r:\ +nN \x18\xa1:\x06&\xac8\xa5LI4\xf8\x91\ +5\xc6h\x86\x90\x15\xe8(\x1bC$\xe7\xa3\xfc\x01\x0a\ +P\x08d]\xa54\x8aKS$\x94\x9a5?\x08\x94\ +\x09d\x82\x814\xe2`|S\xe2uER$\x95B\ +\x81='\xb5R+\x1eG\xc8!b\x82\x81\x0a\x93\x96\ +\x82\x15\x8d\xa9n\xda\x9c@M\x9cq\xcd'\xe9\xfe~\ +Z\x80\xd5\xa8~\x17\xa8(8\xae\x1f\x08(\x9f\x0b\x86\ +\x85\xd2=]$W\x22=_\x22\x11\xe0\x8e\x82\xd0h\ +%\x8a\xa5\x9c-\xa8\xe7Q\x15\xf2\xea-\x1e\x1bh(\ +H\xb1[\xa8 \xa1p\x17,\xf5x\x9e\x5c\xc8\xed\xd0\ +\x85\xcf\xc1\xad\x02` \xa0R\x98F\x82\xd8\x88\xfa\x0f\ +b\x87\xba=|_KA\xec\xda\x87\xf29\x98\x8b`\ +\xa8\xe6B\x8d\xe0\xe84x\x0f \xa6J\x0a\x0c(\xd2\ +\xf2\x086\xdc\x04\xb2S\x8c \x97\xda\xdat<\xa08\ +l\xf4\x05\xafSG\x0e\xa8\xb9\x1a5\x83\x9bZ(\x1c\ +yi\x06*\x0a\x16)\x83-\xc0N%\xf9\xa0\x01\x9b\ +.F\x90\x17\xab\x87\x81N\xb4y\xa1\xda\x123\xaf#\ +\x14\x9a@\x00\xd2\xa5\x9a\x0a%\xa8\xed\xa9A#\x8c)\ +\x9e\xa5\xaa.\xa5\x94\x8e(6\xba\x02\x0d\xb0\x22\xfb\xc2\ +-I\xd2\xa3\xe2@A\xa9\x87\x02\x0a\x15\xdc\x11be\ +\xb7\xaf\xad\xa8\xf1#\x91(V\xf4\x8a\xf1\xe8\xa4u\x1e\ +\x07\x88.\x18\x82\x00zn\x9e\x9c\xf1\x0b\xfb\xf8\x82\x07\ +\x97\x06T\xa8n\xd2V\x06\x9d\xba:)\xb5\xa3\xe9\x06\ +\x92\x0a\x0f\xa9\x87\x0d\x09p\x1f|\xe3\xe5|\xe6\xacG\ +p\x00\x06\x1c/\x22\x89\xf7\xe8\x93\xa3\x1e\x11\xb0\xaa\xa4\ +E\x5c\x03\xba{\xce\xb0\xed\xa9\x05#\x8f\xfe\x0a#\xe9\ +\x22\x06\x87\xad4M9\xf0\x18\xa6\xd1\x22\x0d\x19\xcb\xa7\ +~c\x12w\x81\x1f(<\x15\xfd\x07\xa4k\xd3\xa7S\ +\xf0\xec\x90q\xaa~,\x82\x01\xfd\xa7\x97\xdb\xe3,s\ +j9TD\x7f\xd6\xe9J\x12\x95\x16\xa9\xb0\xa9\x0d\xd5\ +\xc0\xdc\x09\xe8\xd2\x81@\x88\x90\x00RR\x83\x87\xda\xaf\ + \x89\x08\xa7\x8b4\x8e\x13\x9f\xf9JR\xa3e \x95\ +!\x8a\xb8\x1c\xa9\x82G\x82\xf8\x82\x84\x12\xa46\x128\ +)\x83%\x1dJ\xb8`\x00\xacJa\xb5\x16\x89\x1c&\ +\x984x\xa6W\xf1R\x1e\xe9\x1d\x87\x14g\xa8C\xd4\ +\xa8\xdf \xa0\x80\xa9\x0cu\xc0\x0e\x8c\x1a\x95\x18D\x80\ +\x1e\x95!\xbe\x91\xc1\x14+(\xcaU\x5c\x00\x00\x88T\ +\x86\xf2\xe0\x04p\xd8\xf9A\xc8&T\x85\xd2G]p\ +\xf9\xf6\x13\x95*\x22\x08+\xca)\xe3\xe1\xf2\x80\x80\x1e\ +\x0b#\x80\xfa.\xa8\xf2\x07\x10D\xec\x00!\xe9N\x10\ +\xa9\x1d\x14FH\x00O\xd3\xf0\x1f$\x03t\x82\xb9\x92\ +\x9e\x10\xd7\x04$.\x89\xf8\x1f\xb0\xb2\xa4>\x8d\xa8!\ +TK\x1d\xa0\xc6RltT\xa8\xa9$\x01X\xa6\x90\ +\x00\x0c\x09\x183\x82\x9d@\x10\x88L*\x17\x0c\x86\xc3\ +\xa1\xf1\x08\x8cJ&\x00fE\x910\x93\xb4R7\x1c\ +\x8e\xc2\xd3\xe3I\x09\x89\xff$\x8fI\xa4\xf0\xa9#\xfe\ +Q,\x8f@\x80 \x06\x94\xc8D\xfa\x9a\xb4! \xd9\ +l\xea8\xe3\x04O\x84\x82\xca\x0b\xeawD\xa2\xc3\x1a\ +\x94\x803\xe2\x96\xdb\x84\x87\xa8\xd5\x08c\xc0\x07T\x16\ +\x0cj\xeeYUF\xb7Z\xad\xd4%\xf0\xb6u\x88\xbc\ +\xfe\xb2\xa8k\xd5\xe9y\xa6\x0a3LZ-\xf1\xd6m\ +\xc8\xcf*\xb7\x5c(\xd2\xf2\xc5\xb1S)\x92\xdd\xe7U\ +\xdb\xfc\xb2\xc1\x0e\xb93S\xf2\xa3\x06\x0av\xe2\x02c\ +\x85C\x0c\x8b\xd7\x17\x7fhe\x81o\xcc\xcb^\x12\x1d\ +\xcaI\xe5\xe9\xbba\x9a\x1d\x81\xcfGt\xbah\xde\x12\ +\x1a\xe2\xd6\x82][\x06,$c\xa9\x8e\xcb\xd4\xb6\xc2\ +\xe6\xd6\xbd\x16f)a%\xad\xde\xaa\x04\xcd\x06\xf1\x87\ +bnK\xe7I~\xe1D\xf5\x1c\xe8~\xae \xd3\xea\ +\x87_=\x86D$5\xd1\x8d\x9a\xe4#D\xb7vM\ +\x866J\x92~8\x9b\x96\xa8\x03\x1b\xd5\xc6.X\x97\ +C\xd3}\x95\xfd!\xdd8\x9b?\xf60~\xff\x8c4\ +$\x0c}\xd0\xc3\xfd/\x1d\x16\xc2:\x03C\x1b\xd4\x1d\ +\x08F\x10\x84\xc2\x0aBO0\x0a\x15\x0fC(`\xd1\ +G\x1f8\x0e\x1c}\xdf\x94qb3\x84\x95\x94\xfe,\ +\xd0\x90\x0e\x12B\x92\xf2X\x0a\x8b\x87\x80\xa61<\xdb\ +Sj5\x03\x8f(\xe0\x8aBFx\xa9\x0c?R\xf1\ +1l.\x12xy\xf4\x91^\x98\x81\xe4\x5c\x85\xc4\xa8\ +\xa0\x8a#\xd4,\xe7BG\x94$\xa6x\x0f\xb5\x19H\ +5\x14\xa5-\xc1B\x08\x84$\x17\x94\x10\xa8\xfd\x02\x18\ +\x16\xc2\x91\x80sc\xd9\x1d\xe3\x92R\xc8\x88Y\x89J\ +9>cC\x0e\xf4$\xb0Bd4 \xe2\x01g\xf3\ +\x91\x0a>\xe80qNK\xc4\x94\xa8PBA\x09\xda\ +>\x85@!v\x18\x0c\x8au\x1amwigFo\ +N\xe2!R%\xa5\x10\x80\x12\x8d\xa8\x9ac\xf1\xc0x\ +\x0a\xb5z\x98s\xaa\xa7\x0a\x9aQ\x9b\xda)\x08\xa7\xc0\ +\x00\x22\xa3\xad\x95\x17-\x08\x16^\x02\xbdw\xab\x1b\xba\ +\xfd\xb5\xab\x95\xb6\x183J\x8a\xe5:\xb7\xb2\x91\xe3\x8d\ +/\x14\x96\xc31\x94\xb0Z\x9bM\xa6\xb0\xd6\xf6\xf4\x14\ +K\xca\x94\xa8B\xb2\xed\xf40\xc0\xa3\xc5jH\xebm\ +mVz\xe8e-u\xfd*\x00\xe2\x22!*\x1d.\ +\x0a\xd8\x8f[\x07d\xbe\xa5\xaa\xe6\xb8\xaa\xeab\xee\xc6\ +\xd6\x22\x89\x16RI\x09\x09/GD\xddBF\xf7\x80\ +\xb5\x84\xaf\xe6\x0b\x10_\xf0\x07:Z\x97\x0f\x81\xc9\x09\ +\x1f\x10\x90/\x08V\xd94 \x86\x03\xb225\xc9\x09\ +\xab\x99\xb2\xfc\xc3\xf2\xa8+\x14\x80\xdf\xb3<\x1b\x7f\x8f\ +\xd2-\x09\x16q\xe4\xb0\xa9\x9f\xc0Q\xd0/\xcf\xa8\x1b\ ++\x12\xaf\xb2\xc8\x0f.\x9d\xacT\xa8nBEt$\ +\x07\xbd\x144 \xa9K\xc9+C8B4%\xc3X\ +[\xf4k.\x22\x05R\xa1a\x09\x14\x12\xa0\xf9\x09\xa8\ +\x5c+\xe8\x00\x80\x10\x82\xc1\x8e\x01\x0a\x86D0:\xb5\ +d?ZZ7e\xa5\x02\xdd\x12\x84\xc8\xd2\x04Y\x93\ +\xf08\x89[D 0K\xc1\xd4\xa8\x11\xa2\xd0\x98D\ +\x00\x9e\x10\x83\xc1\x098\xd0\x93>\x8f46\xe3\x1c.\ +\xe6\xb8\xfd\xed\xa7\xd1\x1f}\xe3\x9d\xe8\xba>\x93w\xe7\ +\xfa^\xa3\xa9\xea\xb1\x1e\x9f\xab\xeb\xba\xfe\xc1(\xe8{\ +\x1e\xd3\xb5\xde\xfb>\xdb\xb9\xee\xb4\x1e\xb7\xbb\xef\xbb\xfe\ +\x8b\xb8\xf0\x01E\x01L\x01R\x01Y\x01`\x01g\x01n\x01\ +u\x01|\x01\x83\x01\x8b\x01\x92\x01\x9a\x01\xa1\x01\xa9\x01\ +\xb1\x01\xb9\x01\xc1\x01\xc9\x01\xd1\x01\xd9\x01\xe1\x01\xe9\x01\ +\xf2\x01\xfa\x02\x03\x02\x0c\x02\x14\x02\x1d\x02&\x02/\x02\ +8\x02A\x02K\x02T\x02]\x02g\x02q\x02z\x02\ +\x84\x02\x8e\x02\x98\x02\xa2\x02\xac\x02\xb6\x02\xc1\x02\xcb\x02\ +\xd5\x02\xe0\x02\xeb\x02\xf5\x03\x00\x03\x0b\x03\x16\x03!\x03\ +-\x038\x03C\x03O\x03Z\x03f\x03r\x03~\x03\ +\x8a\x03\x96\x03\xa2\x03\xae\x03\xba\x03\xc7\x03\xd3\x03\xe0\x03\ +\xec\x03\xf9\x04\x06\x04\x13\x04 \x04-\x04;\x04H\x04\ +U\x04c\x04q\x04~\x04\x8c\x04\x9a\x04\xa8\x04\xb6\x04\ +\xc4\x04\xd3\x04\xe1\x04\xf0\x04\xfe\x05\x0d\x05\x1c\x05+\x05\ +:\x05I\x05X\x05g\x05w\x05\x86\x05\x96\x05\xa6\x05\ +\xb5\x05\xc5\x05\xd5\x05\xe5\x05\xf6\x06\x06\x06\x16\x06'\x06\ +7\x06H\x06Y\x06j\x06{\x06\x8c\x06\x9d\x06\xaf\x06\ +\xc0\x06\xd1\x06\xe3\x06\xf5\x07\x07\x07\x19\x07+\x07=\x07\ +O\x07a\x07t\x07\x86\x07\x99\x07\xac\x07\xbf\x07\xd2\x07\ +\xe5\x07\xf8\x08\x0b\x08\x1f\x082\x08F\x08Z\x08n\x08\ +\x82\x08\x96\x08\xaa\x08\xbe\x08\xd2\x08\xe7\x08\xfb\x09\x10\x09\ +%\x09:\x09O\x09d\x09y\x09\x8f\x09\xa4\x09\xba\x09\ +\xcf\x09\xe5\x09\xfb\x0a\x11\x0a'\x0a=\x0aT\x0aj\x0a\ +\x81\x0a\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b\ +9\x0bQ\x0bi\x0b\x80\x0b\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\ +\xf9\x0c\x12\x0c*\x0cC\x0c\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\ +\xc0\x0c\xd9\x0c\xf3\x0d\x0d\x0d&\x0d@\x0dZ\x0dt\x0d\ +\x8e\x0d\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\x13\x0e.\x0eI\x0e\ +d\x0e\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\xee\x0f\x09\x0f%\x0f\ +A\x0f^\x0fz\x0f\x96\x0f\xb3\x0f\xcf\x0f\xec\x10\x09\x10\ +&\x10C\x10a\x10~\x10\x9b\x10\xb9\x10\xd7\x10\xf5\x11\ +\x13\x111\x11O\x11m\x11\x8c\x11\xaa\x11\xc9\x11\xe8\x12\ +\x07\x12&\x12E\x12d\x12\x84\x12\xa3\x12\xc3\x12\xe3\x13\ +\x03\x13#\x13C\x13c\x13\x83\x13\xa4\x13\xc5\x13\xe5\x14\ +\x06\x14'\x14I\x14j\x14\x8b\x14\xad\x14\xce\x14\xf0\x15\ +\x12\x154\x15V\x15x\x15\x9b\x15\xbd\x15\xe0\x16\x03\x16\ +&\x16I\x16l\x16\x8f\x16\xb2\x16\xd6\x16\xfa\x17\x1d\x17\ +A\x17e\x17\x89\x17\xae\x17\xd2\x17\xf7\x18\x1b\x18@\x18\ +e\x18\x8a\x18\xaf\x18\xd5\x18\xfa\x19 \x19E\x19k\x19\ +\x91\x19\xb7\x19\xdd\x1a\x04\x1a*\x1aQ\x1aw\x1a\x9e\x1a\ +\xc5\x1a\xec\x1b\x14\x1b;\x1bc\x1b\x8a\x1b\xb2\x1b\xda\x1c\ +\x02\x1c*\x1cR\x1c{\x1c\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1d\ +G\x1dp\x1d\x99\x1d\xc3\x1d\xec\x1e\x16\x1e@\x1ej\x1e\ +\x94\x1e\xbe\x1e\xe9\x1f\x13\x1f>\x1fi\x1f\x94\x1f\xbf\x1f\ +\xea \x15 A l \x98 \xc4 \xf0!\x1c!\ +H!u!\xa1!\xce!\xfb\x22'\x22U\x22\x82\x22\ +\xaf\x22\xdd#\x0a#8#f#\x94#\xc2#\xf0$\ +\x1f$M$|$\xab$\xda%\x09%8%h%\ +\x97%\xc7%\xf7&'&W&\x87&\xb7&\xe8'\ +\x18'I'z'\xab'\xdc(\x0d(?(q(\ +\xa2(\xd4)\x06)8)k)\x9d)\xd0*\x02*\ +5*h*\x9b*\xcf+\x02+6+i+\x9d+\ +\xd1,\x05,9,n,\xa2,\xd7-\x0c-A-\ +v-\xab-\xe1.\x16.L.\x82.\xb7.\xee/\ +$/Z/\x91/\xc7/\xfe050l0\xa40\ +\xdb1\x121J1\x821\xba1\xf22*2c2\ +\x9b2\xd43\x0d3F3\x7f3\xb83\xf14+4\ +e4\x9e4\xd85\x135M5\x875\xc25\xfd6\ +76r6\xae6\xe97$7`7\x9c7\xd78\ +\x148P8\x8c8\xc89\x059B9\x7f9\xbc9\ +\xf9:6:t:\xb2:\xef;-;k;\xaa;\ +\xe8<' >`>\xa0>\xe0?!?a?\xa2?\ +\xe2@#@d@\xa6@\xe7A)AjA\xacA\ +\xeeB0BrB\xb5B\xf7C:C}C\xc0D\ +\x03DGD\x8aD\xceE\x12EUE\x9aE\xdeF\ +\x22FgF\xabF\xf0G5G{G\xc0H\x05H\ +KH\x91H\xd7I\x1dIcI\xa9I\xf0J7J\ +}J\xc4K\x0cKSK\x9aK\xe2L*LrL\ +\xbaM\x02MJM\x93M\xdcN%NnN\xb7O\ +\x00OIO\x93O\xddP'PqP\xbbQ\x06Q\ +PQ\x9bQ\xe6R1R|R\xc7S\x13S_S\ +\xaaS\xf6TBT\x8fT\xdbU(UuU\xc2V\ +\x0fV\x5cV\xa9V\xf7WDW\x92W\xe0X/X\ +}X\xcbY\x1aYiY\xb8Z\x07ZVZ\xa6Z\ +\xf5[E[\x95[\xe5\x5c5\x5c\x86\x5c\xd6]']\ +x]\xc9^\x1a^l^\xbd_\x0f_a_\xb3`\ +\x05`W`\xaa`\xfcaOa\xa2a\xf5bIb\ +\x9cb\xf0cCc\x97c\xebd@d\x94d\xe9e\ +=e\x92e\xe7f=f\x92f\xe8g=g\x93g\ +\xe9h?h\x96h\xeciCi\x9ai\xf1jHj\ +\x9fj\xf7kOk\xa7k\xfflWl\xafm\x08m\ +`m\xb9n\x12nkn\xc4o\x1eoxo\xd1p\ ++p\x86p\xe0q:q\x95q\xf0rKr\xa6s\ +\x01s]s\xb8t\x14tpt\xccu(u\x85u\ +\xe1v>v\x9bv\xf8wVw\xb3x\x11xnx\ +\xccy*y\x89y\xe7zFz\xa5{\x04{c{\ +\xc2|!|\x81|\xe1}A}\xa1~\x01~b~\ +\xc2\x7f#\x7f\x84\x7f\xe5\x80G\x80\xa8\x81\x0a\x81k\x81\ +\xcd\x820\x82\x92\x82\xf4\x83W\x83\xba\x84\x1d\x84\x80\x84\ +\xe3\x85G\x85\xab\x86\x0e\x86r\x86\xd7\x87;\x87\x9f\x88\ +\x04\x88i\x88\xce\x893\x89\x99\x89\xfe\x8ad\x8a\xca\x8b\ +0\x8b\x96\x8b\xfc\x8cc\x8c\xca\x8d1\x8d\x98\x8d\xff\x8e\ +f\x8e\xce\x8f6\x8f\x9e\x90\x06\x90n\x90\xd6\x91?\x91\ +\xa8\x92\x11\x92z\x92\xe3\x93M\x93\xb6\x94 \x94\x8a\x94\ +\xf4\x95_\x95\xc9\x964\x96\x9f\x97\x0a\x97u\x97\xe0\x98\ +L\x98\xb8\x99$\x99\x90\x99\xfc\x9ah\x9a\xd5\x9bB\x9b\ +\xaf\x9c\x1c\x9c\x89\x9c\xf7\x9dd\x9d\xd2\x9e@\x9e\xae\x9f\ +\x1d\x9f\x8b\x9f\xfa\xa0i\xa0\xd8\xa1G\xa1\xb6\xa2&\xa2\ +\x96\xa3\x06\xa3v\xa3\xe6\xa4V\xa4\xc7\xa58\xa5\xa9\xa6\ +\x1a\xa6\x8b\xa6\xfd\xa7n\xa7\xe0\xa8R\xa8\xc4\xa97\xa9\ +\xa9\xaa\x1c\xaa\x8f\xab\x02\xabu\xab\xe9\xac\x5c\xac\xd0\xad\ +D\xad\xb8\xae-\xae\xa1\xaf\x16\xaf\x8b\xb0\x00\xb0u\xb0\ +\xea\xb1`\xb1\xd6\xb2K\xb2\xc2\xb38\xb3\xae\xb4%\xb4\ +\x9c\xb5\x13\xb5\x8a\xb6\x01\xb6y\xb6\xf0\xb7h\xb7\xe0\xb8\ +Y\xb8\xd1\xb9J\xb9\xc2\xba;\xba\xb5\xbb.\xbb\xa7\xbc\ +!\xbc\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\x84\xbe\xff\xbfz\xbf\ +\xf5\xc0p\xc0\xec\xc1g\xc1\xe3\xc2_\xc2\xdb\xc3X\xc3\ +\xd4\xc4Q\xc4\xce\xc5K\xc5\xc8\xc6F\xc6\xc3\xc7A\xc7\ +\xbf\xc8=\xc8\xbc\xc9:\xc9\xb9\xca8\xca\xb7\xcb6\xcb\ +\xb6\xcc5\xcc\xb5\xcd5\xcd\xb5\xce6\xce\xb6\xcf7\xcf\ +\xb8\xd09\xd0\xba\xd1<\xd1\xbe\xd2?\xd2\xc1\xd3D\xd3\ +\xc6\xd4I\xd4\xcb\xd5N\xd5\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\ +\xe0\xd8d\xd8\xe8\xd9l\xd9\xf1\xdav\xda\xfb\xdb\x80\xdc\ +\x05\xdc\x8a\xdd\x10\xdd\x96\xde\x1c\xde\xa2\xdf)\xdf\xaf\xe0\ +6\xe0\xbd\xe1D\xe1\xcc\xe2S\xe2\xdb\xe3c\xe3\xeb\xe4\ +s\xe4\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\x1f\xe7\xa9\xe82\xe8\ +\xbc\xe9F\xe9\xd0\xea[\xea\xe5\xebp\xeb\xfb\xec\x86\xed\ +\x11\xed\x9c\xee(\xee\xb4\xef@\xef\xcc\xf0X\xf0\xe5\xf1\ +r\xf1\xff\xf2\x8c\xf3\x19\xf3\xa7\xf44\xf4\xc2\xf5P\xf5\ +\xde\xf6m\xf6\xfb\xf7\x8a\xf8\x19\xf8\xa8\xf98\xf9\xc7\xfa\ +W\xfa\xe7\xfbw\xfc\x07\xfc\x98\xfd)\xfd\xba\xfeK\xfe\ +\xdc\xffm\xff\xff\ +\x00\x00\x03\xaa\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Default - Sav\ +ed\x0d\x0a \ +Created wi\ +th Sketch.\x0d\x0a \x0d\x0a \x0d\x0a \ + \x0d\x0a \x0d\ +\x0a\x0d\x0a\ +\x00\x00\x04q\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Editor only -\ + Default\ +\x0d\x0a Crea\ +ted with Sketch.\ +\x0d\x0a \x0d\x0a \ +\x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\x0d\x0a\ +\ +\x00\x00\x02\x9b\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + <\ +/circle>\x0d\x0a \ + \x0d\x0a \ +\x0d\x0a\x0d\x0a\ +\x00\x00#k\ +\x00\ +\x00\xf0\x80x\x9c\xed\x5c\x07\x5c\x93\xc7\xdf\xbf'\x09\x09\ +[\xb62\xc4\x88\x03\x17#\xcc\x10\x01\xd9CE\x11\x10\ +\xd4\xbaB\xf2\x00\x91,\x930\xc4=\xaaV\xad\x03\xad\ +\x0b\xb7\xd6=\xea^\xad\xd6\x89V\xabu\xb7\xd5\xb6\x8e\ +?\xae*\xe2`\xa3y\xef\x9e$\x10\x14\x15$\x8a\xf6\ +}\xbe\xf9\xdc\x93\xbb\xdf\xdd\xfd\xd6\xddsw\xcf\xb8'\ +&\x06t\x02\x00\xe8\x83\x16\xe0\x15\xa0\xc1\x18\x06T\x07\ +*\xf17\x14\x1e0\xad8\x85\x88\xc3r\x98+FU\ +\xd3aa\x8c\xae\x8eS\xe0\xc1\x5c\xc3'q!f\xa1\ +U\xc6Z\x1dG\xb5m\xb4x6\xd7\x94\x9f\x0d0;\ +\xa0\xa7\x8ac>\x98}u<\x10s\xd0\xe2\xd3AK\ +\x16\x0b\x1d\x81\x13\x8cE`\x1eD\xdc\x0a\xc6\xd3\xb0\xb8\ +\x9a\xf2\x94\x1f\xd1\x91\xb3\x16&GM<\x08\x18\x90\xde\ +\x0c\x00\x7f\xa7\xc9\xd1\x88n\xea\x08\xe3\x1eS\x04\x1a\x1d\ +\xee.\x9c\x22Ge\x8c\xa3\x00\xe8\x1e;w \x8a\x1b\ +\xec\x83\xf4\xe3\xc8/\xd0O\xaa\x9fa\xd58\x00\x9c\xcd\ +4\xff\xc1|I2\xce\x8cM\x93($\xf24\x89\x94\ +\x19\x1a\xca\xf4pg\xf92;$\x09\xc4|I\x96\xbc\ +#@I\x8e\xbb\x07\xc7\xdd\x93\xc9\xf2\xe1x\xf8p\xbc\ +X\xc0\xbf[\xb6\x94\xcbK\xc7\x15\xccda\xb8\x9c'\x13H\x15\x02\x89\x98\x89\ +\xd2\xdcdI\x86\x22\xc0\xc9\xc9\x90\xa9\x05\xb5]\x22i\ +\xb5 \xb1\xdc\x95\xb0\xd1\x95'\x11\xb9es\xa5n,\ +Ww\xb7\xba*\xf1y\xd5u\xa4\x192!\xa1\x1a\x9f\ +\xe7\x86\x0bq\x11.V\xc8a=V\x9d\xf5\xa4\x9a\xb6\ +\xab[du\xf6[\x05Cmcb\xde\xad\xafHT\ +gM\xb9\x22\x9f\xef\xc3\xe6\xe1P\x0b\x1cOv\ +\xf1\xf3\xf3rwa\xb3y\x9e.\xbe\xc9\x1e\xde\xc9\xc9\ +\x1e8\xd7\xdd\xc3\x8bh\xcb\xda\xd5\xdf`\xad\x91\xaef\ +M\x98\xca\xf3\xf4\xc0}\xfd\xd8.)\xfc\x14\x96\x8b\xb7\ +\x9f\x17\xdf\xc5\xcf;\xc5\xcf\x85\xeb\xe5\xc3\xf2e\xf9&\ +\xfbp\xb9\xbe\x1a\xd6Z\xd5\xdf`\xdd[&\x80\xa31\ +W\xd8H\x11u\xb0yCT\x94\x00ynD\x1dm\ +\x1b\x8f\x0f\x7f[\xdb\x12\xe3\xa6\x94+\x93\xe3hT\x08\ +p\xd2\x0c\x0bNoT@u\x88\xd1\x85\xc3\xe5\xa1\x11\ +7\x90G\x9c\xf8\xb0\xc1kQ\xdf^M\xf0f\x03\xd6\ +\xcf\x05oT\x7f\xbb\x8c\xac4\x5c\xfc\xae\x01K\xab\xd4\ +\xdb\x99\xc8%)\x8a,\xae\x0c\x0fN\x85\x9e\xae\xcfh\ +QW\xb57\xfc]\xe7y\xa1\x93\x86\x90s3\x1b\xd7\ +\x0c\x9e)\xbe>\xc98\xcb\xc7\x85\xcd\xe7y\xb8p\xd9\ +^\xb0\x05\x90\x0f}\xbdY\x9e\x9e\xbe\x1e\x5c/\x16\x8b\ +\xdf\xb8f\x80m\xc0\xd2\x1a\xad\x9b\xae\x19j\xd8\xf3\xd2\ +\xb8\xe2T\x9c\x1f\xe8\xa6\xa9\xa8!|I-W\xbf\x11\ +\xb0\x91-\xe7\xf3\xdfj9\x15\xb5\xf6\x98\xa8\x19g_\ +\x1bCUE\xb5\x96\x99\xaa5\xac\x9bz\x11\x0b\xd7\xcf\ +n\xd5\x0b\xe8\xba\x14\xd6=H!\xa4\x10R\x08)\x84\ +\x14B\x0a!\x85\x90BH!\xa4\x10R\x08)\x84\x14\ +B\x0a!\x85\x90BH!\xa4\x10R\x08)\x84\x14B\ +\x0a!\x85\x90BH!\xa4\x10R\x08)\x84\x14B\x0a\ +!\x85\x90BH!\xa4\x10R\x08)\x84\x14B\x0a!\ +\x85\x90BH!\xa4\x10R\x08)\x84\x14B\x0a\xd1\xb1\ +\x10\xc3\x9a-\xa9\xb8\x98\x1f\xe0\x94\xe5\xd4-\xd0\x01\x1b\ +\x00\xa8\xf6\xed\x22\x1d(\x80\xd8`\xcb\x0e\x89\x8e\xa1\xb5\ +#v\xc3\x9a\xfdz\xbeb\xf4\xfa\xfcC\x06z\xd2\xcc\ +\xad\xdb\xf4\x8e\xf5\x22\xf28D^\x01\xca\xd7\xec'\x06\ +FR\x99@\xac\xe8\x9d\xa1\x90f(`\x12m\xec\x05\ +\xb1rE|\xb2D\x22$JD\x8b\x158.\xce\x10\ +i\xe2\xe8?T(C\xe9fD\xddxA6*\x11\ +\x22P\xa0:5\x8f\xf0\x85\ +\xca6\x8a:E\xa9\x95rW\xe9\xa8\xf6\xb5\x0b\x91\xa2\ +\x13\xfe\xb0%(\xfa\xea^A\xb4\x0d%H\x15\xd4u\ +\x1dk\xb7\x0c\xcd\x0e\xc5\xa8\xde\xd5\x96\xab0T\x1dT\ +\xdey\x13\xda4\xed\xb2u\x16\xd0\xa0fp\x03\xe2\x0c\ +\xa1Pe\x10\xa0'K2\xc4|\xf9kc\x0bO\xc1\ +\xd2\xa8\x89NH\xad\x93\x01\xbcv\xd6\x80\x90\x9a\xb3\x8b\ +P#\xae\xe6\xe4Ai\xba\x5c(\xe0\xe1\xf2DaO\ +t\xfac\xb5\xe4\xe8\x11y0b\x01\x03\x83HD\x87\ +i\xf1f\xa4\xca$\x19\xd2Z$\xba\x84\xd8\x97\xab\x19\ +\xd9\xc3\xe3Q%\xd5^]\x986\xe1f($\x91\xb8\ +\x18\x97\xa1}\xb2\x84\xf6#\xa4\x9a\x89\xc9PU\x18Q\ +PN\xb4(\x95\xf9\x09\xec\xa7f\xc8\x84\xb5\xa67\xc2\ +\xf9\xb5)1\xf2\xd4\xdaS \x9d+T$pSk\ +\xd1Ly8\xac\x87g+\xa2\xe5Q\x091=5\x83\ +\xac\xbe\x86\x5c\xab\xb0A\x9aD\x96\x13,\x14\xa4j<\ +\xd5Le|\x94\x86\x8c\xbc\xcb\xc7S\xb8\x19\xc4(k\ +\x90\x89\xcb\x14u\x14O\xd4\x90k\x177JN%\xf6\ +\xd1k9\xd7\x5cU!$\xb2:\x03\xa9\xd1K\x22F\ +\xff\x06\x0a\x89\x14N\xa5r\x5c\xdbq\x86B\xe8\xc87\ +\xa8\xc6\xc9\xc4p\xfd\x06\xddH\x86\x06\xe5\xd7\xc8\xc4\x19\ +\xd4AU\x0f\x06J\xb7\x22PC\xb7\x22\xa2\xa8\x09U\ +g\xa51\x91\xdc\xa9v\xd1PU\xc0`\x17@g\x02\ +m\x01h\x010\xe5\xef\xca\xc7\xc0\x98\xd8=9$4\ +\x06\xa6\x0b\x81)\x91\x02\xfcq\xa8\x9e\xf2:\x98\x04\x8c\ +\xf5\xf5\xf5\x0d\xf4\x8d\x0d\x0c\x8c\xcd\x8d\x0c\x8d\xcc\xad\x9b\ +\x19\x1b7\xb3naiimi\xd9\xc2\xdc\x98\x80\xfa\ +\xafn`&FF&\xa6&f\xa6\xa6fV\xa6\xa6\ +\xa6V\xe8`j\xa5\xaab^\x1f\x06\xca\x9f\x81\xb9>\ +T~(\x15s\x02\x14s\x8cj\x8e)\xff\x82\x86\xd2\ +\x95\xc7\xb1nPK=\x8c\x80\xdaqT\x80Qhz\ +t\x86\xbe\x81\xa1\x11\xf6z&\x06(TM\xa6\x19\xc0\ +h\x18\x95B\xa3\xe81\xe8\xfazTcO\x98iN\ +\xa5\xb5\xb6`\xe9\x05\xf7\xe1Z:\x0d\x1f\xefA\xb7\x9a\ +\xb3r{H\x9b\xb6\xd6qG\x92=\xbdd\x13.\x84\ +2\xda\xe5\xc6\x17\xddz\xca\x93{\xdb\xac\xda1\xb1}\ +\xd8\xdc\x04~\xf8\xd1\xd5\x0a\x9f\xe6\x17\xfb\xde\xc6\x9f\xed\ +\x9ct\xecR\xc6\x9d\xe7\x11\xce\xf3\xd6|\xbd\xeb\xbb\xe3\ +\x97\xff\xf7\xe2\xfb\xdd'\xae\x14\x14'\xa6dN\x9e\xbf\ +v\xcf\xc9\xabwK|#\x93R\xb3\xa6,X\xb77\ +\xff\xda\xbdRs@\xa1@mi\x84N\x0c\xba\x9e7\ +\xa1Bk\x96\x05\x0dj0\xdc\xc9R\xcfc\xfc\x1c+\ +\xa4\xc1\x91\xb8\x0bE\x9em\x93o\xc9&\xe4\x86\xc6[\ +\xf3\xe4^O\xdb\xd1\x91\x02\x8c\xf6\xdeG/B%V\ +7\xe7\x87\xf7\xf5Q\xe0\xb7\xabUx\xbb\x06\xce5*\ +(\xff\x04\xc6TB\xa69\xe8\x06\x8a\x13r\xa3\xdbu\ +l\x9b\x1b\xdd=\xbamn\x5cnt\xdbyk\xd4\x84\ +\xde\xca\xdf\x8a\xfb\x1a\x89\xf0\xbey\x8e\x0a\xc3a6\x89\ +g\xfa\xf5{\xba\x7f\xdd\x8e\xeei\x17\xae\xb4?\xeaj\ +vq\xf4\x9a\xcc\xb2- \xd8\xc2x\xe2\x91~\xe5\xd9\ +n+\x0a%\x07\x0e;\xde\xa98\xbd}\xa7(\xbf\xe4\ +\xa5\xcf\x98\x07\x8f\x8b\xa2{\x9df\x1aph\xcdSn\ +\xffp\xe4\x87Q\xbb\x95`C\x7f\xdf\x82A#\x16\x1c\ +\xec\xf5\xabC\xeb\xb6\xebD\xfb\xf7ms\xef\xd2*\xa0\ +\xdb\xc39\xe3iQ\x97]G\xd8\xcc\xd8\xb4\xde#_\ +\xf0\xd7\xbaI\xf3\x0a\xd7l\xba}7i\xb6W\xc9\x8f\ +\xb2\x7f\x0a\x13-\x08\xcd\x94@y\x81\xd0\xd7\xfb\xb9G\ +^\xf8\xf0\xd5\xcb\x1e\xdb]\xaaJ\xecx\xc5\xd2\xe9\xde\ + \xf9\xb2\x91K\xfd\xa7\xf0\xbd\xe6}\xbfn\xe2`\xeb\ +\x19\x17V^m\xb6\xa6\xd5\x86\xbeO\x8bF\x8f\x92\xf3\ +z\xfa\xb3\xc75\xa3\x85l\xb3\xb9\x90\x9a\x19\xb0\xa1\xa3\ +\xebH\xbb\xb3\xebv\x8f\xf3^\xe5egJ\xd9\xfa\xed\ +\x933\x07z,\xbe\xb6\x0f\xdb:\xee\xe7\xaf;\x17\x89\ +W\xe6\xfd= \xec\xe4\x94\x97\xdft\xa4:\xdee\xfb\ +\xadu`wNm\xd6q\xbf\xf5\xbf\xdf\x7f[\x11Z\ +\xb69\xa1\xe7\xc8\xa51\xbff?\xcax\xde\xf2b\xfc\ +\x8fP\xadu\xca\x8b\x84R\xec\x11\x0a\xce\xf4%\x97^\ +\xf4\xda\x12\x18\xd5Y\xeel\xbb\xc8\xfaq\xe6\xccV\xe1\ +\x7f\xeb%_\x8f\x7f8\x82w\xdc,w\xa9\xe9\xdc\xa1\ +]F\x0f:r#\x90\x11\x9e\xeel\xf3\xe3\xe4c\xbb\ +\xa4\x15\xae\x0e_O\xef;k\xc3\xadG\xee7w=\ +|8o\xdf\x98\xd96]\x0fKs\x97\xfc\xfb\xf4q\ +\xfe\x98k\xdfveK\x13\x96/\x10\xcd3\x1cR8\ +\xb1\xf5_/\x1em\xfd_\xb1q\xd2\xd5~\xf6\x0f\xcf\ +\xcf\x9c\xa8\x04mwZ\xc4\xcd\xd9\xde\xc1\xf7\xc6\xd5Y\ +\xab\xbc\x87?\x0b\xfe\x8a\xed}\xf1\x17l}\xe1\xf25\ +\x85\xcfr\xf3`+v\x1f\xab\xbc\xa4jb\xc3\xaa\x9c\ +')\xd7fM\x9c\xd2\xda\xba\xed\xbc;C\xc6\x8f\x8e\ +\x18\x10\x10\x91\xbd\xb7lw\xe2 \x8b\xe9\x9b\xb6(A\ +\xf4\xb7Y\xc7)\xb9\xcbF\xf7_M\xa9\xec\xb9/G\ +\xf84\xabh\xd85Q\xc6\xce\xfb\xdb\xb7\x94\x01\xab\xdc\ +\xf8Es/\x19\xd9\x1f\x980\x88z\xe3\xd2\xf9\x13\x1b\ +\xbfo~=\xb4\xd4=\x7fzo\xc6\xed\xbd\xdf\xf5\xdc\ +L\x1f\x9c\xb5\xf3\xf7W\xf7\xb6GG\x0f\xdf\xd1\x7f\xf6\ +\xaf\xbf\x9aE\xccdl\x8f\x88]\xbb6\xbe\xdf\x96\x89\ +\xc1e\xe5\x1b\xd6\xacS^~W_\xfbC5\x9e\xb5\ +&\xce\xd8A\x80Xy\xa0\x8f \x05\x03>\xbc\x18J\ +\x86\x0bC&\xbc4J\x83q\x05\x0cr\x22\x86.\x83\ +\x9a\xbf\xa7\x04\x13\x84\xc2\x1f\x13.\xc7\xdc\x01\x0b\xf8\xaa\ +\x07F\xe3\xa8\x9e\x02\xb1\x84\x02\x17\x0b\x22x\xfd\x81\xbe\ +\x81\xd2\xaf\xff\x00&\xe3\x1c\x5c,\x18\xc05\x0a\x9c\x16\ +\xb9<\xb94&>\x22\x81\x98D\xc3C\x99\xe8C)\ +\xa0\x16J\xae\xaa\xa6\xa1K.Q\xb1L&h\x18\xcc\ +yR\x19\x9cd\xb0X\x18\xf7\xe4\xe3r8-c\x93\ +`\x5c\x98\xa5\x90\x22:\x1a\xe3\xad\x92\xd3Q\x9c\x82F\ +w+\x19T\x10\xc6[\xa0x\xaa*\xde\x85(\xa3\x8a\ +\x07\xa18_$\x86\xcb\x01\x0a\xd2Y\xca\x17\xf1Q\x1c\ +}\x11jjf\x06Z&P{\xc2\xf8\xe4L\x01\x9e\ +\x05\xe3\x97a\xbc\xad0C$\x80q\xb4\x02\xb5\x12\xe1\ +\x5c\xb8t!\xe6\x8f\xb6\x0a\x9c\x97\x06\xe3h\x05h,\ +K\x88\x83W24\x7f8\x07\x1b\xa7j\xc5\x93\xb5\xe2\ +\x0a81#\xa3B%\xd2\x11\xc4\x0c\xc6\xec\xc0\xeb\xc8\ +d\xf9\xf9\xb1\x99Qx\x96\x10W(\x5cb\xe1\x05;\ +W\xc6g\x86JDR\xae\x18\xae\xefU6\x13\xb0x\ +\xe3#4Z\x8ezgf=\x81\xdaV\x15{\xde\x87\ +h3\xcc\xe6l\x0d\xad\xaer\x92Up~\x85\xab}\ +\xea\xec\x1aZ\xf2\x22\x00\xf6~\x0d@\x8b?khm\ +W\xc0>\x0a\xdbm\xcfy-{lP\x7f\xd1\xfa\xf8\ +\x94\x00\xe7\xb9\x22\x87V\xe3\xbd\x05\xea\x01-y\xae\x88\ +]\xb5{\x98a\xaa\x15\x0c\x13\xf9\x8d\x07\xd7)\x192\ +&\xbc\xfe\xe2\xe1L\x97\xd7;\xf1\x07W\xac[\x8f.\ +qx\x0a\x8e\xae\xf3pf\x22\xece\xf02\x156\xb7\ +\x98/ \xbe\xa3%\x10\xbf\xad\x11?\xb0\xdakP\xf5\ +k\x08\xcb5\xaf\x80\xd5\x10W`v\xde\x0aP\x1f\x9f\ +\x054K#@\x1d\xb8\x0c\xe6`\xd5\xed\xd6\xd3 \x11\ +\xa03/\xa9\xd5=U\xbf'P\xc7\xe5\x04e\x16:\ +\xc8\x05\xc4\x22\x1a\x84\xc6%0y\x19\xb2LU\x1e\xb1\ +n\xd6\x83\xd7\x88\xcd\x80\x15\xb0\x85\xd7\xb3m@\x07x\ +u\xe7\x01\x07\x99\xae \x08\x84\x83\xee\xa07\xbc\xbe\xed\ +\x0f\x06\xc3+\xda4 \x82W\xb7Y`\x14\x18\x0f&\ +\x83\xe9`6\xf8\x0e,\x06\xcb\xc1\x1a\xb0\x01l\x05;\ +\xc0^\xf0#8\x02N\x823\xe07p\x05\x5c\x077\ +A\x01\xf8\x17\x14\x81\x12P\x09\x172\x0c\xcc\x04\xb3\xc4\ +l\xb1VX;\xac3\xe6\x81\xb1\xb1@,\x1c\xeb\x89\ +\xc5a\xfd\xb1\xa1X*&\xc62\xb0Q\xd8Dl:\ +\x96\x8b-\xc6Vb\x1b\xb0\xed\xd8~\xec\x08v\x1a\xbb\ +\x80\xfd\x81\xdd\xc2\x1e`\xcf\xb0\x0a\x0a\x95bL\xb1\xa2\ +\xb4\xa4\xb4\xa7\xb8Q\xd8\x94`J\x0fJ\x02e\x10%\ +\x952\x9c\x92C\x99D\x99IYHYE\xd9L\xd9\ +C9B9C\xb9B\xb9I\xf9\x97RL\x05T#\ +\xaa\x0d\xb55\xd5\x85\xca\xa6\x86R{S\x07PS\xa8\ +2\xea\x18\xea4\xea|\xea*\xeaV\xea\x01j>\xf5\ +\x12\xf5&\xf5\x11\xb5\x9cF\xa7Y\xd2\x984\x17ZW\ +Z\x14\xad/\x8dG\x1bN\x1bC\x9bA[L[O\ +\xdbC;N\xbbD\xbbE+\xa2\xbd\xd23\xd1s\xd0\ +\xeb\xac\xc7\xd1\x8b\xd6\xeb\xa7\x97\xaa\x97\xa57Yo\xbe\ +\xdeZ\xbd\xddz'\xf4\xae\xe8\x15\xe8\x95\xd0\xe9t\x1b\ +\xba3\xdd\x97\x1eE\xefO\x1fF\x1fI\x9fA_J\ +\xdfF\xff\x99~\x81~\x87^\xcc`0l\x19\x9d\x19\ +\x01\x8c\xde\x0c.C\xc1\x98\xccX\xc4\xd8\xcc8\xcc\xb8\ +\xc8(`\x94\xe9\x1b\xe9\xb7\xd2\xf7\xd0\x8f\xd0\x1f\xa0/\ +\xd6\x9f\xa0?_\x7f\xa3\xfe!\xfd\x8b\xfa\xf7\xf4+\x0d\ +\xcc\x0c\xda\x19p\x0cz\x1b\xf0\x0dF\x18\xcc2Xc\ +p\xc0\xe0\xbcA\x81A\xa5\xa1\xb9\xa1\xb3a\x80a\x82\ +\xe10\xc3\xf1\x86\x0b\x0d\xb7\x1a\x9e0\xbca\xf8\xdc\xc8\ +\xc8\xc8\xc9\xc8\xcf\xa8\x8f\x91\xc0h\x9c\xd1B\xa3\x1f\x8c\ +N\x19\xdd2*7\xb60\xeed\x1cj<\xd08\xc3\ +x\xa6\xf1:\xe3\x9f\x8d\xff0~nbb\xd2\xde$\ +\xc8d\x80\x89\xc2d\xa6\xc9\x06\x93c&\x7f\x9b\x94\x99\ +Z\x9a\xba\x9aF\x9b\xf2M\xc7\x9a\xe6\x99\xee1\xbdh\ +Z\xd8\xcc\xa0Y\xbbf\xc1\xcd\x067\xcbi6\xbf\xd9\ +\xcef\xe7\x9b=230ko\x16j\xc65\x1bc\ +\x96g\xb6\xdf\xec\x9aY\xb1\xb9\xa59\xcb\xbc\xb7\xb9\xc8\ +|\x86\xf9F\xf3\xd3\xe6\xf7-\x18\x16\xed-\xc2-\xf8\ +\x16\x93,V[\x1c\xb3\xb8cI\xb5lc\x19j\xc9\ +\xb3\x9ch\xb9\xc6\xf2\x84e\x81\x15\xdd\xca\xd9*\xdaj\ +\x98\xd5t\xab-V\xe7\xac\x8a\xac-\xac\xbd\xac\x13\xad\ +\xb3\xad\xf3\xac\x7f\xb2\xbeiC\xb5io\x13m#\xb4\ +\x99e\xb3\xc3\xe6\xaaME\xf3\x96\xcd\x83\x9b\xe3\xcd\xbf\ +i\xbe\xb5\xf9\xc5\xe6\xa5-\xec[\x04\xb5\xc0[Lk\ +\xb1\xad\xc5\x95\x16\x15\xb6L\xdbp\xdbt\xdb9\xb6{\ +m\xff\xb2\xa3\xd9u\xb2\xebc\x97e\xb7\xcc\xee\x84\xdd\ +#{+\xfb\xae\xf6<\xfbi\xf6;\xec\xfft\xa08\ +tr\x88s\x18\xe9\xb0\xda\xe1\xacCqK\xc7\x96\x91\ +-\xa5-\x17\xb5<\xd6\xf2\x91\xa3\x8dc\x90\xe30\xc7\ +y\x8e\x87\x1c\x1f\xb4\xb2l\x15\xd8J\xd0j^\xab\xc3\ +\xad\x1e2\xad\x99\xc1L!s!\xf38\xb3\xa8\xb5C\ +\xeb\xa8\xd6\x19\xadW\xb6>\xd7\xba\xd2\xc9\xd9\xa9\xaf\xd3\ +\x04\xa7mN\x7f\xb51l\xc3n\x93\xd2f^\x9b\xa3\ +m\x8a\xda\xb6j\x1b\xd3vT\xdbMm\xfflg\xd0\ +\x8e\xdd.\xad\xdd\x82v\xf9\xedJ\xdb;\xb7Oj?\ +\xa5\xfd\xde\xf6\xf7\x9d[8G;\xe78or\xbe\xd1\ +\xc1\xa4C\xb7\x0e\xc3;\xac\xeap\xb9#\xbd#\xbbc\ +z\xc7\xa5\x1d\x7f\xebD\xe9\xe4\xdd)\xadS^\xa7\xf3\ +\x9d)\x9d}:\x0b:/\xed|\xa1\x8b^\x17\xbf.\ +\xe2.\xab\xba\x5cs1v\x09v\xc9t\xd9\xe4r\xcb\ +\xd5\xc6\xb5\xa7\xeb\x04\xd7\xbd\xae\x85nm\xdd\x06\xb8\xcd\ +q\xcbw{\xe5\xee\xed.t_\xe3~\x9de\xc1\xea\ +\xce\x9a\xc0:\xc0z\xe6\xd1\xc9\x83\xe7\x91\xe7q\xd9\xd3\ +\xc43\xc2s\xac\xe7>\xcf\xa7^\x9d\xbdp\xafe^\ +\xbf{[z\xc7xO\xf1>\xea\xfd\xd2\xc7\xd7G\xe6\ +\xb3\xd5\xe7\x81o[\xdf\xa1\xbeK|\xaf\xb1\xad\xd8\xb1\ +\xec\x19\xecS~z~!~c\xfd~\xf4+\xe7\xf8\ +p\x14\x9c\x1d\x9c']]\xba\xa6w\xdd\xd8\xf5\xbe\xbf\ +\xb3?\xee\xbf\xc6\xffN\x80S\x007`e\xc0\xcd@\ +f\xe0\xd0\xc0\x15\x817\xbb\xb5\xee\xc6\xed\xb6\xaa\xdb\xed\ +\xa06A\xfc\xa0\xb5A\xf7\x82;\x06\x0f\x0b\xde\x1c\x5c\ +\x18\xe2\x1e\x22\x0b\xd9\x1dR\x1a\xca\x09\x1d\x1d\xfas\x18\ +5,2lZ\xd8\xb9p\x8b\xf0\xbe\xe1\x8b\xc3\xff\x8e\ +p\x8aH\x8d\xd8\x14Q\x14\xe9\x1d92\xf2\xe7(\xbd\ +\xa8\x1eQs\xa2\xaeE\xb7\x8c\xe6Eo\x88.\xea\xee\ +\xdb}t\xf7\xe3=\x8c{\xc4\xf7X\xdc\xe3v\xcfN\ +=e=\x0f\xc4Pb\xba\xc7\xcc\x8d\xb9\xd1\xab]/\ +q\xaf\xbd\xbdA\xef\xe8\xdes{\xff\x15\xeb\x1c;<\ +\xf6`\x1fz\x9f\xd8>y}\xee\xc6\xb1\xe2F\xc5\xe5\ +\xc7[\xc6\x0f\x89\xdf\x18_\x92\x10\x920+\xe1z\xdf\ +\x0e}3\xfa\x1eMl\x9680qCbiRX\ +Rn\xd2\xcd~n\xfdF\xf7;\xd3\xdf\xae\xbf\xa0\xff\ +\xbe\x01\x8c\x01\x89\x03\xd6\x0e(\xfe*\xfc\xab\xef\xbe*\ +\x18\xe8=p\xf2\xc0\xab\x83\x9c\x07e\x0f:=\xd8n\ +\xb0p\xf0OC\x9a\x0d\xe1\x0e\xd99Toh\xd2\xd0\ +\x8dC\xab\xb8\xbd\xb9\xab\xb8\xc5\xc9\xd1\xc9K\x92\x8bx\ +\xa1\xbc\x05\xbc\x7f\xf9A\xfcy\xfc\x07x\x00\x9e\x8b\xdf\ +K\x09H\xc9M\xb9\x9f\x1a\x90:7\xf5AZ\xb7\xb4\ +\xf9i\x8f\x04\xa1\x82\xc5\x82\xa7\xc3\xa2\x86-\x1fV\x9a\ +\xde;}]\xbaR\x98$\xdc&\xd2\x17\x0d\x15\xed\x17\ +[\x88\xd3\xc5\xc7%\x8e\x92l\xc9\x05ig\xe9d\xe9\ +\xcd\xe1\x9c\xe1\xdf\x0d/\x92\xf5\x90\xad\x95c\xf2A\xf2\ +}\x0a+\xb8\x98:\x9b\xd1!\xe3\xeb\x8c[\x99\x81\x99\ +y\x99eY\x89Y;\xb3\xcd\xb3\xc5\xd9gGt\x1a\ +\xf1\xcd\x88{9\x119\xdf\x8f\xa4\x8d\xe4\x8d<:\xaa\ +\xf5\xa8\xf1\xa3n\x8d\x0e\x1e\xbdr\x0c6&y\xcc\xd1\ +\xb1m\xc6N\x1a[0.r\xdc\xfa\xf1\x86\xe3\xd3\xc7\ +\xff:\xc1}B\xee\x84\x17\x13\x93&\x1e\x98\xd4r\xd2\ +\xb8Iw\xbe\x8e\xfcz\xd3d\xd3\xc9\xb2\xc9\xd7\xa6t\ +\x9d\xb2|*m\xaa`\xea\xb9o<\xbfY\xf4\xcd\xab\ +i\xfci\xbfLw\x9f>\x7fz\xd5\x0c\xde\x8c_\xbe\ +e}\xbb\xf0[\xe5\xcc\x94\x99\xe7f\xf9\xccZ6\x9b\ +>[<\xfb\xea\x9cns\xd6\xe7\x9a\xe7\xe6\xe4\xde\x99\ +\x1b3w\xcf<\xe6\xbci\xf3^|7\xe4\xbb\xd3\xf3\ +\xbd\xe6/_`\xb8 c\xc1\xcd\x85=\x17\xee[\xd4\ +v\xd1\xecEU\x8b\xd3\x16_\xc9\x0b\xc9\xdb\xb6\xc4a\ +\xc97KJ\x97\xf2\x97^\x5c\x16\xb4l\xeb\xf2\x96\xcb\ +\xa7/\xafX!X\xf1\xfb\xca\xc8\x95{V\xb5_5\ +\x7f5}u\xe6\xea\xbbk\x12\xd7\xe4\x7f\xcf\xfe~\xc3\ +Z\xbb\xb5\xd3\xd7\xbe\x5c'^ws}\xdc\xfa\xe3\x1b\ +|7l\xd8\xe8\xb0q\xd6&\xca\xa6\x8cM\x0f6\x0f\ +\xdc\xfc\xdb\x96\xb0-\xfb\xb6\xbal]\xb9\xcdf\xdb\xf4\ +\x1f\xc0\x0f\x19?<\xdc>t\xfb\xd5\x1d=v\x1c\xdd\ +\xc9\xde\xb9uW\xbb]Kv[\xee\x9e\xb6\x07\xdb3\ +bO\xd1\xde\xb4\xbd7\xf7\xf5\xdfwa\x7f\xf7\xfdG\ +\x0ft=\xb0\xfb\xa0\xeb\xc1u?\xb6\xfe1\xef'\xeb\ +\x9ff\x1d2<4\xe9\x90\xf2p\xce\xe1\xe2\x9f\xa5?\ +?:\x92z\xe4\xce\xd1!G\xaf\x1f\xebw\xec\xf2\xf1\ +>\xc7\xcf\x9d\xe8q\xe2\xd4\xc9\x88\x93\xc7\xf2\x83\xf3\x0f\ +\x9f\x0a8\xf5\xe3i\xce\xe9\xfd\xbf\xb0\x7f\xd9{\xc6\xe7\ +\xcc\x9e\xb3\xdegw\xff\xea\xfd\xeb\xees>\xe7\xf6\x9c\ +\xf7=\xbf\xef7\xbf\xdf\x0e\x5c\xf0\xbfp\xe8b\xb7\x8b\ +G.\x85]:y9\xfa\xf2\x99+\xbd\xae\x5c\xb8\xda\ +\xf7\xea\xef\xd7\x06^\xbb\xf9;\xff\xf7\xfb\x7f\x08\xffx\ +\xfag\xe6\x9f\x95\xd7\xc7\xdd\xd0\xbb1\xed/\xb3\xbf\xe6\ +\xff\xed\xf0\xf7\xaa\x7f:\xfe\xb3\xed\xa6\xcf\xcd\x9fn\x85\ +\xdd:{;\xfe\xf6\xf5;\xbc;\xff\xfeO\xfe\xbf\xaa\ +\x82IwM\xee\xce\xbf\xd7\xea\xde\x86\xfb\x1e\xf7\x7f|\ +\x10\xf1\xe0\xb7\x87_=,\xf8W\xfao\xe5\xa3\xc9\x8f\ +\xcd\x1f/)\xecP\xb8\xebI\xd0\x93\xb3E\xfd\x8a\x0a\ +\x9e\xca\x9e*\x9f\xcdxn\xfb|\xdd\x0b\xaf\x17G\x8b\ +c\x8b\xff.\x11\x95T\x96N+\xb3-[_\xce.\ +\xcf\xafH\xaa\xb8W\x99U\xc5\xa8Z\xf8\xb2\xe3\xcb\x03\ +\xafz\xbc\xba\xa1\x14U\xdf\x8f&A\x82\x04\x09\x12$\ +H\x90 A\x82\x04\x09\x12$H\x90 A\x82\x04\x09\ +\x12$H\x90 A\x82\x84\x1a\xb1\xb1\xb1\xfc\x9c\x9c\x9c\ +\x85zzz\xf4\xf7\x97&\xa1K\xb8\xba\xba\xfa\xe5\xe7\ +\xe7\x97\x9f={V\xb9l\xd9\xb2\x13VVV\xb6M\ +\xad\xd3\xff\x17\xd8\xdb\xdb;\x1d>|\xf8\x09\xf2\xbd&\ +\xec\xdd\xbb\xb7\x00\xb5IS\xeb\xf6_\x07\x1ak\x0e\x1e\ +<\xf8P\xdb\xf7\x9ap\xf2\xe4\xc9\xb2\xb8\xb8\xb8\x94\xa6\ +\xd6\xf1\xbf\x8cE\x8b\x16\x1d\xae\xcb\xf7\xda\x01\xce\x09\x8b\ +\xe8t:\xa3\xb1\xb2\xac\xad\xad\xed\xc2\xc2\xc2\xfa\x0a\x85\ +\xc2)s\xe7\xce\xdd\xb7q\xe3\xc6+\xfb\xf6\xed\xbb\x0b\ +\xdb\xb9\x14\x05\x14\xdf\xb4i\xd3\xd5y\xf3\xe6\xed\x17\x89\ +DS\xc3\xc3\xc3\x13Q\x1d]\xd8\xf99\x22))I\ +\xf4>\xdfk\x02\x9a\x13>\xc4\x17666\xf6C\x86\ +\x0c\xc9Z\xbe|y\xfe\x993g^\xd5W\x9e&\xa0\ +:+W\xae<=t\xe8\xd0\xec\xe6\xcd\x9b\xb7\xfc\x18\ +~h*\x8c\x1c9rqC|\xa1\x9e\x138\xf5\xe1\ +\xed\xe2\xe2\xc2\x9e\x16X,V\xc0\x81\x03\x07\xee7\xc4\xfe\xd7\xe7\ +\x044\x8f\xa31\xe2\xd8\xb1c/>\xb6\xef5\xe1\xf8\ +\xf1\xe3\xc5\xc9\xc9\xc99\xba\x98\x9b\x9a\x1a\xa8\xdf\xa2q\ +\xb6!\xf6k\xe6\x046\x9b\x1d\xb1u\xeb\xd6?>\x95\ +\xdf_\x0f\xdb\xb6m\xbb\xce\xe1p\xa2\x9a\xda\x87\x8d\x05\ +\xecG\xfac\xc6\x8cY\xd6\x10\xdb\x8f\x1e=\xfa\xbc\xa9\ +\xfc\xfez@k&\x0aDS\xfb\xb1\xb1h\xe8\x9c\xf0\ +9\x85\xdc\xdc\xdc\xbd&&&fM\xed\xc3\xc6\xe2C\ +\xe6\x84\xcf%\xa0\xf1\xc8\xc9\xc9\xa9sS\xfb\xb0\xb1\xf8\ +\x909\xe1s\x09h\x5c\xf4\xf5\xf5\x0doj\x1f6\x16\ +\xea9aiS\xfb\xf3C\x02\xba\xa6\xfe/\xb4\x01B\ +ff\xe6\x5c]\xf9\x05]\xc7)\x14\x8a9h\xcd\x02\ +\xc7\x89N\x86\x86\x86&0\x18;::\xb6\xdf\xb3g\ +\xcf\x1d]\xb7\x01Z\x9f5\xb5\xff\x1a\x03??\xbfH\ +dGc}\xb1s\xe7\xce\x9b!!!\xf1\xefZ\xa3\ +\xa0\xb1\xfbc\x9c\x07\xc8\x86O\xe93]\xa1S\xa7N\ +\xde\xe8:\xa7\xb1>@\xcfu\x18\x0c\x86\xc1\xfb\xe4}\ +\x0c\xff\xa3\x80l\x80\xb6x}\x0a\x9f\xe9\x0a\xe8\xfa~\ +\xff\xfe\xfd\xf7ta?\xba\xb7Y\x9f\xfb6\x1f\xcb\xff\ +( [\xbe\x94{xFFF\xa6\x1b6l\xb8\xa4\ +K\xfb\xd1\xf3\xb5\xb8\xb8\xb8\xd4w\xc9\xfd\x98\xfeGa\ +\xdd\xbau\x17\xd0|\xf3\xa9\xfc\xf8!@\xdfk\x9a=\ +{\xf6\xae\x8f\xe5\x03t\xdf\xf5m\xf7l>\xb6\xffQ\ +\x989s\xe6v\xadoR}v\xe0\xf1x\xa3>\xb6\ +\x0f\x96-[v\x12=\x1bx]\xf6\xa7\xf0?\x0a\xe8\ +\x99DS\xf8\xf6}pww\xf7\xff\xe5\x97_\xaa>\ +\x85\x0f\xd0\x9c\xe0\xe6\xe6\xd6U[\xfe\xa7\xf2?z\x96\ +\xd0\xa5K\x17\xdf\xa6\xf2s]@c>Z#6\xd6\ +\xb6\x86<\xe7BsB|||\xf5\x17\x7f>\x95\xff\ +Q\xd8\xbau\xeb\x9f\xfa\xfa\xfa\x86M\xe9sm\x0c\x1f\ +>\xfc[]\xd85q\xe2\xc45\x0d]7\x8d\x1a5\ +*\x0f\xcd\x09\x9f\xd2\xff(\xa4\xa6\xa6\x8eoj\xbf#\ +\xa0{\x86\xba\xba\x97\xec\xe9\xe9\x19\x84\xc6\xf6\x15+V\ +\x9cjH=\xf4\x8cx\xc7\x8e\x1d\xff|J\xff\x1f:\ +t\xa8\x10]{7\xb5\xff\x07\x0f\x1e\x9c\xa9\x0b{N\ +\x9c8QB\xa3\xd1\x88\x0f\x96\xa3\xfe\xd7\x86\xaelY\ +\xbat\xe9\xf1\xba\xf8\xa3u\xce\x87\xce\x09o\xd3y\xe1\ +\xc2\x85\x87t\xa1\xf3\xe6\xcd\x9b\xaf}<\xcf\xd6\x0fh\ +\xdc\xd0\x85-\xb3f\xcd\xda\xf96\x19\x1f:'\xd4u\ +\x9d\x800c\xc6\x8cm\xba\xd0\x19\xd9\xfe\xf1<[?\ +\xe8j\xdeC\xd7U\xef\x92\x83\xfa3\xea\xd7\x0d\xe1Y\ +\xd7u\x02B^^\xdeQ]\xe8\x0cm\xff\xfb\xe3y\ +\xb6~\x98?\x7f\xfeA]\xd8\xb2}\xfb\xf6\xbf\xea#\ +\x0f\xad\xf95\xef[\xd7'\xa09\x01\xcd#\xda\x058\x1cN\xb4.\xae\x0b\xb2\xb2\xb2\xbe\xfb\x90\xf7\ +\x0e\xd039\xf4\xeePc\xe5#\x1b\xbe\xd4=\x02\xdd\ +\xbbw\x1f\xa8\x8b6@\xeb\x9a\x9e={\x0e\xd1<\x1f\ +x\x17\xd0\xfa5&&f\xa8.\xde\xc1\x86\xba\xbfD\ +6|\x0a_},DFF\xf6\xd7\xd5\xf51z\xe6\ +\x84\xae{\xd0\xfeR\xb4O\x0f\xbdg\x8d\x02\x8a#\x1a\ +Z\x7f\xbd\xbe\x1f\xbc\xb1}\xbf\xb1\xf3\xd0\xe7\x80\xd0\xd0\ +\xd0\x84\xa6x>\xa5\xab\x80\xde\xe3\xfe\xdc\xd7=\xefC\ +PPP\xac.\xde\xc5m\xaa\x80\xf63|)\xeb\x9f\ +\xb7\xa1c\xc7\x8e\x9e\xe8\xfa\xaa\xa9}\xf9\xa1\x01\xcd)\ +\xe8=\xa7\xa6\xf6cc`fff\xb5`\xc1\x82\x9f\ +\x9a\xda\x97\x1f\x1a\xd0\xf3\x84/}N\xa0P(Tt\ +\xaf\xb3\xa9}\xa9\x09h\xbf}C\xaf\x19\xff\x0bs\x02\ +\xbaF\xf8\xd4\xefMi\x07\xf4\xce\x85\xbf\xbf\x7f\x0f\xa4\ +\x0b\xba\x97\x8f\xf6%7\xa4\xbezNphj?6\ +\x06M\xb1\xff\x1d\xc9\xaak\xff;J\xa3\xfd\xf9\x0d\xe1\ +\x85\xe6\x04\xb4\xe7\xb3\xa9\xfc\xa7+\xa0\xb5\xc5\xd7_\x7f\ +\xbd\xf6S\x8c5\xef\xeb\xb3\xe8;\x15\xe8{\x15\xf5\xe5\ +\x89\xe6\x04\xb4\xf7\xf9S\xf9\xeac\x02\xad\x91\xc6\x8f\x1f\ +\xbf\xb2!\xf6\xbf/\xa0\xfbp\x13&LX\xd5\x90}\ +D\xe8\xdeQC\xe7\x84\xcf\xf9\xdetCannn\ +\x8d\xbe3\x84\xd6J\x1fr\xed\x86\xea\xa0\xf7\xac\xfa\xf5\ +\xeb'\xb6\xb0\xb0\xb0\xf9\x10\x1d\x1a:' Y\xba\xf6\ +\xc3\xe7\x00SSSs4W\xa31\x1b}\xa3\x06=\ +w\xdf\xb2e\xcb\xefh\xec\x85\xe1\x01\x8a\xa3g\xeb\xdf\ +|\xf3\xcd&T\xa6k\xd7\xae\xddQ\x1d]\xc8F\xf3\ +S}\xee\xe3}N\xcf\x83\xff\x8b\xe8\xd3\xa7\x0f\xfe\xb6\ +1\x11\xb5\xfd\x97\xbe\x16\xfd\x12\x80\xbe\xeb\xf8\xfa\x9c\x80\ +\xdeM\xfd\xd2\xd7\xa0_\x12\xd0\xf7M5s\x02\x9a\xd7\ +\xffK\xdf4\xfbR\xa0\x99\x13\xd0w\x7f\x9bZ\x17\x12\ +$H\x90 A\x82\x04\x09\x12$H\x90 A\x82\x04\ +\x09\x12$H\x90 A\x82\x04\x09\x12$H\x90\xa8\x06\ +u\x05\x06\xa8\xf0\x1f\x83?\xb0\x82\x02hD\x1c\x80\xa1\ ++\xa85qU\xd1`\xbe$\x19g\xc6\xa6I\x14\x12\ +y\x9aD\xca\x0c\x93\xf02D\xb8X\xc1\x0c\xe3*\xb8\ +\xcc\x10\xa1\x84\x97\x0e\xd8!\xd111\x092\xe2=$\ +\x14\xef\xc9\x1d!\x03\xc0\xc0\x8f\xe0c\x01\x03\xdaI\x17\ +\x03C\x02\xa0)\x95\x00\xd0J\x09\xd6\x85D~!@\ +\xdf7/D\xf5\xc4\x12\x99H\x09\xf4\x91\x02\x9a\xf7\xed\ +;\x00\x80j\xbc?0\xe2\xd3\xb8R\x9c\xc9B|\x84\ +\x19b\xf4\xed\x19+\x18\x18 \x1e\xa4\x01.\x90\x02\x1c\ +0\x01K\xa5\x9fP,G{,irX\x85H\x8f\ +\x10\xa0g\xba\xc8r[\x94\xe6\x09\x93\x85(\x8d\xa9\xed\ +\x11\x88S\xb2\xd5\xf9D:]\x9c.\xd1N\x0b\xe5\xd2\ +\x94Zi\x9e\x10\xf1\xd7\xd7\xb8\x1b\xd1\xe4i\x22$\x03\ +\xed\xe9\xc3\x08\x19\x19r\x85:\x1b\xbd\x9fg\xa6\xf6:\ +\xac%\xc2\x15\x5c>t\xae\x9ab \xe4\x8e\xc0e\x09\ +\x02\x11\xce\x97d$\x07_m\x17Y\x94\xc4\xdaK\xf0\ +L\xc9\x96I\xd5uk\x03\x83\xb2\x0d\x8110\x02\xf6\ +\xa09\xb0\x86\xbf\xe6\xc0\x12\x963V\xffl\xe0\xcfZ\ +\xfd\xb3\x84\xa1\x05Q\xc2\x068T\xff\xec\x81\x1dA\xb1\ +\x84^D\xa5P\x8d\x16\xc0\xb6\x9a\x839\xa4\xb7Ps\ +\xb6\x87r\x8c\xa1<\xfd\x97\x80\xc2_H?\x02^\x01\ +\x9a_\xb9\xf2\x84\xedQ\xa0\x04\x98\xd3\x93WJ\xac\xc0\ +\xef\x18lf\xfa\xc3*%V\x96r\x1c\xeaw\xb8\x5c\ +\x89-b<\x05&v}\xa7\xec\xbbr\xb7\xb4\xf4\xee\ +\xd5\xfdS\x13\xed\xfe\x07\x80\xa8T\x89\x9d\xb0+\x06T\ +\xfb\xac\xfcW\xc5J\xea\xab\xd3\xd9-\xef\x01\xb0\xb8\x04\ +r\xe1\x94\x01\x0a{}\xe5\x13%\xa5r#\xe7\x01\xe4\ +\x98^Q\x8a8V\x02\xcca\xe3\x03%\xb6\xd9\xf1_\ +$2\xe0~\x89\x92\xbe\x88\x01\x00=\xfb\xc5=%\xa5\ +8\x87\xf1\x18\xea\x849\x9c.QRO\xd8E\xfcq\ +G\x89]\x8f*\x04/\x01\xa6\xbf\xacD\x09\x9e\xff\xa3\ +\xc4\xa6R\x8a\x00\xe4\x92^qY\x89\xed5{\x0a*\ +\x10\x17\x18\xbf\xde\xf9\x19(GUa\xfcy\xf8sP\ +\x06\xeb,\x85\xf1\xd2\xf0\x17\xa0\x04\x80\xb9O\x94\x8c\x82\ +9Q\x9dL\x8c\xdb\xdf)\x84\xd4\x88bP\x0a\xb0\xc8\ +\xd2\xa7J\xca\xcdxJ%\x00\xd7\x1fCjd\x09\xaa\ +\xe7]\xfcT\x89-4(\x87\xc4GJ\xac\xd8\xab\x14\ +1v\xbcW\xa4\xc4\xeer\x10\xf1_%v\xafe\x19\ +\x92lz\x09\xf2*O\xad\x80\xc4\x87J\xec\x82I9\ +R\x0d\xdb\x05\x0b,fTA\x22\xb4t;V\x01\x89\ +`\x14\x8c\x9e\xb4\x7f\x09i\xf7\x95 \xab\x02T\xc13\ +\xa7\xea>\xe4\xd8\xf5\x15A\xc3*}+\x91\x9d\xa67\ +\x9f*\xc1\xabR%\xad<\x0d\xd2\xef)\xb1?\x0d\xab\ + \x1d|\x0b\xa5\xaf\xb9W\xa2\xa4\xe41\xae\xdfU\x82\ +\xf1U\xc8Of\xcf\x8b\x94\x94 \xfbS\xd0\xe3\xf9\xff\ +\xdcUb\x85\xc6/!\x19d\x16)i%z\x80\xb1\ +\x04\xba\xac\xb2@\x09\x86\x13\xd4\xddEJp\x03J\x10\ +\xa0\x96\xa8\x18[\xee\x0c\xfe\x80\x94\x1dD\x1e\x8a\x1d\x87\ +*u\x85\x12\xb0<\x06\xd4\xf7\x10\xa4\x5c#\xf2J`\ +l'4K%\xc6\x1e\x9a\xb5\x0dRJ\x88\xbc\x7f`\ +\xec$\xf4\x03#\xaf\x04\x99\x02\xf5<\x0a)\x7f\x13y\ +\x07a\xec/\xe8\xcd\xb4r$O\xa0\x96\xb2\x9f\xc8\x9b\ +\x0a\xad)\xa5\xc3\xcc\xaewaK/a\xe8\x15\xc3\xac\ +I(\x0bkU\x05\xa3!\xb0m\xec\xf3!\xcfS\xf1\ +\xb0d\xb9\xddK\xe4\x82\x0d0cz\x89J\x18x\x01\ +\x13\xcb\x90g0\xa7g\xb0\xb5n\xd3\x8b\x91$\xe8\xaf\ +'PC\xa8\xe3`\x98\x9f\xf6\x02\x09\x80\xfeN\xaaD\ +M\xb0\x1c\x96\xbbe\x04i\xf6'\x1f*\xc1b\xd4,\ +\x98\xc19\xe8\xee5\xcf!\xd3\xc5\xb0\xa9\xcf2*P\ +\xab\xb6,\x80\xc4a\xcf\x00\x96Z\xfeD\x89\xfd\xcf\xbe\ +\x1c\xb5\xbf\xfb\xf3gJ\xec\x95\x0cR9O!\xa3g\ +ne\xa8\xabDW\xc1\xf8w&P\xb8\xe9B\x18\xab\ +\x8a*E\xddj \xa4R\xee\x0e\xd1+\x07\x14\xfa\xd0\ +\xfb\x90\xfer`\x09\xec\x84X\x7fh\x9bA\xe1\xb2D\ +\xb6\x83\x03;q\xf9\x13\xc8\xbcjX\x09\x80\x95\x13*\ +/)\xb1\xa5\x8c2\x00\xb5\x8b-\x85\xf1\xd3P(\xd4\ +\xca\xf3\x16\x8c\xdf\xf7\xaf\x00P\xac\xd5O0^1\xac\ +\x12<\x05\x18u:<\x13\xd6\x17\x94\xa0J/\xc1\x13\ +\xa8\xc7\xf5;J\xda\x8d\x1e\xc0\xee\x04\xa4\x9dvx\x85\ +\x06k\xd59\xf5\x22\x87\x01\xbd\xcaX\x04\xfd|?\x00\ +\x80G\xd0\xfc\xb5\xb0/\xaew\x80\x86\xa6\x94\x95*)\ +\x15\xe9\xe0!\xa0x\xae,\x83'k\xf9*\xd8\xd91\ +\x0ed\x0c\x16\xdf\x07Tk\xd1O\x95\xf0\xc4\xae<$\ +\xb6\x81\x8a\xd9\x9d(U\x02q\x0101\x8f\xce\xd9\x98\ +\xff\xfb\xfd\x07\xbf\x9f\xda\x94\xd3\xdd\xbc\x08`\xf4\x85p\ +\x90X\x06\xe0p\x81\x97U))\xa7\x18\xe0\x18\xc0\xfc\ +\x0a^)\xa9\xf7\x1c`\x97\x004\xdb\x13\xcar\xce+\ +p\x04*\xb5\x90\xff\x12\x0d{\x94\xa6\xf8\x1d8\xdb\x14\ +?\xd2\xda\xff\xb0\xb5\xeauT\x8c\x1c\xedo0U\xad\ +q\x08x\x8cS\xe7\xc5r\x15\x0a\xcd\xda\x22BU\xce\ +X\xbb\x1c:\xfc\x1f\x02\xea)+\ +\x00\x00\x80\x08\ +I\ +I*\x00\x08\x00\x00\x00\x18\x00\xfe\x00\x04\x00\x01\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x01\x01\x03\x00\x01\x00\x00\x00`\x00\x00\x00\x02\x01\x03\ +\x00\x04\x00\x00\x00.\x01\x00\x00\x03\x01\x03\x00\x01\x00\x00\ +\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00\x11\x01\x04\x00\x01\x00\x00\x00NS\x00\x00\x12\x01\x03\ +\x00\x01\x00\x00\x00\x01\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\ +\x00\x04\x00\x00\x00\x16\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x17\x01\x04\x00\x01\x00\x00\x000\x12\x00\x00\x1a\x01\x05\ +\x00\x01\x00\x00\x006\x01\x00\x00\x1b\x01\x05\x00\x01\x00\x00\ +\x00>\x01\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\ +\x00(\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\ +\x00\x22\x00\x00\x00F\x01\x00\x002\x01\x02\x00\x14\x00\x00\ +\x00h\x01\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00R\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\xbc\x02\x01\ +\x00\x1f8\x00\x00|\x01\x00\x00I\x86\x01\x00j\x0d\x00\ +\x00\x9c9\x00\x00i\x87\x04\x00\x01\x00\x00\x00\x80e\x00\ +\x00s\x87\x07\x00H\x0c\x00\x00\x06G\x00\x00\x5c\x93\x07\ +\x00X\x1a\x00\x00\xace\x00\x00\x00\x00\x00\x00\x08\x00\x08\ +\x00\x08\x00\x08\x00\x00\xf9\x15\x00\x10'\x00\x00\x00\xf9\x15\ +\x00\x10'\x00\x00Adobe Photo\ +shop CC 2017 (Wi\ +ndows)\x002017:04:0\ +4 11:01:25\x00\ +\x0a\x0a \ + \x0a \x0a \ + paint.net \ +4.0.9\x0a \ + 2017-03-01T11:2\ +0:20-08:00\x0a \ + 2017-04-04T\ +11:01:25-07:00\x0a\ + 2017-\ +04-04T11:01:25-0\ +7:00\x0a \ + imag\ +e/tiff\x0a 3\x0a \ + sRGB IEC\ +61966-2.1\ +\x0a xmp.\ +iid:7284f562-66e\ +c-6d4b-bfaf-a292\ +c87d086e\x0a \ + adobe:doc\ +id:photoshop:aca\ +ee4ff-1960-11e7-\ +bae7-e6e7a5cd281\ +4\x0a xmp.did:\ +15df0628-f397-b6\ +41-8e6b-37248a21\ +c43f\x0a\ + \x0a \ + \x0a \ + \x0a\ + \ + \ +created\x0a \ + xmp.i\ +id:15df0628-f397\ +-b641-8e6b-37248\ +a21c43f\x0a \ + 2017-03\ +-01T11:20:20-08:\ +00\x0a\ + \ + Adobe Pho\ +toshop CC 2017 (\ +Windows)\x0a \ + \x0a \ + \x0a \ + saved\x0a \ + \ +xmp.iid:7284f5\ +62-66ec-6d4b-bfa\ +f-a292c87d086e\x0a \ + \ +2017-04-04T11:01\ +:25-07:00\x0a \ + Ad\ +obe Photoshop CC\ + 2017 (Windows)<\ +/stEvt:softwareA\ +gent>\x0a \ + /\x0a \ + \x0a \x0a \ + \x0a \x0a <\ +/rdf:RDF>\x0a\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \x0a\x008BIM\x04\ +%\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x008BIM\x04:\x00\x00\x00\ +\x00\x00\xe5\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x0bprintOutput\x00\x00\x00\x05\ +\x00\x00\x00\x00PstSbool\x01\x00\x00\x00\ +\x00Inteenum\x00\x00\x00\x00Int\ +e\x00\x00\x00\x00Clrm\x00\x00\x00\x0fpri\ +ntSixteenBitbool\ +\x00\x00\x00\x00\x0bprinterName\ +TEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0fpr\ +intProofSetupObj\ +c\x00\x00\x00\x0c\x00P\x00r\x00o\x00o\x00f\x00\ + \x00S\x00e\x00t\x00u\x00p\x00\x00\x00\x00\x00\ +\x0aproofSetup\x00\x00\x00\x01\x00\ +\x00\x00\x00Bltnenum\x00\x00\x00\x0cb\ +uiltinProof\x00\x00\x00\x09p\ +roofCMYK\x008BIM\x04;\x00\ +\x00\x00\x00\x02-\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x12printOutputOp\ +tions\x00\x00\x00\x17\x00\x00\x00\x00Cpt\ +nbool\x00\x00\x00\x00\x00Clbrbo\ +ol\x00\x00\x00\x00\x00RgsMbool\x00\ +\x00\x00\x00\x00CrnCbool\x00\x00\x00\x00\ +\x00CntCbool\x00\x00\x00\x00\x00Lb\ +lsbool\x00\x00\x00\x00\x00Ngtvb\ +ool\x00\x00\x00\x00\x00EmlDbool\ +\x00\x00\x00\x00\x00Intrbool\x00\x00\x00\ +\x00\x00BckgObjc\x00\x00\x00\x01\x00\x00\ +\x00\x00\x00\x00RGBC\x00\x00\x00\x03\x00\x00\x00\x00\ +Rd doub@o\xe0\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00Grn doub@o\xe0\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00Bl doub\ +@o\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00BrdT\ +UntF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00Bld UntF#Rlt\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Rslt\ +UntF#Pxl@b\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x0avectorDatabo\ +ol\x01\x00\x00\x00\x00PgPsenum\x00\ +\x00\x00\x00PgPs\x00\x00\x00\x00PgPC\x00\ +\x00\x00\x00LeftUntF#Rlt\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Top U\ +ntF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00Scl UntF#Prc@\ +Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10cropW\ +henPrintingbool\x00\ +\x00\x00\x00\x0ecropRectBott\ +omlong\x00\x00\x00\x00\x00\x00\x00\x0ccr\ +opRectLeftlong\x00\x00\ +\x00\x00\x00\x00\x00\x0dcropRectRi\ +ghtlong\x00\x00\x00\x00\x00\x00\x00\x0bc\ +ropRectToplong\x00\x00\ +\x00\x00\x008BIM\x03\xed\x00\x00\x00\x00\x00\x10\x00\ +\x90\x00\x00\x00\x01\x00\x01\x00\x90\x00\x00\x00\x01\x00\x018\ +BIM\x04&\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00?\x80\x00\x008BIM\x03\xee\x00\ +\x00\x00\x00\x00\x0d\x0cTransparen\ +cy\x008BIM\x04\x15\x00\x00\x00\x00\x00\x1e\x00\ +\x00\x00\x0d\x00T\x00r\x00a\x00n\x00s\x00p\x00\ +a\x00r\x00e\x00n\x00c\x00y\x00\x008BI\ +M\x045\x00\x00\x00\x00\x00\x11\x00\x00\x00\x01\x00\x00\xff\ +\xff\x00\x00\x00\x00\x00\x00\x00d\x01\x008BIM\x04\ +\x1d\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008BIM\x04\ +\x0d\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e8BIM\x04\ +\x19\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e8BIM\x03\ +\xf3\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\ +\x008BIM'\x10\x00\x00\x00\x00\x00\x0a\x00\x01\x00\ +\x00\x00\x00\x00\x00\x00\x018BIM\x03\xf5\x00\x00\x00\ +\x00\x00H\x00/ff\x00\x01\x00lff\x00\x06\x00\ +\x00\x00\x00\x00\x01\x00/ff\x00\x01\x00\xa1\x99\x9a\x00\ +\x06\x00\x00\x00\x00\x00\x01\x002\x00\x00\x00\x01\x00Z\x00\ +\x00\x00\x06\x00\x00\x00\x00\x00\x01\x005\x00\x00\x00\x01\x00\ +-\x00\x00\x00\x06\x00\x00\x00\x00\x00\x018BIM\x03\ +\xf8\x00\x00\x00\x00\x00p\x00\x00\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\ +\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\ +\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\x03\xe8\x00\x008BIM\x04\x00\x00\x00\x00\ +\x00\x00\x02\x00\x008BIM\x04\x02\x00\x00\x00\x00\x00\ +\x02\x00\x008BIM\x040\x00\x00\x00\x00\x00\x01\x01\ +\x008BIM\x04-\x00\x00\x00\x00\x00\x06\x00\x01\x00\ +\x00\x00\x038BIM\x04\x08\x00\x00\x00\x00\x00$\x00\ +\x00\x00\x01\x00\x00\x02@\x00\x00\x02@\x00\x00\x00\x04\x00\ +\x00\x01+\x00\x00\x00\x0a\xc5\x00\x00\x00\x01\x1b\x01\x00\x00\ +\x0a\xd5\x018BIM\x04\x1e\x00\x00\x00\x00\x00\x04\x00\ +\x00\x00\x008BIM\x04\x1a\x00\x00\x00\x00\x035\x00\ +\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\ +\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00nu\ +ll\x00\x00\x00\x02\x00\x00\x00\x06bounds\ +Objc\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Rc\ +t1\x00\x00\x00\x04\x00\x00\x00\x00Top lo\ +ng\x00\x00\x00\x00\x00\x00\x00\x00Leftlo\ +ng\x00\x00\x00\x00\x00\x00\x00\x00Btomlo\ +ng\x00\x00\x00`\x00\x00\x00\x00Rghtlo\ +ng\x00\x00\x00`\x00\x00\x00\x06slices\ +VlLs\x00\x00\x00\x01Objc\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x05slice\x00\x00\x00\x12\x00\ +\x00\x00\x07sliceIDlong\x00\x00\ +\x00\x00\x00\x00\x00\x07groupIDlon\ +g\x00\x00\x00\x00\x00\x00\x00\x06origine\ +num\x00\x00\x00\x0cESliceOri\ +gin\x00\x00\x00\x0dautoGener\ +ated\x00\x00\x00\x00Typeenum\ +\x00\x00\x00\x0aESliceType\x00\x00\ +\x00\x00Img \x00\x00\x00\x06bounds\ +Objc\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Rc\ +t1\x00\x00\x00\x04\x00\x00\x00\x00Top lo\ +ng\x00\x00\x00\x00\x00\x00\x00\x00Leftlo\ +ng\x00\x00\x00\x00\x00\x00\x00\x00Btomlo\ +ng\x00\x00\x00`\x00\x00\x00\x00Rghtlo\ +ng\x00\x00\x00`\x00\x00\x00\x03urlTEX\ +T\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00nullT\ +EXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Msg\ +eTEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06a\ +ltTagTEXT\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x0ecellTextIsHTM\ +Lbool\x01\x00\x00\x00\x08cellTe\ +xtTEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x09\ +horzAlignenum\x00\x00\x00\ +\x0fESliceHorzAlign\ +\x00\x00\x00\x07default\x00\x00\x00\x09v\ +ertAlignenum\x00\x00\x00\x0f\ +ESliceVertAlign\x00\ +\x00\x00\x07default\x00\x00\x00\x0bbg\ +ColorTypeenum\x00\x00\x00\ +\x11ESliceBGColorTy\ +pe\x00\x00\x00\x00None\x00\x00\x00\x09to\ +pOutsetlong\x00\x00\x00\x00\x00\ +\x00\x00\x0aleftOutsetlon\ +g\x00\x00\x00\x00\x00\x00\x00\x0cbottomO\ +utsetlong\x00\x00\x00\x00\x00\x00\x00\ +\x0brightOutsetlong\ +\x00\x00\x00\x00\x008BIM\x04(\x00\x00\x00\x00\x00\ +\x0c\x00\x00\x00\x02?\xf0\x00\x00\x00\x00\x00\x008BI\ +M\x04\x14\x00\x00\x00\x00\x00\x04\x00\x00\x00\x048BI\ +M\x04\x0c\x00\x00\x00\x00\x04\x02\x00\x00\x00\x01\x00\x00\x00\ +0\x00\x00\x000\x00\x00\x00\x90\x00\x00\x1b\x00\x00\x00\x03\ +\xe6\x00\x18\x00\x01\xff\xd8\xff\xed\x00\x0cAdobe\ +_CM\x00\x01\xff\xee\x00\x0eAdobe\x00d\ +\x80\x00\x00\x00\x01\xff\xdb\x00\x84\x00\x0c\x08\x08\x08\x09\x08\ +\x0c\x09\x09\x0c\x11\x0b\x0a\x0b\x11\x15\x0f\x0c\x0c\x0f\x15\x18\ +\x13\x13\x15\x13\x13\x18\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\x0d\x0b\x0b\x0d\x0e\ +\x0d\x10\x0e\x0e\x10\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\ +\x11\x0c\x0c\x0c\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11\x08\ +\x000\x000\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01\xff\xdd\ +\x00\x04\x00\x03\xff\xc4\x01?\x00\x00\x01\x05\x01\x01\x01\x01\ +\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x01\x02\x04\x05\x06\ +\x07\x08\x09\x0a\x0b\x01\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\ +\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x05\x06\x07\x08\x09\ +\x0a\x0b\x10\x00\x01\x04\x01\x03\x02\x04\x02\x05\x07\x06\x08\x05\ +\x03\x0c3\x01\x00\x02\x11\x03\x04!\x121\x05AQa\ +\x13\x22q\x812\x06\x14\x91\xa1\xb1B#$\x15R\xc1\ +b34r\x82\xd1C\x07%\x92S\xf0\xe1\xf1cs\ +5\x16\xa2\xb2\x83&D\x93TdE\xc2\xa3t6\x17\ +\xd2U\xe2e\xf2\xb3\x84\xc3\xd3u\xe3\xf3F'\x94\xa4\ +\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\ +\x86\x96\xa6\xb6\xc6\xd6\xe6\xf67GWgw\x87\x97\xa7\ +\xb7\xc7\xd7\xe7\xf7\x11\x00\x02\x02\x01\x02\x04\x04\x03\x04\x05\ +\x06\x07\x07\x06\x055\x01\x00\x02\x11\x03!1\x12\x04A\ +Qaq\x22\x13\x052\x81\x91\x14\xa1\xb1B#\xc1R\ +\xd1\xf03$b\xe1r\x82\x92CS\x15cs4\xf1\ +%\x06\x16\xa2\xb2\x83\x07&5\xc2\xd2D\x93T\xa3\x17\ +dEU6te\xe2\xf2\xb3\x84\xc3\xd3u\xe3\xf3F\ +\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5V\ +fv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf6'7GWgw\ +\x87\x97\xa7\xb7\xc7\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\ +\x00?\x00\xf4<\xdc\xcc\x96d\x9a\xa9;@\x80\x00\x00\ +\x92O\xc6P\xfd~\xab\xe0\xff\x00\xfbl\x7f\xe4R\xc9\ +\xff\x00\x95\x1b\xfdz\xff\x00\xef\xabR\xcb\x19[\x0b\xec\ +!\xad\x1c\x92\x92\x9c\xbf_\xaa\xf8?\xfe\xdb\x1f\xf9\x14\ +\xbd~\xab\xe0\xff\x00\xfbl\x7f\xe4T\xed\xea\xee\x9f\xd0\ +\xb0\x06\xfe\xf3\xff\x00\xf2#\xff\x00$\x95]]\xd3\xfa\ +f\x02\xd3\xf9\xcc\xff\x00\xc8\x9f\xfc\x92Ja\xeb\xf5_\ +\x07\xff\x00\x98?\xf2(\x98\x19\x99\x16dzV\x9d\xc0\ +\x83\xc8\x00\x82?\xaa\xb4+\xb1\x960>\xb2\x1c\xd3\xc1\ +\x0b+\x07\xfeP?\xf5\xcf\xca\x92\x9f\xff\xd0\xef\xf2\x7f\ +\xe5F\xff\x00^\xbf\xfb\xea\x16fS\xb2-\xff\x00\x83\ +i\x867\xfe\xff\x00\xfd\xa4\x5c\x9f\xf9Q\xbf\xd7\xaf\xfe\ +\xfa\x87\x9b\x8a\xec{\x09\x03\xf4O>\xc3\xe0O\xe6\x1f\ +\xfb\xeaJnt\xecJ\x85-\xb9\xed\x0e{\xf5\x13\xac\ +\x0e\xd0\xa9dc\xde-\xb5\xfe\x9b\x85a\xce;\xbbD\ +\xf2\xad\xe0f\xd4\xda\x856\xb81\xcc\xd1\xa4\xe8\x08\xf8\ +\xa5\x9f\x9bS\xaa4\xd4\xe0\xf7?G\x11\xa8\x03\xe2\x92\ +\x9a\x98y.\xc7\xb4\x7f\xa3y\x01\xe3\xfe\xff\x00\xfd\x94\ +L\x1d:\x8b\x87\x9d\x9f\x95C\x0b\x15\xd9\x16\x87\x11\xfa\ +&\x19q\xf1#\xf3\x07\xfd\xf9O\x07^\xa2\xe3\xe7g\ +\xe5IO\xff\xd1\xef\xf2\x7f\xe5F\xff\x00^\xbf\xfb\xea\ +\xd4{\x1a\xf6\x96<\x074\xe8A\xe1g\xe6\xe1d\xbf\ +$\xdbP\x90b\x0c\xc1\x04!\xfd\x9b\xaa~\xf3\xff\x00\ +\xed\xcf\xfc\xc9%&\xb7\xa4\xb4\x99\xa6\xc2\xd1\xfb\xae\x1b\ +\x87\xdf\xf4\x92\xab\xa4\xb4\x19\xba\xc2\xe1\xfb\xad\x1bG\xdf\ +\xf4\x90~\xcd\xd5?y\xff\x00\xf6\xe7\xfed\x97\xd9\xba\ +\xa7\xef?\xfe\xdc\xff\x00\xcc\x92S\xa8\xc65\x8d\x0c`\ +\x0dh\xd0\x01\xc2\xca\xc1\xff\x00\x94\x0f\xc6\xcf\xca\x9f\xec\ +\xddS\xf7\x9f\xff\x00n\x7f\xe6H\xb88Y\x15\xdf\xea\ +\xda\x03@\x04s$\x92\x92\x9f\xff\xd98BIM\x04\ +!\x00\x00\x00\x00\x00]\x00\x00\x00\x01\x01\x00\x00\x00\x0f\ +\x00A\x00d\x00o\x00b\x00e\x00 \x00P\x00h\ +\x00o\x00t\x00o\x00s\x00h\x00o\x00p\x00\x00\ +\x00\x17\x00A\x00d\x00o\x00b\x00e\x00 \x00P\ +\x00h\x00o\x00t\x00o\x00s\x00h\x00o\x00p\ +\x00 \x00C\x00C\x00 \x002\x000\x001\x007\ +\x00\x00\x00\x01\x00\x00\x00\x0cHLino\x02\x10\x00\ +\x00mntrRGB XYZ \x07\xce\x00\ +\x02\x00\x09\x00\x06\x001\x00\x00acspMSF\ +T\x00\x00\x00\x00IEC sRGB\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xd6\x00\x01\x00\ +\x00\x00\x00\xd3-HP \x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x11cprt\x00\x00\x01\ +P\x00\x00\x003desc\x00\x00\x01\x84\x00\x00\x00\ +lwtpt\x00\x00\x01\xf0\x00\x00\x00\x14bkp\ +t\x00\x00\x02\x04\x00\x00\x00\x14rXYZ\x00\x00\x02\ +\x18\x00\x00\x00\x14gXYZ\x00\x00\x02,\x00\x00\x00\ +\x14bXYZ\x00\x00\x02@\x00\x00\x00\x14dmn\ +d\x00\x00\x02T\x00\x00\x00pdmdd\x00\x00\x02\ +\xc4\x00\x00\x00\x88vued\x00\x00\x03L\x00\x00\x00\ +\x86view\x00\x00\x03\xd4\x00\x00\x00$lum\ +i\x00\x00\x03\xf8\x00\x00\x00\x14meas\x00\x00\x04\ +\x0c\x00\x00\x00$tech\x00\x00\x040\x00\x00\x00\ +\x0crTRC\x00\x00\x04<\x00\x00\x08\x0cgTR\ +C\x00\x00\x04<\x00\x00\x08\x0cbTRC\x00\x00\x04\ +<\x00\x00\x08\x0ctext\x00\x00\x00\x00Cop\ +yright (c) 1998 \ +Hewlett-Packard \ +Company\x00\x00desc\x00\x00\x00\ +\x00\x00\x00\x00\x12sRGB IEC619\ +66-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x12sRGB IEC61966-\ +2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00\xf3\ +Q\x00\x01\x00\x00\x00\x01\x16\xccXYZ \x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ\ + \x00\x00\x00\x00\x00\x00o\xa2\x00\x008\xf5\x00\x00\x03\ +\x90XYZ \x00\x00\x00\x00\x00\x00b\x99\x00\x00\xb7\ +\x85\x00\x00\x18\xdaXYZ \x00\x00\x00\x00\x00\x00$\ +\xa0\x00\x00\x0f\x84\x00\x00\xb6\xcfdesc\x00\x00\x00\ +\x00\x00\x00\x00\x16IEC http://\ +www.iec.ch\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x16IEC http:/\ +/www.iec.ch\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00desc\x00\x00\x00\ +\x00\x00\x00\x00.IEC 61966-2\ +.1 Default RGB c\ +olour space - sR\ +GB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.IE\ +C 61966-2.1 Defa\ +ult RGB colour s\ +pace - sRGB\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00desc\x00\x00\x00\x00\x00\x00\x00,Ref\ +erence Viewing C\ +ondition in IEC6\ +1966-2.1\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00,Reference Vi\ +ewing Condition \ +in IEC61966-2.1\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00view\x00\x00\x00\ +\x00\x00\x13\xa4\xfe\x00\x14_.\x00\x10\xcf\x14\x00\x03\xed\ +\xcc\x00\x04\x13\x0b\x00\x03\x5c\x9e\x00\x00\x00\x01XYZ\ + \x00\x00\x00\x00\x00L\x09V\x00P\x00\x00\x00W\x1f\ +\xe7meas\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\ +\x8f\x00\x00\x00\x02sig \x00\x00\x00\x00CRT\ + curv\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\ +\x05\x00\x0a\x00\x0f\x00\x14\x00\x19\x00\x1e\x00#\x00(\x00\ +-\x002\x007\x00;\x00@\x00E\x00J\x00O\x00\ +T\x00Y\x00^\x00c\x00h\x00m\x00r\x00w\x00\ +|\x00\x81\x00\x86\x00\x8b\x00\x90\x00\x95\x00\x9a\x00\x9f\x00\ +\xa4\x00\xa9\x00\xae\x00\xb2\x00\xb7\x00\xbc\x00\xc1\x00\xc6\x00\ +\xcb\x00\xd0\x00\xd5\x00\xdb\x00\xe0\x00\xe5\x00\xeb\x00\xf0\x00\ +\xf6\x00\xfb\x01\x01\x01\x07\x01\x0d\x01\x13\x01\x19\x01\x1f\x01\ +%\x01+\x012\x018\x01>\x01E\x01L\x01R\x01\ +Y\x01`\x01g\x01n\x01u\x01|\x01\x83\x01\x8b\x01\ +\x92\x01\x9a\x01\xa1\x01\xa9\x01\xb1\x01\xb9\x01\xc1\x01\xc9\x01\ +\xd1\x01\xd9\x01\xe1\x01\xe9\x01\xf2\x01\xfa\x02\x03\x02\x0c\x02\ +\x14\x02\x1d\x02&\x02/\x028\x02A\x02K\x02T\x02\ +]\x02g\x02q\x02z\x02\x84\x02\x8e\x02\x98\x02\xa2\x02\ +\xac\x02\xb6\x02\xc1\x02\xcb\x02\xd5\x02\xe0\x02\xeb\x02\xf5\x03\ +\x00\x03\x0b\x03\x16\x03!\x03-\x038\x03C\x03O\x03\ +Z\x03f\x03r\x03~\x03\x8a\x03\x96\x03\xa2\x03\xae\x03\ +\xba\x03\xc7\x03\xd3\x03\xe0\x03\xec\x03\xf9\x04\x06\x04\x13\x04\ + \x04-\x04;\x04H\x04U\x04c\x04q\x04~\x04\ +\x8c\x04\x9a\x04\xa8\x04\xb6\x04\xc4\x04\xd3\x04\xe1\x04\xf0\x04\ +\xfe\x05\x0d\x05\x1c\x05+\x05:\x05I\x05X\x05g\x05\ +w\x05\x86\x05\x96\x05\xa6\x05\xb5\x05\xc5\x05\xd5\x05\xe5\x05\ +\xf6\x06\x06\x06\x16\x06'\x067\x06H\x06Y\x06j\x06\ +{\x06\x8c\x06\x9d\x06\xaf\x06\xc0\x06\xd1\x06\xe3\x06\xf5\x07\ +\x07\x07\x19\x07+\x07=\x07O\x07a\x07t\x07\x86\x07\ +\x99\x07\xac\x07\xbf\x07\xd2\x07\xe5\x07\xf8\x08\x0b\x08\x1f\x08\ +2\x08F\x08Z\x08n\x08\x82\x08\x96\x08\xaa\x08\xbe\x08\ +\xd2\x08\xe7\x08\xfb\x09\x10\x09%\x09:\x09O\x09d\x09\ +y\x09\x8f\x09\xa4\x09\xba\x09\xcf\x09\xe5\x09\xfb\x0a\x11\x0a\ +'\x0a=\x0aT\x0aj\x0a\x81\x0a\x98\x0a\xae\x0a\xc5\x0a\ +\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b9\x0bQ\x0bi\x0b\x80\x0b\ +\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\x12\x0c*\x0cC\x0c\ +\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\xc0\x0c\xd9\x0c\xf3\x0d\x0d\x0d\ +&\x0d@\x0dZ\x0dt\x0d\x8e\x0d\xa9\x0d\xc3\x0d\xde\x0d\ +\xf8\x0e\x13\x0e.\x0eI\x0ed\x0e\x7f\x0e\x9b\x0e\xb6\x0e\ +\xd2\x0e\xee\x0f\x09\x0f%\x0fA\x0f^\x0fz\x0f\x96\x0f\ +\xb3\x0f\xcf\x0f\xec\x10\x09\x10&\x10C\x10a\x10~\x10\ +\x9b\x10\xb9\x10\xd7\x10\xf5\x11\x13\x111\x11O\x11m\x11\ +\x8c\x11\xaa\x11\xc9\x11\xe8\x12\x07\x12&\x12E\x12d\x12\ +\x84\x12\xa3\x12\xc3\x12\xe3\x13\x03\x13#\x13C\x13c\x13\ +\x83\x13\xa4\x13\xc5\x13\xe5\x14\x06\x14'\x14I\x14j\x14\ +\x8b\x14\xad\x14\xce\x14\xf0\x15\x12\x154\x15V\x15x\x15\ +\x9b\x15\xbd\x15\xe0\x16\x03\x16&\x16I\x16l\x16\x8f\x16\ +\xb2\x16\xd6\x16\xfa\x17\x1d\x17A\x17e\x17\x89\x17\xae\x17\ +\xd2\x17\xf7\x18\x1b\x18@\x18e\x18\x8a\x18\xaf\x18\xd5\x18\ +\xfa\x19 \x19E\x19k\x19\x91\x19\xb7\x19\xdd\x1a\x04\x1a\ +*\x1aQ\x1aw\x1a\x9e\x1a\xc5\x1a\xec\x1b\x14\x1b;\x1b\ +c\x1b\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c*\x1cR\x1c{\x1c\ +\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1dG\x1dp\x1d\x99\x1d\xc3\x1d\ +\xec\x1e\x16\x1e@\x1ej\x1e\x94\x1e\xbe\x1e\xe9\x1f\x13\x1f\ +>\x1fi\x1f\x94\x1f\xbf\x1f\xea \x15 A l \ +\x98 \xc4 \xf0!\x1c!H!u!\xa1!\xce!\ +\xfb\x22'\x22U\x22\x82\x22\xaf\x22\xdd#\x0a#8#\ +f#\x94#\xc2#\xf0$\x1f$M$|$\xab$\ +\xda%\x09%8%h%\x97%\xc7%\xf7&'&\ +W&\x87&\xb7&\xe8'\x18'I'z'\xab'\ +\xdc(\x0d(?(q(\xa2(\xd4)\x06)8)\ +k)\x9d)\xd0*\x02*5*h*\x9b*\xcf+\ +\x02+6+i+\x9d+\xd1,\x05,9,n,\ +\xa2,\xd7-\x0c-A-v-\xab-\xe1.\x16.\ +L.\x82.\xb7.\xee/$/Z/\x91/\xc7/\ +\xfe050l0\xa40\xdb1\x121J1\x821\ +\xba1\xf22*2c2\x9b2\xd43\x0d3F3\ +\x7f3\xb83\xf14+4e4\x9e4\xd85\x135\ +M5\x875\xc25\xfd676r6\xae6\xe97\ +$7`7\x9c7\xd78\x148P8\x8c8\xc89\ +\x059B9\x7f9\xbc9\xf9:6:t:\xb2:\ +\xef;-;k;\xaa;\xe8<' >`>\xa0>\ +\xe0?!?a?\xa2?\xe2@#@d@\xa6@\ +\xe7A)AjA\xacA\xeeB0BrB\xb5B\ +\xf7C:C}C\xc0D\x03DGD\x8aD\xceE\ +\x12EUE\x9aE\xdeF\x22FgF\xabF\xf0G\ +5G{G\xc0H\x05HKH\x91H\xd7I\x1dI\ +cI\xa9I\xf0J7J}J\xc4K\x0cKSK\ +\x9aK\xe2L*LrL\xbaM\x02MJM\x93M\ +\xdcN%NnN\xb7O\x00OIO\x93O\xddP\ +'PqP\xbbQ\x06QPQ\x9bQ\xe6R1R\ +|R\xc7S\x13S_S\xaaS\xf6TBT\x8fT\ +\xdbU(UuU\xc2V\x0fV\x5cV\xa9V\xf7W\ +DW\x92W\xe0X/X}X\xcbY\x1aYiY\ +\xb8Z\x07ZVZ\xa6Z\xf5[E[\x95[\xe5\x5c\ +5\x5c\x86\x5c\xd6]']x]\xc9^\x1a^l^\ +\xbd_\x0f_a_\xb3`\x05`W`\xaa`\xfca\ +Oa\xa2a\xf5bIb\x9cb\xf0cCc\x97c\ +\xebd@d\x94d\xe9e=e\x92e\xe7f=f\ +\x92f\xe8g=g\x93g\xe9h?h\x96h\xeci\ +Ci\x9ai\xf1jHj\x9fj\xf7kOk\xa7k\ +\xfflWl\xafm\x08m`m\xb9n\x12nkn\ +\xc4o\x1eoxo\xd1p+p\x86p\xe0q:q\ +\x95q\xf0rKr\xa6s\x01s]s\xb8t\x14t\ +pt\xccu(u\x85u\xe1v>v\x9bv\xf8w\ +Vw\xb3x\x11xnx\xccy*y\x89y\xe7z\ +Fz\xa5{\x04{c{\xc2|!|\x81|\xe1}\ +A}\xa1~\x01~b~\xc2\x7f#\x7f\x84\x7f\xe5\x80\ +G\x80\xa8\x81\x0a\x81k\x81\xcd\x820\x82\x92\x82\xf4\x83\ +W\x83\xba\x84\x1d\x84\x80\x84\xe3\x85G\x85\xab\x86\x0e\x86\ +r\x86\xd7\x87;\x87\x9f\x88\x04\x88i\x88\xce\x893\x89\ +\x99\x89\xfe\x8ad\x8a\xca\x8b0\x8b\x96\x8b\xfc\x8cc\x8c\ +\xca\x8d1\x8d\x98\x8d\xff\x8ef\x8e\xce\x8f6\x8f\x9e\x90\ +\x06\x90n\x90\xd6\x91?\x91\xa8\x92\x11\x92z\x92\xe3\x93\ +M\x93\xb6\x94 \x94\x8a\x94\xf4\x95_\x95\xc9\x964\x96\ +\x9f\x97\x0a\x97u\x97\xe0\x98L\x98\xb8\x99$\x99\x90\x99\ +\xfc\x9ah\x9a\xd5\x9bB\x9b\xaf\x9c\x1c\x9c\x89\x9c\xf7\x9d\ +d\x9d\xd2\x9e@\x9e\xae\x9f\x1d\x9f\x8b\x9f\xfa\xa0i\xa0\ +\xd8\xa1G\xa1\xb6\xa2&\xa2\x96\xa3\x06\xa3v\xa3\xe6\xa4\ +V\xa4\xc7\xa58\xa5\xa9\xa6\x1a\xa6\x8b\xa6\xfd\xa7n\xa7\ +\xe0\xa8R\xa8\xc4\xa97\xa9\xa9\xaa\x1c\xaa\x8f\xab\x02\xab\ +u\xab\xe9\xac\x5c\xac\xd0\xadD\xad\xb8\xae-\xae\xa1\xaf\ +\x16\xaf\x8b\xb0\x00\xb0u\xb0\xea\xb1`\xb1\xd6\xb2K\xb2\ +\xc2\xb38\xb3\xae\xb4%\xb4\x9c\xb5\x13\xb5\x8a\xb6\x01\xb6\ +y\xb6\xf0\xb7h\xb7\xe0\xb8Y\xb8\xd1\xb9J\xb9\xc2\xba\ +;\xba\xb5\xbb.\xbb\xa7\xbc!\xbc\x9b\xbd\x15\xbd\x8f\xbe\ +\x0a\xbe\x84\xbe\xff\xbfz\xbf\xf5\xc0p\xc0\xec\xc1g\xc1\ +\xe3\xc2_\xc2\xdb\xc3X\xc3\xd4\xc4Q\xc4\xce\xc5K\xc5\ +\xc8\xc6F\xc6\xc3\xc7A\xc7\xbf\xc8=\xc8\xbc\xc9:\xc9\ +\xb9\xca8\xca\xb7\xcb6\xcb\xb6\xcc5\xcc\xb5\xcd5\xcd\ +\xb5\xce6\xce\xb6\xcf7\xcf\xb8\xd09\xd0\xba\xd1<\xd1\ +\xbe\xd2?\xd2\xc1\xd3D\xd3\xc6\xd4I\xd4\xcb\xd5N\xd5\ +\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\xe0\xd8d\xd8\xe8\xd9l\xd9\ +\xf1\xdav\xda\xfb\xdb\x80\xdc\x05\xdc\x8a\xdd\x10\xdd\x96\xde\ +\x1c\xde\xa2\xdf)\xdf\xaf\xe06\xe0\xbd\xe1D\xe1\xcc\xe2\ +S\xe2\xdb\xe3c\xe3\xeb\xe4s\xe4\xfc\xe5\x84\xe6\x0d\xe6\ +\x96\xe7\x1f\xe7\xa9\xe82\xe8\xbc\xe9F\xe9\xd0\xea[\xea\ +\xe5\xebp\xeb\xfb\xec\x86\xed\x11\xed\x9c\xee(\xee\xb4\xef\ +@\xef\xcc\xf0X\xf0\xe5\xf1r\xf1\xff\xf2\x8c\xf3\x19\xf3\ +\xa7\xf44\xf4\xc2\xf5P\xf5\xde\xf6m\xf6\xfb\xf7\x8a\xf8\ +\x19\xf8\xa8\xf98\xf9\xc7\xfaW\xfa\xe7\xfbw\xfc\x07\xfc\ +\x98\xfd)\xfd\xba\xfeK\xfe\xdc\xffm\xff\xff\x80\x00 \ +P8$\x16\x0d\x07\x84BaP\xb8d6\x1d\x0f\x88\ +DbQ8\xa4V-\x17\x8cFcQ\xb8\xe4v=\ +\x1f\x90HdR9$\x96M'\x94JeR\xb9d\ +\xb6]/\x98A\x00\x930(*l\x0c\x9c\x03\x02\x13\ +`P0\x07?\x02?\xe8O\xe8X\x06 \x01\xa4\x00\ +hO\xfa$\x1a\x93H\x82R\xdf\xf0J}J\x06\xff\ +\xa4\x80\xa9t\xd0\x05\x1a\x07U\xa5\xd5\xeb\xf4\xfa\x8d\x86\ +\x05S\x81S\xe9VhK\xfc\x05o\x01\xd5\xaa\xf6\xca\ +\x8d~\x0fh\xb4\xd2\x00P\x8a\x95\x92\xefK\xb0P\xac\ +W\x9aM\xca\x9dI\xbe]05\xca\xf0\x02\x98\xfe\xc8\ +>\xf2O\xa7\xa6U\xe2\xf3\xcc<2O\xb7\xce\x1aS\ +O\x07\xe8Bcm!\x10\x87\xa7)\x89\xb5C\x00v\ +\xb4#?\x01\x81*\x98\x88]\xf7i\x87\xa4g\xb18\ ++\x1e\xe7\x01\xb7\x8e\xed\xaa\x17:\x15\xfa\x0d\xba\xde\xda\ +\xf7\x98Ln;\x7f\xbe\xe2\xf0!\x99\xec\x0d\xe2\xbbj\ +\xe8T\xab\x18^\x7f6\x9f\x90\xc8uz\xbb>\xcf\x81\ +\xfc\xf8\xf4=\x9c\xbe\xb6\xf3#\xdc\xbaa|VN\x0f\ +\xa3c6\xfa\x94\x04\xbfAb\xcf\xf4\xe0)\xc0\x03:\ +r\x08-\xebzc\x03\xc1\x10L\x15\x05>\xe6\x94\x1c\ +c\x13p\x89\x04h\xc2\x86+\xee\x90\x00\xd0\xc8\x10(\ +C\x83 \xdb\x0f\x91\x00LD\x05\xc1q,M\x13\xc5\ +\x09B\xa4fE\x85\xf9\x17\x17\x8d\xa6\xfcdk\xb9\x08\ +\xb0;\x1b\x84\x84LtV5A0_\x14\xc8\x12\x0c\ +\x85!\xa2\xe7\xd4\x8c|\x93RI\x00SI\x84|\x8c\ +}\x1f(\xc4\x0a\x01\x07\xb2\xa8\x9aCK\x058\x0f-\ +\x812$\xbd/\xcc\x13\x0a\x04gL\x86\x08\xed3\x8a\ +\x87\x84\xd4v#\x09\x98\x08\x02\x8a\xf3\x88\xda7\xce\x84\ +\x5c\xa71O\x13\xcc\xf5\x03\x9d\x13\xe9\xc44P\x02\x11\ +\xc9A\x9b\xa8\xc42\x03\x01\x03-\x14?\x0c\x14h\xf0\ +\xe3Ot\x8d%I\xa3\xe7\x95,w\x0c\x94\xc8|o\ +S\x86\xad\x0d\x0d\x0d\x95\x09\x0e\xfe\x8b#}!JU\ +\x15MT\x873\x07\x99\xe1E\x0c\xa1\xf1\xb9Y\x9ah\ +\xc4\xb6\x03\x810\xf8\xdaDN\x22\xb8\xdbS\xc4\xae\x13\ +\x9bU\xc8q\xab\xae\xe1\xa1V\x15UV\x9e\x14\xc8\xc8\ +\x1e\x9b\xb6\x89\xa9O\xd1\x03u\xacD\xd7\xb5\xfb\xa4\x89\ +\xaaF\xdd\xbch\x9e\xb7\x09\xe6\xea\xcav\x13\xa8\xe0\x1f\ +\xb7I\xfb;\xb8\x8e\xdb\xb2\xf29Wr\xd4\x82<\xc8\ +E\x80\xe3\xbb\xab+\xa3d9\xd7\xdd\xe3}-\xd0-\ +\xec\xe0X\xc8\xa3\xc6\xe4\xbc\xce\xc5\xde\xbf\xb9`\x04\xdc\ +\x02\x84X\x88T\x05\xe2\x80r8\xca\x9e\x87\x8d\x9c\x1e\ +\xd6f\xe5j\x8b\xd6\xe0MB6\x10\xe2\xc6L7^\ +\xe8\x89\xf9\x95\x9fc\xa6\x5c(\x9a\xf9\x89\x9be!\xf7\ +2\x87\x02\x80xN\x15\x80:\xb6\x15 \xed.\x92\x9d\ +\xea\x81\xe8O\x06R\xe9\xad\x97r\xb5\x9b\xae\x17\xa6\x8c\ +\xe0`\xeec\xcdr\xbb\xb9\xa5\x93|\xdd\xb4\x83\xcc\x08\ +\xeb\x80\xa8\xf9\xaf\x93an\xc4\x1cb\xec\xb0\xc7\xb3\x87\ +\x96\x89\xbbi\xe4\x12\xe6GQ\xbf\xa3z8\xfb\x8c\xdb\ +\xa8~i\xef\x06E\x89\xbd\xef\x88>\xb8\x08\x82\xb1\xd1\ +\x12U\x86\x5c(~\x8eY\x9b\xa8\xcc\x1f\xdb\xc6\xd9\xa5\ +[K\x95\xd5y9h\xe8{\xefX\x07\xc6\xa74d\ +\xef\xbc\xee\xfa\x08t\x00\xa4^E\x95\xc1\x87L\x1er\ +\xa8v1\x8dS8\xe5i\xc8W\x1b~L,e\x16\ +\xda%'\x9f#?t o\x06\x9e\xf5\xcfx\x15W\ +@\x08tQ\x7fK\xd3\xf5(m\x99\xdd\x0c\xe2\x01\xb5\ +\xe7\x9a6\xa0\x11\xc9T\x9b\x926\xfb\xf9\x82\x04\x1ci\ +\x18\xfe\x0f\xbdJx`\xa1\x15\xf1\x95\xbc(d\x1fl\ +\xb8\xcf\x14\x1f\xf9\xe6\xd7\xa3\xb6\xf65\x17g\xda\xdf\x88\ +\xa7/Es<\xdf\xbf\xfd\xcfmh\x1c\x04\x84d\x01\ +\x15\xcf\x99\xc3\x91\xb5\x98\xe6\x18\xeb\x1f\x22\xcc\x85\xc9-\ +\x97\x92C\x1b\xa3vw\xae\xfd\xfeAT\xc0\xf8]\x18\ +\xae\x060l\x1e\x91\xc5\xc2=G\x93g\x0c`\xf2\x04\ +\xbb\x06D\xfc\x99<\x0f!l\xac~\x0f\xb71\x04\xe0\ +\xb41K\xcd\xfd\xc0\xa3\xb7\xcc\xfa 1\x99\x81\x0e\xbd\ +\xf8\x00\x95\xac\x1b\x96\xc2rnfM\xcc9\xa1\xa8\xe7\ +!\x91\x02\x02\xb1,\x0d\x02\xf8\x9c\x0e\xc1DQ\x06Q\ +D\x14\x03\x10-\x15\xc0\xeb\xfe\x021h\x82)a\xe4\ +;\xd6`\xe9\x8cC\x8cl\xc6Q\x9e6#@\xce\x1a\ +\x11\xacb\x0e\xb8\xdc9\xa1\x8c4|b(V\xc1\xb0\ +c\x07a\xca\xaecm\xa9\xb6@\xb7\x22\x88 s\xb6\ +\x220E\xc5\xc3\x07=\x15\xc0\xb0\x1b\x09R,.\x04\ +\x09\x1c\x14\x01\x5c\x91\x06\xf0\xa8\x8f\x15!\xad%\xc6X\ +\xbf\x93B\xbc\x5cI\xd1J\x9fG@\xe3o\x90a\xe3\ +Gh\xf0F\x9d[\xeb}\xaf\xbe??\x16H\xf5H\ +\xe4,\x1fln#D\x85P\xd8\x81h8T\x81\xc0\ +\xd3\x840\xa6l\x0d\x92`]C\xf2M\x0b\xf1^)\ +\xe6@\x90B\x83Dc)8\xe4\xf9%3\x88\x87O\ +\xe2\x12\xc3\xd7$\xfc\xc8\xe3\xb8}r\xd5=\x01y\xbc\ +\x07\x03\x8c\xe1\x11\xc1\x12r\x05W\xf6/\xa7@\xad\x11\ +\xd3\xac9J\x01\xc4\x98_\x0cs\x8e\xb0rJ\x10\x97\ +\x96\xee\xe5[\xd2\x9a\xf0\xa6A\x11\x09\x08\xdd\xdb\xcaD\ +\x00\xb4\x0c\x03\x05\xba\x0c\x1c\xa1\x10|D@%\x12D\ +\x90\x00z\x07\xc0\xf6\x13\xd4LB\x8aJ,#]\xc2\ +@\x99\xf1\xd2h\x91\xb7V\xc6\xe6\xac\xadW2\x01\xca\ +O\xd7-\x11\x1f\xc4\xdcE \xe2\x96\x04d\xce\x1d\x84\ +\x98\x1e\xa6@\x9a\x87\x11\x11\xc7M\xc6\xe3\xa3\x0d\xa3\x1a\ +\x9e\x0b\x84L\xf8`\x08\x8c\x15\xee\x98\x18\x03\xc7\xd2<\ +eS\xd0\x84\xd06\x92\xbfR',\xa5\xa3\xfaA\x09\ +L4\xd5Q\x06\x18j\xc0z\xa6\xa4\x80P\xd5\xd1\x12\ +%k\x00zhd\xba\x1a8!Y\x0d\xe0\xf2\xe1\x84\ +-\xa2\x90\x91VC\x0f\xc4HV\xaeA\xb2z\x90\x87\ +p\xf6d1.\x01\xb5\xec\x08\x08J\xfc)A\xdd\x81\ +\x09Ul\x93\xd3\xc1\x8c.\x03\xdd\x89\x0b+0\x96Q\ +\xb9\xe7\x1d\xea;\x98q\xae>\x1e\xb6\xf9`\xf5\xcc\x9b\ +\xeb\xaf$\xa5\x88\x82 UPE\x852\x03\xc0\x96\xc2\ +\x12\xean8\xc6\xe3.\x0e\x81A\xb5)\xe2Oc\xa8\ +\xec\xa82\xd6IoYJD\xec\xa7\xe5N\x22Oa\ +\xdd\xbd\xb7\xbaI\xcd 6\x08u\x04W\x93\xc0\x1bi\ +PP\xf6\xb9C\xd2\x97\x85#\xdc2\x05\xd9$\x86\x90\ +f'\x02\xf0w]H<\x07\x9a\x90\xf0\x8b(p\x11\ +\x5c&\xc5\x98\x1fc\xea\x22\xd5\x22D\x0d\xef@D\x9d\ +b8Y2\x1b\x90\x90\x12x\xf8\x0eW\xcc'\x5c\xfb\ +\xa2G\xe5#\xa4\xa8\x8e\xa2\x93:\xa3-R_uK\ +\xa4\x8a\xfa\xec\x10i\xfff\xc8\xe5,\x07\x01\x1a\xf5\x8b\ +\x1b\xbc\x91#p\xeb\x1c\xc2\xf7\x0a\x0a\xcb\x0c-\xe7t\ +b\x1d#\x8c\xa9D\xb0*\x06\x84\xbe!\x17\xb2 \x0e\ +'\xab\xe3|\xc3\x90O\xb9\xe2\xe8\x8eO\x17\xc9Z(\ +\xf5\xb3\x7f\x16O\x01+\xb9\x03n\xa4\x1d\x99\x82T\x04\ +\x8d\x83\x9c|\x11\xc4nA\x16\x18=\x14\x8e|\x8c8\ +D\x86I\x0es\x14W\xd62\x22+\xf2\x80\xdbF\xe0\ +t\x12);\xe2\x1c\xf2\xc0P\x18\xf9l\x5c\x91\x8c]\ +\x1d1\x85\xb2c6\xd1\xc7cW'\x81/\xe9\x0d\xb7\ +\x8f6\xdf\x11\x89\x22\x0a\xc1\xa8\x99\xceC\x00\x04gP\ +\x14\x90(\xb0\xa4\x11\xa2c>\x07\xda =\xc8\xe6P\ +\x15\xf9I\x1c*\xaa =\x9f\x5c\x97\x1a\xc30\x8a_\ +\x985\x07+L cx\xd2\x1e\xd7\x0a\xe4\x15\xab\xa6\ +i\x82\x06M\xec\xe6\xd2&\x065\x00\x1e\xab\xa2\x84d\ +\x81=L\x06\x111\xe6\xa7B\xafV\x09bI\xa0\xb4\ +&To\x83\xb7Z\x0e\x80\xbf\xad\xc1\xb4\xa0\x94D>\ +\xb2\xc3g\x0b\x0e3\x15Hn\xd3\xe4\x8b\xdd\xe0\xe1\xb1\ +\xc4XU\xd9A\xaf\x02\x90\x5c\x0f\x8f\x08q9\x01\xc2\ +wj\x0cPG\xb5\xc1b@\xaf\xc2\x102\x8b\x0d\xbc\ +'\x09F\xb0\xcayU\xcfY5\x9c\x0f \xf8\xf3!\ +\x9a\xf4D\x8a\xc9M\xb3H#\x89n\xdaR\x91T\xcc\ +\xd1\x8e'\xf6:\x90\xbb@\x84\x94\xf1\x1f\xbf\xc5\x98<\ +\xe0A- \x0bN\x0c(\x04\x07\x09\x0c$\xb3qh\ +W\xbe|F\x10\xb2\xcb\x01\xcc(,l\xbe\xf9u\xfc\ +\xd2U\xd0\xed\x8f=&\xde\x16\xb9\x00p\x88w\x8fN\ +\xa0\xeb~B\x96p|P\x01\xa0A\xa4\x01\xcd\xcb\xc7\ +\x06\x97\x05z\x1f\x86e\x1d\xc7\x0cD\xa79\x0f\x02\x83\ +\x9e\x08\x92\x11\x0d\x04GA\x15P\x13x\x108\xbc;\ +\xde\xce\xf4\xad\xc9sKW>\x8a@\x9d\xc7+\x08:\ +x\x83_\xb4\x92&\x86\x038\xdbU\xfbn\xed\xf2a\ +\xc3u\x94\x16\x98p\x88\x1eR\xa2\x05\xa3\xad\x89\x19\xde\ +N/\xa5\x11H\x18\x88/\x09\x1a\xd9\xee\xf8\x82m)\ +\x90)\xc6\x90\x19\xef@} da\xce8B\x8f\x81\ +\x04\xb2\xcb\xaf\xf3n\x1d\x12G\x17\x89\x1b|\x80-\x03\ +\x0d\x0f\xc5\xf3\x09\x19\xa3\xee\xb6\xb6\xf6\xe6\xdd\x0a\x1d\xa7\ +O\x00\x1d\xce\x0a\x00\x09\xc2\x1cDm\x06\x0ba\xc9\x22\ +\x0a/L\x22\x84\x97\xa9\x0e\xe8'\xb0nK\x08'=\ +\x80\x82\xcf\x82`?\xf9\x0e3\x1eVk\xad\x8f\x90\x9a\ +\xdcy\x9d4B\xdf\xba\xb1\x96\xb5\xec\x06\x81\x01k\xf1\ +\xc7\x19<\x01\x89\x13\xa9\x22\xc1\x980=g\x86\xec6\ +\x13\xa3\x84\xcf\xac\x07\x99\x0c\xf2\xed$b\x0f\x8f-'\ +ms7q#>s[\x85\xf0\xec\xae\xb9\xf2C\x1f\ +?\xac{\x83\xff\xdc\x03\xfc\x22\x08\xf5\xb7\xbc\x81\xef\xf1\ +\x1e\x1c\xbe8\xb5\x14w\x12\xfd\xe9\x1f\xbcu\xae\xda\x22\ +j\xdeZ\xed.\xd3-\xf0\xa4\xeeH\xb7\xaeL\x120\ +\x18\x16\xab\x02\x07k\x06He\x06\x1c\x81\xbaC\x80\xa0\ +\xf5\xc4\x14\x03\x904\x04F\x1e$\x8b\xd6\x16KD\xa6\ +\x84\x86\xe2\x01f\x100L\x0c+\x88\xfbb.ul\ +\xc8\xb6\xce\x96W\x0d\xec[P\x0e!\xcf\x82\x7f(\x8e\ +\x15\xd0p\x1b0BH\x89\x96\x18\xae\xc8\xa6\xaff\x17\ +\xc0i\x08`\x82H\x83\xe8\x1c\x01\xb0s\x09\xe4\xbfo\ +5\x05\x8cf\xfc\x09\xac\xc0pdXb&\xf3\x81\x8b\ +\x0a\xe1\xec\xbd\xc4\x86\x18\x90\xb8\x16\x8d\x8e\x0e\x00\x9a\xa6\ +\xab\x88\x91\xc0\x80\x0a\x04\x88\xfda\xf2\x1e\xe0\x97\x0d`\ +<\x9eO\x22#\x0e\xd6q\x90\xa0\xde\xb0\xa4\xf3J\xee\ +wh&\x16P\xf4\x1b\xe05\x0f\xa0@H\x8b|\x0c\ +Q\x04\x07Jj\xf6\x018\x18J\x88\x94\xe4\x82\xe5\xe1\ +\xcc\x1b\xef\xca\x06\xea\xcd\x0d\xe2.\xbbEb\xf2\xb0\x04\ +\x8f\xecl\xa9\xb0\xa8\xb7jP\xf8G6\x12\xd1@\x17\ +k\x82\x08\x84\x88=a\xca\x1b\xc0\x9f\x15 F\xa6\xb0\ +p\x15\xd0t\xa6d\x88\xc5f\xbe\x0f\x80\xb6\x830\x98\ +\xf7\xe2\x14\xfb\xaf\xbe\xcc\xb0\xa3\x13-\xef\x13lr\xbc\ +k4o'$\xfc\xafVHk\xe2\xfd\xc0~\x01\xe4\ +.{\xe6\x1e\x18\x11\xa0\x1e\x0c\xea\x01\x0c\xeeHj&\ +\x13\xc1\x0a\xee\xe1#\x09g\x91\x17\x09\xec3'\xd7\x00\ +\x22$d/z~\x91\x80\xdf1\x84\xc7g|\xef@\ +2\x03\xe1c\x1d\xe1\xba\xebD\x86\xaa\xa0\xd2\x08a\x97\ +\x1e\xe1|\x7f`g\x1f`~\xceA2\xfa\x04\x86>\ +\xe0\x9d `B>\xf0\xdc\xf6\xe25\x0e1\xc4\x220\ +\x06\x88\x10\x0a\xf3Pj\xec\xc0\x00\xe8!\x10\x15)\xc8\ +\x08\x80\xacH\x8c\xf2\x11\x8c\x92\x12\x00\xea\x7fj\xe0\x0b\ +\xd2D\x0e\xc4\x89\x0fAd\x13\xe1\x05% \xc4\xb6\x0d\ + \xc6,\xc7\x09\xf1y\x0e\x85v\xfcB0\xf3\x82\x09\ +\x03@8\x04D\x98\x14\xc1\xa0\xb8\xc4\x80\xc3A\xc7\x02\ +\xc0I\x19\xa5\x88\xa0`\x0a\x00\xcd`\xd4\x000\x03\xc4\ +\x80Y\x85z\x05\x81\xd5*!\xca\xdd\x8a\xcf!\x0e\xd5\ +\x1c\x0d\xe7\x0ep^\xa4rfd\xeeF\xbc\x8aR\xbc\ +\xc2\x0a\xfa\xc0\x98\x0b\xce\x12\x10\x01BH\x92(\x0d!\ +Y-\xa10o\x84\x00\x0a`\xce\x0f2\xe9-\xe4\x86\ +\x0fR\xf0\x0b\x01u/aR \x8c\xbf*\xa0d\x80\ +\xb2\x12\x9aq*\xbb\x92\xb7\x062 \xdfJ\x00\xee\x82\ +\x17,\xe1?,\x80\xc0H\x09\xdc\xd9@\xaa\x05A\xef\ +2\xe1\xeaRj\x16\x01r\xda\x15\x81\xae\x9b\xc0.\x03\ +\xa4\x80\xdb\xc1`\x13m\xb6\x0c\xc2\x104 \x1e\x02g\ +F\xe3\x13\x02\xb22a\x05\xcf-\x06\x10\xeb\x1b\xc2\x11\ +\x222\xc4!\x11\xa6\x01-\xa8\x13\xa1\x8a\x04\xf3\x80\x06\ +$\x80\x93\xa1p\x14\xb1f\x0b\x85&\xdbaJ\x09\x13\ +\x98\x0bD\x80\x8d\x01\xb0\x19\xb1\x04\x0c@v\xa3\x22\x0e\ +\xf6\xc7\xce\xff\xd1w6Q/6\x92\xbav\x89be\ +\x88^\xdf\x82\x19)@:\x13\xf3\xd0\x19\x0c<\x03D\ +\x80p@\xd6\xd5\x81V\xd5\xc4\xc0W\xa0\xd8\x0e\xb3\xec\ +\x12d\x81*!\xd4\x1c\xa5\x1a\x0c\x00o?A\xca\xdd\ +f\xbb\x122\xad\x0e\x13\x08VS\x0d6r\xb8\xcc\xf0\ +\xa7&\xb1;\x06\xc9l\x223\x80\x04\xe0`B!6\ +\x18o\x94D\xc2\xa5\x01\x81\x22\x0e\x8c\xf2\x11\xc4\x81$\ +@\xbc\x0e\xaf\xd0\xf3B@\xb9A\xec\x1ef6\xd8\x82\ +\x1d5@'\x052Z\xd8*AA3\xbbAlo\ +\x1c\xf0\x10\x1fNJ{\x821\x01\xe0\x92\xfe\xc1g\x1e\ +DQ3\xa1,\x12t\x8c\x0f\x0d\xd2%\x8d\xa4\x87\xe1\ +\x14\x0aT\x9c\x0c\xe4\x80<\x01\xfb\x0b\xe0\x98\xb0\xca|\ +\x22k\x1d\x16\xf0f!\xb1u\x00\x12\xb5AJ\xe0[\ +2\xbf\x18s\x18#I\x16\x09@\xb9,\xe1AHD\ +N\x1d\x94\xdc\x1c\xeer\x12\x80\xf38\x81L\xfe\x225\ +(\xc0\x0d9\x80\x90\x0bG$?@$\x02\xf4\xa22\ +\x01\xfb,\xe0\xc0\x16\xd5\x0c\x14\x821%\x8b!%\xc7\ +XY\xf1-\x1cq1A\x90\xecH\xe70\x1a\xb5,\ +\x19BAO@\xb3%!\x04\x14t\xd8H\x0e\x8f\x1a\ +\x01\x80\x16\x0c\xb6\x18\xe1p\x9d\xd3\xf4\x1c\x82\x08\xc4\x92\ +\x94\x03\xd4}\x19@\xa0\xf8\x80 H\x94\xa4\x0f\xd5l\ +\x0b\xb3\x88\x14\xecZt/\xb4\x9e\x93l \xef'Q\ +\xd4iR\x13\xbdRU~\xc0\xd3\x14\xc1\x02?\x22\xe0\ +\xa8\x10\xb5\x9c\x14\xe9\x80\xfe\x84P\x98k\x12\x0f`\xb4\ +\x17\x95\xb0\x15b@\xed\x14b\xf2LdV2\x16\x22\ +\x12\x1a\xae.\x9dX\xe2\x0aw\x15+R\xe2Q\x0c\x80\ +\xa0K\x01\x0c\x14\xec\x89ZB^w\x15\xaa\x0b,\x98\ +$\x93\xb0\xd8\x15\xbc}M\x86\xa9Qz\x11\x12h\x22\ +\xf2l%`S`\xa0f\x835[^BQ(\x13\ +\xec\x0e\xa0\xa4\xd1M\x18\xb5\xf4\x06Gm\xdf\x5c\xc2\x09\ +\x09\xd5\xc1K\xf4k1\x16, p\xeey\xb5\x94%\ +G\xc3\x22\x81S\x08`h\x08V\x14#\xe1\x9bea\ +\x80\x0f\x16\x5c\x0a\xc1\xdff!\xd6\xb1\xa6\xbbW\xb5\x16\ +#O\xba\xec\x95\x1f!\x95#`\x22,\x96S\xc7L\ +\xa2b6\x00\x06r@\xb9h\xe0\xe9e\x22-'a\ +\x1e\xf5!$\x0e\xc8X\x1f\x82aE\xd4af\xf5\xf6\ +\x1e0Z\x9fD@\xb2\xee\xe5Y3\xc8D\xb4|\xb5\ +A$\xe6\xf6\x94 \x90&\x1b\xaa\x82\x0d\xf0\xb8\x18\x81\ +j\xa7\xe7C\x16\xd1\xbbKb\x19c\x00}\x5c\x22\x1e\ +\xed\xf3\xbe\x0d\xd4\xc7\x1dO:H\x14\xf0\xf4J\x10l\ +\xea\x14Djk2\xe1\xee\x1e\xb3\xd0\x13\xe1\x0c\xcf*\ +0H\xe4\x81j\x88\x02\x15\xf0T\x22\xd6\xe9n\xc2\x1c\ +\xbb\xd4\xc3\x13T\x1c\xbck\xca\x88\xe4\xf4\x91\x006N\ +\x80\xde\x11`\x8ft\xa0\xb2\x7fr\xf6\x17ASC\x80\ +\xe8\xc3UTL\x0d\xd8\x15h\xec\x07\xcf5\x12\x94\x10\ +\xe3\xb5\xffF\xe20\xea%\x00\xeanLUV\x0a\x05\ + f~`\x8bx\xa0\xaeP\xe0\x0eL#\xee\x17w\ +\x98\x15!Qy\xe1%b\x05'[\x96\xad@\xce6\ +\xbbwp\xb6\xe5Ek\x8f\xc62f7R\xc1\xabS\ +\x07<\x86\x97J\x08\xe0\xb1]\x97gZ\x22V\x98a\ +\x9f}\xa1\x87TAau!P\x1d\xd7\xe8\x1dIG\ +W\x87\xc70\x13\x04#/\xbb:`v\xf7k+{\ +f\xe3+\xf7;B(b\x8bIp\x07\x14&\x06$\ +z\x06\x13\xcc\xf8\x80#VB\xbeU\xa8\xc032\x80\ +\x8c\xa1\xb2\x19\xe7\xda\x1a\x0b|\xe8\xe8c_.4\x1e\ +\x07\xb3E\x92\xb7\x1c\xb3\x13\x1d-\xf7hV\xcb\x85b\ +7*\x91%r\x83-Fw\xb30\xf3knO\x81\ +k\xd8U\x85\x98t\x22\xd7\xa9\x11W\xac\x1e\x11\xc3c\ +U\x89F\xd75`Xqo\xb8w\x89\x22%\x84\x15\ +\x19\x86H\x15\x86\x96\xf3\x80\x92\xc3s\xd8\x95\x8a\xb8\x97\ +\x7f\x0c\xc1@\xb1'+\x0e\xd9\x88Vx\x95\xc6J\xb7\ +4q\x06\x8d9\x01Ty\x8a\xd8\xd0!\xd2\xa9b\xb8\ +l!U\x82u\xd8f\x22\x970Z\xf6|\x22\xb6\x80\ +\x7f\x16C\x8d8\xf4 \x98]\x8bXa%\xf63&\ +2\xb6\xe9\xad1\x84\xf2\xc1\x13\xd8\xa9\x8fy\x14 \xb8\ +\x99FO)Xx\xbe\x84\xe6H~y\x0d\x80\xb9\x17\ +\x92\xe2\x06\xa8\x17\x22\xff\xa27g*\xd9\x92\x15\xc5R\ +7t\x22\xe9e\x22\x80\xd4FA\xbeF\x82\xe8.\xe3\ +v^B\xb2_C\x19\x96\x06p+b\xbfJT\x84\ +/\x02\xd5\x96\x83zic\x96;Y^6\xa2\x86`\ +\x09\x81\x97B\x1c+#\x0ciF\x9c\x1f\xc1\xfb\x97\xe3\ +\x1cjf\x04-#j \xe3\x94\x1f\xc3\x8c9\xa2\xb7\ +\x96b\x87\x82\x00\xd7\x9ba\x0b\x81\xaa\x8f\x89\xcf\xc3+\ +\xc2>\x96F\x0ak&\x08j\xa3\x14;\x85\xfc!\x86\ +\xa2_\xa5\xe4\x5c\xecpj\xc2\x0b\x9eFl_\xd9\xe0\ +^&\x7f\x9d4\xb7\x9eFy\x9c\xe2\x84]\x82\xcf\x95\ +e\xda^\x06\x1fD\xc2\x07r\xb8\xbd\x945\x8b\x94y\ +1\xa1\xa4\xf7\xa1\x19\x05\x8eD5s1\x7f\xa1\xda,\ +O.\x8e}vw\xa1@\x12\x0dZ<\x10\x97\x01\xa2\ +\xfaDO7\xe8\x1d\xc1\xd4\xe5`\x84S\x81\xbc\xb5\xcb\ +\xbaC \x0fh\xe0\xb8\x0e\x91\xe8\x10z\x0d\xa4zl\ +$\xbaT\x1a\xd7D\x09N\xfc\x1c\x220\x98\x00\x83\xa8\ + \xa3\x04\xc1\x02\x140\xb5\xa6\xfa\x90AC\xcdN\x88\ +\xe6\x0d\x87V#\x02\x9f\x0458\x14L\xde\x06\xda\x93\ +\xaa\xe4\x10\xd6\x81\xda\x1d\x19L\x18:\xbc\x16T\xa4#\ +\x86B\x97\xa0\xa9\x0b\xe1\x19O\xc0-\xab\x1a\xd4$\xe7\ +W'a \x15:\xe0\x12a\xe3\xaea\xda$\x87\xfc\ +\x02Q\x94\x09\xd0\x0a\xda\xe0F\x05r\x8dy\x22'\x9d\ +\xb9\xd9\x9dC\xac^\x19\xeb\xb0\xa3\x93\x9e\xfb\x0f\xb0c\ +\xb3\x9d\xa3\xc5\x9f\xc3\xac\x9e\xb9\xef\x95\xbb\x1e_\x99\xca\ +!Y\xf2a\xbb*_\xe6j)k\xe2\x1c;@\x1b\ +1\xde\x16!=[\x01x\x15zJ\x1d\x22P)\xe6\ +B\xdch\xa8\x06PCVB\x9fN\xd9\xdc`e\xf9\ +\x9d\xb9\xe4gC\xb0h\x05\xfd\x9d\xa6\x8a2\x02\x08g\ +\x14\xa5\xb2Y\xd0a\xa2\xa4j\x86\x1a`\x1a\x00X\xe0\ +\x02<\xdb\x8f\x99\xe2\x05\x99\xc2\xdf\xb7\x99\x5c8\x1bu\ +\x8c`\x01\xb7\xe1\xfd\xb2b\x0eJe\xd4\x1f\xbb\x82@\ +\xb0\xd0\x1f\x18(\xb4\xe1\xb9\xa7)@\x1c7\x0c\x1e\xbb\ +.$\xa2\x9e\x98\x06\x1f\xb9\x03\xb4\x22\x99\xf8A\x1b\xb8\ +H[\xea$[\xf2K\xc3\xcc\x98t\xa5\xbd\xba\xd7\xc0\ +<\x05\xc0|\x09\xc0\xbc\x0d\xc0\xe2\x08 \x00\x00\x03\ +\x00\x01\xa0\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\xa0\x04\ +\x00\x01\x00\x00\x00`\x00\x00\x00\x03\xa0\x04\x00\x01\x00\x00\ +\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00Adobe\ + Photoshop Docum\ +ent Data Block\x00M\ +IB8nrTM\x00\x00\x00\x00MIB8r\ +yaL\xdc\x19\x00\x00\x01\x00\x03\x00\x00\x00\x03\x00\x00\ +\x00]\x00\x00\x00\x5c\x00\x00\x00\x04\x00\xff\xff\x9b\x0b\x00\ +\x00\x00\x00C\x04\x00\x00\x01\x00C\x04\x00\x00\x02\x00C\ +\x04\x00\x00MIB8mron\xff\x00\x08\x00<\ +\x01\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x00\x00\xff\xff\x00\ +\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\ +\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\ +\x00\xff\xff\x07Layer 0MIB8i\ +nul\x14\x00\x00\x00\x07\x00\x00\x00L\x00a\x00y\ +\x00e\x00r\x00 \x000\x00\x00\x00MIB8r\ +snl\x04\x00\x00\x00ryalMIB8d\ +iyl\x04\x00\x00\x00\x03\x00\x00\x00MIB8l\ +blc\x04\x00\x00\x00\x01\x00\x00\x00MIB8x\ +fni\x04\x00\x00\x00\x00\x00\x00\x00MIB8o\ +knk\x04\x00\x00\x00\x00\x00\x00\x00MIB8f\ +psl\x04\x00\x00\x00\x00\x00\x00\x00MIB8r\ +lcl\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00M\ +IB8dmhsH\x00\x00\x00\x01\x00\x00\x00M\ +IB8tsuc\x00\x00\x00\x004\x00\x00\x00\x10\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x08\x00\x00\x00met\ +adata\x01\x00\x00\x00\x09\x00\x00\x00lay\ +erTimebuod\xc2\x93A\xe0\xf78\ +\xd6A\x00MIB8prxf\x10\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\ +\x00S\x00O\x00\x0f\x00\x0c\x00\x0a\x00\x09\x00\x09\x00\x09\ +\x00\x09\x00\x15\x00I\x00K\x00\x12\x00\x14\x00\x14\x00\x12\ +\x00\x13\x00\x13\x00\x13\x00#\x00!\x00\x1e\x00\x1d\x00\x1f\ +\x00\x1d\x00\x1d\x00\x1d\x00\x1c\x00\x1e\x00\x1d\x00(\x00'\ +\x00&\x00&\x00&\x00%\x00$\x00$\x00\x22\x00 \ +\x00 \x00\x1e\x00\x22\x00\x1f\x00\x1e\x00\x1e\x00\x1f\x00!\ +\x00 \x00 \x00#\x00!\x00%\x00%\x00$\x00'\ +\x00(\x00(\x00+\x00\x1d\x00\x1d\x00\x1d\x00\x1d\x00\x1c\ +\x00\x1d\x00\x1e\x00\x1e\x00\x1f\x00 \x00#\x00\x13\x00\x13\ +\x00\x13\x00\x12\x00\x14\x00\x12\x00\x13\x00\x13\x00M\x00L\ +\x00\x09\x00\x09\x00\x08\x00\x09\x00\x0a\x00\x0a\x00\x0c\x00L\ +\x00W\x00 \x00\xfd\x00\x04\x05\x11!-1\xfe/\xfc\ +0\x181//0110010/1/0\ +010/0110/01\xfe0\x0a/2\ +1100/00//\xfe1\x000\xfe1\x05\ +010/01\xfe0\xfe1\xff0\x081/0\ +/-'\x18\x08\x01\xfe\x00\xff\x00\x07\x01\x14X\xab\xdb\ +\xec\xf0\xf0\xfd\xf1\x00\xf0\xfe\xf1\x00\xf2\xfd\xf1\xff\xf0\x01\ +\xf1\xf0\xf8\xf1\x03\xf0\xf1\xf1\xf0\xfe\xf1\xfe\xf0\x07\xf1\xf0\ +\xf0\xf1\xf1\xf0\xf1\xf0\xfc\xf1\xff\xf0\x1b\xf1\xf0\xf0\xf1\xf2\ +\xf1\xf0\xf1\xf0\xf1\xf1\xf2\xf0\xf1\xf0\xf1\xf0\xf0\xf1\xf0\xee\ +\xe4\xc2|.\x06\x00\x00\xff\x00\x03\x16\x86\xed\xfd\xb3\xff\ +\x04\xf9\xbfA\x06\x00\x03\x00\x08l\xf4\xb0\xff\x03\xfe\xbd\ +*\x01\x02\x00$\xd0\xae\xff\x02\xf8x\x07\x02\x02O\xf6\ +\xad\xff\x01\xc0\x14\x02\x05r\xfd\xad\xff\x01\xe1#\x02\x08\ +\x86\xfe\xad\xff\x01\xed+\x02\x08\x8f\xfe\xad\xff\x01\xef-\ +\x02\x09\x91\xfe\xf1\xff\x00\xfe\xdb\xff\xfe\xfe\xfd\xff\x00\xfe\ +\xec\xff\x01\xef,\x02\x08\x91\xfe\xfa\xff\x17\xfe\xcf\xc2\xc3\ +\xc2\xc4\xc3\xc4\xc3\xc3\xc4\xc4\xc1\xc3\xc3\xc2\xc3\xc2\xc2\xc3\ +\xc2\xc3\xc4\xc4\xfe\xc2\xfd\xc3\x0a\xc2\xc3\xc3\xc2\xc3\xc3\xc2\ +\xc3\xc2\xc2\xc1\xfe\xc3\xff\xc4\xf9\xc3\x06\xc4\xc3\xc3\xc4\xc2\ +\xc3\xc4\xfe\xc3\xff\xc2\x01\xc7\xf1\xf9\xff\x01\xf0-\x02\x09\ +\x90\xfe\xfa\xff\x03\xfaK\x18\x17\xfc\x18\x16\x17\x18\x16\x19\ +\x17\x18\x19\x19\x18\x19\x18\x17\x18\x19\x19\x18\x17\x17\x19\x17\ +\x17\x19\x17\xfb\x18\x11\x17\x16\x18\x16\x19\x19\x17\x18\x18\x19\ +\x18\x18\x19\x17\x19\x18\x19\x18\xfd\x19\x08\x18\x19\x19\x18\x19\ +\x19\x17,\xc7\xf9\xff\x01\xf0,\x01\x09\x90\xf9\xff\x01\xf9\ +8\xc0\x00\x01\x15\xc0\xf9\xff\x01\xef.\x02\x09\x91\xfe\xfa\ +\xff\x01\xf97\xc0\x00\x02\x14\xc2\xfe\xfa\xff\x01\xf0-\x02\ +\x09\x90\xfe\xfa\xff\x01\xf88\xc0\x00\x02\x14\xc2\xfe\xfa\xff\ +\x01\xef/\x01\x08\x90\xf9\xff\x01\xf99\xc0\x00\x01\x14\xc1\ +\xf9\xff\x01\xf0/\x02\x09\x90\xfe\xfa\xff\x01\xf97\xc0\x00\ +\x01\x12\xc0\xf9\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\x01\xf9\ +8\xc0\x00\x01\x14\xc2\xf9\xff\x01\xf1-\x02\x09\x90\xfe\xfa\ +\xff\x01\xfa8\xc0\x00\x01\x15\xc1\xf9\xff\x01\xef-\x01\x09\ +\x92\xf9\xff\x01\xf97\xe7\x00\x0d\x1aU\x87\xb8\xd5\xe6\xf7\ +\xf7\xe6\xd6\xb9\x88V\x1a\xe8\x00\x01\x15\xc2\xf9\xff\x01\xef\ +,\x02\x09\x91\xfe\xfa\xff\x01\xf98\xea\x00\x03\x1bw\xc7\ +\xfe\xf5\xff\x03\xfe\xc9x\x1d\xeb\x00\x01\x14\xc2\xf9\xff\x01\ +\xf0/\x01\x09\x90\xf9\xff\x01\xfa7\xec\x00\x028\xa8\xfb\ +\xef\xff\x02\xfb\xaa:\xed\x00\x01\x15\xc2\xf9\xff\x01\xef-\ +\x01\x09\x91\xf9\xff\x01\xf87\xee\x00\x01\x1c\xaa\xe9\xff\x01\ +\xac\x1e\xef\x00\x02\x14\xc1\xfe\xfa\xff\x01\xef/\x02\x08\x90\ +\xfe\xfa\xff\x01\xf98\xf0\x00\x02\x06x\xf4\xe7\xff\x02\xf5\ +z\x07\xf1\x00\x01\x15\xc2\xf9\xff\x01\xf0-\x02\x09\x91\xfe\ +\xfa\xff\x01\xf97\xf1\x00\x01F\xd9\xe3\xff\x01\xdbH\xf2\ +\x00\x01\x14\xc3\xf9\xff\x01\xf0/\x02\x09\x91\xfe\xfa\xff\x01\ +\xfa7\xf3\x00\x01\x02\x85\xdf\xff\x01\x88\x02\xf4\x00\x01\x15\ +\xc1\xf9\xff\x01\xf1-\x02\x09\x92\xfe\xfa\xff\x01\xf88\xf4\ +\x00\x01\x10\xb5\xdd\xff\x01\xb7\x10\xf5\x00\x01\x15\xc2\xf9\xff\ +\x01\xf0.\x01\x09\x90\xf9\xff\x01\xf98\xf5\x00\x01*\xda\ +\xdb\xff\x01\xdb+\xf6\x00\x01\x15\xc2\xf9\xff\x01\xf0.\x02\ +\x09\x90\xfe\xfa\xff\x01\xf99\xf6\x00\x01C\xf2\xd9\xff\x01\ +\xf3E\xf7\x00\x02\x15\xc3\xfe\xfa\xff\x01\xef-\x01\x08\x91\ +\xf9\xff\x01\xf97\xf7\x00\x01D\xf6\xd7\xff\x01\xf7E\xf8\ +\x00\x02\x14\xc2\xfe\xfa\xff\x01\xf0/\x02\x09\x91\xfe\xfa\xff\ +\x01\xf98\xf8\x00\x01F\xf7\xf0\xff\x07\xe5\x91H*\x0d\ +\x0c&\xe3\xee\xff\x01\xf7F\xf9\x00\x01\x14\xc1\xf9\xff\x01\ +\xf0.\x02\x09\x91\xfe\xfa\xff\x01\xf98\xf9\x00\x01G\xf7\ +\xf1\xff\x02\xe0S\x02\xfb\x00\x00\xdb\xed\xff\x01\xf7G\xfa\ +\x00\x01\x14\xc1\xf9\xff\x01\xf0.\x02\x09\x91\xfe\xfa\xff\x01\ +\xf99\xfa\x00\x015\xf5\xf1\xff\x01\x8c\x09\xf9\x00\x00\xdb\ +\xec\xff\x01\xf54\xfb\x00\x01\x14\xc2\xf9\xff\x01\xf1.\x02\ +\x09\x92\xfe\xfa\xff\x01\xf99\xfb\x00\x01\x1e\xe7\xf2\xff\x01\ +\xfdj\xf7\x00\x00\xdb\xeb\xff\x01\xe7\x1d\xfc\x00\x01\x15\xc1\ +\xf9\xff\x01\xf0/\x02\x08\x93\xfe\xfa\xff\x01\xf98\xfc\x00\ +\x01\x0e\xd3\xf1\xff\x00d\xf6\x00\x00\xdb\xea\xff\x01\xd1\x0d\ +\xfd\x00\x02\x15\xc1\xfe\xfa\xff\x01\xef.\x02\x09\x91\xfe\xfa\ +\xff\x01\xf98\xfd\x00\x01\x01\xb4\xf1\xff\x00\x9f\xf5\x00\x00\ +\xdb\xe9\xff\x01\xb1\x01\xfe\x00\x01\x14\xc1\xf9\xff\x01\xef-\ +\x01\x08\x8f\xf9\xff\x01\xf99\xfd\x00\x00|\xf1\xff\x01\xdf\ +\x0a\xf5\x00\x00\xdb\xe8\xff\x00w\xfe\x00\x02\x15\xbf\xfe\xfa\ +\xff\x01\xee.\x02\x09\x92\xfe\xfa\xff\x01\xf89\xfe\x00\x01\ +<\xfc\xf1\xff\x00d\xf4\x00\x00\xdb\xe8\xff\x05\xfb7\x00\ +\x00\x14\xc2\xf9\xff\x01\xef.\x01\x09\x91\xf9\xff\x05\xf98\ +\x00\x00\x0e\xe0\xf1\xff\x01\xe0\x05\xf4\x00\x00\xdb\xe7\xff\x04\ +\xda\x0a\x00\x14\xc1\xf9\xff\x01\xf0-\x02\x09\x90\xfe\xfa\xff\ +\x04\xf98\x00\x00\x8d\xf0\xff\x00\x89\xf3\x00\x00\xdb\xe6\xff\ +\x03\x81\x00\x14\xc1\xf9\xff\x01\xef,\x02\x09\x90\xfe\xfa\xff\ +\x04\xf97\x00\x10\xf3\xf0\xff\x00@\xf3\x00\x00\xdb\xe6\xff\ +\x03\xee\x0c\x15\xc2\xf9\xff\x01\xf1.\x01\x09\x91\xf9\xff\x03\ +\xf98\x00v\xf0\xff\x01\xf6\x05\xf3\x00\x00\xdb\xe5\xff\x02\ +q\x14\xc3\xf9\xff\x01\xf1.\x02\x09\x92\xfe\xfa\xff\x03\xf9\ +9\x00\xb5\xf0\xff\x00\xdc\xf2\x00\x00\x22\xf7'\x00\xd9\xf0\ +\xff\x02\xb2\x13\xc2\xf9\xff\x01\xf0.\x02\x09\x91\xfe\xfa\xff\ +\x03\xf97\x00\xd9\xf0\xff\x00\xc4\xe7\x00\x00\xbe\xf0\xff\x03\ +\xd7\x15\xc2\xfe\xfa\xff\x01\xf0.\x02\x09\x91\xfe\xfa\xff\x03\ +\xf98\x00\xf6\xf0\xff\x00\xb3\xe7\x00\x00\xaf\xf0\xff\x02\xf6\ +\x14\xc1\xf9\xff\x01\xef.\x02\x09\x91\xfe\xfa\xff\x03\xf88\ +\x00\xdf\xf0\xff\x00\xc6\xe7\x00\x00\xc2\xf0\xff\x02\xde\x15\xc1\ +\xf9\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\x03\xf97\x00\xbb\ +\xf0\xff\x00\xdd\xe7\x00\x00\xd9\xf0\xff\x03\xb9\x14\xc2\xfe\xfa\ +\xff\x01\xf1.\x02\x09\x91\xfe\xfa\xff\x03\xf98\x00\x88\xf0\ +\xff\x01\xf7\x06\xe9\x00\x01\x05\xf5\xf0\xff\x03\x84\x15\xc2\xfe\ +\xfa\xff\x01\xef.\x02\x09\x90\xfe\xfa\xff\x04\xf98\x00\x1f\ +\xfc\xf0\xff\x00B\xe9\x00\x00?\xf0\xff\x03\xf9\x1a\x14\xc1\ +\xf9\xff\x01\xef.\x02\x09\x92\xfe\xfa\xff\x04\xf97\x00\x00\ +\xa9\xf0\xff\x00\x8b\xe9\x00\x00\x89\xf0\xff\x03\x9e\x00\x15\xc2\ +\xf9\xff\x01\xf0.\x01\x09\x91\xf9\xff\x05\xf98\x00\x00\x22\ +\xf2\xf1\xff\x01\xe2\x06\xeb\x00\x01\x06\xe1\xf1\xff\x04\xef\x1b\ +\x00\x15\xc1\xf9\xff\x01\xef.\x01\x09\x91\xf9\xff\x01\xf97\ +\xfe\x00\x00^\xf0\xff\x00h\xeb\x00\x00g\xf0\xff\x04W\ +\x00\x00\x14\xc0\xf9\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\x01\ +\xf98\xfd\x00\x00\x9f\xf1\xff\x01\xe1\x0b\xed\x00\x01\x0b\xe2\ +\xf1\xff\x00\x9a\xfe\x00\x01\x13\xc0\xf9\xff\x01\xf0.\x02\x09\ +\x91\xfe\xfa\xff\x01\xf97\xfd\x00\x01\x09\xce\xf1\xff\x00\xa4\ +\xed\x00\x00\xa5\xf1\xff\x01\xcc\x07\xfe\x00\x01\x14\xc1\xf9\xff\ +\x01\xf1.\x01\x09\x91\xf9\xff\x01\xfa8\xfc\x00\x01\x1d\xe5\ +\xf1\xff\x00j\xef\x00\x00l\xf1\xff\x01\xe4\x1b\xfd\x00\x01\ +\x15\xc1\xf9\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\x01\xf97\ +\xfb\x00\x010\xf3\xf2\xff\x01\xfer\xf1\x00\x01u\xfe\xf2\ +\xff\x01\xf2/\xfc\x00\x01\x13\xc2\xf9\xff\x01\xf0-\x02\x09\ +\x91\xfe\xfa\xff\x01\xf99\xfa\x00\x01I\xfc\xf1\xff\x01\x95\ +\x0d\xf5\x00\x01\x0e\x98\xf1\xff\x01\xfcH\xfb\x00\x02\x15\xc2\ +\xfe\xfa\xff\x01\xf1.\x01\x09\x92\xf9\xff\x01\xf98\xf9\x00\ +\x01\x5c\xfc\xf1\xff\x02\xe6_\x05\xf9\x00\x02\x06`\xe8\xf1\ +\xff\x01\xfc\x5c\xfa\x00\x01\x15\xc2\xf9\xff\x01\xf0-\x02\x09\ +\x91\xfe\xfa\xff\x01\xf86\xf8\x00\x01Y\xfc\xf0\xff\x09\xee\ +\x9eV8\x1c\x1c8V\x9f\xef\xf0\xff\x01\xfcY\xf9\x00\ +\x02\x14\xc1\xfe\xfa\xff\x01\xf0-\x02\x09\x91\xfe\xfa\xff\x01\ +\xf98\xf7\x00\x01T\xfb\xd7\xff\x01\xfbU\xf8\x00\x01\x14\ +\xc2\xf9\xff\x01\xf0.\x02\x09\x92\xfe\xfa\xff\x01\xf86\xf6\ +\x00\x01P\xf7\xd9\xff\x01\xf8Q\xf7\x00\x01\x14\xc1\xf9\xff\ +\x01\xf0/\x01\x09\x91\xf9\xff\x01\xf98\xf5\x00\x013\xe1\ +\xdb\xff\x01\xe24\xf6\x00\x02\x15\xc1\xfe\xfa\xff\x01\xf0.\ +\x02\x09\x91\xfe\xfa\xff\x01\xf88\xf4\x00\x01\x14\xbd\xdd\xff\ +\x01\xbf\x15\xf5\x00\x01\x15\xc2\xf9\xff\x01\xf1-\x01\x09\x91\ +\xf9\xff\x01\xfa8\xf3\x00\x01\x03\x8b\xdf\xff\x01\x8e\x04\xf4\ +\x00\x01\x13\xc2\xf9\xff\x01\xf0.\x01\x08\x90\xf9\xff\x01\xf9\ +8\xf1\x00\x01I\xdb\xe3\xff\x01\xdcK\xf2\x00\x02\x14\xc2\ +\xfe\xfa\xff\x01\xf0.\x01\x09\x91\xf9\xff\x01\xf98\xf0\x00\ +\x02\x06x\xf4\xe7\xff\x02\xf4z\x07\xf1\x00\x01\x13\xc2\xf9\ +\xff\x01\xf0.\x02\x08\x91\xfe\xfa\xff\x01\xf97\xee\x00\x01\ +\x1b\xa6\xe9\xff\x01\xa8\x1c\xef\x00\x02\x15\xc0\xfe\xfa\xff\x01\ +\xef-\x02\x09\x91\xfe\xfa\xff\x01\xf89\xec\x00\x023\xa1\ +\xf8\xef\xff\x02\xf9\xa24\xed\x00\x01\x14\xc2\xf9\xff\x01\xef\ +-\x01\x09\x90\xf9\xff\x01\xf96\xea\x00\x03\x15m\xbd\xfb\ +\xf5\xff\x03\xfb\xben\x16\xeb\x00\x01\x14\xc0\xf9\xff\x01\xf1\ +,\x01\x09\x90\xf9\xff\x01\xf97\xe7\x00\x0d\x11Iz\xaa\ +\xc7\xd8\xe8\xe9\xd8\xc8\xabzJ\x11\xe8\x00\x01\x14\xc1\xf9\ +\xff\x01\xef/\x02\x09\x90\xfe\xfa\xff\x01\xf98\xc0\x00\x01\ +\x15\xc1\xf9\xff\x01\xf0-\x02\x09\x91\xfe\xfa\xff\x01\xf98\ +\xc0\x00\x01\x14\xc2\xf9\xff\x01\xef.\x02\x09\x91\xfe\xfa\xff\ +\x01\xf98\xc0\x00\x01\x14\xc1\xf9\xff\x01\xf0-\x01\x09\x91\ +\xf9\xff\x01\xf97\xc0\x00\x01\x14\xc1\xf9\xff\x01\xef.\x02\ +\x09\x90\xfe\xfa\xff\x01\xf99\xc0\x00\x02\x15\xc1\xfe\xfa\xff\ +\x01\xf0-\x01\x08\x91\xf9\xff\x01\xfa8\xc0\x00\x01\x15\xc1\ +\xf9\xff\x01\xf0.\x02\x09\x92\xfe\xfa\xff\x01\xf97\xc0\x00\ +\x01\x14\xc1\xf9\xff\x01\xf0-\x02\x09\x90\xfe\xfa\xff\x01\xf9\ +7\xc0\x00\x01\x14\xc3\xf9\xff\x01\xf1-\x02\x09\x91\xfe\xfa\ +\xff\x02\xfad;\xfe:\xfe9\x1589;;9:\ +:8;9:7:9:;99;:98\ +\xfd:\xff8\x017;\xfc9\x09;:9;;8\ +:979\xfe:\xff9\x09:89::8;\ +9J\xcf\xf9\xff\x01\xf0-\x01\x09\x91\xf8\xff\x00\xfa\xfd\ +\xf9\x08\xf8\xf9\xf9\xf8\xf9\xf8\xf8\xf9\xf8\xfd\xf9\x06\xfa\xf9\ +\xf9\xf8\xf9\xf8\xf8\xfe\xf9\x00\xf8\xfe\xf9\x0e\xf8\xf9\xf9\xf8\ +\xf9\xf8\xf9\xf8\xf8\xf9\xf8\xf9\xf9\xf8\xf8\xfd\xf9\x00\xf8\xfe\ +\xf9\x05\xf8\xf9\xf9\xf8\xfa\xfa\xfe\xf9\xff\xf8\x01\xf9\xfe\xf9\ +\xff\x01\xf0.\x02\x09\x91\xfe\xad\xff\x01\xf0.\x02\x08\x91\ +\xfe\xad\xff\x01\xee-\x01\x09\x8d\xac\xff\x01\xe9+\x02\x07\ +{\xfe\xad\xff\x01\xd5\x1f\x02\x04U\xf6\xae\xff\x02\xfd\xa3\ +\x0f\x02\x01'\xc9\xae\xff\x02\xe7Q\x03\x03\x00\x09]\xe9\ +\xb0\xff\x03\xf0\x80\x13\x00\xff\x00\x03\x12`\xcc\xf7\xfb\xfe\ +\x00\xff\xfc\xfe\x00\xff\xfe\xfe\x02\xff\xfe\xff\xfe\xfe\x05\xff\ +\xfe\xfe\xff\xfe\xff\xfd\xfe\x0a\xff\xfe\xff\xfe\xfe\xff\xfe\xff\ +\xff\xfe\xfe\xfc\xff\xff\xfe\xfe\xff\x04\xfe\xff\xfe\xff\xff\xfb\ +\xfe\xff\xff\xff\xfe\x02\xff\xfe\xff\xfc\xfe\x06\xfd\xf5\xcel\ +\x17\x00\x00\xff\x00\x08\x01\x0a.`\x86\x96\x97\x92\x92\xfe\ +\x91\xff\x92\x13\x93\x92\x92\x91\x92\x92\x90\x91\x92\x91\x90\x91\ +\x92\x91\x91\x93\x91\x91\x94\x91\xfc\x92\x05\x91\x92\x91\x91\x90\ +\x92\xfe\x91\x0f\x90\x92\x92\x93\x91\x90\x92\x92\x90\x92\x91\x90\ +\x91\x92\x92\x90\xfd\x91\x03\x93\x91\x90\x90\xfe\x91\x0b\x93\x90\ +\x90\x92\x8ayU+\x0c\x01\x00\x00\xfd\x00\x04\x01\x05\x0a\ +\x0c\x0b\xfb\x0a\x00\x09\xf2\x0a\xff\x09\xf0\x0a\x00\x09\xf6\x0a\ +\x00\x09\xee\x0a\x02\x08\x04\x01\xfd\x00\x01\x00\x02\x00\x02\x00\ +\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\ +\x02\x00\x02\x00\x02\x00\x02\x00\x06\x00\x06\x00\x06\x00\x06\x00\ +\x06\x00\x11\x00\x10\x00\x0c\x00\x0c\x00\x0e\x00\x0c\x00\x0f\x00\ +\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x12\x00\x11\x00\x14\x00\x10\x00\ +\x12\x00\x14\x00\x12\x00\x0e\x00\x0f\x00\x0f\x00\x10\x00\x0e\x00\ +\x0a\x00\x06\x00\x08\x00\x08\x00\x06\x00\x08\x00\x10\x00\x11\x00\ +\x14\x00\x16\x00\x16\x00\x15\x00\x14\x00\x14\x00\x17\x00\x14\x00\ +\x16\x00\x0c\x00\x0a\x00\x0e\x00\x0f\x00\x0f\x00\x0c\x00\x0e\x00\ +\x0e\x00\x0e\x00\x10\x00\x13\x00\x06\x00\x06\x00\x06\x00\x06\x00\ +\x06\x00\x06\x00\x06\x00\x06\x00\x02\x00\x02\x00\x02\x00\x02\x00\ +\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\ +\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\ +\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xf4\xcc\xc1\xff\ +\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\ +\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xe8\x00\x03\xcd\ +\xcc\xcc\xcd\xf8\xcc\x00\xcd\xe8\x00\xf5\xcc\xf4\xcc\xeb\x00\x02\ +\xcf\xcd\xcd\xf1\xcc\x00\xd3\xeb\x00\xf5\xcc\xf4\xcc\xed\x00\xea\ +\xcc\x00\xce\xed\x00\xf5\xcc\xf4\xcc\xef\x00\x00\xd1\xe6\xcc\xef\ +\x00\xf5\xcc\xf4\xcc\xf1\x00\x00\xd4\xe3\xcc\x00\xda\xf1\x00\xf5\ +\xcc\xf4\xcc\xf2\x00\xe0\xcc\x00\xcd\xf2\x00\xf5\xcc\xf4\xcc\xf4\ +\x00\x01\xff\xcd\xde\xcc\x00\xff\xf4\x00\xf5\xcc\xf4\xcc\xf5\x00\ +\x00\xcf\xdb\xcc\x00\xcf\xf5\x00\xf5\xcc\xf4\xcc\xf6\x00\x00\xce\ +\xd9\xcc\x00\xcf\xf6\x00\xf5\xcc\xf4\xcc\xf7\x00\x00\xcd\xd7\xcc\ +\x00\xce\xf7\x00\xf5\xcc\xf4\xcc\xf8\x00\x00\xce\xd5\xcc\x00\xce\ +\xf8\x00\xf5\xcc\xf4\xcc\xf9\x00\xec\xcc\x04\xcd\xce\xd7\xd4\xd0\ +\xeb\xcc\xf9\x00\xf5\xcc\xf4\xcc\xfa\x00\xee\xcc\x01\xcd\xff\xfb\ +\x00\xea\xcc\xfa\x00\xf5\xcc\xf4\xcc\xfb\x00\x00\xce\xef\xcc\x00\ +\xe2\xf9\x00\xea\xcc\x00\xcd\xfb\x00\xf5\xcc\xf4\xcc\xfc\x00\xee\ +\xcc\xf7\x00\xe9\xcc\x00\xd3\xfc\x00\xf5\xcc\xf4\xcc\xfd\x00\x00\ +\xda\xef\xcc\xf6\x00\xe8\xcc\x00\xd7\xfd\x00\xf5\xcc\xf4\xcc\xfe\ +\x00\x00\xff\xf0\xcc\x00\xcd\xf5\x00\xe7\xcc\x00\xff\xfe\x00\xf5\ +\xcc\xf4\xcc\xfe\x00\x00\xcd\xef\xcc\xf5\x00\xe7\xcc\x00\xcd\xfe\ +\x00\xf5\xcc\xf4\xcc\xff\x00\xee\xcc\xf4\x00\xe5\xcc\xff\x00\xf5\ +\xcc\xf4\xcc\x01\x00\xda\xee\xcc\xf4\x00\xe4\xcc\x00\x00\xf5\xcc\ +\xf4\xcc\x00\x00\xee\xcc\xf3\x00\xe5\xcc\x01\xcd\x00\xf5\xcc\xf4\ +\xcc\x00\xcf\xef\xcc\x00\xcf\xf3\x00\xe4\xcc\x00\xd4\xf5\xcc\xf4\ +\xcc\x00\xcd\xee\xcc\xf3\x00\xe4\xcc\x00\xcd\xf5\xcc\xe1\xcc\xf2\ +\x00\x00\xd2\xf7\xd1\xe2\xcc\xe1\xcc\xe7\x00\xe2\xcc\xe2\xcc\x00\ +\xcd\xe7\x00\xe2\xcc\xe1\xcc\xe7\x00\x00\xcd\xe3\xcc\xe1\xcc\xe7\ +\x00\xe2\xcc\xe1\xcc\x00\xd4\xe9\x00\xe1\xcc\xf4\xcc\x00\xcd\xee\ +\xcc\xe9\x00\x00\xce\xef\xcc\x00\xcd\xf5\xcc\xf4\xcc\x01\x00\xcd\ +\xf0\xcc\x00\xcd\xe9\x00\xee\xcc\x00\x00\xf5\xcc\xf4\xcc\x01\x00\ +\xd2\xef\xcc\x00\xd4\xeb\x00\x00\xd4\xef\xcc\x01\xcf\x00\xf5\xcc\ +\xf4\xcc\xff\x00\x00\xce\xf0\xcc\x00\xcd\xeb\x00\x00\xcd\xf0\xcc\ +\x02\xcd\x00\x00\xf5\xcc\xf4\xcc\xfe\x00\x00\xcd\xf0\xcc\x00\xd0\ +\xed\x00\x00\xd0\xf0\xcc\x00\xcd\xfe\x00\xf5\xcc\xf4\xcc\xfe\x00\ +\x00\xe2\xf0\xcc\x00\xcd\xed\x00\xf0\xcc\x01\xcd\xda\xfe\x00\xf5\ +\xcc\xf4\xcc\xfd\x00\x00\xd3\xef\xcc\xef\x00\x00\xcd\xf0\xcc\x00\ +\xcf\xfd\x00\xf5\xcc\xf4\xcc\xfc\x00\x00\xcf\xf0\xcc\x00\xcd\xf1\ +\x00\xef\xcc\x00\xce\xfc\x00\xf5\xcc\xf4\xcc\xfb\x00\x00\xce\xf0\ +\xcc\x01\xcd\xd7\xf5\x00\x00\xda\xef\xcc\x00\xcd\xfb\x00\xf5\xcc\ +\xf4\xcc\xfa\x00\x00\xcd\xed\xcc\xf9\x00\x00\xd4\xee\xcc\x00\xcd\ +\xfa\x00\xf5\xcc\xf4\xcc\xf9\x00\x00\xce\xeb\xcc\xff\xd1\xff\xcc\ +\x00\xcd\xee\xcc\x00\xce\xf9\x00\xf5\xcc\xf4\xcc\xf8\x00\x00\xce\ +\xd4\xcc\xf8\x00\xf5\xcc\xf4\xcc\xf7\x00\xd5\xcc\xf7\x00\xf5\xcc\ +\xf4\xcc\xf6\x00\x00\xcd\xd9\xcc\x00\xcd\xf6\x00\xf5\xcc\xf4\xcc\ +\xf5\x00\x01\xcc\xcd\xdc\xcc\x00\xce\xf5\x00\xf5\xcc\xf4\xcc\xf4\ +\x00\x01\xff\xcd\xde\xcc\x00\xff\xf4\x00\xf5\xcc\xf4\xcc\xf2\x00\ +\x00\xce\xe0\xcc\xf2\x00\xf5\xcc\xf4\xcc\xf1\x00\x00\xd4\xe3\xcc\ +\x00\xda\xf1\x00\xf5\xcc\xf4\xcc\xef\x00\x00\xcf\xe7\xcc\x00\xd1\ +\xef\x00\xf5\xcc\xf4\xcc\xed\x00\x00\xcd\xeb\xcc\x00\xcd\xed\x00\ +\xf5\xcc\xf4\xcc\xeb\x00\x02\xce\xcd\xcd\xf1\xcc\x00\xd0\xeb\x00\ +\xf5\xcc\xf4\xcc\xe8\x00\x04\xd2\xce\xcc\xcc\xcd\xfa\xcc\x01\xce\ +\xd2\xe8\x00\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\ +\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\ +\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\ +\xcc\xc1\xff\xf5\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\ +\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\x01\x00\x02\ +\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\ +\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x06\x00\x06\x00\x06\ +\x00\x06\x00\x06\x00\x11\x00\x10\x00\x0c\x00\x0c\x00\x0e\x00\x0c\ +\x00\x0f\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x12\x00\x11\x00\x14\ +\x00\x10\x00\x12\x00\x14\x00\x12\x00\x0e\x00\x0f\x00\x0f\x00\x10\ +\x00\x0e\x00\x0a\x00\x06\x00\x08\x00\x08\x00\x06\x00\x08\x00\x10\ +\x00\x11\x00\x14\x00\x16\x00\x16\x00\x15\x00\x14\x00\x14\x00\x17\ +\x00\x14\x00\x16\x00\x0c\x00\x0a\x00\x0e\x00\x0f\x00\x0f\x00\x0c\ +\x00\x0e\x00\x0e\x00\x0e\x00\x10\x00\x13\x00\x06\x00\x06\x00\x06\ +\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x02\x00\x02\x00\x02\ +\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\ +\x00\x02\x00\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\ +\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xf4\ +\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\ +\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xe8\ +\x00\x03\xcd\xcc\xcc\xcd\xf8\xcc\x00\xcd\xe8\x00\xf5\xcc\xf4\xcc\ +\xeb\x00\x02\xcf\xcd\xcd\xf1\xcc\x00\xd3\xeb\x00\xf5\xcc\xf4\xcc\ +\xed\x00\xea\xcc\x00\xce\xed\x00\xf5\xcc\xf4\xcc\xef\x00\x00\xd1\ +\xe6\xcc\xef\x00\xf5\xcc\xf4\xcc\xf1\x00\x00\xd4\xe3\xcc\x00\xda\ +\xf1\x00\xf5\xcc\xf4\xcc\xf2\x00\xe0\xcc\x00\xcd\xf2\x00\xf5\xcc\ +\xf4\xcc\xf4\x00\x01\xff\xcd\xde\xcc\x00\xff\xf4\x00\xf5\xcc\xf4\ +\xcc\xf5\x00\x00\xcf\xdb\xcc\x00\xcf\xf5\x00\xf5\xcc\xf4\xcc\xf6\ +\x00\x00\xce\xd9\xcc\x00\xcf\xf6\x00\xf5\xcc\xf4\xcc\xf7\x00\x00\ +\xcd\xd7\xcc\x00\xce\xf7\x00\xf5\xcc\xf4\xcc\xf8\x00\x00\xce\xd5\ +\xcc\x00\xce\xf8\x00\xf5\xcc\xf4\xcc\xf9\x00\xec\xcc\x04\xcd\xce\ +\xd7\xd4\xd0\xeb\xcc\xf9\x00\xf5\xcc\xf4\xcc\xfa\x00\xee\xcc\x01\ +\xcd\xff\xfb\x00\xea\xcc\xfa\x00\xf5\xcc\xf4\xcc\xfb\x00\x00\xce\ +\xef\xcc\x00\xe2\xf9\x00\xea\xcc\x00\xcd\xfb\x00\xf5\xcc\xf4\xcc\ +\xfc\x00\xee\xcc\xf7\x00\xe9\xcc\x00\xd3\xfc\x00\xf5\xcc\xf4\xcc\ +\xfd\x00\x00\xda\xef\xcc\xf6\x00\xe8\xcc\x00\xd7\xfd\x00\xf5\xcc\ +\xf4\xcc\xfe\x00\x00\xff\xf0\xcc\x00\xcd\xf5\x00\xe7\xcc\x00\xff\ +\xfe\x00\xf5\xcc\xf4\xcc\xfe\x00\x00\xcd\xef\xcc\xf5\x00\xe7\xcc\ +\x00\xcd\xfe\x00\xf5\xcc\xf4\xcc\xff\x00\xee\xcc\xf4\x00\xe5\xcc\ +\xff\x00\xf5\xcc\xf4\xcc\x01\x00\xda\xee\xcc\xf4\x00\xe4\xcc\x00\ +\x00\xf5\xcc\xf4\xcc\x00\x00\xee\xcc\xf3\x00\xe5\xcc\x01\xcd\x00\ +\xf5\xcc\xf4\xcc\x00\xcf\xef\xcc\x00\xcf\xf3\x00\xe4\xcc\x00\xd4\ +\xf5\xcc\xf4\xcc\x00\xcd\xee\xcc\xf3\x00\xe4\xcc\x00\xcd\xf5\xcc\ +\xe1\xcc\xf2\x00\x00\xd2\xf7\xd1\xe2\xcc\xe1\xcc\xe7\x00\xe2\xcc\ +\xe2\xcc\x00\xcd\xe7\x00\xe2\xcc\xe1\xcc\xe7\x00\x00\xcd\xe3\xcc\ +\xe1\xcc\xe7\x00\xe2\xcc\xe1\xcc\x00\xd4\xe9\x00\xe1\xcc\xf4\xcc\ +\x00\xcd\xee\xcc\xe9\x00\x00\xce\xef\xcc\x00\xcd\xf5\xcc\xf4\xcc\ +\x01\x00\xcd\xf0\xcc\x00\xcd\xe9\x00\xee\xcc\x00\x00\xf5\xcc\xf4\ +\xcc\x01\x00\xd2\xef\xcc\x00\xd4\xeb\x00\x00\xd4\xef\xcc\x01\xcf\ +\x00\xf5\xcc\xf4\xcc\xff\x00\x00\xce\xf0\xcc\x00\xcd\xeb\x00\x00\ +\xcd\xf0\xcc\x02\xcd\x00\x00\xf5\xcc\xf4\xcc\xfe\x00\x00\xcd\xf0\ +\xcc\x00\xd0\xed\x00\x00\xd0\xf0\xcc\x00\xcd\xfe\x00\xf5\xcc\xf4\ +\xcc\xfe\x00\x00\xe2\xf0\xcc\x00\xcd\xed\x00\xf0\xcc\x01\xcd\xda\ +\xfe\x00\xf5\xcc\xf4\xcc\xfd\x00\x00\xd3\xef\xcc\xef\x00\x00\xcd\ +\xf0\xcc\x00\xcf\xfd\x00\xf5\xcc\xf4\xcc\xfc\x00\x00\xcf\xf0\xcc\ +\x00\xcd\xf1\x00\xef\xcc\x00\xce\xfc\x00\xf5\xcc\xf4\xcc\xfb\x00\ +\x00\xce\xf0\xcc\x01\xcd\xd7\xf5\x00\x00\xda\xef\xcc\x00\xcd\xfb\ +\x00\xf5\xcc\xf4\xcc\xfa\x00\x00\xcd\xed\xcc\xf9\x00\x00\xd4\xee\ +\xcc\x00\xcd\xfa\x00\xf5\xcc\xf4\xcc\xf9\x00\x00\xce\xeb\xcc\xff\ +\xd1\xff\xcc\x00\xcd\xee\xcc\x00\xce\xf9\x00\xf5\xcc\xf4\xcc\xf8\ +\x00\x00\xce\xd4\xcc\xf8\x00\xf5\xcc\xf4\xcc\xf7\x00\xd5\xcc\xf7\ +\x00\xf5\xcc\xf4\xcc\xf6\x00\x00\xcd\xd9\xcc\x00\xcd\xf6\x00\xf5\ +\xcc\xf4\xcc\xf5\x00\x01\xcc\xcd\xdc\xcc\x00\xce\xf5\x00\xf5\xcc\ +\xf4\xcc\xf4\x00\x01\xff\xcd\xde\xcc\x00\xff\xf4\x00\xf5\xcc\xf4\ +\xcc\xf2\x00\x00\xce\xe0\xcc\xf2\x00\xf5\xcc\xf4\xcc\xf1\x00\x00\ +\xd4\xe3\xcc\x00\xda\xf1\x00\xf5\xcc\xf4\xcc\xef\x00\x00\xcf\xe7\ +\xcc\x00\xd1\xef\x00\xf5\xcc\xf4\xcc\xed\x00\x00\xcd\xeb\xcc\x00\ +\xcd\xed\x00\xf5\xcc\xf4\xcc\xeb\x00\x02\xce\xcd\xcd\xf1\xcc\x00\ +\xd0\xeb\x00\xf5\xcc\xf4\xcc\xe8\x00\x04\xd2\xce\xcc\xcc\xcd\xfa\ +\xcc\x01\xce\xd2\xe8\x00\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\ +\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\ +\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\ +\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\ +\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\ +\x01\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\ +\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x06\x00\ +\x06\x00\x06\x00\x06\x00\x06\x00\x11\x00\x10\x00\x0c\x00\x0c\x00\ +\x0e\x00\x0c\x00\x0f\x00\x0e\x00\x0e\x00\x0e\x00\x0e\x00\x12\x00\ +\x11\x00\x14\x00\x10\x00\x12\x00\x14\x00\x12\x00\x0e\x00\x0f\x00\ +\x0f\x00\x10\x00\x0e\x00\x0a\x00\x06\x00\x08\x00\x08\x00\x06\x00\ +\x08\x00\x10\x00\x11\x00\x14\x00\x16\x00\x16\x00\x15\x00\x14\x00\ +\x14\x00\x17\x00\x14\x00\x16\x00\x0c\x00\x0a\x00\x0e\x00\x0f\x00\ +\x0f\x00\x0c\x00\x0e\x00\x0e\x00\x0e\x00\x10\x00\x13\x00\x06\x00\ +\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x02\x00\ +\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\ +\x02\x00\x02\x00\x02\x00\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\ +\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\ +\xa8\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\ +\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\ +\xf4\xcc\xe8\x00\x03\xcd\xcc\xcc\xcd\xf8\xcc\x00\xcd\xe8\x00\xf5\ +\xcc\xf4\xcc\xeb\x00\x02\xcf\xcd\xcd\xf1\xcc\x00\xd3\xeb\x00\xf5\ +\xcc\xf4\xcc\xed\x00\xea\xcc\x00\xce\xed\x00\xf5\xcc\xf4\xcc\xef\ +\x00\x00\xd1\xe6\xcc\xef\x00\xf5\xcc\xf4\xcc\xf1\x00\x00\xd4\xe3\ +\xcc\x00\xda\xf1\x00\xf5\xcc\xf4\xcc\xf2\x00\xe0\xcc\x00\xcd\xf2\ +\x00\xf5\xcc\xf4\xcc\xf4\x00\x01\xff\xcd\xde\xcc\x00\xff\xf4\x00\ +\xf5\xcc\xf4\xcc\xf5\x00\x00\xcf\xdb\xcc\x00\xcf\xf5\x00\xf5\xcc\ +\xf4\xcc\xf6\x00\x00\xce\xd9\xcc\x00\xcf\xf6\x00\xf5\xcc\xf4\xcc\ +\xf7\x00\x00\xcd\xd7\xcc\x00\xce\xf7\x00\xf5\xcc\xf4\xcc\xf8\x00\ +\x00\xce\xd5\xcc\x00\xce\xf8\x00\xf5\xcc\xf4\xcc\xf9\x00\xec\xcc\ +\x04\xcd\xce\xd7\xd4\xd0\xeb\xcc\xf9\x00\xf5\xcc\xf4\xcc\xfa\x00\ +\xee\xcc\x01\xcd\xff\xfb\x00\xea\xcc\xfa\x00\xf5\xcc\xf4\xcc\xfb\ +\x00\x00\xce\xef\xcc\x00\xe2\xf9\x00\xea\xcc\x00\xcd\xfb\x00\xf5\ +\xcc\xf4\xcc\xfc\x00\xee\xcc\xf7\x00\xe9\xcc\x00\xd3\xfc\x00\xf5\ +\xcc\xf4\xcc\xfd\x00\x00\xda\xef\xcc\xf6\x00\xe8\xcc\x00\xd7\xfd\ +\x00\xf5\xcc\xf4\xcc\xfe\x00\x00\xff\xf0\xcc\x00\xcd\xf5\x00\xe7\ +\xcc\x00\xff\xfe\x00\xf5\xcc\xf4\xcc\xfe\x00\x00\xcd\xef\xcc\xf5\ +\x00\xe7\xcc\x00\xcd\xfe\x00\xf5\xcc\xf4\xcc\xff\x00\xee\xcc\xf4\ +\x00\xe5\xcc\xff\x00\xf5\xcc\xf4\xcc\x01\x00\xda\xee\xcc\xf4\x00\ +\xe4\xcc\x00\x00\xf5\xcc\xf4\xcc\x00\x00\xee\xcc\xf3\x00\xe5\xcc\ +\x01\xcd\x00\xf5\xcc\xf4\xcc\x00\xcf\xef\xcc\x00\xcf\xf3\x00\xe4\ +\xcc\x00\xd4\xf5\xcc\xf4\xcc\x00\xcd\xee\xcc\xf3\x00\xe4\xcc\x00\ +\xcd\xf5\xcc\xe1\xcc\xf2\x00\x00\xd2\xf7\xd1\xe2\xcc\xe1\xcc\xe7\ +\x00\xe2\xcc\xe2\xcc\x00\xcd\xe7\x00\xe2\xcc\xe1\xcc\xe7\x00\x00\ +\xcd\xe3\xcc\xe1\xcc\xe7\x00\xe2\xcc\xe1\xcc\x00\xd4\xe9\x00\xe1\ +\xcc\xf4\xcc\x00\xcd\xee\xcc\xe9\x00\x00\xce\xef\xcc\x00\xcd\xf5\ +\xcc\xf4\xcc\x01\x00\xcd\xf0\xcc\x00\xcd\xe9\x00\xee\xcc\x00\x00\ +\xf5\xcc\xf4\xcc\x01\x00\xd2\xef\xcc\x00\xd4\xeb\x00\x00\xd4\xef\ +\xcc\x01\xcf\x00\xf5\xcc\xf4\xcc\xff\x00\x00\xce\xf0\xcc\x00\xcd\ +\xeb\x00\x00\xcd\xf0\xcc\x02\xcd\x00\x00\xf5\xcc\xf4\xcc\xfe\x00\ +\x00\xcd\xf0\xcc\x00\xd0\xed\x00\x00\xd0\xf0\xcc\x00\xcd\xfe\x00\ +\xf5\xcc\xf4\xcc\xfe\x00\x00\xe2\xf0\xcc\x00\xcd\xed\x00\xf0\xcc\ +\x01\xcd\xda\xfe\x00\xf5\xcc\xf4\xcc\xfd\x00\x00\xd3\xef\xcc\xef\ +\x00\x00\xcd\xf0\xcc\x00\xcf\xfd\x00\xf5\xcc\xf4\xcc\xfc\x00\x00\ +\xcf\xf0\xcc\x00\xcd\xf1\x00\xef\xcc\x00\xce\xfc\x00\xf5\xcc\xf4\ +\xcc\xfb\x00\x00\xce\xf0\xcc\x01\xcd\xd7\xf5\x00\x00\xda\xef\xcc\ +\x00\xcd\xfb\x00\xf5\xcc\xf4\xcc\xfa\x00\x00\xcd\xed\xcc\xf9\x00\ +\x00\xd4\xee\xcc\x00\xcd\xfa\x00\xf5\xcc\xf4\xcc\xf9\x00\x00\xce\ +\xeb\xcc\xff\xd1\xff\xcc\x00\xcd\xee\xcc\x00\xce\xf9\x00\xf5\xcc\ +\xf4\xcc\xf8\x00\x00\xce\xd4\xcc\xf8\x00\xf5\xcc\xf4\xcc\xf7\x00\ +\xd5\xcc\xf7\x00\xf5\xcc\xf4\xcc\xf6\x00\x00\xcd\xd9\xcc\x00\xcd\ +\xf6\x00\xf5\xcc\xf4\xcc\xf5\x00\x01\xcc\xcd\xdc\xcc\x00\xce\xf5\ +\x00\xf5\xcc\xf4\xcc\xf4\x00\x01\xff\xcd\xde\xcc\x00\xff\xf4\x00\ +\xf5\xcc\xf4\xcc\xf2\x00\x00\xce\xe0\xcc\xf2\x00\xf5\xcc\xf4\xcc\ +\xf1\x00\x00\xd4\xe3\xcc\x00\xda\xf1\x00\xf5\xcc\xf4\xcc\xef\x00\ +\x00\xcf\xe7\xcc\x00\xd1\xef\x00\xf5\xcc\xf4\xcc\xed\x00\x00\xcd\ +\xeb\xcc\x00\xcd\xed\x00\xf5\xcc\xf4\xcc\xeb\x00\x02\xce\xcd\xcd\ +\xf1\xcc\x00\xd0\xeb\x00\xf5\xcc\xf4\xcc\xe8\x00\x04\xd2\xce\xcc\ +\xcc\xcd\xfa\xcc\x01\xce\xd2\xe8\x00\xf5\xcc\xf4\xcc\xc1\xff\xf5\ +\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\ +\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xf4\ +\xcc\xc1\xff\xf5\xcc\xf4\xcc\xc1\xff\xf5\xcc\xa8\xcc\xa8\xcc\xa8\ +\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\xcc\xa8\ +\xcc\xa8\xccMIB8ksML\x0e\x00\x00\x00\x00\ +\x00\xff\xff\x00\x00\x00\x00\x00\x002\x00\x80\x00\x00\x00M\ +IB8ttaP\x00\x00\x00\x00MIB8k\ +sMF\x0c\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\ +\x002\x00\x00\x00\x00\x00\ +\x00\x00\x05}\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22 standalone=\x22\ +no\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + eye on<\ +/title>\x0d\x0a Created with \ +Sketch.\x0d\x0a\ + \x0d\x0a \x0d\x0a <\ +path d=\x22M62.3998\ +86,20.262032 C61\ +.0656979,18.1619\ +773 49.0033003,3\ +.55271368e-15 32\ +.0083577,3.55271\ +368e-15 C15.0134\ +15,3.55271368e-1\ +5 2.9510174,18.1\ +619773 1.5499679\ +5,20.262032 C-0.\ +516655982,23.447\ +0641 -0.51665598\ +2,27.7140347 1.5\ +4996795,30.97200\ +66 C2.9510174,32\ +.9991215 15.0134\ +15,50.9422798 32\ +.0083577,50.9422\ +798 C49.0033003,\ +50.9422798 61.06\ +56979,32.9991215\ + 62.399886,30.97\ +20066 C64.533371\ +3,27.7140347 64.\ +5333713,23.44706\ +41 62.399886,20.\ +262032 L62.39988\ +6,20.262032 Z M3\ +2.0083577,39.943\ +5857 C24.6110597\ +,39.9435857 18.6\ +786333,33.503620\ +9 18.6786333,25.\ +4711399 C18.6786\ +333,17.5115986 2\ +4.6110597,10.998\ +6941 32.0083577,\ +10.9986941 C32.7\ +407935,10.998694\ +1 33.4732293,11.\ +0716338 34.14184\ +3,11.2175131 L34\ +.141843,23.15530\ +55 L45.1405371,2\ +3.1553055 C45.27\ +12206,23.8786238\ + 45.338082,24.67\ +48819 45.338082,\ +25.4711399 C45.3\ +38082,33.5036209\ + 39.3387943,39.9\ +435857 32.008357\ +7,39.9435857 L32\ +.0083577,39.9435\ +857 Z\x22 id=\x22Shape\ +\x22 fill=\x22#CCCCCC\x22\ +>\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x1f\xef\ +\x00\ +\x00\xe4\x14x\x9c\xed\x5c\x07\x5cS\xc7\x1f\xbf\x0c\x12\xf6\ +^2c\x98*\x10\xc2&\xc8\xde\x0a\x82l\x14\x85\x90\ +\x04\x88\x84$f\x80\xab\xe2\xaa\xd6U\x95\xba\xb5uo\ +\xc5\xd6]\xad\xdb\xd6Z\xf7\xb6u\xfc\xb1\xd6\xbaW\x15\ +\xb5\xa8\xf0\xbf{!\x10\x10-J\x00\xfb\xff\xbfo>\ +\xf7r\xef\xd6o\xdc\xbd\xbb\xdf\xdd\xbb{\x89\x89\xa0;\ +\x00@\x13\x98\x83Z@\x86>\x02P\x5cH\xd8_\x1e\ +\xbc\x10T\xfcD\xcc\x0f\xd3\x11\xcc\x09\xa4\xfap\x98\x98\ +@\xa9\xf7\x13\xe1\xc5HYN\xeam\x82\xb1J\x1a\xb3\ +z?\x96[\xa5L\x0be\xfa\xe9\x80`\x054\x14~\ +\x82\x1d\xc1\xba\xc1\xefL\xb0Q)\xc7U\x85\x16\x13]\ +\x01\x1d\xfa<\x08^\x98\xdf\x14\xfa\x93\x09)\x8d\xe9\x89\ +?\xa0\xab\x9f5\xbc\xe5\x8e\x8fG~C\x1a\x00\xfe\xa3\ +&\xf0\x95to\xdc\x9e \x05T\x00t\xe3\x00\x88]\ +\x8c\xe4\x87\xfaP\xfc\xb4_\x8f\x02\xc0\xc5P\xf9\x1f\xce\ +\x15\xe5\xf3h\xc9E\x22\x99HZ$\x12\xd3\x22#i\ +^\x9eL\x7f\x9ak&_\xc8\x15\x95I\xbb\x01t\xcb\ +\xf2\xf4byz\xd3\x98\xbe,/o\x96\xa7\x0f\xe8\x19\ +:D\xcc\xe6\x14\xf3d\xb4|^!_\x18L\x7f\xb4\ +k/\x9d\xc6\xe7\x06\xd33}\x13=\x13\xc5\x91\xbc\x22\ +~\xdc0\x09/uX\x9f4\xce\xb0bN \x97\x1e\ +\x1a\xa2\xdds\x08kH\x89\xb8\x84'c\xd3\x86\x94\x08\ +\x84R\xd6\x90`:\x1b\xd1gA?\x0af\xd0iX\ +\x12Yq0]\xc1XVb2-R$\xe1\xd1|\ +=\xfc\xdc9L\xef\x00\x9a\x7f\xa0\x07\xd370\xc0\xcb\ +\xc7\x0d1\xea\xc7\xf0\x0cd0}\xdc=\x99,\xcf@\ +\x96'\x93V\x0fz\x886\xbc\xf6\x94p\x0bX)Q\ +1\xf5\xe4\xe0]0\xbdH&\x13\xb3\x18\x8c\xb2\xb22\ +\x8f2o\x0f\x91\xa4\x90\xc1\x0c\x0c\x0cdxz1\xbc\ +\xbc\xdca\x0aw\xe9P\xa1\x8c=\xc4](uP\x14\ +\xa2,'\x8a'\xe5H\xf8b\x19_$\xa4\xa1{v\ +\xbeH.\x0b\xa6\xd3\xb5i*\xa8\x97\xabD\xdc@H\ +(\xf5\xc0d\xf4\xe0\x88J\x18C\xd8b\x06\xd3\xc3\x93\ +\xd1R&.\xa7!\x8fX.\x11`\xacq9\x0c\x9e\ +\x80W\xc2\x13\xca\xa40\x1f\xb3\xc5|be\xdd\xb5L\ +\xb2!\xfa\x9d\x84!\xb7\x89\x89\xef\xe7\xb7\xa4\xa4\xc5\x9c\ +RYt\xa9\xec\xfd9\xa5iC\xc5\x05\xee\x01\xcc|&|\xc6|}\xb9<\x0e\ +\xd3\xcb\x97\xeb\x8b\xe9\xa0i\xf6\xb7\x8a\x8e\x12q\xe4\xa8\ +\xe9\xd6\x17\xcd\xfd\xc0\xa2U\xb2\xbfUt\x92\x84\x0f\xbb\ +\x1d\xb6\xa0\x8d$Z(\xe6-Rq|\xa9L$\x19\ +\x1a\xd2\xa4\xf9c\x1dB*op\xd3Pe\x84\x80\x8f\ +u\x10b\xb6D\xcaC\xcd?\x98\xael\xff\xf4\xb72\ +\xa0<\xd8c\xc4bsP\xd7\x12\xc2\xc1Z8\xb7'\ +\xa3I\xe8\xbb\xb3\xf1?\xb6\x02\xdf\xca\xfen\x1aeE\ +<\xe1\xfb\x9eL\x95T\xef.D**\x90\x95\xb1%\ +\xbc\xf0B\xa8\xe9\x90\x7f\x1cw\x94\xa56\xcd\xf6\x96\xbe\ +\x19\x0a\x857\xab\x1e\xc6\xdb\xf5\xa3\xac\xf3f\xf5\xa9H\ +\xaa\xd2\xb7+\x06\x0eF\xfd\xc8\x01\x07-F\xc3\xa8\xd5\ +\x92p\xea\x07N\x04'\x82\x13\xc1\x89\xe0Dp\x228\ +\x11\x9c\x08N\x04'\x82\x13\xc1\x89\xe0Dp\x228\x11\ +\x9c\x08N\x04'\x82\x13\xc1\x89\xe0Dp\x228\x11\x9c\ +\x08N\x04'\x82\x13\xc1\x89\xe0Dp\x228\x11\x9c\x08\ +N\x04'\x82\x13\xc1\x89\xe0Dp\x228\x11\x9c\x08N\ +\x04'\x82\x13\xc1\x89\xe0Dp\x22j&\xa2\xddx\x0e\ +\x8c'\xe4\x06\xd3\xcb\xe8\xa1! \x22>\x91\xec\x84\ +\x9d93\x04\xcd\x80\xc5\xb10\xef\xcd\xfax\xect\x1e\ +\xd0\x11K\xf8BY\x92\x5c&\x96\xcb\xe0-:&\x07\ +\x92\xa5\xb2\xd4|\x91H\x80\xa5\x88\x17\xcax<\xa1\xbc\ +D\xe9G\xff\x91\x02\x09\xba7\xc0\xf2\xa6\xf2\x87\xa0\x14\ +\x11|\x19\xca\xd3X&O\xd2\x87]\xc2K\x8b\xceJ\ +k \xa6\xc8\x90,\x11\x89\x0aRy2\xb98)\x7f\ +\x10\x07\x06\xeb\x82d \x01\x22\xf8+\x004\x90\x0ax\ +@\x06\xe4@\x8ce\xd1\x167\xa4V\x16\x13!\x90\x09\ +\xeb9\xd2\xcd\x97\xf3\x052\xbe\x10+\x12\xdeka\xa9\ +#\x13\xb3{+$\x0eB\xe9\x89\xeeM$6V\x91\ +8\x09;y \x85\xa1\x16\x98\x5cb\x99P)\x04\x14\ +2_\xd2p\x93R(Ml\x8c\x91\x08#\x1bo\x84\ +\xb2\xc6\x9b\x84|\x81\xb4\xe1\xa6O\xa1\xac\xb4\xe1&\xba\ +D\x10\xd5p\x03\xf5\xd8Xt\x04\xa7\xb8\xb0^\x11\x0a\ +\x06AJlD$P\x1c\x9b\x04)\x5c\x1a\x8d+\x92\ +\xe7\x87\x89\xaa\x1a*3V\x22|+,B\xf0v\xba\ +\x08\x097-](\x8bqH\x11\xc8T\x1bC\x84\x80\ +Kk)\ +\x8b\xae2\x06\xb1\xa0\x12\xae\xa7\x0cO\xe1\x17\x16\xa9F\ +\xe8(# o\x0d\xc1\xa8\xe5\x90\x1e(x\x00q@\ +q\xf6\xb3\xfe\x1fkU\xceX\x9c~\xa3\x04\xa1\xa3\xea\ +s=T\x90\xd3M\x93\xb0\x85R1[\xc2\x13r\x86\ +*Z\xa2\x19\x16c\x87bA\x1al\xedl \x04R\ +\xd8\xc6\xd9\xd0\xcf\x83~\x0e\x18Z\xff\x94\xfab)\x8d\ +0z\xa0\xae\xae\x9e\x04\x97\xa0\x88\xb5\xc5\xee\xc8J>\ +\xc9z\x0d\xf7v\xd8}\x97\xa6\xf7\xa4\xa7\xd8\xbd\x96\x92\ +SE).\x8a\xfeA[\xd9\xe0\x14r\x91\xaa1\x7f\ +\x1c`\x14\x14\xc0\x18\x01\xbcR\xea3)B\x96\xcc\x9b\ +\xdf\x10\xe2\x85]\xfb\xc1\xab2\xc4\x17\xbb\xba7\x86`\ +e\xbe\xc4\xfcb$IK \xddF\xd1\xea\x8f\xc3t\ +\xa1\x90\x8dX\x7fGlr\xe7\xa9\xe0\xb1^\xab\xee\xd8\ +\x1d\x05\xd3\x07\x09\x0b\xd1\xac\xaf\x7f\xac\x16\x88a\x0aW\ +\x9f\xd7\xaeY\x1dX!\x1f\xc9\xb7Ar\x05\xf2\xea\x9d\ +B;oC5L5m\x8b\x09\x94h\xec\xc6\x80P\ +.\x10(\x04\x02\x94|\x91\x5c\xc8\x956\xebE82\ +\xa6\x92M\xf4\xe8\xa94{\xd0\xec\xf9\x00\x11\x8d\xcf\x11\ +\xc6FJ\xe3c\x82\xee)R\x01\x9f\xc3\x93f\x08\x12\ +\xd0\x83NhBG\x03\x8b\x83\x1ec\xe8\xa8\xd8M|\ +\x94J\xd9\xd4B\x89H.n\x12D\x11a\xc7\xff\x94\ +}xt*\xca\xa48\x12\x08\xef\xf5\xd8r\x99(\x96\ +'\xe4I\xd0q<\x8c\xfb\xa1b\xe5\x10\xa4\xadH\x8c\ +BPL|I!\xad\x03\xe4'\xc9%\x82&\x03\x19\ +\xa6\xfc\xa6!\x89\xd2\xc2\xa6\x83\x1d\x85-\x90\xa5\xb1\x0b\ +\x9b\x84\xe9sx0\x1fo\x88,^\x1a\x97\x96\x98\xa0\ +\xecN5\x95\xc1M\x12k\x15\x89$\xc3\xc2\x05\xfcB\ +\xa5\xa6\x0c\x14\xc2\xc7)\x83\x91v\xb9\xbc\x02\xb6\x1c\xeb\ +O\xb5Jy\x12Y\x0b\xc93\x94\xc1M\x93\xeb\xe4\x17\ +b\xa7\x5cU\x94k\xa4\xc8\x10\x11\xdb\x10\x81\xd8\xe8#\ +\x12\xa2\x7f-\x99H\x0c\x07M)OUq\xda\x02\xa8\ +\xc8\xb7Bu\xf3\xb1\x8e\xf9\xadp\x1d\x09\xea~\x9b\x05\ +cO\x90\xab\x22\x1ft\xc4\xd0\xc7\xa01\xdc\x14\xf3\xa2\ +*T<\x95\xba\x98b\x8f\xd6\xab(O\xe1\x08\xb0\x09\ +\xa0'\x81\xb2\x11X\x02B\xdd\xa5\xba\x07@\x17;\xe5\ +\x98\x1b\x99\x08\xef\x1f\x02}\xec\x0epG\xa1|u\x97\ +\xc18\xa0\xab\xa9\xa9\xa9\xa5\xa9\xab\xa5\xa5k\xa4\xa3\xad\ +cdf\xa0\xabk`fibbfbbi\xa4\ +\x8b\xa1\xfe\xafe\x10\xf4tt\xf4\xf4\xf5\x0c\xf5\xf5\x0d\ +M\xf5\xf5\xf5M\xd1E\xdfT\x91\xc5\xa85\x05\xd4\xed\ +\x03F\x9a\x90\xf9<\x12\x81\x0e\x88F\x04\x92\x11\xa1\xee\ +*\x14\x94Rw\x88\x10\x0a\xb9\xd4 `\xa8W\x1c\x09\ +\x10\x88d\x0d\x0aUSK[\x87\xd0<\x92\x00\x88$\ +e\xa4! \x90\x09$\x22\x99\xa8A\xa5hj\x90t\ +\xbda\xa4\x11\x89\xdc\xd5\x98\xa9\x11\xde\x97mB\x1f<\ +\xda\x8bb:c\xc9w\x11\x0e\x8ef)\xfb\xf3\xbd}\ +$cNGR\x9d*R\x1f_\x7f\xc2\x91\xfa\x9a/\ +\xdd4\xd69\xea\xab4n\xf4\x81e2?\x8b3\xe9\ +\xbf\xf3\xfe\xda<\xee\xe0Y\xf9\x8d\xa71.3\x97\x7f\ +\xbee\xd6\xa1s\x7f<[\xb1\xf5\xc7\xf37\xab3\x0a\ +J\xc7\xcf^\xb9\xed\xa7\x0b\x7f>\xf7\x8f\xcd,,\x9b\ +0g\xd5\xf6\xc3\x17o\xbd0\x02D\x22\xe4\x96\x8c\xf1\ +D\xa5h\xf8b,te\x1a\x93!\x07\x83\xe9&\x1a\ +^\xa3g\x98\x22\x0e\xf6\xa7\x9c~\xec\xed\x98\x7f]2\ +\xa6\x222\xd5\x8c#\xf5y\xe2DA\x0cP\x9d}\x0f\ +\x9c\x81L,\xb3\xe0F\xa7\xfb\xc9x\xbf7\xb0\xf0n\ +\x0e\x5c\x1aY\xa8\xfb\x0d\xe8\x920\x9aF \x14T\xa7\ +U\xc4;us\xac\x88\xef\x15\xefX\x91R\x11\xef8\ +sy}@R\xdd\xa9\xea4\xb7\x05\xbfN\x9dV\x10\ +\xb9\xdfm\xcd\x8a\x87z\xd6S\x08!\x17\xc6\xa5U\xa4\ +F.\xaf^h\xb7!v\xdd\xa4G\xe2Z\xc9\xaa\xa2\ +\xe7\xf67\xfdk_o\xac\x94\xae{\xd3o\xeb\xf7\xf6\ +[Y\xd6\xb5G\x17^\x1d\x90uz\xf6\x90:\xe0T\ +Y\xf6f{>\xa8y\x9a\x1e \x12\x16\x04\xdd\xbb\x9b\ +n\xfb\xe5\xe11V\x03^\xbd\xfa\xfa\xf5\xe5\xf2\x8a\x8a\ +oV\x9c(\xee\x97\xf3\xbb\xd5\xe95.a\xe6\xbc:\ +\x90\xbdS?h\xb2\xe0\xf8\xfa\x19'\x8el\xae\x03\xc1\ +\xd4\x8d\xc1ww\x97\xad\x91\xf7\xd7\xfaeKM\xf9\x82\ +\xde>y\xb5\x87o\x95g\xdf\x0bv\x0aY\x11{A\ +8vz\xd5oO\x8b\xc9O\xf6\x14\xd7\x81\x93K&\ +'V\x16\xcf\xb79csv\xb5Cr\xe6Tw\xfd\ +Y\x17\x87\xcd\xe9\xf5m\x90\xf9\xd9;\xb5r\xc6\xf9\x09\ +\xaf\xec\x17\xbc)p\x5cu\xbe\x22;X\xe7\x9e\x9d}\ +\xd6\xda.5n+'n\x1a#\xaf\xddZ\xf8\xf4\xd2\ +V\x85\xf8u\xa0\xee4\xa6\x94@\xfb\xac\x9a7\x03\xec\ +\x8d\xfb\x1b\x1c\xdd\x16m\xb6\xe8\xf5\xcb:\xf0\x07s\xe8\ +\xca\x98\xe8_\xfdC^]\x13l;\xc8\x18\xfe\xbd\x13\ +d$\xe6\xda\x8as\xc2_\xbe\xfdY\xfa\xc5\xfa\x91\xf7\ +\x08o\xacc\x9f\xa7;\xce\x9cc>;\xf7\xab\x01\x8c\ +\xad[\xfc\x0eS\x16\xe4>\xebgp+pXh\x9f\ +\x9aU\xa7\x9et\x17\x9c\xba\xb2j\x86\xf3\xe5\xddO\x1f\ +\x17>-\xc9\xda\x9bZ\xe3\xac_\x1es\xa4t\x00\xbb\ +v\xeb.\xd2\xde\xc0%S\xad\x07\xf4\xdf\xeb{y\xfd\ +0o\x8f\xbb{\xd3\x1e\x98q\x85\xc7o\x9f\xf0;lV`\x82\xa7U\xd5e\xea#\xcf:\x10p\ +\xa6bQ6l:\xa9ug0\xad\x85\xa4W\x8f]\ +q\xf9L\xcd\xd7[\x02\xec\xe3\x0f\xed\x99P\xea\xe6r\ +\xb9VtJrb\xe0\xba\x8b,\x8f\xd0\xac\x83>\xfd\ +\x1fl\xddyVT\xba\xab\x0e\xd0\x1e\x9d\x1c\xc8[4\ +z\xd6\xd9\xbfr'\xcf<>\xdb\xfb\xa1\xc3\xe6K\xe5\ +\x81_\xc6'\xb1k\x19\x9b\xb3\xee\xe7L!\x96\xc7\x1d\ +\xae9n\xe7..\xb6\xad\xe5\xcc\xab\x03\x89\xf2\xd9\x99\ +\x03\xd6\xcbK}\x82\xa7\x19\xfe\xb9#\x8e\xf6\x9c\xb6\x91\ +\xbe\xff\xf8\x91}\xbf\x14dxo\xeb\x9f=}\xb8\xd1\ +\xd5J\xfb\x9d7\xaf\xe4}6\xe2\xce\x9c\x0cJq\xee\ +\xd4\xa2\x9ad\x9dK\xe5k\xb3\x1f\xd5\x81\xc5\x153n\ +>\x1b\xb9\xec\x14\xa3,\xf9v\xaa\xb0\xdbE\x07\xf2\x91\ +\xb3\x7f\xdb\xef\xae\x03\x8b`\xdb\x19\x97\xfd\x9f~#7\ +\xb1\xab\x07\xce\x9a\x9a\xe1\xb3\xedp\xf6\x9e\xbbW\x8f\xfa\ +\xe9\xed*\x9f\xb5\xeb\xdc\xf3\x0d9u`\xe3\xd7^\xf3\ +\x7f>P\xfcWIhL8\x87Z=\xf9M\x1dx\ +f\xa15kFAF\xf4<\xf3\xc3k\x8a'\x7f\xbd\ +\x8521DoP\xaf\x98-{6>[\xc7Z\x95\ +\xbav\xfc\xd8;\x9e\x13\xae=\xdc?\xea\xd5\xde\x935\ +zi\x8f\x9fx\x1c\xffe\xa3\xf9\x17~W\x1el\xb8\ +\x92\x974\xaf\xc7\xd9\x1a\xfb\xe8\xc3\xb7_\xe6O\xda}\ +aW\xff\x0d\x0fo\x95\xffa\xfd\xf7\xc4n\xa71]\ +\x9eU<\x96}\xef^\xb5\xa6\xae\xe3\xd4\x04&\x9c+\ +\xf7/\x96\x0d7\xf8k@\x1d\xf8,\x13>\x9b\xf3\xab\ +Ekm3W\xed\xd8\xb3\xd2\xe6\x87\x91\x17\xd2\x1cG\ +\xd7\x01\xad\x8c\xcaA\x07ic\xc9\xe16\x9f\xdf\xff\xd9\ +\x82\xcd\xe6v\xd9k\xb7.i\xf2\xa5\x8b\xe5\xe7\xc93\ +\xff\x06\xe4\xb9\xc4\xf8*\xf7\x13_U\xc7\xae\xad\xa6R\ +/\x1d\x9c8\xed\xda\xd1(\xe3\xd0\xaa\xbb\xa7O\x0c\x9c\ +\xd0\xc7\xdc

\xf3\xee\xea\x97\xdfM\x15\xbd\x89\x8b\ +\x8dO\x0a\x15\x9d\xdbS\xf1\xf0\x9b+\xfdFW>\x1f\ +\xbe`\xd6\xd1\x17\xe5\x8c\x17\xf3^\x7f\xde\x9fZyK\ +6b\xfc\xb3\x93\xebi\x0fX}\x17\xfa\x16Ox\xed\ +\xb07\xa8\xa6\x0e$\xbd\xe9~7}\xc4}\xdd>K\ +\xbe\xbc/\xd8\xf9\xe3\xac]\xdf\xfda\x7f\x8d\xb3\xd7\xf7\ +H\xb7\xa0\x9c\x8b[\x02\x97\x07V\x1a_\xd4\x0b\xa7\xee\ +\xe8\xb3$\xd1\xb7o\xefn+\x96\xcc\xca e\x9d\xde\ +GHt\x0a\xad;\xf7\xbe>\xe8W\xc58\xd7\x15\xeb\ +\xc9\x07\x00\xcc\x22\x85#7\x08\x07\x5c8\x1d\xce\x87S\ +\x03\x1a\x9c\x1c\x17A\xbf\x0c:)\xe6C\x13a\x8b\x7f\ +HA\x03\x91\xf0G\x83f\xba'`\x02\xff\xfa\x01S\ +7.\x81/\x14\x11\xa1\x11Y\x02g\xa0\xe8\xcb\x15Y\ +\xd9\xfdh\xd4\x13\xd0\x88\xd4\x82\xb6+4\x97\xd8\x1c\xa9\ +815&\x0d3\xae\xa2#i\xe8\xf3\x16\xa0\x09\x9e\ +_P\x98'g\xdd\xe3\x92i4\xf0a0\xe2\x88%\ +2\xf4U\x1d\xe8\xf7\xe6\xf2\xa4\xd0\x5c#\x8c\x83~A\ +\x99L\x8c\xc2\xd1\xd8o\x9a_\x8c\xfcD4\xea\x9bJ\ + \x83\xd0o\x89\xfc\x85\x0a\xbf\x1b\x96F\xe1\x0fC~\ +n\x89\x10\x9a\x89D\xc4\xb3\x98[\xc2E\xfeC\xd0\xff\ +E\xa9\x1c\x99\x8f\xa4\x04\xe8\x1f_\xca\xe7\x95A\xff9\ +\xe8w\x14\xc8K\xf8\xd0\x8ff&\xa6%<64i\ +1\xbb\xc2Q\xc6\xe3\x14A?\x9a\x19\xe8J\xd2R\xe0\ +\x5c\x96\xdc\x13\xdaf\xba\x85*\xfe|\x15\xbf\x0c\x1al\ +H\xa8H\x91x(f\xd9\xd0\x5c9\xddh\xcc\xc0\xc0\ +\x00Z\x1c\xafL\xc0\x93\xc9\xdc\x93\xd9\x9cb\xb6\x84K\ +\x8b\x14\x95\x88\xd9B8\xc3S\xc8\x8c\xc1\xf8\xadO\x87\ +\xa8(\xea\xbd\x91\xad\x04\xaa[\x85\xefi_\xac\xce\x08\ +\xe6\xc7\x1a\xc3ZJ'Z\x0a\xed.8\x0b$Mo\ +\x0c\xcb\x9f\x07\xc0\xf6\xcf\x01\xb0\xfc\xad1\xccq1l\ +\xa3\xb0\xde\xb6\x9dT\x91\xc7\x1c\xb5\x17\x95\x8f\xfc\xf0y\ +\x1c\x0f\xa4\xd0\x06\xfcc\x82V@\x85\x9e\x07*\xaeA\ +=\xb4(\x85eKCz\xe3@\xfbU.\xa1\xc1\x19\ +8\x87Gso\xde\x88?:c\xcb|\xb8\xa5\xf0\x0a\ +xh\xa6\xcf\xa3e\xc0V\xc6\x17\x16\xc2\xea\x16r\xf9\ +\xd8\xf7\x8a\xf8\xc2wU\xe2Gfk\x06E\xbb\x860\ +Y^\x0bLs=\x80\xe1IS@zp\x0c\x90M\ +t\x00)\xe7\x1b\x18Ch\xa8\xb7\x04\xad\x0c\x80\x9e\xbc\ +L\xfb[\x8av\x8f\xa1\x85i&q\x1a\xbaH\xf9\xd8\ +\xe4\x0aD\xa6\xa4\xd18rI\xa9\x22\x0e\x9bOi\x00\ +m\xd8I\x99\x82.\xc0\x0e8\x00W8\xeb\xf7\x82\x9d\ +L\x10\x08\x03\xd1\xa0\x17H\x02i \x1b\x0c\x04\x1c\xd8\ +\x19\x95\x00\x09(\x03#\xc0h0\x1eL\x06\xd3\xc1,\ +0\x1f,\x02\xcb\xc1\x1aP\x096\x81\xed\xe0\x07\xb0\x1f\ +\xfc\x04\x8e\x82S\xe0<\xb8\x0c\xaa\xc0Mp\x0f<\x06\ +\xcf\xc1+h\xe0R\x09z\x04\x13B\x17\x82=\xc1\x89\ +\xd0\x83\xe0E\x08 \x84\x10\xa2\x09\x09\x84\x14B6!\ +\x8fPH\x10\x12\xe4\x84\x11\x84\xb1\x84\xc9\x84\x0a\xc2|\ +\xc2\x12\xc2\x1a\xc2w\x84\xef\x09\xfb\x09G\x08\xa7\x09\xbf\ +\x12\xae\x13\xee\x10\xfe\x22\xd4\x10ID]\xa2)\xd1\x96\ +\xe8Ld\x10\x03\x88\xe1\xc4\xde\xc44\xe2\x00b!q\ +0q\x18q\x1c\xf1K\xe2\x5c\xe2R\xe2z\xe26\xe2\ +~\xe2Q\xe2yb\x15\xf1\x1e\xb1\x9a\x04H:$s\ +RW\x92;)\x80\x14IJ\x22\xf5#\x15\x90$\xa4\ +\x91\xa4I\xa4\xd9\xa4\xa5\xa4J\xd2N\xd2a\xd2YR\ +\x15\xe9>\xe9o2\x85lB\xa6\x91\xdd\xc9A\xe48\ +r:\x99C\x1eL\x1eI\x9eB\x9eO^M\xdeF\ +>D>K\xbeN~L\xae\xd5\xd0\xd3\xb0\xd1\xe8\xa1\ +\xc1\xd2\x88\xd7\xc8\xd2(\xd4(\xd3\x18\xaf1[c\xa5\ +\xc6V\x8d\x1f5\xcek\xdc\xd4xN\xa1P\xcc).\ +\x14\x7fJ\x1c%\x9b2\x882\x9c2\x85\xf25e#\ +e\x1f\xe54\xe5\x06\x05\x0eh\xd4.\xd4\x1e\xd4`j\ +\x12\x95M\x95Q\xc7S\xe7Q\xd7S\xf7R\xcfPo\ +R_j\xeah\xdakzi\xc6h\xf6\xd3\x14j\x8e\ +\xd1\x9c\xad\xb9Vs\x8f\xe6\x19\xcd[\x9a\xaf\xb4\x0c\xb5\ +\x9c\xb4XZIZ\x5c\xad\xa1Z\xd3\xb4\x96k\xed\xd4\ +:\xa9uS\xeb\x95\xb6\x91\xb6\x8bv\xb0v\x9a\xf6 \ +\xed\xd1\xdas\xb5+\xb5\x7f\xd4\xbe\xa2\xfdTGG\x87\ +\xae\x13\xa8\xd3W\x87\xaf3Jg\xae\xce\xb7:?\xeb\ +\x5c\xd7\xf9[\xd7X\xb7\xbbn\xa4n\x8e\xae\x5c\xf7K\ +\xddU\xba\xfbt\x7f\xd5}\xaa\xa7\xa7\xe7\xac\x17\xa6\xd7\ +OO\xa6\xf7\xa5\xde\x1a\xbd\x83z\xd7\xf4^\xea\x9b\xe8\ +{\xe8\xc7\xebs\xf5\xcb\xf5\x17\xe8o\xd3?\xa3\xff\xd0\ +@\xcb\xc0\xc9 \xdc`\xa0\xc10\x83\xd9\x06\x9b\x0dN\ +\x1a\xdc7\xd42t6\x8c4d\x1b\x8e4\x5c`\xf8\ +\xbd\xe1E\xc3j#\x13#\xa6Q\x92Q\x89\xd1\x14\xa3\ +\xb5FG\x8cn\x1bS\x8d\x9d\x8d\xa3\x8d\xb9\xc6\xe3\x8c\ +\x97\x19\x1f4\xbeaB2q0\x894\xe1\x98\x8c5\ +Yn\xf2\xa3\xc9MS\x8a\xa9\x8bi\xbc\xe9 \xd3\xc9\ +\xa6\x1bLO\x98>636\xf31\xcb0\x1bb\xb6\ +\xc0l\xb7Y\x959\xc9\xdc\xd9<\xde\x5c`>\xcd|\ +\x93\xf9\x05\xf3\x1a\x0b[\x8bp\x0b\x9e\xc5D\x8bJ\x8b\ +3\x16/,\xad-\xc3,y\x96\x93,7Z\x9e\xb7\ +\xac\xe9B\xeb\x12\xdd\xa5\xb8\xcb\x8c.\xdb\xbb\x5c\xb5\x22\ +[u\xb7\xeakUf\xf5\x8d\xd5\x8fV\xf7\xadM\xad\ +\x83\xac9\xd6\x93\xac7Y\xfffC\xb4\xe9n\x93b\ +3\xdcf\x99\xcd1\x9bj[;\xdbX[\xb1\xed<\ +\xdb\x83\xb6\xf7\xed\xcc\xed\xc2\xec\x06\xd9\xcd\xb4\xdbcw\ +\xc7\xde\xc4>\xc4\x9eo?\xd3~\xaf\xfd]\x9a\x19-\ +\x9c&\xa0\xcd\xa5\x1d\xa2=\xeej\xd35\xae\xab\xbc\xeb\ +\x92\xae'\xba\xbe\xa2\xbb\xd0\xd3\xe9c\xe8\x1b\xe9W\x1d\ +\xb4\x1d\x02\x1c\x0a\x1cf:\x1cpx\xech\xef\x98\xe8\ +8\xc2q\x9d\xe3oNZN\x01NENs\x9c\x0e\ +;\xbdpvq\xcet\x9e\xe0\xbc\xdd\xf9\xb6\x8b\xa5K\ +\xbc\xcb0\x97u.W\x5c\xf5\x5cC]\x07\xbb.u\ +=\xd7\x8d\xd2-\xa0[q\xb7\xaf\xbb\x9d\xeaN\xec\xee\ +\xdb\xbd\xa8\xfb\x82\xee'{\x10{\xf8\xf5\xe0\xf7\xf8\xba\ +\xc7i7\x0d\xb7@7\xa1\xdbR\xb7\x8b\xee\xba\xee\xe1\ +\xee\xa5\xee\xeb\xdc\xaf{\x98{$x\x8c\xf1\xd8\xee\xf1\ +\x90\xe1\xc8\xe8\xc7\x98\xc18\xcc\xa8\xf5\xf4\xf5\x14x.\ +\xf7\xbc\xcc4f\xf6b\x8ea\xeed\xfe\xe5\xd5\xdd\x8b\ +\xe3\xb5\xc0\xeb\x9c\xb7\x9ew\x8cw\xb9\xf7\x0e\xef'>\ +=|x>\xdf\xf8\x5c\xf25\xf1M\xf4\x9d\xe0{\xc0\ +\xf7\x8d\x9f\xbf\x9f\xc4\xaf\xd2\xef\x8e\xbf\xa3\x7f\x9e\xffB\ +\xff\x8b\x01\xa6\x01\xc9\x01S\x02~\x0e\xd4\x08\x8c\x08,\ +\x0f\xfc!\xf0o\x96\x1fK\xc6\xda\xc4z\x14\xe4\x1eT\ +\x1c\xb46\xe8vO\x97\x9e\xbc\x9e\xcb{\xde\x08\xa6\x07\ +\xb3\x83\x97\x04W\x85\xd0B\xf2B\x16\x87T\x85v\x0d\ +e\x87.\x0d\xfd=\xcc!\x8c\x1b\xb62\xecVx\xb7\ +\xf0A\xe1\xeb\xc3\x1fFxFH\x22\xb6F\xbc\x88d\ +E~\x16\xb9/\x8a\x14\x15\x1b5)\xeaD\xb4qt\ +z\xf4\xfc\xe8k1\xf4\x98\xc2\x98u1\x8fc}c\ +\x87\xc7\xee\x8b\xd3\x88\xeb\x1d7#\xeeb\xbcm<'\ +~M\xfc\xe3^\xfe\xbd>\xebu\xa8\xb7n\xef\xd4\xde\ +\xf3{\xff\x9e\xd0=A\x92\xb03\x91\x98\xd8+\xf1\xab\ +\xc4+}\x9c\xfa\x08\xfblO\x02I\xf1I_%]\ +MvI\x1e\x9c\xbc\xab/\xa5or\xdf\x05}\xffL\ +a\xa6\x8cH9\x9cj\x92\x9a\x9b\xba6\xf5yZD\ +\xda\xb4\xb4\xcb\xe9\xae\xe9\xf2\xf4\x03\x19\x06\x199\x19k\ +2^dFeVdVe1\xb2>\xcb:\x9am\ +\x95\xcd\xcf\xde\xd1\x8f\xda/\xa3\xdf\xca~\xd5\xfd\xa3\xfb\ +\xcf\xea\x7f3\xc77g|\xce\x85\x01.\x03\x86\x0c8\ +2\xd0j\xa0`\xe0\xee\x5c\x83\x5cv\xee\xe6<\x8d\xbc\ +\xcc\xbc\xb5y\xaf\xd9I\xec\xa5\xec\xea\xfc\xf8\xfc\x85\xf9\ +\x8f9\x91\x9c9\x9c{\xdc0\xeeL\xee\x1d^0\xaf\ +\x82w\xab \xb8\xa0\xa2\xe0vap\xe1W\x85w\x8a\ +B\x8bf\x17\xdd\xe7G\xf2\xe7\xf3\x9f\x0c\x8a\x1b\xb4h\ +\xd0\x8b\xe2\xa4\xe2U\xc5u\x82L\xc1\xc6\x12\xcd\x92\xbc\ +\x92\xef\x85\xc6\xc2b\xe1!\x91\x9dh\x88\xe8\xb4\xb8\x87\ +x\xbc\xb8j0k\xf0\xac\xc1\x8f%\xbd%+\xa5\x04\ +\xe9\x00\xe9\x0e\x99)4\xa6\x8e\xc9]\xe5\x9f\xcb\xaf\x97\ +\x86\x94.(}Y\x96Q\xb6y\x88\xd1\x10\xe1\x90c\ +C\xbb\x0f\x9d8\xf4\xd6\xb0\x98a+\x86\x93\x87s\x86\ +\x1f\x18\xd1u\xc4\xe8\x11\xd7?\x0b\xffl\xc9H\xc2\xc8\ +\xfc\x91\x07\xca\x1d\xca\xc7\x95\xdf\x1c\x15;j\xf5h\xed\ +\xd1\xc5\xa3\x8f\x8f\xf1\x1cS1\xe6\xd9\xd8\xcc\xb1;\xc7\ +\xd9\x8e\x1b5\xee\xc6\xe7\xb1\x9f\xaf\x1b\xaf?^2\xfe\ +\xe2\x84\xa0\x09\x8b\xbe \x7f\xc1\xff\xe2\xc4D\xef\x89\xf3\ +&\xd6N\xe2N\xfae\xb2\xe7\xe4\xd9\x93_O\xe1L\ +\xf9e*s\xea\xdc\xa9u_\x16|yb\x9a\xdf\xb4\ +o\xa6S\xa6\x0b\xa7_\x98\x11:cu\x85Q\xc5\xb0\ +\x8a\x1b_%~\xb5m&m\xe6\xa4\x99\xcff\xe5\xce\ +:2\xdbg\xf6\xa29\xdas\xe4s\xaa\xe6&\xcc\xdd\ +1\xcfq\xde\xf4y\xaf\xe7\x17\xcd?\xbf b\xc1\xc6\ +\x856\x0b'.|\xf15\xf7\xeb3\xdf\x84}S\xb9\ +\xc8v\xd1\xe4E5\x8b\xf9\x8b/-\x89]\xb2m\xa9\ +\xf3\xd2\xd9\xcb(\xcbJ\x97\xfd\xb9\ +\xf1\xbe\xfb\xfb\x0b\xf7\xdf8\x90{\xe0\xf2\xc1\xac\x83\xe7\ +\x0e\xf5=t\xe2\xc7\xde?\xfe\xfcS\xccO\x07\x0f\x87\ +\x1f\xde\xfbs\xf0\xcf?\x1ca\x1d\xf9\xfe\x97\x80_\xb6\ +\x1f\xf5;\xba\xed\x98\xef\xb1\xad\xc7}\x8fo=\xe1w\ +b\xdbI\xff\x93;N\x05\x9e\xday\xba\xe7\xe9=g\ +B\xcf\xec?\x1bu\xf6\xa7s\xf1\xe7\x8e\x9e\xefs\xfe\ +\xf4\x85\xf4\x0b\x97.\xe6\x5c\xac\xba\xc4\xbdt\xfbW\xc1\ +\xafO~+\xfd\xed\xd5\xe5QW4\xaeL\xbajx\ +u\xf65\x9bkK\xff\xd3\xed?\x1b\xab\xfc\xaav_\ +\x8f\xba~\xec\xf7\xd4\xdf/\xdf\xe0\xdc\xb8\xf7\x87\xf4\x8f\ +\xd77\xc7\xfd\xa9\xf7\xe7\xec[\xf6\xb7\xd6\xdc\xf6\xba\xfd\ +\xc3\x9d\x98;\xa7\xee\xf6\xbf{\xf3\x9e\xf8\xde\xab\xfb\xe3\ +\x1f\x18=X\xf8\xd0\xf5\xe1\x96Ga\x8f\x8e=\xcez\ +|\xf3\x89\xe4I\xdd_S\x9evy\xba\xea\x99\xcf\xb3\ +\x03\xd5\xc9\xd5\xd7\x9e\x97<\x7f\xf5b\xd2\xcb./W\ +\xff\x1d\xf0\xf7\xe1\x9a\xcc\x9a[\xaf\xca^S_\xcf}\ +\xd3\xed\xcd\xce\xda\xde\xb5W\xeaJ\x1a\xdeH\xe0\xc0\x81\ +\x03\x07\x0e\x1c8p\xe0\xc0\x81\x03\x07\x0e\x1c8p\xe0\ +\xc0\x81\x03\x07\x0e\x1c8p\xe0\xc0\x81\x03\x07\x8e\xffc\ +\x90\xc9dMGG\xc7\xac\xce\xe6\xe3\xff\x15aaa\ +\x8b\x0a\x0a\x0a\xea\xe0\xff7\xa8.:\x9b\x9f\xff'\xa0\ +v\x8ft\xaft)))Guuu\xed\xda\x83\x96\ +\x8e\x8e\x8e\x15\x9dNO\xf7\xf3\xf3\x9b\xd0\xabW\xaf\x1d\ +\x19\x19\x19\xe7srr\xfe\xe4p8/\x90C~\x18\ +v\x01\xc6}\xef\xef\xef\xff\x85\x83\x83C\x06\xca\xd3\x1e\ +\xbc|*@\xed\x1d\xb5{\xd5:\x188p\xe0]k\ +k\xebw\xec\xf2\xfd0@\xfdY{zz\x96%'\ +'\x1f\x86e\xd7\xaa\xd2i\xa5\xab\xed\xdb\xb7\xef\x11X\ +\xc6\x10\xd8.l\xd5\xc1\xd3\xa7\x0877\xb7\x12.\x97\ +[\xa3\x94\x9b\xc7\xe3\xbdvww\x17}ly\x16\x16\ +\x16\x01QQQ\xaba9\xaf>B\xe7-:T\x16\ +,s\xad\xa5\xa5%K\x9d\xb2\x7f*\x80r\x05\x0d\x18\ +0\xe0\x96\xaa\xcc\x91\x91\x91+\xe13\xa2\xdd\xda2`\ +\x1b\xb5A:R\x97\xce\xdf\xe5\xa2\xa3\xa3\xd7\xb7W?\ +\xd9\x99\xd0\xd6\xd6\xee\x02\xfb\x8a\x9fTeMKK;\ +\xad\xaf\xafO\x7f_>\x12\x89DA}D~~\xfe\ +\xb3\xf6\xd6\xbd\xd2AZ\xd5L&s\x18\xa4M\xed(\ +\xfdt\x04\x90.CBB\xe6\xa9\xca\x9a\x97\x97\xf7\xd0\ +\xd6\xd66\xb6\xa5\xf46661\x99\x99\x99\xbfv\x94\ +\xde\x9b\xbb\xac\xac\xac\xcb\x90\xb7\xb8\x8e\xd6S{\xc3\xd5\ +\xd55\x1f\x8e\x09/Ud}\x83\xc6R\xe59g\xf8\ +G\x84\xb6\xcc\xe7\x9d\xa5\xf7\xe6\x0e\xd9L\x88\xa7\xce\xd6\ +\x9b:ann\xee\xdb\xbf\x7f\xff\xdfU\xe5\x8c\x89\x89\ +\xd9\x80\xfa\xf9\xf8\xf8\xf8\xad\x9d\xad\xf3\xe6\x0e\xda\xae\xdb\ +\xa9T\xea[\xdf:\xfa7CKK\xcb,11q\ +\xaf\xaa\x9c\xf0\xb9\xf8\xbb\xb3u\xfd\xbe\xfe\xc8\xd8\xd8\xb8\ +Gg\xebM\x9d \x12\x89d\x16\x8b5\xa3\xb3u\xdb\ +Z\x07\xc7\xe6\xa7p\x5c\x8a\xeel\xbd\xa9\x13P\x9e\xa8\ +O\xb9\xdd7whN\xfd\xbfR\x07\xc8\xc6A\xf2\xa8\ +C/9997\xd1\xb3\x84l\x16##\xa3\xee\x14\ +\x0aEOCCC\xd7\xd0\xd0\xd0\x19\x8e57\xda\xa1\ +\x0eb:[\x7fm\x01\xb2;\xd5\xa1\xfb~\xfd\xfaU\ +\xd1\xe9\xf4\xd4\xf7\xd9(\xa8\xefn\x8f\xe7\xe0]\xb6\xf3\ +\xa7\x0ed\xff\xa0yN[u\x10\x10\x100\x05\xce\xa3\ +\xb5\xfe\x89^{\xe8\x1f9$\x83\x99\x99\x99OG\xe8\ +L]@\xf3\xfb\xe6\xeb\x11\x1f\xe1\xde\xf4\xe8\xd1cP\ +ki\xb6\x97\xfe\x91C\xb2\xfc[\xd6\xf0`\x9f\xac\x9f\ +\x9e\x9e~\xb6\xad2\x7f\xe8\x1a^{\xea\x1f9\xb4\x96\ +\x82\xc6\x9b\xf6\xd2\x9b:\x80\xe6\xb7qqq[\xd4!\ +/\xea\xf3MMM[\xfd\xa5\x90\xf6\xd6?r\xb1\xb1\ +\xb1\xdf\xa9|\xab\xec\x93\x83\x97\x97\xd7\x085\xc8\xd9\xb0\ +\xd6\x0f\xc7\xbf\xe7\xce\xce\xce\x03ZC\xbb#\xf4\x8f\x1c\ +ZGio=~\x0c,--{\xa2\xb5\xff\xb6\xca\ +\x87\xec\xcb\xbc\xbc\xbc\xc7\xcd\xc2\xa6\xa3y\xdc\xfb\xe8w\ +\x94\xfe\xd1\xbb\x04h[\xf8w\x94^[\x03\xd4\xe7\xa3\ +\xfeB\x1d}\x0e\xd4\xb3\x86\x81\x81\x81\x13\x1cC\xce\xa9\ +\xc6\xf5\xe9\xd3\xe7\x80\xb6\xb6\xb6\xc5\xbbx\xe8(\xfd#\ +\x07i\xfd\xf6!\xef6\xda\x1b\x81\x81\x81S\xd5!\x97\ +\xbf\xbf\xffDe\x99h^\xd5\xfc}\x0c\x9c\x7f\xfd\xf1\ +\xae\xb6\xd7\x91\xfaG\xce\xc7\xc7gt\xc7i\xf8\xdd@\ +k\x86h\xcdD\x1d2YYY\x855/\x1f\xbd\x9b\ +Q\xed\xd7\xd0:\x86\xab\xab+\xb7y\xba\x8e\xd6?z\ +\xaf\x81\xdaH\x87(\xf9=\x80\xfa)U\x87H\x15h\x8d\xa8\xf9\xf8\ +\x8a\xd6\x92\x0c\x0c\x0c\x1cQ\xd9\xd0^\xb8\xdeV>\xe1\ +\xdc\xf7\x91r\xde\xf1\xa9\xc0\xd9\xd9y\xa0:\xdaU\xf7\ +\xee\xdd?\xf0+\xdao\x03\xf5\xf1={\xf6\x9c\xd9\x5c\ +gAAA_\xa9\x83G\x07\x07\x87Lu\xe8L\xdd\ +P\x9e\x87i\x8bC\xedS\xb5\xbfh\x0bP\x9bh\xf6\ +\xfe\xffc\xce\x114qh\xddC\x1d\xbc\xb5\x07\x90\xed\ +\xdd|\x8d\xe0c\x1c\x9c\xe3.W\x17O\xa6\xa6\xa6^\ +\xeaX\x1bG.%%\xe5\xd8\xa7\xbeg\x1a\xbd\xa7F\ +\xfbt\xda*\xeb\x87\xbc{\xff'\xa8c\x8d\x10\x8d\xeb\ +\x9fZ\x9f\xff.\xc06\xe7\xa9\x865\xe9Z\x0f\x0f\x0f\ +I[yA6h[\xfb\x1dd[\x98\x98\x980\xd4\ +\xa1\x9b\x8e\x82\x9d\x9d]\xbc:\xe6\x05p\x1c\x9d\xf51\ +\xfb\x0e\xd0;\xb9\xe0\xe0\xe0\xb9m\xa5\x8fd\xf8\xb7\x9e\ +\x11prr\xcaQG\x1d\xa0\xf3\x8dp,\xcd}\xd7\ +\xfb\x01U \x1b\xd3\xc5\xc5%\xaf\xf9\xda\xd0\xc7\xea\x1e\ +\xc9\xd0\x11\xbaj/8::f\xabk~\x8c\xde9\ +\xa1y\x0f:_\x8a\xe6Z\xe8,\x01r\xc8\x8f\xc2\x90\ +\xfd\x85\xecLu\xd0B<#\xde;[\x7f\xea\x00\x9d\ +NOS\xe7y\xc6\xf6vH\xf7\xe8\xbcqg\xebM\ +\x9d\xa0\xd1h\xc9\xea\xda\x07\xdd\x11.00\xf0\xcb\x7f\ +\xda\xf3\xf2o\x83\x99\x99\x99\xb7:\xe6\xff\xed\xe5T\xcf\ +1#\x87\xce\xee\xa03<\x9d\xad7uBSS\xd3\ +4!!awg\xeb\xba\xb9C\xba\xd6\xd7\xd7\xa7\xc5\ +\xc4\xc4T\xaa\x86\xa35)\xb4\x97\xbb\xb3\xf5\xa6N\xc0\ +\xe7\x9a\x84\xd6:;[\xe7-\xf55h\x7f\xa7\x97\x97\ +\xd7p\x18\xfeF\xe5\xb9x\x89\xcetv\xb6\xde\xd4\x0d\ +4G\xe8\x8c}#J\x87\xf6\x5c@\x1ez\xb7\xc4\x1b\ +\xb2\xf9\x91\xbd\xa5\x9a\x1e\xceEf\xb7\xc6\x06\xfe7\xa1\ +\x93\xce\xbf?k\xcd\xf9w\xb4\xae\x8e\xf6\x9d\xab\xe6M\ +JJ\xfa\x11\x9d\xf5\xef(\xfdt\x14\xd0\xda\x0a\xfaf\ +D{\xeb\x1e}\xdb\x03\xcd\x19Z\xcb\x17\xda\xe7\xd9\x9c\ +/4\x1fD\xdf\xbchO}t\x16\x90\x8d\x14\x1e\x1e\ +\xbe\xa4\xd9\xf9\xf969\xf4\xae\x10\xed\x8dh\xcb9\x22\ +\xb4\x16\xd5l\x1fd\x8d:\xd7\x08?5 \xbb\x0f}\ +\xdb\x06\xd9J\x1f3wCy\xd0>+X\x86\x10\x96\ +e\xae\x0e\x9e\xac\xac\xac\xc2\xd17\x8eT\xe9\xa09\xf7\ +\xff\xfa\xb7\xbf\xa8T\xaa\x11\x1a\xabQ\x9f\x8d\xde\xe7\xa2\ +o2eff^Bk\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + <\ +/circle>\x0d\x0a \ + \x0d\x0a \ +\x0d\x0a\x0d\x0a\ +\x00\x00\x04\x5c\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / error / Edit\ +or only\x0d\ +\x0a Creat\ +ed with Sketch.<\ +/desc>\x0d\x0a \x0d\x0a \ +\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x15\xcc\ +I\ +I*\x00j\x08\x00\x00\x80?\xe0@\x08$\x16\x0d\x07\ +\x84BaP\xb8d6\x1d\x0f\x88DbQ8\xa4V\ +-\x17\x8cFcQ\xb8\xe4v\x0b\x02\x7f\xc7\xa4R9\ +$\x96M'\x94JeR\xb8\xa4\x82Y/\x98Lf\ +S9\xa4\xd6a.\x9bNgS\xb9\xe4\xf6}#\x9c\ +O\xe8T:%\x16\x8d/\xa0\xd1\xe9T\xbae6\x9d\ +\x1f\x81\xd3\xeaU:\xa5V\x91Q\xabVkU\xba\xe4\ +B\x93]\xb0XlU*\xfd\x8e\xcdg\xb4N\xec\xb6\ +\x9be\xb6\xdd&\xb5\xdb\xeeW;\xa4J\xe3u\xbc^\ +o7{\xd5\xf6\xfdi\xbe_\xf0X:\xde\x07\x09\x87\ +\xc4Sp\xd6`\x0e6\xb4\xd0\xc8\x02\xdf\xb91L\x14\ +3\x05\x0dH\x02PWX\x0b<\xe6\xc6\x80\x5c\xc0]\ +#XY\xa7}Vqx\x0a\xc5\xcfCJflB\ +\x90R\x86\x84\x9f \x22AA\x11\xd7\x9e\x85o X\ +\x828K-8\xb1\xe9G\xd5\xda96\x1d|\xf5\xa9\ +\xcf\x06>:G8)\xd2\x0a\x0c\x9e; \xa8>\x10\ +!1\xc5\xd4\xcf9vo\x1dw\x9b6f\xfaK\x12\ +\x04\x84\x14/RohL\xc3?\xa2\xf6s\xe5\xb1~\ ++~y\x5c\x80\x04\xf4\x99\xa4*\x0a<,G\xe3B\ +8\xbe\x81\x99(\x99\xbfK\x04\x1a\xac\xbf\x893\x9ej\ +\x00\xce\x91\xf0U\xa0\xa2r\xde\xd0\x92\xd0H\xd6\xab\xa4\ ++\xec\x1e\xabB)+bf\x13\x88(\xc6\x94\x1c\xa8\ +)\x9c\x82\x9dH)\xe2\xd0\x82I\x03\xde\x82\x06\xa8+\ +f\x924#\xe4\x13\x01%\x11\x1b\x0a\xd6\xaeQ*;\ +\x13\x8d\xe8+\xda\x8e\x1d\xc8)*\x82\x94\xc1\xa4\xa4l\ +\x22\xc9\x00\x02h\xcb\x01\x81\xf9-\x8c\x11R\x0a\x05\xa3\ +1\x08\x00)JA\xa1`\x92\xc8J\xd4\xd2\xa9\xc8\xc8\ +\xc4N\x16\xa0\xa6z\x0a\x01\xa2\xb3\x11\x1c\x02O\x03\xf8\ +a=\x9e\xa9y\xad?\x82g\xb5\x05%\x80\x02\xe22\ +y\xcf\x00 I=\x86\x11\x8a;5\xaa\xd4\x82\x9f6\ +\xa2\xf19j\x82\x89H\xa9\xea\xcf\x00B\x88eO\x97\ +j\x19\x9dQ\x8b\xc7\xf5LONh\x93BJ\xc1#\ +b=I*\x95\x82\x9bJ\x22F}l 2g\xe9\ +~\x8a\x9f\x14\xc4\xcb]\xa9\x918\xb6\x82\x94H(\x04\ +\x88\x9fm\x08U\x04\x9b\x88\xd5d\xb2H\x90\xdb\x1c\x8d\ +D\xe5\xa2\x0a%\xa2\xa3\xa4\xcaF\xab0\x01(\x90C\ +\xe8\xa1)2\x8d\xb6u\xa2\xbaY\xea]h\x86\xc2n\ +\x8b\xa4\xed \x80:\x22i\xc1!\x8bB~\xab&\xbd\ +\xf4\x06\x9e\xb7\xed\x9a\x82\x02\xc8\x89\xc72\x83\xd74\xc4\ +\xbd\xdc\xebm\xd6\x86@\x02\x9a@VULh\xdd\x04\ +\x92k\x1cNG \xa3\x8a*\x19\xcc\xb1|\xeb\x84\xae\ +WJ\x95\x85\xa1p\x012\x90\x0c\xc8\xa852\x9c\xeb\ +\x1dFg\x07U1\xfcb\xe2 \x08\xf1\x04\x912\xae\ +@\xb7\xe4J>H\x85D\xe5\xb2\x0a$\xa2'\xac\xca\ +\xec-\x19x+\x99Q\xc8\x849\x0fgX:\xf1\x9e\ +\xa8\xd9\xfa\x13\x13\x9a\x08(^\x88\x9bs(L\xb7\xc4\ +\xe7\xda\x0a\x02\x22%\x84\xca(\xea+\xf6\xa8\xa2\xea\xc8\ +DO\xa6\x82\xa8\x89\xb12\xb2\xabtO^\xde(\x89\ +\x932\x87\x1bLE\x9d\xad\x1bj\x0f\x13\xb8\xe8$\xc0\ +\x88nr\x96\xea\xb6\xee\xe8-\xe4\x88\x1a\x93,\xe1\x8f\ +\xeaK\xae\xd6\xa2pH7\x09/\xee[\xa6\xc0\xd8\xef\ +\x00\x07\x1e\x87\xf22\x97&\x96\xf0\x0bo.\xa1\xf3(\ +/7\xc3s\xbcW>ft=\x1a\x1d\xd2\x86\x9d:\ +'\xd5\xa8\xdd\xe2\x7f\xd6\xa0\x9dx\x01\xc3\xa1\xfcHi\ +\xc5\xad\x9cn\xf3\xc8r[\xf2\xf5\xdf'\xde\x00\x01\xe1\ +x\x88w\x8d\xe4->WD\x88\xf7\x1d\xd2\xed\xd4\xad\ +\x9e\x82{\xc1\x1b_(\x0ey}\x12j\x08\x05k\x94\ +\xe0v\xb7\xe6G\x22\x0a\x03\x22&\xbe\xb52\xecH\x8f\ +\xc4\xa1\x7fi\xdc\x22\x95\x99xk$\x0a\xb8\x82\x02s\ +\x12K\x13\x10\xd14\x22%\x04\x8a\x82\x14\xffI\xf4\x10\ +'&\xbd\x09\x81$,+\x88(?\x80\xe5\x14V\x9d\ +\xd0\xb4x \x91\xe2|\x05\x8c\xd7\xa2qLAB\xcc\ +\x1b]F4C\xa0\x90\xf5\x08KT#,J\xd8g\ +\x84Vd.\xa1QS\x1f\xa6\x84\x16\xa9\xf0d\xfd\x9e\ +|2,(\x00L\x10P\xcf\x0eJ\xb0zA\x22\x1d\ +\xbf\xb9R\xdc\x80\x05\xc9\x05\x08\xc4T^/x\x90\xe5\ +\x1a@\x00}\xe4D\xd0\x89\xb8|\xcab\x0cN-\xa8\ +\x00`A\x92$\xa2@b\x8cO\xb1\x5c\x89\x99\x01\xa0\ +\x0b\x12\xd8\xfc\x1adTQ \x90\xbf\x13K\xc4d\x8c\ +\xc4F4F\xa8\xd9\x1bL\x84pKq\xcc\x8aGS\ +\xe9\x1d\xe3\x0cy=1\x94\x82A\xa8\xf8\x9e#J|\ +\x8f\xe4J7H(\xe5\x1d#\xb4x.\xb1\xeaF\xc6\ +y!\x1f\xa4\x99\x10\x92\xb1\xc6B\x119\x0c\x0c\xe4C\ +\x08\x8cE\xb2N\x00\x09\x1cD#\xec\x92\x94$>Q\ +\xc891!\xe4\xd1t\x95\xb2\xbc\x87\xcb\x10a\x1a\xc9\ +\xdcn\x04*\xe4 \x12\x00(hG8\x07\x99B\xf0\ +\xe2\x8e\x82{-d\xbc\x85\x932&M\x90\x06k5\ +\x80\x00\x82\x8f\xe0\xb0\x88L*\x0a\x04\x86\x83\x06\x11\x07\ +\xac.'\x14\x8a\xc5\xa1,\xc8\xc8*\x10\x88\x84\x1b!\ +\x00\x18\xbb\xec\x03$A\x0c\xa4\xe8\x89 \x05\xf9\x17\x96\ +\xcb\xa2\x8d\x09\x88\xb1\xf94i\xcb\xe1J!\x9c\xe8\xbf\ +7\x9e\xcf\xa7\xf4\x08\xbb\xfe\x87A\xa2\xd1\xa8\xf18\x14\ +\x0e\x11\x07\x9b\xc3@\x90\xf8\x8d\x22z\xda\xaa\x81\xdeU\ +\x86D aS\x82\xad\x86\x96\x02]v\x7f1hL\ +\xe6\xb4\x09\xcc\xee\xc7l\xb6\xcbho\xfbu\xca\xe7J\ +\x82A\xa7\xb4\xfa\x88\xc2%s\x89\xc6Y\x88xA\xe2\ +\xe7*4N\x86i\x9b\xecN\xcbg~M\xa7\xf6\xa1\ +\x9c\xf3\x15\x94\xae\xdc2\xb9\x8a\x0d\xd6\x99x\x87D/\ +y[\xf8>\x10\xeb\x84\x01r\x8e\x9c8jT\xfe\xca\ +\xe3&\x98\xebN\x1f'\x99\xda\xcf\xb2\xfbm\xcc_7\ +w\xa7g\xaaYFw\x08\x8a\xfe\xe2\xae\xb6\xd4\xf1>\ +}\xb5\xae\x99l1\xf3\xec\x8e\xd3u\xd4\x8an:\xbd\ +\x88.\xf0\x01M\x97\xdes\xf7\xccU\xfc\xa3\x08Wu\ +\x06v\x01\xa3;\x9bf\xe7\xec\xad}\x9f\x8c\x17\xaf\xf2\ +\xddv\xfb\xb2\xee\xff\x03\x14\xcf\xfe\x8c\x0f\xd8\x00\xcfm\ +\x80\x88\x10\x13\x0b s\xb9\xecc]\x04\xf5\xd2}_\ +\x17\xd2\x0fm_vuPx\x19U\xc0\x02R\x9c\xc4\ +\x14#e\x0b\xf7\xa4Bm\x9a\xf5\xa1\x90l\xe1'b\ +\x11\x8a\x19XQ\xbe\x85\x9f\xb6a\x7f\x15\x90\x82\xa5r\ +?\x008\xe0@\x0cc\xb3\x16#s\xa2WF'\x8a\ +\xdb\xa8\xaaC_b\xd7y\xbfh\x1dE\xfczB\x08\ +D\x81@>\x90\x81\x99\xe9(]X\x91\xb1\x89\x9f\x09\ +\x19\xb5\x91e\xd5\xbaH~d\xa7\x85\xd8R\x88e\xc0\ +yP\x06W\xa4\x9c|\xa5\x9817\x83\xa6\x06f_\ +\x9d\x169\x89-~\xa4\xb7eJ\x1d\x97\x02%@\x15\ +\x9e\x92\xaeo\x8f\xe5\xa9\x06\x5c\x9d\xd8\xa9\xda\x8bR'\ +\x94^{\x99]Y\xfa\x80\xa0\xa8J\x19\xed\x90 \xd9\ +\x0a\x8e_h\xdayE\xa4\x11jI\xf2\xa5T:\x05\ +?\xa0\xd6\x0a\x15\xf1\x9c\x1e\xf6J\xa1\xa7\xd4J\xc9l\ +\xa8\xd1Z\x95\xf1R\x85\xc5\xc0\xa3Oc\x80\x0c<\x8e\ +\xc3\x18\xf6\xae\xa1\xe7\x14\xbes\xadV\xda\x82\xcbOk\ +tR\xb9vMKP\x12>-sY\x08\x05\xd1s\ +V\x04\x02\x03(\x1c,\x94\xecjj\x88\xa7(\xab9\ +S\xb3n\x94\xba\xd0D\xed'\xd5\x7f\x0a\x11\xc4 )\ +J\x8c\xb0\x0a\xf9\x1d\xec3\x96(\xab\xe5\xba\xc6\xece\ +\xabL\x0a\xa2@\x97gr\x15^\xa9<\x16\xfe\xb1\xeb\ +\x07O\x0dPn\xbcII\xc1\xd9\xc8\xbb\x0b\xc5d;\ +\xfe\x89\xc0q\xb5\x03\x14\xc8\x10\x8b\xb9\x0b\xbc2;\x92\ +\x0b\xc42\x84\xff\x22\xca2T+'\xcb%\x8c?\x00\ +\xc4s5\x0b\x04\xce\x11\x5c\xc1\x09\xcc\xb3\xb8\xfa\xe5\xb2\ +\x12\xeb+@E\xb2\xec\x8dJ/\x90\x81\x05=J\x85\ +E\xc0\xf8\xd1\x9f\x10\x81\x08%4\xe4\x90\xa0I\xc3!\ +\x87S\xceW\x1dy\x0bR\xaa\xd0\x00T\xd8vt$\ +\x8da\xc7M\xa1\x13\xd22\x05(oB\x09\x0d\xb7h\ +\x14Xr\xc3uB\xb6\xfcl\xd8\xdf\x80\xd3\xd7\x81t\ +\x01\xfd\xeb,2\xd8p\xe9\x08K8W\xcf:\xda\x12\ +\xa0\x01\x7f\x88\x90R\xe1\xa6\xe3.\xc3\xd1*\x0d\x98s\ +_|\xca9\xecK\x90BVP\xc5\xb0\x95\xd0P\xb7\ +\x97\xa2\xcc \x1b\xad\x18B\xee\xc0\xdeBz\x0c\x83\xb4\ +\xea\xbb~\xe2\xe9\xed\xbb\x9e\xf3\xbd\x9d;\xbe\xfb\xc1\xf0\ +\x9f/\x03\xc3\xf1\xbcy\xd7\x8e\xf2<\xbf1\xf5\xf1|\ +\xdfC\xd1\xc8|\xafK\xd5\xf5\x96\xef?\xd7\xf6\xbd\xbf\ +g\xdb\xf7\xbd/w\xdf\xf8\xbc\xbf\x87\xe3\xf9\xbc/\x97\ +\xe7\xfa\xbb\x9f\xa7\xeb\xfb\xb8\xcf\xb7\xef\xfc\xb6\x8f\xc7\xf3\ +\xfd\xb4o\xd7\xf7\xfe\xb2\xc4\x04\x13\x00\xfe\x00\x04\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x01\x00\x00\x00`\ +\x00\x00\x00\x01\x01\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x02\ +\x01\x03\x00\x04\x00\x00\x00T\x09\x00\x00\x03\x01\x03\x00\x01\ +\x00\x00\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x11\x01\x04\x00\x01\x00\x00\x00\x08\x00\x00\x00\x15\ +\x01\x03\x00\x01\x00\x00\x00\x04\x00\x00\x00\x16\x01\x04\x00\x01\ +\x00\x00\x00`\x00\x00\x00\x17\x01\x04\x00\x01\x00\x00\x00b\ +\x08\x00\x00\x1a\x01\x05\x00\x01\x00\x00\x00\x5c\x09\x00\x00\x1b\ +\x01\x05\x00\x01\x00\x00\x00d\x09\x00\x00\x1c\x01\x03\x00\x01\ +\x00\x00\x00\x01\x00\x00\x00(\x01\x03\x00\x01\x00\x00\x00\x02\ +\x00\x00\x001\x01\x02\x00\x10\x00\x00\x00l\x09\x00\x00=\ +\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x00R\x01\x03\x00\x01\ +\x00\x00\x00\x02\x00\x00\x00S\x01\x03\x00\x04\x00\x00\x00|\ +\x09\x00\x00s\x87\x07\x00H\x0c\x00\x00\x84\x09\x00\x00\x00\ +\x00\x00\x00\x08\x00\x08\x00\x08\x00\x08\x00\x802\x02\x00\xe8\ +\x03\x00\x00\x802\x02\x00\xe8\x03\x00\x00paint\ +.net 4.0.9\x00\x01\x00\x01\x00\x01\ +\x00\x01\x00\x00\x00\x0cHLino\x02\x10\x00\x00m\ +ntrRGB XYZ \x07\xce\x00\x02\x00\ +\x09\x00\x06\x001\x00\x00acspMSFT\x00\ +\x00\x00\x00IEC sRGB\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xd6\x00\x01\x00\x00\x00\ +\x00\xd3-HP \x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x11cprt\x00\x00\x01P\x00\ +\x00\x003desc\x00\x00\x01\x84\x00\x00\x00lw\ +tpt\x00\x00\x01\xf0\x00\x00\x00\x14bkpt\x00\ +\x00\x02\x04\x00\x00\x00\x14rXYZ\x00\x00\x02\x18\x00\ +\x00\x00\x14gXYZ\x00\x00\x02,\x00\x00\x00\x14b\ +XYZ\x00\x00\x02@\x00\x00\x00\x14dmnd\x00\ +\x00\x02T\x00\x00\x00pdmdd\x00\x00\x02\xc4\x00\ +\x00\x00\x88vued\x00\x00\x03L\x00\x00\x00\x86v\ +iew\x00\x00\x03\xd4\x00\x00\x00$lumi\x00\ +\x00\x03\xf8\x00\x00\x00\x14meas\x00\x00\x04\x0c\x00\ +\x00\x00$tech\x00\x00\x040\x00\x00\x00\x0cr\ +TRC\x00\x00\x04<\x00\x00\x08\x0cgTRC\x00\ +\x00\x04<\x00\x00\x08\x0cbTRC\x00\x00\x04<\x00\ +\x00\x08\x0ctext\x00\x00\x00\x00Copyr\ +ight (c) 1998 He\ +wlett-Packard Co\ +mpany\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00\x12sRGB IEC61966\ +-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\ +sRGB IEC61966-2.\ +1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00\xf3Q\x00\ +\x01\x00\x00\x00\x01\x16\xccXYZ \x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\ +\x00\x00\x00\x00\x00o\xa2\x00\x008\xf5\x00\x00\x03\x90X\ +YZ \x00\x00\x00\x00\x00\x00b\x99\x00\x00\xb7\x85\x00\ +\x00\x18\xdaXYZ \x00\x00\x00\x00\x00\x00$\xa0\x00\ +\x00\x0f\x84\x00\x00\xb6\xcfdesc\x00\x00\x00\x00\x00\ +\x00\x00\x16IEC http://ww\ +w.iec.ch\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x16IEC http://w\ +ww.iec.ch\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00.IEC 61966-2.1\ + Default RGB col\ +our space - sRGB\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.IEC \ +61966-2.1 Defaul\ +t RGB colour spa\ +ce - sRGB\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00,Refer\ +ence Viewing Con\ +dition in IEC619\ +66-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00,Reference View\ +ing Condition in\ + IEC61966-2.1\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00view\x00\x00\x00\x00\x00\ +\x13\xa4\xfe\x00\x14_.\x00\x10\xcf\x14\x00\x03\xed\xcc\x00\ +\x04\x13\x0b\x00\x03\x5c\x9e\x00\x00\x00\x01XYZ \x00\ +\x00\x00\x00\x00L\x09V\x00P\x00\x00\x00W\x1f\xe7m\ +eas\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x8f\x00\ +\x00\x00\x02sig \x00\x00\x00\x00CRT c\ +urv\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x05\x00\ +\x0a\x00\x0f\x00\x14\x00\x19\x00\x1e\x00#\x00(\x00-\x00\ +2\x007\x00;\x00@\x00E\x00J\x00O\x00T\x00\ +Y\x00^\x00c\x00h\x00m\x00r\x00w\x00|\x00\ +\x81\x00\x86\x00\x8b\x00\x90\x00\x95\x00\x9a\x00\x9f\x00\xa4\x00\ +\xa9\x00\xae\x00\xb2\x00\xb7\x00\xbc\x00\xc1\x00\xc6\x00\xcb\x00\ +\xd0\x00\xd5\x00\xdb\x00\xe0\x00\xe5\x00\xeb\x00\xf0\x00\xf6\x00\ +\xfb\x01\x01\x01\x07\x01\x0d\x01\x13\x01\x19\x01\x1f\x01%\x01\ ++\x012\x018\x01>\x01E\x01L\x01R\x01Y\x01\ +`\x01g\x01n\x01u\x01|\x01\x83\x01\x8b\x01\x92\x01\ +\x9a\x01\xa1\x01\xa9\x01\xb1\x01\xb9\x01\xc1\x01\xc9\x01\xd1\x01\ +\xd9\x01\xe1\x01\xe9\x01\xf2\x01\xfa\x02\x03\x02\x0c\x02\x14\x02\ +\x1d\x02&\x02/\x028\x02A\x02K\x02T\x02]\x02\ +g\x02q\x02z\x02\x84\x02\x8e\x02\x98\x02\xa2\x02\xac\x02\ +\xb6\x02\xc1\x02\xcb\x02\xd5\x02\xe0\x02\xeb\x02\xf5\x03\x00\x03\ +\x0b\x03\x16\x03!\x03-\x038\x03C\x03O\x03Z\x03\ +f\x03r\x03~\x03\x8a\x03\x96\x03\xa2\x03\xae\x03\xba\x03\ +\xc7\x03\xd3\x03\xe0\x03\xec\x03\xf9\x04\x06\x04\x13\x04 \x04\ +-\x04;\x04H\x04U\x04c\x04q\x04~\x04\x8c\x04\ +\x9a\x04\xa8\x04\xb6\x04\xc4\x04\xd3\x04\xe1\x04\xf0\x04\xfe\x05\ +\x0d\x05\x1c\x05+\x05:\x05I\x05X\x05g\x05w\x05\ +\x86\x05\x96\x05\xa6\x05\xb5\x05\xc5\x05\xd5\x05\xe5\x05\xf6\x06\ +\x06\x06\x16\x06'\x067\x06H\x06Y\x06j\x06{\x06\ +\x8c\x06\x9d\x06\xaf\x06\xc0\x06\xd1\x06\xe3\x06\xf5\x07\x07\x07\ +\x19\x07+\x07=\x07O\x07a\x07t\x07\x86\x07\x99\x07\ +\xac\x07\xbf\x07\xd2\x07\xe5\x07\xf8\x08\x0b\x08\x1f\x082\x08\ +F\x08Z\x08n\x08\x82\x08\x96\x08\xaa\x08\xbe\x08\xd2\x08\ +\xe7\x08\xfb\x09\x10\x09%\x09:\x09O\x09d\x09y\x09\ +\x8f\x09\xa4\x09\xba\x09\xcf\x09\xe5\x09\xfb\x0a\x11\x0a'\x0a\ +=\x0aT\x0aj\x0a\x81\x0a\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\ +\xf3\x0b\x0b\x0b\x22\x0b9\x0bQ\x0bi\x0b\x80\x0b\x98\x0b\ +\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\x12\x0c*\x0cC\x0c\x5c\x0c\ +u\x0c\x8e\x0c\xa7\x0c\xc0\x0c\xd9\x0c\xf3\x0d\x0d\x0d&\x0d\ +@\x0dZ\x0dt\x0d\x8e\x0d\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\ +\x13\x0e.\x0eI\x0ed\x0e\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\ +\xee\x0f\x09\x0f%\x0fA\x0f^\x0fz\x0f\x96\x0f\xb3\x0f\ +\xcf\x0f\xec\x10\x09\x10&\x10C\x10a\x10~\x10\x9b\x10\ +\xb9\x10\xd7\x10\xf5\x11\x13\x111\x11O\x11m\x11\x8c\x11\ +\xaa\x11\xc9\x11\xe8\x12\x07\x12&\x12E\x12d\x12\x84\x12\ +\xa3\x12\xc3\x12\xe3\x13\x03\x13#\x13C\x13c\x13\x83\x13\ +\xa4\x13\xc5\x13\xe5\x14\x06\x14'\x14I\x14j\x14\x8b\x14\ +\xad\x14\xce\x14\xf0\x15\x12\x154\x15V\x15x\x15\x9b\x15\ +\xbd\x15\xe0\x16\x03\x16&\x16I\x16l\x16\x8f\x16\xb2\x16\ +\xd6\x16\xfa\x17\x1d\x17A\x17e\x17\x89\x17\xae\x17\xd2\x17\ +\xf7\x18\x1b\x18@\x18e\x18\x8a\x18\xaf\x18\xd5\x18\xfa\x19\ + \x19E\x19k\x19\x91\x19\xb7\x19\xdd\x1a\x04\x1a*\x1a\ +Q\x1aw\x1a\x9e\x1a\xc5\x1a\xec\x1b\x14\x1b;\x1bc\x1b\ +\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c*\x1cR\x1c{\x1c\xa3\x1c\ +\xcc\x1c\xf5\x1d\x1e\x1dG\x1dp\x1d\x99\x1d\xc3\x1d\xec\x1e\ +\x16\x1e@\x1ej\x1e\x94\x1e\xbe\x1e\xe9\x1f\x13\x1f>\x1f\ +i\x1f\x94\x1f\xbf\x1f\xea \x15 A l \x98 \ +\xc4 \xf0!\x1c!H!u!\xa1!\xce!\xfb\x22\ +'\x22U\x22\x82\x22\xaf\x22\xdd#\x0a#8#f#\ +\x94#\xc2#\xf0$\x1f$M$|$\xab$\xda%\ +\x09%8%h%\x97%\xc7%\xf7&'&W&\ +\x87&\xb7&\xe8'\x18'I'z'\xab'\xdc(\ +\x0d(?(q(\xa2(\xd4)\x06)8)k)\ +\x9d)\xd0*\x02*5*h*\x9b*\xcf+\x02+\ +6+i+\x9d+\xd1,\x05,9,n,\xa2,\ +\xd7-\x0c-A-v-\xab-\xe1.\x16.L.\ +\x82.\xb7.\xee/$/Z/\x91/\xc7/\xfe0\ +50l0\xa40\xdb1\x121J1\x821\xba1\ +\xf22*2c2\x9b2\xd43\x0d3F3\x7f3\ +\xb83\xf14+4e4\x9e4\xd85\x135M5\ +\x875\xc25\xfd676r6\xae6\xe97$7\ +`7\x9c7\xd78\x148P8\x8c8\xc89\x059\ +B9\x7f9\xbc9\xf9:6:t:\xb2:\xef;\ +-;k;\xaa;\xe8<' >`>\xa0>\xe0?\ +!?a?\xa2?\xe2@#@d@\xa6@\xe7A\ +)AjA\xacA\xeeB0BrB\xb5B\xf7C\ +:C}C\xc0D\x03DGD\x8aD\xceE\x12E\ +UE\x9aE\xdeF\x22FgF\xabF\xf0G5G\ +{G\xc0H\x05HKH\x91H\xd7I\x1dIcI\ +\xa9I\xf0J7J}J\xc4K\x0cKSK\x9aK\ +\xe2L*LrL\xbaM\x02MJM\x93M\xdcN\ +%NnN\xb7O\x00OIO\x93O\xddP'P\ +qP\xbbQ\x06QPQ\x9bQ\xe6R1R|R\ +\xc7S\x13S_S\xaaS\xf6TBT\x8fT\xdbU\ +(UuU\xc2V\x0fV\x5cV\xa9V\xf7WDW\ +\x92W\xe0X/X}X\xcbY\x1aYiY\xb8Z\ +\x07ZVZ\xa6Z\xf5[E[\x95[\xe5\x5c5\x5c\ +\x86\x5c\xd6]']x]\xc9^\x1a^l^\xbd_\ +\x0f_a_\xb3`\x05`W`\xaa`\xfcaOa\ +\xa2a\xf5bIb\x9cb\xf0cCc\x97c\xebd\ +@d\x94d\xe9e=e\x92e\xe7f=f\x92f\ +\xe8g=g\x93g\xe9h?h\x96h\xeciCi\ +\x9ai\xf1jHj\x9fj\xf7kOk\xa7k\xffl\ +Wl\xafm\x08m`m\xb9n\x12nkn\xc4o\ +\x1eoxo\xd1p+p\x86p\xe0q:q\x95q\ +\xf0rKr\xa6s\x01s]s\xb8t\x14tpt\ +\xccu(u\x85u\xe1v>v\x9bv\xf8wVw\ +\xb3x\x11xnx\xccy*y\x89y\xe7zFz\ +\xa5{\x04{c{\xc2|!|\x81|\xe1}A}\ +\xa1~\x01~b~\xc2\x7f#\x7f\x84\x7f\xe5\x80G\x80\ +\xa8\x81\x0a\x81k\x81\xcd\x820\x82\x92\x82\xf4\x83W\x83\ +\xba\x84\x1d\x84\x80\x84\xe3\x85G\x85\xab\x86\x0e\x86r\x86\ +\xd7\x87;\x87\x9f\x88\x04\x88i\x88\xce\x893\x89\x99\x89\ +\xfe\x8ad\x8a\xca\x8b0\x8b\x96\x8b\xfc\x8cc\x8c\xca\x8d\ +1\x8d\x98\x8d\xff\x8ef\x8e\xce\x8f6\x8f\x9e\x90\x06\x90\ +n\x90\xd6\x91?\x91\xa8\x92\x11\x92z\x92\xe3\x93M\x93\ +\xb6\x94 \x94\x8a\x94\xf4\x95_\x95\xc9\x964\x96\x9f\x97\ +\x0a\x97u\x97\xe0\x98L\x98\xb8\x99$\x99\x90\x99\xfc\x9a\ +h\x9a\xd5\x9bB\x9b\xaf\x9c\x1c\x9c\x89\x9c\xf7\x9dd\x9d\ +\xd2\x9e@\x9e\xae\x9f\x1d\x9f\x8b\x9f\xfa\xa0i\xa0\xd8\xa1\ +G\xa1\xb6\xa2&\xa2\x96\xa3\x06\xa3v\xa3\xe6\xa4V\xa4\ +\xc7\xa58\xa5\xa9\xa6\x1a\xa6\x8b\xa6\xfd\xa7n\xa7\xe0\xa8\ +R\xa8\xc4\xa97\xa9\xa9\xaa\x1c\xaa\x8f\xab\x02\xabu\xab\ +\xe9\xac\x5c\xac\xd0\xadD\xad\xb8\xae-\xae\xa1\xaf\x16\xaf\ +\x8b\xb0\x00\xb0u\xb0\xea\xb1`\xb1\xd6\xb2K\xb2\xc2\xb3\ +8\xb3\xae\xb4%\xb4\x9c\xb5\x13\xb5\x8a\xb6\x01\xb6y\xb6\ +\xf0\xb7h\xb7\xe0\xb8Y\xb8\xd1\xb9J\xb9\xc2\xba;\xba\ +\xb5\xbb.\xbb\xa7\xbc!\xbc\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\ +\x84\xbe\xff\xbfz\xbf\xf5\xc0p\xc0\xec\xc1g\xc1\xe3\xc2\ +_\xc2\xdb\xc3X\xc3\xd4\xc4Q\xc4\xce\xc5K\xc5\xc8\xc6\ +F\xc6\xc3\xc7A\xc7\xbf\xc8=\xc8\xbc\xc9:\xc9\xb9\xca\ +8\xca\xb7\xcb6\xcb\xb6\xcc5\xcc\xb5\xcd5\xcd\xb5\xce\ +6\xce\xb6\xcf7\xcf\xb8\xd09\xd0\xba\xd1<\xd1\xbe\xd2\ +?\xd2\xc1\xd3D\xd3\xc6\xd4I\xd4\xcb\xd5N\xd5\xd1\xd6\ +U\xd6\xd8\xd7\x5c\xd7\xe0\xd8d\xd8\xe8\xd9l\xd9\xf1\xda\ +v\xda\xfb\xdb\x80\xdc\x05\xdc\x8a\xdd\x10\xdd\x96\xde\x1c\xde\ +\xa2\xdf)\xdf\xaf\xe06\xe0\xbd\xe1D\xe1\xcc\xe2S\xe2\ +\xdb\xe3c\xe3\xeb\xe4s\xe4\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\ +\x1f\xe7\xa9\xe82\xe8\xbc\xe9F\xe9\xd0\xea[\xea\xe5\xeb\ +p\xeb\xfb\xec\x86\xed\x11\xed\x9c\xee(\xee\xb4\xef@\xef\ +\xcc\xf0X\xf0\xe5\xf1r\xf1\xff\xf2\x8c\xf3\x19\xf3\xa7\xf4\ +4\xf4\xc2\xf5P\xf5\xde\xf6m\xf6\xfb\xf7\x8a\xf8\x19\xf8\ +\xa8\xf98\xf9\xc7\xfaW\xfa\xe7\xfbw\xfc\x07\xfc\x98\xfd\ +)\xfd\xba\xfeK\xfe\xdc\xffm\xff\xff\ +\x00\x00\x03\xae\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Default - Upd\ +ated\x0d\x0a \ + Created \ +with Sketch.\x0d\x0a <\ +/defs>\x0d\x0a \ +\x0d\x0a \x0d\x0a <\ +/g>\x0d\x0a\x0d\x0a\ +\x00\x00\x04\x8d\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / error / Not \ +active \x0d\ +\x0a Creat\ +ed with Sketch.<\ +/desc>\x0d\x0a \x0d\x0a \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x04s\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Editor only \ +- Updated\x0d\x0a Cre\ +ated with Sketch\ +.\x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\ + \x0d\x0a\ +\x0d\x0a\ +\x00\x00\x15\x0a\ +I\ +I*\x00\xa8\x07\x00\x00\x80?\xe0@\x08$\x16\x0d\x07\ +\x84BaP\xb8d6\x1d\x0f\x88DbQ8\xa4V\ +-\x17\x8cFcQ\xb8\xe4v\x0b\x02\x7f\xc7\xa4R9\ +$\x96M'\x94JeR\xb8\xa4\x82Y/\x98Lf\ +S9\xa4\xd6a.\x9bNgS\xb9\xe4\xf6}#\x9c\ +O\xe8T:%\x16\x8d/\xa0\xd1\xe9T\xbae6\x9d\ +\x1f\x81\xd3\xeaU:\xa5V\x91Q\xabVkU\xba\xe4\ +B\x93]\xb0XlU*\xfd\x8e\xcdg\xb4N\xec\xb6\ +\x9be\xb6\xdd&\xb5\xdb\xeeW;\xa4J\xe3u\xbc^\ +o7{\xd5\xf6\xfdi\xbe_\xf0X:\xde\x07\x09\x87\ +\xc4Sp\xd6`\x0e6\xb5 \x05\xc1E0P\xcc\x14\ +5\x05\x09A]pW6r\x0a\xd6\xc6\x80_U\x9c\ +^\x02\xb1s\xd0\xd2\xa4\x01H)B\x0aO\x82\x91 \ +\xa0\x88\xeb\xce\x0a\xb7\x82\xac`\xab-\x0b\xd2\x8f\xa5\xb4\ +p,:\x99\xec\x80\x19\x059\xc1N\x90^<\xed\xd9\ +\x05A\xc1S\x1a\x1d\x1c\xf3\x85f\xeb\xd7x\x93i\x01\ +b\x0a\x90\x82\x85\xeaM\xe8)\x9bB\xbd\x9c\xf6l^\ +\xaa\xdfnW \x01AP\xb0S\xc5\x89\xf9\x058\xe8\ +R\x93?e\x83\xfa\xac\xbd\xcb\x82\x04\x03 \xa5Z\x0a\ +'.\x84\xb3B5\xaa\xe9\x0a\xfa\xff\xaa\xd0\x0aJ\x90\ +\x13\x88(\xc6\x94\x1c\xa8)\x9c\x82\x9dH)\xe2\xcc<\ +((j\x82\xb5\x898\xf8\xd0\xbeiD \xc2\xb4\xeb\ +\x94$\x8e\xa4\x03{\xbe\x8e\x9d\xc8)*\x82\x94\xcd\x09\ +\xb0\x8b$\x00\x0a\x0a\x18 \xa3\x04,\x82\xb2(\xc4\x1c\ +\x00\x0aM\x09a\x09\xc5\xab\xacV\xaaE\xf22\x04\x16\ +\xa0\xa6z\x0a\x01\xa2\xb29\x1c\x82\x8f\xed\x09\xeb\x06\x82\ +q\x9a\x08.#-\xb2\x08\x1240\xeca&\xae\x92\ +z\xa7(\xa2\xe9\x01j\x82\x89H\xac\xbe\x82\x0a-\x09\ +v\xa1\xa4\x02\xf2\x0aOJ\xe8\xa9*\xd0\x8d\x88\xf4\xdc\ +\xabP\xea|\xe0\xbb B\x02\x0a_\xa2\xa7\xc4\xe8\xd0\ +\xd2\x0ab@-\xa0\xa5\x12\x0a\xf8\xa2\x07\xda\x0a\x154\ +&\xe25D\xaa\x95\x22\x9bE\xa2)\x01h\x82\x89h\ +\xa8\xe8\xd0\x91\xad\x22\x04\xfd\xa0\x90b(J4#m\ +G6.u2\x99T!\x8e2\x0a\xe7\xa0\x80:\x22\ +i\xa0\xa1\x8bB~\xd6G\xf8\x1a\x82\xd4H ,\x88\ +\x9cm\x08=]\xc8\xeb\xddx\xb6X\x08Z@)\xa0\ +\xa5b*74$\x9a\xc6\x90Kh \xe2\x8a\x86m\ +\x0c7,\xdbk}|\xa5\xdb\xa8R@L\xbc\xa8\xa8\ +4\xd0\x9c\xf7:\x04\x1d \xa6**<4$Ly\ +x\xad\xd7\x9a\x95z\xa1)\x01l\x82\x89(\x89\xea\xd0\ +\xb9\xab:@\x0a\xc3\x88\xac\x14\xc6\xd6\xc9n\x10\xb6\xe1\ +J>\x18\x84$\x06\x82\x0a\x17\xa2&\xdbB\x13^H\ +\x15<\x82\x00\x88\x89`\xd0\x8a8=\xb2\xbcd*6\ +F\x83\xa4\x13P\x01\x8c\x22\x06\xc3B\xc9\xe1(\x15%\ +b\xa2&KB\x1cf\xcb\xf6r\xa2\xe7h2@\xdf\ + \x92*\x1f\xa11\xba&A\xa3 \xb62 j4\ +2\xa5\xe1\x9b\xc9\xd8\xfb\x18\xc7f\xda\xa0\x01\xab!\xda\ +\xc0\x03\xad-\x89\x06\x8e\x00k\xc8~\xc0\xc6\xecX\xf6\ +\xc96\xec\xcb\x1e\xa2\xa8\x1f\xfbV\xd8\x86\xed\xdb\x83L\ +\x7f\xee{\xaa\x1d\xbb\x80;\xca'\xa7\xb5[\xea\xc5\xbf\ +\xa0\x9a\x9c\x88\x88\xf0\xb9o\x11\xae\xa2\x9aJ\x80\xf0fD\x18aE\ +\x81\xad\xa4d\xbal\xdaU,\x81\xcb\xa6\xd3\x18\x1c\xce\ +\xa5\x5c\xae\xceb\xb5\xeb\x0d\x8a\x0d@\x00Af\xd40\ +\x0c:\xc7\x09\x95!\xe0\xc7\x8b`\x00\xd1#L\xdc\xa1\ +\x15Il\xde\xb2\x00\xad\xdd\xef\xf3\xb9\xd6\x03\x076\xb2\ +\xd9\xe6\xb6\x9b]\xb2T\x0f\x83:\xe0\xc0[\x93\xa6\x0c\ +\x1a\x91\xbf\xae\xf7\x9a\xb5\xeee\x84\xce\xcb\xf0Y\xed\x0c\ +G\x0dB\x91\xe2\xacr\xa2,\x19u\xa1\x13\xc8\xdbY\ +\x88\xadT\x01W\x9a\xdf/\xda-\xccR\xc1\xba\xde\xc0\ +\xf4\x96\x8d6\xc5\xfeQ\x83+\xb4#9\x1b;\x87\xb3\ +\xda\xcd6\xfb\xed\xf6\x83\xa3\xa2\xe0b8W)U2\ +\x06\xcf\xd0\x84\xe4n\xeee\xea\xb1\x9c\xeanz~l\ +\xefZi\x89\xe1\x80\xa0\xdb\x08\x18\x8e\xe4\xbf\x91\x90\xb0\ +\x99\x9d\xa6n\xb5\xe9\xd1z\x1f\xd6\x01\xebK\xde\xd6\x0d\ +*\x15\x90b\xa5aIP1\x01#1_\x86\xc9\xe3\ +m\x9eX\x02\x10?\xe1W\xa9\x06a\xde\xc7a\x9eJ\ +\x87\xa4\x18\x84G\x13s\xe9\x06\x19\x922\x85\xfe\x84Y\ +\xa7\x91\xfc\x86 V\xf2/_\xe0$\xa2\x04n\x92\xa2\ +\x19\x06\x1eSq\x95#'\x1d(\xad\xfa\x8bW\xd8\xc9\ +\x80\x7f\xe4U\x864I\xe3g\x9d\x15\x1d\x90b%7\ +\x15\x922\xae@J\xe1'>\x14\x92\x1a\x88\xc6[X\ +\xa4\xa4\x9aL\x8a\x8f\xf9=\x03\x94SiM\x0c\x95[\ +\xd7\xe5\xceK\xdd\x09z\x5c\x85\xe7\x19~\x1aiTI\ +\xb2N\x94%)RVs_\xb9\x12tXdz\x09\ +>\x98\x119\x89\xa1J\x85\xc4\x18\xa3M\xc3\xc8:~\ +\x96&\xf9j\x85ShJY;\xa1\xd1*&\x1eE\ +A$\x18\xd6A\x81tL\xd5A\x83$\x8e%\x9ee\ +x\xb2\x13\x8b\xa9\x9a^]\xac\x13\xdam\x11\xa7d\xd3\ +\xfc(F\x10`\xa5\x062\xd0a\xdd#9^\x99\xb6\ +\x80n+4\xf2\x98\xb2\x13J\xd5\x10\xad\xec\xbaJ\xad\ +\x96j\xfbA8\xb2\xadT\x9a\xcdC\xec\xfbbc\x9f\ +\xe4;\x1e\xddg\xeb+\x89(\xb6\x90\x9br\xe5\x85\xad\ +\xfa\xba\x81\xba\xae9\xce\xef\xb9\xa7g\x06x\xbc\xab\x8b\ +\xb2\xd3\xbb\xafu~\xf1\xbf\x13\xfb\xd1\xd7\xbd\xaf\xf6v\ +\xc5\xb80K\xf7\x08D\x8b\xe4\x18AM\xc5D\x18\xf8\ +\xc2\x9a \x81\x06%\x13r\x81\x06\x18q4\xa6\xe4\xc7\ +\x00\x09\xac\x00\xc41\xfcL\x8dA\x87L\x91x\xc7\xb1\ +\xc1\xbd\x06$2\x9c)\xc5@\xcb\x0c\xc1\x06\xb5\xf0@\ +6\x12\x07\xf3[\x8a\xbf@\xc3\xa4\x1a\x0b\xcds{\xc9\ +#\x00\x12\xa7\xdd\x03.\x19\x0c\xf2\x99=\x10`\xd9#\ +5\xf4K\xffT\xba\xb4f\xec\xff\x0cPh\xa5\x03\x0b\ +t\xd9\x14\xc2\xc6\x923{Y\xd85m\x83i\xda\xac\ +\xbd\xa3k\xdb\xb6\xf9{m\xdc7=\xd2\xc4\xca\xf7]\ +\xe3y\xdd\xaf\xed\xeb}\xdf\xaa\xbd\xff\x81\xe0\xa9\xed\xf3\ +\x83\xe1\xb8usr\xe28\xbe1m\xdd\xf8\xdeC\x91\ +\xe3\xb8^K\x95\xe5\xb8\xae[\x99\xde\xb9\x8ek\x9d\xdc\ +\xf9\xce{\xa1\xda\xba\x0e\x8b\xa5\xd0\xf8\xfe\x9b\xa9\xdb\xfa\ +N\xab\xad\xc1\x10\x10\x00\x13\x00\xfe\x00\x04\x00\x01\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x04\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x01\x01\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x02\x01\x03\ +\x00\x04\x00\x00\x00\x92\x08\x00\x00\x03\x01\x03\x00\x01\x00\x00\ +\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00\x11\x01\x04\x00\x01\x00\x00\x00\x08\x00\x00\x00\x15\x01\x03\ +\x00\x01\x00\x00\x00\x04\x00\x00\x00\x16\x01\x04\x00\x01\x00\x00\ +\x00`\x00\x00\x00\x17\x01\x04\x00\x01\x00\x00\x00\x9f\x07\x00\ +\x00\x1a\x01\x05\x00\x01\x00\x00\x00\x9a\x08\x00\x00\x1b\x01\x05\ +\x00\x01\x00\x00\x00\xa2\x08\x00\x00\x1c\x01\x03\x00\x01\x00\x00\ +\x00\x01\x00\x00\x00(\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x001\x01\x02\x00\x10\x00\x00\x00\xaa\x08\x00\x00=\x01\x03\ +\x00\x01\x00\x00\x00\x02\x00\x00\x00R\x01\x03\x00\x01\x00\x00\ +\x00\x02\x00\x00\x00S\x01\x03\x00\x04\x00\x00\x00\xba\x08\x00\ +\x00s\x87\x07\x00H\x0c\x00\x00\xc2\x08\x00\x00\x00\x00\x00\ +\x00\x08\x00\x08\x00\x08\x00\x08\x00\x802\x02\x00\xe8\x03\x00\ +\x00\x802\x02\x00\xe8\x03\x00\x00paint.n\ +et 4.0.9\x00\x01\x00\x01\x00\x01\x00\x01\ +\x00\x00\x00\x0cHLino\x02\x10\x00\x00mnt\ +rRGB XYZ \x07\xce\x00\x02\x00\x09\x00\ +\x06\x001\x00\x00acspMSFT\x00\x00\x00\ +\x00IEC sRGB\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\xf6\xd6\x00\x01\x00\x00\x00\x00\xd3\ +-HP \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x11cprt\x00\x00\x01P\x00\x00\x00\ +3desc\x00\x00\x01\x84\x00\x00\x00lwtp\ +t\x00\x00\x01\xf0\x00\x00\x00\x14bkpt\x00\x00\x02\ +\x04\x00\x00\x00\x14rXYZ\x00\x00\x02\x18\x00\x00\x00\ +\x14gXYZ\x00\x00\x02,\x00\x00\x00\x14bXY\ +Z\x00\x00\x02@\x00\x00\x00\x14dmnd\x00\x00\x02\ +T\x00\x00\x00pdmdd\x00\x00\x02\xc4\x00\x00\x00\ +\x88vued\x00\x00\x03L\x00\x00\x00\x86vie\ +w\x00\x00\x03\xd4\x00\x00\x00$lumi\x00\x00\x03\ +\xf8\x00\x00\x00\x14meas\x00\x00\x04\x0c\x00\x00\x00\ +$tech\x00\x00\x040\x00\x00\x00\x0crTR\ +C\x00\x00\x04<\x00\x00\x08\x0cgTRC\x00\x00\x04\ +<\x00\x00\x08\x0cbTRC\x00\x00\x04<\x00\x00\x08\ +\x0ctext\x00\x00\x00\x00Copyrig\ +ht (c) 1998 Hewl\ +ett-Packard Comp\ +any\x00\x00desc\x00\x00\x00\x00\x00\x00\x00\ +\x12sRGB IEC61966-2\ +.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12sR\ +GB IEC61966-2.1\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00XYZ \x00\x00\x00\x00\x00\x00\xf3Q\x00\x01\x00\ +\x00\x00\x01\x16\xccXYZ \x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\x00\x00\ +\x00\x00\x00o\xa2\x00\x008\xf5\x00\x00\x03\x90XYZ\ + \x00\x00\x00\x00\x00\x00b\x99\x00\x00\xb7\x85\x00\x00\x18\ +\xdaXYZ \x00\x00\x00\x00\x00\x00$\xa0\x00\x00\x0f\ +\x84\x00\x00\xb6\xcfdesc\x00\x00\x00\x00\x00\x00\x00\ +\x16IEC http://www.\ +iec.ch\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x16IEC http://www\ +.iec.ch\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00desc\x00\x00\x00\x00\x00\x00\x00\ +.IEC 61966-2.1 D\ +efault RGB colou\ +r space - sRGB\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00.IEC 61\ +966-2.1 Default \ +RGB colour space\ + - sRGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00des\ +c\x00\x00\x00\x00\x00\x00\x00,Referen\ +ce Viewing Condi\ +tion in IEC61966\ +-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\ +Reference Viewin\ +g Condition in I\ +EC61966-2.1\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00view\x00\x00\x00\x00\x00\x13\xa4\ +\xfe\x00\x14_.\x00\x10\xcf\x14\x00\x03\xed\xcc\x00\x04\x13\ +\x0b\x00\x03\x5c\x9e\x00\x00\x00\x01XYZ \x00\x00\x00\ +\x00\x00L\x09V\x00P\x00\x00\x00W\x1f\xe7mea\ +s\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x8f\x00\x00\x00\ +\x02sig \x00\x00\x00\x00CRT cur\ +v\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x05\x00\x0a\x00\ +\x0f\x00\x14\x00\x19\x00\x1e\x00#\x00(\x00-\x002\x00\ +7\x00;\x00@\x00E\x00J\x00O\x00T\x00Y\x00\ +^\x00c\x00h\x00m\x00r\x00w\x00|\x00\x81\x00\ +\x86\x00\x8b\x00\x90\x00\x95\x00\x9a\x00\x9f\x00\xa4\x00\xa9\x00\ +\xae\x00\xb2\x00\xb7\x00\xbc\x00\xc1\x00\xc6\x00\xcb\x00\xd0\x00\ +\xd5\x00\xdb\x00\xe0\x00\xe5\x00\xeb\x00\xf0\x00\xf6\x00\xfb\x01\ +\x01\x01\x07\x01\x0d\x01\x13\x01\x19\x01\x1f\x01%\x01+\x01\ +2\x018\x01>\x01E\x01L\x01R\x01Y\x01`\x01\ +g\x01n\x01u\x01|\x01\x83\x01\x8b\x01\x92\x01\x9a\x01\ +\xa1\x01\xa9\x01\xb1\x01\xb9\x01\xc1\x01\xc9\x01\xd1\x01\xd9\x01\ +\xe1\x01\xe9\x01\xf2\x01\xfa\x02\x03\x02\x0c\x02\x14\x02\x1d\x02\ +&\x02/\x028\x02A\x02K\x02T\x02]\x02g\x02\ +q\x02z\x02\x84\x02\x8e\x02\x98\x02\xa2\x02\xac\x02\xb6\x02\ +\xc1\x02\xcb\x02\xd5\x02\xe0\x02\xeb\x02\xf5\x03\x00\x03\x0b\x03\ +\x16\x03!\x03-\x038\x03C\x03O\x03Z\x03f\x03\ +r\x03~\x03\x8a\x03\x96\x03\xa2\x03\xae\x03\xba\x03\xc7\x03\ +\xd3\x03\xe0\x03\xec\x03\xf9\x04\x06\x04\x13\x04 \x04-\x04\ +;\x04H\x04U\x04c\x04q\x04~\x04\x8c\x04\x9a\x04\ +\xa8\x04\xb6\x04\xc4\x04\xd3\x04\xe1\x04\xf0\x04\xfe\x05\x0d\x05\ +\x1c\x05+\x05:\x05I\x05X\x05g\x05w\x05\x86\x05\ +\x96\x05\xa6\x05\xb5\x05\xc5\x05\xd5\x05\xe5\x05\xf6\x06\x06\x06\ +\x16\x06'\x067\x06H\x06Y\x06j\x06{\x06\x8c\x06\ +\x9d\x06\xaf\x06\xc0\x06\xd1\x06\xe3\x06\xf5\x07\x07\x07\x19\x07\ ++\x07=\x07O\x07a\x07t\x07\x86\x07\x99\x07\xac\x07\ +\xbf\x07\xd2\x07\xe5\x07\xf8\x08\x0b\x08\x1f\x082\x08F\x08\ +Z\x08n\x08\x82\x08\x96\x08\xaa\x08\xbe\x08\xd2\x08\xe7\x08\ +\xfb\x09\x10\x09%\x09:\x09O\x09d\x09y\x09\x8f\x09\ +\xa4\x09\xba\x09\xcf\x09\xe5\x09\xfb\x0a\x11\x0a'\x0a=\x0a\ +T\x0aj\x0a\x81\x0a\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\xf3\x0b\ +\x0b\x0b\x22\x0b9\x0bQ\x0bi\x0b\x80\x0b\x98\x0b\xb0\x0b\ +\xc8\x0b\xe1\x0b\xf9\x0c\x12\x0c*\x0cC\x0c\x5c\x0cu\x0c\ +\x8e\x0c\xa7\x0c\xc0\x0c\xd9\x0c\xf3\x0d\x0d\x0d&\x0d@\x0d\ +Z\x0dt\x0d\x8e\x0d\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\x13\x0e\ +.\x0eI\x0ed\x0e\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\xee\x0f\ +\x09\x0f%\x0fA\x0f^\x0fz\x0f\x96\x0f\xb3\x0f\xcf\x0f\ +\xec\x10\x09\x10&\x10C\x10a\x10~\x10\x9b\x10\xb9\x10\ +\xd7\x10\xf5\x11\x13\x111\x11O\x11m\x11\x8c\x11\xaa\x11\ +\xc9\x11\xe8\x12\x07\x12&\x12E\x12d\x12\x84\x12\xa3\x12\ +\xc3\x12\xe3\x13\x03\x13#\x13C\x13c\x13\x83\x13\xa4\x13\ +\xc5\x13\xe5\x14\x06\x14'\x14I\x14j\x14\x8b\x14\xad\x14\ +\xce\x14\xf0\x15\x12\x154\x15V\x15x\x15\x9b\x15\xbd\x15\ +\xe0\x16\x03\x16&\x16I\x16l\x16\x8f\x16\xb2\x16\xd6\x16\ +\xfa\x17\x1d\x17A\x17e\x17\x89\x17\xae\x17\xd2\x17\xf7\x18\ +\x1b\x18@\x18e\x18\x8a\x18\xaf\x18\xd5\x18\xfa\x19 \x19\ +E\x19k\x19\x91\x19\xb7\x19\xdd\x1a\x04\x1a*\x1aQ\x1a\ +w\x1a\x9e\x1a\xc5\x1a\xec\x1b\x14\x1b;\x1bc\x1b\x8a\x1b\ +\xb2\x1b\xda\x1c\x02\x1c*\x1cR\x1c{\x1c\xa3\x1c\xcc\x1c\ +\xf5\x1d\x1e\x1dG\x1dp\x1d\x99\x1d\xc3\x1d\xec\x1e\x16\x1e\ +@\x1ej\x1e\x94\x1e\xbe\x1e\xe9\x1f\x13\x1f>\x1fi\x1f\ +\x94\x1f\xbf\x1f\xea \x15 A l \x98 \xc4 \ +\xf0!\x1c!H!u!\xa1!\xce!\xfb\x22'\x22\ +U\x22\x82\x22\xaf\x22\xdd#\x0a#8#f#\x94#\ +\xc2#\xf0$\x1f$M$|$\xab$\xda%\x09%\ +8%h%\x97%\xc7%\xf7&'&W&\x87&\ +\xb7&\xe8'\x18'I'z'\xab'\xdc(\x0d(\ +?(q(\xa2(\xd4)\x06)8)k)\x9d)\ +\xd0*\x02*5*h*\x9b*\xcf+\x02+6+\ +i+\x9d+\xd1,\x05,9,n,\xa2,\xd7-\ +\x0c-A-v-\xab-\xe1.\x16.L.\x82.\ +\xb7.\xee/$/Z/\x91/\xc7/\xfe050\ +l0\xa40\xdb1\x121J1\x821\xba1\xf22\ +*2c2\x9b2\xd43\x0d3F3\x7f3\xb83\ +\xf14+4e4\x9e4\xd85\x135M5\x875\ +\xc25\xfd676r6\xae6\xe97$7`7\ +\x9c7\xd78\x148P8\x8c8\xc89\x059B9\ +\x7f9\xbc9\xf9:6:t:\xb2:\xef;-;\ +k;\xaa;\xe8<' >`>\xa0>\xe0?!?\ +a?\xa2?\xe2@#@d@\xa6@\xe7A)A\ +jA\xacA\xeeB0BrB\xb5B\xf7C:C\ +}C\xc0D\x03DGD\x8aD\xceE\x12EUE\ +\x9aE\xdeF\x22FgF\xabF\xf0G5G{G\ +\xc0H\x05HKH\x91H\xd7I\x1dIcI\xa9I\ +\xf0J7J}J\xc4K\x0cKSK\x9aK\xe2L\ +*LrL\xbaM\x02MJM\x93M\xdcN%N\ +nN\xb7O\x00OIO\x93O\xddP'PqP\ +\xbbQ\x06QPQ\x9bQ\xe6R1R|R\xc7S\ +\x13S_S\xaaS\xf6TBT\x8fT\xdbU(U\ +uU\xc2V\x0fV\x5cV\xa9V\xf7WDW\x92W\ +\xe0X/X}X\xcbY\x1aYiY\xb8Z\x07Z\ +VZ\xa6Z\xf5[E[\x95[\xe5\x5c5\x5c\x86\x5c\ +\xd6]']x]\xc9^\x1a^l^\xbd_\x0f_\ +a_\xb3`\x05`W`\xaa`\xfcaOa\xa2a\ +\xf5bIb\x9cb\xf0cCc\x97c\xebd@d\ +\x94d\xe9e=e\x92e\xe7f=f\x92f\xe8g\ +=g\x93g\xe9h?h\x96h\xeciCi\x9ai\ +\xf1jHj\x9fj\xf7kOk\xa7k\xfflWl\ +\xafm\x08m`m\xb9n\x12nkn\xc4o\x1eo\ +xo\xd1p+p\x86p\xe0q:q\x95q\xf0r\ +Kr\xa6s\x01s]s\xb8t\x14tpt\xccu\ +(u\x85u\xe1v>v\x9bv\xf8wVw\xb3x\ +\x11xnx\xccy*y\x89y\xe7zFz\xa5{\ +\x04{c{\xc2|!|\x81|\xe1}A}\xa1~\ +\x01~b~\xc2\x7f#\x7f\x84\x7f\xe5\x80G\x80\xa8\x81\ +\x0a\x81k\x81\xcd\x820\x82\x92\x82\xf4\x83W\x83\xba\x84\ +\x1d\x84\x80\x84\xe3\x85G\x85\xab\x86\x0e\x86r\x86\xd7\x87\ +;\x87\x9f\x88\x04\x88i\x88\xce\x893\x89\x99\x89\xfe\x8a\ +d\x8a\xca\x8b0\x8b\x96\x8b\xfc\x8cc\x8c\xca\x8d1\x8d\ +\x98\x8d\xff\x8ef\x8e\xce\x8f6\x8f\x9e\x90\x06\x90n\x90\ +\xd6\x91?\x91\xa8\x92\x11\x92z\x92\xe3\x93M\x93\xb6\x94\ + \x94\x8a\x94\xf4\x95_\x95\xc9\x964\x96\x9f\x97\x0a\x97\ +u\x97\xe0\x98L\x98\xb8\x99$\x99\x90\x99\xfc\x9ah\x9a\ +\xd5\x9bB\x9b\xaf\x9c\x1c\x9c\x89\x9c\xf7\x9dd\x9d\xd2\x9e\ +@\x9e\xae\x9f\x1d\x9f\x8b\x9f\xfa\xa0i\xa0\xd8\xa1G\xa1\ +\xb6\xa2&\xa2\x96\xa3\x06\xa3v\xa3\xe6\xa4V\xa4\xc7\xa5\ +8\xa5\xa9\xa6\x1a\xa6\x8b\xa6\xfd\xa7n\xa7\xe0\xa8R\xa8\ +\xc4\xa97\xa9\xa9\xaa\x1c\xaa\x8f\xab\x02\xabu\xab\xe9\xac\ +\x5c\xac\xd0\xadD\xad\xb8\xae-\xae\xa1\xaf\x16\xaf\x8b\xb0\ +\x00\xb0u\xb0\xea\xb1`\xb1\xd6\xb2K\xb2\xc2\xb38\xb3\ +\xae\xb4%\xb4\x9c\xb5\x13\xb5\x8a\xb6\x01\xb6y\xb6\xf0\xb7\ +h\xb7\xe0\xb8Y\xb8\xd1\xb9J\xb9\xc2\xba;\xba\xb5\xbb\ +.\xbb\xa7\xbc!\xbc\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\x84\xbe\ +\xff\xbfz\xbf\xf5\xc0p\xc0\xec\xc1g\xc1\xe3\xc2_\xc2\ +\xdb\xc3X\xc3\xd4\xc4Q\xc4\xce\xc5K\xc5\xc8\xc6F\xc6\ +\xc3\xc7A\xc7\xbf\xc8=\xc8\xbc\xc9:\xc9\xb9\xca8\xca\ +\xb7\xcb6\xcb\xb6\xcc5\xcc\xb5\xcd5\xcd\xb5\xce6\xce\ +\xb6\xcf7\xcf\xb8\xd09\xd0\xba\xd1<\xd1\xbe\xd2?\xd2\ +\xc1\xd3D\xd3\xc6\xd4I\xd4\xcb\xd5N\xd5\xd1\xd6U\xd6\ +\xd8\xd7\x5c\xd7\xe0\xd8d\xd8\xe8\xd9l\xd9\xf1\xdav\xda\ +\xfb\xdb\x80\xdc\x05\xdc\x8a\xdd\x10\xdd\x96\xde\x1c\xde\xa2\xdf\ +)\xdf\xaf\xe06\xe0\xbd\xe1D\xe1\xcc\xe2S\xe2\xdb\xe3\ +c\xe3\xeb\xe4s\xe4\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\x1f\xe7\ +\xa9\xe82\xe8\xbc\xe9F\xe9\xd0\xea[\xea\xe5\xebp\xeb\ +\xfb\xec\x86\xed\x11\xed\x9c\xee(\xee\xb4\xef@\xef\xcc\xf0\ +X\xf0\xe5\xf1r\xf1\xff\xf2\x8c\xf3\x19\xf3\xa7\xf44\xf4\ +\xc2\xf5P\xf5\xde\xf6m\xf6\xfb\xf7\x8a\xf8\x19\xf8\xa8\xf9\ +8\xf9\xc7\xfaW\xfa\xe7\xfbw\xfc\x07\xfc\x98\xfd)\xfd\ +\xba\xfeK\xfe\xdc\xffm\xff\xff\ +\x00\x00\x04o\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Editor only \ +- Saved\x0d\ +\x0a Creat\ +ed with Sketch.<\ +/desc>\x0d\x0a \x0d\x0a <\ +g id=\x22icon-/-out\ +liner-/-entity-/\ +--Editor-only---\ +Saved\x22 stroke=\x22n\ +one\x22 stroke-widt\ +h=\x221\x22 fill=\x22none\ +\x22 fill-rule=\x22eve\ +nodd\x22>\x0d\x0a \ +\x0d\x0a \ +\x0d\x0a\x0d\x0a\ +\x00\x00\x07\x1d\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22 standalone=\x22\ +no\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + lock on\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \x0d\x0a \ +\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x05\x1d\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / slice \ +/ standard copy<\ +/title>\x0d\x0a Created with \ +Sketch.\x0d\x0a\ + \x0d\x0a \x0d\x0a \ +\x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x04\xa0\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Not active - \ +Updated\x0d\ +\x0a Creat\ +ed with Sketch.<\ +/desc>\x0d\x0a \x0d\x0a <\ +g id=\x22icon-/-out\ +liner-/-entity-/\ +-Not-active---Up\ +dated\x22 stroke=\x22n\ +one\x22 stroke-widt\ +h=\x221\x22 fill=\x22none\ +\x22 fill-rule=\x22eve\ +nodd\x22>\x0d\x0a \ +\x0d\x0a \ + \x0d\x0a\x0d\x0a\ +\x00\x00\x04\x9c\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Not active - \ +Saved\x0d\x0a \ + Created\ + with Sketch.\x0d\x0a \ +\x0d\x0a \x0d\x0a \ +\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x06\xdc\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Group 13\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\ + \ +\x0d\x0a \ + \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x03\x87\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Loop v2\x0d\x0a Cr\ +eated with Sketc\ +h.\x0d\x0a <\ +g id=\x22icon-/-out\ +liner-/-entity-/\ +-Loop-v2\x22 stroke\ +=\x22none\x22 stroke-w\ +idth=\x221\x22 fill=\x22n\ +one\x22 fill-rule=\x22\ +evenodd\x22>\x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00_l\ +I\ +I*\x00\x08\x00\x00\x00\x17\x00\xfe\x00\x04\x00\x01\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x01\x01\x03\x00\x01\x00\x00\x00`\x00\x00\x00\x02\x01\x03\ +\x00\x04\x00\x00\x00\x22\x01\x00\x00\x03\x01\x03\x00\x01\x00\x00\ +\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00\x11\x01\x04\x00\x01\x00\x00\x00DS\x00\x00\x12\x01\x03\ +\x00\x01\x00\x00\x00\x01\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\ +\x00\x04\x00\x00\x00\x16\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x17\x01\x04\x00\x01\x00\x00\x00\xfa\x0b\x00\x00\x1a\x01\x05\ +\x00\x01\x00\x00\x00*\x01\x00\x00\x1b\x01\x05\x00\x01\x00\x00\ +\x002\x01\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\ +\x00(\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\ +\x00$\x00\x00\x00:\x01\x00\x002\x01\x02\x00\x14\x00\x00\ +\x00^\x01\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00R\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\xbc\x02\x01\ +\x00\xfa8\x00\x00r\x01\x00\x00I\x86\x01\x00\x90\x0c\x00\ +\x00l:\x00\x00i\x87\x04\x00\x01\x00\x00\x00@_\x00\ +\x00s\x87\x07\x00H\x0c\x00\x00\xfcF\x00\x00\x00\x00\x00\ +\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\xf9\x15\x00\x10'\x00\ +\x00\x00\xf9\x15\x00\x10'\x00\x00Adobe P\ +hotoshop CC 2015\ +.5 (Windows)\x00201\ +7:03:08 11:37:45\ +\x00\x0a\x0a \x0a \ +\x0a pain\ +t.net 4.0.9\x0a \ + 2017-03-0\ +7T11:32:29-08:00\ +\x0a 2017-\ +03-08T11:37:45-0\ +8:00\x0a <\ +xmp:MetadataDate\ +>2017-03-08T11:3\ +7:45-08:00\x0a \ + image/tiff\x0a \ + 3\x0a \ + sR\ +GB IEC61966-2.1<\ +/photoshop:ICCPr\ +ofile>\x0a \ +\x0a \ + \x0a \ + adobe\ +:docid:photoshop\ +:94a27cdb-0433-1\ +1e7-b02d-9f84d9f\ +5a326\x0a \ + \x0a <\ +/photoshop:Docum\ +entAncestors>\x0a \ + xmp.iid\ +:16fdf09c-857d-9\ +44e-9783-e127cb1\ +b9cf4\x0a \ + adobe:docid:\ +photoshop:9f9351\ +ac-0436-11e7-b02\ +d-9f84d9f5a326\x0a xmp.did:ca7\ +71a70-f965-e14f-\ +9103-360465543db\ +f\x0a \ + \x0a \ + \x0a \ + \x0a \ + <\ +stEvt:action>cre\ +ated\x0a \ + xmp.iid:\ +ca771a70-f965-e1\ +4f-9103-36046554\ +3dbf\x0a \ + 2017-03-07\ +T11:32:29-08:00<\ +/stEvt:when>\x0a \ + <\ +stEvt:softwareAg\ +ent>Adobe Photos\ +hop CC 2015.5 (W\ +indows)\x0a \ + \x0a \ + \x0a \ + saved\x0a \ + <\ +stEvt:instanceID\ +>xmp.iid:16fdf09\ +c-857d-944e-9783\ +-e127cb1b9cf4\ +\x0a \ + 2\ +017-03-08T11:37:\ +45-08:00\x0a \ + Ado\ +be Photoshop CC \ +2015.5 (Windows)\ +\x0a \ + /\x0a \ + \x0a <\ +/rdf:Seq>\x0a \ + \x0a \x0a \ +\x0a\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \x0a8BIM\x04\ +%\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x008BIM\x04:\x00\x00\x00\ +\x00\x00\xe5\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x0bprintOutput\x00\x00\x00\x05\ +\x00\x00\x00\x00PstSbool\x01\x00\x00\x00\ +\x00Inteenum\x00\x00\x00\x00Int\ +e\x00\x00\x00\x00Clrm\x00\x00\x00\x0fpri\ +ntSixteenBitbool\ +\x00\x00\x00\x00\x0bprinterName\ +TEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0fpr\ +intProofSetupObj\ +c\x00\x00\x00\x0c\x00P\x00r\x00o\x00o\x00f\x00\ + \x00S\x00e\x00t\x00u\x00p\x00\x00\x00\x00\x00\ +\x0aproofSetup\x00\x00\x00\x01\x00\ +\x00\x00\x00Bltnenum\x00\x00\x00\x0cb\ +uiltinProof\x00\x00\x00\x09p\ +roofCMYK\x008BIM\x04;\x00\ +\x00\x00\x00\x02-\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x12printOutputOp\ +tions\x00\x00\x00\x17\x00\x00\x00\x00Cpt\ +nbool\x00\x00\x00\x00\x00Clbrbo\ +ol\x00\x00\x00\x00\x00RgsMbool\x00\ +\x00\x00\x00\x00CrnCbool\x00\x00\x00\x00\ +\x00CntCbool\x00\x00\x00\x00\x00Lb\ +lsbool\x00\x00\x00\x00\x00Ngtvb\ +ool\x00\x00\x00\x00\x00EmlDbool\ +\x00\x00\x00\x00\x00Intrbool\x00\x00\x00\ +\x00\x00BckgObjc\x00\x00\x00\x01\x00\x00\ +\x00\x00\x00\x00RGBC\x00\x00\x00\x03\x00\x00\x00\x00\ +Rd doub@o\xe0\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00Grn doub@o\xe0\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00Bl doub\ +@o\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00BrdT\ +UntF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00Bld UntF#Rlt\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Rslt\ +UntF#Pxl@b\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x0avectorDatabo\ +ol\x01\x00\x00\x00\x00PgPsenum\x00\ +\x00\x00\x00PgPs\x00\x00\x00\x00PgPC\x00\ +\x00\x00\x00LeftUntF#Rlt\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Top U\ +ntF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00Scl UntF#Prc@\ +Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10cropW\ +henPrintingbool\x00\ +\x00\x00\x00\x0ecropRectBott\ +omlong\x00\x00\x00\x00\x00\x00\x00\x0ccr\ +opRectLeftlong\x00\x00\ +\x00\x00\x00\x00\x00\x0dcropRectRi\ +ghtlong\x00\x00\x00\x00\x00\x00\x00\x0bc\ +ropRectToplong\x00\x00\ +\x00\x00\x008BIM\x03\xed\x00\x00\x00\x00\x00\x10\x00\ +\x90\x00\x00\x00\x01\x00\x01\x00\x90\x00\x00\x00\x01\x00\x018\ +BIM\x04&\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00?\x80\x00\x008BIM\x03\xee\x00\ +\x00\x00\x00\x00\x0d\x0cTransparen\ +cy\x008BIM\x04\x15\x00\x00\x00\x00\x00\x1e\x00\ +\x00\x00\x0d\x00T\x00r\x00a\x00n\x00s\x00p\x00\ +a\x00r\x00e\x00n\x00c\x00y\x00\x008BI\ +M\x045\x00\x00\x00\x00\x00\x11\x00\x00\x00\x01\x00\x00\xff\ +\xff\x00\x00\x00\x00\x00\x00\x00d\x01\x008BIM\x04\ +\x1d\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008BIM\x04\ +\x0d\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e8BIM\x04\ +\x19\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e8BIM\x03\ +\xf3\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\ +\x008BIM'\x10\x00\x00\x00\x00\x00\x0a\x00\x01\x00\ +\x00\x00\x00\x00\x00\x00\x018BIM\x03\xf5\x00\x00\x00\ +\x00\x00H\x00/ff\x00\x01\x00lff\x00\x06\x00\ +\x00\x00\x00\x00\x01\x00/ff\x00\x01\x00\xa1\x99\x9a\x00\ +\x06\x00\x00\x00\x00\x00\x01\x002\x00\x00\x00\x01\x00Z\x00\ +\x00\x00\x06\x00\x00\x00\x00\x00\x01\x005\x00\x00\x00\x01\x00\ +-\x00\x00\x00\x06\x00\x00\x00\x00\x00\x018BIM\x03\ +\xf8\x00\x00\x00\x00\x00p\x00\x00\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\ +\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\ +\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\x03\xe8\x00\x008BIM\x04\x00\x00\x00\x00\ +\x00\x00\x02\x00\x008BIM\x04\x02\x00\x00\x00\x00\x00\ +\x02\x00\x008BIM\x040\x00\x00\x00\x00\x00\x01\x01\ +\x008BIM\x04-\x00\x00\x00\x00\x00\x06\x00\x01\x00\ +\x00\x00\x0a8BIM\x04\x08\x00\x00\x00\x00\x00\x10\x00\ +\x00\x00\x01\x00\x00\x02@\x00\x00\x02@\x00\x00\x00\x008\ +BIM\x04\x1e\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008\ +BIM\x04\x1a\x00\x00\x00\x00\x035\x00\x00\x00\x06\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\ +\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00null\x00\x00\ +\x00\x02\x00\x00\x00\x06boundsObjc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Rct1\x00\x00\ +\x00\x04\x00\x00\x00\x00Top long\x00\x00\ +\x00\x00\x00\x00\x00\x00Leftlong\x00\x00\ +\x00\x00\x00\x00\x00\x00Btomlong\x00\x00\ +\x00`\x00\x00\x00\x00Rghtlong\x00\x00\ +\x00`\x00\x00\x00\x06slicesVlLs\ +\x00\x00\x00\x01Objc\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x05slice\x00\x00\x00\x12\x00\x00\x00\x07s\ +liceIDlong\x00\x00\x00\x00\x00\x00\ +\x00\x07groupIDlong\x00\x00\x00\ +\x00\x00\x00\x00\x06originenum\x00\ +\x00\x00\x0cESliceOrigin\x00\ +\x00\x00\x0dautoGenerated\ +\x00\x00\x00\x00Typeenum\x00\x00\x00\x0a\ +ESliceType\x00\x00\x00\x00Im\ +g \x00\x00\x00\x06boundsObjc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00Rct1\x00\x00\ +\x00\x04\x00\x00\x00\x00Top long\x00\x00\ +\x00\x00\x00\x00\x00\x00Leftlong\x00\x00\ +\x00\x00\x00\x00\x00\x00Btomlong\x00\x00\ +\x00`\x00\x00\x00\x00Rghtlong\x00\x00\ +\x00`\x00\x00\x00\x03urlTEXT\x00\x00\x00\ +\x01\x00\x00\x00\x00\x00\x00nullTEXT\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00MsgeTEX\ +T\x00\x00\x00\x01\x00\x00\x00\x00\x00\x06altTa\ +gTEXT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0ec\ +ellTextIsHTMLboo\ +l\x01\x00\x00\x00\x08cellTextTE\ +XT\x00\x00\x00\x01\x00\x00\x00\x00\x00\x09horz\ +Alignenum\x00\x00\x00\x0fESl\ +iceHorzAlign\x00\x00\x00\x07\ +default\x00\x00\x00\x09vertA\ +lignenum\x00\x00\x00\x0fESli\ +ceVertAlign\x00\x00\x00\x07d\ +efault\x00\x00\x00\x0bbgColo\ +rTypeenum\x00\x00\x00\x11ESl\ +iceBGColorType\x00\x00\ +\x00\x00None\x00\x00\x00\x09topOut\ +setlong\x00\x00\x00\x00\x00\x00\x00\x0al\ +eftOutsetlong\x00\x00\x00\ +\x00\x00\x00\x00\x0cbottomOutse\ +tlong\x00\x00\x00\x00\x00\x00\x00\x0brig\ +htOutsetlong\x00\x00\x00\x00\ +\x008BIM\x04(\x00\x00\x00\x00\x00\x0c\x00\x00\x00\ +\x02?\xf0\x00\x00\x00\x00\x00\x008BIM\x04\x14\x00\ +\x00\x00\x00\x00\x04\x00\x00\x00\x0c8BIM\x04\x0c\x00\ +\x00\x00\x00\x038\x00\x00\x00\x01\x00\x00\x000\x00\x00\x00\ +0\x00\x00\x00\x90\x00\x00\x1b\x00\x00\x00\x03\x1c\x00\x18\x00\ +\x01\xff\xd8\xff\xed\x00\x0cAdobe_CM\x00\ +\x01\xff\xee\x00\x0eAdobe\x00d\x80\x00\x00\x00\ +\x01\xff\xdb\x00\x84\x00\x0c\x08\x08\x08\x09\x08\x0c\x09\x09\x0c\ +\x11\x0b\x0a\x0b\x11\x15\x0f\x0c\x0c\x0f\x15\x18\x13\x13\x15\x13\ +\x13\x18\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x01\x0d\x0b\x0b\x0d\x0e\x0d\x10\x0e\x0e\ +\x10\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\ +\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11\x08\x000\x000\ +\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01\xff\xdd\x00\x04\x00\x03\ +\xff\xc4\x01?\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\ +\x00\x00\x00\x00\x00\x03\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\ +\x0b\x01\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x10\x00\ +\x01\x04\x01\x03\x02\x04\x02\x05\x07\x06\x08\x05\x03\x0c3\x01\ +\x00\x02\x11\x03\x04!\x121\x05AQa\x13\x22q\x81\ +2\x06\x14\x91\xa1\xb1B#$\x15R\xc1b34r\ +\x82\xd1C\x07%\x92S\xf0\xe1\xf1cs5\x16\xa2\xb2\ +\x83&D\x93TdE\xc2\xa3t6\x17\xd2U\xe2e\ +\xf2\xb3\x84\xc3\xd3u\xe3\xf3F'\x94\xa4\x85\xb4\x95\xc4\ +\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\ +\xc6\xd6\xe6\xf67GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\ +\xf7\x11\x00\x02\x02\x01\x02\x04\x04\x03\x04\x05\x06\x07\x07\x06\ +\x055\x01\x00\x02\x11\x03!1\x12\x04AQaq\x22\ +\x13\x052\x81\x91\x14\xa1\xb1B#\xc1R\xd1\xf03$\ +b\xe1r\x82\x92CS\x15cs4\xf1%\x06\x16\xa2\ +\xb2\x83\x07&5\xc2\xd2D\x93T\xa3\x17dEU6\ +te\xe2\xf2\xb3\x84\xc3\xd3u\xe3\xf3F\x94\xa4\x85\xb4\ +\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\ +\xa6\xb6\xc6\xd6\xe6\xf6'7GWgw\x87\x97\xa7\xb7\ +\xc7\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xf5\ +T\x92P\xb5\xceensZ^\xe6\x82CG$\xa4\ +\xa5YuU\xff\x008\xf6\xb2x\xdc@N\xd7\xb1\xe3\ +s\x1c\x1c\xdf\x10d*X\xb8,\xb1\x9e\xbe[K\xee\ +\xb3S\xbb\xb0\xec6\xa6}#\x07&\xa7\xd2H\xaa\xe7\ +\x06=\x93\x22O\x05%:\x09$\x92J\x7f\xff\xd0\xf5\ +T\x1c\x9c\xaa\xf1\x9a\x0b\xe4\x97\x18kZ$\x94eK\ +\xaa{YM\xbd\xeb\xb0\x1f\x97\xfa\x84\x94\xb7\xdb\xefw\ +\xf3x\xaf>\x05\xda\x7f\x04+\x9b\xd4.s.}m\ +`\xa6\x5c\xd6\x93:\xf3:|\x15\xac\xf3x\xc6s\xa9\ +0F\xae#\x9d\xa3\xe9mK\x05\xd6Y\x88\xd7Zw\ +\x17L\x1e\xf1:nIL\xf1/7\xe3\xb2\xd2\x00.\ +\xe4\x0e$\x1d\xa8\xca\x97J\xd3\x1d\xf5\x9ekyj\xba\ +\x92\x9f\xff\xd1\xf5UW\xa8\xd6l\xc3\xb0\x01$A\x00\ +y\x1f\xfc\x8a\xb4\x92Jh7\xa95\xf5\x86\xb6\x8b-\ +1\x0e\x86\xe8t\xd7\xc5&_\x9b\xb42\x8cA[\x06\ +\x808\xc0\x1f/b\xbe\x92Jj`\xe3\xddQ\xb5\xf7\ +@u\xae\xdd\xb5\xbc\x05m$\x92S\xff\xd98BI\ +M\x04!\x00\x00\x00\x00\x00a\x00\x00\x00\x01\x01\x00\x00\ +\x00\x0f\x00A\x00d\x00o\x00b\x00e\x00 \x00P\ +\x00h\x00o\x00t\x00o\x00s\x00h\x00o\x00p\ +\x00\x00\x00\x19\x00A\x00d\x00o\x00b\x00e\x00 \ +\x00P\x00h\x00o\x00t\x00o\x00s\x00h\x00o\ +\x00p\x00 \x00C\x00C\x00 \x002\x000\x001\ +\x005\x00.\x005\x00\x00\x00\x01\x00\x00\x00\x0cHL\ +ino\x02\x10\x00\x00mntrRGB X\ +YZ \x07\xce\x00\x02\x00\x09\x00\x06\x001\x00\x00a\ +cspMSFT\x00\x00\x00\x00IEC s\ +RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xf6\xd6\x00\x01\x00\x00\x00\x00\xd3-HP \x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11c\ +prt\x00\x00\x01P\x00\x00\x003desc\x00\ +\x00\x01\x84\x00\x00\x00lwtpt\x00\x00\x01\xf0\x00\ +\x00\x00\x14bkpt\x00\x00\x02\x04\x00\x00\x00\x14r\ +XYZ\x00\x00\x02\x18\x00\x00\x00\x14gXYZ\x00\ +\x00\x02,\x00\x00\x00\x14bXYZ\x00\x00\x02@\x00\ +\x00\x00\x14dmnd\x00\x00\x02T\x00\x00\x00pd\ +mdd\x00\x00\x02\xc4\x00\x00\x00\x88vued\x00\ +\x00\x03L\x00\x00\x00\x86view\x00\x00\x03\xd4\x00\ +\x00\x00$lumi\x00\x00\x03\xf8\x00\x00\x00\x14m\ +eas\x00\x00\x04\x0c\x00\x00\x00$tech\x00\ +\x00\x040\x00\x00\x00\x0crTRC\x00\x00\x04<\x00\ +\x00\x08\x0cgTRC\x00\x00\x04<\x00\x00\x08\x0cb\ +TRC\x00\x00\x04<\x00\x00\x08\x0ctext\x00\ +\x00\x00\x00Copyright (c)\ + 1998 Hewlett-Pa\ +ckard Company\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00\x12sRGB \ +IEC61966-2.1\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x12sRGB IEC\ +61966-2.1\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\ +\x00\x00\x00\x00\x00\xf3Q\x00\x01\x00\x00\x00\x01\x16\xccX\ +YZ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00o\xa2\x00\ +\x008\xf5\x00\x00\x03\x90XYZ \x00\x00\x00\x00\x00\ +\x00b\x99\x00\x00\xb7\x85\x00\x00\x18\xdaXYZ \x00\ +\x00\x00\x00\x00\x00$\xa0\x00\x00\x0f\x84\x00\x00\xb6\xcfd\ +esc\x00\x00\x00\x00\x00\x00\x00\x16IEC h\ +ttp://www.iec.ch\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16IEC \ +http://www.iec.c\ +h\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00.IEC 6\ +1966-2.1 Default\ + RGB colour spac\ +e - sRGB\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00.IEC 61966-2.\ +1 Default RGB co\ +lour space - sRG\ +B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00,Reference Vie\ +wing Condition i\ +n IEC61966-2.1\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00,Refere\ +nce Viewing Cond\ +ition in IEC6196\ +6-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00v\ +iew\x00\x00\x00\x00\x00\x13\xa4\xfe\x00\x14_.\x00\ +\x10\xcf\x14\x00\x03\xed\xcc\x00\x04\x13\x0b\x00\x03\x5c\x9e\x00\ +\x00\x00\x01XYZ \x00\x00\x00\x00\x00L\x09V\x00\ +P\x00\x00\x00W\x1f\xe7meas\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x8f\x00\x00\x00\x02sig \x00\ +\x00\x00\x00CRT curv\x00\x00\x00\x00\x00\ +\x00\x04\x00\x00\x00\x00\x05\x00\x0a\x00\x0f\x00\x14\x00\x19\x00\ +\x1e\x00#\x00(\x00-\x002\x007\x00;\x00@\x00\ +E\x00J\x00O\x00T\x00Y\x00^\x00c\x00h\x00\ +m\x00r\x00w\x00|\x00\x81\x00\x86\x00\x8b\x00\x90\x00\ +\x95\x00\x9a\x00\x9f\x00\xa4\x00\xa9\x00\xae\x00\xb2\x00\xb7\x00\ +\xbc\x00\xc1\x00\xc6\x00\xcb\x00\xd0\x00\xd5\x00\xdb\x00\xe0\x00\ +\xe5\x00\xeb\x00\xf0\x00\xf6\x00\xfb\x01\x01\x01\x07\x01\x0d\x01\ +\x13\x01\x19\x01\x1f\x01%\x01+\x012\x018\x01>\x01\ +E\x01L\x01R\x01Y\x01`\x01g\x01n\x01u\x01\ +|\x01\x83\x01\x8b\x01\x92\x01\x9a\x01\xa1\x01\xa9\x01\xb1\x01\ +\xb9\x01\xc1\x01\xc9\x01\xd1\x01\xd9\x01\xe1\x01\xe9\x01\xf2\x01\ +\xfa\x02\x03\x02\x0c\x02\x14\x02\x1d\x02&\x02/\x028\x02\ +A\x02K\x02T\x02]\x02g\x02q\x02z\x02\x84\x02\ +\x8e\x02\x98\x02\xa2\x02\xac\x02\xb6\x02\xc1\x02\xcb\x02\xd5\x02\ +\xe0\x02\xeb\x02\xf5\x03\x00\x03\x0b\x03\x16\x03!\x03-\x03\ +8\x03C\x03O\x03Z\x03f\x03r\x03~\x03\x8a\x03\ +\x96\x03\xa2\x03\xae\x03\xba\x03\xc7\x03\xd3\x03\xe0\x03\xec\x03\ +\xf9\x04\x06\x04\x13\x04 \x04-\x04;\x04H\x04U\x04\ +c\x04q\x04~\x04\x8c\x04\x9a\x04\xa8\x04\xb6\x04\xc4\x04\ +\xd3\x04\xe1\x04\xf0\x04\xfe\x05\x0d\x05\x1c\x05+\x05:\x05\ +I\x05X\x05g\x05w\x05\x86\x05\x96\x05\xa6\x05\xb5\x05\ +\xc5\x05\xd5\x05\xe5\x05\xf6\x06\x06\x06\x16\x06'\x067\x06\ +H\x06Y\x06j\x06{\x06\x8c\x06\x9d\x06\xaf\x06\xc0\x06\ +\xd1\x06\xe3\x06\xf5\x07\x07\x07\x19\x07+\x07=\x07O\x07\ +a\x07t\x07\x86\x07\x99\x07\xac\x07\xbf\x07\xd2\x07\xe5\x07\ +\xf8\x08\x0b\x08\x1f\x082\x08F\x08Z\x08n\x08\x82\x08\ +\x96\x08\xaa\x08\xbe\x08\xd2\x08\xe7\x08\xfb\x09\x10\x09%\x09\ +:\x09O\x09d\x09y\x09\x8f\x09\xa4\x09\xba\x09\xcf\x09\ +\xe5\x09\xfb\x0a\x11\x0a'\x0a=\x0aT\x0aj\x0a\x81\x0a\ +\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b9\x0b\ +Q\x0bi\x0b\x80\x0b\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\ +\x12\x0c*\x0cC\x0c\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\xc0\x0c\ +\xd9\x0c\xf3\x0d\x0d\x0d&\x0d@\x0dZ\x0dt\x0d\x8e\x0d\ +\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\x13\x0e.\x0eI\x0ed\x0e\ +\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\xee\x0f\x09\x0f%\x0fA\x0f\ +^\x0fz\x0f\x96\x0f\xb3\x0f\xcf\x0f\xec\x10\x09\x10&\x10\ +C\x10a\x10~\x10\x9b\x10\xb9\x10\xd7\x10\xf5\x11\x13\x11\ +1\x11O\x11m\x11\x8c\x11\xaa\x11\xc9\x11\xe8\x12\x07\x12\ +&\x12E\x12d\x12\x84\x12\xa3\x12\xc3\x12\xe3\x13\x03\x13\ +#\x13C\x13c\x13\x83\x13\xa4\x13\xc5\x13\xe5\x14\x06\x14\ +'\x14I\x14j\x14\x8b\x14\xad\x14\xce\x14\xf0\x15\x12\x15\ +4\x15V\x15x\x15\x9b\x15\xbd\x15\xe0\x16\x03\x16&\x16\ +I\x16l\x16\x8f\x16\xb2\x16\xd6\x16\xfa\x17\x1d\x17A\x17\ +e\x17\x89\x17\xae\x17\xd2\x17\xf7\x18\x1b\x18@\x18e\x18\ +\x8a\x18\xaf\x18\xd5\x18\xfa\x19 \x19E\x19k\x19\x91\x19\ +\xb7\x19\xdd\x1a\x04\x1a*\x1aQ\x1aw\x1a\x9e\x1a\xc5\x1a\ +\xec\x1b\x14\x1b;\x1bc\x1b\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c\ +*\x1cR\x1c{\x1c\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1dG\x1d\ +p\x1d\x99\x1d\xc3\x1d\xec\x1e\x16\x1e@\x1ej\x1e\x94\x1e\ +\xbe\x1e\xe9\x1f\x13\x1f>\x1fi\x1f\x94\x1f\xbf\x1f\xea \ +\x15 A l \x98 \xc4 \xf0!\x1c!H!\ +u!\xa1!\xce!\xfb\x22'\x22U\x22\x82\x22\xaf\x22\ +\xdd#\x0a#8#f#\x94#\xc2#\xf0$\x1f$\ +M$|$\xab$\xda%\x09%8%h%\x97%\ +\xc7%\xf7&'&W&\x87&\xb7&\xe8'\x18'\ +I'z'\xab'\xdc(\x0d(?(q(\xa2(\ +\xd4)\x06)8)k)\x9d)\xd0*\x02*5*\ +h*\x9b*\xcf+\x02+6+i+\x9d+\xd1,\ +\x05,9,n,\xa2,\xd7-\x0c-A-v-\ +\xab-\xe1.\x16.L.\x82.\xb7.\xee/$/\ +Z/\x91/\xc7/\xfe050l0\xa40\xdb1\ +\x121J1\x821\xba1\xf22*2c2\x9b2\ +\xd43\x0d3F3\x7f3\xb83\xf14+4e4\ +\x9e4\xd85\x135M5\x875\xc25\xfd676\ +r6\xae6\xe97$7`7\x9c7\xd78\x148\ +P8\x8c8\xc89\x059B9\x7f9\xbc9\xf9:\ +6:t:\xb2:\xef;-;k;\xaa;\xe8<\ +'\ + >`>\xa0>\xe0?!?a?\xa2?\xe2@\ +#@d@\xa6@\xe7A)AjA\xacA\xeeB\ +0BrB\xb5B\xf7C:C}C\xc0D\x03D\ +GD\x8aD\xceE\x12EUE\x9aE\xdeF\x22F\ +gF\xabF\xf0G5G{G\xc0H\x05HKH\ +\x91H\xd7I\x1dIcI\xa9I\xf0J7J}J\ +\xc4K\x0cKSK\x9aK\xe2L*LrL\xbaM\ +\x02MJM\x93M\xdcN%NnN\xb7O\x00O\ +IO\x93O\xddP'PqP\xbbQ\x06QPQ\ +\x9bQ\xe6R1R|R\xc7S\x13S_S\xaaS\ +\xf6TBT\x8fT\xdbU(UuU\xc2V\x0fV\ +\x5cV\xa9V\xf7WDW\x92W\xe0X/X}X\ +\xcbY\x1aYiY\xb8Z\x07ZVZ\xa6Z\xf5[\ +E[\x95[\xe5\x5c5\x5c\x86\x5c\xd6]']x]\ +\xc9^\x1a^l^\xbd_\x0f_a_\xb3`\x05`\ +W`\xaa`\xfcaOa\xa2a\xf5bIb\x9cb\ +\xf0cCc\x97c\xebd@d\x94d\xe9e=e\ +\x92e\xe7f=f\x92f\xe8g=g\x93g\xe9h\ +?h\x96h\xeciCi\x9ai\xf1jHj\x9fj\ +\xf7kOk\xa7k\xfflWl\xafm\x08m`m\ +\xb9n\x12nkn\xc4o\x1eoxo\xd1p+p\ +\x86p\xe0q:q\x95q\xf0rKr\xa6s\x01s\ +]s\xb8t\x14tpt\xccu(u\x85u\xe1v\ +>v\x9bv\xf8wVw\xb3x\x11xnx\xccy\ +*y\x89y\xe7zFz\xa5{\x04{c{\xc2|\ +!|\x81|\xe1}A}\xa1~\x01~b~\xc2\x7f\ +#\x7f\x84\x7f\xe5\x80G\x80\xa8\x81\x0a\x81k\x81\xcd\x82\ +0\x82\x92\x82\xf4\x83W\x83\xba\x84\x1d\x84\x80\x84\xe3\x85\ +G\x85\xab\x86\x0e\x86r\x86\xd7\x87;\x87\x9f\x88\x04\x88\ +i\x88\xce\x893\x89\x99\x89\xfe\x8ad\x8a\xca\x8b0\x8b\ +\x96\x8b\xfc\x8cc\x8c\xca\x8d1\x8d\x98\x8d\xff\x8ef\x8e\ +\xce\x8f6\x8f\x9e\x90\x06\x90n\x90\xd6\x91?\x91\xa8\x92\ +\x11\x92z\x92\xe3\x93M\x93\xb6\x94 \x94\x8a\x94\xf4\x95\ +_\x95\xc9\x964\x96\x9f\x97\x0a\x97u\x97\xe0\x98L\x98\ +\xb8\x99$\x99\x90\x99\xfc\x9ah\x9a\xd5\x9bB\x9b\xaf\x9c\ +\x1c\x9c\x89\x9c\xf7\x9dd\x9d\xd2\x9e@\x9e\xae\x9f\x1d\x9f\ +\x8b\x9f\xfa\xa0i\xa0\xd8\xa1G\xa1\xb6\xa2&\xa2\x96\xa3\ +\x06\xa3v\xa3\xe6\xa4V\xa4\xc7\xa58\xa5\xa9\xa6\x1a\xa6\ +\x8b\xa6\xfd\xa7n\xa7\xe0\xa8R\xa8\xc4\xa97\xa9\xa9\xaa\ +\x1c\xaa\x8f\xab\x02\xabu\xab\xe9\xac\x5c\xac\xd0\xadD\xad\ +\xb8\xae-\xae\xa1\xaf\x16\xaf\x8b\xb0\x00\xb0u\xb0\xea\xb1\ +`\xb1\xd6\xb2K\xb2\xc2\xb38\xb3\xae\xb4%\xb4\x9c\xb5\ +\x13\xb5\x8a\xb6\x01\xb6y\xb6\xf0\xb7h\xb7\xe0\xb8Y\xb8\ +\xd1\xb9J\xb9\xc2\xba;\xba\xb5\xbb.\xbb\xa7\xbc!\xbc\ +\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\x84\xbe\xff\xbfz\xbf\xf5\xc0\ +p\xc0\xec\xc1g\xc1\xe3\xc2_\xc2\xdb\xc3X\xc3\xd4\xc4\ +Q\xc4\xce\xc5K\xc5\xc8\xc6F\xc6\xc3\xc7A\xc7\xbf\xc8\ +=\xc8\xbc\xc9:\xc9\xb9\xca8\xca\xb7\xcb6\xcb\xb6\xcc\ +5\xcc\xb5\xcd5\xcd\xb5\xce6\xce\xb6\xcf7\xcf\xb8\xd0\ +9\xd0\xba\xd1<\xd1\xbe\xd2?\xd2\xc1\xd3D\xd3\xc6\xd4\ +I\xd4\xcb\xd5N\xd5\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\xe0\xd8\ +d\xd8\xe8\xd9l\xd9\xf1\xdav\xda\xfb\xdb\x80\xdc\x05\xdc\ +\x8a\xdd\x10\xdd\x96\xde\x1c\xde\xa2\xdf)\xdf\xaf\xe06\xe0\ +\xbd\xe1D\xe1\xcc\xe2S\xe2\xdb\xe3c\xe3\xeb\xe4s\xe4\ +\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\x1f\xe7\xa9\xe82\xe8\xbc\xe9\ +F\xe9\xd0\xea[\xea\xe5\xebp\xeb\xfb\xec\x86\xed\x11\xed\ +\x9c\xee(\xee\xb4\xef@\xef\xcc\xf0X\xf0\xe5\xf1r\xf1\ +\xff\xf2\x8c\xf3\x19\xf3\xa7\xf44\xf4\xc2\xf5P\xf5\xde\xf6\ +m\xf6\xfb\xf7\x8a\xf8\x19\xf8\xa8\xf98\xf9\xc7\xfaW\xfa\ +\xe7\xfbw\xfc\x07\xfc\x98\xfd)\xfd\xba\xfeK\xfe\xdc\xff\ +m\xff\xff\x80\x00 P8$\x16\x0d\x07\x84BaP\ +\xb8d6\x1d\x0f\x88DbQ8\xa4V-\x17\x8cF\ +cQ\xb8\xe4v=\x1f\x90HdR9$\x96M'\ +\x94JeR\xb9d\xb6]/\x98LfS9\xa4\xd6\ +m7\x9cNgS\xb9\xe4\xf6}?\xa0PhT:\ +%\x16\x8dG\xa4RiT\xbae6\x9dO\xa8Tj\ +U:\xa5V\xadW\xacVkU\xba\xe5v\xbd_\xb0\ +XlV;%\x96\x0a\x01\xb4\x00Cv\xb1\x15\xb4D\ ++\x0c\xdcC\xe0\xeb\xa0F\xd2\x01y\xde^\x0e;\xe3\ +q\xc5\x7fm_\xdcM\xbb6\x17\x0d\x1b\x0a\xe2CD\ +\x5caX\x8f\x8f,\x88rB\xa0VT\x19\x14|\xe6\ +^\xedl\xe31\x85\x9fY.tJ\x87V\x95\xc9\x87\ +\xd4a\xc4\xda\xb1\x81g\x5co\x1elIa\x1d\xa0V\ +X\xf0\xdc;\x17\xdb\xb5r\xc7|\x9dj\xf0YZ\x9e\ +%d\xbd\xc7:\x99yG\xf0O4\x17<}t_\ +\x08\x9e\xa1\xb3|\xb1N\xf1{T\x9b\xa08$f\xf0\ +\x1f\xcb\x1e3u1_\xe7M\xa5\xfdG\xd7w\xb5\xd3\ +\xdb\xf8NA\x7f0rS\xec\xb9\x16\xfeG\x15FO\ +\xf5v6\xc0\x02A\xfd\x01\x9f\xcf\x8c\x0c\x98\x8e\xf0I\ +**\xc1\x83R0m\xc2\x06\x938k\x19fl,\ +`\x9dp\xc9\xca\xe8\x9fG\xcb\xe6\x05\x81\xabX6\x11\ +\x06q(\x80\x11\xc5\x01`A\x15\x85\x0b\xba0V\xc6\ +\x04\xc1\x15\x19\x8d\xa7\xecl~@\xf1\xcaJ-\xc7\x83\ +\x90\xe3\x1f\x91\xa8\xa3\x82j\x99D\xb4\x8c>\x19rI\ +{\x02@\xa8\xb8\x09'\x80\xa1\x94\xa4\x1f\x0cr\xa8\xf9\ +\x12\x86b\x020O\xcb\x849+/\x8fQ\xd4\xc4\x8e\ +\x84\x93(ZPM\x06@\x115\x81H\x81m7\x94\ +\x84,\xe433'\xc9\xee\x94\x00S\xc8\x04-O\x83\ +\x88\xdd?\x913\xd0\x06\x88\x1eT)\xde*Q\x01C\ +\xdaw\x1dS\x1d\x1c\x8a\x92T\x89l\x1dR\x82J \ +]S\x05H\xffM\x8b\xc7\xdd<}&\x81\xb5D!\ +\x90u)H\x09\xd5\x00\xc2 ZU\x85\x09\x01W\x8c\ +\x14}d\x86\x82\xd5\xa87\x18\x15\xa6\xcb\x9a\x04\xb9\xe8\ +[Ju\x1c\x8dp\xb2\x18\x1e6)\xda\x9e\x05\xd6H\ +tMY\x86\x04\x9e\x02\x00\xa8e<}\x9fB\xbd\xac\ +\x160L%gm\xa0\x83E\xbcA\x0c\x97\x08\xfa\x88\ +\x10\xd7(\xd0W]\x04\xca\x889]\x84|\xf8-\x0e\ +\x08\x84\xd0P\x11\x0f\xb1(<\xdb\x95\x9b\xba\x09W\x06\ +\xc3h\x08\xb6\xc8]\x16u\x0a\x18(H{a\x07\x9a\ +\x88\x03a\x80AO\x87\x9a\x00\xfe$\x13\xa1\x876,\ +p\x0a\xd8\xc8V|c\x87\xb5\xf31c\x81\xbbH \x86\x19\xfbi\x863\xee\ +\x02\x04\x98\xa8\x14[\xa9\x96\x15o\x01\xa2\x19R\x90c\ +#\xae\xec\xec.\xd2\xef\xa2\x03[\xc0T\x1a\xdd\xe3\x84\ +\xa4\x19\x07\xe8$\xfe7\x09F7$[\xaa\x84o,\ +X\x07\xfc\xc8\x9e\x86\x11|\xe8\xdcT\xf4\x04\x9f\x03|\ +\xd4A\xb0\x87\xd2\x88\x96a4@C\x87\xca\xa9H\x92\ +T\x9d+\xces\xdd\x01S\xd1t}\xca\xb0\x03\xf7\x80\ +N\x1eS\x9a \xf7\x84\x12\xa1\x84\x17\x8c1\x96^I\ +=\xddy\x8a\xab\xf2\x16\x87\x04\xf7\xa4cE\xc8X\xeb\ +\xeb\x8aE\xff\xb4W\xf9\xbe\xea\xa2>|\x04\xde\x0a(\ +\x0c\x99\xa4\x06~\xb8\xe2\xf0ll}\x86w\xbd\xf7\xa9\ ++pTR~\x86v\x18\x03\x00\xe8a\xbd\xfd\x9a\xd1\ +\xe0\xb6\x0cV\x9a\xa0~\x10\x0du\xae\xc1\x1c\xbb\xc3\x89\ +\x10uB\x05\xd5\x08\x08\x09\x03\xca\x0b\xf7\x00\xedTV\ +\x0d\x805\x05\xc1\x01\x0cC#\xacs\x05\xd8<\x0d\x07\ +d!\x1c\xf0B\x12\x13\xd0k\x09\xc2\x12F\x12\xc2\xf1\ +\xea\x90\xa2\xf2<\xc7\x80\xe1\x86Ce=\x00!\xd1\x0d\ +\xc7\x18\xc1\x87B\xc4Z\xc3\xd1E\x09b\x01\x152\xa0\ +(\x06/\xf0,\x88\x81\x08\x1c\x89@\x90\x0cD\xd0:\ +\xaa\x00\x98\x18\x061L\x1f< <\xf1\x09\x18\xc8\x8b\ +B\xe8FE\xd0\xde8#\x00\xd8\x88.\xea!\x80\xd5\ +k\x11\xe0\xb8\x1a\x04\x11X\x13\x19 B\x0a@\xecq\ +\x04\x91(\x0e\x028\x8c\xae\xd5\xe9:B\x03li\x06\ +\x18\xfc\x0eX\xe0\xf8c\xd1\x8c\xad\xc3Uv\xcf\x99\xf8\ +\x17\x91@u\xa9\x01 -#@\xb4P\x03\x12(\x0b\ +\x81\xd6$\x07\xc1<\x94\x03\xa0RN\x01\x98$V\x1e\ +\xb8u{/nB\x14u\x9e\x01\x5c04\x8e\x80\x91\ +\x14\x020W%\xc1:\x22\x04@>Z\x01$\xd6\x02\ +\x00[XQ\xc2\xce^\x0a\x01\x03/\xc3\x0c\xa5'\xcd\ +\xa4\x06\x81\x04\x18\x15CXD\x99AT\xd5\x82`^\ +\x8eG\xe4\xd1\x1fr\x04{\x1b\x81\xe0;\x1a\xc2\xfb!\ +\x83:n\x0c#\xc0\x19\x9cl\xc2'\x11\xc4\x0e\x82G\ +,#E\x81n\x05e\x81\x1b\x0f\xd1\xf8\xc0\xe1\xb8\xe8\ +\x1cQ\x80p\x0d\x83\x046\xa1\x90\xe1\x1b0ls\x0f\ +I\xfc\x1b\xf0\xb8\x1a\ +\xe2hK\x00\x80\x130\xc4\xe2\x02\x1e\xebn\xb3\x09\x02\ +2\x13\xb0\xd4\x18\x80_\x0d\xa0v!\x87\xd8\x1b\x01\x9c\ +\xe3\xa0h\xe0%\x1c\xb9\xcb\x98\xfe\x82\x16<\xe1^\x13\ +d\xe4\x10\xa0\xcd\x0d\x021\x01\xa0\xfa\x13\x80\x9f\x10\xed\ +v!l\x06\xd8\xcb<[e\xfa\x96\x02\x18\xc8\x81\x94\ +\x17\x8c\x82\x0dP\x01\x10B-\x06.A\x06\x8f\xd2i\ +/D\x06l\xc2\x1b,\xc6;h$\xe5@ \x02\xa8\ +\x90\x0f1T\x12\xe9\xd4!\x91B\x19\xeb\xc5\x13\x02,\ +\xe8\xce\x90\xb2.\x94!\xea\xc8\x0a\x86P\x15\xa2\xa0\x86\ +\xa9\x10\x02\x0c\x92\x9b\x22\xe8\x02Np\x8d\x8f\x10\xb9\xc7\ +\x08C\xe0\x1cHC\x84\xfdj=\x16B(\x8d\xc0R\ +\xfe!\xa4\x00q\xac\x00\x82\x18\x85@\xf6zA<\xbb\ +\xc2nPH\xa0\x92\x89*\xb5\xa9\xd4\xb5\xaf\x10\x93\x80\ +(\x03$>\x01\xa7x\x00\xe0\x12&\x01G\x1e!\x18\ +\x121\xe8\xa7\x91\xa0\x22oh\xe5\xa1\xb2\xc9\x82\x18v\ +\xc1&\xca\xe3\xca#e\x04\x96\xe0\x14\xc9\x22\xe2\x03 \ +>\x8d\x89.\x8d\xa3&\xb9\xc94\xc9\x22\x90\xa0\x8b\xc4\ +b\xc1\xcc\x1b\xf1\xee\x22b\xef\x1b\x81\x8cy\xe3\xf6!\ +aq#\xe1L|\x00\xf8\x0bb\x08\xc9\x91\xd1!\x06\ +$\x04\xcf\x10\xe7\x09e\x19F\xb1\x17\xed^+\x8a\xb8\ +\xcb\xec\xee\x1aa\x91\x22\xe24\x10\x92t\x14\xa0\x91'\ +\xa0\xb4!\x8d\x91&\xa1\x8e\x8d\x92\x0e\x03\xe9\x88\x02.\ +\x148\xb0\xae\x1e\xa1\xe4B\xc1\x9a\x18\x011* \xfe\ +g!\xb4grp#\x80\xc1+ \xf0\xf0\xa0\xd8\x10\ +\xe4r\xae\xe9\xe0\x86\xe3\x04\x1cC\x02/\xe1\xb6\xa9\x81\ +\xda\xb3\xcc\x06\xc7J\x9e\x1cr\xae$\x8f\x92\xf9o\x9a\ ++\x8aA-j\xfc\xa4\xe2\xf8\x1bj\x5c\xa5a\xac\xd9\ +\x0af\xd3r\xde'\xf1\xda\x01!\x1f0\xa1du\x02\ +p\xe0,\x07-\xa5~\x1c\xb2\xd0\xb3\xc86\x1c\xa9\xf2\ +\x1b*P\x1b\x8f\x8a\xab\xa6\x130\x22\xb5\x19Mr\x14\ +G2\x07\xe76#j\xee\xc0k\xd6\x1b\xed\xa6\xa5a\ +\xaa\xa5\xd3J\x9e!\xc2\xb1S4[\x88j\xf8\x00{\ +6`\x9a\x96\x80\x1e\x02N2\x1f\x8a&\x86(d\x1b\ +S+\x0b\x81\xbe\x1a\xcd\xa71\xb0\x095\xf3\x8d8\xf3\ +\x9193\x959s\x999\xb3\x9d9\xf3\xa1:3\xa5\ +:s\xa9:\xb3\xad:\xf3\xb1;3\xb5;s\xb9;\ +\xb3\xbd;\xf3\xc1<3\xc5\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Default\x0d\x0a Cr\ +eated with Sketc\ +h.\x0d\x0a <\ +defs>\x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \x0d\ +\x0a\x0d\x0a\ +\x00\x00\x03\x87\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / entity\ + / Loop v2\x0d\x0a Cr\ +eated with Sketc\ +h.\x0d\x0a <\ +g id=\x22icon-/-out\ +liner-/-entity-/\ +-Loop-v2\x22 stroke\ +=\x22none\x22 stroke-w\ +idth=\x221\x22 fill=\x22n\ +one\x22 fill-rule=\x22\ +evenodd\x22>\x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x19\xf8\ +\x00\ +\x00\xe0\x1cx\x9c\xed\x9c\x09\x5c\x14\xd5\x1f\xc0\xdf\xec\xc5\ +\xb5\xdc\x87\x17\xea\x8a\x1c^\xdc7\xca\xb9\xa0\xa0\x22\x08\ +xf\xc9\xb2;\xc0\xea\xb2\xbb\xee\x01hVj\xa6\xa5\ +\x96Gf^e\x1e\x99GiY\x1e\x99\x1d\x9af\x87\ +\x7f\xcb\xdb\xb2\x03\xf3oj\x99Q\xa6V\x9a\xfc\x7f\xbf\ +Y\x16\x06\x04E\x19\xc0\xfe\xcd\x97\x0f\xb3o\xde\xf5\xfb\ +\xbd\xdf\xbcy\xd7\xcc\x9b\xccL\xd2\x8b\x10bK\xbc\xc8\ +M\x22\x02\x17E,\x07!\xf3\x93\x0f\x07\x8a\xe5\x160\ +n\x88GyQ\xc2j\x7f\x88LI\xaa\xdd\x028\xb8\ +Z\xf3\xc9\xaa\xa4\xdcXq<\xab\xddLjV\x9e\xed\ +\xac\xf1\xe7\x11\xaa#\x11[\xdcT\x17\xaaS\x8d\xdb\x9f\ +\xf2f\xe5\xd3\x83%+\x14\x8f\xc4\x07\x5cAT\x18\xe3\ +\xf6\x00w6\x95S\x1b_\xb0\x0b\x8fQ\x9d\xe0T5\ +#\x03\xdd\xd2\x1e\x84DO\x99\xa9\xb6\xca\xfd\xaer\xa6\ +\x91\xd8\x80\x7f:!\xf2uX~\xb0\x87\xe5\xcf\xfe\xc6\ +\x14B\x02\x5c\xac\xbf\xc9*]\x01-\xcb.\xd6\x99t\ +\xc6b\x9d^&\x97\xcb\xc2BB\xa3e=F\xa8\xb5\ +*]\x99\xb1'\xc1\xd3\xb8\x90\xb0\xb8\x90pYhd\ +\x5cX88H\xbf\xc4r\xbdB9\x9e6\xc9\x0a\xe8\ +\x22\xb56\xde\xe7\x97w\xde\xf7\x91\xa9U\xf1>#\x22\ +3C2\xf5r\xbaX\x9d>\xc9@\xe7N\x1a\x92\xa7\ +\x9c4^\x19\xab\xf2IL\xb0\xefW\x1eW^\xa2/\ +\xa1M\x0aYy\x89Fk\x8c+\x8f\xf7Q\xa0\xfc8\ +p\xa3w\xb0\x8f\x8c\x89b\x1a\x1f\xefcQldf\ +\xb6L\xae3\xd0\xb2\xc8\xa0\xa8@ehx\x8c,:\ +6(426&,\xa2\x0f*\x1a\x15\x1c\x12\x1b\x1c\ +\x1a\x11\x18\x12\x1a\x17\x12\x1b\x17\x12*\xab\xc6'\xc1\x1e\ +\x8e\xfd\x0c\xaa\xc2\xb8\x9c\xd4\xfe\xd5\xe2\xe0,\xde\xa7\xd8\ +d\xd2\xc7\x05\x07\x97\x95\x95\x05\x95\x85\x07\xe9\x0cE\xc1\ +\xa1\xb1\xb1\xb1\xc1!a\xc1aa\x81\x10#\xd08Q\ +kR\x94\x07j\x8d\xdd-\x99X\xf3I\xa5\x8dJ\x83\ +ZoR\xeb\xb4284($\xb8\xa1D*eM\x1a\ +\xbd\xd9\xa0aTS)\x83i\x0d]BkMFH\ +\x17\xda`:\xbd\xf5\xda5,\xb2&\xb8Q\xc1\xa0m\ +f\xe6\xed\xf5-)i0\xa5\xd1\x94Vj\xba}J\ +c\xdeD=\x1d\x9cC\x1buf\x83\x92N+\x85\xa2\ +\xd4\xda\x15M\x0b\xd2\xe3\xe4\x06Za\xa2S\xe1?\x01\ +k[`HX`Hx\x1e\xd4\xb6\x90\xa8\xb8\xb0\xc8\ +\xc0\x90\x98\xb8\x90\x90~\xc1\xf5b\xd6\xcb#S\xa7R\ +\x17Nl \x0f\xa6\xc6\xb2\xf3`\xc5\xac\x9f\x07\xd4A\ +\x95\xc2\xa4hR.\xec\xb8\xac|T\xca\xb8B\x9d\xa1\ +DaJP\x97(\x8a\xe8`\x93\xba\xb0\xb0_p\xad\ +/+j\xcd\xa5\x89\x93\xeb4:\x03\xe8E'\x84\xf7\ +\x0bn\xc8\xbb\xc1T\x19ry\xb6AW\xa8\xd6\xd0\x09\ +\xc6\x9c\x01)\xb2\x8c4yThlTT`XP\ +(;\x1bV\xbc\xba\x05\xce\xcc\x8c\xcb\xd0\x1aM\x0a\xad\ +\x92\xceHM\x00\x8f \xb5Z\x15WX\x18AG\x85\ +\xc7D\x05\xd2\x85\xe1t`(\x1dA\x07\x16D+c\ +\x02\xa3\x14\x11\x91ttxAX\xb4R\xc5\xd8\xa0n\ +\xf2[\xb2N\xd5)\xcdXu\xab\xb3V\xdde\xd6\xac\ +\xe4\xb7d\x9dePC\xb3\xa3\xd04SD\x03\xd9\xdc\ +\x22*]m4\xe9\x0c\x13\x13\xeaT\x7f\xa6A\xc8\xa5\ +'\xd4\xf5\xb5\x06h\xd4L\x03\xa1W\x18\x8c4V\xff\ +x\x1fk\xfd\xf7\xb9%\x01\xa6an\xa38\x85\x12\x9b\ +\x96\x04%S\xc3A\xc5:\xbe\x8d'S\xdf\xeb\x05\xbc\ +%y\xe32\xca\x8ai\xed\xed\xeeLV\xac\xc631\ +\xea\x0aMe\x0a\x03\x9d\x5c\x04\x96N\xb8c\xbfc\xcd\ +\xb5n\xb2[\xec\x1dl1x\xbd\xcb\x13|\xeb\xf5\xb1\ +^\xf3z\xd7\xd3\x12\x95\xd5\xb6[:\x8e\xe0\xea\x9e\x03\ +:\xad\xe0\x9a^\xab\xa1\xc2q\x0f/\x84\x17\xc2\x0b\xe1\ +\x85\xf0Bx!\xbc\x10^\x08/\x84\x17\xc2\x0b\xe1\x85\ +\xf0Bx!\xbc\x10^\x08/\x84\x17\xc2\x0b\xe1\x85\xf0\ +Bx!\xbc\x10^\x08/\x84\x17\xc2\x0b\xe1\x85\xf0B\ +x!\xbc\x10^\x08/\x84\x17\xc2\x0b\xe1\x85\xf0Bx\ +!\xbc\x10^\x08/\x84\x17\xc2\x0b\xe1\x85\xf0Bx!\ +\x1c\x0b\xb1\xaf\xdd\x07FkU\xf1>e>\x89\x09$\ +&%#S\xe4\xc7\xec9s!\xf5`\xc2\xe2\x18\xe7\ +\xd9\xeapfw\x1eq\xd0\x1b\xd4ZS\x96\xd9\xa47\ +\x9b\xe0\x14\xb7\xc9\x91l\xa3)\xb7@\xa7\xd3012\ +\xb4&\x9a\xd6\x9aK\xacn\xfc\x95k\x0cx\xee\xcc\xa4\ +\xcdU\x97c\x8c\x14\xb5\x09\xd3\xd4\xe6I\x1b\x86(J\ +\xe8\xbc\xb4\x91y5\xc2,\x09\xb2\x0d:]a.m\ +2\xeb\xb3\x0a\xc6)\xc1[J\xb2\x89\x81\xe8\xe0\xaf\x90\ +\xc8H.\xa1\x89\x89\x98\x89\x9eIb\xaf\xaf\x89m\xcd\ +&Ec\xd2Vk$-0\xab5&\xb5\x96\xc9\x12\ +\xce\xed\x98\xd8\xf2\xccQ\x83,%\xee\x8b\xf1\x05\x81u\ +J\xec\xc6*q\x16\xb3\xf3\xc0\x08\xbe\xed\x98r\xe9M\ +Zk!\xa0\x90\x05\x86\x9a\x93\x9c\x22cfm\x88A\ ++\xaf=\xd1\x9ajO\x06\x17h\x8c5'C\x8aL\ +\xa55'i%\x9a\xd4\x9a\x13\xb0cm\xd6)\xca\xf1\ +E\xd5\x86\xb0(Hr\x06\xa4\xc8\x89e\xdb$\xc9Q\ +\xc9d*\x9d\xb9 IWQs1\x07\x18\xb4\xb7\xf8\ +\xa5hn\x8d\x97bP\xe5\x0d\xd3\x9a\xfaw\xcf\xd1\x98\ +\xd8\x95!E\xa3\x925\xe4\x9fc\xd4\x98\x18\xff\xecr\ +MRN\x8d\xb7})\xad4\xe9\x0c\xa9\x0a\x93\xa2\xa6\ +Vd\x17e\x1b\xad\xb5\x02\xdd\xd5\xbfr\xc6\x08t\xa1\ +\xa9\xa1\xec\xf3t\xfa\x06\xc5\xe6*5\x16\xffl\x832\ +iT\x8d\xb7\x8b\xd2\xa0\xd3\x8f(\xa6\xe1\xe2\xc2\xf5R\ +k\x8b\xac\x16s\xc2\x80\x1c\xd0)Eg2\xe9J4\ +:mQu\x12\xa95\x04U`\xf9;Z\xfds\xd4\ +E\xc5\xec\x00\x07k\x00\xe8V\xe3\x8d5G\xf8\xb3E\ +\x07\x92N,{?\xab\x7f\x99Z\xe5\xcf\x849\xd5\x96\ + qJu\xaaK\x16q\xd2<\x83Bk\xd4+\x0c\ +\xb4V9\xd1R\x13=\x99\x90.\x18J\xf2\xa0\xb6+\ +\x88\x96\x18\xa1\x8e+\xc0M\x83[I&V\xdf\xa5\x91\ +LLWF\x1e\xa9\xaa\xaa\x16\xa1\xa2,\xa1\x9d\x993\ +\x91UO\x91c\xcdy\x17\xe6\xbcC\xdds\xe1e\xe6\ +\xdc\xce\xaa\xa9%\x97\x00K\xfb`o\xadp\x96r\x09\ +\xaf0\xeet\x12\x5cX\x08!\x1a8J\xaa\x13Y|\ +V.YZ\xe3\x13\xc6\x1cG\xc3\xd1\xea\x13\xc9\x1c\x03\ +k}\x98<\xff`\xdcz,IC\x08\xcfc0\xf7\ +a\x8c-,e\x13T\x9f\x09\xea\x9c\x85Xt\xac\xb6\ +j`\x9d0\xdb\xeak\xcf\x5c\x01A\x92\xe5\xbf:\xac\ +K=\xfbwD\x970\xb2\xa6\xd4\x16\xf2\xab\xff-\x96\ +\xb9\x15\xb6\x1f;n\x83\x11\xac\xd46aDk\xd6h\ +,\x0a\x13I\x81\xce\xacU\x19\xeb\xb5 JS\xa8U\ +M\xbc\xedXU\x9e\xd4\xbb7HJ\xed=\xc4\xa8\x91\ +S{\x8b\xe0\xb9\xc4\xa8Q+i\xe3p\xcd`\xbc\xc9\ +\xa9:r\xc4L\x188\xdc\xe0\xdf\x869\xc9He\xe5\ +mSd\xd0\x99\xf5u\xbc$:f\xeb\x9f\xb5\xfdN\ +\xcb\xc5D\x96\xed\x80p\xee\xa80\x9bt\x03h-m\ +\xc0\xadx\x8c\xf6\x13\xf5\xd6\xee\xc7\xde\x12\x19}0$\ +\xa3\xa4H\xd6\x0a\xe5\x17\x9a\x0d\x9a:\x9d\x18c\xfc\xba\ +>\x99\xc6\xa2\xba\x1d\x9dD\xa11\xe5)\x8a\xea\xf89\ +)iHG\x97\x9b2\x8c\xe9y\x99\x83\xadM\xa9\xad\ +\xd5\xbbNd\xbbb\x9daR\xb2F]d\xb5\x94\xb3\ +\xa5\xf0\xe9Vo\xb4\xae\x8a.T\x98\x99\xb6\xd4\xae\x94\ +6\x98\x1a\x88>\xdc\xea]7\xbaCA\x11\xb3\xc3\x95\ +e\x5cWK\x82\x94\x015\x01\xa8\xc6\x10\x9d\x16\x7f\xed\ +L:=t\x98F\x9am8{\x0d\x18\xf2\x16_i\ +\x01\xd3(\xdf\xe2\xef`\xc0\xa6\xb7\x9e7s\x07\xf5\xb0\ +\xa4\x83\x7fAb%\xa9\xf5\xf7`\x9cx\x09\x85\xcc9\ +\xc6 \x82\x93\xd5&\xca\xb7\xfcSP\x05\x98;a\x17\ +iO\xa8\xaa\x93U?\x13)\xb3\xc3q\xac<\x13\xce\ +/\x11'\xe6\x8c\xa8\xa6`\xba\xaaSd:\x91\xda\xda\ +\xda\xda\xd9J\xed\xec\xa4\xae\x0e\xf6\x0e\xae\x9e\xceR\xa9\ +\xb3g{wwOw\xf7\xf6\xaeR\x86\xea\x9f\x86\xa1\ +\x1c\x1d\x1c\x1c\x9d\x1c]\x9c\x9c\x5c<\x9c\x9c\x9c<\xf0\ +\xe0\xe4aI\xe2\xda\x94\x0c\xaa> \xae\xb6\xa0|\xbe\ +\x90\xf2!\x02WJ\xe8JU}\x03\x05\x95T\xed\xa5\ +\x12AK1\xc5Pm8!\xa1\x04\x22\xb1\xc4\xc6\xd6\ +\xce\xde\x81\xaa\x1fH\x11\x81\xd0\x1a\xe8B(\x11%\x14\ +\x88\x04b\x1b\x89\xadX(\x0d\x87@W\xa1\xa8\x9b[\ +\xa88y\xa8\xc2\xddg\xc2\xd40\x89\xc7\xfc\x95o\xa4\ +t\xf7\xf5\xcc\xd9]\x10\x1ea\x98vHn\xe3\xb7 \ +\xb7\xf2\xf4\xafJc\xa4\xd7\xaa-\x8f\xfb\xa7>\x9b\xa7\ +J\xdb\xb3\xda\x14\xd5\xee\xf0\xb0\xef\xe9\xdf\xde\x9c\xfe\xe1\ +\x11\xf3\x99\xcb\xfd\x03\x16\xaey\xe2\xad\xe7\xf6\x1e\xfd\xef\ +\xef/o\xddw\xec\xec\x95\xe1\x85\xa53\x16\xad\xdd\xf6\ +\xd1\xf1\x1f\xaeF\x0f\x18QT6\xf3\xf9W\xb6\xef?\ +q\xee\x9a+\x11\x08@[\x11\xa3\x93\x8dD\x1c\xc9\xa8\ +\xd0-\xd4M\x04\x1aL\xf0q\x17\x87M\x9d\xef\x81\x1a\ +\xec\xce9T\x19\xee[p\xda0m\x81<\xd7Si\ +\x8c\xf8\xd5O\x82\x0a\xd8\xf8G\xee9\x0cJ\xacn\xa7\ +J\x1b\x16e\xa2\xbf\xafQ\xa1q\x0d\x02jU\xa8\xfa\ +\x8aH\x85\x8cLW\x92H\xae\xe4-\xc8\xf0\xeb\xe9\xbb\ + c`\x86\xef\x82\x9c\x05\x19\xbe\x0b\xd7T{dU\ +}q\xbb\xc0C\xb7\x0b<|\xbb\xc0#\xb7\x0b\ +!\xc02\xd4m\x03\x9b\x08^[\x8b\xeb\xf2P\xe6\x9a\ +Q^\x07j\xfd\x1a\x8a\xa7[\x05m0\x8c\x06\x85\xf3\ +j\xfd\x0a\x96\x10\xb2\xfd\x09B\xda\x7fU\xeb\xe7\xfb\x12\ +\xd4Q\xb8n\xdb>g\x95\xc7\x0b\xeb\x0b\xebc\x1fj\ +Z\x19\x84\x06\xad\xe1\x8e\x11\x9a\x00K^\x10fWc\ +\x1eY\xaa\xa5\x97\x93\xa1\xdd\x94\xd0\x97\x99\x0d2\x18\x89\ ++iY`\xfdJ|\xcf\x09\x1b\xd6\xa3O\x0e]H\ +\xe3\x88\x9f\x96\x0d\x87Z\x06\x13\x16\xb8\xdcZ\x95\x9a\xf9\ +n\x89Z\xdb\xd8E\xbc\xc7d\xf5\xb0\xd4k\xc0}\xcd\ +M\xe216\x88\xb8|\xeeA\x84?\x1f \x22w\x07\ +\x22\x1c\xf3\x22\x84P5\xd7m\xb0\xddp\x82w\xde\x88\ +\xae\xe7,\xf5\x9e\xa1\x81!\xa7`.\x1e\x8cjf\xa0\ +E\xe49y2\xa5\xd9Pj\x09c\xc6Vb\x98C\ +8\x13\x0f\xd2\x01f6\xddI\x0f\x18\xfd\x87A#\xd3\ +\x97$\x9142\x90d\xc1Lg\x14y\x08\xe66\xc5\ +\xa4\x04\xe69ed2\x99Jf\x90\xd9d\x1ey\x8e\ +,%+\xc8\x1a\xb2\x9el\x22[\xc8v\xb2\x8b\xec&\ +\x1f\x91\xcf\xc8\x17\xe4\x189E*\xc8Y\xf2\x13\xa9$\ +W\xc9u\xe8\xecl(G\xca\x9d\xea@u\xa5\xfc\xa8\ +\xdeT\x18\x15C%Pi\xd4`*\x87\x1aE\xe5S\ +E\x94\x962S\x93\xa9\xc7\xa9\xd9\xd4\x02j)\xb5\x92\ +ZO\xbdA\xbdM\xed\xa6>\xa1\x0eQ_R\xa7\xa9\ +\x0b\xd4o\xd4_\x02\xa1@*\xf0\x10t\x16\xf8\x0b\x82\ +\x051\x82d\xc1 A\x9e\xe0AA\x91`\x82`\x92\ +`\xba\xe0\x19\xc1b\xc1*\xc1\xab\x82m\x82\xdd\x82\xcf\ +\x04\xc7\x04\x15\x82\x9f\x04W\x84D\xe8 \xf4\x12v\x13\ +\x06\x0ac\x84ra\x96p\xb4\xb0Ph\x10>*\x9c\ +%\x5c$\x5c%\xdc$\xdc)\xdc/<\x22\xac\x10^\ +\x14\xfe)\x92\x88\xdcE2Q\xa0\xa8\xaf(]4L\ +\xa4\x14M\x10=*\x9a#Z*Z'\xda&\xda+\ +:\x22:-\xaa\x14\xdd\x14;\x8a\xbd\xc5\xbd\xc5q\xe2\ +\x0c\xf1Hq\x91\xb8L\xb7;kw\xdd\xde\xd5>\xc0>\xde>\xcf~\x9c\ +\xfdT\xfb\xc5\xf6\x9b\xec\xf7\xd9\x7fm\x7f\xd9\xc1\xc1\xc1\ +\xc7!\xd6a\xa8\x83\xdaa\x8a\xc3b\x87\xd7\x1d>v\ +8\xed\xf0\xa7\xd4M\xdaK*\x97\x8e\x91\x9a\xa5\xcfH\ +_\x91~ \xfdRz\xd9\xd1\xd1\xd1\xdf1\xc9q\xb4\ +\xa3\xc9\xf1\x19\xc7\xf5\x8e\x1f:~\xeb\xf8\x87\x93\xbbS\ +\x90S\x86\x93\xca\xe91\xa7eN\xdb\x9c\x0e;]r\ +\xb6s\xf6sNv~\xc8y\x92\xf3\x22\xe77\x9d?\ +w\xbe\xe8b\xe7\xe2\xef\x22wQ\xb8<\xea\xb2\xcc\xe5\ +m\x97\x13.W\x5c\xdd]C]\xb3\x5cK\x5c\xe7\xb8\ +np\xfd\xc4\xf5\xbc\x9b\x8d\x9b\xbf[\x9a\x9b\xcam\xba\ +\xdbj\xb7\x0f\xdd\xce\xb8\x0b\xdd\xbb\xbb\xcb\xdd\x95\xee\x8f\ +\xbb\xafq\xdf\xe7~\xd6C\xe2\x11\xe0\x91\xe11\xcec\ +\xb6\xc7k\x1e\x07=*=\xdd<#<\x87{\x96{\ +.\xf3|\xd7\xb3\xc2K\xe8\xe5\xef\x95\xe1\xa5\xf1\x9a\xeb\ +\xb5\xc5\xeb\xb8\xd7_\xed:\xb7KnG\xb7{\xaa\xdd\ +\xa6v\x87\xdb]k\xdf\xa9}R{\xba\xfd\xac\xf6\x9b\ +\xdb\x1fk\xffW\x07Y\x87\xb4\x0e\xe3;\xcc\xef\xb0\xbd\ +\xc37\x1dE\x1d{u\x1c\xda\xb1\xac\xe3\x8b\x1d\xf7u\ +\xbc\xd8\xc9\xa3S\xdfN\xcaN\xb3:m\xe9\xf4\x95\xb7\ +\xc0\xbb\x97w\x8e\xf7\xc3\xde\xab\xbd\x0fx_\xe9\xdc\xa5\ +\xf3\x80\xce\xfa\xceK:\x7f\xd8\xf9b\x17\xaf.I]\ +\xc6uY\xd8\xe5\xbd.\x17\xba\xbawM\xe8\xaa\xee\xba\ +\xb0\xeb\xfb]\x7f\x94y\xca\x92e\x1a\xd9b\xd9^Y\ +e7\xefn\xe9\xdd\xcc\xddVv;\xd8\xed\xbaO\x80\ +\xcf0\x9fi>\x9b}\xbe\xe9n\xdf=\xa6{a\xf7\ +\x85\xdd\xf7t\xaf\xf4\xed\xea\x9b\xe9;\xd9w\xa3\xefW\ +~v~1~\xc5~\xcf\xfb\xed\xf7\xbb\xe6\x1f\xe0?\ +\xc2\x7f\xa6\xffv\xff\xf3\x01\xed\x032\x02&\x05l\x0c\ +\xf8\xba\x87c\x8f\xc4\x1e\x13z\xac\xeaq\xb4\xa7\xa4g\ +L\xcf\xf1=_\xe8\xf9E/A\xaf\xc8^\xc5\xbd\x96\ +\xf5\xfa\xbc\xb7\xa0wTou\xef\x17z\x1f\xea#\xee\ +\x13\xdbG\xdbgU\x9f\x13\x81\xd2\xc0\xe4\xc0\xd2\xc0\x8d\ +\x81\xa7\x83\xbc\x82\x06\x07M\x0b\xda\x1et)\xd87x\ +t\xf0\xfc\xe0\xfd\xc17C\x22C4!kBN\x85\ +\xba\x85\x0e\x0c\x9d\x16\xba3\xf4\xb7\xb0^a\xca\xb0e\ +aG\xc3\x1d\xc3\xfb\x87?\x16\xbe#\xfc\xd7\x88\xde\x11\ +t\xc4\x8b\x11'#\xdd#3#gF\xee\x89\xfc;\ +*:\xca\x10\xb5)\xeaB\xb4ot~\xf4\xf2\xe8\x13\ +1\x1e1\xd91sb>\x8e\x15\xc7\xa6\xc4>\x16\xbb\ ++\xf6\xcf\xb8\xa88S\xdc\x96\xb8_\xfa\x06\xf6\x1d\xdf\ +wC\xdf\xf3\xfd\x02\xfa\xd1\xfd\xd6\xf4;\x13\xef\x13\xaf\ +\x88_\x19_\x91 K\xc8Ox)\xa1\x22\xb1[\xa2\ +\x22qU\xe2\xf7I\xdd\x93TIk\x93\xce%\xf7L\ +\x1e\x97\xfcj\xf2\xa5\x94\x90\x14C\xca\xd6\x94k\xf28\ +\xf9#\xf2\x0fR\x85\xa9\x03Rg\xa5\x1eLsK\x1b\ +\x96\xb64\xed\xdb\xfe>\xfd\x8b\xfao\xec_9 r\ +\xc0\xc3\x03>H\x17\xa7\x0fJ\x9f\x9f~\x22\xa3s\x86\ +2c}F\xe5\xc0\xe8\x81\x8f\x0c\xdc;H:(w\ +\xd0\xd2A\xdf\x0f\xee5\xd80xg\xa6 s`\xe6\ +\xb3\x99_\x0f\xf1\x1b\xa2\x1d\xb2=\x8bded=\x9b\ +\xf5Mv@\xf6\x84\xecw\x86J\x86f\x0f]6\xf4\ +\x87\x9c\xd0\x9c\xc99\xfbs\xdds\xc7\xe6n\xc8\xbd\x9a\ +\x97\x9277\xef\xd4\xb0\x1e\xc3\xcc\xc3\xf6\x0cw\x1e>\ +f\xf8\xfa\xe1\xd7F\xa4\x8eX0\xa2bd\xf0\xc8G\ +F~6\xaa\xe3(\xf5\xa8\x1d\xa3mF\x0f\x1f\xbdv\ +\xf4\x95\x07\xd2\x1ex\xee\x81\xb3c\x22\xc7\xcc\x18s\xfc\ +\xc1\x80\x07\xcb\x1f\xfc\xe4\xa1\x8e\x0fi\x1ezw\xac\xf3\ +X\xc5\xd87\xf3\xc5\xf9#\xf27\xe4\xdfPd)V\ +)\xae\x14d\x14,/\xa8T\xca\x95\xcf+\x7fR%\ +\xa9\x16\xaa.\xd0\xf1\xf4\x02\xfa\x5ca|\xe1\x82\xc2\xf3\ +E\xf1E\xcf\x16](N,^T|Q-W/\ +U\xff:.}\xdc\x8aq\xd7\xc6g\x8d\x7fe|\x95\ +f\x84fs\x89mI~\xc9\xdbZ7\xedx\xed^\ +]\x17]\xb9\xee\x90\xbe\xb7~\x86\xbebB\xdc\x84\xe7\ +&T\x1a\x06\x19\xd6\x1a)\xe3\x83\xc6\x1d&\x0f\x18L\ +\x1d0\xf70?a>]\x9aP\xba\xac\xf4\x8f\xb2\xe1\ +eo\x96\xbb\x96k\xcb\x0fL\xec5\xf1\xa9\x89\xe7&\ +\xf5\x9f\xf4\xf2\xc3\xa2\x87\x95\x0f\xef\x99\xdcm\xf2\xd4\xc9\ +\xa7\x1fI~d\xe5\xa3\xd4\xa3\x05\x8f\xeey\xac\xfbc\ +\xd3\x1f;;e\xc0\x94uS\xed\xa7\x8e\x9f\xfa\x9fi\ +!\xd3\x16L\xfb\xfd\xf1\x11\x8f\xef\x9c\xdey\xfa\x94\xe9\ +g\x9e\x18\xf0\xc4\xc6\x19N3\x0c3N\xcc\xec;s\ +\xc5\x93\xa2'\xd5O\x1e|*\xfc\xa9%O\xdd\x9c\xa5\ +\x9a\xf5\xe9\xec\x90\xd9\x8bf\xdf\x98\xa3\x9c\xf3\xe9\xd3\xa1\ +O/~\xba\xea\x99\xc2g\x0e\xce\x8d\x9a\xfb\xe2<\xc9\ +<\xed\xbc\xe3\xf3\x13\xe7\xaf[\xe0\xba`\xd2\x823\xcf\ +f>\xbbm\xa1l\xe1\xac\x85\xbf?7\xf6\xb9O\x16\ +E,Z\xf1\xbc\xfd\xf3\xe6\xe7+\x16\x0f^\xbcc\x89\ +\xef\x92yKn,-^zlY\xca\xb2\xcd\xcb\xbd\ +\x97?\xb5\xfc\xda\x0b\xaa\x17\x0e\xbf\x98\xf4\xe2\xa6\x15\x9d\ +W\xcc^\xf1\xd7K\xea\x97N\xae\x1c\xb0r\xdb*\xff\ +U\x8bVKV\x97\xae\xfea\xcd\xf05\xfb_\x8ey\ +y\xfd\xda\x8ekg\xaf\xfd\xfb\x15\xed+\x15\xebr\xd6\ +\xed]\x1f\xbd~\xfd\x06\xef\x0ds7\x0a6\x9a7^\ +xu\xcc\xab_\xbc\x96\xfa\xda\x8eM\x81\x9bVn\xf6\ +\xda<\xfbu\xf2\xba\xf9\xf5\x1f\xdf\xc8\x7f\xe3\xf8\x96A\ +[\xf6\xbc\x19\xf3\xe6\xa6\xb7\xfc\xdeZ\xbe\xd5}\xeb\xac\ +m\xd4\xb6\x89\xdb*\xb7\x17o\xaf\xd81j\xc7\xa1\xb7\ +\x07\xbe\xbdgg\xdf\x9d[\xdf\x09z\xe7\x95]\xddv\ +-{\xd7\xf3\xdd\xb9\xef\xd9\xbf7\xfd\xbd\xaa\xf7'\xbd\ +\x7f\xe5\x03\xfd\x07\x17w\x17\xed>\xb3g\xec\x9eS\x1f\ +\x8e\xfc\xf0\xe8\xde\xa1{\x0f\xee\x1b\xb4\xef\xe3\x8f\xfa\x7f\ +\xf4\xe1\xfe\xe4\xfd\xef\x7f\x1c\xff\xf1\xaeO\xe2>y\xfb\ +\xd3\x98O\xb7\x7f\x16\xf5\xd9\xb6\x03\x91\x07\xb6\xfe'\xf2\ +?[\x0fF\x1d\xdc\xf6y\xf4\xe7;\xbe\x88\xfdb\xe7\ +\xa1~\x87\xde;\x9cxx\xf7\x91\xd4#\x1f\x1d\xcd8\ +\xfa\xd9\xb1!\xc7\x0e\x1d\x1fv\xfc\xe4\x891'*N\ +\xaaN\x9e\xffR\xf3\xe5\xaf_\x95~u\xfd\xd4\x94\xaf\ +\xc5_\xcf\xfa\xc6\xe5\x9bE\xdfz\x7f\xbb\xea\xbb\x9e\xdf\ +m\xae\x88\xaax\xf7t\xea\xe9\x03\xdf\xe7~\x7f\xea\x8c\ +\xf2\xccO\xff5\xfe\xf7\xc6\xd9\xe9?8\xfe\xb0\xe8\x5c\ +\xd7s\xeb\xcf\x87\x9d\xdfu\xa1\xff\x85/~|\xe0\xc7\ +\xb3?\xe9\x7f\xba~q\xc6\xcf\xae?/\xbf\xd4\xe3\xd2\ +[\xbf$\xfdr\xa0rd\xe5\xd9_\x0d\xbfV\xfd6\ +\xe7r\x87\xcb\xaf\xfc\x1e\xf1\xfb\x9e+\xd9W\xbe\xbdZ\ +r\xf5\xfa\xb5Y\x7ft\xf8c\xdd\x9f1\x7f\xee\xffk\ +\xc4_\xe7\xae\x97\xdd\xb0\xb9\xb1\xf8\xef\x9e\x7f\xef\xbc9\ +\xe8\xe6\xd7U%5+\x93<<<<<<<<\ +<<<<<<<<<<<<<<<<\ +<<<<<\xffbl\x81\x91@[\xeb\xf1oe\ +\x05\x80{e^\x04\xf0Z\xb4\xb5>\xff&\xb0\xde\xb3\ +\xf7+}\x06t\x01ZBVG`\x180\x13\xd8\x01\ +\x1c\x03~\x00\xaeU\x83\xee\xe3\xc0\xdb\xc0\x93\xc0p\x00\ +\xd3\xb4\x84.\xf7\x0bX\xdf\xb1\xde\xb3\xaf\xc1\x8f@\x0a\ +\xc0E\xfe\x9d\x802`?p\x13hx\x97Z\xe3`\ +\x9aO\x80r\xa03\xc0\x85N\xf7#%\xc0_\x80\xb5\ +\xdc7\x00\x1dp\xaf\xf9\xc5\x00\xeb\x80\xeb\xc0\xdd\xda\xbc\ +10\xaf\x0d@\x1c\xc0e\xd9\xef\x17\xfa\x02\xe7\x00v\ +\x99\xd7\x02\xf6@S\xf3\xf0\x06\xd0F\x5c\xd9\xbc1^\ +\x05Z\xaa\x9dlK:\x00\x1f\x01\xec\xb2\x1e\x02|\x80\ +\xdb\xa5\x93\x00\xd8F\xfc\x0e\xb4\xb4\xed\xad\x5c\x01&\x01\ +6@k\xd9\xa75@[.\x01\xd8e\xbd\x04\x0c\x00\ +\x1a\x8a\xdf\x1f\xf8\x12h-\xbb\xd7\xe7\x14\x90\x0e\xb4\xb6\ +\x9dZ\x9a\x02\xe0\x0f\xc0Z\xce\xbf\x01\xecK\xad{\x1e\ +\x05\xc0\x13@[\xd9\xbd>8fB\x9d\xda\xdan\x5c\ +\x12\x09|\x0f\xb0\xcb\xf9\x1a\x80\xed\xfcV\xa0\xadl\xdd\ +\x18\xdb\x01\x17\xa0\xad\xed\xc6%\x9e\xc0\xfb\x00\xbb\x9c\x7f\ +\x02md\xe2;\x82\xedQo\xa0\xad\xed\xc6%\x22`\ +>\xd0\xd6\xb6m*\x97\x814\xa0\xad\xed\xc6%\xa9\xc0\ +\xfd\x5c\xef\xeb\x83s\xea\xff\x97k\x80c\x1c,\x0f\x17\ +v9\x0b\xe0\xbd\x84c\x96^\x80#\x80\xdfB\xf0\x07\ +\xce\x00\x5c\xc8\xb0\x82:\xa3\xeemm\xbf\xe6\x80\xe3N\ +.l_\x01\xe4\x02\xb7\x1b\xa3`\xdb\xcd\x85\xdd\xd9\xa0\ +\xee\x8d\x8d\x9d\xefwp\xfc\x83\xf3\x9c\xe6\xda`\x0e`\ +\x07\xdcI^K\xd8\x1f\xc12D\x00\xada3\xae\xc0\ +\xf9}\xfd\xf5\x88\xbb\x05\xe7\x0c\xe3\x80\xa6\xcal)\xfb\ +#X\x96\x7f\xca\x1a\x1e~\xc7\xe6\x08\xd0\xdc2\xdf\xed\ +\x1a^K\xda\x1f\xc1\xb5\x14\xecoZ\xcan\x5c\x80\xf3\ +\xdb\xb7\x00.\xca\x8bm~(\xd0T\xd9-m\x7f\xe4\ +\x0d\x80\xf5\xdd\xa2\xfb\x8e\xc9@s\xcb\xc8^\xeb\xbf\x0a\ +<\x084Evk\xd8\x1f\xc1u\x94\x96\xb6\xe3\xbd\xd0\ +\x0f\xc0\xb5\xff\xe6\x96\x0f\xc7\x97\x95\x00\xdbo\x1e\x80\xf3\ +\xb8\xdb\xc9o-\xfb\xe3\xb3\x84h\xa0\xb5\xec\xda\x14\xb0\ +\xcd\xc7\xf6\xa2\xb9e\xc3<\xc4\x80\x1fp\x14`\x87\xed\ +\x01\xda\x01\x8d\xe9\xd0Z\xf6G\xbe\x02\xee\xe6\xd9FK\ +\xf34\xc0E\xb9\x9e\x02\xacy\xe2\xbc\xaa\xfe\xf3\x98\xff\ +\x02\x8d\xd5\xbd\xd6\xb4?2\x15h=\x0b7\x0e\xae\x19\ +\xe2\x9a\x09\x17eJ\x02\xea\xe7\x8f\xcff\xd8\xed\x1a\xae\ +c\xa8T*U\xfdx\xadm\x7f|\xae\x81u\xa4U\ +\x8c|\x1bJ\x01.\xca\x83}-\xb6=\x0d\xc9\xc0\xf5\ +\xa3\x8b\x00;\xfeR\x00\x9f\xf7X\xe3\xb4\xb6\xfd\x91\x09\ +@\xebY\xbaa\xb8Z\xcb\xff\x1a\xb8\x9d\x1c\xfc`\xf9\ +A\x80\x9d\xe6c\x00\xdf\x93\xc0p|\xce\x19\xd0D\xb8\ +z\xe6\xb6\x05h\x1d+7\x0eWe\xd9\x0b\xdcI\x16\ +\xaeC\xac\x06\xd8\xe9\xce\x03\x09\xc0\xdd\xe8\xfc\x1e\xc0\x85\ +\xce'\x80{\xb7\x1c7`\xbb\xc1EY\xde\x04\x9a*\ +S\x0f\xb0\xdfOA\xb7F\xa3\xd145\xfdf\x80\x0b\ +\x9d\xb1\xec\xf7f5\xee\xf8\x0e\xe0\xa2,\xf8\xee\xc4\xdd\ +\xc8\xc5:\x8fu\x9f\x9d\xc7*\xa0)ku8\x96\xe5\ +B\xe7o\x81{\xb7\x1c7\xbc\x03pQ\x96o\x80\xbb\ +\x95\x8dm?\xf6\x01\xec|\xb0\x8f\xc0\xbe\xe2v\xe9\xb8\ +j3\xf1}\xc7{6\x1cG\xe0\xbb\x03\x5c\x94\x05\xd7\ +\xda\xd9\xe3\x99\xa6\x82\xef\xf0,\x07\xd8y\xe1X\x09\xc7\ +L\x0d\xc5\xc71\x16\x17\xeb\xe2\xc8t\xa0\xf9\x16l\x1e\ +]\x01.\xd6\x1d\x90\xe6\xbc;Z\x08\xb0\x9fq\xa2N\ +f\xa0~\xbcD\x80\x0b]Q\xd6\xfd\xf2\x8e\xefz\x80\ +\x8b2\xcd\x06\x9a\xa3\x07\xbe;\x8asdv\x9e8\x87\ +v\x00\xacqp\x8e\xcd\x85\xae\xf8\xeeq\xf3-\xc7\x0d\ +8\xf6\xfe\x0dhn\x99\xf0}\xa1{i\x83\xd8\xe0\x1a\ +Q\xfd\xfe\x15\xd7\x92|\x01\xcc\xfb4\xd0\x5c=\x7f\x01\ +\xac\xf3\x8e\xfb\x85\x87\x80\xe6\x96\x0b)\x06\x9a\xab\x0b\xb6\ +\xf1\x0b\x81\xfa6{\x16\xe0B\xc7\x11\x00\x176\xe3\x1a\ +\xeb~\x98\xe6\x80\xf5\x93\xdd^4\x07\xac\x13\xec\xe7\xff\ +\xf7\xb2\x8f\xa0>\xb8\xee\xc1\x85n-\x01\x8e\xbd\xeb\xaf\ +\x11\xdc\x0bk\x00\xaet\x0a\x03\xb8X\x1bG\x0e\x00\xf7\ +\xfb;\xd3\xf8\x9c\x1a\xdf\xd3inY\xef\xe6\xd9\xfb\x9d\ +\xe0b\x8d\x10\xfb\xf5\xfb\xad\xcdo\x8c\x10\xa0\xb9k\xd2\ +\xd8V\x18\x80\xe6\xea\x82c\xd0\xe6\xb6;8\xb6\x08\x06\ +\xb8\xb0Mk\x91\x01p1/x\x0e\xb8\x97\xf7\x0e\xf0\ +\x99\xdcb\xa0\xb9\xf2\xb1\x0c\xff\xd4=\x02c\x00.\xae\ +\x01\xeeo\x1c\x0b4\xf6|\x80\x0d\x8e1\xf3\x81\xfak\ +C\xf7j{,Ck\xd8\xaa\xa5\x18\x05p5?\xc6\ +gN8\xef\xc1\xfd\xa58\xd7\xf2\xae\x06\xdd\xe8\x87\xe3\ +/\x1cgr!\x0buF\xdd\xdb\xda~\x5c\x90\x07p\ +\xb9\x9f\xb1\xa5A\xdb\xe3~\xe3\xb6\xb6\x1b\x97d\x03\x5c\ +\xbd\x07\xdd\x1a<\x03\xdc\xe9\x9d\x97\x7f\x1a\xe1\x00\x17\xf3\ +\xff\x96\x82\xbd\x8f\x19\xc1\xbd;\xb8\x87\xa7\xad\xed\xc6%\ +\x1e\xc0\xbb@[\xd9\xb81\xd0\xd6\xf8\xdc`\x13\xc0\xf6\ +\xc75)|\x97\xbb\xad\xed\xc6%B\x00\xd7:\xdb\xca\ +\xd6\xf5a\xb75\xf8~\xe7\xc3\x00\xbe\x7fm\x0d\xc7\xbd\ +\x9c\xb8\xa7\xb3\xad\xed\xc658Gh\x8b\xf7F\xac\xe0\ +;\x17\x83\x80\x86t\xc31?\x8e\xb7\xd8\xf1\x17\x01M\ +\x19\x03\xff\x93h\x8b\xfd\xef(\xab)\xfb\xdfq]\x1d\ +\xdf;g\xa7\xdd\x07\xe0^\xff\xd6\xb2Ok\x81k+\ +\xf8\xcd\x88\x96\xb6=~\xdb\x03\xe7\x0cM\xd5\x0b\xdf\xf3\ +\xac\xaf\x17\xce\x07\xf1\x9b\x17-i\x8f\xb6\x02\xc7H+\ +\x01\xf6\xfe\xf9\xe6\x82\xcf\x0a\xf1\xdd\x88\xe6\xec#\xc2\xb5\ +(\xf6<\x12\xc7J\x5c\xae\x11\xdeo\xe0\xb8\x0f\xbfm\ +\x83c\xa5{\x99\xbba\x1a|\xcfJ\x0bx\x01\x5c\xe8\ +\x94\x0c\xe07\x8e\xd8rp\xce\xfd\xff\xfe\xed/W\x00\ +\xfbjl\xb3\xf1y.~\x93\xe9$\x80k<\x17\x00\ +t\xe3{(\x1b\x01\x8c3\x10\xc04-\xa1\x0b\xae\xaf\ +\xe3\xb7\xbe\xd8\xd7\x80\xff\x06^\xeb\x82\xf5\x9d\xfd\xed\xbb\ +\xb6\xd6\xe7\xdf\x0a\xd6\xfb\xff\xf7\xb6\x87\x87\x87\x87\x87\x87\ +\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\ +\x87\x87\x87\x87\x87\x87\x87\x87\xe7\x1f\x81\xf0%\x8a\x08\xe1\ +\x97\x82?\xf2\x92\x80\x88\x187!\xf9/\x09k\xdd\x96\ +\xa8\xff\x03;\xbc\xca\x99\ +\x00\x00\x087\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / slice \ +/ not active - s\ +aved\x0d\x0a \ + Created \ +with Sketch.\x0d\x0a <\ +/defs>\x0d\x0a \ +\x0d\x0a \x0d\x0a <\ +path d=\x22M3.11645\ +814,1.98491547 C\ +1.81085731,3.256\ +48192 1,5.033548\ +03 1,7 C1,10.691\ +145 3.85693233,1\ +3.7150177 7.4801\ +6927,13.9809903 \ +C7.55923919,13.9\ +867947 7.5433296\ +9,11.4601372 7.4\ +3244079,6.401017\ +95 C4.76920501,3\ +.70741298 3.3305\ +4412,2.23537882 \ +3.11645814,1.984\ +91547 Z\x22 id=\x22Com\ +bined-Shape\x22>\x0d\x0a \ + \x0d\x0a <\ +path d=\x22M14.9826\ +165,6.50282053 C\ +14.7276121,2.868\ +84654 11.6988335\ +,0 8,0 C6.821531\ +33,0 5.71107957,\ +0.29121506 4.736\ +68211,0.80560781\ + C5.52211072,1.5\ +7704166 8.929157\ +37,4.97484587 10\ +.4514738,6.50452\ +22 C10.82345,6.5\ +0282053 14.72604\ +01,6.5045222 14.\ +9826165,6.502820\ +53 Z\x22 id=\x22Combin\ +ed-Shape\x22>\x0d\x0a <\ +path d=\x22M12.3449\ +55,7.6097276 C12\ +.344955,7.609727\ +6 13.3370708,7.7\ +3374209 14.60803\ +62,8.42756253 C1\ +4.2393452,8.7623\ +7994 12.344955,1\ +0.6534218 12.344\ +955,10.6534218 L\ +12.344955,7.6097\ +276 Z\x22 id=\x22Trian\ +gle-2\x22 transform\ +=\x22translate(13.4\ +76496, 9.131575)\ + rotate(90.00000\ +0) translate(-13\ +.476496, -9.1315\ +75) \x22>\x0d\x0a \ + \x0d\x0a \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x05(\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \x0d\ +\x0a \x0d\x0a <\ +/g>\x0d\x0a \x0d\x0a<\ +/svg>\x0d\x0a\ +\x00\x00\x05\x14\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a <\ +g id=\x22lock_on_No\ +tTransparent\x22 tr\ +ansform=\x22transla\ +te(3.000000, 1.0\ +00000)\x22 fill=\x22#E\ +9E9E9\x22 fill-rule\ +=\x22nonzero\x22>\x0d\x0a \ + \x0d\x0a \x0d\ +\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x07o\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / out\ +liner / visible \ +/ mixed state - \ +hover\x0d\x0a \ + Created\ + with Sketch.\x0d\x0a \x0d\x0a\ + \ +\x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \ + \x0d\x0a \ + \ +\x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ +\x0d\x0a\x0d\x0a\ +\x00\x001/\ +\x00\ +\x01\x04 x\x9c\xed=\x07@SW\xd7\xf7%\xec\xa5\ +lpF\x147#\xcc$\xa8\x88\xb8P\xa9\x0a\x8a\xa3\ +\xfaIH\xc2\xd0\x90`\x12\xdc\xab\xcb~\xd5\xd6\x81\xd6\ +]m\xd5\xda\xaa\xd5\xd6Zg\x9d\xe0.8\xab\xb6\xda\ +\xe1\xde\x0a8\x90\x99\xfc\xe7\xbc\xe4a\x8c\x8c\xa0Q\xfb\ +\xf7\xcb\x0d\xe7\xbd;\xce=\xe7\xdcs\xef;\xf7\xdc\xfb\ +\x06\xb1\xb1\xa4-!\xc4\x86x\x115\xb1\x80\x18E\xb4\ +\x076}J\x84\x03\xa5\x17g\xd1q\xc0\xa3\xfc)\xb6\ +.\x1f\x90)+]\x9c\x05\x07g\x86\xce\x90\x06\x94\x8b\ +\x1e\x8e\xbb.\x8e\xb5=\xf4hz2\xf8s\x08\xd5\x80\ +Xj\xe3T\x18\xd5\xb02\xde\x89j\xa4G\xa7\xb5\x1e\ +/.\x1e\x89\x0f\xc4\xbaSAt\xdc\x0d\xe2\xa9T\xdc\ +3|\xd6.\xa7\xe7\x0d\xc3x\ +\x83_!\xbf'\xea\x05\xf4\xa4\xfd\xd9\x95O#\xa4U\ +}\xe6\x1c%\x96'I8\xfdR\xe5*\xb92U\x9e\ +\xc1\x89\x8e\xe6\x04\x05r\xc39\xad\x07\xa5\xc9\xc4\xf2\xb1\ +\xca6\x04\x93\x82\xc0\x10\xf8\xe3p\xb9\x82 \x9e\x80\x1b\ +F:D\x8e\xcb\x10\x8aFIT\x9c$IJ\x9a\xac\ +\xa3O\xfe\xcf{}8i\xe2\x8e>\x83Bc\x03c\ +3\xa2%\xa9i='($\xf1\x13\xde\x19 \x9a0\ +J\xc4\x17\xfbDv\xb2\xeb0N0.=#]\xa2\ +\x12r\xc6\xa5KeJ\xc1\xb8\x8e>B\xe4/\x808\ +f\x07\xf8ph\x14\xd5\xa8\x8e>Z\xc1\x06\xc7\xf6\xe3\ +D\xcb\x15\x12N\xa8\x7f\x98\x9f\x88\x1b\xcc\xe3\x84\xf3\xfd\ +\xb9\xa1|^PH{\x144, \x90\x1f\xc0\x0d\xf1\ +\x0b\xe4\x0a\x02\xf9\x82@.G\x17|:\xd9\xc1\xb1\x83\ +B\x9c,\x88\xeb\xda]\xc7\x0eR\x1d}RU\xaa\x0c\ +A@\xc0\xd8\xb1c\xfd\xc7\x06\xfb\xcb\x15)\x01\x5c>\ +\x9f\x1f\x10\x18\x14\x10\x14\xe4\x07\x18~\xca\xf12\x95p\ +\x9c\x9fL\xd9\x5cK\x84\xa1\xd3U\xa2\x14)\xd22T\ +ir\x19\x07\xd3\xc2$y\xa6\xaa\xa3\x8f\x8f\x1dG/\ +\xe8\xda\x95\x9eQ\xc9H\xa6\xf4\xa7\xdb\xe8/\x92\xa7\x07\ +\x8c\x13f\x04p\xfd\x03\x03\xaa\xaa$\x16U\xd6\xc9\xc8\ +THi\xd1\xc4\xa2\x00\x89T\x92.\x91\xa9\x94P\x8f\ +[e\xbd\x0c\xa6\xef\xaafYY\x5c-c\x9066\ +\xb6fy\xd3\xd3\xab\xac\xa9Tu\x1b\xa3\xaa\xb9\xa6r\ +\xc0\xf8\x0cI@\x9cD)\xcfT\x88$\xdd\xc6@S\ +\x9e\xe9\x15U\x0b\xdc\x05\xd1\x0a\x89P%\xe9\x0a\xd0\x09\ +G\x9b_`\x90_`\xf0\x00n\xa8 0L\x10\x14\ +\xea\x17\xc8\x13\x04\x06v\x080\xc04\xa0\x11+\x17\xa7\ +%\x8f\xd7\xa3\x01\x83\x22d\x003b\xfd\x02\xc3+i\ +\xe8a\x1a\xd2\x801(\x16\xaa\x84FQ\xd1\xc7\xad\xaa\ +=r\xc5\x00\xb9\x5c\xda\xa9\xd6\x0bL\xafa\xba*z\ +\xd4\xc4\x22A\xb2\x5c\x91.TuJK\x17\xa6H\x02\ +Ti\xc9\xc9\x1d\x02\x9e\xe5\xea\xa1Vv\xb4 Z.\ +\x95+\xa0\x95\x92N\xc1\x1d\x02\xaa\xca\xae\xb2VLt\ +t?\x85<9M*\xe9\xa4\x8c\xeb\xd1\x85\x13\xd3-\ +:\x8c\xcb\x0f\x0b\xf3\x0b\xf2\xe7\xea\x93\xd1\xc3\xab\x92N\ +W\xb9(\x13Gl\x94L$QB\x93\x94\x9d\x9e\x1b\ +9\xf4\xb5\xd4E\x98\xf2|.S M\xeb\xa4\xb5\x08\ +b\xb9(M\xfcll\x0b\x84\x22\xa1D\x12\x92\x9c\xec\ +\x07\x22\x05\xfaq\xb9\x92p\xbf$!\x1c$a\x92p\ +a\xa8H\x1c\xc4\xe3\x86t\x08\xd0\x91\xa8\x8e4(\xda\ +_\x0cd\x93\x93C$a\xc1\xbc0?Ir\xb0\xc4\ +\x8f+\x09\x91\xf8%\x85\x8bx~a\xc2\x90PIx\ +pRP\xb8H\x5c51m\xee\xf3\xe2\xeb+\xa7\xa6\ +\xc6w\xa0/4A\x8cL\xa9\x12BqLWZ\x9e\ +4\x90\x87\x1f\x1a,\x11\xf3\xf9\xc9~ S\xb2_R\ +(\xc8#\x0c\x0a\x0c\xf3\x13\x06\x07\x07qy\x81a\x10\ +\x0f\xa1\x87\xc9\xf3\xd5_ \xcdp\x87\xb2\xaa\xb5\x18\x1e\ +\x12\x1c\xce\x0b\xe2\x89Q\x8b!Z-\xf2D!I~\ +\xe2\xb0d\x89(\x5c\x98\xc4\x0b\x16\x051\x8c\xf4\x88\xbd\ +\xc0\xa8\xaf\x22\x0d\xcc\xbeP\xaa\x87S\xa9[Qp\x90\ +$\x9c\xcf\xf3K\x16's\xfdB\xf9!b?~h\ +2\xdfO\x18\x12\xc6\x0d\xe7\x86'\x85\x09\x85\xe1\x0c\x8b\ +*\xc8\xbc\xc0\xaag\x1a\xeaq|\x15\x83(^2\xba\ +\xba\x9e\xa6\x0dt\x86P\xa1\x94\xa0\xf9\xe9\xe8\xc3\xd8\x1f\ +\x9f\x17*`\x1d\xda\x8c\xc1\x08C\xd3\xdeID[\x18\ +\xe8\xfe\xe7r\xab\xaf\x96\xf6bw\x1a\xa7\x82\x17\xaaW\ +\xcfcl\xaaDV\x93e\xd4\xc3\xaa\x9e\x88R\x9e\xac\ +\x1a+TH\xa2R@\xd3\xc6\x98\xa5\xaa\xaa\xbd\xa0\xef\ +Z.\xb9W\xe8\x08\xa5p\xcc\xabuCprxX\ +\x92\x04\xac6O,\x0a\xf2\x13\xf2B\xa0\x07P\x87\xe1\ +\xa1\xdc\xe0\xe0\xf0 a\x08\x97+~\xb5n\x80>\xe0\ +\x0aB\xb8o\xbf\x1b\x9e\x91\x17\xa5\x0ae)\x12q\xa7\ +\x00\xa6\x22\x93\xf1\xff\xa9\xe7\x8c\xb3\x87/\xd7sUN\ +\xe8\xff\x82\x9e\xd3\xe6>o\x13\x19;k`C\xb5\xa8\ +z\xfe\xac\xd6Y\x0e\xd0y\xcb\xe0\xa8\x07Tz\xeaU\ +\x09l\xfa`fbfbfbfbfbfb\ +fbfbfbfbfbfbfbfb\ +fbfbfbfbfbfbfbfb\ +fbfbfbfbfbfbfbfb\ +fbfbfbfbfbfbfbfb\ +fbfbfbfbfbfbfbfb\ +b&v\xcf\xde}\x95\xc8\xc4\x1d}\xc6\xfaDv\x22\ +\x8d\xa8\xa1\x84\xdd\xd0\xb7G\xe5\x99E\xe8Wzy]\ +bb-|\xe9\xf7o\xeb\x1f\x1e\xeey]\xb5E\xf6\ +\xe4\xe2\x1e\xfe\xfc}\xb7\xc7\x0f\xa3\xcb\x04t\xd9u,\ +g\xde`&\xf6\x19\x8a4\x99\xaao\xa6*#S\x05\ +I|\x95\x98\xf4S\xaa\xe2\x93\xe4r)\x8d\x11#S\ +I$\xb2\xcct&\x8e\xe7h\xa9\x02\xd3\xf5\xe8\xba\xf1\ +i\xe3\x10\xa3K\x9a\x0a\xeb<\xa3)Q\xbc#L\x97\ +\x0c\xe86x@%3m\x85~\x0a\xb9<9^\xa2\ +\xca\xcc\xe8\x9b4R\x04\xd9\x0e\xa4\x1fQ\x109\xfc\x92\ +\x09\x87\xc4\x13\x09Q\x91L\x92AW\xb1\xcb\xa8\xc4f\ +\xc8t\x91\xaad:\x89\x1c\x922\xd3\xa4\xaa4\x19M\ +\x12\xd2\xb64vt\xec\x90\xdeZmD >\xcb\xef\ +\xb9\x16\xbb\xe8\xb5\xb8/\xfdF\x82R\xfb\xae3\xb4+\ +C%c\x1a\x01\x8dLRT&\xe2R\x94\xb1\xcfJ\ +\x14\xb2\xe8g\x09\x99\xeaY\xa2O\x92TY\x99x'\ +E5\xa62\xd1-]\xda\xb52\x01z|F\xba\x8b\ +hT\x8aN\x11Z\x01I\x5c\x8f.\xd1D\xfbj9\ +\x89\x13s8byfRg\xf9%\xc2\x84\x1e\x0a\xd9\ +\x0by]\xa4/\xe2uQ\x88\x07\x0c\x94\xa9\xba7\x8f\ +\x93\xaa\x88^\xe8\x22\x15s\xaa\xca\x8fSJUt~\ +\xbfq\xd2\xceq\x95\xd9vc$\x22\x95\x5c\xd1U\xa8\ +\x12V\x8e\x8a~)\xfd\x94\xcc\xa8\xc0\xb8\xee\x1cM+\ +A\x92\xac\xaa\x8a\xfc\x00yF\x95l\xe3ERm~\ +?\x85\xa8\xf3\x90\xca\xec\xfa\x22\x85?\xf0\xeb\xb5\xc7_o\ +9x\xf6\xfa\x93\x84\xe41\xd3\x17\xac\xd9z\xe8\xdc\x8d\ +\xa2\xf0\x1e\x83R\xc6~\xbc\xf0\x9bm\x87\xcf\xdf|\xea\ +LX,\x90\xd6\x82\x96\xc9\xda\xca2\x94\x16\xa1\x19\xd7\ +\xc5\x02$\x18\xed\xe3j\x19\xf4\xde\x5c7\x94`\x7f\xdc\ +\xa9\x82\xe0\x16I\x97\x15\xefgE\xc7\xbb\x8b\x94!\x85\ +\xbeV(\x80u\xcb\xd0\xec\xd3 \xc4*Oq\xb7\x81\ +a*\xc9\x95J\x11\xaa\x97\xa0\xd53\x114\x17\x88\x03\ +\x9b\xe6\xe9L\x22\xc9\x93\x86\xf9\x12\xf7\x85{g\xe5\x1c\ +X\xd7j}\xf1\xc0b\x0d\xd9\xd4w}\xd3S\xe7:\ +\x9c\xb86\xe4\xec\xb9\xbbM\xbc\xf6\xfbw\xb0\x98\xffW\ +\xc5\xc6\xa9\xa7\xa6D\xde\xeb]\xb4CC\xba!\x12\xbb\ +h\xcc\x88\x07S\x9a\x05\xfc\xfe\xfb\xd4\x1d\xc5\x1f\xce_\ +QY\x10p\xe1f\x85\xe7U\xf5\x81z\xda\x92\xbd)\ +\x12\xf5\xcd+.qSu\xd9{G\xadx\ +\x9a\xea\xb2.qj\xef\xef\xafLm\x16\xf3\xce\x02Y\ +|\xea\xc6\x90\x80\xa9\xa7\xd2\xca7\x0e>6\xf6\xc2\xdd\ +\x0d\x99\xb3\x1e\xa8\x9a\xb0\x7f\xe8\x17\xd3k\xe8\xe0P\xe1\ +\xe0\xfd\x8e\x7f&\x96F]P7\xed1\xf2\xfb\xcf>\ +*\xfatl\xc75M\x1a\x93&\x0e\x166[gF\ +l\xba\x1f\x13\x13\xbc\xe8\x9c\xa4\xe3w\xd4\x06g2z\ +_\xe7\xeeO\xaf\xff\x11\xe37\xea\xcfF\xb7\x1c\x1c\xd7\ +\xe6g\xef\x9cn\xbd\x87\xb3\xe8\xe4\xea\xd5~\xcaE\x97\ +n,\xca\xf8\xb5\xfe\xd6\xc0\x87\x87\xa2CO\x1f\xaah\ +\xa6!S\xec\xbd\xdf\x99\xf9E\xec\x86\x1f\xc6\x08f;\ +\xff\xd1bM\xad\x8d9]sc\xfa(R3=\xf9\ +\x1f\xcf\x1a:\xa9\x8b\xcb\x1f\xc7\x0e\xc9\xbaM|gV\ +\x83-\x7fY&\xfd\xfe\xee\x9d\xf1\xa2\x03\xbc\x16\xdfD\ +\xaf\xbe\x10\xb6g\xe3\xa5\x07M\xe7\xac\x1f\xc2\xdbr-\ +\xac\xf1\xaa\x8fOe\x5ck?\xf4\xdedy=\xe1\xc7\ +Nv\xa7\xd7\xaf\x8b\xbb\xd4\xb1\xdf\xe0\x07\xd7F\xba\xc4\ +\xaf\x98\xd2q{\xfe\x85\xc2oR\x9c\xec>_\x9c\xf0\ +{\xe7\xaf\xd5\x97\xce4\xfb\xb3\xf8#e\xaboE\x9d\ +~\xdc\x97#.\xbe\xd5)r\xd7\xe6\xb8\xb8\xb9\xa3\xbf\ +\xcd|\x1a\xfa\xc1\xaca\x93X.\xbf\xef\xdb\xb8\xa0\x9f\ +cW\xde\xcf\xb7#N\xec\xf7\xe5\x8f\x1fqZZc\ +\x93\xce\xd4\xd2?\x17<\xba\x87\x0eq\xbcP\xf8\xed\xe5\ +\x9c93\xb2z\x8d\x96\xef\xdb3\x8f\xbb{\xc5\xae\xbf\ +\xaf\xb8g\x9ej-X\xb2\x5cro\xd4\xecO\xc8\xea\ +o\xa8\xc1\xab\xac\xca\xfe\xcb\xdd\xb4bW\xfe%\x8f\x9f\ +\xd3\xd6N\x1f|\xe8\xa7\xe2i\xac%\xbd\x14\x0f'\x08\ +E\xe1q\xbd/M\x5c2\xbex\xcb\xc0\x95e\xa9S\ +nx\x15\xa6]*Ox\xb8\xf6\xf3i\xbf\x1c/\x9a\ +\xd8\xf4\x8f\xa26m\x8emQ\x7f=kR\xf7\xab\x81\ +?\xed\xd4\x0ak/\xb8a5\xb4\xc8\xa9A\xe2\xa4\x05\ +\xb3{\xa9\x85\x7f\x9fSS\xbe\x91\x9a_\x1f\xcb6\x94\ +\xff9\xd5e\xf3\xe0\xa7\x09\x97\x8a\xbc2.m\xb8\xb8\ +V\x92\xdb~\xaa\xdd\x8a{K4\xe4t\xd3\x1d\xbe\xa1\ +\x9d\xfe^\xa8\x9eS\x91S\x96U6eDi\xf4\xd3\ +z\x0f\x124D\xbc\xb9\xa2YYVE\xd6\x89\xc7\x1f\ +?\x88\x89\xed\xbb\xed\xba\x86\xac\x8e\xccP_\xdb\xa3\xc3\ +\xfd|\xef\xef\xed5\xa4\xe7\x94k{\xb5\x98\xf1\xf1\xf7\ +K\xa6\xde\xae\xf7G\xe4\x8e\x00\x06\xf7\xba\xbc\xa8w$\ +0\x08\xd0a\xce\xff\x95\xa1\xdf\x9b\xc1}W\xcb\xa0\xb7\ +\x16\xd1\x97\x7f\x5c\xb9\xfc\xcaM\xefVq\x09\xdf\xf4j\ +\xfa\xe5\xd8\xa5{\xa7\xf5o\xdcr\xc7\xd9k\x1f)v\ +\xfe\x1e\xcf\xf6j\xf6\x15\xb4\xe7w\xed\xac\xd7\x8c\xb6\xeb\ +\xc3\x09\xed\x9f\xc2\ +i29\x0b\x5c\xcatX\x8f\xe2Wx\x06\x0f\x19\xca\ +\xb1>\x0e.\xa5-x\xb2\xe0<\x09E\xca\x8c\xd8\xf8\ +\xee\x03hW\xab[4\x07?\xd5C\x9e\x0bE\xe7\xb4\ +\xce\xca\x19\xbf\x9e\xfd8\x1cR\xb7\xe0,\xcaP\x80+\ +B\xf5\x83x\xb0X\xa2\x04\xe7\x8d\xfa\x10\xe2\xd2\xb1\xaa\ +\x0c\xccGO\xc0-i\x14\xc6Y\xe8\x03\xb8)@@\ +\x88{a\x83\xa4\xa7\ +\xa8\x1a\x0b\x8d\x0c\xd8\xb7\xda\xd8\xa3\xfet\x9fQ\x1e\xb9\ +\xcf\xf2\xaa\xc2\x93\xaf\x04/\x0c\xd6\x84\xec9\xcf\xf2\x92\ +\x16\x13\xb2\xed#B\xbc.<\xcbk\xf1%\x8cQ\xe8\ +\xb7\xad'\xf4\xda\xe3\x81\xe3E\xef\xf3gi\x12\x91?\ +*\xb42\xd4\x8a`D\xd0\xe3\xe7\x8f\xe4*\xd5\xc3\xe9\ +\xaa\xf5s9\xa87\x11x\xb3\x99\x0a\x0e\xac\xc7E\x12\ +\x8e\x9f\xe1 ~\xe9\x8aU\xcb\xd1>N\x92,\xc1u\ +\xbf\x84\x93\x00\xa3,M\x96\x02\xdd-\x13\xa7\xd1_r\ +K\x93U\xd7\x89/Y\xcd h\xc75\x04\xd7\xd5j\ +\xe26\xc2\x9f\xd4?\xe1F\xd8\xf7s\x89\x85\xab=a\ +\x0f[\x0e%Te\xbf\xf5\xb1M x\xe5\x0djz\ +S;\xee\xe9P\xc5\xa2\x935\x1b\x0f\xca4z\xa9E\ +\xa2\xe3\x06pD\x99\x8a1\xda2zueI\xec\xc0\ +H\xb9\x11o\xd2\x844'\xad\x89\x1f\x18\x9ap\x12A\ +:\x93n\xa4\x17\xe9K\x06\x90!\xe4?D\x04\xc6(\ +\x9d(\xc8X2\x89\xbcG\xa6\x93\x99d\x0e\xf9\x9c,\ +!+\xc8j\xb2\x96l$?\x92md\x17\xd9O\x0e\ +\x91_\xc8Ir\x96\x5c$\x97\xc8ur\x97\x14\x90\x22\ +R\x06\xee\xae5\xe5H\xb9R\xdeTS\xca\x97jG\ +\x05Q<\xaa\x13\xd5\x8d\xeaC\xc5QC\xa8D*\x85\ +\x92Q\x99\xd4$\xea\x03j&\x95E-\xa1\xbe\xa2\xd6\ +R\x9b\xa8\x1d\xd4~\xea(u\x8a\xfa\x9d\xbaL\xdd\xa6\ +\x1eR\xa5,6\xcb\x81\xe5\xc6j\xccj\xc9\x0a`\xf1\ +XQ\xac\xde\xac\x01\xac\xe1\xac\x14\xd6h\xd6\x04\xd6\x87\ +\xacY\xacE\xac\x95\xac\xefX[Y\xfbY\xbf\xb0\xce\ +\xb2.\xb1\xee\xb2\x9e\xb0\x09\xdb\x9e\xed\xc1n\xc6\xf6c\ +\xf3\xd8\xd1\xec\xbe\xec\xa1\xecd\xb6\x82=\x85=\x83\xbd\ +\x80\xbd\x92\xbd\x91\xbd\x93}\x98}\x86}\x89}\x8f]\ +bae\xe1j\xc1\xb1\xf0\xb3\x88\xb0\xe8i1\xd0B\ +d1\xdab\x8a\xc5\xa7\x16K,\xbe\xb5\xd8jq\xc0\ +\xe2\x8c\xc5e\x8b\x02\x0b\xb5\xa5\xa3e#\xcbv\x96\x02\ +\xcb\x18\xcb\xc1\x96)\x96c-\xa7[.\xb0\x5cc\xb9\ +\xc5\xf2\xa0\xe5Y\xcb\xeb\x96EVVV\x1eV\xad\xac\ +\xc2\xadzZ\x0d\xb1\x1ai5\xd1\xeaS\xab/\xac\xbe\ +\xb7\xdagu\xca\xea\xaa\xd5\x13kkko\xebv\xd6\ +\x1d\xad\xfbZ\x0b\xadU\xd6\xd3\xad\x17[\x7fg\xbd\xd7\ +\xfa\xb4\xf5u\xebb\x1b{\x9b\xa66A6\xddm\x86\ +\xda\xc8l\xde\xb7Y`\xb3\xcef\x8f\xcdi\x9b\x9b6\ +e\xb6\xf5m}m\x05\xb6}m\xc5\xb6\xe3mg\xdb\ +\xae\xb6\xddi{\xc2\xf6\xbam\x99\x9d\xb3]+\xbb\x8e\ +v\x03\xecF\xda\xbdg\xb7\xc8n\xa3\xddA\xbb?\xec\ +\x1e\xd9\xdb\xdb\xfb\xd8\xf3\xed\xfb\xdb\xa7\xd9O\xb3_d\ +\xff\x83\xfd\x11\xfb\xcb\xf6%\x0e.\x0em\x1d\xa2\x1d\x86\ +9d:\xccr\xf8\xc6a\x9f\xc3\xef\x0e\x8f\x1c\x1d\x1d\ +[:vv\x1c\xea\xa8r\x9c\xe5\xb8\xd61\xc7\xf1/\ +\xc7b'W'\x7f\xa7\x18'\xb1\xd3T\xa7\xa5N[\ +\x9dN;=\xa8g[\xcf\xb7^T\xbd\xff\xd4\x9bP\ +oA\xbd\xcd\xf5N\xd4\xbbW\xdf\xb6~\xcb\xfa\xd1\xf5\ +\x85\xf5\xa7\xd4_Z\x7fG\xfd\xf3\xf5\x9f8\xbb:s\ +\x9d\xfb:\xa7;\x7f\xea\xbc\xce\xf9\xa8\xf3-\x17k\x97\ +\x96.\xdd\x5c\xc4.\x1f\xba\xacr\xc9q\xb9\xea\xcav\ +m\xee\x1a\xed*r\xfd\xc0u\xb5\xebA\xd7\xebnV\ +n\xad\xdcb\xdcF\xba\xcdt\xdb\xe0v\xdc\xad\xc0\xdd\ +\xc5=\xc4=\xc1}\x9c\xfbR\xf7\xdd\xee\x97<\xd8\x1e\ +-=b<\xa4\x1e\xb3=~\xf48\xe7Q\xea\xd9\xd8\ +3\xcaS\xe2\xf9\x89\xe7F\xcf\xd3\x9eO\xbd\x1azu\ +\xf6\x92x\xcd\xf0\xfa\xde\xeb\xacW\xa97\xc7\xbb\x9b\xf7\ +(\xef\xb9\xde\xdb\xbc\xffl`\xd1\xa0m\x83\xfe\x0d\xc6\ +6X\xde\xe0`\x83{\x0d\xdd\x1aF4\x145\x9c\xd1\ +\xf0\xc7\x86\x17\x1a\xb1\x1a\xb5m\x14\xd7hb\xa3U\x8d\ +r\x1b=i\xdc\xa4q\x8f\xc6\x19\x8d\x177\xcei|\ +\xaf\x89G\x93\xceMF6\x99\xdfdO\x93\xdbM]\ +\x9bvj\x9a\xd6t~\xd3\xbdM\xefp\xdc9Q\x1c\ +)g\x11\xe7\x00\xa7\xa0Y\xa3f=\x9be6\xfb\xaa\ +\xd9\xf1fe>\xad|\x06\xfa\xbc\xef\xf3\xbd\xcf\x9f\xcd\ +\xed\x9a\xf3\x9a'7\x9f\xdf<\xbbyA\x8b\xa6-b\ +[Lj\xb1\xbe\xc5\x05_[_\x9eo\xaa\xefB\xdf\ +\xc3\xbeO[\xb6j9\xa8\xe5\xc7-\xb7\xb5\xbc\xd5\xca\ +\xabUL\xab\x09\xad\xd6\xb7\xfa\xa3\xb5c\xeb\xc8\xd6\xa3\ +[\xafl\xfdk\x1b\xab6\xbc6\xa3\xda|\xd1\xe6d\ +[V\xdb\xd0\xb6\xa9m\x97\xb6=\xd1\x8e\xd5.\xac]\ +Z\xbb/\xda\x9djo\xd9\x9e\xdf^\xd6~e\xfb\xf3\ +~\x0e~Q~c\xfc\xd6\xfb]\xf6\xf7\xf0\xef\xe3\xff\ +\xbe\xff6\xff\x07\x01-\x02\x86\x06\xcc\x0d8\x1c\xa0\x0e\ +\x0c\x0d\x94\x06\xae\x0e\xbc\xc8u\xe1\xf6\xe2\xbe\xcf\xdd\xc9\ +}\x18\xd46H\x14\xb44\xe8\xd7`\xc7\xe0\xee\xc1S\ +\x83\xb7\x07\x17\x86\xb4\x0b\x91\x84,\x0f\xf9-\xd454\ +6\xf4\xe3\xd0\xec\xd0\x8a\xb0\xf00E\xd8\xc6\xb0\xdb\xe1\ +-\xc2\x13\xc3\x97\x85\x9f\xe7\xb9\xf1\xfa\xf1>\xe5\x1d\xe1\ +[\xf2\xbb\xf0\xa7\xf2w\xf1K\x04a\x02\x95\xe0GA\ +~\x84_\xc4\xa8\x88u\x11\xb7:\xb4\xea \xe9\xb0\xba\ +\xc3\xd5\x8e>\x1d\x85\x1d\xbf\xeax\xa9\x13\xa7Sb\xa7\ +/;]\x8al\x16)\x8c\x5c\x19y\xa5s\xf3\xce\xe2\ +\xcek:\xdf\x8cj\x1352\xea\xbb\xa8\x07]\x02\xbb\ +(\xbal\xe9\xf24Z\x10=9z_Wv\xd7\x1e\ +]gt=\xde\xcd\xa5\xdb\xc0nK\xba\xfd\xd5\xdd\xa7\ +{J\xf7\xf5\xdd\x0bz\x84\xf6\x98\xd8c_O\xcb\x9e\ +\xbd{\xce\xedy>\xa6q\x8c(fmLA\xaf\xf0\ +^\x93{\x1d\xe8\xed\xd0;\xbe\xf7\x92\xdeW\xfa\xb4\xed\ +\xa3\xe8\xb33\x96\x15\xdb+v^\xec\x1f\xef\xf8\xbe#\ +{g[_\xd27\xa6\xef\xbc\xbe\x7f\xf6k\xd5ot\ +\xbf\x9f\xfb[\xf5\xef\xd7\x7fi\xff\x1bq\xdc\xb8Iq\ +\x87\xe3]\xe3G\xc4\xaf\x8b/\x1a\xd0e\xc0\xec\x01\x17\ +\x07\xb6\x1e\x9890;\xa1^\xc2\xb0\x84\xb5\x09O\x07\ +u\x1d\x945\xe8\xd2\xe0\x80\xc1\x93\x07\xff2\xa4\xc1\x90\ +\xb4!\xdb\x87Z\x0fM\x18\xbaf\xe8\x93w\xbb\xbd\xfb\ +\xf9\xbb\xd7\x87\x85\x0e\x9b>\xec\xdc\xf0V\xc3\xc7\x0d?\ +\xfa\x9f\x06\xff\x91\xfeg\xf7\x88z#\x84#6'Z\ +&\x0eJ\x5c\x97X.\xec+\x5c)|\x92\x14\x93\xb4\ +,\xa9@\x14-Z(\xba+\xee,\x9e/\xbe-\xe9\ +(\xc9\x92\xdcL\xee\x98\x9c\x95|+\xa5c\xca\xbc\x94\ +\xdb\xa9\x91\xa9\x0bR\xef\xa5E\xa7-I+\x1c\xd9s\ +\xe4\x8a\x91OG\xf5\x1d\xf5\xcd(\x8dt\x90\xf4\xfbt\ +\x9b\xf4\xc4\xf4\x1d2\x17\xd9(\xd9\x01y\x13\xf98\xf9\ +\xa9\x8cv\x19\xd33.\x8d\x16\x8c\xfe|t\x81\xa2\xb7\ +b\x8d\x92R\x0eWnW\xb9\x813\x95\x9b\xd9:\xf3\ +\xa3\xcc\xcbc:\x8dY:\xa6xl\xc2\xd8\xcd\xe3\x9c\ +\xc7\xc9\xc6\xe5\x8eo;\xfe\x93\xf17't\x9f\xf0\xf5\ +D\x8b\x89\xa2\x89\xd9\x93\x9aMzo\xd2\xe5\xc9Q\x93\ +\xbf\x9aBMI\x9a\x92=\xb5\xf9\xd4\x0f\xa7^\x9f\xd6\ +c\xda\xb7\xef\xd9\xbd7\xea\xbd\xbc\xf7\x03\xdf\xcfz\xff\ +\xf1\x07\x83>\xd8\xf9a\xe3\x0f\xa7}x\xf5\xa3\x1e\x1f\ +\xad\x9f\xee4]1\xfd\xfc\xc7\x11\x1f\xaf\xf8\xaf\xc5\x7f\ +\xd3\xfe{\xfc\x93\xe0O\x16\x7f\xa2\x9e!\x9eqlf\ +\xe0\xcc\x053\xcb?\x15}z\xec3\xeeg\x8b>\xd3\ +\xccJ\x9eu|v\xd8\xec\xe5s\xac\xe6\xc8\xe6\x9c\x9b\ +\x1b9\xf7\xdb,\xe7\xac\x09YW\xe7\xc5\xce\xdb:\x9f\ +3\x7f\xc6\xfc\xc7\x9f\x8f\xf8\xfc\xe8\x82\x90\x05+\x16\xda\ +-\xcc\x5cxiQ\x9fE\xdb\x17\xb7XY\xf6\xf4\ +\x0b\xf1\x17\xa7\x97w^\xbeqE\xe3\x153W\x94~\ +\x99\xf6\xe5o_\xf5\xf8j\xeb\xca\x96+\x17\xac\xb2Z\ +5f\xd5\x8d\xd5\x09\xab\x0f\x7f\xcd\xfbz\xed\x9a\x06k\ +f\xae\xa9\xf8F\xf6\xcd\xa5o\xe3\xbe=\xb06|\xed\ +\xdau\x8d\xd6\xcd^\xcfZ\x9f\xb9\xfe\xf6w\xc3\xbe;\ +\xb9\xa1\xeb\x86\xed\x1b\xfd6~\xf5\xbd\xc7\xf73\x7f \ +?d\xfepgS\xe2\xa6s?\xf6\xfe1{3o\ +\xf3\xc6\x9f|\x7fZ\xb6\xc5u\xcb\x8c\xad\xd4\xd6\xf1[\ +\x0b\xb6\xa5n\xbb\xb4}\xc8\xf6S;z\xed\xc8\xde\x19\ +\xb1s\xcb\xcf\xfe?\x7f\xb3\xab\xd9\xae\xa5\xbb\xddw\xcf\ +\xdec\xb7\xe7\xc3=\x9a\xbd\x13\xf6>\xd9\x97\xb1\xef\xde\ +\xfe\x94\xfdW\xb3Gd_\xcc\x19\x9c\xf3\xeb\x81\xfe\x07\ +\x8e\x1f\xec}\xf0\xc8\xa1\xee\x87r\x0eG\x1d\xde{\xa4\ +\xe3\x91]G\x05Gw\x1c\xe3\x1d\xdb\xf6K\xd8/[\ +sCs\xb7\xe4\x85\xe6m9\x1ev|\xeb\x89\xf0\x13\ +\xdbO\xf2O\xee<\xd5\xe1\xd4\x9e\xd3\x91\xa7\xf7\x9f\xe9\ +z\xe6\xd0\xaf1\xbf\xfer\xf6\x9d\xb3\xa7\xce\x0d<\xf7\ +\xdb\xf9a\xe7/\xfd&\xfe\xed\xd6\xef\xd2\xdf\x0b/\x8c\ +\xb9Pvq\xda\x1f\x96\x7f\xcc\xf8\xb3\xfe\x9f\x0b\xfej\ +\xf4\xd7\xca\xbf\xdb\xfc\xfd\xfd\xa5\xb0K\xbb/w\xbd\x9c\ +{%\xfe\xca\xc5\xab\xa2\xabw\xaf)\xaf\x95_\xff\xf0\ +\x86\xe3\x8d\x057\x9b\xde\x5c{+\xe8\xd6\xae\xdb\xddo\ +\x9f\xbc\xf3\xee\x9d\xebw3\xee\x96\xdd\x9b~\xdf\xf9\xfe\ +\xb2\x07\xad\x1f\xfc\x94\xdf9?\xb7`p\xc1\xf5BE\ +\xa1\xe6\xe1\xa7\x8f\xbc\x1f}\xf38\xe4q\xf6\x93~O\ +\xfe*J/*{:\xa3\xd8\xbb\xf8\xdb\x12^\xc9\xe1\ +\xd2A\xa57\xcb\xc6\x96[\x97/\xaahS\xb1S\xdd\ +[\xfd\x87&\xbd\xf2\xfe\x849\x98\x839\x98\xc3?*\ +XXXX:9997h\xd0\xa0Y\x8b\x16-\ +\xfcZ\xb5j\xc5m\xd9\xb2e\x80\x1e\x04\x1a\x82\x01\x0e\ +\x9d\xd6\xe5\xe9\x97c<@\x0f\xf7\x85\xb2*\xea\x06\x18\ +\xe00\x10\xd0\xbau\xeb C>\x06\xf2\x050\xfc\xf4\ +\xf1\xaaJ\xd7\xc4\xcb\x10\xdf\xa0\xbdt=\xd4S\xd3\xa6\ +M[\xb9\xba\xbaz\xc1:\xc4F\xefN\x83\xd1\x01\xeb\ +\xd4\xaf_\xdf\xad{\xf7\xee\x83?\xf9\xe4\x93\xf5\xdf\x7f\ +\xff\xfd\xc5\xdd\xbbw\xdf\xdb\xb7o_\x01\xc2\xfe\xfd\xfb\ +\x0b\xf5\xc10/;;\xfbaU8\x0c^U4\xaa\ +\x02}<\xc3:\x0c=}\x9aU\xe1W\xc7\xab\xba\xba\ +\x8c\xecU\xd5\xc72\xc3|C:{\xf6\xecy\xb0u\ +\xeb\xd6kK\x97.\xcd\x16\x89D\x93|}}\xfd-\ +--\xadj\xd7\xfa\xb3\xe0\xe2\xe2\xe21j\xd4\xa8\xe9\ +@\xeb\xfe\xb1c\xc7*rss5f\xa8;\x1c>\ +|\xb8d\xf1\xe2\xc5{CBB\xa2\x8c\xed\x03\xbcf\ +\x12\x12\x12\xa4999\x8f\xdf\xb6\xfc\xff\x06\xf8\xe5\x97\ +_\xd4\x0b\x17.\xdc\xed\xe3\xe3\xd3\xd6\x18[\xd4\xb8q\ +\xe3\x16k\xd6\xac9\xf9\xb6\xe5\xfe7\xc1\xa1C\x87\x8a\ +\x13\x13\x13\xc7\xc1\xd8\xb6\xaeI\xf7,\x08\x91\x91\x91}\ +\x0f\x1e\xae\x0dj\xd2\xbf\xb5\xb5\ +\xb5\xadJ\xa5\x9ak\x0a\xfd\xff\x93\xfb\x10ec\xe0M\ +\xc9\x8d\xfao\xde\xbcy\xfb\xda\xc6\xbf1\xfa\xc7r\xf0\ +\x91N\xe0\x1ac\xd9\xb2e9x\xfe\xe2\x8b/\x0e`\ +\x9c93q\x04\x98{\xf63y\xfau\xf4\xe3\xfa\xf8\ +L\x19\x93\xaf\x0f\xfayU\xc5\x99z\xfa<\xf5i\x1b\ +\xd23\x04C\xf9\xf5e3\x94\x03a\xc5\x8a\x15\x87q\ +\xedU\x9b\xfeqmf\xcc\xf8W*\x95sj\xd3\xff\ +\x91#GJ;t\xe8\xd0\x1b\xd7\xd8\xb0V\xf3\xc4\xb3\ +!0\xf9\xb8\x96sss\xf3f\xd2\x18G`\xe2\xfa\ +\xb8\xfae\x98\x87\xc0\xd4\xd5\x8fW\xc5O\xaf\x8e\x87>\ +_\xc3:\x0c}&\xce\xd0\xd5/c\xea\x19\xcaj\x18\ +\xc7\xba8\xa6\xb1\x0f\x8c\xd1\x7fm\xe3\x9f\xd1\x7fm\xb4\ +p]\xe7\xe7\xe7\xc7\xab\x89\xd6\xffJpvvvG\ +\xdf\xd2\x18\xfb\x83{\x115\xd12\xd6\xfe\xa3\xfe\xdb\xb7\ +o\x1f\xfe\xa6\xda\xf8O\x0e\xe8\xd3\xa3\x1d\xaaMg\xa6\ +\xb4?\xb8\x9e3\x8f\x7fm0V\xff8\xfeq\x7f\xb4\ +&Z8\xff\x1ak\x7f\xfc\xfd\xfd\xf9L\xbd~\xfd\xfa\ +\x89'L\x98\xb0\xa8\xae\xfb|\xff\x86\x80\xfa\xc7\xb9\xde\ +\x18\xfb_\x9b\xfe\x8d\x1d\xff\xfa\xf6\x07\xfb\x01\xd3\x98\xbf\ +|\xf9\xf2\x838/\xbd\x99\x96\xff3\x82\x93\x93\x93\x8b\ +\xb1\xf6\xdfT\xfe?3\xff6l\xd8\xd0\x07\xe9\xea\x97\ +m\xdb\xb6\xed\xba\xfe\xb5\xf1o\x0f\x8c\xfd\xa9M\xff\xe8\ +\xa3\x9a\xd2\xff\xe4r\xb9\x1d\x7f\xfe\xf9\xe7;\xd5\xcd\x0f\ +qqq\xc9oJ\x07o3\xe8\xfc\x9fZ\xed\x8f\xb1\ +\xe3?333\xcb\x18\xfb\xbfz\xf5\xea\xbc\xda\xf0`\ +NX\x5c\xdb\x9e\xab1\x01\xef\xf1)\x14\x8aY\xe8g\ +\xef\xde\xbd\xfb.\xf6\xaf\xfe\x1e!\xc61\x0f\xef\xd1!\ +\x0e\xee\xa1`\x9dW\xe5kL@\xfd\x1b3\xfe\x8dY\ +\xff\x1ak\x7f\xa0\xbd\xe5\xb5\xf1c\x00\xe7\x04ww\xf7\ +\x06umW\x9b6m\x82\xb3\xb2\xb2\xb6\xe6\xe4\xe4<\ +2\x96\x97!\xe0=\xa4y\xf3\xe6m\x07[\xf9\xda|\ +ec\xedO]\xe6\xdf\xdah\xd5u\x7fZ7'\x08\ +\x8ci\x0f\xfaR\xb8W\xfb\xb2:\xaf\x0e\xb6n\xddz\ +5>>>\xa5v\x09\xea\x16\xea2\xfe\x8d\x9d\x7fk\ +\xa3\x85\xd7\xba\x5c.\x9f\x89\xf3\x80\xb1\xed\xafmNh\ +\xd7\xae](\xea\xc8\xd4z\xaf\xa2\x1f\xae\x99r\xed\xc2\ +\xf8\x9f\xa6\xf4\xff\x8d\xf5\x7fp\x0e\xde\xb9s\xe7\xad\xba\ +\xb4\xdfpN\xb0\xb3\xb3s@\x1b\xf1\xba\xf5n\x08\x9f\ +\x7f\xfe\xf9N\x07\x07\x07\xa7\x9a\xf4aL0v\xfc\x1b\ +\xbb\xfe\xad\xeb\xfe\x83\x87\x87G\xa3\xaf\xbe\xfa\xeah]\ +\xda\xce\xcc\x09B\xa1p<\xb3vx\x1b\x80\xd7\xafX\ +,\x9e\xf4*\xfa\xc7\xf1\x8f\xfb\xa1\xc6\xe8\xdf\x18\xfbo\ +\x8c\xfeQn\xfd\xfd\x1f\xbcn\xa6L\x99\xb2\xbc\x8em\ +\x7fkz7\x04\xf4\x99, \xbc\x8c\xfe\x8d\xf5?\x8d\ +\xf5\xff\xd1\xff|\xd9\xfd\x9f\x84\x84\x84Qu\x99\x13\xfe\ +I\xb0k\xd7\xae\xbb\xde\xde\xdeM_F\xff\xc6\xda\x1f\ +c\xf6?_u\xff\xf9e\xe6\x84\x7f\x0a\xe0\xd8\xe9\xdc\ +\xb9s\xdf\xb7\xad\x7fc\xec\x7fM{\x0c/3'\xfc\ +S\x00\xdb\x9e\x98\x988\xb6.\xfaG\xfbo\x8c\xffS\ +\x9b\xfda\xee?\x1a3\xfek\xdb\xff\xd7\xcd\x09_ \ +~^^\xde[\xd7k]\xc1\xd8>\xa8\xcb\xfe\xbf\xa9\ +\xe6\xdf\xba\xdc\xff\x9a5k\xd6&S\xe9\x04\xd7\xdd\xf8\ +l\x1e\xfa,\x9d:u\x8a\x05\x1f\x0a\xef\x07z\xf2\xf9\ +\xfc\xeeuY\x93\x1b\x0b\xe8\x9f\x19\xa3\x7fc\xf7\x9fM\ +y\xff\xcb\x18\xfd'%%M0\x85\x1e\x8e\x1e=Z\ +\x96\x91\x911\xb3&\x1f\xe5u\xcd\xfb\xd8\x86\x7f\xa2\xfe\ +k\xdbc\xee\xd3\xa7\xcf\x08S\xb4\xff\x9bo\xbe9U\ +\xaf^=\x97\x9a{\xfa\xf5\xe9\x1f\xa1w\xef\xde\xc3k\ +\xd2\xbf\xb1\xfb\xcf\xc6\xe8\xdf\x18\xff\xb36\xfd\xe3\xb5a\ +\xaag\x18\xb7o\xdf~\xc3\x98\xbd\xa3\xd7\xa9\x7flK\ +u{x\xc6\xfa\xff\xc6\xd8\x7f\x9c3\xc7\x8c\x193\xef\ +U\xec\x0f\xf8>\x0da}\xf0\xd4\x94\xedG~qq\ +q5\xee\x9d\xbd\xeeu\x07\xb6\x09\xe7\x9b\xea\xf4o\x8c\ +\xff\xf3\xba\xed?>?\x0d\xbe\xff\xedWmku\xfe\ +\xd2\xc4\x89\x13\x97Tw?\xe1M\xac\xfbv\xec\xd8q\ +\x13\xdb\xa8\xcf\xd7X\xfbo\xac\xff\x89\xf7.\x8c\x19\x8f\ +U\xd9\x9f\x85\x0b\x17\xeez\xdd:X\xbe|\xf9!\xbc\ +\xc6\xde\x86\xfe\x11\xf0\x9e\x84>_\x1c\xff\xf8\x8eKm\ +c\x16\xdfK2\x95\xfd\xc7\xfd\x07C\xfd\x0f\x1c8p\ +\xe4\x9bh?\x02\xce\x09\x01\x01\x01\x11oC\xff\x08\xef\ +\xbc\xf3N\x92\xfe\xf87v\xff\xdfX\xfbS\x1b-C\ +\xfb\x83\xe3\x11}\xc47\xd5~F\x86\xf8\xf8\xf8\xca/\ +\xfe\xbcI\xfd#/\xe6]\x8a\xba\xf8\x9f\xa6\xba\xffn\ +\xa8\xffU\xabV\xe5\x9a\xa2]\xeb\xd6\xad;\x8b6\xb6\ +.u&M\x9a\xb4\x14\xe7\x847\xbd\xef\x07c~\x7f\ +]\xf4o\xec\xfd\xdf\xba>\xff\x83{\x86\xa6zf{\ +\xe8\xd0\xa1\x19x-}\xf9\xe5\x97G\xeaR\x0f\xf7\x8f\ +\xdf\xb4\xfe\xb1\xcd\xb8\xf66v\xff\xcdX\xff\xbf\xae\xf6\ +\x07\xe6\xa3-\xa6j\x93\x8d\x8d\x8d\x1d\xd2\xc4\xf1ZSW\xfdcx\x999\xe1\ +e\x01t4\x87\xe1\xab\xb3?o\xe5\xfd#\xc3\xfa?\ +\xfc\xf0\xc3\xc5Wm\x1b\xfat\xf8\xee\xf4\xcb\xf4\x01\xce\ +\x09x\x7f\xe0u\xea~\xc3\x86\x0d\xe7\xf5y\xe2\xfa\xd7\ +\x18\xfd\xbf\xae\xf5\xaf\x81,.\xa6\xf8v\xd0\xe6\xcd\x9b\ +\xff~\x19\xfdc\xc09\x01\xef\x93\xbd\x0e\xddC\xdb\x9e\ +\x18>3m\xec\xfck\xca\xfd7\x9c\xc3\xaa\xbb\xff\x8b\ +\xf7\xa9M\xf1,\xce\xf4\xe9\xd3\xbf}\xd9>\xc0\x80\xf7\ +\x8bM9'`\x9b\x18\x9b\xaf\x1f\xde\xf6\xfa\xab\xaa\xd0\ +\xbd{\xf7A\xa6\x98\x8f\xf1\xd9\xadW\xe9\x03|n\xc2\ +\x14\xcf\x02`[\xbav\xed:\xb0*\x1eu\xd9\x7f3\ +\xa5\xfek{\xfe\xc7T~\xf6\xbau\xeb\xceT\xf5\xdc\ +Am\x01\xe7\x02\xbc\x9fc\x0a\x19jzG\xc0\xd8\xf5\ +W]\xf6\x9fk\x93\xc7\xd8\xe7\xdf\xc6\x8f\x1f\xbf\xd0D\ +\xd7~\x05\xfa\xf9\xcc\xfd\x81\x9a\x02\xfa\xaf\xb8\xd65\xd5\ +\xf3G\xd8\x86\x9a\xf8\xbd\x8d\xf7_p\xff\xad]\xbbv\ +a\xb5\xe9\x02\x03\xee\xc7\x9bB\x0f\x08(\x17\xae{\xf0\ +\x19\x0d|O\x0f\xdf\x19C\xc08\xe6\xa1\xffe\xca\xfb\ +0@\xab\x02\x9f+\xa8\xa9}\xc6\xbe\xffej\xfbS\ +\x97w\xd8F\x8f\x1e\xfd\xa9\xa9t\xf26\x00\x9f\xe3\xae\ +\xee~B]\xde?5\xe5\xf3'\xc6\x8e\x7f&H\xa5\ +\xd2\x8f\xdf\xb6\x1e_\x05\xf0}\x86\xaa\xee'\x98\xfa\xfb\ +\x1b/\xb3\xff`l\xe8\xd5\xab\xd7\xb07\xfd\x9c\x8a)\ +\x01\xef7\x06\x06\x06v\xd0o\x93\xb1\xcf\x1f\x9ar\xfd\ +\xf5*\xdf\xffi\xd2\xa4IK\xf0\x85Mr\xff\xefm\ +\x00\xae}\xf4\xe7\x84\xba<\xff`\xaa\xf7\xafkZ\x7f\ +\x19\x13\xf0;A\xb8\xd7\xf9\xb6u\xc9\xc0\x86\x0d\x1b~\ +\xdb\xbe}\xfb\xf5\xba\xd4a\xe6\x04S\xde\x7f\xaf\xcb\xf7\ +\x97L\xf1\x0e9\xae\x11\xde\xe6\xfb\x92\xc8;99y\ +\x1a\xca\x82\xef#\xe3{\xc9u\xa9\x8fs\x02\xea\xd4\xd4\ +\xf7\xdf\xdf\x94\xfe1\xfc\x93\xde\x7f\xc7\xf1\x8c\xef\xe7\xd7\ +\x85\x0e>\x7f\x83\xef\x88\x98j\xfc\xbf\xad\xef\xef\xe1\xde\ +\xca\xe6\xcd\x9b/\xbdn\xbd\xe3\xb7=p\xcdP\x93,\ +\xf8\x9d\x0a\x9c\xe3\x8c\xa5Y\xdd\xb7\x5c\xf5\xc1\x94\xfb?\ +U=\xffl\xaa\x80>\x12\xac\xa3\xfe0\xb5\xde7m\ +\xda\xf4gM\xef\x11\x19\x06\xdc;\xc2\xef\xb6\x98\x8a\xbf\ +\xb1\xfa7\xd6\xfe\xd7\xd5\xff\xafk\xe0p8\xad\xf1\x7f\ +\x9f\xbc\x8a\xaf\x84\xcfY\xcd\x981c\x03\xfe\xef\x83\x97\ +\x91\xe1e\xe6\x84\xea\x00\xfd\x1fS~\x7f\xf5M~\xff\ +\xb3a\xc3\x86\xcdp\xaeF\x9b\x8d\xdf\xa8\xc1o2\xa1\ +\x0c\xb8\xc7\x83\x80\xf1\xec\xec\xecGX\x8680\xa7N\ +\xc5:\xa6\xe0\x8d\xbe\x1a~\xdb\xd1\x14\xe3\xdf\x94\xdf?\ +\xfc_\xfb\xfeg\xff\xfe\xfd%\xd5\xcd\x09\xf8\xecXm\ +\xfa\x7f\x9b\xfb?\xff\x96\x80s\x9e\xe1\x9c\x80\xef\xbb\x19\ +\xf3\xad\x857\xf1\xfe\xdd\xffB\xc0\xef\x9b2s\x02\xea\ +A \x10\xc4\x98\xf2\xfd\x17S}\xff\xe1\xdf\x1c\x989\ +\x01\xf7\xbdM\xfd\xfc\x95)\xbe\x7f\xf2\xbf\x14\xea\xb2\xff\ +f\xaa\xef\x9f\xbc\xea\xfe\xcf\xbf)\xd4\xe5\xfd\xf7\xd7\xf5\ +\xfc\xe1\xffrx[\xef?\x9a\xf5\xaf\x0duy\xff\xcb\ +T\xfe'\xda\x1f\x9c{\xf0\x99?\x5c\xa7\xeb\x01\x9f\x01\ +\xb4OxF\x1c\x1d\x1e\x9f\xc1\xc32\xfd<\xecK\xbd\ +\xfa\x02\xbd2\xbe\x1e\x0f\x86\xa6>?\x9a\x16\x02\xd2\xd0\ +\xaf\xa7\x0f\xfa4\x18y\xf4h\xf1u\xfc\xf9\xfeU\xb7\ +E\xc0\xb4\xa5*\xf9#\x22\x22z\xe1\xff\xc2y\x93\xfe\ +\x0f\xd3\x07x\x1d\xe8\x03\xaeO\x0c\xe3x\xd6\x8f\x1b\xd6\ +\xd1\xa7SU\x9d\xaah\xd7\xc4\xab&\x9eU\xd15\x86\ +6\xcah\xd8^}Z\xc6\xdc\xf37\xe5\xfd/3\xd4\ +\x1dL\xb9\xffl\x86\xba\x03\xee\x05\x1ac\x7fF\x8f\x1e\ +\xfd\xd9\xdb\x96\xf5\xdf\x08\xb8Oa\xc4\xff\x1f\xb4NJ\ +J\x9ah\x1e\xff\xa6\x07|\x16\xb2\xb6\xef\xbb\xb2\xd9l\ +\x0b|\xce\xd4\xfc\xffgM\x0b\xb8G\xfe\xfe\xfb\xef\xaf\ +rpp\xa8W\x93\xfe\xf1\xff3\xe3\xff\x8e\xff\xff\xfa\ +\xed\xd4\x7f*\xe0\xfb\xfbQQQ\xfdY,\x16\xbb&\ +\xfd3s@\xaf^\xbd\x86\x9b\xe2;bf\xd0\xfa=\ +\xf8\x8c\x05>\xa7R\x9b\xee\x99\x80\xffS)666\ +\x11\xbf\xeb\x83\xcf\xcf\xeb\xff\xbfP\xc3\xff\x1djL>\ +\x93\xae.\xbf&\x9c\xea\xca\xf4\xf3\x0c\xf3\x0d\xa1\xaa\xb2\ +\x9ahT\xc5\xdfXY\x11\xd0\xde\x1c8p\xe0\xc9\xd7\ +_\x7f}\xeb\xac_\xce\xc4\ +qO\x83I\xe3\xd90\x8d\x80u\xf5\xe2\xf3\xf5\xeb\xeb\ +\x83a=\xa4e(\x0f\x93\xc7\xc8d\x98\xa7_G_\ +\x16}Y\xf5A\x9f\x86a9\x961\xf9X_&\x93\ +\xcd\xc0\xef\xe6\xe2<\x8a\xfe\x0e>_\x83\xfa\xac\x8b\xfe\ +\xf5\xfb\x01\xff'9\xf6\x05\xd2\xb1\xb5\xb5\xb5\xc7xU\ +\x80v\xab\xba2c\x00\xeb\xbf*\x8d\x97\x95\xc5T|\ +\x19\xc0u\x14\xfa2/\xabws0\x07s0\x877\ +\x14\xd8_R\x04\xfdR\x0a~\xe4K\x16\xb1\xa0\xe3\x84\ +$~\xc9~\x16\xd7\xa2F\x89\xe5I\x12N\xbfT\xb9\ +J\xaeL\x95gp\xba\xcaE\x99\xe9\x12\x99\x8a\xd3U\ +\xa8\x12r\xbaH\xe5\xa2Q\x84\xd7%&6v\x80B\ +\x86\xf8\x18\xef#\x1c\xaf \xa4A\x02M\x87\xad\x03|\ +\x0em\x18\xb1\xd0h\x08q\xa0\xdfi\xb0\x88\xc2r8\ +\xb2\xf0\x88\xf5drE\xba\x86\xd8\xa0\x00\xcc\x13\xf7\xad\ +\x09\xc1\x1a\xb5\x83u|\xaa0C\xc2\xe1\x22\x1di\xa6\ +\x0c\xbf>\x83_\x8b\xb4&\xf1$\x95\x08I\x06\x91\x10\ +\x0e\xe1j\xe5\x93\xca\x94\x0a\x14@\x09U\xe8\xf4\xf84\ +1\xa6\x01\xbc1-\x92&I1M\xe9\xda\x93&K\ +\x1e\xa7+\xa7\xd3\xa3d\xa3\xe4\xfai\xa92#\xf9\xb9\ +\xb4H\x8a\xf4m\x18uc\x9e25\x1dy\xf4\xc4\xc6\ +\xd1<2\x95*]q\x08@}\x9d\xd6\xa1V\xbaD\ +%\x14\x83ru9\xb6R\xe1x\x89b@Z\xbaD\ +,\xcfL\x8a:\xc7+\xc9\x90\xf4\xb8B\xd3L\x1e\xa7\ +\xc8\xd0\xd5}>P\xd0\xea\xbe\xa4\x1eq v\xc4V\ +\xf7s'1\xa47q\x01\xad\xb8\x11O\xe2M\x1a\x00\ +\xb4&\xcdA+\xcd\xe1\xe7\x03\xe7\xc6\xa4\x11i\x02\xd0\ +\x92\xf8\x92\x16pl\x05\xd0\x02\xca\x9a\xd1\x18\x1c\xc0n\ +\x039\xdac\x0b\xc0\xc1Z\xbe4vK\xfa\xd7\x16\xea\ +6\xd2QiF\xff\x1aC\xac9p\xf3\x02p\x07\xbe\ +.\xc4\x15~\xb1\xa4\x0f-\x91\x0d\x80\x1d\xfc\x1c =\ +\x88p*\x88\x85\xa5s3?\xae:\xa0<\xd0\x8b\x1b\ +\x10\x10\xc8\xe5\x06\x06r\x03\x03\xb8\x01\xf4\x09\x93pP\ +\x07\xda\x05\x04a\x09\xe4\x06\x04\xa8\xb9$P\xcd\xb5\x0c\ +\xd4\x15\xa9\xb9\x9a@\x1b\xc0\x0f\xf0k\xe5eC\xa9\x89\ +\x86XSn\x83\xd7_\xbcWPPQH\x0a\xd4\x85\ +\xe4aE\xa1\xa6\x80*,(.d\x17\x14\x16B\x8e\ +\xba\xc0\xba\xb0\x00\xa3\x85\x05\xe5P\xd4\x10\x13\x0f1U\ +X\xf8\x10\x8f\x90,xp-{\x92\xbf\x15\x8c2\xc2\ +\xf6\x98~\xbfb\xb3\xc6\xa2do\x94\x15\x8cj\x1b\xe9\ +\xe3\x1f4l\xf5\xee\xb6\x14\x8b\xb48\xb9Q\xc3*\x1e\ +g\xcdb\xf5-\xda\xa0\xa1\xf6\xb9\xb1,\x15\x15\x10\xb9\ +\xdc\x9ce3]\x0d\x91\xfb\xedX6\xb31\x92\xef\xc7\ +\xb2\x9d\xab.\xd4\x10\xf5E\x8dZ]\x01\xe7{\x90\xd9\ +\x9ee3W]\xaa\xf1T\x9f\xc8\xce\xc9>\x90s \ +'\xe7\xc0\x81\xfd99\x90\x80?\x88\xab\xb3+r\xec\ +\xb21\x83\xce\xdb\xaf\xce\xd1\x1c(\xc9\xb1B<(\x86\ +T6u\xb8\xb0DC\x15\x00\xf59@\x88]\xda\xdb\ +\xcb\xb3\xdc\xcb\xc3\xd3\xcb\xc3\xdb\xd3\xcb\xdb\xdb\xcb\xdb\x8b\ +>yzz\xe3_\x99\x97\xb3\xa7\x07\x14A\x19\x94x\ +y{\x22B\x85\xb7\x0d\x8d\xe9\xed\xd9\xfe0\x92jO\ +\xd9\xce\x81s\x09o\x1f\xa1\xdc\xf7A,\xdf\x1fE/\ +\x85\xac\xf0}\x84\xe5\x96\x8dQ\x86!U\xcc\xab\xcc\xcb\ +\x0f\xa0l\xa0\xa6U\x09\x9f\x10\xf1\x22\xab\x83\x84r\xdb\ +\x8f\x04\x03\xb4\xa8\xd6%\xe1\x84_\xa29\xe8}\x88P\ +.4]?-]v\x09\xcf'_\xad\xa1\xae\xf3\x0f\ +C\x95l()\xd4Qg\x95v\xbcS\x0e<\x92\x8f\ +\x80(\xfb\xe9*\x94m\x16\x0a\x97\x07\x87\xc5\xd6\x85\xc4\ +\x91;\xeb\xf0\xdd\xe2\x0a\x8d\xa6\xa2\xf8\xde\xe1\xd9\xdc[\ +\x80\x87\x04@\xb14iR\xfeTC\x1dl\xf0\x84\xb0\ +\x82\xb7>z\xa4a=\xde\x1e~W\xc7\xa3 \x80n\ +'\xa9(\x02\xce\x82bB\x89/\xdf\xd7PWS\xee\ +3$\x80\xd5\xdc\x12\xe8\xdd\x99\xa5OQ\x842B\x85\ +^\xbd\xad\xa1\xae\xf1\xf2\xa1\xc1\xfb\xb5\x0df\xd9\xcc\xd1\ +6\xa0\xe3\xad\x22\x8d\xe5bk0s\xdb\xafk\xa8\x9d\ +N\x85:*\x05\xba6Z@\xe3\x1b\x1d-\xd2\xb0\x0f\ +6\x18_rUC\x95Nz\x08r\xe4\xe8\xd4\x83\x18\ +\x96\xa5\xe1`\x07\x96\x17iH\xc9\xdf\x1a\xea\xb0\xc5c\ +]\x93Q\x17YZMW\x10jT\xe9\xaf\x1a\xean\ +\xd3'\x0cy\x7f\xa6\xb3\xca\x09\xd5\xf1\x16\x94\x95\xf6-\ +\xaa,\xd3uQ\x09\x1fdot\x14\x0a\xd5c\x9f\x12\ +\x96{\x0e-\xbb\x1fe3\x17k\x86\x97\x12\xca\xe6\x8b\ +3\x1a2\xb6\xb8\xb2s\x03\x98\x0e\xe7\x15\x13\xb2)_\ +c]~rR\xac\xb7g\xf7\xf2\xfb\x1a2\xbe\x84\xe9\ +\xd7\xcaa\xc1+!dB\x81\x86U6\xd3\xa2\x8c\x90\ +\xd2{\x1a2\xa1\xf4\x05$~)!#\x0a4\xd4)\ +\x17\xc0.\xbd\xab!\xc3\xcb\x98\xde\xf6\xd7\xb5\x90\x16\x94\ +W\x01H7\x04Z$\xaa\x22\xbc\x5c\xa7\x07\x10\xcaf\ +\x9e\x96\x1dd5|\xfa\x00b)@\xb3\xf4\x8e\x86z\ +\xea]\x01\xed\xd2\xf6\xc93\xa9@]\xac\xdb@c\x89\ +u9\xa0A\xd7\xddd\xa9uba\xebgc\xeb\xf9\ +\x15\x84\xec\x82\xa2C\x0d!RzKC\xb6\xaa\x81\xd2\ +^$\xf0\x80\x11\xab\x98\xaf&d\xe4M\x90*\x02\x22\ +\xa575\x16I0\xcf\xd0c\xc4\x9f\x1e#\x96%<\ +B\x1a\x96\xdd\x80^N\xd5\x22\x94\x82\xc1\xdd\xafw]\ +Z F.4\xec\xec\xcd\x22\x0dk\xa9u\xe9\x0d\x0d\ +{\xbf\x16\x05\x87\xea\x1c\xdd\x08i\xaa\x06-f4<\ +\x02C\xf20\xa2\xa8=\xdd\xb5\x0a\xa2\xd9\xc00#[\ +\xf25l;b\xbd\x0c\x86\x89\xfa\xba\x86\xf5\x03=\x84\ +\x18\x05\xb2\xe1\xca\xbbS\xa0!\xa5\xd0\xf24\x1c\xb5e\ +\xf3Jz\x92\x12\xa0y\xcb5[\xaf3\xd8\xc0\x0ar\ +I!(2\x02\x04\xa2\x96ZC\xbf=(\x80\x02w\ +\xed\x00\xd0!\xb2\x80c\xbe\x86\xdc\x82n\xd1\x0a\xd5\x10\ +4y#_\xa3S\x22C\xae\x98G\xca\x80\xdcC\xe8\ +t\xeb\xa5E\xa8'Pf\x01\x0e\x08\xddE\xfb\x8c\xed\ +}\x14\x0f.\xb6\xd4\x12\x10\xaf4\x0d\xf4\x09\x82\xb0\xef\ +i\xc7]a\xa5|\x05\xe9\x0e\x03\xdb#\x93\xf35,n\xa5hY\ +:\xa5\x92+\xc0\xf3X\x91V6R\x0a\xec.\x90\xca\ +\x8b\x87\xe9\xbdH0\xcdT\xb9\xc3\x13\x94\x0c\xfaO\x1d\ +L\xb4\xa2kQ\xe0*\x01I\x17\x03\x9d5\x8fQ\xa4\ +[\x1a\x8b90\x04\xf4\x8d\x18=\xde\xc8E\xa0R\xe6\ +\x018\x0d\x0f\xdd\xd1\x90\xf30\xdc\x5c\xf7=w\x15\x84\ +C\xd7\xb8\x14\xc14\xf0\xf7#\x10h\x09\xcc\x01O\x9c\ +\x98A\xa9\xebf\xaa\x14\x07x8LL\xe4[0\x12\ +)%\xa0\xef\xf2\xe0\x8aJ\x0bXI\x0aze\x10\xce\ +(\x9b\x00K\xf0\x148\xab\x07BEW}\xb3\xa3\xbd\ +\xec&\xc3\xc0;\xe3\x0dmkx\x16\x9a0\xa9\x0cF\ +\xb8N\xdf:k\x8a\x17\xf0B\xe8\xaf\x8a\xa5v%\x84\ +\xe5\xb0\x0c.A\xb2\xb0\xd4\xc0\x84Q\xc5a`\x0d\xe6\ +\xe5kl\xd5\x17f\x8bCC\xc5\xb3/\xaaA\xb4\x8a\ +\x91\xa5\x8c=|\xc6\x95\xf7\x94\x90OOk\xa8/\xe0\ +\xd2\xd3\x99\xdb\xca\xab)\xac\x88\x90\x8f\xa1\xec(\x5cr\ +\xcc\x0c\xa1\xd32\x0fd\x1cVvFC\xdd\xea\xa0f\ +.\xe8J\xdd\x15\xf3@\xaf-\x1f\x9c\x01K9\x92h\ +{\xb0\xd0\x8f\xd1\x18h\xd3\xea\x18\xd8\xcd\xdf\xae\x83\x8d\ +\xfd\xc2Z7\xdc\xe9\xf9\x08\xc8\x825\x9e\x5cz\x15.\ +\xcbi\xa4\xc1A@8\xea\xaf\x1b\xe6\x0c\xdf\x02B\xe9\ +\x0c7t\x86\xf5b\x18LwNi\x85\xb6\xd1)\xfb\ +\x01\xa1\x82/\x81\xa1\xb8\x1c\x0azI.~\xaa\xd1h\ +\xad(#\x1c\xff\x1e\x88\xfe\x07\xcc \x7f\x0e\x87\xf1(\ +\xb8\x9e\xaf\x9dAh\xfeaw\x08\xab\xf5\xfa\x070\xff\ +<\xd8\xd0\x16\x1a\xd1\xe0 tH!3\xcf\x86\xdf$\ +\x8e\xcd&\xef\xbc\xfa\xa8\xa4\xa2\xa2\xe4\xd1\xd5\x9dS\x9b\ +\x814V\x8b\x0a\xe9\xf9Eg\xc1y0gJ\x8a\xcb\ +\xe1\x1a{\xaesy0\xc7\xf2\xaf\xc3\xd4y\xfb\xe8s\ +\xf34\xef\x00\xb1\xf4>\xa8)\x89\xd1\xda f\x0c\xe7\ +\x10\x0b\xabE\xe2\xca\x0bD79\xec\xab\xd4\xb4nJ\ +(\xa5g\xfe\xfd\xfa\x9d\x863?\xf5\x9cE\xd3e\xe5\ +\xe8f(\xdaZ\x94\x8a#\xd4\x025\xdf\x9d\xc7\x8f\x88\ +\xe0\x0b\x04<8\x84\x0b\xf8\x82\x08>?B\xc0\xe7U\ +\x084<*<\xa2\x9co\x0b\xa9\x88\x08\x9e\x80\x1f\xce\ +W\x0b4|[\x01O\x8b\xdc\xeb\x04#m1\xda\xb0\ +\x12\x9b\xe2\x92\x92\xe2\x92b\xf8\xab(\xb1*\xd5\xc6\xd5\ +%\x04\xc0I[B\x97A\x0c\xca1\xd7\x12\xe3\xa5\xa5\ +\xea\x12M1U\xa2\xae\xbc\xec60\xfd\x08\x91\x07@\ +\xfd\xb3\xef@_\xedX\xd6\x131\xe3lS\x96\xc5\xc0\ +\x22p\xe2*V\xd5cQ\xad\x8eB\xecf\x7f6\x9b\ +\xd8\x0e\xbf\x0dN^\xc14W\xf4\xff\x5c\x12s\x9f\x96\ +\x81cY\x0e\xa0V\xb34j8Z\x02@\xa4Bm\ +G'\xe1\x00\xc7r<\x80\xf5\xc0T\x19\x80\x86F.\ +W[U<9.\xf5D_\xd2\x86\xb2\xf3O\x9c\xbe\ +`aV\x96z\xae&\xcbu^V\xd6\xdc\xac\xac9\ +s\xb3\xe6\x22\xcc\x9d7w\xee\xfc\xb9\xe5Y\x96\x18\x9f\ +\x03\x18\xf5\xe6de\xcd\x83X\xd6\x1c\x1a!kN\xc5\ +\x5c6\xa4\xe7\xa8\xe7\xda\xcf\x9b3'k\xc6\xf8\x81\xed\ +\x1c`\xb9\x00N4ei\xe7`_\x06\x8e\xffC;\ +\x8dm\x01\x9c\x8b\x00\x1e\xd8\xb1l,\xa8\x0a\x5c\x1a\xb0\ +j\xf8Y\x81;nE\xff\x9caY\xe1\x00?'p\ +\xd3\xb51\x078\xbb@\xbe\x1b}\xf6\x00p\x82%\x86\ +=\x1c\x9d\xe8\x85\x86\x03}\xb4\xa1q\xb4\xcb\x0cw\xfa\ +\xecI\x83\x16\xc3\x11~\x0et\x0d\xfc\xb9\xeax=\xfb\ +U#\xd7\xb7\xb9\xd5\xff\x1e\xe7\xee\xd7<\x81#\xf4G\ +\xeeA\x88\xe5\xe7\x1e\x82\xe3\x83\xdc\xc3p\xbc\x9f{\x04\ +\x8ews\x0b\x09;/77\xaf8\x97\xe4\xdd\x22O\ +r\xef\xe4\x82\x17x\x04-\x92\xe3\x15:\x8d\x92\xe3\ +\x05t\xd7B\xcb\xf2\xca+5_\x00\x95N?\x01\xf5\ +\xe6W*\x1f\xe6\xcf\xbc\xfb\xa0~r\x12QKu\xfd\ +H\xf2\xee\xe6jNh\xe8\xc6Py\xba\xbe\x83\xe91\ +\xefd\xae\x96\xdaSr\x22W\xcb\xab\x88\xce\xd5J\x02\ +\xf3[n\xde)\x9d\xfaa&\xb8\x81\x03*\xef\x16$\ +\x1e\x02\xd6%h\xfeM\x88\x17\xea\x06\xa0\x9a`I>\ +\xc8\x0a=x\xa2\x14\x9a\xf48\xf7>\xcd\x1a\x07\xd8]\ +H\xdd%j\x1c\xd0\xa0\x82{\x90\xbaE,N\xe0\x90\ +/\x05\x91N\x14\x90\xfb\x90uX\x93\x0f\xc7C\x9a\x02\ +8\x1e\xd4\x14\xc2\xf1\x80\xe6a\xe5\xe5cx\xac\xeer\ +3\x1b\x10\xb3\x011\x1b\x10\xb3\x011\x1b\x10\xb3\x011\ +\x1b\x10\xb3\x01y\x0b\x06D{\x8f)V9\x8a\x10\xb8\ +\x84\xe9\xfb?t\x08\x9a\xa6\xbb\xd7\xd2O\xa8R1\xf7\ +]\xbak\xf1\x1c\xf4\xf1\xf0\xf0\x7f\xa8-\xb6Q\ +\x00\x00\x09t\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Sky Icon\ + / System / View\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\ +\x0a \ + \x0d\x0a\ + \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \ + \x0d\x0a \ + <\ +/polygon>\x0d\x0a \ + \x0d\ +\x0a \x0d\x0a \x0d\ +\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x14\xc4\ +I\ +I*\x00b\x07\x00\x00\x80?\xe0@\x08$\x16\x0d\x07\ +\x84BaP\xb8d6\x1d\x0f\x88DbQ8\xa4V\ +-\x17\x8cFcQ\xb8\xe4v\x0b\x02\x7f\xc7\xa4R9\ +$\x96M'\x94JeR\xb8\xa4\x82Y/\x98Lf\ +S9\xa4\xd6a.\x9bNgS\xb9\xe4\xf6}#\x9c\ +O\xe8T:%\x16\x8d/\xa0\xd1\xe9T\xbae6\x9d\ +\x1f\x81\xd3\xeaU:\xa5V\x91Q\xabVkU\xba\xe4\ +B\x93]\xb0XlU*\xfd\x8e\xcdg\xb4N\xec\xb6\ +\x9be\xb6\xdd&\xb5\xdb\xeeW;\xa4J\xe3u\xbc^\ +o7{\xd5\xf6\xfdi\xbe_\xf0X:\xde\x07\x09\x87\ +\xc4Sp\xd8\x9cf6\x7f\x8b\xc7drS<\x86O\ +-\x97\x93\xe5s\x19\xbc\xe4k5\x9d\xd0hk\xd5\x8d\ +\x16\x97M-\xd2Q\x00:\xbb\xf4\x805\x05\x1d\xc1F\ +PQ\x8c\x14;\x05\x08\xee!.\xf8+\xc2\x0a\xe3\x82\ +\xb3\xe0\xac\xe8+\x13V\x01s_s\xf9MM\x0f\x8f\ +l\x90\x06\xe0\xa5\xc8)B\x0a7\x82\x80f\xd2\x18#\ +.\x0a\xaf\x82\xa9x\xfc\x0bG.e\xe6\x98\xf3\xeb\xb2\ +\x01\xc4\x14\xe1\x05)\xc1@\x95g\xe7~\x0a\x90\xe3\xb1\ +\xbd|\xda\x17\xa2`\xf5,\x88\x13n\x82\x11\xa8(\xaa\ +\xbd\x15\xa8(\xe4\xe3\x9cJ\x9b\xfc\xab\xbb\x8a4\x00\xa2\ +\xa4\x002\x0a9\xa0\xa3\xda\x0a\x05\xb1\x07\xb2\x0aB\xa0\ +\xa4k\x8e|\xa8\xf0zY\x13%p\x9a|\x90\x08\xc8\ +)&\x82\x84\xcd\x11\xb6\x82\x8d\xce9p\xa1\xc5\x09T\ +r\x94\xc5Na\xfe\x01 \xa4\x1a\x0a=4\xe8y\x11\ +\x0c\xb8\xe7\xf2s\x1d\xa5\x12bO\x1e\xc4\xe8\x10 \x82\ +\x94\xc8(\x93\x22\xa2\xd1\xba\x08,\xb8\xed\xf3\xce\xfe1\ +\xf3\x02}(3(\x10T\x82\x96\x08(K,#\xa6\ +\xe3\xaa\xe3\x9a\xb0\x82\x95'$\xd3\x22D\x90\x08o\xb2\ +\x08\x06\xcd\x899\xe8\x82\x8a.9x\x94\xce\x89-\x0a\ +\x92N\xcc\xf2\x04\x22\xa0\xa5\x8a\x0a\x04\xcf\xa9\x81\xf0\x82\ +\x89\xce9wCLI\xed\x0e\x91\xd1(\xacYF\xa0\ +\xa0B\xa4\xe4\xa0\x85b\x0a[\xa0\xa7\x0a\x0ar#\xe8\ ++^\x82\x17\xa8(8\xaeRh \x9e\xe3\x97H\xf5\ +7;\xd3)\xe5:\xd1\x9f\xe2<\xd1P\xa9\x87\x02\x0a\ +:;\xf2J,\x90M\xc8 F\xb1V\xa0\x00\xa0\xe3\ +\x974T#\x0aW\xa9\xdd~\x85\xa4\x01\xaa\x0a`\xd1\ +\xeaa\x18\x82\x8f\xce9\xef] Vh\x01g\xac\xf0\ +\xf2\x08\x1f\xb8\xe6e\x97l\xa7U\xda=m\xa0\xc9\x00\ +<\x82\x99((0\xa3IH \xd8\xe3\x92\xf4%\xd2\ +\x82\xdd\x8ba\xce\xeb\xbck\xb5\xe9%\xe1\xe9\xb5\xb6\x90\ +\x01\xc8)\x8a\x82\x85\x8a`\xc8\xe3\x93\xb0\x85\xd5\x84\xad\ +\xe6\x92\x0a\x1e8\xe7\x9a\x1d{#\xb9B9;$\x0e\ +\xd2\x08Y\xa0\xa2Z\x98P8\xe3\x0c}\x8f\xafE\x94\ +\xde\xd5\xda\xf7\xce\x22\x9a\xe5H\xdeX\x81\x0f\x92\x0d\x8a\ +\x82\x85W6o\x840C\xbb\x8eE!Z\x0d\xac\xa5\ +EI\x00z\x82\x97\xe8(\x06\xa6\x0c\xae99\x88\x1f\ +\xf9\xc2\xfe\xfa\xa0\x99#W~\xaa\x19\xecq\x9f\xa6\x8e\ +~(\x82\xe4H >\xa6UH J\xe3\x9f{\x06\ +\xc4\xc2Fh `\xe3\x9e\xda\x923\xc1#\x1br\x05\ +\x02\xa0\x83\x92\xa4D\xb8\xe3\xc55\x83\xd9\xccq\x02\xe3\ +\x90\x1c\x22/\xcb#2\x9a\x08\xf2\x00\x00b\xa4 8\ +\xf7\x02yfi\x8ckx\x82_h$\xfe\xa3s\x08\ +\xc0\xec\x82\x91*\x95\xce\x82\x01\xfb\xc7\x1f\xb0\xf4\xac\x97\ +\x14\x82\x11\xf1.\xd8\x9b\x16\xa8(\x94\xa9\x1b\xae8I\ +\xb5\x9f\xe1\x12\x0a\x02\xa5\x99\x86\xec\xa9y\xc0\x00\x9d\xdf\ +mJ)\xb3\x18*F+\x8e\x1e0i\x06\xb2\x82\x08\ +\x0a\x91\xb0\x82\x85>\xa2\x9bw\x00\x14\x82\x9eZ8\xe2\ +o\xba\x81<\x08#\xac\xa7\xfd\x10\xe7Y\xdf\xa6\xa6\xfa\ +\x0a\x10*F3\x8elL\x11 \x18D\x15\xab\x94\xf1\ +\xbc\xeeJ#\xad\x22\xea\x5c\x82\x04B\xa47\x8e; \ +9D\x08m&\xa2\xa4\xb5H H|\xc51#\x90\ +@\xeeT\x96\x8b\xb55c\xe8\xba\x92\x07\x98A\x12\xf0\ +\x00\x01EHB\x10P\xfb\x06\xca[s \x83u\xad\ +\x15 \x86q\xc5\xf4% O\x84\x82=\xf2\x9d\x09\x08\ + ! \xaa\x91l=R~\xe1\x87\xf8\xa9 \xa1X\ +\xa9\x10\x04`\x06\x06u\x00A\xa0\xf0\x88L*\x17\x0c\ +\x86\xc3\xa1\xf1\x08\x88\x01\xff\x14D\xc1\xce\xd1(\xccj\ +7\x09O@\xc0&8\xa3\xfe9$\x92\xc2$Ri\ +Lr?\x13\x8a\x08\xa0\xed\x0886U4\x8d8\xe0\ +\xe2H\xfb\xeak<\x9e\xc2\xa4@h;r\x0e\x1d\x9f\ +Q\xa1N\xf88\xb6>\xe5\x94Q\xe8\xf4\xea|\xfaY\ +'\x8a\x17\xa0\xea\x1a\x95j\x0ci\x8f\xa6+v\x08\xcc\ +\x88\xd1\x07K\xd8i\xe5x\xfa\xaa\xab#\xb3\xca\xaa6\ +\xe9-R\x17\x22O\xc1\xcc\x17\x19\xa3\x8a\x0e*\x8f\xbd\ +o6\x19\x102\x0e\xd6\xa2`%)\xa8\xf9\x9e\x19p\ +\xc3\xd8\xa2\x98\xe9\x5c\x0e\x1b\x22\x04A\xd8\xd0q\x8eF\ +H\xa5\x8f\x973t\xf9\x12\x9a\x0eY\xd0F\xd9\x90q\ +\xe4}\xf3\x8c\xc8i\xa28\xdd|2\xe7\x0e\x91\x07 \ +\xec\x988ke\x125\xc7\xd2\xdb\xc8\xe4\x88\xdb\x07I\ +pb\x0eH8\xe2\x99\x10\xd8\xf1\xe0\xfc\xeex\x03i\ +\xb0\x8a\x0c \xec8>\x0f\xa5'\x83\x9d#\xe8\xee\xe4\ +&E\x05\x83E\xa0\xc0\x1f\x14\x1d\xe7\xa9\x8f\xb4\xa3]\ +\x1e\x7f\xc7\x8f\xd4\xf8E\x090u\x94\x1c\x09\xea\x85Y\ +\x90a\xdd\x1f{\x18\xe4\x88\x0eA\xc8\xb4\x1cf\x7fP\ +\x93\xf1\x07\x13\x11\xf2\xe5%|\xdc\x18M\xbc}RD\ +\x89\x9fA\x8a\x04\x1c\x03\x82\xd0\x83\xa1\x07\x1e\x10r\x99\ +\x1f>\xd3\xe5\x01\x07\x16\xd0r\x1d\x07\x05\xe1\xf4\x1c\xfd\ +]\xd1\xf2\x914\x85[(\xdd\xaf\x85\xd2\x94\x88XA\ +\xca7\xee0B\x8f\x04\x1c\xb0A\xcb\x84\x1c\xe1A\xdc\ +\x94)\xb7A\x81\xf4\x1cHA\xc5\x04\x1c\x10\x90\x90\x98\ +\xc9\x06\x17\x11\xf2\xa2(k\xa0\xb8\xe5\xa6\x8e\xd3T\x88\ +T\x89\x10p\x16W\x9a\x97\x985\x06\x16Q\xf2\xb1R\ +\x98Z\x09\xcd\x9b\x98\xe5\xe3\xfeTA\x8at\x1d\x96\x9a\ +\xe7\xf4\xf4\xf8i\x11\xf9\x19`\x9dY\x1a\x1d\x8e\x9d\xda\ +\x14P3A\xca\xe4\x1c\x1e\xa0)4i{A\x854\ +}\xa8\x5ch\x96\x1e\x9c`(\xb5m\x22\x05\x10yu\ +\x06\x10\xe9J\xa1\x08/\xd0u\xa5\x03:\xe8\x89}\xfd\ +\xa7\x97\x9a\x81gH\xa1\xe4\x18\x88w\xaa\x99\xa9\xe1A\ +\x87d~Y\x9d+\x17\xaa\xb3\x5ckZ\xc0\xff~\x10\ +bE\x07\x09k\xc6\x99CA\x86\xf4|\xb6x\xace\ +\xba\xd8Y\xec\x86\x9a)A\x87$\x1c{v\xad\x05\x1d\ +\x7fA\x88d\x1c\x8dj\xe1\xfbi\x81\xb1\x1e+q\xd2\ +H\x81\xb4\x1c\x8aA\xc5\xab\x91&\xa9@\x01\xd5\x1f\x93\ +'\xfb\xb6\x86\xbb\xdd\xcb\xc60H\x83$\x1cnA\xe3\ +\xe4\x18\x07\xaaZ\xc4\x19k\xb3\x11\xf36\xf9KV\xd9\ +\x83\x03t\xb0ZM\x22\x05j\xc9M\x07\x0f\xe4\x16\x82\ +m\x00\x0c)\x15\x07*Q\xf3\xab\x16kq\x8a\xcb\x1a\ +s\xf1\xcc\xb9\x06H\xa5d\x188fs\xb4\x19EA\ +\xb3\x80\x00\x11B\xe4D\x19IA\x93t\x191A\x8c\ +\xf4\x1c\xc8G\xf4l\xd5\xf6\xcc,\x5c\xcbQ\xd5\xb5}\ +a\xbc\xc0u\x9ds]\xd7\xb5-\x7fa\xd8\xb64\xf7\ +[\xd96}\xa3.\xd9\xb6\x9d\xb3m\xa06\xbd\xbbq\ +\xdc\xb5MOs\xdd\xb7{Cp\xde7\xbd\xf2\xd9\xd5\ +w\xde\x03\x81\xd6\xb7\xfe\x0b\x85\xe1\x97\x9d\xeb\x87\xe2\xb8\ +\xb6\xd7\x84\xe38\xfeA&\xe2y\x1eS\x86\xe4\xf9^\ +c}\xe5\xf9\x9esv\xe6\xf9\xde\x83m\xe7\xfa\x1e\x93\ +d\xe8\xfa^\xa3^\xe9\xfa\x9e\xb3W\xea\xfa\xde\xc3\x16\ +\xeb\xfb\x1e\xd2\xa9\xec\xfb^\xe3\x00\xe3\xbb\x9e\xf3d@\ +@\x13\x00\xfe\x00\x04\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\ +\x01\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x01\x01\x04\x00\x01\ +\x00\x00\x00`\x00\x00\x00\x02\x01\x03\x00\x04\x00\x00\x00L\ +\x08\x00\x00\x03\x01\x03\x00\x01\x00\x00\x00\x05\x00\x00\x00\x06\ +\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x00\x11\x01\x04\x00\x01\ +\x00\x00\x00\x08\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\x00\x04\ +\x00\x00\x00\x16\x01\x04\x00\x01\x00\x00\x00`\x00\x00\x00\x17\ +\x01\x04\x00\x01\x00\x00\x00Z\x07\x00\x00\x1a\x01\x05\x00\x01\ +\x00\x00\x00T\x08\x00\x00\x1b\x01\x05\x00\x01\x00\x00\x00\x5c\ +\x08\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00(\ +\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\x00\x10\ +\x00\x00\x00d\x08\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00R\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x00S\ +\x01\x03\x00\x04\x00\x00\x00t\x08\x00\x00s\x87\x07\x00H\ +\x0c\x00\x00|\x08\x00\x00\x00\x00\x00\x00\x08\x00\x08\x00\x08\ +\x00\x08\x00\x802\x02\x00\xe8\x03\x00\x00\x802\x02\x00\xe8\ +\x03\x00\x00paint.net 4.0\ +.9\x00\x01\x00\x01\x00\x01\x00\x01\x00\x00\x00\x0cHL\ +ino\x02\x10\x00\x00mntrRGB X\ +YZ \x07\xce\x00\x02\x00\x09\x00\x06\x001\x00\x00a\ +cspMSFT\x00\x00\x00\x00IEC s\ +RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xf6\xd6\x00\x01\x00\x00\x00\x00\xd3-HP \x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11c\ +prt\x00\x00\x01P\x00\x00\x003desc\x00\ +\x00\x01\x84\x00\x00\x00lwtpt\x00\x00\x01\xf0\x00\ +\x00\x00\x14bkpt\x00\x00\x02\x04\x00\x00\x00\x14r\ +XYZ\x00\x00\x02\x18\x00\x00\x00\x14gXYZ\x00\ +\x00\x02,\x00\x00\x00\x14bXYZ\x00\x00\x02@\x00\ +\x00\x00\x14dmnd\x00\x00\x02T\x00\x00\x00pd\ +mdd\x00\x00\x02\xc4\x00\x00\x00\x88vued\x00\ +\x00\x03L\x00\x00\x00\x86view\x00\x00\x03\xd4\x00\ +\x00\x00$lumi\x00\x00\x03\xf8\x00\x00\x00\x14m\ +eas\x00\x00\x04\x0c\x00\x00\x00$tech\x00\ +\x00\x040\x00\x00\x00\x0crTRC\x00\x00\x04<\x00\ +\x00\x08\x0cgTRC\x00\x00\x04<\x00\x00\x08\x0cb\ +TRC\x00\x00\x04<\x00\x00\x08\x0ctext\x00\ +\x00\x00\x00Copyright (c)\ + 1998 Hewlett-Pa\ +ckard Company\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00\x12sRGB \ +IEC61966-2.1\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x12sRGB IEC\ +61966-2.1\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\ +\x00\x00\x00\x00\x00\xf3Q\x00\x01\x00\x00\x00\x01\x16\xccX\ +YZ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00o\xa2\x00\ +\x008\xf5\x00\x00\x03\x90XYZ \x00\x00\x00\x00\x00\ +\x00b\x99\x00\x00\xb7\x85\x00\x00\x18\xdaXYZ \x00\ +\x00\x00\x00\x00\x00$\xa0\x00\x00\x0f\x84\x00\x00\xb6\xcfd\ +esc\x00\x00\x00\x00\x00\x00\x00\x16IEC h\ +ttp://www.iec.ch\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16IEC \ +http://www.iec.c\ +h\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\ +esc\x00\x00\x00\x00\x00\x00\x00.IEC 6\ +1966-2.1 Default\ + RGB colour spac\ +e - sRGB\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00.IEC 61966-2.\ +1 Default RGB co\ +lour space - sRG\ +B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00desc\x00\x00\x00\x00\x00\ +\x00\x00,Reference Vie\ +wing Condition i\ +n IEC61966-2.1\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00,Refere\ +nce Viewing Cond\ +ition in IEC6196\ +6-2.1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00v\ +iew\x00\x00\x00\x00\x00\x13\xa4\xfe\x00\x14_.\x00\ +\x10\xcf\x14\x00\x03\xed\xcc\x00\x04\x13\x0b\x00\x03\x5c\x9e\x00\ +\x00\x00\x01XYZ \x00\x00\x00\x00\x00L\x09V\x00\ +P\x00\x00\x00W\x1f\xe7meas\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x8f\x00\x00\x00\x02sig \x00\ +\x00\x00\x00CRT curv\x00\x00\x00\x00\x00\ +\x00\x04\x00\x00\x00\x00\x05\x00\x0a\x00\x0f\x00\x14\x00\x19\x00\ +\x1e\x00#\x00(\x00-\x002\x007\x00;\x00@\x00\ +E\x00J\x00O\x00T\x00Y\x00^\x00c\x00h\x00\ +m\x00r\x00w\x00|\x00\x81\x00\x86\x00\x8b\x00\x90\x00\ +\x95\x00\x9a\x00\x9f\x00\xa4\x00\xa9\x00\xae\x00\xb2\x00\xb7\x00\ +\xbc\x00\xc1\x00\xc6\x00\xcb\x00\xd0\x00\xd5\x00\xdb\x00\xe0\x00\ +\xe5\x00\xeb\x00\xf0\x00\xf6\x00\xfb\x01\x01\x01\x07\x01\x0d\x01\ +\x13\x01\x19\x01\x1f\x01%\x01+\x012\x018\x01>\x01\ +E\x01L\x01R\x01Y\x01`\x01g\x01n\x01u\x01\ +|\x01\x83\x01\x8b\x01\x92\x01\x9a\x01\xa1\x01\xa9\x01\xb1\x01\ +\xb9\x01\xc1\x01\xc9\x01\xd1\x01\xd9\x01\xe1\x01\xe9\x01\xf2\x01\ +\xfa\x02\x03\x02\x0c\x02\x14\x02\x1d\x02&\x02/\x028\x02\ +A\x02K\x02T\x02]\x02g\x02q\x02z\x02\x84\x02\ +\x8e\x02\x98\x02\xa2\x02\xac\x02\xb6\x02\xc1\x02\xcb\x02\xd5\x02\ +\xe0\x02\xeb\x02\xf5\x03\x00\x03\x0b\x03\x16\x03!\x03-\x03\ +8\x03C\x03O\x03Z\x03f\x03r\x03~\x03\x8a\x03\ +\x96\x03\xa2\x03\xae\x03\xba\x03\xc7\x03\xd3\x03\xe0\x03\xec\x03\ +\xf9\x04\x06\x04\x13\x04 \x04-\x04;\x04H\x04U\x04\ +c\x04q\x04~\x04\x8c\x04\x9a\x04\xa8\x04\xb6\x04\xc4\x04\ +\xd3\x04\xe1\x04\xf0\x04\xfe\x05\x0d\x05\x1c\x05+\x05:\x05\ +I\x05X\x05g\x05w\x05\x86\x05\x96\x05\xa6\x05\xb5\x05\ +\xc5\x05\xd5\x05\xe5\x05\xf6\x06\x06\x06\x16\x06'\x067\x06\ +H\x06Y\x06j\x06{\x06\x8c\x06\x9d\x06\xaf\x06\xc0\x06\ +\xd1\x06\xe3\x06\xf5\x07\x07\x07\x19\x07+\x07=\x07O\x07\ +a\x07t\x07\x86\x07\x99\x07\xac\x07\xbf\x07\xd2\x07\xe5\x07\ +\xf8\x08\x0b\x08\x1f\x082\x08F\x08Z\x08n\x08\x82\x08\ +\x96\x08\xaa\x08\xbe\x08\xd2\x08\xe7\x08\xfb\x09\x10\x09%\x09\ +:\x09O\x09d\x09y\x09\x8f\x09\xa4\x09\xba\x09\xcf\x09\ +\xe5\x09\xfb\x0a\x11\x0a'\x0a=\x0aT\x0aj\x0a\x81\x0a\ +\x98\x0a\xae\x0a\xc5\x0a\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b9\x0b\ +Q\x0bi\x0b\x80\x0b\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\ +\x12\x0c*\x0cC\x0c\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\xc0\x0c\ +\xd9\x0c\xf3\x0d\x0d\x0d&\x0d@\x0dZ\x0dt\x0d\x8e\x0d\ +\xa9\x0d\xc3\x0d\xde\x0d\xf8\x0e\x13\x0e.\x0eI\x0ed\x0e\ +\x7f\x0e\x9b\x0e\xb6\x0e\xd2\x0e\xee\x0f\x09\x0f%\x0fA\x0f\ +^\x0fz\x0f\x96\x0f\xb3\x0f\xcf\x0f\xec\x10\x09\x10&\x10\ +C\x10a\x10~\x10\x9b\x10\xb9\x10\xd7\x10\xf5\x11\x13\x11\ +1\x11O\x11m\x11\x8c\x11\xaa\x11\xc9\x11\xe8\x12\x07\x12\ +&\x12E\x12d\x12\x84\x12\xa3\x12\xc3\x12\xe3\x13\x03\x13\ +#\x13C\x13c\x13\x83\x13\xa4\x13\xc5\x13\xe5\x14\x06\x14\ +'\x14I\x14j\x14\x8b\x14\xad\x14\xce\x14\xf0\x15\x12\x15\ +4\x15V\x15x\x15\x9b\x15\xbd\x15\xe0\x16\x03\x16&\x16\ +I\x16l\x16\x8f\x16\xb2\x16\xd6\x16\xfa\x17\x1d\x17A\x17\ +e\x17\x89\x17\xae\x17\xd2\x17\xf7\x18\x1b\x18@\x18e\x18\ +\x8a\x18\xaf\x18\xd5\x18\xfa\x19 \x19E\x19k\x19\x91\x19\ +\xb7\x19\xdd\x1a\x04\x1a*\x1aQ\x1aw\x1a\x9e\x1a\xc5\x1a\ +\xec\x1b\x14\x1b;\x1bc\x1b\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c\ +*\x1cR\x1c{\x1c\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1dG\x1d\ +p\x1d\x99\x1d\xc3\x1d\xec\x1e\x16\x1e@\x1ej\x1e\x94\x1e\ +\xbe\x1e\xe9\x1f\x13\x1f>\x1fi\x1f\x94\x1f\xbf\x1f\xea \ +\x15 A l \x98 \xc4 \xf0!\x1c!H!\ +u!\xa1!\xce!\xfb\x22'\x22U\x22\x82\x22\xaf\x22\ +\xdd#\x0a#8#f#\x94#\xc2#\xf0$\x1f$\ +M$|$\xab$\xda%\x09%8%h%\x97%\ +\xc7%\xf7&'&W&\x87&\xb7&\xe8'\x18'\ +I'z'\xab'\xdc(\x0d(?(q(\xa2(\ +\xd4)\x06)8)k)\x9d)\xd0*\x02*5*\ +h*\x9b*\xcf+\x02+6+i+\x9d+\xd1,\ +\x05,9,n,\xa2,\xd7-\x0c-A-v-\ +\xab-\xe1.\x16.L.\x82.\xb7.\xee/$/\ +Z/\x91/\xc7/\xfe050l0\xa40\xdb1\ +\x121J1\x821\xba1\xf22*2c2\x9b2\ +\xd43\x0d3F3\x7f3\xb83\xf14+4e4\ +\x9e4\xd85\x135M5\x875\xc25\xfd676\ +r6\xae6\xe97$7`7\x9c7\xd78\x148\ +P8\x8c8\xc89\x059B9\x7f9\xbc9\xf9:\ +6:t:\xb2:\xef;-;k;\xaa;\xe8<\ +'\ + >`>\xa0>\xe0?!?a?\xa2?\xe2@\ +#@d@\xa6@\xe7A)AjA\xacA\xeeB\ +0BrB\xb5B\xf7C:C}C\xc0D\x03D\ +GD\x8aD\xceE\x12EUE\x9aE\xdeF\x22F\ +gF\xabF\xf0G5G{G\xc0H\x05HKH\ +\x91H\xd7I\x1dIcI\xa9I\xf0J7J}J\ +\xc4K\x0cKSK\x9aK\xe2L*LrL\xbaM\ +\x02MJM\x93M\xdcN%NnN\xb7O\x00O\ +IO\x93O\xddP'PqP\xbbQ\x06QPQ\ +\x9bQ\xe6R1R|R\xc7S\x13S_S\xaaS\ +\xf6TBT\x8fT\xdbU(UuU\xc2V\x0fV\ +\x5cV\xa9V\xf7WDW\x92W\xe0X/X}X\ +\xcbY\x1aYiY\xb8Z\x07ZVZ\xa6Z\xf5[\ +E[\x95[\xe5\x5c5\x5c\x86\x5c\xd6]']x]\ +\xc9^\x1a^l^\xbd_\x0f_a_\xb3`\x05`\ +W`\xaa`\xfcaOa\xa2a\xf5bIb\x9cb\ +\xf0cCc\x97c\xebd@d\x94d\xe9e=e\ +\x92e\xe7f=f\x92f\xe8g=g\x93g\xe9h\ +?h\x96h\xeciCi\x9ai\xf1jHj\x9fj\ +\xf7kOk\xa7k\xfflWl\xafm\x08m`m\ +\xb9n\x12nkn\xc4o\x1eoxo\xd1p+p\ +\x86p\xe0q:q\x95q\xf0rKr\xa6s\x01s\ +]s\xb8t\x14tpt\xccu(u\x85u\xe1v\ +>v\x9bv\xf8wVw\xb3x\x11xnx\xccy\ +*y\x89y\xe7zFz\xa5{\x04{c{\xc2|\ +!|\x81|\xe1}A}\xa1~\x01~b~\xc2\x7f\ +#\x7f\x84\x7f\xe5\x80G\x80\xa8\x81\x0a\x81k\x81\xcd\x82\ +0\x82\x92\x82\xf4\x83W\x83\xba\x84\x1d\x84\x80\x84\xe3\x85\ +G\x85\xab\x86\x0e\x86r\x86\xd7\x87;\x87\x9f\x88\x04\x88\ +i\x88\xce\x893\x89\x99\x89\xfe\x8ad\x8a\xca\x8b0\x8b\ +\x96\x8b\xfc\x8cc\x8c\xca\x8d1\x8d\x98\x8d\xff\x8ef\x8e\ +\xce\x8f6\x8f\x9e\x90\x06\x90n\x90\xd6\x91?\x91\xa8\x92\ +\x11\x92z\x92\xe3\x93M\x93\xb6\x94 \x94\x8a\x94\xf4\x95\ +_\x95\xc9\x964\x96\x9f\x97\x0a\x97u\x97\xe0\x98L\x98\ +\xb8\x99$\x99\x90\x99\xfc\x9ah\x9a\xd5\x9bB\x9b\xaf\x9c\ +\x1c\x9c\x89\x9c\xf7\x9dd\x9d\xd2\x9e@\x9e\xae\x9f\x1d\x9f\ +\x8b\x9f\xfa\xa0i\xa0\xd8\xa1G\xa1\xb6\xa2&\xa2\x96\xa3\ +\x06\xa3v\xa3\xe6\xa4V\xa4\xc7\xa58\xa5\xa9\xa6\x1a\xa6\ +\x8b\xa6\xfd\xa7n\xa7\xe0\xa8R\xa8\xc4\xa97\xa9\xa9\xaa\ +\x1c\xaa\x8f\xab\x02\xabu\xab\xe9\xac\x5c\xac\xd0\xadD\xad\ +\xb8\xae-\xae\xa1\xaf\x16\xaf\x8b\xb0\x00\xb0u\xb0\xea\xb1\ +`\xb1\xd6\xb2K\xb2\xc2\xb38\xb3\xae\xb4%\xb4\x9c\xb5\ +\x13\xb5\x8a\xb6\x01\xb6y\xb6\xf0\xb7h\xb7\xe0\xb8Y\xb8\ +\xd1\xb9J\xb9\xc2\xba;\xba\xb5\xbb.\xbb\xa7\xbc!\xbc\ +\x9b\xbd\x15\xbd\x8f\xbe\x0a\xbe\x84\xbe\xff\xbfz\xbf\xf5\xc0\ +p\xc0\xec\xc1g\xc1\xe3\xc2_\xc2\xdb\xc3X\xc3\xd4\xc4\ +Q\xc4\xce\xc5K\xc5\xc8\xc6F\xc6\xc3\xc7A\xc7\xbf\xc8\ +=\xc8\xbc\xc9:\xc9\xb9\xca8\xca\xb7\xcb6\xcb\xb6\xcc\ +5\xcc\xb5\xcd5\xcd\xb5\xce6\xce\xb6\xcf7\xcf\xb8\xd0\ +9\xd0\xba\xd1<\xd1\xbe\xd2?\xd2\xc1\xd3D\xd3\xc6\xd4\ +I\xd4\xcb\xd5N\xd5\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\xe0\xd8\ +d\xd8\xe8\xd9l\xd9\xf1\xdav\xda\xfb\xdb\x80\xdc\x05\xdc\ +\x8a\xdd\x10\xdd\x96\xde\x1c\xde\xa2\xdf)\xdf\xaf\xe06\xe0\ +\xbd\xe1D\xe1\xcc\xe2S\xe2\xdb\xe3c\xe3\xeb\xe4s\xe4\ +\xfc\xe5\x84\xe6\x0d\xe6\x96\xe7\x1f\xe7\xa9\xe82\xe8\xbc\xe9\ +F\xe9\xd0\xea[\xea\xe5\xebp\xeb\xfb\xec\x86\xed\x11\xed\ +\x9c\xee(\xee\xb4\xef@\xef\xcc\xf0X\xf0\xe5\xf1r\xf1\ +\xff\xf2\x8c\xf3\x19\xf3\xa7\xf44\xf4\xc2\xf5P\xf5\xde\xf6\ +m\xf6\xfb\xf7\x8a\xf8\x19\xf8\xa8\xf98\xf9\xc7\xfaW\xfa\ +\xe7\xfbw\xfc\x07\xfc\x98\xfd)\xfd\xba\xfeK\xfe\xdc\xff\ +m\xff\xff\ +\x00\x00\x08A\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + icon / o\ +utliner / slice \ +/ not active - s\ +aved copy\x0d\x0a Cre\ +ated with Sketch\ +.\x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\ + \x0d\x0a \ + \x0d\x0a \ + \ +\x0d\x0a \ + \x0d\x0a\ + \x0d\x0a \ + \x0d\x0a\x0d\x0a\ +\ +\x00\x00\x09\xa3\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Artboard\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a\ + \ +\x0d\x0a \ +\x0d\x0a \ +\x0d\x0a \x0d\x0a\ + \x0d\x0a\ +\x0d\x0a\ +\x00\x00\x02\xb6\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a \ + Icons / \ +System / Carat /\ + White / Default\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a <\ +polygon id=\x22Tria\ +ngle\x22 fill=\x22#FFF\ +FFF\x22 transform=\x22\ +translate(8.0000\ +00, 8.000000) sc\ +ale(1, -1) rotat\ +e(90.000000) tra\ +nslate(-8.000000\ +, -8.000000) \x22 p\ +oints=\x228 6 12 10\ + 4 10\x22>\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x8e\x0c\ +I\ +I*\x00\x08\x00\x00\x00\x19\x00\xfe\x00\x04\x00\x01\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x01\x01\x03\x00\x01\x00\x00\x00`\x00\x00\x00\x02\x01\x03\ +\x00\x04\x00\x00\x00:\x01\x00\x00\x03\x01\x03\x00\x01\x00\x00\ +\x00\x05\x00\x00\x00\x06\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00\x11\x01\x04\x00\x01\x00\x00\x00`V\x00\x00\x12\x01\x03\ +\x00\x01\x00\x00\x00\x01\x00\x00\x00\x15\x01\x03\x00\x01\x00\x00\ +\x00\x04\x00\x00\x00\x16\x01\x03\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x17\x01\x04\x00\x01\x00\x00\x00\xd9\x15\x00\x00\x1a\x01\x05\ +\x00\x01\x00\x00\x00B\x01\x00\x00\x1b\x01\x05\x00\x01\x00\x00\ +\x00J\x01\x00\x00\x1c\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\ +\x00(\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\x001\x01\x02\ +\x00\x22\x00\x00\x00R\x01\x00\x002\x01\x02\x00\x14\x00\x00\ +\x00t\x01\x00\x00=\x01\x03\x00\x01\x00\x00\x00\x02\x00\x00\ +\x00R\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\xbc\x02\x01\ +\x00\x1a;\x00\x00\x88\x01\x00\x00\xbb\x83\x07\x00\x0f\x00\x00\ +\x00\xa2<\x00\x00I\x86\x01\x00f\x0d\x00\x00\xb2<\x00\ +\x00i\x87\x04\x00\x01\x00\x00\x00\x0a\x0a \x0a\ + \x0a \ + Adobe Photosho\ +p CC 2015.5 (Win\ +dows)\x0a \ + 2017-03-07T11:3\ +2:29-08:00\x0a \ + 2017-04-04T\ +11:28-07:00\x0a \ + 2017-04-\ +04T11:28-07:00\x0a image/tiff\ +\x0a \ + 3\x0a sRGB IEC61966-\ +2.1\x0a \ + \x0a \x0a \ + a\ +dobe:docid:photo\ +shop:94a27cdb-04\ +33-11e7-b02d-9f8\ +4d9f5a326\x0a \ + adobe:\ +docid:photoshop:\ +acaee4ff-1960-11\ +e7-bae7-e6e7a5cd\ +2814\x0a \ + \x0a \x0a \ + xmp.iid:\ +fa5f33a4-5729-d3\ +41-9ab8-5edce3a2\ +68a4\x0a \ + adobe:docid:p\ +hotoshop:696e80e\ +4-1964-11e7-8c4b\ +-d6fec7ab83c2\ +\x0a xmp.did:ca77\ +1a70-f965-e14f-9\ +103-360465543dbf\ +\x0a \ + \x0a \ + \x0a \ + \x0a \ + crea\ +ted\x0a \ + xmp.iid:c\ +a771a70-f965-e14\ +f-9103-360465543\ +dbf\x0a \ + 2017-03-07T\ +11:32:29-08:00\x0a \ + Adobe Photosh\ +op CC 2015.5 (Wi\ +ndows)\x0a \ + \x0a \ + \x0a \ + saved\x0a \ + \ +xmp.iid:16fdf09c\ +-857d-944e-9783-\ +e127cb1b9cf4\x0a\ + \ + 20\ +17-03-08T11:37:4\ +5-08:00\x0a \ + Adob\ +e Photoshop CC 2\ +015.5 (Windows)<\ +/stEvt:softwareA\ +gent>\x0a \ + /\x0a \ + \x0a \ + \x0a \ + saved\x0a \ + xmp.\ +iid:fa5f33a4-572\ +9-d341-9ab8-5edc\ +e3a268a4\x0a \ + 2017-0\ +4-04T11:28-07:00\ +\x0a \ + \ +Adobe Photo\ +shop CC 2017 (Wi\ +ndows)\x0a \ + <\ +stEvt:changed>/<\ +/stEvt:changed>\x0a\ + <\ +/rdf:li>\x0a \ + \x0a\ + \x0a \ +\x0a \ +\x0a\x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \x0a\x1c\x01Z\x00\x03\x1b%G\x1c\x02\x00\x00\x02\x00\x00\ +\x008BIM\x04%\x00\x00\x00\x00\x00\x10\xcd\xcf\xfa\ +}\xa8\xc7\xbe\x09\x05pv\xae\xaf\x05\xc3N8BI\ +M\x04:\x00\x00\x00\x00\x00\xe5\x00\x00\x00\x10\x00\x00\x00\ +\x01\x00\x00\x00\x00\x00\x0bprintOutp\ +ut\x00\x00\x00\x05\x00\x00\x00\x00PstSbo\ +ol\x01\x00\x00\x00\x00Inteenum\x00\ +\x00\x00\x00Inte\x00\x00\x00\x00Clrm\x00\ +\x00\x00\x0fprintSixteenB\ +itbool\x00\x00\x00\x00\x0bprint\ +erNameTEXT\x00\x00\x00\x01\x00\x00\ +\x00\x00\x00\x0fprintProofSe\ +tupObjc\x00\x00\x00\x0c\x00P\x00r\x00\ +o\x00o\x00f\x00 \x00S\x00e\x00t\x00u\x00\ +p\x00\x00\x00\x00\x00\x0aproofSetu\ +p\x00\x00\x00\x01\x00\x00\x00\x00Bltnenu\ +m\x00\x00\x00\x0cbuiltinProo\ +f\x00\x00\x00\x09proofCMYK\x008\ +BIM\x04;\x00\x00\x00\x00\x02-\x00\x00\x00\x10\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x12printOu\ +tputOptions\x00\x00\x00\x17\x00\ +\x00\x00\x00Cptnbool\x00\x00\x00\x00\x00\ +Clbrbool\x00\x00\x00\x00\x00Rgs\ +Mbool\x00\x00\x00\x00\x00CrnCbo\ +ol\x00\x00\x00\x00\x00CntCbool\x00\ +\x00\x00\x00\x00Lblsbool\x00\x00\x00\x00\ +\x00Ngtvbool\x00\x00\x00\x00\x00Em\ +lDbool\x00\x00\x00\x00\x00Intrb\ +ool\x00\x00\x00\x00\x00BckgObjc\ +\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00RGBC\x00\x00\ +\x00\x03\x00\x00\x00\x00Rd doub@o\ +\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00Grn do\ +ub@o\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00Bl\ + doub@o\xe0\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00BrdTUntF#Rlt\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Bld Un\ +tF#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00RsltUntF#Pxl@b\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0avector\ +Databool\x01\x00\x00\x00\x00PgP\ +senum\x00\x00\x00\x00PgPs\x00\x00\x00\ +\x00PgPC\x00\x00\x00\x00LeftUnt\ +F#Rlt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00Top UntF#Rlt\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00Scl Unt\ +F#Prc@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x10cropWhenPrintin\ +gbool\x00\x00\x00\x00\x0ecropRe\ +ctBottomlong\x00\x00\x00\x00\ +\x00\x00\x00\x0ccropRectLeft\ +long\x00\x00\x00\x00\x00\x00\x00\x0dcrop\ +RectRightlong\x00\x00\x00\ +\x00\x00\x00\x00\x0bcropRectTop\ +long\x00\x00\x00\x00\x008BIM\x03\xed\x00\ +\x00\x00\x00\x00\x10\x00\x90\x00\x00\x00\x01\x00\x01\x00\x90\x00\ +\x00\x00\x01\x00\x018BIM\x04&\x00\x00\x00\x00\x00\ +\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\x80\x00\x008\ +BIM\x03\xee\x00\x00\x00\x00\x00\x0d\x0cTran\ +sparency\x008BIM\x04\x15\x00\ +\x00\x00\x00\x00\x1e\x00\x00\x00\x0d\x00T\x00r\x00a\x00\ +n\x00s\x00p\x00a\x00r\x00e\x00n\x00c\x00\ +y\x00\x008BIM\x045\x00\x00\x00\x00\x00\x11\x00\ +\x00\x00\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00d\x01\ +\x008BIM\x04\x1d\x00\x00\x00\x00\x00\x04\x00\x00\x00\ +\x008BIM\x03\xf2\x00\x00\x00\x00\x00\x0a\x00\x00\xff\ +\xff\xff\xff\xff\xff\x00\x008BIM\x04\x0d\x00\x00\x00\ +\x00\x00\x04\x00\x00\x00\x1e8BIM\x04\x19\x00\x00\x00\ +\x00\x00\x04\x00\x00\x00\x1e8BIM\x03\xf3\x00\x00\x00\ +\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x008BI\ +M'\x10\x00\x00\x00\x00\x00\x0a\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x018BIM\x03\xf5\x00\x00\x00\x00\x00H\x00\ +/ff\x00\x01\x00lff\x00\x06\x00\x00\x00\x00\x00\ +\x01\x00/ff\x00\x01\x00\xa1\x99\x9a\x00\x06\x00\x00\x00\ +\x00\x00\x01\x002\x00\x00\x00\x01\x00Z\x00\x00\x00\x06\x00\ +\x00\x00\x00\x00\x01\x005\x00\x00\x00\x01\x00-\x00\x00\x00\ +\x06\x00\x00\x00\x00\x00\x018BIM\x03\xf8\x00\x00\x00\ +\x00\x00p\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\ +\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\ +\xe8\x00\x008BIM\x04\x00\x00\x00\x00\x00\x00\x02\x00\ +\x008BIM\x04\x02\x00\x00\x00\x00\x00\x02\x00\x008\ +BIM\x040\x00\x00\x00\x00\x00\x01\x01\x008BI\ +M\x04-\x00\x00\x00\x00\x00\x06\x00\x01\x00\x00\x00\x038\ +BIM\x04\x08\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\ +\x00\x02@\x00\x00\x02@\x00\x00\x00\x008BIM\x04\ +\x1e\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008BIM\x04\ +\x1a\x00\x00\x00\x00\x035\x00\x00\x00\x06\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\x00\x00\x00\x00\ +\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00`\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00null\x00\x00\x00\x02\x00\x00\ +\x00\x06boundsObjc\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00Rct1\x00\x00\x00\x04\x00\x00\ +\x00\x00Top long\x00\x00\x00\x00\x00\x00\ +\x00\x00Leftlong\x00\x00\x00\x00\x00\x00\ +\x00\x00Btomlong\x00\x00\x00`\x00\x00\ +\x00\x00Rghtlong\x00\x00\x00`\x00\x00\ +\x00\x06slicesVlLs\x00\x00\x00\x01\ +Objc\x00\x00\x00\x01\x00\x00\x00\x00\x00\x05sl\ +ice\x00\x00\x00\x12\x00\x00\x00\x07slice\ +IDlong\x00\x00\x00\x00\x00\x00\x00\x07gr\ +oupIDlong\x00\x00\x00\x00\x00\x00\x00\ +\x06originenum\x00\x00\x00\x0cE\ +SliceOrigin\x00\x00\x00\x0da\ +utoGenerated\x00\x00\x00\x00\ +Typeenum\x00\x00\x00\x0aESli\ +ceType\x00\x00\x00\x00Img \x00\x00\ +\x00\x06boundsObjc\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00Rct1\x00\x00\x00\x04\x00\x00\ +\x00\x00Top long\x00\x00\x00\x00\x00\x00\ +\x00\x00Leftlong\x00\x00\x00\x00\x00\x00\ +\x00\x00Btomlong\x00\x00\x00`\x00\x00\ +\x00\x00Rghtlong\x00\x00\x00`\x00\x00\ +\x00\x03urlTEXT\x00\x00\x00\x01\x00\x00\x00\ +\x00\x00\x00nullTEXT\x00\x00\x00\x01\x00\ +\x00\x00\x00\x00\x00MsgeTEXT\x00\x00\x00\ +\x01\x00\x00\x00\x00\x00\x06altTagTEX\ +T\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0ecellT\ +extIsHTMLbool\x01\x00\x00\ +\x00\x08cellTextTEXT\x00\x00\ +\x00\x01\x00\x00\x00\x00\x00\x09horzAlig\ +nenum\x00\x00\x00\x0fESliceH\ +orzAlign\x00\x00\x00\x07defa\ +ult\x00\x00\x00\x09vertAlign\ +enum\x00\x00\x00\x0fESliceVe\ +rtAlign\x00\x00\x00\x07defau\ +lt\x00\x00\x00\x0bbgColorTyp\ +eenum\x00\x00\x00\x11ESliceB\ +GColorType\x00\x00\x00\x00No\ +ne\x00\x00\x00\x09topOutsetl\ +ong\x00\x00\x00\x00\x00\x00\x00\x0aleftO\ +utsetlong\x00\x00\x00\x00\x00\x00\x00\ +\x0cbottomOutsetlon\ +g\x00\x00\x00\x00\x00\x00\x00\x0brightOu\ +tsetlong\x00\x00\x00\x00\x008BI\ +M\x04(\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x02?\xf0\x00\ +\x00\x00\x00\x00\x008BIM\x04\x14\x00\x00\x00\x00\x00\ +\x04\x00\x00\x00\x0d8BIM\x04\x0c\x00\x00\x00\x00\x03\ +\xfb\x00\x00\x00\x01\x00\x00\x000\x00\x00\x000\x00\x00\x00\ +\x90\x00\x00\x1b\x00\x00\x00\x03\xdf\x00\x18\x00\x01\xff\xd8\xff\ +\xed\x00\x0cAdobe_CM\x00\x01\xff\xee\x00\ +\x0eAdobe\x00d\x80\x00\x00\x00\x01\xff\xdb\x00\ +\x84\x00\x0c\x08\x08\x08\x09\x08\x0c\x09\x09\x0c\x11\x0b\x0a\x0b\ +\x11\x15\x0f\x0c\x0c\x0f\x15\x18\x13\x13\x15\x13\x13\x18\x11\x0c\ +\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x01\x0d\x0b\x0b\x0d\x0e\x0d\x10\x0e\x0e\x10\x14\x0e\x0e\ +\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\x0c\x0c\x11\x11\ +\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\ +\x0c\x0c\x0c\xff\xc0\x00\x11\x08\x000\x000\x03\x01\x22\x00\ +\x02\x11\x01\x03\x11\x01\xff\xdd\x00\x04\x00\x03\xff\xc4\x01?\ +\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\ +\x00\x03\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x01\x00\x01\ +\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\ +\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x10\x00\x01\x04\x01\x03\ +\x02\x04\x02\x05\x07\x06\x08\x05\x03\x0c3\x01\x00\x02\x11\x03\ +\x04!\x121\x05AQa\x13\x22q\x812\x06\x14\x91\ +\xa1\xb1B#$\x15R\xc1b34r\x82\xd1C\x07\ +%\x92S\xf0\xe1\xf1cs5\x16\xa2\xb2\x83&D\x93\ +TdE\xc2\xa3t6\x17\xd2U\xe2e\xf2\xb3\x84\xc3\ +\xd3u\xe3\xf3F'\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\ +\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf6\ +7GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\xf7\x11\x00\x02\ +\x02\x01\x02\x04\x04\x03\x04\x05\x06\x07\x07\x06\x055\x01\x00\ +\x02\x11\x03!1\x12\x04AQaq\x22\x13\x052\x81\ +\x91\x14\xa1\xb1B#\xc1R\xd1\xf03$b\xe1r\x82\ +\x92CS\x15cs4\xf1%\x06\x16\xa2\xb2\x83\x07&\ +5\xc2\xd2D\x93T\xa3\x17dEU6te\xe2\xf2\ +\xb3\x84\xc3\xd3u\xe3\xf3F\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\ +\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\xc6\xd6\ +\xe6\xf6'7GWgw\x87\x97\xa7\xb7\xc7\xff\xda\x00\ +\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xf4<\xdc\xcc\x96\ +d\x9a\xa9;@\x80\x00\x00\x92O\xc6P\xfd~\xab\xe0\ +\xff\x00\xfbl\x7f\xe4R\xc9\xff\x00\x95\x1b\xfdz\xff\x00\ +\xef\xabN\xd79\x95\xb9\xcdi{\x9a\x09\x0d\x1c\x92\x92\ +\x9c\xa7e\xf5\x16}79\xb3\xc6\xe6\xb4~V\xa7n\ +OSp\x96\x978\x1e\xe1\x80\x8f\xc1\xaa\xc6.\x0bl\ +g\xaf\x96\xd2\xfb\xac\xd4\xee\x9d\x07a\xb53\xea\x18Y\ +5>\x92EW81\xec\x99\x12x))\x0f\xaf\xd5\ +|\x1f\xfe`\xff\x00\xc8\xa2`fdY\x91\xe9Zw\ +\x02\x0f \x02\x08\xfe\xaa\xd2Y\x18?\xf2\x81\xff\x00\xae\ +~T\x94\xff\x00\xff\xd0\xef\xf2\x7f\xe5F\xff\x00^\xbf\ +\xfb\xea\xd0\xc9\xca\xaf\x19\xa0\xbeIq\x86\xb5\xa2IY\ +\xf9?\xf2\xa3\x7f\xaf_\xfd\xf5Y\xea\x9e\xd6Soz\ +\xec\x07\xe5\xfe\xa1%-\xf6\xfb\xdd\xfc\xde+\xcf\x81v\ +\x9f\xc1\x0a\xe6\xf5\x0b\x9c\xcb\x9f[X)\x975\xa4\xce\ +\xbc\xce\x9f\x05k<\xde1\x9c\xeaL\x11\xab\x88\xe7h\ +\xfa[R\xc1u\x96b5\xd6\x9d\xc5\xd3\x07\xbcN\x9b\ +\x92S\x01E\x01L\x01\ +R\x01Y\x01`\x01g\x01n\x01u\x01|\x01\x83\x01\ +\x8b\x01\x92\x01\x9a\x01\xa1\x01\xa9\x01\xb1\x01\xb9\x01\xc1\x01\ +\xc9\x01\xd1\x01\xd9\x01\xe1\x01\xe9\x01\xf2\x01\xfa\x02\x03\x02\ +\x0c\x02\x14\x02\x1d\x02&\x02/\x028\x02A\x02K\x02\ +T\x02]\x02g\x02q\x02z\x02\x84\x02\x8e\x02\x98\x02\ +\xa2\x02\xac\x02\xb6\x02\xc1\x02\xcb\x02\xd5\x02\xe0\x02\xeb\x02\ +\xf5\x03\x00\x03\x0b\x03\x16\x03!\x03-\x038\x03C\x03\ +O\x03Z\x03f\x03r\x03~\x03\x8a\x03\x96\x03\xa2\x03\ +\xae\x03\xba\x03\xc7\x03\xd3\x03\xe0\x03\xec\x03\xf9\x04\x06\x04\ +\x13\x04 \x04-\x04;\x04H\x04U\x04c\x04q\x04\ +~\x04\x8c\x04\x9a\x04\xa8\x04\xb6\x04\xc4\x04\xd3\x04\xe1\x04\ +\xf0\x04\xfe\x05\x0d\x05\x1c\x05+\x05:\x05I\x05X\x05\ +g\x05w\x05\x86\x05\x96\x05\xa6\x05\xb5\x05\xc5\x05\xd5\x05\ +\xe5\x05\xf6\x06\x06\x06\x16\x06'\x067\x06H\x06Y\x06\ +j\x06{\x06\x8c\x06\x9d\x06\xaf\x06\xc0\x06\xd1\x06\xe3\x06\ +\xf5\x07\x07\x07\x19\x07+\x07=\x07O\x07a\x07t\x07\ +\x86\x07\x99\x07\xac\x07\xbf\x07\xd2\x07\xe5\x07\xf8\x08\x0b\x08\ +\x1f\x082\x08F\x08Z\x08n\x08\x82\x08\x96\x08\xaa\x08\ +\xbe\x08\xd2\x08\xe7\x08\xfb\x09\x10\x09%\x09:\x09O\x09\ +d\x09y\x09\x8f\x09\xa4\x09\xba\x09\xcf\x09\xe5\x09\xfb\x0a\ +\x11\x0a'\x0a=\x0aT\x0aj\x0a\x81\x0a\x98\x0a\xae\x0a\ +\xc5\x0a\xdc\x0a\xf3\x0b\x0b\x0b\x22\x0b9\x0bQ\x0bi\x0b\ +\x80\x0b\x98\x0b\xb0\x0b\xc8\x0b\xe1\x0b\xf9\x0c\x12\x0c*\x0c\ +C\x0c\x5c\x0cu\x0c\x8e\x0c\xa7\x0c\xc0\x0c\xd9\x0c\xf3\x0d\ +\x0d\x0d&\x0d@\x0dZ\x0dt\x0d\x8e\x0d\xa9\x0d\xc3\x0d\ +\xde\x0d\xf8\x0e\x13\x0e.\x0eI\x0ed\x0e\x7f\x0e\x9b\x0e\ +\xb6\x0e\xd2\x0e\xee\x0f\x09\x0f%\x0fA\x0f^\x0fz\x0f\ +\x96\x0f\xb3\x0f\xcf\x0f\xec\x10\x09\x10&\x10C\x10a\x10\ +~\x10\x9b\x10\xb9\x10\xd7\x10\xf5\x11\x13\x111\x11O\x11\ +m\x11\x8c\x11\xaa\x11\xc9\x11\xe8\x12\x07\x12&\x12E\x12\ +d\x12\x84\x12\xa3\x12\xc3\x12\xe3\x13\x03\x13#\x13C\x13\ +c\x13\x83\x13\xa4\x13\xc5\x13\xe5\x14\x06\x14'\x14I\x14\ +j\x14\x8b\x14\xad\x14\xce\x14\xf0\x15\x12\x154\x15V\x15\ +x\x15\x9b\x15\xbd\x15\xe0\x16\x03\x16&\x16I\x16l\x16\ +\x8f\x16\xb2\x16\xd6\x16\xfa\x17\x1d\x17A\x17e\x17\x89\x17\ +\xae\x17\xd2\x17\xf7\x18\x1b\x18@\x18e\x18\x8a\x18\xaf\x18\ +\xd5\x18\xfa\x19 \x19E\x19k\x19\x91\x19\xb7\x19\xdd\x1a\ +\x04\x1a*\x1aQ\x1aw\x1a\x9e\x1a\xc5\x1a\xec\x1b\x14\x1b\ +;\x1bc\x1b\x8a\x1b\xb2\x1b\xda\x1c\x02\x1c*\x1cR\x1c\ +{\x1c\xa3\x1c\xcc\x1c\xf5\x1d\x1e\x1dG\x1dp\x1d\x99\x1d\ +\xc3\x1d\xec\x1e\x16\x1e@\x1ej\x1e\x94\x1e\xbe\x1e\xe9\x1f\ +\x13\x1f>\x1fi\x1f\x94\x1f\xbf\x1f\xea \x15 A \ +l \x98 \xc4 \xf0!\x1c!H!u!\xa1!\ +\xce!\xfb\x22'\x22U\x22\x82\x22\xaf\x22\xdd#\x0a#\ +8#f#\x94#\xc2#\xf0$\x1f$M$|$\ +\xab$\xda%\x09%8%h%\x97%\xc7%\xf7&\ +'&W&\x87&\xb7&\xe8'\x18'I'z'\ +\xab'\xdc(\x0d(?(q(\xa2(\xd4)\x06)\ +8)k)\x9d)\xd0*\x02*5*h*\x9b*\ +\xcf+\x02+6+i+\x9d+\xd1,\x05,9,\ +n,\xa2,\xd7-\x0c-A-v-\xab-\xe1.\ +\x16.L.\x82.\xb7.\xee/$/Z/\x91/\ +\xc7/\xfe050l0\xa40\xdb1\x121J1\ +\x821\xba1\xf22*2c2\x9b2\xd43\x0d3\ +F3\x7f3\xb83\xf14+4e4\x9e4\xd85\ +\x135M5\x875\xc25\xfd676r6\xae6\ +\xe97$7`7\x9c7\xd78\x148P8\x8c8\ +\xc89\x059B9\x7f9\xbc9\xf9:6:t:\ +\xb2:\xef;-;k;\xaa;\xe8<' >`>\ +\xa0>\xe0?!?a?\xa2?\xe2@#@d@\ +\xa6@\xe7A)AjA\xacA\xeeB0BrB\ +\xb5B\xf7C:C}C\xc0D\x03DGD\x8aD\ +\xceE\x12EUE\x9aE\xdeF\x22FgF\xabF\ +\xf0G5G{G\xc0H\x05HKH\x91H\xd7I\ +\x1dIcI\xa9I\xf0J7J}J\xc4K\x0cK\ +SK\x9aK\xe2L*LrL\xbaM\x02MJM\ +\x93M\xdcN%NnN\xb7O\x00OIO\x93O\ +\xddP'PqP\xbbQ\x06QPQ\x9bQ\xe6R\ +1R|R\xc7S\x13S_S\xaaS\xf6TBT\ +\x8fT\xdbU(UuU\xc2V\x0fV\x5cV\xa9V\ +\xf7WDW\x92W\xe0X/X}X\xcbY\x1aY\ +iY\xb8Z\x07ZVZ\xa6Z\xf5[E[\x95[\ +\xe5\x5c5\x5c\x86\x5c\xd6]']x]\xc9^\x1a^\ +l^\xbd_\x0f_a_\xb3`\x05`W`\xaa`\ +\xfcaOa\xa2a\xf5bIb\x9cb\xf0cCc\ +\x97c\xebd@d\x94d\xe9e=e\x92e\xe7f\ +=f\x92f\xe8g=g\x93g\xe9h?h\x96h\ +\xeciCi\x9ai\xf1jHj\x9fj\xf7kOk\ +\xa7k\xfflWl\xafm\x08m`m\xb9n\x12n\ +kn\xc4o\x1eoxo\xd1p+p\x86p\xe0q\ +:q\x95q\xf0rKr\xa6s\x01s]s\xb8t\ +\x14tpt\xccu(u\x85u\xe1v>v\x9bv\ +\xf8wVw\xb3x\x11xnx\xccy*y\x89y\ +\xe7zFz\xa5{\x04{c{\xc2|!|\x81|\ +\xe1}A}\xa1~\x01~b~\xc2\x7f#\x7f\x84\x7f\ +\xe5\x80G\x80\xa8\x81\x0a\x81k\x81\xcd\x820\x82\x92\x82\ +\xf4\x83W\x83\xba\x84\x1d\x84\x80\x84\xe3\x85G\x85\xab\x86\ +\x0e\x86r\x86\xd7\x87;\x87\x9f\x88\x04\x88i\x88\xce\x89\ +3\x89\x99\x89\xfe\x8ad\x8a\xca\x8b0\x8b\x96\x8b\xfc\x8c\ +c\x8c\xca\x8d1\x8d\x98\x8d\xff\x8ef\x8e\xce\x8f6\x8f\ +\x9e\x90\x06\x90n\x90\xd6\x91?\x91\xa8\x92\x11\x92z\x92\ +\xe3\x93M\x93\xb6\x94 \x94\x8a\x94\xf4\x95_\x95\xc9\x96\ +4\x96\x9f\x97\x0a\x97u\x97\xe0\x98L\x98\xb8\x99$\x99\ +\x90\x99\xfc\x9ah\x9a\xd5\x9bB\x9b\xaf\x9c\x1c\x9c\x89\x9c\ +\xf7\x9dd\x9d\xd2\x9e@\x9e\xae\x9f\x1d\x9f\x8b\x9f\xfa\xa0\ +i\xa0\xd8\xa1G\xa1\xb6\xa2&\xa2\x96\xa3\x06\xa3v\xa3\ +\xe6\xa4V\xa4\xc7\xa58\xa5\xa9\xa6\x1a\xa6\x8b\xa6\xfd\xa7\ +n\xa7\xe0\xa8R\xa8\xc4\xa97\xa9\xa9\xaa\x1c\xaa\x8f\xab\ +\x02\xabu\xab\xe9\xac\x5c\xac\xd0\xadD\xad\xb8\xae-\xae\ +\xa1\xaf\x16\xaf\x8b\xb0\x00\xb0u\xb0\xea\xb1`\xb1\xd6\xb2\ +K\xb2\xc2\xb38\xb3\xae\xb4%\xb4\x9c\xb5\x13\xb5\x8a\xb6\ +\x01\xb6y\xb6\xf0\xb7h\xb7\xe0\xb8Y\xb8\xd1\xb9J\xb9\ +\xc2\xba;\xba\xb5\xbb.\xbb\xa7\xbc!\xbc\x9b\xbd\x15\xbd\ +\x8f\xbe\x0a\xbe\x84\xbe\xff\xbfz\xbf\xf5\xc0p\xc0\xec\xc1\ +g\xc1\xe3\xc2_\xc2\xdb\xc3X\xc3\xd4\xc4Q\xc4\xce\xc5\ +K\xc5\xc8\xc6F\xc6\xc3\xc7A\xc7\xbf\xc8=\xc8\xbc\xc9\ +:\xc9\xb9\xca8\xca\xb7\xcb6\xcb\xb6\xcc5\xcc\xb5\xcd\ +5\xcd\xb5\xce6\xce\xb6\xcf7\xcf\xb8\xd09\xd0\xba\xd1\ +<\xd1\xbe\xd2?\xd2\xc1\xd3D\xd3\xc6\xd4I\xd4\xcb\xd5\ +N\xd5\xd1\xd6U\xd6\xd8\xd7\x5c\xd7\xe0\xd8d\xd8\xe8\xd9\ +l\xd9\xf1\xdav\xda\xfb\xdb\x80\xdc\x05\xdc\x8a\xdd\x10\xdd\ +\x96\xde\x1c\xde\xa2\xdf)\xdf\xaf\xe06\xe0\xbd\xe1D\xe1\ +\xcc\xe2S\xe2\xdb\xe3c\xe3\xeb\xe4s\xe4\xfc\xe5\x84\xe6\ +\x0d\xe6\x96\xe7\x1f\xe7\xa9\xe82\xe8\xbc\xe9F\xe9\xd0\xea\ +[\xea\xe5\xebp\xeb\xfb\xec\x86\xed\x11\xed\x9c\xee(\xee\ +\xb4\xef@\xef\xcc\xf0X\xf0\xe5\xf1r\xf1\xff\xf2\x8c\xf3\ +\x19\xf3\xa7\xf44\xf4\xc2\xf5P\xf5\xde\xf6m\xf6\xfb\xf7\ +\x8a\xf8\x19\xf8\xa8\xf98\xf9\xc7\xfaW\xfa\xe7\xfbw\xfc\ +\x07\xfc\x98\xfd)\xfd\xba\xfeK\xfe\xdc\xffm\xff\xff\x80\ +\x00 P8$\x16\x0d\x07\x84BaP\xb8d6\x1d\ +\x0f\x88DbQ8\xa4V-\x17\x8cFcQ\xb8\xe4\ +v=\x1f\x90HdR9$\x96M'\x94JeR\ +\xb9d\xb6]/\x98A\x00\x930(*l\x0c\x9c\x03\ +\x02\x13`P0\x07?\x02?\xe8O\xe8X\x06 \x01\ +\xa4\x00hO\xfa$\x1a\x93H\x82R\xdf\xf0J}J\ +\x06\xff\xa4\x80\xa9t\xd0\x05\x1a\x07U\xa5\xd5\xeb\xf4\xfa\ +\x8d\x86\x05S\x81S\xe9VhK\xfc\x05o\x01\xd5\xaa\ +\xf6\xca\x8d~\x0fh\xb4\xd2\x00P\x8a\x95\x92\xefK\xb0\ +P\xacW\x9aM\xca\x9dI\xbe]05\xca\xf0\x02\x98\ +\xfe\xc8>\xf2O\xa7\xa6U\xe2\xf3\xcc<2O\xb7\xce\ +\x1aSO\x07\xe8Bcm!\x10\x87\xa7)\x89\xb5C\ +\x00v\xb4#?\x01\x81*\x98\x88]\xf7i\x87\xa4g\ +\xb18+\x1e\xe7\x01\xb7\x8e\xed\xaa\x17:\x15\xfa\x0d\xba\ +\xde\xda\xf7\x98Ln;\x7f\xbe\xe2\xf0!\x99\xec\x0d\xe2\ +\xbbj\xe8T\xab\x18^\x7f6\x9f\x90\xc8uz\xbb>\ +\xcf\x81\xfc\xf8\xf4=\x9c\xbe\xb6\xf3#\xdc\xbaa|V\ +N\x0f\xa3c6\xfa\x94\x04\xbfAb\xcf\xf4\xe0)\xc0\ +\x03:r\x08-\xebzc\x03\xc1\x10L\x15\x05>\xe6\ +\x94\x1cc\x13p\x89\x04h\xc2\x86+\xee\x90\x00\xd0\xc8\ +\x10(C\x83 \xdb\x0f\x91\x00LD\x05\xc1q,M\ +\x13\xc5\x09B\xa4fE\x85\xf9\x17\x17\x8d\xa6\xfcdk\ +\xb9\x08\xb0;\x1b\x84\x84LtV5A0_\x14\xc8\ +\x12\x0c\x85!\xa2\xe7\xd4\x8c|\x93RI\x00SI\x84\ +|\x8c}\x1f(\xc4\x0a\x01\x07\xb2\xa8\x9aCK\x058\ +\x0f-\x812$\xbd/\xcc\x13\x0a\x04gL\x86\x08\xed\ +3\x8a\x87\x84\xd4v#\x09\x98\x08\x02\x8a\xf3\x88\xda7\ +\xce\x84\x5c\xa71O\x13\xcc\xf5\x03\x9d\x13\xe9\xc44P\ +\x02\x11\xc9A\x9b\xa8\xc42\x03\x01\x03-\x14?\x0c\x14\ +h\xf0\xe3Ot\x8d%I\xa3\xe7\x95,w\x0c\x94\xc8\ +|oS\x86\xad\x0d\x0d\x0d\x95\x09\x0e\xfe\x8b#}!\ +JU\x15MT\x873\x07\x99\xe1E\x0c\xa1\xf1\xb9Y\ +\x9ah\xc4\xb6\x03\x810\xf8\xdaDN\x22\xb8\xdbS\xc4\ +\xae\x13\x9bU\xc8q\xab\xae\xe1\xa1V\x15UV\x9e\x14\ +\xc8\xc8\x1e\x9b\xb6\x89\xa9O\xd1\x03u\xacD\xd7\xb5\xfb\ +\xa4\x89\xaaF\xdd\xbch\x9e\xb7\x09\xe6\xea\xcav\x13\xa8\ +\xe0\x1f\xb7I\xfb;\xb8\x8e\xdb\xb2\xf29Wr\xd4\x82\ +<\xc8E!C\x81\x17\xc8\x10\x05P\xe0<\xdc\x02\xa0\ +\x97Q\xf8|\xe0\x87\xc6\x08|\x9e\xefA\xf0{C\ +<\x01\x08\x0eli\xb9\xd4\xf2k@p\x12xa\xfd\ +\xfd\x06\xe5&+\xe0\x80\x9b\x12\xf0L>\x8e\xe8,:\ +]\xbb\xe7\x02\x0f\xa5\x17\xbe\xc7\xdc\xfc\x08z\xcc\x80!\ +\x9c 6\x11\xb44V\xa0\x08yn\x89\xcb\x198F\ +\x10\x10p\xd2\x18\xe9\x85\x90\x00\xb0\x1c%!\xc0\xb9p\ +\xa0\xb5\xc3\xb9\xe1\x93\x0f\xc5\xda\xba\x09\x0b\xd5T\xc1\xa0\ +(\x22\xa2@\xadw\xc0\xc8\x1f8\x86J\xf0\xc1\xfc&\ +\x85\x0eA\xfc*(\x1b\x08\x08s\xd0QOI\xea&\ +\x10\xef\x17\xc4\xa8U\x8cA\xa8\x8c\x0c\xb9\x5c\ +/b!\x16_\xf1,\x1f8\xa0\xc6\x1f#\xb83\x08\ +\x04`O\xcb\xd1\x0e%f\x00zRq\x19\xf5\x0a\xe0\ +c1\xc1\xe9\x1c\x5c#\xd4yK`y%\x1f\xbb.\ +\x8a\xcc\xceM\x10\xb6n?\x07\xdb\xd1\x93\xc9\x00\x12M\ +\xd0Z('\x00\xc8_@(\x88\x0bi\xcc)\x04,\ +\xe9\x0c\xcc\x1c{\x92\x84\xa6\x16\xa7\x80qZ\xc1\xb8D\ +\xb6\xe2\x1e\xa5\x87\x90\xef\x0a\x93\xec\x14Aa\xdc:\x93\ +\xd3\xb9wb\xb2Z<\x133$\xdf\xb4T\x013\xcd\ +l'(Z>\xc7\xd3\xd1zcQ\xea\xa2\x91%E\ +\xc5\xb0:\xa3A$\x88\x0b\xaa<*C\xfd!\x0b\xc8\ +]\x04\x1a@l\x10\xc4\x1d)\x14\x80N\x96\x01\x82 \ +-)\x80\xa1\x10\x14\xcc0P\x17\x01!El\xc7\x06\ +3&H\x99\x96N\xe3\x5c|\x96yH\x82L\xbf\xd7\ +\x9ed\xe2\x84\xdbD\xa0Z\xa6\x01\xb9\x04+F\xcb\xdc\ +!\x83\xaa\xaa\x0eEH\x0c\x07\x8dY\x1d\xa9\x00\x17U\ +\xd0t\x92D\xd0\xc0_\xf2l\xc9\xab\xd0X8\xab@\ +\xdbL3\x12\x0e\xd3\xaaxF\x9f\x94P\x8aSE\xc9\ +B\xc275\xc7\xdb'\xa2tU\x05\xa8\x00\xd0 \x96\ +p}\x22\x09`C\x06\x81]a\xc4\xcaa\x0eV,\ +G\xcf\x00\xb4\x1c\x08\x84\xe0\x14\x02\x22\x1c\x09@\xf2\x98\ +]\xcd8\xad\xd4\x19WP\x86U4^\x5c\x0d#\x8f\ +\xfe(W\xb4\x13\x22\xaax\xd8w$2\x7f\x0e\xa48\ +\x14\x01 \xf6\xb6C\xcd0\xaf\x81Om\xc6\x80\x1f\xb7\ +@\x9c\x86\x0ek|8\x02\xb5\xc1\x05l({$H\ +\x8df\xa6D\xd5!P\x8a\x01W2.\xbe-\x0c\xd4\ +\xa8\xc4BN-+\x1bLK\xc9\ +\xc8\x0f\x15x\x0ck\x01\x5c\x0c\x06\x8e\x99Kd\xe8P\ +h\x8cU&~\x80\x90\x17\x15\x98Lk\xc0\x80\x22C\ +\x22\xf8w\x0a\xa2\xf7\x0e\x0a\xcb\x8c\xfa$`\x8c\x15\xef\ +\xb4\x18\x03\xc8\x9c'\xc8\x90\xe8\x12;\ +\xa0\xa4ne\xcc\xd7\x17}\xea\x12\xb8\xa1\x97\x04+\x06\ +\xcb\x94B_\xfc/\xba\xe4\xb4\xa7\xe0`*\x06\xb28\ +*\x06\xb688H\xf2\x09<\xc2P\xc6\xceB\xdd\xdb\ +\x88\xdc\xec,\x1a\x90?\x09\xe41\xf5\x06\xe1S\x9f\xc4\ +\x9d\xf0\xa6\xf7\xcadbw\xa3\x19\xab\xa2\xa2\xae\xc4j\ +\xea\xb9\xa78\x91)0C\xa4\xc1\x12\xb0\x08\x07\xfe\xed\ +\xe8\xb8\x92\xa34o>\x22\xfc\xfd\xa04\x13\xa8\xb9\x14\ +\xefCE\xbd\x11B\x9f\xcd\xd2\xc5\xc4P\xfb\xc2\xf8c\ +\x0c\xdf*\x91e\xb6\xdcS\x8d\x16\xb0\x07\x81)\x0c\x10\ +Z\xec1\x8b-|'\xb5\x08\x15\x98\xa0\xbfb\x03\xbc\ +\xbeB$\x94[\xcb\x04U|P\xcbDF\xef\xec\x5c\ +\xa2\x9a\xc5H\xc3\xb0p'\xb6\xc0\xc6\x90\x01\xd7n\x05\ +&\xce/\xc5~\x1f\x83s\x17\x12>\xfb\xf6C\xab\x8b\ +\x99\xb9\xd9e\x5c\xe2\xc5}\xb1\xc8>\x8d\xcc{Q1\ +8 \xf8&\xedxd\xbd&@~\xbdp\xbc\x0d\x86\ +\xc7\x01\x19\xdb\x8a#\xc4\x9c\xa7\x5c\x0c\xb6\x87[\xcf\x22\ +\xfcn\xe5\xb5\xaa\xc8\x9e\xf2\xd1\xfb\xd101\xd0D\x0a\ +\x85'\x19\x19\xcb\xf4\x86)\xc1\xbc5\xa5\x08[\x064\ +\x91!\x5c~\x0c\xef\xa2m\xf4\xe1:\x9b\x85\xe2\xb5w\ +Qx\x81\x12\xd5\xb0\x0bW\xf1D\xc1b\xc3\x90\x8e\xb1\ +\xc1\xc4\x88V\x01\x03\xa5k[\xe8\x98\xb7\xcc\x8deV\ +O\xa9\xf7f[\xb89{s\x90\xdeg\x099\xaf6\ +H+\xf7\x09\x8a\xc1\xb0\x06\xba\xc0 !\x91\xb8u\x8e\ +`\xbb\xd7\xc1\xa0\xec\xecC\x9e\xcc8\x0a\x07\xc1\xc8\xce\ +\xe9x\x9b\xaff!\xa0\xe1\xdb\xc4\x5cb\x0a\xa1\xafx\ +\x10n%\x01z\x92A\x06\xbd\xec!J\xb1,/$\ +\x02\xcc\x1c>\x0cl\xa54\xfa:\x07\x18\xc1\xf1B\xc4\ +Z\xf8\xd1D\x89\xb2\x88\x89\x15\x95\xbb\xba\x90W\x84\xe6\ +zA\x15\x92\xfc;\xca\x90N\xef'\xfb\xc9!'\x80\ +1\xd3\x81h\xe8\x06\xc1\x08\x1c\xf5@\x90\x0cz\xd0;\ +K\x00\x98\x18\xa7@\xfb[\xeb\x92F{\x86@\xba\xc4\ +!\xbc\xfa\x0e\x01\xb0L90\x8a\x89\x5c\xa2\xce*\xfd\ +\x95BH\xb2\xf8rY\xb2\x87\x8f\xad\x5c\x83\xb5\x87\xa1\ +!$\xf0\x06\xd4\xcfM\xd6\x00\xd0 \xd6\xe0\x98\x10\xfd\ +\xd0R\x8d\xc0\xe8$\xf5@p\x11\xfaZ\xa4\x8ac0\ +a\xfd@\xe6\xe2\x12\xc7r\x22?\x80\xaa\x91\xfet\x81\ +\xcf\x81\xdf\x0b\xfc\xc9\x14e\xbd+.\xffB\x04\x7f\xea\ +\xfc\x08.\xa2K\xe4\xa6kbr\x01\x80\x1e\x02\xf0\x14\ +\x03\xac \x02\xd0\x1a\xf6\x001\x01@.\x03\xabt\x03\ +\xe0O\x02@:i\xe0(\x03.8\xda\x8d\xb8\x0e\xad\ +\xbcm\x02X\xad\x87\xd6\xe8\x822\xf2\xe7\x88\xff\x22&\ +\xf3ev\xd9\xed\x18\xa9\x09:\xe2b\x5c_\xec\xd2\x06\ +\x8f\xc6\x04\x88\xf4\x04`W\x02\xa0N\xf4\xe0D4 \ +\x1e\x02E\xf4\x86\xc3\x5c\xfaB4\x16p\x90\x14\x01\x03\ +\x09`\xc3\x04g\xd0\xa7\x0e\xd0#\x0b\xea~\x8d\x96\xff\ +G\x22\x9ag\xf6\xff\xc0\x00\xf3\xe2P\xc7@\x1a\x02\x0e\ +\xe4\x0d`\x89\x0c`\xaaG\xa4\x7f\x08\xc2(\xaf\x0b\x88\ +ua\xd8\xc2\xc9\x14!\x84\xc8\x19\xc1\x84\x8a\x10\x9c\x83\ +p\xa0\xf8\x8azU\xca~ZM\x12f\x08\xae\xe9\xaa\ +\xc8\xa2\x0a$\xc6BD\xfc\x00H\xce\xc1\x1a\x16\x0e,\ +\x05p\xd0`KZ\xf0\xe1\xc4\xf7\xa1\xb0\xad\x01\xc4\x1b\ +O\x06\x1c!\xb2\xeb\x81\xcc~O\xecY\x80\xf1\x13\xe1\ +*\x09\xd1D\x0cB\x18\x1d1L\x1cez\x05\xa7\xe4\ +%\x0b\xe2\xf8PL#\x0e\x8c~\x90T\x22PX\x11\ +\x10\x5c#0\xb8#\xe5\xfe\x13\x91x\x18`Y\x17\xe0\ +nRf\xbe\x1d\xf1\x88\x1dc\xd6\x1c\xa1\xbc\x1cq\x94\ +\x1b\x8e<\x1a\xd1$\x1c\xf1\xa0\x1c1 \xb6A\xec\xb6\ +\x826\x9ea\x14\xdf\xc0\xea!\x87\xfe\x7f@]\x12\x81\ +\xb4%\x08\x8c\xc4,F}\xc9\x94\x5c!\xe4\xe8\xeeZ\ +\xa1O\xf8\xe9\x8eb\xa8\xea \xf9\xe8d$\x00\x9b\x1e\ +\xa0\xc0\xa4 \xfe\x13\xe2`<\xca\xb2\x1e!\xdb\x14\xc1\ +\xd2\x1cq\xc1\x12\x81\xb6mA\xda\x1d\x11\xfa\x1d\xc7V\ +\xeb\xab~\x9f\xc1\xd3\x1a\x81\xe8A,\xb8\x0dk\xc2\x12\ +\x82 N\x80\xde\x09a\x8b#al%\x10\x80\x02q\ +\xc9\x15\xe2.~N\x14x\xee\x5cy\x8d\xdf\x10\x02\x16\ +\xda+\xfe#\xd1\xc8j\xa0\x80\x0a\x020`Q\x88\x1d\ +\xf2\x18\x1c\xc1\xc0Pa\xc8q\xa7\x1d\x19A\xc6\x1br\ +t\x1b\xb1 \xfe\xc7n\xd2n\xfc\x17b \x11\xd2\x94\ +\x0e$\x98\x14\xcb\xd0$\xf1Z\x15\xad\xcb\x0bRH\xe5\ +\x92L\xe1\xae^\xc5\xa5\x86\xe20`\xba\xd0d#\xaa\ +\xc0\x180\xa2 \xf2j\x1daK,\xe1\x1c\x8c\xd1\xa0\ +\x1c\xe1\xc2\xaa\x81\xd4\x1c\x91W\x0d\x02\x16\xfc`F\x15\ +R\xec\x1a\xb0:!J\x9e\x13\x01\x0f/\xa0\xd3\x1cP\ +\x9e\xe4\xe8\x98\xf8\xa8\xa1\x16b#\x16\xaea+g\xfc\ +H\xec\xc5+\xe29\x09a\x02\x14 \x972`\xbc\xeb\ +h\xdc\x1c\xd3&\x09`<<\x01\xfb.K\x9eCN\ +\xaa\x1a\xef\xb0\xebB\x16E\x81\x98\x17\xea\xfc\x08QX\ +\xec\xcc\xa5\x0f\x024\xd9%c\x0a\xb0V\xa8r\xb3%\ +1\xde\xba\x83'\x10m\xa6#\xef\xd4\x0c \xf2\x0ds\ +\x80\x10\xcd\xf6\x1f\xc1\xfa\xe4 e0\xd3<\x22J\xc0\ +\x18\x0c\xdc!q\x8e\x1b\xd1R\xfd\xa2G\x04\x88<\xc4\ +\xb0\xb5\x16%\x9f9\x02\x1f1\x12\xb4#\x11r#\xc0\ +}< \x9b)A\x1c\x16B \xc3,6\xc3\xa4\x12\ +\x91\xe0\x8b=\xa0\xac\xf4\xe0C\x0d\x81\xa1>a\x88\x16\ +\xf3\xec\x14\xa1\xef?!\xea%\x91>\x0f\x01,\x9f`\ +\xa9/\xe2\x16k\xe7\xf4\x05\xf1\xc0$\x92\xa3*rT\ +\xb9c30\xb1\xd6\xdd\x8dS\x0b4\x16!3\xbe#\ +\xa0KB\xe0],\xe1J\x19\xe6\x1e!!'C\xe0\ +\xf0\x144D\x11\x22X_\x0b\xcc\x0ea\x1e\x0a4T\ +\x0c\xd4:!\x08b\x18\xc0\xfdF \xbd(\x22P\xe4\ +)F\x94\xa2 \x0dTt\x08\x81\x95G\xa1z$\x8f\ +\x82\xf8s\x07\x0f!\xe1A\xd2\xae\xe9%\xae\xcb\x91\xdd\ +1Q\xe0\xa2(\xb7%\xa2;\x0b\xc0 \xb5, \x02\ +\xe2\x18\x9c\xc1l\x14tb\x0f\xd3*$\xe2\x9e\x98\xb2\ +a&B/\x12\x14N\x0a\x01\xb3L\xe1\x9e$\x8a4\ +\x07@\x90\xd3,\xe8!\xef\xe0\x11\x00\xd4\xea\xa1/A\ +\x0d\x07\x15\xcd\x0a\xe5FK$\xae\x18\xdd\x8b\xa2\x7fo\ +\x9a\xa93\x1c#\xec\x88\x14L\x8c\xc9\x02\x18\x93\xd3z\ +\x07\x22P\x91\xf3\x96$\x0c\xe4\x18\xc1p\x9ej8$\ +px\xcf\xe1R\x1aj\xc6!k$\xb2\x88p\xb2\xe2\ +F\xf2*\x095\xd0OA\xaf1A\xef56\x91l\ +fo\x9b7J\xf8$+\x08\x14\xe0\x8dV`\xb0\xaa\ +j\xab@\x00U\x1a\x91\xac$Jf\x10\x01@\x09\x95\ +\x80\x0b\xe2@\xecA\xd8\x1c\xf4T\x0a K?!\xef\ +?b>\xf4n\xaa\x1b\x0f\xac\x03b\x18\xd7\xc1d\x13\ +\xcdv\x10@\xc7H\x13\x02\x11UF\x06I!5\xea\ +\x0e\xf8\xeb?+\x12P\xe1\xf4\x997\x0a!PN\xf0\ +$il\x0f\x80\xd3]\xe1\x06!\x8b\x88\xee@U-\ +a\xc2$\x95\x0bP\xe0T\xc9\x22>k\xee\xe4\x05s\ +\x9e$\x91x\x13\x81\x86\xdc\xa2\x18\x18v\x10\x16\x89H\ +\x0e \x9a$\x92>}T\x85[\xee\xd2\xe5ec;\ +B\x1d;\x93m\x5c\xeb\xf974\x9f\x10\x82CV`\ +\x8c\x0a\xeb\x08\x15\x13\x878\xa9C8\xf5P$\x13\x97\ +9\xa2=\x22\x0c\xb8\x05q $\x95{W\xf5\x82!\ +\x88\xd0\x1a\xc1\x98\x0b\xf6t\x06\xc6$\x22\xf4\x83,b\ +-;\x00{b\xc2\x1b\x16\xb1n#\x0a\xf0\x9bU\x06\ +#\xc7@\xe3!H\x19\xb4Z \xf3S4\xc1~$\ +\x8a\xfc\xb0\x052\xb0B?(4\x00\x05*\xf0$\x8e\ +\xbe\x0b\xa0\xe9#\x01\x16\xb5\x88.\xcb\x80Y\x0d\x82A\ +TV\x80\x22\xb3`VO\x91U%p\xbf63;\ +\xd68\xbf\xd6<$\x08\x8c\xb0\xe1\x5c\x1bL,!\x91\ +\xf0\x0b\xef\x1a\x16\xaf\x1e$q\x0d.\xc1T\x1a\xa0\x0b\ +q`\x0c#\x8dh\x121\x10\xb2\x02O< |\x09\ +\xd3\xc8\x16\x22\x18`S\x8d6B5#\xf2CO.\ +\x10d\xab\xedn0\xadno9Bb\x10\xe9\xe8`\ +\xfa\x02HJu\xf2\x05\x17`\x06B\x19P\xa1\x16\xd3\ + \xec%\x12'\x22\xa2(*T\xb0\x14r\xfa\x10\xe0\ +\xd3:BL\xcd kP\xa7l!\xf3\xd0\xc3\x81z\ +\xc3\xc2?A(?u\x02\x0fhV\x88!\x8f\xf6Z\ +\xe5\xb3P0c]bL\x12\x17\xb8\x16\x87H\x07\x80\ +\x96!\x8d\xbe\x15\xf0>\x0aBYL'$\xc6\x80P\ +!\x8a\xf0@\x00\xa6\x05\x16\x02%w\xd4\x15\x17\xe8\x1a\ +U6!K*\x0f5;mt\xee\xa77Abw\ +E\x0a\x97I6wM6\xb5\xcc#\x07\xfez)Q\ +x\xe2M2\x13%2\x82\x18\x8c\xcb\xd6\x0b\x80f\x91\ +\x02V4\xe0\x86\x0a\x87v\x15b!R\x95\x22\x17\x02\ +Y\x02\x0bR\xc0\x22\x19\x09\x01g\x09P\x99oU\xb7\ +\x7f\xcaw*\x83-tu\xc7O\xd7O6\xf67]\ +7\xb2\xf4\x02H\xed\xe0\xe0\x11\x98&\x0eb\x18\xfe\xd5\ +\xe9 \xc1\xd0%\x8a\xdcB!6\x18B!{\x81 \ +\x0e\x96\x9c\x94\xc2Wq`\x0a\x00\xd50\x1a\x8fj!\ +\x8d^\x0cX\xb0\x07XT\xdcj\xdb\x7f\xf0\xa5b\x80\ +}zb\x17z\xa9\xe9IP\xb5\x81(\xb7\x81tj\ +\x94V\x17\x89\xc6\xf2(Vt\x0b\xe0mf\xe1\x98%\ +\x8fk~\x81P\x1aE\xf0!\x96\xf8\x13+\x08\x0d\x02\ +`\x98\x01*\x17@o\x90\xa0\x8bV\xd2\xdfm\x12\xe2\ +#V\x7fT\x98\xbe\x89\xed\xd4\xc5U\xc9h\xe2/B\ +\xa2H\x07\x190\x08\xcb*\x17\x22 \x0f\xb9<\x0b\x93\ +\xec\x16\xe1KD\xa45q\x01\xa9.\x82\x19q\xf7\x22\ +&\x0ep\xb1\xa9\xe1rB\x14`K\x1c\x06*\x80#\ +\x95D\xf2\x97\xa0 \xd2\xabb\xb6S\x80\x8d\xdb\x80\xd8\ +\xcf1\x88\x04\xdeu,\xb7u1SBh!\x8e|\ +\xe8\x02_l6\xc6N\xa2\x10\xfe\xc8\xa1s\x82R\xee\ +@\xd4\xc3!*\x22\x18t\x09\x81\x89\x9b\xa1k\x96\xd7\ +\xfb$V\x83\x1d\x09\x9d\x9a\x82!h\xd5X\xae\xe6q\ +iW\xb4$\xa7\xb9400!\x97\x94\x15\x93\xd0A\ +-'\x83\x00\xa6\x99a\xe7\x84\xc1>FA\xbe\x1a\xe4\ +\x12\x97 \x80\x13:\x09j\xa2\x1fv\xd7h#\x97<\ +\x91\x81_\x9cB+\x97x\xc3\x97\xa2$\xba\x04@\xd1\ +qq+\xad\x1d\x9d\xa2P\x13\xba8\x18\x8d\x88\x05\xe0\ +v!\x8e\x02\x1b\x01\x9dl ig\xb3\x92!sG\ +48\xf6!h \x15\xe16\x9d!\x0a\x0c\xc29:\ +\x94\x15\x86\x8d\xd1\x8c\x18\xc4!Y\xd1P\x0d\xa1\xa3\x19\ +\x88$\xd9<\x0f\xa18\x09\xfa\x8d[\x22\x17\x0d\x95\xe9\ +,\xbaR#\x0bS\x07\x82\x19G\xa1\x94\x17\x94t\x0d\ +Y\x0e#z\x16\xc4Z\x1c\x22\x9a!\xa7b\x12\xd9\xb7\ +\xad;\xb9+n\xed\xa5U\xe2Q\x99\xd6\xc8\xc1\x22\x85\ +\x82`gL\xe1\xb3M:\x9a!E\xfa\x83@+>\ +\x00\xf3\xae\xe1/\x11b\x19\xad\xc1\x9f8\xd9\xc0u\x18\ +6\xf6p\xb5m\xf9\xcd;uU18\x10H\xf0\x03\ +\x00bSr\x97-)W0!\xeb\xc2\x0a\x8b\xbc\x15\ +\xa5TJd\x07JP\xdc5\xa0%\x10\xcf\xb7}S\ +F\xcc\xc04\x86\xa6D\x22x\x17\x8e1\x82#s\xa9\ +\xabb'\xb0\x98\x07\x16\x90\xaef\x1a-n\xca d\ +\xf8\xd4%o\xba\x04 S\x8f\x01\xa46\x03d!n\ +\xfc\x0fm\xb0\x13\xd3\x84D\xa6\x88\xf6\x10%\x02r\xe9\ +\x11r\xe9}P4\x03(\xe4e\xa2`\x14{\xa8\x11\ +\x81#\xba\xf1\xb7\xb5U\xb7[\xb6%\x16\x11\xd1\x8b\x00\ +\xc4\x07yj\xd5\x0d\x14r\x95[c\xb3v%L\x03\ +o\x81\xb3J\xa2\x19S\x01&\xcf\xa28h\x89\xc7J\ +@3\xbe\xe0>\xfbp*\xfb\x8f\xba\x05SG\x03\x14\ +\xa4OE\x998\xcb|\x1c\xc1\xbf\xa6\xb8Wm\xa2(\ +\xb9\x88H\xed\x97J\x9aP\xfc\xd5V4\x8b:\x81i\ +bJ)\xfb\x86\x18\xcd\xac!\x81q\xc3\xa1L\xde\xc0\ +\xb6 \x94\xab\xb9\xfb\xf7}Q\x0d\x07\xdbH\xc2\xc4\x06\ +\xf4n\xf3!\xd4\xcb\xa8\x223m\x99\x1d$x_\x80\ +XcnY\x7f\x5c\xb0\xb5\x92\xc2V\x10\x9c|\x14\xa0\ +\x91\xc8 \xb4!\x95\xec\x80\x81\x8e\xfb{\xee\x03 ?\ +\x0b\xc0#j:\xe2 \x8c\xaa\x8da\x9a\x18\x011\xca\ +\xa0\xfe\xbd\xd5\xb5\x8b\x90K\x8b\xc2/\x05\x07\x8b\xa2S\ +\x0f\xb0\xfa\xc4\x22\xdcx%E\x1a\x0c\x00\xf0r\x5c\x9e\ +!\xb1\x1c\x82\xc1\xd5\x12\x12\x06\xad1\xfa\x1d\xb2\xcb\x0d\ +\x92\x83 \x01\xc7\x0e\xae\x0a\xf8\x5c\x14\x22p\xa6Y\xfb\ +\x0bb\xf5U\x92\x9c\xc9\xac\x94\xa1\x88\x89\x91\x88\xd8\x90\ +\xeaF\xbf\xce\xc5\x07\x191\x94\x1bq%\x19\xb5\xed\x1a\ +kfOY\x1bHu\xc0U\xd4\x8dO\xbcqB'\ +\xf9\xa7\x0e\x9c\x85\xceiu\x82^e\xa1\x1f\xd5!d\ +\xd2dL*P\xd9\xcf\x12\xdc\x1c\xbc\xe7,\xae\xb8\x1c\ +\xb1.\x1b2|\x1b\x92\x0d!\xfd/\x08\xd9n\xb99\ +r \xbc\xfe~\xbcn\x22\x9a\xc0\x9e\x9d\x08\xc5\xf9\xd6\ +\x8b|`%\x9bIZ\xe1D\xcf,\xf6#f\x05\x0d\ +\x91\x8e\x1b\xf3\x9e\xe3\xc1\xab\x12]\xae\xf0\xe1\xc3(\x9c\ +\xd6\x22\xfce\xd3X\x00\x1e4\xf8\x9a1\xdb\xc7}\x0d\ +o$\x10JxzJ\xa0z\x09\xb0\x80\x02I\xae\x1f\ +\x86H\x1e\x11.\x1b]s\x9f\xc1\xad9\xfdc\x91}\ +\xc3\xcfP\xef\xdc\x99\x1e~}\x01\xb5\xfc\xc2\x8a\xbc#\ +B]E\x104\x9do\x1b\xd3\xe0>%0\x087\x1c\ +\x96\x0c\xca\x99\xc8\xca\xfe\x11\x9c\xfc\xc5n\xa2.\xaf\x14\ +\xe2\x0dY\xfc\x1a\xec\x18\xc1\xde'\xe4\xe2D\x07~T\ +\x09S\x80\x0da\x0at\x0cO\x86\x09+\xc7\x14\xfe\x81\ +\xc2>\xaf\x1aP9\xc6\x92b&\xee1C\xb9\xe7b\ +\x18nF\x84cG\x9co\x02\x0b\xe8\xde\x86^E\xce\ +;&\x99\xe7\xddE\xe8\xc6\xf7\xe9\xa2\x85C\xa5\x8d\xe9\ +`\x02_\xf0\xb4 \x9a\xbb\xcc\x1e9\x80\xbcu\xd8>\ +Q\xec.\xa5\xebt\x8e\xed\xa5\xab\xac>=\xec^\xd5\ +\xdc9\xa4s=\x03h\xa4\xb9\xaa\xa1\x09F\xde\xd7\xee\ +\xbe$\xb5\xb3S\xdbE\xa8\x00\xf8&\x0e\x95\xde\x0d!\ +\x07\xeb>\xed\xf0e#\x19\xb20\x09U\xec#\x1b|\ +\x08?\x18\x0a8\x1d\xba_\x09\xf2.l<\xdc:\x17\ +\x01L\x90\xa0\xd9\xe0\x06&)/\xb7\xda\x00W\xf3\xe0\ +m\xf2_D\xda\x98\x85\xe4/\x14\x18!e3\x828\ +e\xb80\x0a\x98t\x11\x90\x1b\xf4\x7fdRG\xe5)\ +\xa1!\xbe\x1c\xe6$\x89\x14\xcf \x9dIPr\x05x\ +\xa0\x00\xff6c\x02\x1cnE\xe1\xe9%\xed\xe7\xe3\xac\ +<_\x95\xe8?\x94n_\x98\xc5\xde\xac\x22\x1f\xa6c\ +>\x94oE\xdb\xf8~y\xe8\x9f\x9f\xf9\xa6\x82)f\ +\xbf\xd6\xe7\xc4\x13\xc1y\xfc\xa1W!\xc2P)\xe6[\ +\x10\xd7`\x05\x00d\xfbt\xa4)\xf6\xbf\xfb\x22\x0en\ +?\xbc,\xff\xb0 F\x8c;\x06\x9a \x0f\xf7\xf8\x06\ +\x09\x05\x82@\xa0@\x08P\x00\x05\x0d\x01?\xa2\x0f\xe8\ +\x5c2\x1a\x03\x88\xbf`\xd18\xd4l\x01\x08\x81\xc1c\ +\xd18\xf48\x05!\x8eH\xe1\xd184E\xfd(\x86\ +\xc4\xe5\x92H\xf4z\x0d\x19\x85\xcb&\xd1\xc8\x5c\x92Y\ +&\x85NcRG\xed\x0d\xfb\x13\x92>i\x0f\x87\x9d\ +-\xde\xe3\xa77\x1b\xd5\x16\xb3\xa2\xa8\xe1{\xd5\xde\xb3\ +\xe9\xd5n\xb9]\x8eA\x80v\x10%\x8c\x08\x05\x99L\ +\xe15\xe9<\x22\x81j\xb7[\xeb\x95\xabm\xc2\xe9u\ +\x9dM \xb7k\xd4v\xd9y\xbd\xdf\xf0\x11\xa9e\x11\ +\xf9\x17\xad`q\x18\x9cV/\x19\x8d\xc7c\xf2\x19\x1c\ +\x96O)\x95\xcbe\xf3\x19\x9c\xd6o9\x9d\xcfg\xf4\ +\x1a\x1d\x16\x8fI{\x80\x80\x00\x00\x00\x03\x00\x01\xa0\x03\ +\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\xa0\x04\x00\x01\x00\x00\ +\x00`\x00\x00\x00\x03\xa0\x04\x00\x01\x00\x00\x00`\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00Adobe Pho\ +toshop Document \ +Data Block\x00MIB8n\ +rTM\x00\x00\x00\x00MIB8ryaL$\ +!\x00\x00\x01\x00\x03\x00\x00\x00\x03\x00\x00\x00]\x00\x00\ +\x00\x5c\x00\x00\x00\x04\x00\xff\xff\x88\x0d\x00\x00\x00\x00\x0c\ +\x06\x00\x00\x01\x00\x0c\x06\x00\x00\x02\x00\x0c\x06\x00\x00M\ +IB8mron\xff\x00\x08\x00<\x01\x00\x00\x00\ +\x00\x00\x00(\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\ +\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\ +\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x07\ +Layer 0MIB8inul\x14\ +\x00\x00\x00\x07\x00\x00\x00L\x00a\x00y\x00e\x00r\ +\x00 \x000\x00\x00\x00MIB8rsnl\x04\ +\x00\x00\x00ryalMIB8diyl\x04\ +\x00\x00\x00\x03\x00\x00\x00MIB8lblc\x04\ +\x00\x00\x00\x01\x00\x00\x00MIB8xfni\x04\ +\x00\x00\x00\x00\x00\x00\x00MIB8oknk\x04\ +\x00\x00\x00\x00\x00\x00\x00MIB8fpsl\x04\ +\x00\x00\x00\x04\x00\x00\x00MIB8rlcl\x08\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00MIB8d\ +mhsH\x00\x00\x00\x01\x00\x00\x00MIB8t\ +suc\x00\x00\x00\x004\x00\x00\x00\x10\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x08\x00\x00\x00metadat\ +a\x01\x00\x00\x00\x09\x00\x00\x00layerTi\ +mebuod\xc5\xa4\xf3l\xf98\xd6A\x00M\ +IB8prxf\x10\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00S\x00O\ +\x00\x0f\x00\x0c\x00\x0a\x00\x09\x00\x09\x00\x09\x00\x09\x00\x15\ +\x00K\x00K\x00\x1e\x00\x1e\x00\x1e\x00\x1c\x00)\x00'\ +\x00(\x00$\x00'\x00\x22\x00'\x00$\x00&\x00%\ +\x00%\x00 \x00\x1d\x00\x1c\x00\x1c\x00\x1d\x00\x1b\x00\x1d\ +\x00!\x00\x22\x00#\x00%\x00!\x00$\x00+\x00+\ +\x00,\x00+\x00(\x00%\x00&\x00.\x00/\x00.\ +\x00+\x00-\x00-\x00.\x00-\x00.\x00/\x00-\ +\x00.\x00-\x00.\x00-\x00-\x00*\x00%\x00#\ +\x00(\x00,\x00+\x00,\x00,\x00%\x00#\x00!\ +\x00&\x00\x22\x00!\x00\x13\x00\x14\x00L\x00\x09\x00\x09\ +\x00\x08\x00\x09\x00\x0a\x00\x0a\x00\x0c\x00L\x00W\x00 \ +\x00\xfd\x00\x04\x05\x11!-1\xfe/\xfc0\x181/\ +/0110010/1/0010/\ +0110/01\xfe0\x0a/21100\ +/00//\xfe1\x000\xfe1\x05010/\ +01\xfe0\xfe1\xff0\x081/0/-'\x18\ +\x08\x01\xfe\x00\xff\x00\x07\x01\x14X\xab\xdb\xec\xf0\xf0\xfd\ +\xf1\x00\xf0\xfe\xf1\x00\xf2\xfd\xf1\xff\xf0\x01\xf1\xf0\xf8\xf1\ +\x03\xf0\xf1\xf1\xf0\xfe\xf1\xfe\xf0\x07\xf1\xf0\xf0\xf1\xf1\xf0\ +\xf1\xf0\xfc\xf1\xff\xf0\x1b\xf1\xf0\xf0\xf1\xf2\xf1\xf0\xf1\xf0\ +\xf1\xf1\xf2\xf0\xf1\xf0\xf1\xf0\xf0\xf1\xf0\xee\xe4\xc2|.\ +\x06\x00\x00\xff\x00\x03\x16\x86\xed\xfd\xb3\xff\x04\xf9\xbfA\ +\x06\x00\x03\x00\x08l\xf4\xb0\xff\x03\xfe\xbd*\x01\x02\x00\ +$\xd0\xae\xff\x02\xf8x\x07\x02\x02O\xf6\xad\xff\x01\xc0\ +\x14\x02\x05r\xfd\xad\xff\x01\xe1#\x02\x08\x86\xfe\xad\xff\ +\x01\xed+\x02\x08\x8f\xfe\xad\xff\x01\xef-\x02\x09\x91\xfe\ +\xf1\xff\x00\xfe\xdb\xff\xfe\xfe\xfd\xff\x00\xfe\xec\xff\x01\xef\ +,\x02\x08\x91\xfe\xfa\xff\x17\xfe\xcf\xc2\xc3\xc2\xc4\xc3\xc4\ +\xc3\xc3\xc4\xc4\xc1\xc3\xc3\xc2\xc3\xc2\xc2\xc3\xc2\xc3\xc4\xc4\ +\xfe\xc2\xff\xc3\x0c\xcb\xd5\xdc\xe1\xe1\xdd\xd5\xcc\xc2\xc3\xc2\ +\xc2\xc1\xfe\xc3\xff\xc4\xf9\xc3\x06\xc4\xc3\xc3\xc4\xc2\xc3\xc4\ +\xfe\xc3\xff\xc2\x01\xc7\xf1\xf9\xff\x01\xf0-\x02\x09\x90\xfe\ +\xfa\xff\x03\xfaK\x18\x17\xfc\x18\x14\x17\x18\x16\x19\x17\x18\ +\x19\x19\x18\x19\x18\x17\x18\x19\x19\x18\x17/~\xce\xf4\xfb\ +\xff\x13\xf7\xc8\x810\x18\x16\x19\x19\x17\x18\x18\x19\x18\x18\ +\x19\x17\x19\x18\x19\x18\xfd\x19\x08\x18\x19\x19\x18\x19\x19\x17\ +,\xc7\xf9\xff\x01\xf0,\x01\x09\x90\xf9\xff\x01\xf98\xeb\ +\x00\x020\x9f\xea\xf5\xff\x02\xeb\x996\xe8\x00\x01\x15\xc0\ +\xf9\xff\x01\xef.\x02\x09\x91\xfe\xfa\xff\x01\xf97\xec\x00\ +\x01u\xf4\xf1\xff\x01\xf7c\xe9\x00\x02\x14\xc2\xfe\xfa\xff\ +\x01\xf0-\x02\x09\x90\xfe\xfa\xff\x01\xf88\xee\x00\x01\x12\ +\x91\xed\xff\x01\x9a\x17\xeb\x00\x02\x14\xc2\xfe\xfa\xff\x01\xef\ +/\x01\x08\x90\xf9\xff\x01\xf99\xef\x00\x01\x0e\xc7\xeb\xff\ +\x01\xba\x02\xec\x00\x01\x14\xc1\xf9\xff\x01\xf0/\x02\x09\x90\ +\xfe\xfa\xff\x01\xf97\xef\x00\x00\x95\xfa\xff\x09\xd1\x9c]\ +B;Hj\xaa\xd6\xfe\xfa\xff\x01\x97\x04\xed\x00\x01\x12\ +\xc0\xf9\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\x01\xf98\xf0\ +\x00\x00r\xfb\xff\x02\xc9E\x02\xfa\x00\x02\x05C\xbf\xfa\ +\xff\x00\x86\xed\x00\x01\x14\xc2\xf9\xff\x01\xf1-\x02\x09\x90\ +\xfe\xfa\xff\x01\xfa8\xf1\x00\x01-\xf5\xfc\xff\x01\xa3\x09\ +\xf6\x00\x02\x02s\xfc\xfc\xff\x01\xee\x16\xee\x00\x01\x15\xc1\ +\xf9\xff\x01\xef-\x01\x09\x92\xf9\xff\x01\xf97\xf1\x00\x00\ +\xb6\xfc\xff\x01\xa8\x06\xf3\x00\x01C\xe7\xfc\xff\x00\xa0\xee\ +\x00\x01\x15\xc2\xf9\xff\x01\xef,\x02\x09\x91\xfe\xfa\xff\x01\ +\xf98\xf2\x00\x01\x1b\xf4\xfd\xff\x01\xe3\x13\xf1\x00\x01:\ +\xfa\xfd\xff\x01\xf9%\xef\x00\x01\x14\xc2\xf9\xff\x01\xf0/\ +\x01\x09\x90\xf9\xff\x01\xfa7\xf2\x00\x00\x82\xfc\xff\x00g\ +\xef\x00\x00\x8f\xfc\xff\x00\x87\xef\x00\x01\x15\xc2\xf9\xff\x01\ +\xef-\x01\x09\x91\xf9\xff\x01\xf87\xf3\x00\x01\x12\xea\xfd\ +\xff\x01\xe9\x0d\xef\x00\x01\x08\xd8\xfd\xff\x01\xdf\x0a\xf0\x00\ +\x02\x14\xc1\xfe\xfa\xff\x01\xef/\x02\x08\x90\xfe\xfa\xff\x01\ +\xf98\xf3\x00\x00n\xfc\xff\x00\xad\xed\x00\x00o\xfd\xff\ +\x01\xfd0\xf0\x00\x01\x15\xc2\xf9\xff\x01\xf0-\x02\x09\x91\ +\xfe\xfa\xff\x01\xf97\xf4\x00\x01\x0f\xe5\xfd\xff\x01\xfe;\ +\xed\x00\x01\x17\xee\xfd\xff\x00T\xf0\x00\x01\x14\xc3\xf9\xff\ +\x01\xf0/\x02\x09\x91\xfe\xfa\xff\x01\xfa7\xf4\x00\x01(\ +\xef\xfd\xff\x01\xc2\x02\xec\x00\x00\xcb\xfd\xff\x00c\xf0\x00\ +\x01\x15\xc1\xf9\xff\x01\xf1-\x02\x09\x92\xfe\xfa\xff\x01\xf8\ +8\xf4\x00\x06\x01\x1bP\xc0\xff\xffI\xeb\x00\x00\xb0\xfd\ +\xff\x00m\xf0\x00\x01\x15\xc2\xf9\xff\x01\xf0.\x01\x09\x90\ +\xf9\xff\x01\xf98\xf0\x00\x02C\x87\x07\xeb\x00\x00\xb6\xfd\ +\xff\x00m\xf0\x00\x01\x15\xc2\xf9\xff\x01\xf0.\x02\x09\x90\ +\xfe\xfa\xff\x01\xf99\xd8\x00\x01\x09\xda\xfd\xff\x00b\xf0\ +\x00\x02\x15\xc3\xfe\xfa\xff\x01\xef-\x01\x08\x91\xf9\xff\x01\ +\xf97\xd8\x00\x018\xfe\xfd\xff\x00Q\xf0\x00\x02\x14\xc2\ +\xfe\xfa\xff\x01\xf0/\x02\x09\x91\xfe\xfa\xff\x01\xf98\xd8\ +\x00\x00\x9b\xfd\xff\x01\xfc2\xf0\x00\x01\x14\xc1\xf9\xff\x01\ +\xf0.\x02\x09\x91\xfe\xfa\xff\x01\xf98\xd9\x00\x01*\xf8\ +\xfd\xff\x01\xd5\x06\xf0\x00\x01\x14\xc1\xf9\xff\x01\xf0.\x02\ +\x09\x91\xfe\xfa\xff\x01\xf99\xd9\x00\x00\x8e\xfc\xff\x00\x80\ +\xef\x00\x01\x14\xc2\xf9\xff\x01\xf1.\x02\x09\x92\xfe\xfa\xff\ +\x01\xf99\xda\x00\x01\x07\xdf\xfd\xff\x01\xe5\x19\xef\x00\x01\ +\x15\xc1\xf9\xff\x01\xf0/\x02\x08\x93\xfe\xfa\xff\x01\xf98\ +\xda\x00\x01B\xfe\xfd\xff\x04\xef\xc8\xca\xad^\xf2\x00\x02\ +\x15\xc1\xfe\xfa\xff\x01\xef.\x02\x09\x91\xfe\xfa\xff\x01\xf9\ +8\xe0\x00\x06\x0c\x22Cg\x84\x9c\xda\xf7\xff\x01\xb9(\ +\xf4\x00\x01\x14\xc1\xf9\xff\x01\xef-\x01\x08\x8f\xf9\xff\x01\ +\xf99\xe6\x00\x07\x0d(Hn\x97\xbb\xde\xf4\xf1\xff\x01\ +\xd1\x0a\xf5\x00\x02\x15\xbf\xfe\xfa\xff\x01\xee.\x02\x09\x92\ +\xfe\xfa\xff\x01\xf89\xef\x00\x0a\x02\x0e\x1d:Pf~\ +\x9b\xc2\xdf\xf8\xea\xff\x00Q\xf5\x00\x01\x14\xc2\xf9\xff\x01\ +\xef.\x01\x09\x91\xf9\xff\x01\xf98\xf4\x00\x07\x059]\ +\x88\xaf\xd1\xe3\xee\xe2\xff\x00\x9f\xf5\x00\x01\x14\xc1\xf9\xff\ +\x01\xf0-\x02\x09\x90\xfe\xfa\xff\x01\xf98\xf6\x00\x02\x10\ +{\xd0\xe3\xff\x03\xfa\xf0\xdc\xed\xfd\xff\x00\xc1\xf5\x00\x01\ +\x14\xc1\xf9\xff\x01\xef,\x02\x09\x90\xfe\xfa\xff\x01\xf97\ +\xf7\x00\x01$\xd4\xe8\xff\x0a\xfb\xe5\xc7\x9fyR8)\ +\x18\x08\x9d\xfd\xff\x01\xe2\x0f\xf6\x00\x01\x15\xc2\xf9\xff\x01\ +\xf1.\x01\x09\x91\xf9\xff\x01\xf98\xf8\x00\x01\x05\xc8\xed\ +\xff\x07\xf7\xe2\xc0\x9crJ+\x0d\xf9\x00\x00\x8a\xfd\xff\ +\x01\xf8&\xf6\x00\x01\x14\xc3\xf9\xff\x01\xf1.\x02\x09\x92\ +\xfe\xfa\xff\x01\xf99\xf8\x00\x00`\xf4\xff\x09\xfd\xea\xcd\ +\xa7\x82jXC#\x0c\xf3\x00\x00k\xfc\xff\x00J\xf6\ +\x00\x01\x13\xc2\xf9\xff\x01\xf0.\x02\x09\x91\xfe\xfa\xff\x01\ +\xf97\xf8\x00\x00\xaf\xfa\xff\x07\xfb\xe6\xc6\xa3wR/\ +\x11\xeb\x00\x00D\xfc\xff\x00p\xf6\x00\x02\x15\xc2\xfe\xfa\ +\xff\x01\xf0.\x02\x09\x91\xfe\xfa\xff\x01\xf98\xf8\x00\x00\ +\xc1\xfd\xff\x04\xeaxJ+\x0f\xe5\x00\x01#\xf8\xfd\xff\ +\x00\x97\xf6\x00\x01\x14\xc1\xf9\xff\x01\xef.\x02\x09\x91\xfe\ +\xfa\xff\x01\xf88\xf8\x00\x00\xa1\xfd\xff\x01\xe5\x03\xe2\x00\ +\x01\x08\xdf\xfd\xff\x00\xbe\xf6\x00\x01\x15\xc1\xf9\xff\x01\xef\ +-\x02\x09\x91\xfe\xfa\xff\x01\xf97\xf8\x00\x00y\xfd\xff\ +\x01\xfc.\xe1\x00\x00\xc0\xfd\xff\x01\xdd\x0a\xf7\x00\x02\x14\ +\xc2\xfe\xfa\xff\x01\xf1.\x02\x09\x91\xfe\xfa\xff\x01\xf98\ +\xf8\x00\x00M\xfc\xff\x00T\xf3\x00\x05?\x95\xb6\xa2f\ +\x0b\xf5\x00\x00\x96\xfd\xff\x01\xf7&\xf7\x00\x02\x15\xc2\xfe\ +\xfa\xff\x01\xef.\x02\x09\x90\xfe\xfa\xff\x01\xf98\xf8\x00\ +\x01.\xfd\xfd\xff\x00w\xf5\x00\x02\x08\x97\xfd\xfd\xff\x01\ +\xc5#\xf6\x00\x00r\xfc\xff\x00C\xf7\x00\x01\x14\xc1\xf9\ +\xff\x01\xef.\x02\x09\x92\xfe\xfa\xff\x01\xf97\xf8\x00\x01\ +\x10\xe8\xfd\xff\x00\xa2\xf6\x00\x01\x01\xaf\xfa\xff\x01\xe11\ +\xf7\x00\x00H\xfc\xff\x00i\xf7\x00\x01\x15\xc2\xf9\xff\x01\ +\xf0.\x01\x09\x91\xf9\xff\x01\xf98\xf7\x00\x00\xcb\xfd\xff\ +\x00\xc7\xf6\x00\x00?\xf8\xff\x00\xb7\xf7\x00\x01'\xfa\xfd\ +\xff\x00\x87\xf7\x00\x01\x15\xc1\xf9\xff\x01\xef.\x01\x09\x91\ +\xf9\xff\x01\xf97\xf7\x00\x00\xa7\xfd\xff\x01\xe3\x0d\xf7\x00\ +\x00\xa0\xf8\xff\x01\xe6\x0b\xf8\x00\x01\x0c\xe4\xfd\xff\x00\x9d\ +\xf7\x00\x01\x14\xc0\xf9\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\ +\x01\xf98\xf7\x00\x00|\xfd\xff\x01\xf5\x1f\xf7\x00\x00\xcb\ +\xf8\xff\x01\xf7\x22\xf7\x00\x00\xc3\xfd\xff\x00\xb3\xf7\x00\x01\ +\x13\xc0\xf9\xff\x01\xf0.\x02\x09\x91\xfe\xfa\xff\x01\xf97\ +\xf7\x00\x00W\xfd\xff\x01\xfc.\xf7\x00\x00\xc0\xf8\xff\x01\ +\xf3\x1e\xf7\x00\x00\xa0\xfd\xff\x01\xcb\x01\xf8\x00\x01\x14\xc1\ +\xf9\xff\x01\xf1.\x01\x09\x91\xf9\xff\x01\xfa8\xf7\x00\x01\ +0\xfd\xfd\xff\x00A\xf7\x00\x00\x82\xf8\xff\x01\xdc\x05\xf7\ +\x00\x00t\xfd\xff\x01\xe8\x14\xf8\x00\x01\x15\xc1\xf9\xff\x01\ +\xef-\x02\x09\x91\xfe\xfa\xff\x01\xf97\xf7\x00\x01\x14\xee\ +\xfd\xff\x00^\xf7\x00\x01$\xf9\xf9\xff\x00\x90\xf6\x00\x00\ +N\xfd\xff\x01\xfb-\xf8\x00\x01\x13\xc2\xf9\xff\x01\xf0-\ +\x02\x09\x91\xfe\xfa\xff\x01\xf99\xf7\x00\x01\x02\xcd\xfd\xff\ +\x00\x8a\xf6\x00\x00k\xfb\xff\x02\xfe\xa1\x0a\xf6\x00\x005\ +\xfc\xff\x00T\xf8\x00\x02\x15\xc2\xfe\xfa\xff\x01\xf1.\x01\ +\x09\x92\xf9\xff\x01\xf98\xf6\x00\x00\xb4\xfd\xff\x00\xae\xf5\ +\x00\x01P\xd7\xfd\xff\x01\xfa\x22\xf5\x00\x01(\xfa\xfd\xff\ +\x00x\xf8\x00\x01\x15\xc2\xf9\xff\x01\xf0-\x02\x09\x91\xfe\ +\xfa\xff\x01\xf86\xf6\x00\x00\xa1\xfd\xff\x01\xd1\x04\xf5\x00\ +\x00T\xfc\xff\x00H\xf5\x00\x01\x18\xf0\xfd\xff\x00\x9f\xf8\ +\x00\x02\x14\xc1\xfe\xfa\xff\x01\xf0-\x02\x09\x91\xfe\xfa\xff\ +\x01\xf98\xf6\x00\x00\x8c\xfd\xff\x01\xee\x18\xf5\x00\x00=\ +\xfc\xff\x00s\xf5\x00\x01\x06\xda\xfd\xff\x00\xc6\xf8\x00\x01\ +\x14\xc2\xf9\xff\x01\xf0.\x02\x09\x92\xfe\xfa\xff\x01\xf86\ +\xf6\x00\x00r\xfd\xff\x01\xfe4\xf5\x00\x01%\xf7\xfd\xff\ +\x00\x98\xf4\x00\x00\xba\xfd\xff\x01\xe3\x0f\xf9\x00\x01\x14\xc1\ +\xf9\xff\x01\xf0/\x01\x09\x91\xf9\xff\x01\xf98\xf6\x00\x00\ +F\xfc\xff\x00[\xf5\x00\x01\x08\xdc\xfd\xff\x00\x90\xf4\x00\ +\x00\x8f\xfd\xff\x01\xfb,\xf9\x00\x02\x15\xc1\xfe\xfa\xff\x01\ +\xf0.\x02\x09\x91\xfe\xfa\xff\x01\xf88\xf6\x00\x01'\xfa\ +\xfd\xff\x00\x80\xf4\x00\x00t\xfe\xff\x01\xee-\xf4\x00\x00\ +j\xfc\xff\x00K\xf9\x00\x01\x15\xc2\xf9\xff\x01\xf1-\x01\ +\x09\x91\xf9\xff\x01\xfa8\xf6\x00\x01\x0b\xe2\xfd\xff\x00\xab\ +\xf3\x00\x03D\x97\x8a)\xf3\x00\x00@\xfc\xff\x00u\xf9\ +\x00\x01\x13\xc2\xf9\xff\x01\xf0.\x01\x08\x90\xf9\xff\x01\xf9\ +8\xf5\x00\x00\xc4\xfd\xff\x01\xcd\x01\xe2\x00\x01 \xf7\xfd\ +\xff\x00\x9a\xf9\x00\x02\x14\xc2\xfe\xfa\xff\x01\xf0.\x01\x09\ +\x91\xf9\xff\x01\xf98\xf5\x00\x00\x9c\xfd\xff\x01\xeb\x15\xe1\ +\x00\x00\xd8\xfd\xff\x00\xbb\xf9\x00\x01\x13\xc2\xf9\xff\x01\xf0\ +.\x02\x08\x91\xfe\xfa\xff\x01\xf97\xf5\x00\x00t\xfd\xff\ +\x01\xfe1\xe4\x00\x03\x07\x1c=\xd4\xfd\xff\x00\xce\xf9\x00\ +\x02\x15\xc0\xfe\xfa\xff\x01\xef-\x02\x09\x91\xfe\xfa\xff\x01\ +\xf89\xf5\x00\x00N\xfc\xff\x00S\xec\x00\x09\x02\x12\x22\ +3Ei\x8f\xb7\xd7\xf1\xfb\xff\x00\xc9\xf9\x00\x01\x14\xc2\ +\xf9\xff\x01\xef-\x01\x09\x90\xf9\xff\x01\xf96\xf5\x00\x01\ +)\xfb\xfd\xff\x00z\xf2\x00\x08\x03\x1a7Z\x85\xa8\xd0\ +\xe9\xf6\xf4\xff\x00\x8b\xf9\x00\x01\x14\xc0\xf9\xff\x01\xf1,\ +\x01\x09\x90\xf9\xff\x01\xf97\xf5\x00\x01\x0f\xe8\xfd\xff\x00\ +\x92\xf9\x00\x08\x03\x0d\x1d\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / Globa\ +l\x0d\x0a <\ +desc>Created wit\ +h Sketch.\ +\x0d\x0a \x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x09\xba\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / Camer\ +a\x0d\x0a <\ +desc>Created wit\ +h Sketch.\ +\x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \ +\x0d\x0a \ + \x0d\x0a \x0d\ +\x0a\x0d\x0a\ +\x00\x00\x00\xab\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x0d\x08\x06\x00\x00\x00\xa0\xbb\xee$\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +UIDAT8Oc\xfc\xff\xff?\x03%\x80\x09\ +J\x93\x0d\xf0\x1a\xd0\xd8\xd8H\xd0y\x04]@\xc8\x10\ +\x940\xc0\xa7\xb8\xbe\xbe\x9e\x11\xcaD\x01D\x87\x01.\ +\xc3I\x0aDl\x86\x90d\x006o\x10m\x00\xae0\ +\xc0\x9b\x90`N\xc6\xa5\x19\x04\x08\xba\x00\x9ff\x10\x18\ +\xe8\xa4\xcc\xc0\x00\x00\x9d\xda\x22\x8d\x12\xa2\xae,\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x05{\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / viewp\ +ort\x0d\x0a \ + Created w\ +ith Sketch.\x0d\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \ +\x0d\x0a\x0d\x0a\ +\x00\x00\x00\xca\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x0d\x08\x06\x00\x00\x00\xa0\xbb\xee$\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x00_IDAT8O\xc5\x91\xc1\x0e\ +\xc0 \x08C\xc1\x1f\x07\xbe\x9c\x05'\x86\xc4\xb9\x89\x1e\ +\xf6.r\xa1\xd2\x16U\x15\x0c\x11\xb9\x87\x07\x88\x08\xdb\ +8\x80\xcc<]\x5c\xa1\xb4w\x9b\xff\x05z\x88\x19b\ +\xe0\xe9\x10\xbd\x11\x17I[\xf0E\x17*o\x1d\xcf\x88\ +\x16\x8eB\xb4\xcf\xab\xc0\xc9\x15\xfd\x82\x1d\x11c\xa81\ +\xfa\xfb\x06\xe0\x02\xfbk*\x0b\x22\xb70[\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x06\x06\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a Icons / Edit\ +or / EMFX / Moti\ +on\x0d\x0a \ +\x0d\x0a \ +\x0d\x0a \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x00\xa0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x0d\x08\x06\x00\x00\x00\xa0\xbb\xee$\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\ +\xa8d\x00\x00\x005IDAT8Oclhh\ +\xf8\xcf\x80\x06\xea\xeb\xeb\x19\xa1L\x82\x80\x09J\x93\x0d\ +\xb0\xba\x80\x18\x00s%\xd9.hll\x04[L\xb1\ +\x17F\x0d\x185\x80\x81\x81\x81\x01\x00\xc0\x1c\x0a\x95\xd5\ +0\x97g\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x11\xc7\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / Debug\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \ + \x0d\x0a \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x12>\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / Exper\ +imental\x0d\ +\x0a Creat\ +ed with Sketch.<\ +/desc>\x0d\x0a \x0d\x0a <\ +path d=\x22M15.1323\ +36,4.99076918 C1\ +5.5178274,4.9907\ +6918 15.8485779,\ +5.34903984 16.04\ +13236,5.67028261\ + C16.2340693,5.9\ +9152539 16.20194\ +5,6.90832486 16.\ +0413236,7.261691\ +92 C15.7410536,7\ +.67565898 15.559\ +016,8.029019 15.\ +4952109,8.321771\ +96 C15.4376576,8\ +.58583981 15.459\ +0738,9.15831061 \ +15.5594594,10.03\ +91844 C17.808158\ +9,11.0992855 19.\ +0519645,13.44435\ +78 18.9234674,15\ +.9500515 C18.797\ +9586,19.0877716 \ +16.2512974,21.89\ +56839 13.1394393\ +,22.1143917 L12.\ +9162275,22.12557\ +98 L12.1999856,2\ +2.1255798 C8.794\ +81215,22.1255798\ + 6,19.0982307 6,\ +15.660933 C6,13.\ +3376593 7.088222\ +19,11.2234803 9.\ +09581042,10.1537\ +076 L9.3226506,1\ +0.0391844 L9.162\ +02921,8.06479886\ + L8.71228932,7.2\ +6169192 C8.55166\ +793,6.90832486 8\ +.64804077,5.9915\ +2539 8.84078643,\ +5.67028261 C9.04\ +316938,5.3811641\ +2 9.51285982,5.0\ +6205304 9.885797\ +26,5.00112201 L1\ +0.0060269,4.9907\ +6918 L15.132336,\ +4.99076918 Z M14\ +.5477747,9.22030\ +884 L10.3382049,\ +9.22030884 L10.3\ +087674,10.505628\ +3 C10.3087674,10\ +.5645034 10.2793\ +298,10.6233785 1\ +0.2204547,10.652\ +8161 C8.30701393\ +,11.5065051 7.07\ +063679,13.419945\ +9 7.07063679,15.\ +5394495 C7.07063\ +679,18.5420797 9\ +.54339107,20.985\ +3965 12.5754588,\ +20.8970838 C15.3\ +720262,20.838208\ +7 17.6681551,18.\ +5715173 17.78590\ +53,15.8043875 C1\ +7.9036555,13.626\ +0087 16.6378408,\ +11.5359426 14.63\ +60874,10.6528161\ + C14.5919311,10.\ +6307379 14.56433\ +34,10.5921011 14\ +.5532943,10.5493\ +247 L14.5477747,\ +10.5056283 L14.5\ +477747,9.2203088\ +4 Z M16.248292,1\ +5.5813393 C16.43\ +33656,15.5813393\ + 16.5920002,15.6\ +606566 16.697756\ +5,15.792852 C16.\ +8035128,15.92504\ +74 16.856391,16.\ +1101211 16.82995\ +19,16.2951947 C1\ +6.6977565,17.009\ +05 16.4069265,18\ +.0137353 15.6930\ +712,18.806908 C1\ +4.4693191,20.158\ +1342 12.6801281,\ +20.2309765 12.48\ +27708,20.2344561\ + L12.4146244,20.\ +2346187 C12.0973\ +553,20.2346187 1\ +0.4581319,20.155\ +3014 9.29481208,\ +18.806908 C8.501\ +63946,17.8815399\ + 8.2108095,16.92\ +97328 8.10505315\ +,16.2951947 C8.0\ +7861407,16.11012\ +11 8.10505315,15\ +.9250474 8.23724\ +859,15.792852 C8\ +.32185367,15.687\ +0957 8.44030078,\ +15.6321024 8.579\ +05311,15.6007985\ + L8.68671307,15.\ +5813393 L16.2482\ +92,15.5813393 Z \ +M13.525066,16.42\ +73901 C12.996284\ +3,16.4273901 12.\ +5468198,16.87685\ +46 12.5468198,17\ +.4056363 C12.520\ +3807,17.9344181 \ +12.9698452,18.38\ +38826 13.525066,\ +18.3838826 C14.0\ +538478,18.383882\ +6 14.5033123,17.\ +9344181 14.50331\ +23,17.4056363 C1\ +4.5033123,16.876\ +8546 14.0538478,\ +16.4273901 13.52\ +5066,16.4273901 \ +Z M10.5110101,16\ +.5331465 C10.114\ +4238,16.5331465 \ +9.79715474,16.82\ +39764 9.82359382\ +,17.2205627 C9.8\ +2359382,17.59070\ +99 10.1408629,17\ +.907979 10.51101\ +01,17.907979 C10\ +.8811573,17.9079\ +79 11.1984264,17\ +.5907099 11.1984\ +264,17.2205627 C\ +11.1984264,16.85\ +04155 10.8811573\ +,16.5331465 10.5\ +110101,16.533146\ +5 Z M11.0960618,\ +12.7156002 C11.6\ +351612,12.715600\ +2 12.0891397,13.\ +1695787 12.08913\ +97,13.7086781 C1\ +2.0891397,14.247\ +7775 11.6351612,\ +14.701756 11.096\ +0618,14.701756 C\ +10.5569624,14.70\ +1756 10.1029839,\ +14.2477775 10.10\ +29839,13.7086781\ + C10.1029839,13.\ +1695787 10.55696\ +24,12.7156002 11\ +.0960618,12.7156\ +002 Z M12.429623\ +6,10.4173342 C12\ +.8268547,10.4173\ +342 13.1389649,1\ +0.7294444 13.138\ +9649,11.1266755 \ +C13.1389649,11.5\ +239067 12.826854\ +7,11.8360169 12.\ +4296236,11.83601\ +69 C12.0323924,1\ +1.8360169 11.720\ +2822,11.5239067 \ +11.7202822,11.12\ +66755 C11.720282\ +2,10.7294444 12.\ +0323924,10.41733\ +42 12.4296236,10\ +.4173342 Z M14.4\ +300245,6.2371834\ +4 L10.39708,6.23\ +718344 C10.16157\ +96,6.23718344 9.\ +95551678,6.35493\ +364 9.83776658,6\ +.5609965 C9.7396\ +4141,6.73271554 \ +9.72328721,6.924\ +87734 9.77166837\ +,7.10341063 L9.8\ +0832903,7.208622\ +62 L10.39708,8.0\ +9174915 L10.3970\ +8,8.60845497 L14\ +.5919794,8.60845\ +497 L14.5919794,\ +8.09174915 L15.0\ +187755,7.2086226\ +2 C15.1365257,7.\ +00255976 15.1070\ +882,6.76705935 1\ +4.989338,6.56099\ +65 C14.8715878,6\ +.35493364 14.665\ +5249,6.23718344 \ +14.4300245,6.237\ +18344 Z M12.3728\ +762,6.5763594 C1\ +2.7984811,6.5763\ +594 13.1389649,6\ +.91684325 13.138\ +9649,7.34244807 \ +C13.1389649,7.76\ +805289 12.798481\ +1,8.10853674 12.\ +3728762,8.108536\ +74 C11.9472714,8\ +.10853674 11.606\ +7876,7.76805289 \ +11.6067876,7.342\ +44807 C11.606787\ +6,6.91684325 11.\ +9472714,6.576359\ +4 12.3728762,6.5\ +763594 Z M14.933\ +6824,2 C15.64302\ +38,2 16.2388706,\ +2.53909944 16.23\ +88706,3.27681446\ + C16.2388706,4.0\ +1452947 15.64302\ +38,4.58200257 14\ +.9336824,4.58200\ +257 C14.2243411,\ +4.58200257 13.62\ +84943,4.04290313\ + 13.6284943,3.30\ +518811 C13.62849\ +43,2.56747309 14\ +.2243411,2 14.93\ +36824,2 Z\x22 id=\x22C\ +ombined-Shape\x22 f\ +ill=\x22#FFFFFF\x22 fi\ +ll-rule=\x22nonzero\ +\x22>\x0d\x0a <\ +/g>\x0d\x0a\x0d\x0a\ +\x00\x00\x04\x17\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / Files\ +\x0d\x0a Created with\ + Sketch.\x0d\ +\x0a \x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x03i\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a \x0d\x0a <\ +title>icon / Pre\ +ferences / Gizmo\ +s\x0d\x0a <\ +desc>Created wit\ +h Sketch.\ +\x0d\x0a \x0d\x0a \ + \x0d\x0a \x0d\x0a\ +\x0d\x0a\ +\x00\x00\x00[\ +\x00\ +\x00\x01Fx\x9c\x8d\x8c1\x0a\x800\x10\x04\xe7\xacD\ +P;\xeb\x94\x96>\xc1\xa7\xf9d\xad\x05\xcf\x8d\x88B\ +\xc0\x98Y\x86\x83cY\xa80B\x80V\x99\x0c\x06`\ +\x94z1KS\x22\x0b/\xd5m\xc4\xdd)\xa2\x93\xcd\ +\xb7\xfd~Pk5\xde\x5c\xef\xdaI\xf0\x12\xb6\xbc+\ +\xf6\xf8\xd7M9\x01\x0cb\x81\xee\ +\x00\x00\x00[\ +\x00\ +\x00\x01Fx\x9c\xc5\xc81\x0e@@\x18D\xe1\xf9W\ +!**\xad-\x95n\xc0\xcd\xec\xd1\x1c\xc5\x11\x94\x0a\ +\xf1\xec\xc6\x8a\x0bH|\x93\xd7\x8c\xe4d\xf2^jT\ +i0\xa9\x95\xd4\xc7\xe2\xa5)fqI\xd0\xcb\xe5\x12\ +@\x7f\xe3q\xcep\x8c\xb0w\xb0\xd5\xb0\x96\xb0\x14\x10\ +\xec\xfe\xbe.\xbb\x00\x9d\x16jC\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x0c\x00\x04\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x07\xf8\x00\x00\x07\xf8\x00\x00\x0f\xfc\x00\x00\x0f\xfc\ +\x00\x00\x1f\xfc\x00\x00\x1f\xfe\x00\x00?\xfe\x00\x00/\xfe\ +\x00\x00o\xfe\x00\x00\xef\xfe\x00\x00\xcf\xf6\x00\x00\x0d\xb6\ +\x00\x00\x0d\xb4\x00\x00\x0d\xb0\x00\x00\x0d\x80\x00\x00\x0c\x00\ +\x00\x00\x0c\x00\x00\x00\x0c\x00\x00\x00\x0c\x00\x00\x00\x0c\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x03\ +\xff\xff\xf0\x03\xff\xff\xf0\x03\xff\xff\xe0\x01\xff\xff\xe0\x01\ +\xff\xff\xc0\x01\xff\xff\xc0\x00\xff\xff\x80\x00\xff\xff\x80\x00\ +\xff\xff\x00\x00\xff\xfe\x00\x00\xff\xfe\x00\x00\xff\xfe \x00\ +\xff\xff\xe0\x01\xff\xff\xe0\x03\xff\xff\xe0\x0f\xff\xff\xe0\x7f\ +\xff\xff\xe1\xff\xff\xff\xe1\xff\xff\xff\xe1\xff\xff\xff\xe1\xff\ +\xff\xff\xf3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x00\xa7\ +\x00\ +\x00\x0c\xbex\x9c\xed\x921\x0a\xc20\x18\x85_t\xe8\ +Rp\x13\xc7\x8e=\x867\xf0J\x1e\xc1cx\x0c\xc1\ +\x8btsut(<_\x8bC1\x12K\x93\xdfA\ +\xfe\x0f\x1e\x09\x09\xf9^\x02\x01V\x08h\x1a\x8c\xe3\xb9\ +\x06\xb6\x00ZEK\xd8+\x01;\x8c\xd4p\x1c\xc7q\ +\x9c?\x85\x11\xa6\xf2\x82\xfe\x8f\xf2R\x15\x09\xf9\x0f\xfc\ +\x99\x15_\xe59\xfe\xa9\xc1\xce?\xe7!\x8b+\xe2\xb7\ +\xbci\x8b\xf8c\xdbt\x92}\xf7\x94\xbf\xa0\xdc\x08k\ +\xbf\x11\xe9\x0f9\x8f~C>*\xf2\xaetk\xf22\ +$\x90G\x05C\xd4rR\xba\xf0\xda?\x90W\x9d\xbb\ +)O\x85i\xaa%\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x06\x00\x06\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x000\x00\x00\x000\x00\x00\x000\x00\ +\x00\x01\xfe\x00\x00\x01\xfe\x00\x00\x000\x00\x00\x000\x00\ +\x00\x001\x80\x00\x00\x01\x80\x00\x00\x03\x00\x00\x00\x03\x00\ +\x00\x00\x06\x00\x00\x00F\x00\x00\x00l\x00\x00\x00|\x00\ +\x00\x00\x7f\x80\x00\x00\x7f\x00\x00\x00~\x00\x00\x00|\x00\ +\x00\x00x\x00\x00\x00p\x00\x00?\xe0\x00\x00 \x00\ +\x00 \x00\x00 \x00\x00 \x00\x00 \x00\ +\x00 \x00\x00 \x00\x00?\xe0\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xcf\xff\xff\xff\xcf\xff\xff\xff\xcf\xff\ +\xff\xfe\x01\xff\xff\xfe\x01\xff\xff\xff\xcf\xff\xff\xff\xce\x7f\ +\xff\xff\xcc?\xff\xff\xfc?\xff\xff\xf8\x7f\xff\xffx\x7f\ +\xff\xff0\xff\xff\xff\x10\xff\xff\xff\x01\xff\xff\xff\x00\x1f\ +\xff\xff\x00?\xff\xff\x00\x7f\xff\xff\x00\xff\xff\xff\x01\xff\ +\xff\xff\x03\xff\xff\x80\x07\xff\xff\x80\x0f\xff\xff\x9f\xcf\xff\ +\xff\x9f\xcf\xff\xff\x9f\xcf\xff\xff\x9f\xcf\xff\xff\x9f\xcf\xff\ +\xff\x9f\xcf\xff\xff\x9f\xcf\xff\xff\x80\x0f\xff\xff\x80\x0f\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x00`\ +\x00\ +\x00\x01Fx\x9cc``b`dPP``\xe0\ +f\xe0e0`d`\x10c``\xd0\x00b\xa0\x10\ +\x83\x03\x103\x02!\x0840 \x00\x13\x14\x83\xc0\xff\ +\xff\xff\x19H\x025@\x1c\x82\x03\x0b\xe0\x91\xab\xc1m\ +\xe4\x7fR@3&\xfe\xdd\xbc\xff\xff\xe7\xe6\xf9\xff\x1f\ +0\xf0\x83i\x10\x1f\x9b:\x5c\x00\x00\x81\xb3~\xf0\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x0f\x00\x0f\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x03\x80\x00\x00\x02\x80\x00\x00\x02\x80\ +\x00\x00\x02\x80\x00\x00\x02\x80\x00\x00\x04@\x00\x00\x0c`\ +\x00\x03\xf0\x1f\x80\x02\x01\x00\x80\x03\xf0\x1f\x80\x00\x0c`\ +\x00\x00\x04@\x00\x00\x02\x80\x00\x00\x02\x80\x00\x00\x02\x80\ +\x00\x00\x02\x80\x00\x00\x03\x80\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\ +\xff\xff\xfe\xff\xff\xff\xfc\x7f\xff\xff\xfc\x7f\xff\xff\xfc\x7f\ +\xff\xff\xfc\x7f\xff\xff\xfc\x7f\xff\xff\xf9?\xff\xff\xf1\x1f\ +\xff\xfc\x03\x80\x7f\xf0\x1e\xf0\x1f\xfc\x03\x80\x7f\xff\xf1\x1f\ +\xff\xff\xf9?\xff\xff\xfc\x7f\xff\xff\xfc\x7f\xff\xff\xfc\x7f\ +\xff\xff\xfc\x7f\xff\xff\xfc\x7f\xff\xff\xfe\xff\xff\xff\xfe\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x10\x00\x0d\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\ + \x01\x00\x00@\x00\xc0\x01\x80\x008\x0e\x00\x00\x07\xf0\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf9\xff\xff\xcf\xf8\xff\xff\ +\x8f\xfc?\xfe\x1f\xfe\x07\xe0?\xff\x00\x00\x7f\xff\xc0\x01\ +\xff\xff\xf8\x0f\xff\xff\xff\xff\xff\xff\xff\x7f\xff\xff\xfe?\ +\xff\xff\xff\x7f\xff\xff\xff\x7f\xff\xff\xff\x7f\xff\xff\xfe?\ +\xff\xff\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x00X\ +\x00\ +\x00\x01Fx\x9c\xc5\xcd1\x0a\x800\x0cF\xe1\x97.\ +\xe2\xa4\x93k;:z\x83z\xb3\xf6\xc8\xbdA\xfc\x0b\ +\x05A\x9c\x5c|\xe1#\x90\xa1\x85\x80\x91\x12\xac\xcc\x1c\ +\x06\x1b\xb0\x8bN\x9cb\x9a^\xe5.\x0c=w\xe7\xef\ +\xfc\xa5V>+\x92\x1bDYd\x1a;\xea\xd9,\xe5\ +\xf9\xd7\x05'\x93i\xe6\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x03\x00\x03\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\ +\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x18\x1e\x00\x00\x14\x0c\x00\ +\x00\x12\x00\x00\x00\x11\xe0\x00\x00\x10\x10\x00\x00\x10 \x00\ +\x00\x10@\x00\x00\x10\x80\x00\x00\x11\x00\x00\x00\x12\x00\x00\ +\x00\x14\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xf3\xff\xff\xff\xe1\xff\xff\xff\xc0\xff\xff\xff\xc0\xff\ +\xff\xff\xc0\xff\xff\xff\xc0\xff\xff\xe7\xc0\xff\xff\xe3\xe1\xbf\ +\xff\xe1\xf3\xbf\xff\xe0\x1e\x0f\xff\xe0\x0f\xbf\xff\xe0\x1f\xbf\ +\xff\xe0?\xff\xff\xe0\x7f\xff\xff\xe0\xff\xff\xff\xe1\xff\xff\ +\xff\xe3\xff\xff\xff\xe7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x00\x00\x18\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xf0\x00\x00\x00\x88\x00\x00\x00\x84\x00\x00\x00\x82\x00\x00\ +\x00A\x00\x00\x00 \x80\x00\x00\x10@\x00\x00\x0b\xa0\x00\ +\x00\x05\xd6\x00\x00\x02\xe9\x00\x00\x01a\x00\x00\x00\x81\x00\ +\x00\x00@\x80\x00\x00\x80@\x00\x00\x80 \x00\x00p \ +\x00\x00\x08 \x00\x00\x04@\x00\x00\x03\x80\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\x0f\xff\xff\xff\x07\xff\xff\xff\x03\xff\xff\xff\x01\xff\xff\ +\xff\x80\xfc\xff\x0f\xc0~|c\xe0?1\xf9\xf0\x1f\x87\ +\xff\xf8\x09\xfd\xff\xfc\x00\xf8\xff\xfe\x00\xf0\x7f\xff\x00\xfd\ +\xdb\xff\x80}\xdb\xff\x00=\xdb\xff\x00\x1d\xc3\xff\x80\x1d\ +\xdb\xff\xf0\x1d\xdb\xff\xf8=\xdb\xff\xfcp\x7f\xff\xff\xf8\ +\xff\xff\xff\xfd\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x01\x00\x01\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x18\x00\x00\x00\x18\x00\x00\x00\x18\x00\x00\x00\xff\x00\x00\ +\x00\xff\x00\x00\x00\x18\x00\x00\x00\x18\x00\x00\x00\x18\xc0\x00\ +\x00\x00\xc0\x00\x00\x01\x80\x00\x00\x01\x80\x00\x00\x03\x00\x00\ +\x00#\x00\x00\x006\x00\x00\x00>\x00\x00\x00?\xc0\x00\ +\x00?\x80\x00\x00?\x00\x00\x00>\x00\x00\x00<\x00\x00\ +\x008\x00\x00\x000\x00\x00\x00 \x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xe7\xff\xff\xff\xe7\xff\xff\xff\xe7\xff\xff\xff\x00\xff\xff\ +\xff\x00\xff\xff\xff\xe7\xff\xff\xff\xe7?\xff\xff\xe6\x1f\xff\ +\xff\xfe\x1f\xff\xff\xfc?\xff\xff\xbc?\xff\xff\x98\x7f\xff\ +\xff\x88\x7f\xff\xff\x80\xff\xff\xff\x80\x0f\xff\xff\x80\x1f\xff\ +\xff\x80?\xff\xff\x80\x7f\xff\xff\x80\xff\xff\xff\x81\xff\xff\ +\xff\x83\xff\xff\xff\x87\xff\xff\xff\x8f\xff\xff\xff\x9f\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x0f\x00\x0f\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\xe0\ +\x00\x00\x18\x18\x00\x00 \x04\x00\x00@\x02\x00\x00\x80\x01\ +\x00\x01\x00\x00\x80\x01\x00\x00\x80\x02\x00\x00@\x02\x00\x00\ +@\x02\x02\x00@\x02\x02\x00\x00\x02\x03\x00\x00\x02\x00\x00\ +\x00\x01\x00\x7f\x80\x01\x00 \x80\x00\x80\x10\x80\x00@\x08\ +\x80\x00 \x04\x80\x00\x18\x1a\x80\x00\x07\xe1\x80\x00\x00\x00\ +\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x1f\ +\xff\xff\xe0\x07\xff\xff\xc7\xe3\xff\xff\x9f\xf9\xff\xff?\xfc\ +\xff\xfe\x7f\xfe\x7f\xfe\x7f\xfe\x7f\xfc\xff\xff?\xfc\xff\xff\ +?\xfc\xfc\x7f?\xfc\xfc\x7f\xff\xfc\xfc\x7f\xff\xfc\xff\xff\ +\xff\xfe\x7f\x80\x7f\xfe\x7f\xc0\x7f\xff?\xe0\x7f\xff\x9f\xf0\ +\x7f\xff\xc7\xe0\x7f\xff\xe0\x04\x7f\xff\xf8\x1e\x7f\xff\xff\xff\ +\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x0f\x00\x0f\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x80\x00\x00\x04@\ +\x00\x00\x08 \x00\x00\x02\x80\x00\x00\x22\x88\x00\x00B\x84\ +\x00\x00\x9e\xf2\x00\x01\x00\x01\x00\x00\x9e\xf2\x00\x00B\x84\ +\x00\x00\x22\x88\x00\x00\x02\x80\x00\x00\x08 \x00\x00\x04@\ +\x00\x00\x02\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xfc\x7f\xff\xff\xf8?\ +\xff\xff\xf0\x1f\xff\xff\xfc\x7f\xff\xff\xdcw\xff\xff\x9cs\ +\xff\xff\x00\x01\xff\xfe\x00\x00\xff\xff\x00\x01\xff\xff\x9cs\ +\xff\xff\xdcw\xff\xff\xfc\x7f\xff\xff\xf0\x1f\xff\xff\xf8?\ +\xff\xff\xfc\x7f\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x04\x00\x06\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x03\xe0\x00\x00\x03\xe8\x00\x00\x03\xe0\x00\x00\x03\xe8\ +\x00\x00\x00\x08\x00\x00\x01\xf0\x00\x00\x0c\x00\x00\x00\x0c\x00\ +\x00\x00\x00\x00\x00\x00`\x00\x00\x00`\x00\x00\x00\x00\x00\ +\x00\x1f\x00\x00\x00\x13@\x00\x00\x13@\x00\x00\x1f@\x00\ +\x00\x00@\x00\x00\x0f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x0f\ +\xff\xff\xf8\x07\xff\xff\xf8\x03\xff\xff\xf8\x03\xff\xff\xf8\x03\ +\xff\xff\xf8\x03\xff\xff\xf0\x03\xff\xff\xe0\x03\xff\xff\xe1\xff\ +\xff\xff\x93\xff\xff\xff\x0f\xff\xff\xff\x0f\xff\xff\xc0\x1f\xff\ +\xff\xc0?\xff\xff\xc0\x1f\xff\xff\xc0\x1f\xff\xff\xc0\x1f\xff\ +\xff\xc0\x1f\xff\xff\xe0\x1f\xff\xff\xf0\x1f\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x01\x00\x01\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\ +\x00\x0e\x00\x00\x00\x0e\x00\x00\x00\x00\x80\x00\x00\x01@\x00\ +\x00\x00\xa0\x00\x00\x00P\x00\x00\x00(\x00\x00\x00\x14\x00\ +\x00\x00\x0a\x00\x00\x00\x05\x00\x00\x00\x02\x80\x00\x00\xc1\x00\ +\x00\x00\xc0\xe0\x00\x01\x80\xe0\x00\x01\x80\xe0\x00\x03\x00\x00\ +\x00#\x00\x00\x006\x00\x00\x00>\x00\x00\x00?\xc0\x00\ +\x00?\x80\x00\x00?\x00\x00\x00>\x00\x00\x00<\x00\x00\ +\x008\x00\x00\x000\x00\x00\x00 \x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xf1\xff\xff\xff\xe0\xff\xff\ +\xff\xe0\xff\xff\xff\xe0\xff\xff\xff\xf0\x7f\xff\xff\xfe?\xff\ +\xff\xff\x1f\xff\xff\xff\x8f\xff\xff\xff\xc7\xff\xff\xff\xe3\xff\ +\xff\xff\xf1\xff\xff\xff\xf8\xff\xff\xff<\x7f\xff\xfe\x1e\x1f\ +\xff\xfe\x1e\x0f\xff\xfc>\x0f\xff\xbc>\x0f\xff\x98\x7f\x1f\ +\xff\x88\x7f\xff\xff\x80\xff\xff\xff\x80\x0f\xff\xff\x80\x1f\xff\ +\xff\x80?\xff\xff\x80\x7f\xff\xff\x80\xff\xff\xff\x81\xff\xff\ +\xff\x83\xff\xff\xff\x87\xff\xff\xff\x8f\xff\xff\xff\x9f\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x01\x00\x01\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\ +\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x00\ +\x00\x00\xc0\x00\x00\x01\x80\x00\x00\x01\x80\x00\x00\x03\x00\x00\ +\x00#\x00\x00\x006\x00\x00\x00>\x00\x00\x00?\xc0\x00\ +\x00?\x80\x00\x00?\x00\x00\x00>\x00\x00\x00<\x00\x00\ +\x008\x00\x00\x000\x00\x00\x00 \x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\ +\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff?\xff\xff\xfe\x1f\xff\ +\xff\xfe\x1f\xff\xff\xfc?\xff\xff\xbc?\xff\xff\x98\x7f\xff\ +\xff\x88\x7f\xff\xff\x80\xff\xff\xff\x80\x0f\xff\xff\x80\x1f\xff\ +\xff\x80?\xff\xff\x80\x7f\xff\xff\x80\xff\xff\xff\x81\xff\xff\ +\xff\x83\xff\xff\xff\x87\xff\xff\xff\x8f\xff\xff\xff\x9f\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x00\xc9\ +\x00\ +\x00\x0c\xbex\x9cc``b`dPP`\x00\x83\ +\x15<\x0c\x0cb@Z\x03\x88AB\x0e@\xcc\xc8 \ +\x01\x91\xe4a\x18@`<\x93\x04D\xba\xe13\xff\xff\ +g\x98I4\x22\xd5\x0a\x88\xf9g\x88C\xa3\xe6\x8f\x9a\ +?\x10\xe6\x13\x8f\xc8\xcbb4\xcc\xbf\xc3\x0e\xfc\x07\x83\ +!j\xfe\xa8\xe31\xcd\xa4\xb5\xf9\x103i\x142\xff\ +Q\x01\xad\xcd\xa7\xbaE45\x1c\xab\xf9T4\x1c\xd3\ +|\xea\x1a\x8ef>\xd5\x0dG6\x9f\x16\x863\xd0\xa5\ +\xc0\xa1\x9d\xe1C\x1a\x80\xc2\xfd\x01\x83\xfd\xff\x03\x0c\xf2\ +\x041H\x1d\x18\x00\xa9\x7f\xf2\x10\xfc\x07\xc8\xde\x03\xc4\ +3\xea\xff\xff\xef\x00\xe2\x06\xa0t\x03?\x10\x03\xe5\x1a\ +\x80\xe2\x0dP\xb1F n\x06\xe2v \xee\x07\xe2\xf9\ +\xd0\x04\x05\x00\x85\x1b/\xe1\ +\x00\x00\x00\x5c\ +\x00\ +\x00\x01Fx\x9cc``b`dPP``\x10\ +`\xe0d0`d`\x10c``\xd0\x00b\xa0\x10\ +\x83\x03\x103\x02!\x0840 \x00\x13\x14\x83\xc0\xff\ +\xff\xff\x19\x06\x1a\xfc\x87\x81\x1f\xf2\xd4\xc7\x0d\x8c\xff\xff\ +\x1f`\xfe\xff\xff\x01\xfb\xff\xff\x1f\xf8!b\x7f\xec\xff\ +\xff\xffW\x0f\xb7\x16\x00\xb3\x96jC\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x07\x00\x18\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x01\xe0\x00\x00\x01\x10\x00\x00\x01\x08\x00\x00\x01\x04\x00\ +\x00\x00\x82\x00\x00\x00A\x00\x00\x00 \x80\x00\x00\x17@\ +\x00\x00\x0b\xac\x00\x00\x05\xd2\x00\x00\x02\xc2\x00\x00\x01\x02\ +\x00\x00\x00\x81\x00\x00\x01\x00\x80\x00\x01\x00@\x00\x00\xe0\ +@\x00\x00\x10@\x00\x00\x08\x80\x00\x00\x07\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xfe\x1f\xff\xff\xfe\x0f\xff\xff\xfe\x07\xff\xff\xfe\x03\xff\ +\xff\xff\x01\xff\xff\xff\x80\xff\xff\xff\xc0\x7f\xff\xff\xe0?\ +\xff\xff\xf0\x13\xff\xff\xf8\x01\xff\xff\xfc\x01\xff\xff\xfe\x01\ +\xff\xff\xff\x00\xff\xff\xfe\x00\x7f\xff\xfe\x00?\xff\xff\x00\ +?\xff\xff\xe0?\xff\xff\xf0\x7f\xff\xff\xf8\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x11\x00\x0d\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff?\xff\xff\xff\x1f\xff\ +\xff\xff\x8f\xff\xff\xff\xc4\x0f\xff\xff\xe1\xe7\xff\xff\xf3\xf3\ +\xff\xff\xe3\xf9\xff\xff\xef\xfd\xff\xff\xef\xfd\xff\xff\xef\xfd\ +\xff\xff\xef\xfd\xff\xff\xe7\xf9\xff\xff\xf3\xf3\xff\xff\xf9\xe7\ +\xff\xff\xfc\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x07\x00\x0e\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf8\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xf7\xff\xff\xff\xe7\xff\xff\xff\xe7\ +\xff\xff\xff\x0f\xff\xff\xfe\x7f\xff\xff\xfe\x7f\xff\xfe\xfe\xff\ +\xff\xff~\xff\xff\xfe \x01\xff\xff\x00\x01\xff\xfe0\x03\ +\xff\xff~\xff\xff\xfe\xfc\x7f\xff\xff\xfc\x7f\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x03\x00\x03\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x18\x00\x00\x00\x14\x00\x00\x00\x12\x00\x00\ +\x00\x11\xf8\x00\x00\x10\x08\x00\x00\x10\x10\x00\x00\x10 \x00\ +\x00\x10@\x00\x00\x10\x80\x00\x00\x11\x00\x00\x00\x12\x00\x00\ +\x00\x14\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xe7\xff\xff\xff\xe3\xff\xff\xff\xe1\xff\xff\ +\xff\xe0\x07\xff\xff\xe0\x07\xff\xff\xe0\x0f\xff\xff\xe0\x1f\xff\ +\xff\xe0?\xff\xff\xe0\x7f\xff\xff\xe0\xff\xff\xff\xe1\xff\xff\ +\xff\xe3\xff\xff\xff\xe7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x10\x00\x08\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xf0\x00\x00\x07\xf0\ +\x00\x00\x0f\xf8\x00\x00\x1f\xf8\x00\x00\x1f\xfc\x00\x00?\xfc\ +\x00\x00w\xfc\x00\x00g\xfe\x00\x00\x07\xf6\x00\x00\x0d\xb6\ +\x00\x00\x0d\xb2\x00\x00\x19\xb0\x00\x00\x19\xb0\x00\x00\x01\x80\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xfc\x0f\xff\xff\xf8\x07\xff\xff\xf0\x07\ +\xff\xff\xe0\x03\xff\xff\xc0\x03\xff\xff\xc0\x01\xff\xff\x80\x01\ +\xff\xff\x00\x01\xff\xff\x00\x00\xff\xff\x90\x00\xff\xff\xe0\x00\ +\xff\xff\xe0\x00\xff\xff\xc0\x05\xff\xff\xc0\x07\xff\xff\xe4\x0f\ +\xff\xff\xfe\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x03\x00\x03\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x0a\x00\ +\x00\x00\x11\x00\x00\x00*\x80\x00\x18[@\x00\x14\x80 \ +\x00\x12[@\x00\x11\xea\x80\x00\x10\x11\x00\x00\x10*\x00\ +\x00\x10D\x00\x00\x10\x80\x00\x00\x11\x00\x00\x00\x12\x00\x00\ +\x00\x14\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff\xff\xf1\xff\ +\xff\xff\xe0\xff\xff\xff\xd1\x7f\xff\xe7\x80?\xff\xe3\x00\x1f\ +\xff\xe1\x80?\xff\xe0\x11\x7f\xff\xe0\x00\xff\xff\xe0\x11\xff\ +\xff\xe0;\xff\xff\xe0\x7f\xff\xff\xe0\xff\xff\xff\xe1\xff\xff\ +\xff\xe3\xff\xff\xff\xe7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x0f\x00\x0f\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00?\xff\x80\x00!\x00\x80\x00!*\x80\x00!T\ +\x80\x00!*\x80\x00!T\x80\x00!*\x80\x00!T\ +\x80\x00!\x00\x80\x00!\xfe\x80\x00 \x00\x80\x00 \x00\ +\x80\x00 \x00\x80\x00 \x00\x80\x00?\xff\x80\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xc0\x00\x7f\xff\xc0\x00\x7f\xff\xce\x00\x7f\xff\xce\x00\ +\x7f\xff\xce\x00\x7f\xff\xce\x00\x7f\xff\xce\x00\x7f\xff\xce\x00\ +\x7f\xff\xce\x00\x7f\xff\xce\x00\x7f\xff\xcf\xfe\x7f\xff\xcf\xfe\ +\x7f\xff\xcf\xfe\x7f\xff\xc0\x00\x7f\xff\xc0\x00\x7f\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x03\x00\x03\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x14\x00\x00\ +\x00\x12\x00\x00\x00\x15\xf0\x00\x00\x16\x10\x00\x00\x17\xa0\x00\ +\x00\x17@\x00\x00\x16\x80\x00\x00\x15\x00\x00\x00\x12\x00\x00\ +\x00\x14\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe7\xff\xff\xff\xe3\xff\xff\ +\xff\xe1\xff\xff\xff\xe0\x0f\xff\xff\xe0\x0f\xff\xff\xe0\x1f\xff\ +\xff\xe0?\xff\xff\xe0\x7f\xff\xff\xe0\xff\xff\xff\xe1\xff\xff\ +\xff\xe3\xff\xff\xff\xe7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x06\x00\x06\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x01\xfe\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x01\x80\x00\x00\x01\x80\x00\x00\x03\x00\x00\x00\x03\x00\ +\x00\x00\x06\x00\x00\x00F\x00\x00\x00l\x00\x00\x00|\x00\ +\x00\x00\x7f\x80\x00\x00\x7f\x00\x00\x00~\x00\x00\x00|\x00\ +\x00\x00x\x00\x00\x00p\x00\x00?\xe0\x00\x00 \x00\ +\x00 \x00\x00 \x00\x00 \x00\x00 \x00\ +\x00 \x00\x00 \x00\x00?\xe0\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xfe\x01\xff\xff\xfe\x01\xff\xff\xff\xff\xff\xff\xff\xfe\x7f\ +\xff\xff\xfc?\xff\xff\xfc?\xff\xff\xf8\x7f\xff\xffx\x7f\ +\xff\xff0\xff\xff\xff\x10\xff\xff\xff\x01\xff\xff\xff\x00\x1f\ +\xff\xff\x00?\xff\xff\x00\x7f\xff\xff\x00\xff\xff\xff\x01\xff\ +\xff\xff\x03\xff\xff\x80\x07\xff\xff\x80\x0f\xff\xff\x9f\xcf\xff\ +\xff\x9f\xcf\xff\xff\x9f\xcf\xff\xff\x9f\xcf\xff\xff\x9f\xcf\xff\ +\xff\x9f\xcf\xff\xff\x9f\xcf\xff\xff\x80\x0f\xff\xff\x80\x0f\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x0b\x00\x09\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x18\ +\x00\x00\x000\x00\x00\x000\x00\x00\x00`\x00\x00\x04`\ +\x00\x00\x06\xc0\x00\x00\x07\xc0\x00\x00\x07\xf8\x00\x00\x07\xf0\ +\x00\x00\x07\xe0\x00\x00\x07\xc0\x00\x00\x07\x80\x00\x01\xff\x00\ +\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\ +\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\xff\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xe7\xff\xff\xff\xc3\xff\xff\xff\xc3\ +\xff\xff\xff\x87\xff\xff\xf7\x87\xff\xff\xf3\x0f\xff\xff\xf1\x0f\ +\xff\xff\xf0\x1f\xff\xff\xf0\x01\xff\xff\xf0\x03\xff\xff\xf0\x07\ +\xff\xff\xf0\x0f\xff\xff\xf0\x1f\xff\xfc\x00?\xff\xfc\x00\x7f\ +\xff\xfc\xfe\x7f\xff\xfc\xfe\x7f\xff\xfc\xfe\x7f\xff\xfc\xfe\x7f\ +\xff\xfc\xfe\x7f\xff\xfc\xfe\x7f\xff\xfc\xfe\x7f\xff\xfc\x00\x7f\ +\xff\xfc\x00\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x00V\ +\x00\ +\x00\x01Fx\x9c\xc5\xc8\xb1\x0d\x800\x0cD\xd1\xef4\ +\x88\x0a*\xda\xa4\xa4d\x03\xd8\xcc\x8c\x9c\x0d\xccE\x8a\ +D\x01\x15\x0d\xdfz\xb2t\x900J\x81\x99\x91\xcd`\ +\x01V\xd1\xc4!\xa6k\x9d\xdc\xa5\xae\x15\x11\xfc]<\ +s\xd9+d\x99d\xe8?W\xd7\xee\xe1\x12_\xbcu\ +\x01\xec\xfbi\xe6\ +\x00\x00\x01F\ +\x00\ +\x00\x02\x00\x01\x00 \x00\x00\x10\x00\x11\x000\x01\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\ +\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\ +\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfc\x1f\xc0\x00\x04\x10\ +\x00\x00\x04\x10\x00\x00\x04\x10\x00\x00\x04\x10\x00\x00\x04\x10\ +\x00\x00\x07\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xfc\x01\xc0\x1f\xfc\x01\xc0\x1f\xfc\x01\xc0\ +\x1f\xff\xf1\xc7\xff\xff\xf1\xc7\xff\xff\xf1\xc7\xff\xff\xf0\x07\ +\xff\xff\xf0\x07\xff\xff\xf0\x07\xff\xff\xff\x7f\xff\xff\xfe?\ +\xff\xff\xff\x7f\xff\xff\xff\x7f\xff\xff\xff\x7f\xff\xff\xfe?\ +\xff\xff\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\ +\x00\x00\x00\xef\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00\x09pHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\ +\x01\xc7o\xa8d\x00\x00\x00\x18tEXtSof\ +tware\x00paint.net \ +4.0.6\xfc\x8cc\xdf\x00\x00\x00mIDA\ +T8O\xb5\x8c\xd1\x0a\xc0 \x0c\x03\xfdt\xff\xbc\xb3\ +\x83d]\xa8\xd82\xf6p\xa2\xc7\x99af\x9fHe\ +\x87\xe7\xb2\xae\x15\xd0\xf3\xdf}Htb\xce\xb9\xbe\xc9\ +\x80\xcb\x0a\xdb\x01\x88\x13\xff\x0d\xec@\x08\xdc\xa5\x03.\ +\x15\x8dc\xcb7$DD\xe3\xccQBD4\xce\x1c\ +%DD\xe3\xccQBD4\xce\x1ce\x07|\xe6\x80\ +\xe3\xab\x15\xd0\x83\xd7\xa3\x8f\x8d\x0b\xd1.k\xedV\x14\ +\x8b0\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xd0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00\x09pHYs\x00\x00\x0e\xc2\x00\x00\x0e\xc2\ +\x01\x15(J\x80\x00\x00\x00\x18tEXtSof\ +tware\x00paint.net \ +4.0.6\xfc\x8cc\xdf\x00\x00\x00NIDA\ +T8O\xdd\x8c\xc1\x09\x00 \x0c\x03\xbb\xffT\xdd\xac\ +\x1a\xb0\x82\xb6*U\x10\xf4q\x8f\x5cBHD\x8ep\ +e\x04+\xb2\x02+\xa7XQ\xc6\xcc\x9c\xe3\xd8\xd5\xce\ +\x88\x7f\x0e<\xee\x1e`\xacl\x1f\xcc\x5c\xed\x8cx\xff\ +\x00`\xd8\x8f=\x07\x9a\x10G(\x01oN\x98?\xf6\ +\xff\xda\xc5\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xd4\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00\x09pHYs\x00\x00\x0e\xc2\x00\x00\x0e\xc2\ +\x01\x15(J\x80\x00\x00\x00\x18tEXtSof\ +tware\x00paint.net \ +4.0.6\xfc\x8cc\xdf\x00\x00\x00RIDA\ +T8O\xed\x8fA\x0a\xc00\x08\x04}\xba?\xb7\xce\ +a/a\xa1m\xbc\xe4\x90\xc0\x043\xd1\x05\xa3\xaaF\ +X\xf9\x07+\xa1\x0fW\x97\xfe_X\x09\x0a\xc8\xcc~\ +\xfa\x1e\xb0R0<\x0a\xf8\x82\x95\xa0\x15V\xbfb%\ +(`{\x85\x1bpB\x000<\x0ax\xa7\xe2\x01V\ +T\xcf_\x16\xfbf\x81\x00\x00\x00\x00IEND\xae\ +B`\x82\ +\x00\x00\x03\xea\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x93\x00\x00\x00\x15\x08\x06\x00\x00\x00B\x0c\xdc\xd0\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\ +\x09pHYs\x00\x00\x0b\x11\x00\x00\x0b\x11\x01\x7fd\ +_\x91\x00\x00\x03\x7fIDAThC\xed\x9a\xbbN\ +*Q\x14\x86\x07Cb,\xec}#\x1e\x81\xc6\xca\xbb\ +!\xbe\x81\x8d\x8d\x8d%\x0d=Zig\xb4\xd0\xc4H\ +c(H,\x88\x85Zx\x04\x04\x01\xaf\xf1\x0e\xe2\x92\ +o\x9d\xb3\xf7\x19\x07\xf4\xe4h5\xb0\xff\xe4\x97q\xad\ +=4|\xf9\xf7\x9a\x0d^,\x16\x93n\x9e\x9e\x9e\x96\ +\xa9\xa9)\x99\x9c\x9ctvV\xc3\x03\x5ct\xe3\x05w\ +\xc0\xb4\xbe\xbe.\xa7\xa7\xbf\xda>\x95\xf3\xf3s\xb9\xb8\ +\xb8\x90\xeb\xebk\xb9\xb9\xb9q\xeeS\xf3\xf9\xc3\x01<\ +\xc0\x05|\x04\xb9\xc1\x16& :;;\x93B\xa1 \ +\xb5ZMnoo\xe5\xe1\xe1A\x9e\x9f\x9f\xa5\xd1h\ +8\xf7\xb9\xe1\x00\x1e\xe0\x02>\xe0\x04f:`\xa2X\ +n\x83T*\x95\x94\xc2\xc7\xc7G}\x83V\xab%o\ +ooj\xa7\xfe\x95a\x00\x1e\xe0\x02>\xe0\x04^\x08\ + \x0b\x13 U\xabU-\x12i\x10\xf8\xfa\xfa\xea\x00\ +\xeaS\xb1\x9d\xe5\xf3y\xc9d2\xb2\xb9\xb9\xa9\xde\xdd\ +\xdd\xd5\x1a=\xbf\xe0\x05nLB)L\x14 \x0d\x90\ +\xa0\x0f\x98\x9a\xcd\xa6\xec\xec\xec\xc8\xd2\xd2\x92\x0e^\x98\ +kj\xf4\x9czO\xc7\xc7\xc7\xb2\xbd\xbd\xadpt3\ +=\xd6\x98\xa0\x81\x17\xb81@y\xa4\x12{ \xd1e\ +\x12\x09XVVV$\x1e\x8fw5=\xd6\xb8\xf4\xea\ +\x1d\x01\xc9\xc6\xc6F\x07@A\xb3\x86\xb5\xb0\x82\xe1\x06\ +~\xe8y\x95JE\x87*\xf6B\xe0\xc0\x10\x0843\ +33\xb2\xba\xba*\xc5bQ\xcd55z\xac\xe1\xcd\ +\x9c\xc2/\xb6/\x7f\x22\xed\xed\xed\xd9\xebn5\xd6r\ +\x0f\xac\xc0\x0d\xfcP\xd7dbJg{C,X\x5c\ +\x5cT`\x80'(j\xf4XC\xcc\xb9t\x0a\xbf\x98\ +\x87\xfc\xd0\xa0\xc3\xc3C[\xe3\x1a\xf9\x81\xe2\x1e\x047\ +\xf0C\xcd\x830?\x144'&&\x14\x18\xd2((\ +j\xf4Xs\x7f\x7f\xef`\xea\x011`\x1bH\xb0\x81\ +\x87W\xff\xb5\x7f\x0d\xf7 >\x7f\xf8\xa1\xe6]^^\ +\xca\xcb\xcb\xcb\x07\x98\xc6\xc7\xc7\xff\x09\x13k\x887\xa7\ +\xf0\x8b'6?(\xd8@\x84\x82 a\xeeAp\x03\ +?\xd4<\xa6q3/!`ZXXP`\xbe\xda\ +\xe6X\x03Lf{t\x0a\xaf~\x0a\x13\xfcP\xf3\xae\ +\xae\xae:\x92\xc9\x00\xf3\xd5\x00\x9eN\xa7\xe5\xee\xee\xce\ +\xde\xe7\x14^}g\x9b\xe3\x1c\x0a}H&\xb69\xff\ +\xcc\xc4+\xe7\x06\xc9dR\xa1\xf9\xcc\x89DBO@\ +]2\x85_?\x19\xc0\xe1\xc5\xceL\x0c\xe0L\xe3\x9e\ +\xe7\xd9&\x89srr\x22\xcb\xcb\xcb2??/c\ +ccj\xaeS\xa9\x94M\xa7\xb9\xb99==7 \ +:\x85S\xdf=\x1a@\x84\x89}\x9ac\x9b\xe3X\x1c\ +\x98\x0cP\x9c\x1f\x01T\xb9\x5c\xd6\x03\xaa\x83\x83\x03\xf5\ +\xd1\xd1\x91\xfe\x9f\xcdfevv\xd6\x02\xc5\xa1\x95S\ +\xb8\xf5\xdf\x87\x96\xad\xdfg\x92\xccK\xf0CO\xe9!\ +\xa6\x86\x86\x86d``@\xd6\xd6\xd6\xf4\xcd\x01\x8a:\ +\x8f\xff\x0c\xda\x18\xc00i\x94\xcb\xe5\x14\xa8\xd1\xd1Q\ +\x85\xcb)\xfc\x02\x12\x7fB\x05M\xcf\x80\x84`\xc4\xa4\ +\x92\xfd\xd5\x00\xc0\x0c\x0e\x0e\xdat\x1a\x19\x19\xd1\xc5\x9f\ +\x09\x22\xeb\xf5\xba\xec\xef\xef\xcb\xd6\xd6\x96\x90nN\xbd\ +!\xb6/\xe6!\x86r\x9e\xd8\xb0\xf9\xa2\xb7\xde\xee5\ +\x9a\x7fgd3+\xd9_\x0d\xf0\x07\x0d\x0f\x0fK$\ +\x12\x91h4\xaa\xaff\xcb\xfbLL\xf0\xc4\x1b =\ +==\xfd\xa9:\xf5\xba\x08\x12\x12\x89\xcf\xdc\x0f\x92\x85\ +\x09\xd3\xe0\xcc\xa9V\xab* \xec\x85\x0cW\xdc\xec\x06\ +\xec\xfe\x96a\x00\x1e\xe0\x82Q'\x08\x12\xb609;\ +\xff\xcc1y\x07P\x7f\x17\xd6Q\x02K\x02\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x01\xbb\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x10\x08\x06\x00\x00\x00\xc9V%\x04\ +\x00\x00\x01\x82IDAT(\x91\x8d\xd2O\x88\x8eQ\ +\x14\x06\xf0\xdf7\xf3\xa6Qc#\xb1\x9b\xbe\xc6\xd0,\ +,&\x1dM\x8aI\xa9Ql\xecg\xa1H2J\xb3\ +\xb2\xb4\xc5\xdeJ\x91\xb5\xa6\xc8\xbf\xa5P\x16rD\xb1\ +\xa0\x11\x92\x92\x05if\xc1\xa4)\x8b\xf7}\xa7\xfb}\ +>\xe5Y\x9d{\xcf}\xce9\xf7yN\xc7\x7f 3\ +Gq\x0a\xdb#b\xbe\xbd\xaf\x9a\xe4\x16\x8c\x16\xefW\ +\x22\xe2[fV8\x8e1\x5c\xc1\x91\xa2\xe0H\xa7\x09\ +\xf6a\x02\xd3x\x8aw\x98\xc5\x14~c\xa5(<\x8c\ +M\xd8\xda)*m\xc0\x09\x1c\xc5$\xae\xe3E\xdf\x0f\ +f\xf0\x18\x9b\xf1\xbd\xca\xcc\xcb\xf8\x899\xbc\xc7%\xdc\ +\xc78\xe6\xf1*\x22\xae5\x0d\xba\x11q+3\xbb\x98\ +\x1a\xc2g,#\xb0\x18\x11w\x22b\x0d\x8bX\xc0\xd5\ +\xcc<4H\xc8*\x22.\x16\xa3\x97\xb9\xc9\x22\xde\x9f\ +\x99\x0f0\x91\x99\xc3\xed\xe5\xd0\xa0\x8a\x0d\xde\x16\xf1K\ +\xdcS\x0b\xfa\x05\xc7z\xc8\x99\xb9\x13\x072s.3\ +\xf7\xe2\x1c\x96p\x13\x9fp7\x22\xf6\xe0\xb4\xda\xb2\x91\ +\xaa\xa8\xbe\x80\x8f8\x8b\xdd\xf8\x85\x0b\xb8\x81\xd5\xa6\xc1\ +\x13\xb5\xa5\xb7\xf1\xbc\x1c\xfb\x07\xb6E\xc446\xe2\x0d\ +\x0e\xab}\xff\x80nC\x9a\x8d\x88\x93\x11\xb1T\xfa\xbc\ +\xabI\xee\x88\x88\xb5\xcc\x9c\xc1\xa36\xdd\x8c\xdc\x83\xf5\ +\xce\x11\xf1\x1a\xcb\x8dM\x1aqZ\x8c\xf7\x13\xfb\x05\x1b\ +S/I\x8b\xce?\xe2\xbf\xc9\xf8\x8a3\xc5\xf9Y\x11\ +\xf7,@\x8bu\xb5#bU\xeda\x8b\x87\xea-;\ +\x88\xf3\x83\xc8\x7f\x00k\xb9|\xe7dF#\x7f\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\x01\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x12\x08\x06\x00\x00\x00\x84\x9e\x84\x0f\ +\x00\x00\x00\xc8IDAT8\x8d\xa5\xd3=N\x031\ +\x10\x86\xe1gQ\x02]\xe8R\xf3\x97\x0e\xaa\xbd\x12E\ +.\x10J\x84\xc4=\xd2%\x12\x15\x11\xe7\xd8\x8d\x10\xa2\ +\xe5 H\x88M\x11G\xdaX\xded1_7\xdf\xe8\ +\x9d\xf1\x8c\xed\xa2i\x1am\xd5u-\xd2\x19\xbec\xb3\ +,K'\xb1\x99\xd0\x03\xceS\x89>\xf0\x04W\xb9\xf0\ +\xf5\x7f\xe0\x9b\x5cx\x841.s\xe0]\xc7,x\x12\ +\x15\xd9\xd3\xa0\x03\xba\xc3\x10\xb7!\xbe@i;\xffJ\ +\xb8\xf7.x\x88\x0aE\x88OC\xfc\x8a\x97c\xc7^\ +\xe3-\xf2~\xf1\xd86\x0e\xcd\xfc\x84\xf6\xdb]\xe2\xb3\ +/\xfcn;\x1f\xfc\x84b{:\xb6\xed]\xf79\xbe\ +\xfe\x0a\x7f`\x81\xe7T\xb2k\xdbm\xddK|I(\ +\xaa\xaa\xea\xc1\xa75\xc0\x14\xb3\x1cx\x03\xb2\x99!K\ +\xef\xfb\x80\xb9\x00\x00\x00\x00IEND\xaeB`\x82\ +\ +\x00\x00\x02\x1e\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x18\x00\x00\x00\x11\x08\x06\x00\x00\x00\xc7xl0\ +\x00\x00\x01\xe5IDAT8\x8d\x95\xd4]h\x8ea\ +\x18\x07\xf0\xdflIQC\xf2q\x22\x8a\x92\xe2\xd8G\ +!\x0e\x97\x16j\x07D\x88\x94R\xcb\xe7\xa2\x9c\x8d\x92\ +\x906\x16K\xf9\x96!\xc7\xac\xa4\x90\x83\xb7)\x07r\ +\x22\x12\xcd\xc1\x88%\x8b\xf9:\xb8\xafw\xef\xd3\xb3g\ +\xeb\xdd\xbf\x9e\xee\xfb\xbe\xee\xeb\xb9\xfe\xd7\xe7]S*\ +\x95\xea\xf1\xd5pl\xc2\x0d\x5c\xc7\xc6\xdc\xddy\xec.\ +\xf8g\x18\xc6\xe1\x07\x9a\xb0\x03\xbf\xf0\x1d\x9b\xf1$t\ +\xce\xe2p\xec\xbbC\xb7\xb3\x1a\xe3P\x87At\xc5y\ +:\x8ea1\xae\x85\xac\x84\xd3\xf8\x8c-\xe8\xad\xd6x\ +9\x82,N\xe0\x19\xf6bY\xc8Z\xb0\x14\xbb\xc6j\ +\xbc\x88\xe0Ox9\x80\xcbX\x81\xa3\xb8\x82\xbb\x19\xbd\ +\x16\xfc\xcb}\x0dq\xf72+\xcf\x13\xc0\x1b\xec\xc3<\ +<\x0c\xaf\xf7\xe4t\xeeK\xb5x\x1f\xe7\xfd\xe8\xc9\xec\ +\xbbc\xdf^D\x00\x17\xc3\x93\xf1hC\x7f\xee\xfe\xb5\ +T\xb7\x8e8ORI\xdf\x03\xcc\xc0_\xb4\x8eD\xb0\ +\x06\x8bB\xe9\x10f\x8e\xe2\xc8\x00\xb6\xaa\xa4{v\xfc\ +\xfb\x14\xbdE\x04\xf5\xb8\x84W\xd2,L\x93\xda\xb2\xa6\ +@\xb7\x0f71\x07\xabC\xb66\xd6;\x0c/2\x9c\ +\xc1\xac\xf0\xea\x96T\xe0\x06\xec\x1c!\x8a\xb6X\xb7\xc5\ +\xbaN\x8a\xbc\xab\x88\xa01\x0c\x1f\x97\xfa\x1f\x9a\xf1\x01\ +\xa7\xa4\xc2\xe7\xf1\x02\x8f\xb1\x1e\xf3\xb1J\xa4\xa7LP\ +\x8b)\x11f\x87T\xc0\xf6\x90\xc3O\x1c\xc4D\x5c\xc5\ +TL\xce\x91\x9c\xc3\x04)-\xb5\xb1*\x13,\xc4\x17\ +\xbc\x95\x8a\xb9\x00\x9fB\x0e'\xa57\x09\x96H\x13\xfd\ +.Gp\x0f\xad\x98+\xa5ghf\xea\xa4^n*\ +\x08\xbd\xdc\xe3\x9dx\x94\xbb\x1b\xcc\x9d\x7f\xe36\x8eH\ +3\xf01K\xf0M\xe5-*B\x8f\xca\x10\xe5\xb1]\ +\xea\xb6\xe78\x10\xb2\x0bY\x85\xbaQ\x0cW\x83f)\ +\xbd}\xd8 \xbd\xc0\xd9'\xa5\xb0M\xc7\x82~\xac\xc4\ +r)\x95\x8dR\x0d\x86\xf0\x1f\x84\xf8v\x1d=\x86M\ +P\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01p\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x12\x00\x00\x00\x12\x08\x06\x00\x00\x00V\xce\x8eW\ +\x00\x00\x017IDAT8\x8d\x9d\x93\xc1J\xc3@\ +\x14EO\xa2\xa0\xa0\xa2\x05\x11\xba\xf3#\xde\xd2\x8d\xf4\ +\x1b\xc4\x1fp'\x88\xa0\x82Pp\xa3\xa2;\xc5\x95\xee\ +\x14\x7f\xe7\x82\x1f\xe1\xc6\x8d\xa4P\xb5\x9a\xb4\xd5E_\ +a\x88IF\xbc0d\xde\xcd\xc9\x9d7a\x06\x22\x92\ +\xb4#i/\xc6\xcd\xc6\x00\xe0\x02\x98\x01n\x9a\xa04\ +\xd6\x0d\xb0\x0a\xb4|\xfe\xbf \xef\xa6j\xfe\xf7 I\ +\x9d\xa0\xfc\x06\x0aI\x9b\x91\x85\xeb%i \xe9#\xc6\ +\xfd\xeaH\xd2v\xc9\xfa\xf4\x112\x07\x92\xe6+\x83$\ +\xedK\xca\x80\xdbR\xd0\x08(J\xde!\xd0\x93t)\ +)\x05H$\xed\x02g\xc0J\x00\x0e\x81\xdcC\x16\x99\ +\xfc\xa3w&\xc7`\xce\x9fS\xbd\x01\xdd\x14x\x02^\ +K+\x0e\x80\x9e\x8f\xb1\x07\xf5\xdd\xcf\xbd\x9e\xea\x0b\xc8\ +\x92`k\x1d\xe0\x0eh\x9b\xd9B\xe0g@afk\ +\x81\xf7\x0c,\x03'fvM\x95$m\x94\xea\xbe\x87\ +\x85\xdeV\xe5\xc7M\x92\x94K\x1a\xc4\xb8\xa4\xee\x85_\ +\x89.\xb0\xee\xd6\x0bplf\x0fU|\xd3\x15\xb9\x07\ +\xdaA\xbd\x04<\xd6\xc1\xb5Af6\x04\xae\x02\xeb\xd4\ +\xcc\xc6u|\xed\xd6\x00\xfc\xb0\xf5\x81\xdc\xccZMl\ +\xe3\xed\xf7\x0e\xce\x81\xa3&\x0e\xe0\x07\x81\x1e\x7f\x14\x8c\ +;\xe7\x95\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01{\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x12\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1b\x06/\x5c\ +\x00\x00\x01BIDAT8\x8d\x95\xd3?K\xdbQ\ +\x14\xc6\xf1OB\xaa\x9bb\xfd\xb3IQ\xc8(\x08\x97\ +:\x1b\xa8\x9d\x5c\xb5\xd0\xcdEqt\x93\x80\xef\xa0o\ +\xa0N.\x0e\xba\xd61\x10p\x90\x0a\xbf\x0cm\x17\xb3\ +\xb8\xb8H\x15u\x8a\xa0`\x1c\xe2\ +\x06\x15\x9c`$\xa5t\x05\xc5\x1c\x90\x11\xccD^\xc6\ +%\xfe\xbf\x1e\x96r\x80\x86P\xc5'\x1c\xe3\x0b4\x1a\ +\x8d:\x96\xf3\x80\xae\x03\x92\xd0\x88\x05\x8b8(\x0c\xd8\ +\xa3\x02\xbec\x12-L\x04T\x00k\xfd*\xaa\xa0\x1e\ +y\x1b\xfb]\xe0RJmz7{\x12\x9b\x91\x0f\xf5\ +\xf0\xb4c\xe9\x07\x1aG\x0d\x19\xf6\xfa\xc0\xde\xa2\xdb\xd5\ +fq\x869/\x8d-\xe1a\x10\xd02\x8eB\x7f\xc0\ +\x1a\xa6p\x8b\x0d\x0c\xe3g\x9c\xef\xe8\x98\x9d\xce(\xe2\ +\x17\x96B?\x06d=*\xdb\xc5\xbf\xd0\xeb\x98\xeeU\ +Q!\xcb\xb2\xc3\xc8\x7fx\x9f\x93Y\x5c\xe07F\xf1\ +5<\xb5\xa8\xf4/\x9a\xb17\x81\xfb\x12Vc#\x0b\ +\xc8\x06\xb6\xb1\x82C\xfc\xc1|\x17O\x13\x0b8E9\ +\xcf_\xeb\x16\xafC\xa9\xe8}\x1e\xb60\x86\xcf\xa1\xbf\ +\x85\xae\x0e\xe09\xef|\xfe\x16\xee\xf0\x14\xfa)t\xab\ +\x9f'\xa5t\x07\xcf)mR\xad\xb7y\x8e\x81\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x02,\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x11\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\ +\x00\x00\x01\xf3IDAT8\x8de\xd3K\x88\x8fa\ +\x14\x06\xf0\xdf\x7f\xfc\x95\xb0@\x22\x9a\x92{\xe4\x9aS\ +\x9a\x85\xb2\x125R\xee\xa5L)\x16\xa6\x90\x8d\xb2D\ +\x12Rl$\xc9(I.Qr\xbf\xaf\xa4\x8e\x14\xd1\ +\xe4R\xc8\x06\xc9%\xb7$,\xbeW}\x8dSo_\ +o\xe7;\xcf\xfb<\xe79\xa7\xa1Gd\xe6h\xac\xc1\ +\x1c\x8c\xc1\x1f\xf4\xc2C\x9c\xc6\xc1\x88\xf8X\xafi\xa9\ +\x15\xf7\xce\xcc\x9d\xb8\x83N\xbc\xc5R\xdcB_\x1c\xc5\ +(<\xcd\xcc5u\x90F\x01\xe8\x8bs\xf8\x8c\x09\x18\ +_\xf2kq\x11\x0b1\xb5\xb0\x9a\x82\xa18\x1b\x11\x9d\ +u&\xc70\x02W\xf1\x18\xaf\xb0>\x22\xf6G\xc4\x0b\ +\xfc\xc4Jt`:V\xa0-3;\xa1\x91\x99m\xb8\ +\x81O\x18\x19\x11\xdf3s\x11N\xe15\x9e\xe1:\x96\ +a\x12\xeeEDd\xe6\xf4\x22\xbd\xb5\x05\xdb\xd1\x07;\ +\x22\xe2{a\xb6\xa4|[1\x1bG#br\x91z\ +937`0\xbebs#3_c\x0b\xc6\xe1\x01\ +\xba\x0b\xb3~\x05\xa8;\x22&\xd4\x0c8\x8b\x05\xe5z\ +\x18\x93\x9a\x18\x80\x13\xf8P\x12\xcf0\x11\xf3\xcay\x92\ +\x99\xcd\x88\xf8\xe5\xff8\x8e\xad\xcdri\xd6\x12\x8f\x22\ +\xe2\x15\x0e\xe0@fvcca\xfc\x02\x17\xf0\xa3<\ +\xf4\x05?Z\xf0\x06]\xd8\x8d\x9b\xb8_\xa3>Ye\ +w/\x95{\xad\xd8\x13\x11\xcb\xb1\x17\x97\xf0\xb1Y\xa4\ +tFD{)\xec\xca\xcc{8\x89\x81=\xe8o\xab\ +\xc9Z\xa22\xe4H#3\x87\xe0)fFDwf\ +v\xa9\xe6\x01\x16\xe16\xc6b\xbe\xaa\xd9\x0f\xf0\x5c5\ +\x9c\xdf0\xbc%\x22\xdeb\x03\xceg\xe6*\xf4\xae\xbd\ +\x9c\x11\xf1^5|\xab\xb1\x1e\x87\xd0^\xfa\xd2\x1e\x11\ +\xbf\x1b5\xfd\xeb\xb0\x09g\xb0\x0b\x8b1\xab\xa4\xaf\x14\ +\x80q\xb8[\xfa\xd3\x11\x11W\xa8-`D\xec+\xf4\ +g\xa8\xf6%0\x0c\xfd\x8b\x0b\xd7\xf0\xae\xb0j\xfb\x07\ +@Y\xc0\x9e\x91\x99\xd30W\xb5\xb5\x83J\xf1C\x9c\ +\x8f\x88\x97=\xff\xff\x0b\x84C\xb2\xa8\xaa\xc4\xf2\x0e\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xee\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0a\x00\x00\x00\x0d\x08\x06\x00\x00\x00\x907\xff\x05\ +\x00\x00\x00\xb5IDAT(\x91}\xd0\xa1\x8aBQ\ +\x14\x85\xe1O\xb9\xcdd\x9b\xeek\x08\xbe\xc7X\x0c:\ +\x08\xfa V\x1f@\xb1h1\xcc4\xc1l\xbc0\xc1\ +7\x10\x83ED\xc3\x14\x83\x96s`s\xb9\xcc\x82\x13\ +\xf6f\xb1\xfe\xb5O\xa3,\xcbO\xac\xfd\xafY\x11\x86\ +\x01\xfe\xc2\xdc\xc0\x1c\x1f8F\xe3\x0f\xeea\x9e&\xd3\ +\x0e\x9b&N\xd8\xe2\x19L\x1d\xccp\xc3\x17\x148\xa4\ +\x17\x91\x0b\xb4R\xea\x19\x9a5\xc5'\xe8%\xe4*/\ +\xab\xc6\x8c\xbcgdV<&\x22\x87\x19Y\x97\x98\x91\ +{,\xab}rbF>0\xc2+x\xc6\xb8\x145\ +\xc8S%\xac/}\xf88!\x0f\xf8F\xbb\x8eZ\xa0\ +\x9b\x16]\x5c\xab\xdd\x92~\xdf\xc4\xcd&:\xc3\xef\xf7\ +E\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x015\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x0f\x08\x06\x00\x00\x00;\xd6\x95J\ +\x00\x00\x00\xfcIDAT(\x91\x95\xd0\xcf*D\x01\ +\x14\xc7\xf1\xcf\xcc\xcaB<\x06YX\x9d\xe6\x09\x94\x12\ +SJ\x9a\x97\xb0'\x8bY\xc8+XY\x121\xfe\x16\ +\x1b;ew\x1e@\xd9YX\xb0A\xd9\x10c\xe1\xca\ +\xedv\xa7\xc6os:\xe7\xf4\xfd\xfdN\xa7\x91\x99\xab\ +\xf8\xc0aD\xdc\xfb\x87\x1a\x99\xd9\xc1\x1e\xfa\xb8\xc1\x01\ +\x8e\x22\xe2a\x18x\x14O\x18)\xcd\xfb\xb8\xc6>\x8e\ +#\xe2\xb1\x16\x86\xcc\x06Z\xa5\xb1W#\xc4HW\x92\xccZ\xcf5\x97\ +i|8\xb0\x14h\x0e\xcc\x06^\x06\xb6\x03#\xf0~\ +\xfdU\xf5@aq\xa2\x8d\xf7\xfe\x00p\x13\xf0\xb8\xb3\ +\xe6\x9bx(\x80M\xc0 `\x82\xb3f\xd1U\xf1\x80\ +\xf7\xfe-\xa0-0/e\x1c\xc0Y\x03\x88\x22\xe0_\ +`Faq\x22+}M:@\xfc\xca\xf1\xc0?\xc0\ +{\x99\xeb\xce&\x0f\x03K\x80N\xde\xfb\xe7\xb3\xd1\xd9\ +`\x08\xa4\xd2\x03\x08q\xed\x07\xb4\x8f\x06\x0f\x03'\x80\ +\xe1\xc02g\xcd\xd8K\xec\xed\x05T\x02\xbb\x81\xbd@\ +\x0f\xa0\x03p\x168\x00\xac\x13B\xcc+\x8b\x89*\x00\ +\x0a\x8b'u\xf6\xbe\xee\x1d\xe0]g\xcda\xa9t\x0d\ +!\xc1\xceE\xa37\x12J-E\xe7\x81\xcd\xc0\xa7 \ +\x968\x9bD*\xdd\x1f(\x06F\xc6C\xa7>\xae6\ +\xeah\x09\xb4\x89\xbc\xfd\xc0@g\xcd\xef1\x04~p\ +\xfc\xe2\x1f\xa5\xd2}\x9d5-\x80\x02\xef}\x0bg\xcd\ +\xad\xce\x9a\x9b\xbd\xf7\xad\x81\x83@\x0d\xb0\x03x\x04X\ +\x0c\xbeZ*\xbd\x03\xd8\x0a\xbc\x12\xc3\xba?\x1a\x1a\xeb\ +\xaci\xee\xac\xe9\xec\xaci\x8b\x10]\x81R\xe0.`\ +Qz\x0e\x5c\x1b\xc7N\xc0\xf7R\xe9!\xce\x9a/W\ +\x94.\xb8\x18+!N\x01]\x81]\xce\x9a<`*\ +\xe0\x09\xd5\x90\x1b\xc5*\x81{\x81\xe9q\xde\xa5^\x8e\ +\x94$\x8f8k\x14\xf0\x0bP \x95n0\x09o\x00\ +\xd6I\xa5'\xd4K\x16!\xee\x8f\x07>-\x95\xdeD\ +\xa8\x7f\x80\x95\xc0\x9b\xc01\xa07\xb0\x0f\x18\x10\xd7z\ +]\x22\xf7\x0e\x03\xcd\x80\x8e\x97\xaa\x82k\x80\x85R\xe9\ +\x0fR\x0c\xef\xfd\x9d\xf1\xef@B\xad\x7f\x0d\xf4u\xd6\ +\x8cv\xd6\xcc\x11B\xdc\x02L\x8b\x8au\x94\xed\x9e\xa9\ +8\x96g?\xa0\xcaYs\xbc\xb12|C*\xbd\x22\ +\x96_N\xe4\xfd\x09\x14:k\x06;k*S\x82e\ +%I\x9c5\xb3@\xf4\x04R\xdd\xb0\x8fT\xba^\xb5\ +x\xef_\x8fa[\x09\x17\xaa \xf1\x82\xf7\xfe\x93\xff\ +9\xc8fg\xcd\x00\xa9\xf4d`\xb7\xb3\xe6B\xbb-\ +,Nt\x00r\xcaJ\x92\xbb\xd37H\xa5\xf7\x00w\ +\xc7\xe9\x82\xe8\x95n\xc0\xcf\xd1K=\x9c5\x87\xb2m\ +D\x0fK\xa5\xf79k\xe6\x0a!\xd6\xa7\x19\x19\xe3\xbd\ +?\xea\xbd\xdf%\x95\xde\x94\xd1\xfd\xaa\xe2\xf8\x1b0\x11\ +\xf8\x22\xfeZ\x01S\x9c5\x87R\xb1\xce\x96\xbaK\xa5\ +\x97\x96\xd5G\xba\xf7\xe3\xd7\x00\x0c\xf2\x9eQikg\ +S|\xe0;`\x18\xa1Z\xca\x9c5\x1f\xa6\x84\x9a\x8a\ +\x05\xe73\xe6u\x8d\xcc!4\xb4\x96\x97\x92i\xca\x01\ +v9k\xc6e\xf0^\x03\xce\xc4\xff\x1b\x84`u\xda\ +Z\xca\xe8\xb7\xc0\x83\xc0\x1a\x02d\x17I\xa5_m\xea\ +\x016:kr\xa5\xd2oK\xa5\x87\xa5\x98\xce\x9a5\ +@\x8e\x10\xe26g\xcd\x90\x8c\xf0\xb4\x8fcW`\xbe\ +\xb3f\x04\xf0,P\x0d\xcc\x91Jw\x86\xd8\x01{\xe5\ +\xe5\xf7!\x80LCT\x0a\x8c\xcc\xcd\xcb\xd7\x84\x98\x0f\ +\xcd\xcd\xcb?\xb1\xb3\xa2|;\xc0\xce\x8a\xf2\x9a\x9d?\ +m=\x99\x12\x96Jw\xcb\xcd\xcb\xff\x8cP\xebg\x80\ +\x22g\xcd\xbc([\x9d\x9b\x97\x7f\x1ax\x06h\xb6\xb3\ +\xa2|Cc\x1e\x98\xe9\xacQ\x01\xeb9\x1ay\xad\x81\ +\xc5R\xe9-R\xe9\x87.\x1aN \x95\x9eE@\xc1\ +\xc7\x22\xbb\xc2Y\xb32]\xa1\x10b\x01p\x12\x18\x0d\ +\x1738\x93j\x81\xf1\xce\x9a\xd2\xb4\x8d\xfb\xbd\xf7\x00\ +\x1b\x09\x19^\x00l\x91J\xaf\x03~\x00?\x09hG\ +hT\x06\x98L\xe8\xf9\xf5\xa8\xac$\x89Tz\x1b0\ +X*\xdd\xa6!\x0fT\x03O\xa6\x1b\x07\xf0\xdeW\x10\ +2\xb8\xa3\xb3f(0\x85\x00FO\x033\xa2\xf1J\ +\xe0>B\xb2A@\xcd\x86\xe8v\x02\xaa\xfe\x9d\xf2@\ +m\x1c\x8f\x00O8kvK\xa5G\x01\xab\xa2\xfb!\ + \xe5!\xe0\x1e\xa9\xf4^\xa0g\xe4W\x11\xe0\xb7?\ +\x01\x8c~%\xe0?@\x95T\x9a\x94\x0e\xa9tw`\ +&\xa1#\xaer\xd6\xc4V\xfc\xe2\xa4\x1c_W7\x15\ +\x98\xed\xac9&\x95>\x97\x16\x9ej\xe0zB=\xa7\ +\xa8\x06\xf8\x0aX*\x84X^V\x92D\x16'r\xf1\ +\xbe\x18\x18E\xc8\xfc\xf4\xdb\xd6\xe9\xb8\xff\xba8\xdf\x85\ +\x10\x8f\xba\x92\xe4\x89\x86\xafd\xc5\x89\x07\xf0\xfe%B\ +&\xb7#\x5c4\x0f\x02\x7f\x01c\x80\xe5\xce\x9a\xa2\x06\ +\xf7*\xdd\x9b\xd0\xef+\xe3\xd83\xea\xa8\x8d\xdeY+\ +\x84XT\xefJ\x96-\x8d\x1e7\x11!\xc4\x1f\x044\ +\xeb\xe2\xac\xa9\xca\x94\x91J/\x04&\x10\x10\xd35\xa6\ +\xb3I\xad8\xde\x90\x16\x12\xba\xdc\xf4\xccu\xa9\xf4\x1d\ +\x80\x02\x8e\x08!\x1a5\xde\xe4\x03\x00\x08!\xe6\x02\xc7\ +\x81\x84T\xfa\xa94\xe3\x00\xcb\x09q\x9eV\x96\xe5\xf3\ +\xec\xb2\xde\x86R\xe9\x02`-\xa1,?\x22@\xeex\ +\xc2}\xf0sg\xcd\xf0lu]\xd6\xdb0^H\x9e\ +\x03N\x01\x09`~4\xbeL\x08\x91\xb5\xf1\xcb\xf6@\ +\x9a' `H\xeay\xbe\xa7\xa9:\xfe\x03\x5c\xaa\xea\ +&\xfd\x17M\xd2\x00\x00\x00%tEXtdat\ +e:create\x002016-01\ +-08T15:18:18+00:\ +00\x01E\x99\xf0\x00\x00\x00%tEXtda\ +te:modify\x002016-0\ +1-08T15:18:18+00\ +:00p\x18!L\x00\x00\x00\x00IEND\xae\ +B`\x82\ +\x00\x00\x00\xce\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0c\x00\x00\x00\x0c\x08\x06\x00\x00\x00Vu\x5c\xe7\ +\x00\x00\x00\x95IDAT(\x91\x9d\xd0\xb1\x09\x02Q\ +\x10E\xd1\xb3\x22\x08\xb6a,\x98\xd9\x80\x81\xa0e\x88\ +\x91%\x18Z\x82\x89b\x19\x0a\x826 \xfc`\xc1\xd8\ +\xd0\x16\x04#\x0d\xfe.\xac\xe8\xdf\x05_x\x87\xcb\xcc\ +\x9b,\x84\xf0\xc2\x01S1{L$\xd2\xc6\x19y\x85\ +\xe5\xe8\xa4\x84,\x84\x90\x9a%7lp\xc5\xba`\x0b\ +\xf4\xeb\x84\xb9\xd8\xa1\x14\xc6\x1a:\xf4\xf0\xa8\xb0\x19\xba\ +)\xa1\xd5|\xf5\xf7\x86\x9b\xcf\xb7\xee\x9aN\xda\x8a\xa5\ +\xcb\x1cqO\x09\x7f\xbd\xf5\x84\x0b\x96\x05[aX'\ +\x8c\xf0\xac\xb0A\xc1~\xe6\x0d\xc9\xd6\x1dQ\xcd\xba\xaa\ +\xa1\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01}\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x01DIDAT8\x8d\x95\xd2\xb1KVQ\ +\x14\x00\xf0\xdf\xf7Qa\xe2\x12N\x91\x93\x85MN\x9d\ +\xa1E\x10\x84\x86j\xd4M\x87\x96\xc0\xdd]\xb7hh\ +\x8d \xa1!A\x1cZ\x85\xa6j*\xf2P\xe0\x90\xe2\ +\x1f\x10\x88A\x86aa|\xe2\xf0\xae\xa1\xaf\x0b\xe6\x99\ +\xde;\xf7\x9c\xdf=\x5cNG\x89\xcc\xbc\x829Lb\ +\x18\x7f\x90X\xc4RD\x1c\xaaD\xa74\x8f\xe25\xae\ +\x96\xfc/\xf40P\xfeW1\x15\x11\xfbm\xa0[n\ +^=\xd1\x0c\x971\x8bql\xe2.\x9e\xd5&\xe8\x96\ +\xb1\x87*g\xdf#\xe2\x1d\xc6\xf0\x153\x99y\xab\x06\ +L\xd5d\xecBD|\xc3\xa3\x92\xfb\xa7\xf6\x82\xe6\xc1\ +~\xa3\xaf\x0dd\xe6\x00\x06\xb1Qr\xd7k@\x0f{\ +xP\x90\x9f\xd8\xc6+\xdcl\xd5\xf7j\xc0:\x02[\ +\x11\xf1\xe9\xf8 3\xdb\x13\xc1\xe7v\xa2\x8b\x17\xe5\xfb\ +if\xf6W\x9ahv\xe2\x00/k\x13,b\x06\xb7\ +\xb1\x96\x99\xf3x\x8f\x8b\xa5\xe6\x00\x97\xf0\x04;m\xe0\ +x\x91\x06\xb1\x82\x89\xca\xed=,c\x1a\x1fq'\x22\ +~\x9c\x02\x0a\xd2\xc1=\xcd*\x8fh\xb61\xf1\x5c\xf3\ +\xa8_4\xfbr\x0a\xf9\x0b\x9c\x15\x99y\x03oq\x0d\ +\x1f0\x11\x11\xfb\xff\x0dT\x907\xb8\x7f.\xa0\x82<\ +>7p\x02Y\xc0\xc3#S\xefd\xae\x0f\x1eB$\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01x\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x12\x08\x06\x00\x00\x00R;^j\ +\x00\x00\x01?IDAT8\x8d\x9d\xd3=K\x5cA\ +\x14\xc6\xf1\xdf\xae\x06\x04\x8bt\xdb\xa8\x88q#\xa2\xd8\ +X\xa4\xb2\x15\x041\xd8\x05\xa2\x95\xe5\xd6!\xd8,n\ +!~\x06+IJ\xf3\x09\x94\x10P\x904n\x11H\ +0$\xf8\x02.\x0b\x0a\x16Vb\xe1\x8a\x8538\x19\ +]\xc2\xfa\xc0\xe5\x9e\xf3\x1c\xce\x7f\xce\x9d\x99[\xa8\xd7\ +\xeb\x12\xad\xa1\x8a\x96\x7fu\x89\x97\x99w\x8a\xc1bf\ +\x8e\xa3O\x07\xca\x01\xaf\xc2\xf3,@\x01\xe5N\x01\xdd\ +I\xdc\x8f\x9e6\x80\x1d\xf4\x86E&\xf0\x13\xe7\xf9\x04\ +\xb1q\xe8\x09\xc0<\xa6\xf1\x19%|\xc2B\x0ex\x9d\ +\x81r\xbd\xc0J\x88\xabq\xfa\x22\xc6BS\x0a\x18\xc5\ +\x1c\xba\x12\xc0\x12\x86C<\x82\xf7\x110\x80#|\x0c\ +\xc5\x12~\xe3\x9d\x87\xfb\xd0\x13VMUEw\x11\xdb\ +\xf8\x9e\x15oPK\xf2\x8a\xc7\xf7\xa3\x8c\xc5\xb8\x07\xb5\ +\xac\xb8\x81\xc3$ob\x06?B\xbe\x857hD\xc0\ +W\xec\x85\xf8\x1a\xab\x19p3Lz\x10\xf2_\xd8\xc7\ +\xb7\xf4\x14\xe2\x0e\xaf\xa3\xe1i\x1d\x87\xf7Q4\xe2E\ +Z\xc6$\xce\xdc\x9f\xc2\x97\xe0Wp\x91\x00N\xda\x01\ +\xa60\x1b\xe2\xb7I\xc3\x876\x13\xfc\x8dF\xfe3\xfd\ +O\xc7\xb8\x92|b\xa7\x80&\xfe\xe0\xf6\xb9\x80\x16v\ +S\xe3\x0e3\xc49\xa5\x12)N:\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x01\x1b\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0e\x00\x00\x00\x0d\x08\x06\x00\x00\x00\x99\xdc_\x7f\ +\x00\x00\x00\xe2IDAT(\x91\x9d\xd1/K\x83Q\ +\x14\x06\xf0\xdf\xe6\x8aE\x98_`&\x83U\x10\xbf\x82\ +\x98\x9d0\x16\x945\xbb \xd6\x05\xcd.\x88}j\xb1\ +\x0cV4\x1b_\x164\xac\xb8b\x96\x81\xc5 2\x16\ +v\x06\x97\xd7wC|\xe0p\xcfs\xce\xf3\xdcs\xff\ +\x94\xb2,\xbb\xc0\x99\xdf8\xc1u\xe4}\xec\xa7\xcd2\ +\xeePG7j\xc3\xe0O\x89\xee\x12\xc7\xf8\xc2\x18\xcd\ +\x0a^#z\xd8\xc4\x0e\xaa\x18%\xc6g\x1ca\x15\x0d\ +\xf4\xcaI\xf3\x1b\x07\xf8\xc0\x15\xb6\x93^\x1d-tb\ +\x80\xd4\x08\xefh\xa2\x82\x07\xacc\x037\x18\xe04\xbd\ +c\x1e\x8fh\x87\xa1\x8b\xdb\xd0\x1d\xc6\xa9\x88\x9d\x8b\xd0\ +\xc6.\xf6\x827\xf0\x96\x0a\x8a&\xc2\x04\xe7\x91\x0fq\ +\x9f\x17,2\xc2On\xfd\xb3q)\xfem,z\x9c\ +\x15\xacE\xccy\xd5\xec\xde\x9f\xcb\x8c[x\xc9\xf1\xb1\ +\xd9\x1f\xd7\xe6\xc5)P\xcb+^\xfd\x05+R\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xcf\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x01\x96IDAT8\x8dm\xd3M\x88\x8eQ\ +\x14\x07\xf0\xdf\xf3\xf4R\x83\x8d\x05\xc5\xbc\x14)\xcd\x8a\ +\x95(R\x13\xa5\xac\xcc\xc6GMJY\x8cX(K\ +\x99\x8c\x05\x9a\xb1b\xc3b\xc6\x82R2\xd9\xc8(_\ +eA\x16b\xa1\xde\x84\x89Y\xccBB\xa4\x19\x1f\xc3\ +\xe2\x9e\x9b\xdb\x93S\xb7\xdb9\xf7\x9c\x7f\xff\xf3?\xe7\ +V\x9dN\xc7\x7fl\x1b\xfa\xb1\x05m\xfc\xc6\x1bL`\ +\x0c/sb\xd5\x00X\x82\xab\xd8^\xc4>\xa3\x85E\ +\xe1\xcf\xe1\x22\x8eb\xb6.\x12\xdbxZ\x14\x7f\xc3q\ +,\xc7\x0a\x9c\xc3\x0f\xd4\x18\xc0=,\xc8\x0cj<\xc6\ +\x06\xcc\xe0\x12\xce`\xba\xd1\xda\x1a\x0ca\x0f*\x8ce\ +\x80\xfd\xb8\x8c)\xf4\xe2uQ\xd4\x1d\xa0\x1f\x8b\xd8F\ +\x5c\xc1\xea\xdc\xc2\xc1\xb8\x87\x1b\xc5\x83x\x8bI\x9c\xc4\ +\xc2\x88?\xc1mT5\xba\xb0)\x1e\xde5(\x0fc\ +/n\xe20\x9ec}\xa1\x91VP\xccLv\xe2\x96\ +46\xf8\x8e\xf18-i\xac\xfb\xd0\x87u\x19\xa0\x9c\ +\xc44\x0ea-\x1e\xe2\x0e\xbe\xc6\xdb/<\x88C\xd2\ +L\x8d\xf7\x98\x8d\xe0+\x9c\xc7(\xce\xe2\x83\xb4<\x03\ +X\xdah\xefE\x06\x98\xc1\xfd\x08\xce\x8b\xfb\x19zp\ +,\xee\xc1L\xb9\xb0\x1e\xfem\xe2\x0eI\xd5I\x1c\x08\ +\xfa\xa5U\xf8S\xf8}\xb8\x86\xb9\xdc\xff\x84$\xd4\xaa\ +\xe8q\x5cZ\x9al\xb9xs\x80\xdf\x08\xb6\xa7K\x01\ +\xfb\xa5m\x84]\xd2\x87\x19\xc1b\xac\x8c\xa2G\xd8\x1a\ +9\xa3\x18j~\xa6.\x9c\xc2\x11\xcc\x8f\xd8\xcfh\xa1\ +\x15\xfe'\x9c\xc0\x85R\x83\xa6\xb5\xb1[\x9a\xfb2i\ +\x84S\xb8\x8b\xeb\xf8\x92\x13\xff\x02w\x5ckPMg\ +\xdc\xa5\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\x86\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x0e\x08\x06\x00\x00\x00\xf0\x8aF\xef\ +\x00\x00\x01MIDAT(\x91\x85\xd21K\xd6Q\ +\x14\x06\xf0\x9fW\x0a)jhh\xc8h\x91\x1c5\xe9\ +PRIC\x82\x0e\xd9 !\x82\xd1j\x1f \xa8o\ +\xd0\x16\xb4\xb4\xd4\xe6`\xb4DB\xadA\xe0\x10\x1dT\ +hlh\x11A\x8c\x8ah\x10\xa9lx\xef?^\xfe\ +\xf0\xf2\x9e\xe9\x9e\xe79\xcf}\xce9\x9c\x01\xad\xc8\xcc\ +\x9bX\xc6\x15\x9c\xc2O|\xc4s\xbc\x8c\x88\xbfM\xed\ +@\x97\xe8\x18V0\x8f/X\xc3W\x9c\xc6\x0cF\xf1\ +\x1e\xb7#b\xaf-^\xc3\x1c\x1e\xe0qD\xfc\xee\xe2\ +\x0a\xee\xe2)>a*\x22\x0e\x1ar!3\x0f3\xf3\ +~{\x8c\xd6H\xb3\xb5\xee\xe1\x7f\xe7\xcc\x5c\xc7qL\ +D\xc4a\x9f\x0f^\xe0\x1a\xce\x95\xcc<\x89I\xac\xf6\ +\x13\xd6X\xc50F\x0b.\xa3`;3\x8f\xf4q=\ +\x8a]\xfc\xc1\xa5\x82G\x95\x1b\xc7\xab>\xaeou\x96\ +:\x88{\x05\x17*1\x86\xc1\xcc\x1c\xe9\xe1:\x82)\ +|\xab\xd0\xc5\x82\x1f5\xb9\x8e7X\xea\xe1z\x07\xef\ +p\xb5\x01\x0a>\xd7\xf7\x10~a\xb1\x87xI\xe7p\ +\xa6k\xbeY\xf0\xba\xab\xe0\x06\xf62s\xac\xd5\xf2$\ +\xcec\x1f'*\xbcR\xf0\x04\x9b\x15\x98\xc7\x87\xae\xb9\ +\x9a\xd8\xc7\x16n\xd5|\x03\xcf\x9a#9\xab\xb3\xe93\ +X\x88\x88\xf5v\xcf\x999\xa1\xb3\xed\xef\x98\x8e\x88\x9d\ +\x7fI\x1dq\x82#,l\xf1\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x01\xc6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x11\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\ +\x00\x00\x01\x8dIDAT8\x8d\x9d\xd3M\x88\xcda\ +\x14\x06\xf0\xdf\x9d\x99\xcd\xd8h\xc6,$ERS\xcc\ +4\xa5\x135\xd9\xcclg(\x12\x1b\x0b)\x16>v\ +\x16\xa2,(\xcdF\xcab\xa6\x1b[\x85\x22+K\xf9\ +X\xb9\x0ee!#D\x16\xa8!\x09\xc9\xc2\xc7\xe2\xff\ +\xcet\xfb\xd7\xcc\x8dS\xef\xe6=\xe7<\xcfs\xbe\x1a\ +\xfe\xd32\xb3\x89\xfd8\xde\xe8\x108\x82\xb5X\x85\xd5\ +\xe8F\x17\xd6c\x1bz\xd0\xea\xe9@x\x06O1\x83\ +\xe5\xd8\x83e\xb8\x82\xbeBp\xa2{\x09\x15\x1bp\x16\ +\xc3\xe8G/\x9ax\x84\x83\xd8\x87\x81\x88hv-\x02\ +\xb0\x157\xf0\x10o\xd1\x8a\x88i\x0c\xe04\x0eG\xc4\ +\x17\xfcVj\x9aO\x5c\x89\xbdE\xe2\x1b\xac@\x94w\ +)3[8R\x00~df?>B#3\x03\xc7\ +\xd0\xc0\xbb\xc26\x8a\x8b\x111U\x08\xee\xa8\x9ay\x0b\ +\xcf\xf1\x12?\xf1=\x22\xee\xf7\x14\xc6\xed\xc51\x8b\xc7\ +\xb8\x80'm\x15\xee\xc4H\x01\x1a\xc4I|\xc2\x98\xc2\ +.3\xc7q\x0d\xbb#\xe2\xf6b\xcd.\xb1S\x98\xc0\ +xD\xccQ\xcd\x5cI\xdc\x81\xcb\x999\xf9/\x00\x0b\ +J\xda\x82\xb6\xe0\xaejt\xdfj\xbeI\x9c\xc7h;\ +\xc0\x82\x926{\x8d\xafu\x80b\xef\x8bo\xae\xee\xa8\ +o\xec\x90jCef/\x8e\xe2\x05n\xe2\x19\x063\ +\xb3;\x22~-\xa5d\x08\xb3\x99y\x08\xaf\xb0\x19\xa7\ +\xf0@5\xf6\x0fXWWR\x07\x19\xc6\x01\xd5qM\ +D\xc4.l\xc29Lc\x0d6v*\xe73\xc6\x22\ +\xe2\xde\xfcGD\xfc\xc1\xd5\xcc\xbc\xae\xba\x97\xbe:\xc8\ +_\xc6l\x83y\x0c\xda\xbaW\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x01\x89\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x15\x00\x00\x00\x14\x08\x06\x00\x00\x00bKv3\ +\x00\x00\x01PIDAT8\x8d\xad\xd4\xb1K[Q\ +\x14\xc7\xf1O$\xb4\xee\x12j\xabq\xaaYJ\x05\xc9\ +_P\x07\x17\x07\x17\xc1BE:T\x90\xae\x9d\x0a\xed\ +\xa0t\x13\x97v\xc8\xd8\xc5\xc1E7\xdduN\x16\xa5\ +\x94\xd6M#m\x05'\x95XQ\xe2\x90\x9b\x12\x02\xef\ +\xbd\x9b\x92\x1f\x5c.<\xce\xf9\xf2;\xf7\xc7y\xb9f\ +\xb3)I\xb5Z\x0d\x9e\xe3\x00oQ)\x97\xcb\x89\xf5\ +m\x0ddV\xfc\x87b\xa1?P\x8d\x85\xe6\x92\xc6\x0f\ +\xa3\xc3C\xdc\xe16\x16\x1a\xe3\xf4o\x00\xe6\xf1\x1a\xab\ +\xfd\x80\xe6\xb1\x88\xef\xf8\x8a\xc7\x18\xc7`RC\xd6\xf8\ +S\xa8\xa0\x94\xd0\x7f\x86S\xd4q\x1c\xee\xbd|\x86\xcb\ +}\xac\xe1#\xc6\xc2\xb7?\xf8\xd6\x01\xfb\x85\x93p\xd7\ +\xf1;W\xadF\x85\xfa\x00\xcbx\x8f\x1d\xbcI+\x8e\ +y\xd3\x12n\xf0\x19O\xb1\x9d\xd5\x10\x03\xdd\xc2\x86V\ +8W\xd8\xcdj\xe8|\xd3A\x8c\xe0\x09F;\xee!\ +\xbc\xc2\xbcV\xfa\x9f\xb4B\x91\xb4\xb2y\xbc\xc44&\ +\x03\xb4\x90b`\x093x\x81\x9fiN7\xc3\xe9t\ +\x5c\x0c.\x8bx\x87\x09\x9cc\x1d_p\x99\x04lC\ +\xbbu\x8d\xa3p`\x01\x1f\xb4\x82\xbaH\x83\xa5A\xbb\ +5\x8bF\x0c\xac\xad\x98\xf4\x1bx\x86\xb9Xh\xe2\x9a\ +\xf2oU\x0b8\xc4\x8a>\xfe\xa4\x87\xf1(\xcab\x0f\ +\xd0\x9eu\x0f\xb5UQ\xc4\x18\x14\xeam\x00\x00\x00\x00\ +IEND\xaeB`\x82\ +\x00\x00\x01J\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0d\x00\x00\x00\x0d\x08\x06\x00\x00\x00r\xeb\xe4|\ +\x00\x00\x01\x11IDAT(\x91\x85\xd2\xbf+\xc4q\ +\x1c\xc7\xf1\xc7\x9d\x93\xe4\xa2\xcb \x8b\xb2(\x7f\x00\x03\ +e2\xa0\x94\xd1 \xeab\xb1 \x0b2#\x93,\x16\ +\x89\x0c&E\xba\xc2\xa0\x84\x81\xbe\xc9$\xeb1\xa9\xeb\ +&\x8b\x1f\xc9p\x9f\xab\xef\xe9\xe2=\xbe?\xaf\xe7\xe7\ +\xf5\xfa|z%\xa2(\xca\xa3M\xe5\xaca\x11\x0bX\ +\xfduv\x9d\xc24\xd2\xd8@\x0b\xe6p\x11\x04Gx\ +\xc5\x16^\xb0\x84B\x0a\xb9 x\xc3\x09\x06\xb0\x19v\ +O\x98G\x0ac\xb8\x85d\xcc6\x87\xed\x00M\x85\xdd\ +0&\xb1R\x06 \x11EQ\xcc\xe2\x1c\xcb\xe8\xc0z\x5c\x14\xff\ +\xbdN\xdc\xe3,\xc4\x83\x1a\x5c\xa2\x07C8-;e\ +\xd0\x8c=\xbc+\xd5\xa7.@)\xa5\x86|`G\xa9\ +n\x99$\x8a(\xa0\x0bMxD6@Y\xdc\x85K\ +Z\x91G\xf1\x07i\x04=o\x03\x18\x5c\xb7\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x02\x15\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x13\x00\x00\x00\x14\x08\x06\x00\x00\x00oU\x06t\ +\x00\x00\x01\xdcIDAT8\x8d\x9d\xd4]h\xcfQ\ +\x1c\xc7\xf1\xd7\x7ff)\xe5\xa15\xca\x05\x174\xb5\xd4\ +\x94\x8b\xb1%\xb1\xac<\xa4(S\xd4.\xdcz\xb8q\ +\xe7F\x89\xc2\x8dZ+)\x96I\xc9\xe4!\x91\x9a\x84\ +\x95q\xf1\xbf\xf0\xfc\x90;#\xa1\x11.H\xcd\x5c|\ +\xcf?\xbf\xfe\xfe\xff\xfd\xd6N\x9d\xce\xefw\x1e\xde\xe7\ +\x9c\xcf\xf7\xf3=\x85b\xb1h\x02\xa5\x06\xbbS]\x88\ +W8\x8a\xb3\xd9I\xb5\x13\x00MA/\xba\xd2\xff\x07\ +4\xa1\x0f\xd3q\x22\xbbc\x1e\xe8L\x02\xbdG+\xe6\ +a#\xc6p \xcb\xc8\x83m\xc5z\xbcD\x1b\x1e\xa4\ +\xfe\x1b\xf8\x84\xb9\x98Q\x9a<\xde5g\xe2\xb9\xd0\xe8\ +\x07F3c+1\x07\x1f\xf1}\xbc\x93M\xc3)\x8c\ +\xe0Ij{Q\x97\xc6\x97\xe2\x0a\x0a8\x84?\xd5N\ +6\x0bW\xb1\x0a_q_\xe8\xd4\x85~!\xfe\x00\xea\ +q\x1c=\xd9\xc5YX\x03n\xa1\x19/\xb0\x0d\xef\xd2\ +\xce\xab\xf1\x13w\x84F=\xd8W~\xa5\x12lv\xda\ +\xb1\x19\x0f\xb1\x01\x97\xb1\x1c\x17D\xe4v\xa4\xf9G\xb0\ +?\xf5\xfd\x07\xab\xc3\xcd\xa4\xc5\x10\xd6\x09Q\xcfc\x85\ +\x7f\xfe\xfa\x85\xbd2\xbe\xaa\x04\xdb\x8e\x16<\x126(\ +E\xe7\xa4\xd0\xaf\x15SqOD\xafj\xa9\xc1\xb2\xf4\ +\xdd\x8do\x99\xb1\xcd\xb8\x8bMx\x9c\x07*\xc1\x86\xd3\ +w{\xa6\x7f\x0f.b\xb10\xec\xeb\xfd7\x08\xdf\xb5\xd4\xa6\xe3w\x88\x1c\x5c\x92&\ +<\xc3.\x0cV8@\xbf\xc8\xcfF!\xc55\xe1\x82\ +\xa6B\xd9\x134?\xb5\xc3*\x84>\x95v\x5c\x12\xe9\ +\xf6[\xb8a\x04[\xca\xd3\xe9m\xaa\xd5@p[\x04\ +e,\x81F\xb1\x16\x83y\xafF\xa5\xd2\x88\xd3\x227\ +\x89g\xea\x1c\x16L\x06v\x1d\x8b\xc4\xd5:\xf1Y<\ +\x96\x03\x93\x81\x1d\xc6\x1b\xac\x11\xf6\xe9\x10\xd69\xf8\x17\ +-\x83l\x7f\xf5\xb9\xae\x1b\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x02\xff\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\ +\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x00\x06\ +bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\ +\x00\x09pHYs\x00\x00\x0b\x12\x00\x00\x0b\x12\x01\xd2\ +\xdd~\xfc\x00\x00\x00\x07tIME\x07\xe0\x01\x08\x0f\ +\x12,c4\xebp\x00\x00\x01\xeeIDATX\xc3\ +\xed\x95\xbdk\x14A\x18\xc6\x7fo\x12D\x09\x09\x82 \ +h#H\x02\x16I\x99\xbf }\xdap\xdb\xc8\x92\xca\ +\xe2\x16\xff\x00\xc12U\x88\xec\x15!\xd5\x14\xe2\x1e)\ +\x02Q\xc4t6\x92\xc2\x90\xc6B,\xceBN\x11<\ +\x0c\xf9 \x09\x5c>nR\xdc\x9c\xbeYw7\x93\x8f\ +\xce}\x96\x81w\xe6}v\xe6\xd9g\xde\x99\x85\x12%\ +J\xfc\xef\x90\xbcD%\xac\xde\x03\x06]\xd7*\xfeA\ +\xdd\xd4~\xa6\xb8\x0f\x81\xfb\xc0\x100\x00\x1c\x01m`\ +\x17\xd8\xac\x9b\xda\xb7\xbcu\xfa\xf2\x95\xc9\xa2 \x0d\xa0\ +!\xc8W\xd7\x1a@3\x08\xa3\xf5 \x8c\x1e)\xeec\ +A>\x08\xf2\x0ex#\xc8\xaa \xef\x05\xd9\x00\xee\x04\ +a\xc4\x85\x05\x00\x87\xea\xeb;\xca\x85~`\x02\xf8\xa8\ +&>*\x98g\xb7h\x0b\x06\xce\xdd\xa4\xae\xedS\x89\ +\x89\xdf\x06at\x17X\x03F\x80a\xe0)\xf0\x22\xc5\ +\x9f\x06\xda\x89\x89_{\xcc\x9d\xef\x80uO/\x06H\ +L\xdc\x02\x9e\xdb?Y;\xa9\xb9\x8a\xe7\xb5x\xa1\x03\ +\xe2\xea\xd3b\x11D\x17\xeb'\xf9[\xbb\x0f4W\x8b\ +\xbd\xb2\x80\x02\xec\x03\xc7\xee\xdd\xe1,B\x10FO\x80\ +&\xb0e\xb1;\x82|NL|m\x02\x8e\x81\x13\xf7\ +\xee\xcd\x8c\xfcR\xaa?v)\x07\xb4\x95)[\xc5b\ +{\x9e\xf7g\xe4\xcf\xed{\x09H\xd5\x80N\xdd\x12\xe4\ +\x86\x8b\xdb\x9a\xeb\xf8\xd3\x82\x9c$&^\xf6\xb1\xb3\xcf\ +\x87\x94\xc2m\x15og\x11|\x17\xbf\x88\x80\x8e\x8a'\ +U\xfc\xe5\x12\x1fp\x06\xbe50^\x09\xab[\xc0(\ +\xf0\xccb-\xdd\x0b\xeaU\x9a\x0bP\x09\xabU\xa0\x05\ +\x1c\xb8\xb6\x0d\xfc\xaa\x9b\xda\x0fo\x01\xbd\xb3\xefj`\ +V\x0bs{\xbe\x9a\x98xEs]~)}/\xb8\ +\xfe\x18\xe0/\xc0\xa9n\xd1=r\x1a-\xe0eb\xe2\ +95\xb6\x07\xfc\xe6\xdf\x7fB\x07\xf8\x0e,\x90\xf3\xbf\ +(\x120\x935X7\xb5\xac\xe1y\xd7\xb2\x9c\x04 \ +\xef\x22*Q\xa2D\x89S\xad\x83\xa9\xe2\x81\x96I:\ +\x00\x00\x00%tEXtdate:cre\ +ate\x002016-01-08T1\ +5:18:44+00:00\x8e\x05\xfd\ +\xe0\x00\x00\x00%tEXtdate:mo\ +dify\x002016-01-08T\ +15:18:44+00:00\xffX\ +E\x5c\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xeb\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x11\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\ +\x00\x00\x01\xb2IDAT8\x8d\x95\xd3Oh\xcfq\ +\x18\x07\xf0\xd7\xf7\xb7%s\x908(.\x8aYM\x19\ +y\x14\x8a\x1c89\xb98\x92\x9b\x96\x7f3\xb2&\x9b\ +?5\x89\xb3\xc2\x8a\x5c8)M\x9a\x03Jqz\x0e\ +\x86\x83\x03\x07\xb2\xd5\xe4_\x88R3\x87\xdfw\xf5m\ +~#O}\xeaS\xef\xe7\xfd\xee\xfd|\x9e\xcf\xbb0\ +Cef+\xce\xa1\x1d\x0f\xd1\x13\x11\xef\x1b\xf5\x16\x0d\ +\xc8\x813X\x8d\x05\x15h\x14\x8fq$\x22^7\x14\ +\xc9\xcc-\xe8\xc7J\xcc\x9d\xc9!\xde!q4\x22\x9e\ +C\x91\x99;p\x18+0\xe7/\xe4\xe9\xf5\x09#\xe8\ +\xada\x02\xcd\xe5\xf9\xdf\x9a\xc0\xaf\x022\xb3\xc0nt\ +\xa3\x0dM\xffp\xf0\x04]\x111\x02\xb5\x12X\x8e]\ +X\x88>\xbc\xc0\xe44\xf2\x07\xdc\xc1N\xacCWf\ +\xce\x83Zf>\xc2S\xbcA[D\x0c\xa0\x03\xa7\xf0\ +\x0a\xe3\x18\xc2\x86\x88\xd8\x16\x11\xb7\xd57\xb7\x0c\xe3\x99\ +y\xbdY}m\xab\xb0V}3\x0f\x22\xe2'Nd\ +\xe6Y\xcc\x8f\x88\xd1);\x99Y\xc3&\xb4\xe23\xee\ +O\xbd\xc9,\x9c\xc4\x01\xbc\xc5\xfe\x88\x18\xae\xceR\x92\ +;\xcb\xbeB}\xa3W\x22b\xb2\xc8\xcc\x96\xd2zg\ +i\x7f\x08{\xf1\x11\xfb0\x8cC\xe8\xc1w\x9c/\xef\ +\x05\x8ec\xb0\xc8\xcc\xaf\x98\x8d\xed\xe5\xbc2\xb3\xb9$\ +\xf6\xa2\x05c\xe8\x8e\x88\x9b%^\xe04\x8e\xe1e\x91\ +\x99m\xb8\x88\xf5\xb8\x85\xce\xa9\x8cdf\x13\xda#\xe2\ +Ye\xac\x0e\x5c.\xdf\xef\x06\x0eV\xbf\xfd\x12\x0cb\ +#\xeebOD\x8cU\xf05\xb8\xa4\x1e\xc8k\xea\x19\ +\xfaB\xe3\x00..\x9dm\xc5=\x5c\xc0\x00\x96\xe2\xaa\ +zf\xbeU9\x7f\x88T\xc4\x16\x95\x02\x9bK\xfb}\ +\x11\xf1\xa3Q\xefos\xf3\x98\x0b\x981\xe3\xce\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x02\x91\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x14\x00\x00\x00\x11\x08\x06\x00\x00\x00\xddD\x8c\xbe\ +\x00\x00\x02XIDAT8\x8d}\x93QhVe\ +\x18\xc7\x7f\xcf\xc7>(u\xcb\xae\xc4\xd01\x91\x85|\ +Q\x9a\x9eH\xac\xad\x18\x12\x14\xee\xb2\xb0\xebH(\x1a\ +\x0c\x04a\xdd\x85\x17\x0e\x12/\x82\x04\xe92\xbf`\x22\ +\x08\x0e\xbb\x182%\xb5\xa0\xde\xb352\xa9\x1b\xa9\x8b\ +\xe9\x96\xae\xf8J\xc4\xc2\xfau\xd1\xfb\xe9\xd9\xe7\xf2\xb9\ +9\xe79\xe7\xf9\xff\xfe\xff\x07\xde\x17:J]\xa3\x8e\ +\xa8g\xd5y\xf5\xb6\xba\xa4\xce\xa9\x1f\xa9;;5Y\ +\x87\xfa\xc0\xc7\xb7\xd4\x9b\xea\xd5\x0c|^\xedS\x9fR\ +\x87\xd5O\xd4[\xea\xa4\xfa\xc4C\x81\xd9\xfd\x8e\xfa\xae\ +\xfa\xb1\xba\xf1\x7f\x92lR\xcf\xa9\xd7\xd4-+\x02\xd5\ +\xd1\xbc\xda.\xb5\xa6\x9eZ\x09V\x11\xd7\xd5\x93y\x93\ +\xb5\xcb\x80joN6\xa6nW_S\x0f?\x0c\x98\ +\x01\xab\xd4\x9f\xd5\x0f\xab\xc0.\xe0m\xe0Z\x9e\xdb\x09\ +\xec\x02\xba\xd5O\x81_\x81q\xe0zD,\x03F\xc4\ +mu\x0a\x18P\x1f\x01\xee\x00\x84:\x0bLD\xc4x\ +vz\x07\xb8\x1c\x11\x17\xd4\xae\x99\x99\x99iu.\x22\ +N\xf7\xf4\xf4|\xd1\xdf\xdf\xffg\x9e[\x0d<\x0b<\ +\x07\x5c\x8c\x88o\x00j\xc0\x93\xc0\xb7\x15\xf3.\xe0\xaf\ +\x9c\xe2\xaez\x1exO\x9dj\xb5Z\x8beYN\xa4\ +\x94^_XXx\x1c\xf8\x0ah\x02\x7f\xb4\xc55\xe0\ +Q\xa0U\x01.\x01\xab*}\xb3\xf2\xfe\x98\xfa\x06p\ +b~~\xbeY\x96\xe5\x9a\x88\xf8\x05X\xac\x02\x17\x81\ +\xde\x8a\xe8\x07\xe0\x99vS\x14\xc5\x8f@by}\x07\ +\x0c\x15E\xd1\x0e\xf2{\x15\xf8%\xf0Jex\x0ex\ +\xa9\x03\xd0\xec\xe8\x9f\x06\x0e\xb6\x9b\x88\xf8\xbb\x0a\xfc\x0c\ +xS\xddP\xf9\xf9\x93\xda\x00Pw4\x1a\x8du\x80\ +\xc0\x11\xe0b\xd6\x8e\xa5\x94\xf6u\x18\x11j\x0d\xf8\x1e\ +\xa8\x03G\xf3\xb3\x17\xd8\x03\x5c\x00\xae\x00\xcd\xb2,_\ +\x05\x8e\xe5\x10\x1f\x00\xefg\x93\xe1\xa2(>\xbf\x07\xcc\ +)\x1aY\x5c\x02\x07\xf8\xef\x5c\x0e\x02/\x03#\x11\xd1\ +q\xeb!\xa5\xb4\x1b8\x0et\x03\x83EQ\x94\xed\x95\ +\x89\x88+\xc0\x00\xb0\x19\x98\x00\x86\x80I`\x1a8\xa1\ +n\xcd\xc6\xf56\xb0(\x8a\xb3\xc06\xe0\x12p&\xa5\ +\xd4w/a\xbb\xf2a\xdd\x0f\x8c\xe6\xd5\xbf\x06~\x03\ +\xb6\x00S\xc0\xa1\x88\xb8\xd1\x91\xb4\x96\xd7\xdf\x0b\x0c.\ +\xbfO\xf7\xc1u\xe0\x05`+\xb0\x1e\xb8\x09\x9c\x03f\ +#\xe2\x9f\x954)\xa5\x17\x81}\xff\x02\xe28ya\ +\x96\xfd\xc4\x97\x00\x00\x00\x00IEND\xaeB`\x82\ +\ +\x00\x00\x04x\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\ +\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x00\x06\ +bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\ +\x00\x09oFFs\x00\x00\x01@\x00\x00\x00\x00\x00s\ +\x05\xb7\x16\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\ +\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\ +\x07\xdf\x0c\x12\x0e\x1b\x0b$&IW\x00\x00\x00\x09v\ +pAg\x00\x00\x03@\x00\x00\x00 \x00\xbe\xe6\x89Z\ +\x00\x00\x03=IDATX\xc3\xe5\x97MhUG\ +\x14\xc7\x7f'\xba\xf3\xa3`\xa3Q\xb1\xa9-(*\xb5\ +(6\x96 \xadP\xdc\xc4\x16\x04\xad\xe0}\xbb\xa1\xb8\ +\xb0p\x1fZ\xa8\x8b\x82\xe8\xa6\x14\xba\x11\xee\x05\x15\x17\ +N\xa1p/h\xa4.\x0b\x8d\xa4\xa4i\xd4E\x055\ +~ H\xd1*\xa95DP*\x88\xf6\x9d.\xdey\ +\x8f\xf1a\xde\xbb\xe6C\x17\x9e\xcd\x999\x1f\x9c\xff\x9c\ +\xff\xdc\x99\xb9\xf0\x8aE\x8a\x04\x95\x5c\x19`\x8f\xa2?\ +\xe4>\xbd\xff*\x00\xfc\x02lR\xf4\x0fA>\xc8|\ +\x12\xfa\xf6\x03\xf3\x81S\x99O\xfa\xcc\xd6\x03lVT\ +\x049\x92\xf9d\x18 r\xf1^A:\x81\x93\x99O\ +\xfa\x01f\x16(~\x14\xd8d\xd3u\xc0I`[\x10\ +\xf21\xf0\x090\x0f\xe83\xdb\x97\xc0g6\x1e\x05\x86\ +K\xae\x8c\xa2_\x03\xed\xc0O\xb5\xe4\xb6f\xc5#\x17\ +\xefUt\xa7\xa2\xaah\x05PE\xb7F.\xfe\xbe\x16\ +\xa3\xe8iEQ\xf4\xbd\xc0\xd6\xadh}l\xfam\xa0\ +\xddbO\xb7\x04`\xbc\x7f+\xc8cA\x9e\x0a\xd2\x06\ +\xfcg\xf3\xafJ\xae\xdc^\xe5P\x06\x05A\x90w\x22\ +\x17Sr\xe5E\x82\xbc)\xc8C\xe0\xb1 \xdd\x16\xb7\ +\xca\xf4\x95\xdc\xa7\xb4\x04`\xb2\ +y/0\x02\xcc1\xb0k\xcc~.\xac\xd3\x94\x82\xdc\ +\xa7c\x99O\xfe\x01\xc6\xcc\xf4 \xf3\xc9hP\xbc\x06\ +\xf4b\xb0G6\xda\xf8\x14p\xd9\xc6\x1b\x80\xb5\x0d\x80\ +[\x03\x088\x95\x1a\xa7\xe3\xf8\xcf\x99\xbfK\xd1.\xe3\ +y\x10\x18\xb2q\xb7\xa2\xab-\xf6\xec\x0b\x030\x8e\x9b\ +\xf9\x07\xcd\xdf#\xc8rAn\xe5>\x1d\x03\x86,w\ +\x8b \x8b\x80G\xb9O\xaf\x85\xb9-?\xc3\x82\xf2\xbb\ +\xe9\x95\xa6\x7f6}\xde\xf4\x0a\xd3\xfd\x8d\x89\x85:\xd0\ +J2\x9f\xdc\x00\x1eA\x9d\xa7~\x80\xdc\xa7\x0f\x80+\ +A\xe8\xaf\x13\x02`<\xb6\x8a9\x03\x88\xc5\x0e\x04\xf6\ +\x0bA\xfePc^!\x0aj\xfc7\x03!\xc8nE\ +\x97\x0b\xa2\x99O\xae\x06\xf6\x03T?\xc9\x8a\xa2}\x8d\ +yS\xb5\x07\xb0\xf3~\xf89\xf6\xeb\xc0\xf5\xf1\xf2\xa6\ +d\x0fLF\x0au\xa0\x15\xff\xd3\x0e\xa0\xc8\x1eh&\ +v=\xc7\x8an\x0e\xef\x01xy\x14,\x03z\x80[\ +%W^\xfa\xc2\x1d\x00*\xa6\xe7\x96\x5c\xb9\xa3\x96\xa7\ +\xe83'\xe48\xf3\xa7\xc0\x1bfz\x0b\xb8Tr\xe5\ +O3\x9f\x0cT\xbb[@\x22\x17\x1f\x07\xb6O\xb2\x0b\ +\x15\xab'\xc0\x13`q\xee\xd3\xd1\x22/\xa2\x13\xc0\xe7\ +\xc0\xdf\xf6$\xab\xd3V\xb0\x03\x15\xe0]`\xa5\xa2j\ +\xfe\xdf\xa8\xbe\x94\x9aS\x10\xb9\xb8\x97\xea\xf3\xeb&\xd0\ +\x95\xfb\xf4\xdeD\x96^r\xe5]\xc0![\xfd\xb1\xcc\ +'_\xd4|mA\xd0\xda\x86\xa4<(\xde\x9d\xf9d\ +B\xc5Mf\x98\xfe&,^\x07\x10\xb9x\x81\xa2\xe7\ +#\x17\x1f\xb6y\xa6\xe8\x0e\xe0\x8e\xa2\x1ff>\x19\x99\ +Dq\x14\xedUtc\xee\xd3\xef\x1a}b\xab]\x0a\ +\xfci\x9c\xfdEu\xb7\xdeV\xf4\xfd\xa9\xfe\x0fh\x94\ +\x1a\x05\xed\xa6+V\xfc_`\xfdt\x17\x0f\x01t4\ +\xd8g\x01\xfb\xa6\xbbx\x1d\x80\xa2\xf3\xec\x98m\xd3\xfa\ +\xed\xaf\xbb\x22\x17G\xd3\x0d`&\x80 ?\x02\x9d\x8a\ +>\x11d\x04\xb8\x0b\xdcU\xf4\xc2\xcb\xe8\xc2\xeb-\xff\ +\x03Q\x9bYF6Y\xf5\xec\x00\x00\x00%tEX\ +tdate:create\x00201\ +5-12-18T14:27:11\ ++00:00YM\xe3\xc6\x00\x00\x00%tE\ +Xtdate:modify\x0020\ +15-12-18T14:27:1\ +1+00:00(\x10[z\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x01U\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\ +\x00\x00\x01\x1cIDAT8\x8d\xa5\x92?K\xc3P\ +\x14\xc5\x7f\xa9\x11%\x8b\x1d\x5c\x5c\x05\x05'\xa1\xdcM\ +\xfc\x02*n\xd9\x5c]\xc5/P\x1c;\xf9=\x1cU\ +\xea\x87\x10\x0eq\x12\xc4\xa5\x11\x8b\xe0\xa6(4R\x9a\ +8\xe4EB\xdaj\x82\x07\x1e\x8f\xfb\xe7\x9c\xfb\xce\xe3\ +z\x922 c6\x92J\xbc\x08\xf8\xc0\x17\xb0\x04\x8c\ +}G\xf6f\x90\x07f\xb6^NH:\x05B3\xdb\ +\x91t\x09\x1cx\xee\x05\xef\x15r\xcbM\xb8\x02\xd2R\ +~\x1b\xd8\x04\xae\x81=`\xc1\x07\x86\xc0\xee\x1c\x0b\xbf\ +\xe1\x0c\xe8\xfb\xc0\xc4\xcc\xe2\xa6lIm \xf5\x8bD\ +\x14E!\xb0_\x93\xff\x9c\xa6\xe99\xe4?Z\xe0(\ +\x08\x82\xc3:\xec$I\x1e\x0a\x81V\xcd\x89s\xf1o\ +\x81\xb2\x85\xa7\xd1ht\x0b,\x03\x9f\x95\xda\x9a;\x8f\ +\xc0[\x96e\xe3)\x81N\xa7s2o\x8a\xa4\x10\xb8\ +\x00\xb6\x80.\xd0\x03V\x9aX\x18\xba\xde\x04\xd8 _\ +\x22\x9a\x08\x0c\x80;\xe0\x1ex1\xb3\x9b)\x0b\x7f\xe0\ +\x158&_\xef\xbe\xa4\xdeOER\x5cS\xa4\xe8_\ +uw[R\xecI\xfa\x00&MD\x1c< \xfe\x06\ +\x8cB^q\x9bC\x89i\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x01\xf9\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x13\x00\x00\x00\x13\x08\x06\x00\x00\x00rP6\xcc\ +\x00\x00\x01\xc0IDAT8\x8d\x9d\xd4M\x88\x8eQ\ +\x14\x07\xf0\xdf\xc3\xe4c\xf0\x8e\xa4\xa4X!a3\xf9\ +*55)!\x0b\x1f\xb1bMYH\x0a)+\x91\ +\x22\x89a%I\x16^5Y\xf8JV>J\x91G\ +Y(\xb11,$\xe3{2\xc64bq\xcfSO\ +\x0f\xaf\xf7\xcd\xa9[\xb7s\xcf\xfd\x9f\xff\xf9\x9fso\ +\x96\xe7\xb9\x16\xac\x1b\x07\xb0\x10\xef\xd0\x83S\xf8U\x0e\ +jk\x01h#.E\xec\x10f\xe1$j8T\x0e\ +\x1c\xd5\x04h-.\x22\xc3\x0eL\xc4\xb2\x00\xdd\x8b\xb1\ +\xad\x82M\x93J\xc9\xb0\x05\xa7\xf1\x13\x0fbM\xc2\x8c\ +\xf2\x85fe\xce\xc58|\xae$\xe9\xc40\xde6c\ +\xb6\x14O#p\x00\xe70=\xce:p\x0d\x93q\x16\ +\xdf\xfe\x05\xb6\x1a\xb7\xb1\x00/\xf1\x15\x1b$}j\xb8\ +\x89%\xb8\x87=U\x16e\xb05\xb8\x82v\x1c\xc4l\ +I\x93\xed\xb8\x80\x1b\x92\xf8O\xa4\xc6\x0c6\x02\xebB\ +/\xc6`\x1f\x1e\x06\xab\x13\x18\xc1\xf9\x88y\x8c\x15\xf8\ +R\x05\x225`\x1e\xae\x07\xa3\xfd8\x82\xc5\xd2@n\ +\x8b\x05w\xb0\xbe\x11P\x01v\x5c\x12\xf6(\x0e\x87?\ +\xc7|l\xc2L<\x0a\xe6#q^\xc3\x1c\xbcF\x7f\ +\xb9\xcc\xe5\xf8\x18\xac\x0a\x1b\x8d]\x18\x8f\xab\xa8\x97\x80\ +`w$\xe1\ +M\xd9\xf9\x1b\x87\xd0i\x8b\x22\xfc\x9f\xfe\x00\x00\x00\x00\ +IEND\xaeB`\x82\ +\x00\x00\x02\xbf\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x16\x00\x00\x00\x13\x08\x06\x00\x00\x00\x94y\xfd\x88\ +\x00\x00\x02\x86IDAT8\x8d\x8d\xd4]\x88\x97U\ +\x10\x06\xf0\xdf\x7fQ\xd0B\xdb2)\x08D]\xc9B\ +\xec\xa6!\x03\xa3\xb2 \xd1J(\x0d#\x13\x14\x0b\x8b\ +\xc0\x82\xad\x9b0\x0a\xa4@\x906BJ\x141\xb0 \ +$\xfb\xf0\x22r\xefV\xcc\x14fS\xe8\x036HJ\ +J\x0d!\xdd\xf2\x03#\xda.\xceYx\xf7M\xc9\x81\ +\xc3\xcb{f\xe693\xcf|t\xb4$3\xc7\xe1s\ +\x04\x0e\xe3B=k#\xe2\xf7js5^\xc63\xf8\ +\x0bK#\xe2@\x13\xa7\xab\x0d\x8c\xf9\x98\x80)8\x8e\ +\x07\xb1\x0c\xab*\xe8t|\x85n\xf4\xe0\x05\xack\x83\ +\x5c\x0a\xf8Z\x1c\x8d\x88\x11|\x8a\x91z\x7fCfN\ +\xc3\x00\xfa\x22\xe2\xb9\x888\x83\x1f0\xf5J\x80\x7f\xc5\ +4\x88\x88\xdd\x98\x8e\xdb\xf0\x08\x0e\xe1\xd5\x88\xd8\xd1\xb0\ +\x9f\x81_\xae\x04\xf8{\xcc\xcd\xcc\xf1\x15\xfc\x18\xbe\xc5\ +QL\xc2`\xcb\xfeN\x1c\xf9_\xe0\x888W\x9d\xef\ +o\x5c\xf7*\x94<\x81\x0fk\xf1Fe)\xf6\xb4q\ +:\xcd\x9f\xcc\x9c\x80\xdb\xf1\x12f)\xb4\xdc\x84[p\ +\xb1\x9en\x9c\xc0\xcf8\x85\x05X\x83\xfd\x11qb\x0c\ +pf\xce\xc3\x8b\xd5h\x10\x07\xf1<\x1e\xc5f\xbc\x16\ +\x11\xbb\xaa\xedU\xf8\x0e\xaf`%~\xabXw\xe1\x1c\ +\xb6bk'3{\xf1,6`wD\x9c\xad\x00\x9b\ +p7\xceD\xc4\x03\xad\xcc\x96\xe0\x0d\xa5\x83f7|\ +\xee\xa8\x01\xcd\x91\x99g3\xb3\xbb\xcdQf\xf6d\xe6\ +Hf\xceo\xeb\xaa\xfe\xeb\xcc|\xeb2\xba\xf7\xc6\xd5\ +\xb47d\xe6\xfa\x88\x18n\xe8\x17\xd4\xd4\x1e\xc2\x97-\ +\xc7\x99J\x1bv5\xee\x16b\x0b~\xc4\xad\x9d\xcc\x9c\ +Tix\x12\xfd\xf5\x1c\xc4'\x95\xa2\x0fp_D\x0c\ +5@\xf6`/\x1eG\x1f\xfe\xac\xdf9\xd5\xe4\xe9N\ +\xc3\xf8\x1a,Rx]X#\x1a\xc2u\x18V\xc6\x98\ +\xc2\xeb\x12\xa5\xc8=\x98\x88/p\x12O\xe1\x18\xe6\x8d\ +i\xb7\xc6#\xdbq\x00\x9f\xe1Fl\xc2\xdf\x15`#\ +\x1eS\x86\xe64~\xc2\x8c\x88\xf8\xa3\xf6\xf7\x85\x88\xf8\ +\xe7?\xc0\x99\xb9\x02\xef\xe0\xe1\x88\xd8W\xef\xaeG\xd6\ +\x94\xdf\x8e\x88m\x0d\xfb]\xf8h\xb4\x1dGe\xcc\xe4\ +e\xe6l\xbc\x8f\xc9x\xb3\xa1:]#\xbc\x19\xfbZ\ +\xb1\x0c\xe0\x9ev\x80\xed\x91\x1eVv/\x85+\x99\xd9\ +\xa5\x0c\xc9De\xff\xf6g\xe6\xac\x86\xcf\x90\xc2\xf5\xe5\ +\x81#\xe2$V+\x85Z\x91\x99\x93\x95\xd59S\xa1\ +f\x07\xd6c 3\xefm\xb8\x8eh\xc9\xa5\xb6[\xbf\ +\xb26\xfb\xf0M=\x8b#\xe2|}|'\x96\xe3\xdd\ +\xcc\xfc\x18\xaf+E\x1d#\xff\x02\xad\xb1\xf2$\x9a\xc7\ +|\xa6\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x02=\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x13\x00\x00\x00\x12\x08\x06\x00\x00\x00\xb9\x0c\xe5i\ +\x00\x00\x02\x04IDAT8\x8d\xad\xd4\xcfKTa\ +\x14\xc6\xf1\xaf6\x5cc&\x09\x84\x89\x91\x81\x82D.\ +.t\xa3\xa9\xa0\xb6\x17\x83\xcc\x7f\xa0\xeex\xab\x8d\x08\ +\xd5JJC\x0b\xda\x14ZV\x1b\xdf\xdew\xfa!\xb4\ +S\xaaM\xe0\xca\x19#52\xf1N\xbd\x17\xa1\xc8\x8d\ +\xad\xd2`\xae\xc9\x85\x99\xdb\xa2\x94\x8c\x08g\xecY\x9d\ +\xc5\xe1\xc3\xe1p8%Z\xeb\x1bV\xc2\xee\xa3\xc8\xbc\ +\x9aIm\xd7%Z\xeb\x00\xb8\x0b\xf4Z\x09;\xd8\x0b\ +V\x0a\xb0\xbc\xbc\xdc\x03H%\xc5\xbeb'\xdc\xc6n\ +\xde\x1a\xe6\x83\xd6g\x80Y%\x85\xb1'\xcc\xf7}\xd6\ +\xd6\xd6p2\x99z`RI\x11.\x1a\x03\x10B2\ +<|\x9b\x85\x85\x85v\xe0\x8d\x92\xa2\xbch,\x08~\ +\xee\xfe\xfb\xe6&\xa9t\xba\x06\x98RRT\x14\x85E\ +\xa3QB\xa1\x10cc\x0fP\xea!ss\xf3\x8d\xc0\ +[%E\xac`\xac\xaa\xea(\x03\xfd\x97\xa9\xac\x8ca\ +\x18\x06\xd9l\x96\x89\x89\xc9#\xc0\xb4\x92\xe2pA\x18\ +@<\x1e\xffxu\xa0\x7f\xa3\xa1\xbe\x9e\xc7O\xc6y\ +\xf6\xfc\x05s\xf3\xf3\xd5A\x10\xcc*)\xaa\x0b\xc2\x80\ +\xd7eeeM\xb6\x9d\xd0\xdd\xdd\x16\x87\xa2QV>\ +\xaf\x90L>\x8a\xe5\xf3\xf9i%Em!\x18V\xc2\ +v\x80\x86\xd6\x96\x96doo\x0f\xef\x16\x17I\xa5\xd3\ +8\x99L,\x97\xcb\xa5\x94\x14\x8d\xbb\xc6~\x81\x9e\x95\ +\xb0\xadxm\x9a\xe6\ +\xd3\xad\xa6\xd0V\xb1\xb4\xe408t\xad\x02\xf8\xe7\xa1\ +\x1e\x88Dp2\xefY_\xfff\x9c\xea<9\xee\xba\ +\xee~\xd34\x93;0\xcf\xf3\xf0<\xef_\xce\x8ed\ +\xb3Y\x06\x87\xae\x97^\xbaxA\xba\xae\x1b6M\xf3\ +\xfe\xd6?\xfb\x1f\xe9\xfb\x01\xfd\x9c\xd6\xcao\xa8\xd4\xb1\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\x5c\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0c\x00\x00\x00\x0c\x08\x06\x00\x00\x00Vu\x5c\xe7\ +\x00\x00\x01#IDAT(\x91m\x90?(\x84\x01\ +\x18\xc6\x7f\xef\xe7\x9b.\x03\xc9\xec\x06\x8aA\xc8`P\ +$\xebe0\xd8t\x03\x83\xc5`30;\x93U\xf9\ +su\x06\x8b\xc5\x22euJ1*\x97l\xca \x94\ +\xae\xfcI\xfd,\xdf\xe9K\x9e\xf1\xe9\xfd=\xcf\xd3\x0b\ +\x99\xd4.\xb5\xa2\xde\xa97\xea\xadZW\xcbjB^\ +\xea\x88\xdaP\xb7\xd5\xc9\x9c_T\xab\xea\x89Z\xc8'\ +7\xd4!u\x8d\x7f\xa4\xae\xaa5\x80P7\x80\x01\xe0\ +\x1c\x98\x02\x8e\x80\xb3\x88x\xf8\x03\xdd\x03s)0\x03\ +\x8cEDS\x058\x05Jj\x118\x88\x88[\xb5\x1b\ +\xa8\x03\xb3)@D4[I\x11\xf1\x08\xec\xa8)\xb0\ +\xac\xf6\x03\x83@\x15XJ\x80\xb6\x5cs\x9a\x03\xbf#\ +b\x0b(\x01_\xc0\x07\xf0\x91\x00\xcfY=@\xaaF\ +\xb6\xb9S]\x07\x8e\x81v`\x02\xb8\x0e\xb5\x0c\xcc\x02\ ++\x999\x0c\xbc\x00\xef\xc0nD\xbcf\xe0<0\x8e\ +\x9a\xa8\x97jM\x9dV7\xd5\xbe\xdcw:\xd4\x0bu\ +\x11\xa0U_\x00\xb6\x81\x1e`\x0f\x18\x02\xde\xb2\xdd\x0b\ +@%\x22\xf6\x7f\x81\x5c\xdah6\xaf\x17\xf8\x04\xae\x80\ +\xc3\x88xj\xdd\xfc\x00\xa7\xf6\xbd\x96\x10\xb6\xbf\xd9\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xd0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x14\x00\x00\x00\x10\x08\x06\x00\x00\x00\x16\x18_\x1b\ +\x00\x00\x01\x97IDAT8\x8d\xad\xd4\xcf\x8bOQ\ +\x18\xc7\xf1\xd7w\x9aIJ\x8a\xac\xfc\xc8ld$\x0b\ +\xd33ca!ij\xa2\x99\xd9XY\xda\xd9Q\x9a\ +\x85\xc2\x1f )Jb\xa9l\xa7(EJ\x84,\x1e\ +\xc2\x86\x155\x89\x05\x9b\xafh\xfcfq\xcf\x9d\xb9\xa6\ +kL\x8dO\xdd\xee\xe9~\xde\xcf\xe7\xdc{\xcfsN\ +GQf\xae\xc55L\xe39n\xe0nD\xfc\xd2P\ +fn\xc38\x02\x1bq'\x22\x8e\xd4~O\x81\x86q\ +\x00\x83\x05\x1a\xc4\x15\xbc\xc8\xcc\xbd\x85\xd9\x93\x99\x0f\xf0\ +\x0c\x93X\x81\x01\xec\xca\xcc-\x7f\x04\x96\xb0\xa3e|\ +6\x22F#b\x1d\x0e\xe2tf>\xc4-|\xc1>\ +\xac\x89\x88\x11\xbc\xc4v\x9c\x9c\x0d\xcc\xcc{\x18\xc6X\ +y\xf6\xa96#\xe2>Na\x07ND\xc4\xee\x88\xb8\ +\x1e\x11\xdf\x0a\xf2\x1dO1\x9a\x99\x1b\xea7\xdc\x89\x9b\ +xW\xa0\x1a\xae\xf5\xa3\xdc/k\xd7\x13\x5c\xc2tf\ +\x1e\xee\xf9\x0b\xd4Tw\x11\xccq\xcc\xa0\x7f1\x81\xff\ +TD|\xc6W\xe6\x16\xe5\xbf\xa9-pI\x93\xb4\x15\ +\xaf\x5cJ}o\x0b\xb4?3\xfb0\x15\x11\xef\xd1i\ +\x9a\x99\xb9\x1a#\xd8\x8a~<^(\xb0\x8b!U\xf3\ +\x9e\xc9\xccc\xf8P\xbcU\x999\xa9j\xf6e\xf8\xa8\ +Z\xd9\x99f@'3\x7f\xe2*\xa6\xcc\xb5H\x9fj\ +\x07\x8c\x95\xe2Mx\x8d\xb7\xb8\xad\xea\xbd:hya\ +\xce\xe3b/.\xe0\x10&Z>\xbf\xa9\xf5\xe5\x1aZ\ +\x80y\xd3\x81\xcc\xdc\x5cfjj@\xb5;\xe6\xff\x96\ +\x09\xd5\x894_\xdd\x88x\xd5i1f\x95\x99\xe3\xaa\ +\xa3\xaa\x0e}\x14\x11\xe7\x16\xaa\xf9\x0d\xa0\xcdx<3\ +\xa5\x8a\xf0\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01X\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x10\x08\x06\x00\x00\x00\xc9V%\x04\ +\x00\x00\x01\x1fIDAT(\x91\x95\xd0\xbf\xab\xcfQ\ +\x1c\xc7\xf1\xc7\xe7v\xb8r\x19X\x94\x1feq\x07\x85\ +\xd4{`0\xf9\x03\x84R\x18\xac\x06\x832Q\xca\xbf\ +`Q\x06\xfe\x03\x0c\xba\x7f\xc3]^\x0b\x912(%\ +E\x08I_\xe12\xdcs\xeb\x9b\xe9x\xd6\xbbsz\ +\x9d\xf7\xb3\xf7\xe9=%\xd9\x84\x078l\x8c\xaf\xb8W\ +U\xb7\xa7$\x87\xf0tP\x9cg\xd7\x02\xde\xe3\xe7\x7f\ +\x8a\xcf\xf1i\x82$\xcb88(~\xc1jU\xcdZ\ +\x0f\x1a>\x0f\xcak\xd8\x86\xd9\x94\xe4\x12n\xe0\xc5\xa0\ +<\xe1(\xce5\x9c\xc0\xf5\xaaz8(Kr\x19\xa7\ +\x1b^\xe2T\x92\xb5Aw\x09WpkJ\xb2\xb5\x7f\ +{\xef\xa0\xfc\x1d+U\xb5\xd2\xf0\xabO\xff0(\xff\ +\xb1\xbeq\x0b\xb8\x8a\x0b\xd6\x171\xc2\x22\xee$9\xd3\ +\xb0\x8c\xbbU\xf5hP\x96\xe4\x1b\x8e5\xac\xe2Z\x92\ +#\x83\xeev\x9c\xc7\xd9\x86\xfb\xf8\x88\xfd\xff4\xed\xc0\ +\x01<\xc1\x8f\xb9\xfc\x0d\x8eW\xd5\xeb)\xc9nl\xee\ +\x0f\xfbp\xb3\xdf\xb7`'\xde\xe17\x1e\xf7\xda`\xd6\ +p\x11{z\xb0\x88\xb7s\x0d\xaf\xfa\xb9\x84\x93\xbd6\ +x\xf6\x17\xf1zO\xd5\x8bA\xa4\xa9\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x01\x0a\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x08\x00\x00\x00\x0d\x08\x06\x00\x00\x00\x94\xc2/8\ +\x00\x00\x00\xd1IDAT\x18\x95]\xd1\xbf/\xc3a\ +\x10\xc7\xf1\x97'\xd5\xc9h\x11F\x83\xe8\x82\xa4\xb1\x96\ +\x8d\x8dAl\x9a\x0e\xfe\x83\xae\x1d\x18\xc4`\xa71\x1b\ +\x0c\x12\x8b\xc9`\xd3\xe1;t3h\x18-\x12\x93(\ +\xea\xc7\xe0\x9e4\xf5I\x9e\xdc=w\xef{\xee\xc9\xdd\ +XQ\x14B\xab8B\x05\x1d\xec\xe01Er\x06\x97\ +X\xc01^q\x81R)\x80\x06&\xd0\xc7\x1efq\ +\x8b\xad\x0c\xac\x87=\xc7s\x9c;l&T\xb1\x14@\ +\x17\xe5\xf0oPM\xd8\xc5x\x04\x9b\x86\xeaa*\xa1\ +\x15\xbd\xa1\x8d\x8f\xf0\xdf\xf0\x95\xf0\x84\x83H\x9c\x18\xd5\ +K\xfe\xe4!~\x02\xce\x9aF/\xcf\xe1\x1d\xfb\xff\xaa\ +\x17\xd1\xcd\xc0<\xceP\x8f\xfb$Vp\x9d[\xdcc\ +\x19\xdb\xf8F\x0d\x0f\xb8\xca/|b\xcd\xdf\x0eN1\ +\x87\x0d\x0c~\x01]'-R\x7f\xd2\xe3\xe9\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x01~\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x10\x08\x06\x00\x00\x00\xc9V%\x04\ +\x00\x00\x01EIDAT(\x91\x85\xd2\xbbjVQ\ +\x14\x04\xe0/\x1a\xa3h\xe5\x05A\xd0\x17\x10\x04\x85\x05\ +\xa6P\x10D\xb0\xb5\xb0\xb2Jc\xa1\x9d\x85\x8d\x01\xb1\ +\x15\x1b\x0b\x11\x0b\xf1\x15\x22v\xa9,l\x84)R\x05\ +\x84\x08\x0a\x81\x88\x22\xa8` x\x89\xc5\xd9\x07~\x0f\ +\xffe`\xc3\xda\xb3Xkf\x0f[\x92\x0fIN\x1b\ +A\x92\x85$\x17\xcd\xc0\x5c\x92]\x5c\xc5\xbb\xc6\xed\xc3\ +S\x5c\xc6\xc3V\x8f\xc3v?\xbc\x85\x13\xb3\x94\x06x\ +\xb9\xa7\x15\xd7q\xb8\x9d#x\x81\xbf\xb89\xc2\x0f\xcf\ +\x0dIv\x93\x9c\x1d]\x99d.\xc9\xc9Y\xd2\xbd\xed\ +Gx\x82E|\xc27\x5c\xc2\xca\x14\xee\xf5X\xe5!\ +\x92\x1cL\xf2|\xe8\xa6W\xbe\xa5\x0bm\x1c\x16\xb0\x8c\ +3\xcd\xc12\xbeb\xab\x1f^k!L\xc21\x1c\xc2\ +\x0e>\xeb\xc2\x5c\x9do\xcd\xa5\xaaZ\x9bb{/\xee\ +\xe1qU}\x1f\xda\xbe\x80\xb7\xd8\x8f?m\xf3\x01l\ +O\xe2\xaa\xeag\xaf|\x05\x1fq\x0e_\xf0C\x97\xe8\ +\xea\x14\xeeU?\xbcRU\x9bIvp\x17\xf7\xabj\ +\xbd\xf56G^\xf0\x1f\xd7\xdb~\x80_\xb8\xad\xfb\xa6\ +\x1bx\x86\xdf\x93r\xc0\xfb^y\x11Gq\xbc\xddO\ +\xe1Z[8\x09o\xe6\xdb[\xefT\xd5z\x92\xf3\xba\ +\xdf\xb6TU\x1bS\x06\xc1?\xb7?\x88\x1a\xfe<\x0d\ +\xb2\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x02\xce\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x17\x00\x00\x00\x13\x08\x06\x00\x00\x00{\xbb\x96\xb6\ +\x00\x00\x02\x95IDAT8\x8du\xd5]\x88\x96U\ +\x10\x07\xf0\xdf\xbe\xec\xa6\xb5R\x86\x19\x15Kx\x11F\ +B\x118\xb1\x98e\x94PT\x0a\xf6\xa9\x90\x10^I\ +Y$\xacE7\x19!D]\xa4E\x1f.\xa1\x88\x22\ +\xde\x88X\x04]\xecEEA\x9f'D\x12\x17\xb2\x94\ +\xa4\x15\xc2\xb0\xb7R\xa3\x0f\xb2\x8b3k\x8f\xcf\xbe\x0e\ +\x1c\x9e\xe7\x9c\x99\xf9\xcf\xccyf\xfeO\x9f\x96\x94R\ +\xfa\xf1>\x02\xfb\xf0G\xae\xd58\x91f}8\x93\xcf\ +\x95X\x86\x19\xf8\x02\x9b\xf0\x0bt\xda\xe0X\x88\xe9\x98\ +\x85c\xb8\x17\x0f\xe2\xd1\xd4Ok\xd8n\xc5\x0e\xdc\x8f\ +\x1b\xf0\x0c>\xc7\xec\xf3\x81_\x8a\xc3\x11q\x06\xefd\ +\x860\x13\x17\xe3\x82<{\x00\xabR\xf7\x0fn\xc6s\ +\x98\x8b7\xce\x07>\x81\xab!\x22\xf6`Nf5\x8c\ +\x058\x95vO4|\xf6\xe2\x086\xa3\x8b\x870\xb7\ +\x17\xf8A\x5c_J\x19\xc8\x00Gq\x00_\xe3'\xfc\ +\x9b\xd5-j\xf8\xbc\x95\xcf\x93\xd8\xae~\x8b\x15S\xc0\ +#\xe2T\x02-n\x1c\xaf\xc1\x8f\xf86\xf7\x8b\x1aU\ +\xef\xc3G\xad*\xe0\xb6\xbe&p)e:\xe6\xe3i\ +\x5c\x93\x80Wa\x00\xa3\xd8\x95\xc0kqK\xba\x8d\xab\ +w<\x99\xfdL\xb5[\x8eu\x12t\xb8\x94\xb2;\xc1\ +\xd6c?\x86\xf02\xfe\xc6\xe3\x11\xf1\x1a\x8e'\xd8p\ +#\xa7\xebpQc\xdf\xc5o\xb8\xac\xbf\x942\x82\xc7\ +\xb0\x01\xab\x22\xe2d\x06\x1c\xc4\x8b\xf8!\x22>l8\ +\x1f\xc4\x96\xf4\xa1\xde\xf3\x16\xe7J\x17C\xfdx\x01C\ +\x11\xd1m\x19l\xc6\x93\xb8\xd5Ty\x1e\x0f\xab\xb3\xb0\ +-\xc1\xdar\xa2\xa36\xfd\x86R\xca%-\xe5\xedj\ +\xdb\xdd\xd9\xc3\xf18F\xd4\xfe~\xb5\x87~\x16&\xfa\ +q\x9fz%\xdf\x97R\xc60\x96\x01GR7ZJ\ +\xd9\x16\x11\x13-\x80\x1d\xea@\x1dn\x9d_\x8eA\x8c\ +\x9f\xed\x96\xcc\xfcn\xb5\x1b\xeeR\x87g\x5c\xe5\x8c/\ +\xf1\xba\xda\xa2\xa7{d\xda\x94%x\x0f#}\xbd\xb4\ +\xa5\x94\xad\xf8\x14\xef\xe2\x0a<\x8bO\xd4\x1e\xee\xe2/\ +\xdc\x84\x1b\xf1\x9d\xda\xe7\x9341\xaa\x92\xdc\xbc)C\ +TJyD%\xaaC\x11\xf1sD\x1c\xc0SX\x9a\ +k0M/\xc4\xdb\xf8\x00\x1b\xf3lHe\xc9\xcf0\ +\xdei\x01_\x8b\x9d*Aml\xa8\xba\xf8\x1dwd\ +\xd6\xf01v\xe7\xfbZ\xbc\xa4\x12\xdd@&3\x85\xb8\ +~U\xb9\x1b\x8ef\xc0\x8e:\x81\x83x\x13W6\xec\ +W\xe2\x95\x0c\xbeN\xe5\x94%\xf8Jn\xda\xd7\xb2<\ +#/\xce,v\xaa\x1c\xbe,\x03O\xc3\x9fm?\xff\ +\xff@\xceJ/V\x1cS)w\x13\xbe\xc9uOD\ +LV\xd4\x0bX\x1b\x18\xfe\x03:\x81\xbf\x95\xfd\x1fR\ +q\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01]\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0e\x00\x00\x00\x0e\x08\x06\x00\x00\x00\x1fH-\xd1\ +\x00\x00\x01$IDAT(\x91\x9d\xd2!K\xa4a\ +\x14\xc5\xf1\xdf\xbcc\x90\xc5\xb5\xd8\xd7\xb22i\xda\xfd\ +\x00b\xd02A6\xca&\xd3\xb2U\x90A\xfc\x00&\ +\x8b\xb8[D\x10\xd4\x22\x82\xc5f\xb5\xb8\xb7\x88\x96\x0d\ +\xe6M\xc2*\x83\x82\x08\x1a\xe6\x1dy}w4\xeci\ +\x0f\xf7\xfc9\xf7\x1e\x1e\x99\xf9\xcd\x7f\xa8\x91\x99W8\ +\x88\x88\xee{\xc6\xcc\x1c\xc5\x0c:X.pQ\x0e\xd6\ +\xde\x00\xda\x99y\x84k\x1cc*\x22z\x05\x94i\x8f\ +o\xc0\x97\xf8\x88\x0f\xe5{\x17\x8a\xc14\x22V\xf1w\ +\x08\xdc\xc14np\x8f\xc3W`\x09\xafU\xe1\xccl\ +c\x1f\xa7\xf8\x8c\xa5\x88\xe8\xd1/\xe7(\x22\xe6kw\ +u\xd1*\x93~\xe1kDC\xc7x\x17\xfa*\x00\xe6\ +KG\x079=z\xde\xaek\xf8{\x85\x5c\xa3S\x0b\ +(C\x22!\x1c\xc1ry\x9e\x91\x17_~.\xb0\x89\ +\xf5\x8b+#\xe7.\x94\xcd\xa69c\x9d\x1d@()\ +\x9b\x14vJ\xc0\xd9{\x06\xbf\xaf\x80m\xd3\xbe\xdab\ +\xad\x07\xa8z\xbd~ \x02\xcd\xf2\xbb\xa1f\x89}\x89\ +\x8e\x14\x19\x22\x96\x04\xfe\xa8\x94\xe7\x97\xb76\x1f\x1e\x88\ +@\xdc(\xd6\xcaw=\xdd6N\x00\xc3@\x09\x9d\x1b\ +\x14\xfajV\x80Z.\xbf\xf7\x15\xef\x94\x80\x0d\xb50\ +,\x81\xd7\x0c\x81[\xe8\xec\xe9\x00\x09\xe0%`(\x99\ +J\x93/\x14Y_\xf3:&`7\xednC\xc0\xc2\ +VAo>\xda\xdeZ\x07H\xa6\xd2\xa03h\x1e\xa2\ +?\xc7aX\xb9DD\xf9\xf8\xaf?]\xe7\xfa\xd7\x9f\ +\xdb\xbe\xf4\xad\x14\xf19~\xac\x94\xa2\xaf\xdfF\x98_\ +\x1f\xc6\xcc)k(e\x9dp!\xa0\xcb\x1e\xaf\x8a\x10\ +d\x0b\xbdv\xbd\xf2\xe7\x22\xe6\x81#\xb9B/\xe3\x13\ +\x93\xb6?\x1aPT\x03\xd2\xb1C\xf1\xe2\xf8\xc4\xa4\x07\ +\x9cg\xb7p\xb5[\x8c\x8dOL\xce\x1aR\x05\xb4\xf3\ +6@W\xc8\xfb\x11\xb0O\xed{\xe6\x0b#mH\x8c\ +\x00\x0fBs\xb1\xc0\xf9n\xb6\xbb\x9bp\xc9\xee\x13\xf0\ +\xeeWh4\xea\x1b\xae\x1b\xfb\x18\x1dN%\xb3a\xc2\ +\x9c\xee\x11\xf0-0\x80~v\x934\x97\xec\xdf\x1bU\ +5\xb4\xb3\xda\xfaP\xa2\xdf\x8c\xdb^u\x99`E\xbc\ +\xe7F\xce\x8c\x8eqj\xf8,\xdd\xdd)\x84\xb3\x9b\xba\ +\xa5R(\xd9\xe0\xde\xec\x0c\x99l\x9e\x9eb\x1f\x8e\xe3\ +\xea\xa7O)\xa4\x94T\xca\xf7\x00\xe8\x1f\x18\xc2q\x1c\ +\x84\x10\xbe\xd7I)Y\xf5\xaa\xccLO\xb10w\x97\ +g\xf8_\xe1\x1f\x16\x8f\x0dW\xeaa@l\x00\x00\x00\ +%tEXtdate:create\ +\x002015-12-18T14:2\ +7:00+00:003\x90\xe8\xec\x00\x00\ +\x00%tEXtdate:modif\ +y\x002015-12-18T14:\ +27:00+00:00B\xcdPP\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xa9\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0f\x00\x00\x00\x0e\x08\x06\x00\x00\x00\xf0\x8aF\xef\ +\x00\x00\x01pIDAT(\x91}\xd2\xbbk\x94Q\ +\x14\x04\xf0\xdf.A\x22\xd8\x88O\xb0\xf0Q\xf9\x8a\xa8\ +\x1c\x9bhaa\x1b%\x01\x11\x8b\xfc\x01\x06A1+\ +\x096Z\x88HH\x0am\x04\x03*ha\xe3\xa3\xb3\ +\x08\x98*\x12\xe5@\x1a1\x08V\x0a\x8a\x06\x82b*\ +\x1bS\xec\xfd`\xf7#\xe4\xb4wf\xce\xcc\x9c\xdb\xb0\ +\xc6d\xe6~\xdc\xc1!\xbc\xc3XD\xfc\xaa\xe3\x1a5\ +R?n\xe2\x046w<\xfd\xc4\x5c\x11\xf9\xd2E\xce\ +\xcc\xb3h\xe1\x186\xad\xe5\xa6\xcc2>\xe0VD\xbc\ +od\xe63\x0ca\xe3:\xa4\xfa\xfc\xc5\xedj\xf30\ +\xae\xe10z\xd6!\xfd\xc1|\xd9<_\x01\xdf\xe2\x22\ +\xbea7\xfat\xf7\xb1\xac]\xdc\x06|\xc5Ghf\ +\xe6,\x16\xf1\x19\x17\xb4s\xdf\xc0',\xe15NF\ +\xc4\x00\x86\xb1\x05?2\xf3IO\xb1q\x14\xc7\xb1'\ +\x22\x16q73\xa7\xb0-\x22\xbew88\x82\x83X\ +\xc1\x5c\x95\xb9\x17\xafp\xaaX\x1a\x89\x88\x85\x8a\x91\x99\ +\xe70\x89\xed\x98\x8e\x88\x1642s\x1c\xa3%\xcb%\ +\x9c\xc6\xf5\x92\xff)\xae\xe0\xbf\xf6)\x9b\x98(\x9ac\ +M\x0c\xa2\x17/\xb1\x10\x11\x13\xd8\x81\xc7\xa5\xc4\x91\x88\ +\xd8\x1b\x11/0S\xca\xdd\x89\xf3\x95\xed\x03x\xa8}\ +\xaa\x07\xe5\x14\xff:lo\xc5=\x0c\xe0\x0d.G\xc4\ +R\xfd{\xee\xc34\x02\x8fp\x1fS8S\x9c]\x8d\ +\x88\xdf\x15\xbe\x8b\xdc!\xb2\xab\x88\xf4\xe39Z\x11\xb1\ +R\xc7\xad\x02\xcc\x87tf \xe3\x97\xea\x00\x00\x00\x00\ +IEND\xaeB`\x82\ +\x00\x00\x01\x04\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x08\x00\x00\x00\x0d\x08\x06\x00\x00\x00\x94\xc2/8\ +\x00\x00\x00\xcbIDAT\x18\x95]\xd0!K\x83a\ +\x14\xc5\xf1\xdf\x1e\xa7\xc5j\x19[\xb1\xc9\x92\x22\xab\x82\ +\xc1\xb0\xa8\xc1l\xda\x17\x10\xeb`\x03\x0d~\x82\x81\xb2\ +:0\x18\xd6V\xdc\xa2\xe1\x05WDP\xd1\xb8\xe2\x07\ +\x105\x18\xde\xfb\xe0\xf0\x94{9\xfc9\x97{*E\ +Q\xc0&\xae\xb0\x87G\x9c\xe2\x0e\x12\xaa\xb8\xc57\x06\ +\xd8\xc6\x18\x8d\x0c\x1c\x87\xd9C\x1f\x9fXG'\x03G\ +x\xc2=>p\xa3\xd4A\x06Z\x98\x86\xb9\x86y\xec\ +\xbbh%\xd4\xf0\xeaOg1W\xd1IX\x89\xbb\xf0\ +\x15\xdf\x08\xaf\x9b\xe2\xee\xb2\x06\x01^`\x91\xf0\x8c\xfa\ +\x12\xb0\xc09.E\x07\x0f\xd8\xf9\x97\xd2\xcfK\xc2\x04\ +\xfb\xd8\x08\xef\x04#4s\xc2\x04o\x119\xc3\x10\xef\ +x\xc9\x09?8\xc4\x16\xae\x95\x85\xb5\x95\xd5\xfb\x05\x08\ +|+}\x8e\x949\xf5\x00\x00\x00\x00IEND\xae\ +B`\x82\ +\x00\x00\x06\x1d\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\ +\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x00\x06\ +bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\ +\x00\x09oFFs\x00\x00\x01\x00\x00\x00\x00\x00\x00|\ +]\xbdz\x00\x00\x00\x09pHYs\x00\x00\x0b\x12\x00\ +\x00\x0b\x12\x01\xd2\xdd~\xfc\x00\x00\x00\x07tIME\ +\x07\xdf\x0c\x12\x0e\x1b\x00\xb3\xf4\x90\xdf\x00\x00\x00\x09v\ +pAg\x00\x00\x02`\x00\x00\x00 \x00\x1f=\x87\xd8\ +\x00\x00\x04\xe2IDATX\xc3\xed\x97YlTU\ +\x18\x80\xbf\xdb\xb9C;3]fa\xe8\x02\xb4@\x99\ +.\xd0R*\xb4\x104b\xc2\xea\x86\x18\x97 \xea\x83\ +F4\xfaf4F_\x5c\xde$\xf2\xa0\x0f&FE\ +\xe2\x83\x01#\xa6\x88@@)E\x81\xcaRZ\xbaP\ +\xbaP\xa4\xdbl\x1d\x0b\x85v\x98i;\xbd>\xdcs\ +\x87)\x9d\xa5`|\xd2?\xb99\xe7\xfc\xe7\xfc\xe7\xff\ +\xce\xb9\xff\xf9\xcf\xbd\xf0_\x17\xe9^\x0d\x93S\x0cX\ +\xac3\x99\x91\x9c\x82N\x96\x19\x1f\x1b%\x18\x08\xf0\xd7\ +\x80\x9bP(\xf4\xef\x00\xd8\xecY\xcc\xce\x9d\x8f}V\ +6\xf6\xac\xd91\xc7\x0dx\x9c\xf8\xbc.\xdc\xfd\xbdx\ +\x5c\xbd\xff\x1c \xddleaa\x09\x05\x8b\xca4\xd5\ +\xeb\xc0\x9b\x80#\xca\xf03\xc0\x0f\xc0\x1e\xa0\xbf\xf7j\ +\x17\xed\x17\xeb\xf1y\xdd\xf7\x06`\xb6\xd8\xd8\xb0i\x0b\ +RR\x12\xc0W\xc0f \x1d\x98\x00\xde\x07N\x01)\ +b\xae\x5c\xe0\x9b\x08\xf3s\xc0c\x80\xf7\xf7_\x7f\xc6\ +\xd9w\xf5\xee\x00\xcc\x16\x1b\x1b7o\x05\xb0\x00>\xe0\ +i\xa0J\xeb\xbf\xe5\x1f\xc1`4\x85\xc7\x8f\x8f\x8f!\ +\xcbz\xadY\x00\xb4\x8b\xfas\xc0\x9e\xdf~\xd9\x8f\xab\ +\xbf{\x92\x8f\xa4x\x00y\x0b\x0a\xb5j\x0fP\x01T\ +\xdd\xb8>\xa8\xe9\xca\x0dF\xd3v\xe08\xd0\x08\x9c\x96\ +e\xfd\x0e`\x0e@(\x14\xea\x10\x0b<\x09\xec\x06^\ +Z\xbd~\x136{\xd6\xf4v\xc0:3\x93\xf5\x8f?\ +\x0b\xf02\xf09`\x08\x06\x03$'\xa7d\x02M\xc0\ +\xac8\xec\xe7\x81\xe5\x11\xed\xb3b\x019\xed\x17/\xb8\ +\x1a\xce\x9eH\xbc\x03s\xf3\xf2\xb5\xea\x87@\x99p\x0e\ +\xe0N\xe0\x1c`\x19\xa0\x88\xd7\x00P)\xcao\x1dE\ +\xa5\x98\xd2\xd2\x13\x03dXl\x88\x09\xe6\x02\x1dMu\ +\xb5Z\xd7\xa7L_\x0e\x00f\xcd9\xb0.I\xa7\xc3\ +>+'1@\xce\xdcy\x00\xcf\x00-\xa0\x9emW\ +_7\xa8\xc7\xef\xebi\x028\x80\x8fD\xfd\x0bM\x99\ +\x9a\x9e\x11\x1f\xc0f\xcf\xd4\xaa\xaf\x01}\x8a\xa2pc\ +\xe8\x1a\xe7jk\xb89t\x0d`\x1b\xb0e\x9a\x10/\ +\x88r4\xacQ\x94D;\x10\x8e\xcdl\xa0\x5c\x92$\ +\xd22,\xf8GnRsd\x1fW:[\x01\xbe\x17\ +\x03\xb7'\x00\xb0\x8a2\x9c:\x83\xc1@|\x00\xb1\xfd\ +\x002\xf0\xaa\xd7\xdd\xaf\xad\x1c\xff\xc80gOVS\ +sd\x1f\x97\xdbZ\x00\xde\x05\xd6\xc4\x01xO\x94o\ +\x01]\xa0\xbe\xce\x98\x00\x16\x9b\x9d\xc5e\x15\xa0\x1e=\ +\x80\xfd\x91\x06\x9ax\x9c\xbd\xd4\xfdQC\xed\xf1\xc3\x00\ +\xc7\x80w\xa28\xf7\x00\x1f\xa3&\xb2\xd5\xc0\x06\x9f\xd7\ +\xc5\xf5A_l\x809\xb9\xe1\xe3\xf7\x06\xf0\x0a\x80\xbb\ +\xbf'\xe6\xf2z\xfe\xec\xe4Rs=\xc0'\xc0:\xc0\ +\x1f\xd1]!\xcaZ\xd4\x14\xdd\xd5T\x7fz\x92\xbd|\ +\xe7\x84\xb2^\x1f\xd9\xdc\xa9(\x0a\xa3\xa3A\xe2Ic\ +\xdd)<\xce\x1e*\x1fX{\xd4hJ5E\x19R\ +\x04\x14{\x5c}x]}\x93:\xa6\xec\xc0\xc4\xe4\xbb\ +\xfc\xb2$I\xdc\xff\xd0F\x8c\xa6\xb4\xb8\x10ng/\ +\x87\x7f\xda\xcd\x89\xea\x03(\xb7\xa3\xfcm \x84\x88\xea\ +\x96\x863S\xec\xa6\x00\xb8o\xdf\xdf\x0f\x02\xf9\x80\x92\ +n\xb6\xe2(.%\x91\x8c\x06\x03\xd8\xecYH\x92d\ +A\xcd\x84\x1f\x00:\x80\xeaC?\x12-\x96\xa6\x00x\ +\x9c\xbd4\xab\xa4'\x22\xf5#\xc37\x13\x02\x00\xcc_\ +X\x0c\xf0\x99h\xa6\xf9G\x869zpoT\xe7\x10\ +%\x06\x00|\x93\x07\x8f\x03\xa4\x18\x8c\x94\x96\xaf\x00\xa0\ +\xb3\xad\x99\xc0-\xff\x14\xbb\x0c\x8bM\xbb\x9e\x9f\xd7\xec\ +N\x1e;\xc8\xa0\xcf\x1b\x138*\xc0\x80\xd7\xa5UO\ +\x03+\x01J\x96V\x86\xfb\x0d\xc6TZ.\x9c\xc1?\ +2|\xdb\xb9\xd9J\xee\xbc\x85\x8c\x8f\x8d!\xeb\xf5\xdf\ +\x09\x08R\xd3\xccq\x01\xa2&\xa2\x89P\x88+\x1d\xad\ +\x00O\x09\x95\x22\x9e\x10\xf0\xf0\x82\x82E8\x8a\x96\x00\ +`\xb1\xdaYr\xdfJ6n\xde\xca\xe2\xa5\x95\xc8z\ +\xfd*\xa0\x5c\xcc]\x90=;7\xee+\x8by\x19\xb5\ +_l@Q\x14'j\x04k\xcf#\xc0!`g^\ +~!\xa5\xe5+\xd8\xf0\xc4\x16\x16\x95UX$I\xda\ +. O\x01%b\x9a]\xf3\x1d\xc5\xe8d9&@\ +\xdcO\xb2\x0c\xb3\x15{f\x0e\x06\xa3\x89\xa4$\x1d\xc5\ +K\x96i\xbbQ\xad(\xcaZI\x92@M\xb1;\x80\ +\x00\xf0\x22\xb0W\x98\x97\x00\xcd\x80Ts\xb8\x0a\xcf\x1d\ +\xe7?n\x0ch2t}\x90!\xf1\x09\x96\xb7\xa0 \ +\xb2k\x8d$I_\xa2\xde\x8a\x00\xfb\x80'\x01.5\ +\x9d'\x10\xf0\x93_X\xd2\x92\x9ea\x91n\x0c]c\ +\xc0\xe3\x8a\xe9#.@\xa4\x0cx\x5c\x8c\x8f\x8d\x22\xeb\ +g\xecB\x8d\x8dm\xa8A\xfa(0\xd8\xd1\xda\xc8\xd5\ +\xae\xb6p\xc0u_\xe9\xc0QTJg[3\x13\x13\ +\xb1\x7fT\xee\xea\xc7\xa4l\xf9*\x1cE\xa5\xc8\xfa\x19\ +a]wW;]\x9d\xadSR\xect\xe5\xae\x7f\xcd\ +dY\x8f=3\x1b\x9dNf\xc0\xeb\x22\x18\xb8uO\ +\x8e\xff\x17M\xfe\x06<\xc9\x9b\xa3b\xcb\x06=\x00\x00\ +\x00%tEXtdate:creat\ +e\x002015-12-18T14:\ +27:00+00:003\x90\xe8\xec\x00\ +\x00\x00%tEXtdate:modi\ +fy\x002015-12-18T14\ +:27:00+00:00B\xcdPP\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01Z\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0e\x00\x00\x00\x11\x08\x06\x00\x00\x00\xed\xc8\x9d\x9f\ +\x00\x00\x01!IDAT(\x91\xc5\xd3\xbf+\xc5Q\ +\x18\xc7\xf1\xd7\xe5\x86\xe4\x12\x8b\xd1j5\xca\x8f.!\ +\xe4\x0fP\xc4\xceD\x06e\x91\x12\x7f\x81\xcd \x19d\ +\xb0\xc8d\x91\xc1f\xfe\xda\xc8b\x10I\xa9\x9b\x9f\xc3\ +9\xd7\x8f\xd3\xa5L>u\x86\xf3<\xe7\xfd\x9c\xf3\x9c\ +>O.\xcb\xb2\x02\x96\xd1\xe0S\x8d\xa8\xf6]\xb5\xa8\ +/o\xf2x\xc0\x0e\x0ep\x85M\xdc\xe35\x01\x1fQ\ +\xc2\x14\x86\xf21x\x86N\x1c\xa2\x1d\x0b\x15@\x98A\ +\x11\xc5\xaa/\xc1Kt\xa3\x03\xbb\xa8K\xa0E\xcc\xa3\ +\x17YU\x92\xbc\xc30\x9ep\x84\x96\x18_\xc5$z\ +pQ\xee1U\x09\x13X\xc3)N\xe2+\x8a\xb8)\ +\x1f\xaa\x04\xc2[|\xda\x05\xc6\xd1/|\xd8\x87rY\ +\x96\xfd\xc0\xfe\xae\xb4\xc7\xff\x07g1\xf8Wp\x09s\ +\xd8\xc6t\x9a\xac\xf4\xab9\xaccD0D\x93\xe0\xa8\ +6\xac\xfctc\x0e\x1b\xa2\xadp\x8dstaL\xf0\ +q>\x05\xab\xb1%xu\x00\xb7_r\xd7\xe8C\xab\ +0\x0c\x852X\x83=\xc1b\xa3\xb1`s\xb2\x0a\xb1\ +\xe7\x17\x1c\xe7\x85\x19\xdb\xc7P,\xf2(\x8c\xdas\xd2\ +F)\xe6\xc0;/f:\x9b\xed\xa3J\x96\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x02\x0c\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0e\x00\x00\x00\x0f\x08\x06\x00\x00\x00\xd4\x14\xfet\ +\x00\x00\x01\xd3IDAT(\x91\x8d\x90\xbfk\x13a\ +\x18\xc7?wy#I\x04\xc1HMq(m\x22\xa5\ +\x15/\x15DA\xed\xdda\x8d.\x82q\xabm\x97\x03\ +AJ]\x5cJ\x97\xc6?\xc2\x1f\xc5\xd1\xc9\xae\xea\x5c\ +\xa5w7\xd4\xa1\x08=\x0bbQ\xc8-\xd23%C\ +\x04\xf1\x02i\x1c\xbc\xf7\xed\x05\x1c|\xb6\xef\xe7\x9e\xe7\ +\x9e\xf7\xf9hc\xe5\xca7\x8ej\xd6\xb6\xccm\xd7\xf3\ +\x17\x81\xe5\x84m\xdb\x969\xebz\xfe4\xe0\xcbF\xf1\ +\xec\xe9\x93\xb2\x0c\xcf\xd7\xd6\x86\x01n\xd6j\xe3\xf5\xfa\ +\x9d2\xc0\x97\xbd\xbd_\xc1\xce\x0e\x86a\x14\x1f7V\ +\xd5\x06Q*\x95\x8e\x82\xc8\xea\x00\xb9|>#y\x14\ +E:\xff(\xd1\xe9t>\xcb\xa0\xebz\x07\xa0\x7fx\ +\xf8]\xf28\x8e\xbf\x02d2\x99V\x14E\xea\xa9\x9a\ +\xe38\xb7S?\xda\x02\xda@\x05\x98HX;\xe1'\ +\x80)\xb5\xd1\xf5\xfcG\xa9\xc1\xd0\xb6\xcc\xb6\xeb\xf9\x97\ +\x80\xfb\x09\xdb\xb5-s\xcb\xf5\xfcq\xe0\xa5\x1al4\ +Vk2\xac\xbfZ\x1f\x01v\xa7\xaf]\xbdx}f\ +\xa6\x06\x106\xc3\xa10l2991\xfcpiI\ +\x89\x14U\xc3P\xeb^\x17\xde\x08\x80\x93\xc5S\xc7\x14\ +\xef\xf7\xb3a\xd8D\x88\xac> \xb2\xdb\xed\x1e\xa8\x83\ +5-\xfe\xdb\xdb\xff)y\xaf\xd7kKqi\x91\x9a\ +\xe38\xf7R7\xbe\x03Z\xc09\xa0\x9a\xb0\x03`\x03\ +(\x02W\xd2r.\xa4\x06?\xd8\x96\xd9r=\xff4\ + y\xd3\xb6\xcc\x0d\xd7\xf3\xcf\x00J\xa4XX\x98_\ +\x91as\xd3}\x0f4\x0d\xe3\xfc\xadj\xb5\xba\x02\xd0\ +\xfa\xd1\xfa\x18\xc7\xbf_\x9c\xadTF\xe6\xe6\xe7\x94H\ +q\xb7^W\xeb\x82\xe0S\x0e`tt\xec\xb8\xe4A\ +\x10\xe4<\xcf#_(\x0c\x88\x14\xfcgi\x9a\x16\x0f\ +\x88t\x1c\xe7A\xea\xfb[`?\xb9\xefr\xc2\xf6\x13\ +>\x04\xdc\x90\x8d\x7f\x00\xbf\x16\xa5`bD\x0f\x81\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01}\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x14\x00\x00\x00\x10\x08\x06\x00\x00\x00\x16\x18_\x1b\ +\x00\x00\x01DIDAT8\x8d\xad\x93A+DQ\ +\x18\x86\x1f#\x8d\xc8l\x94\x14QC\x16\xa2,N\x83\ +\x8d\x8dbJI\x8d,X($\x14\x0b!)%;\ +eq\xcb\xf0c\xf8\x09\xde\x95\xad\x85\xb2\xb1\xb1`c\ +cc\xe1\xbb:\x9d\xb9\xf72x7_\xefw\xcf\xf7\ +\x9c\xf7\x9e\xd3\x81\x14I:\x95\x94K\xfb\x9e\xa6\xc4\x01\ +I\x1d\xc0!0\xf7/@`\x15h\x02v\xeb\x056\ +\x84\x0d\xfb\xcd\x07\xa0\xc7Z%\xe7\xdc\xed_\x12\xcex\ +0\x80\xbdz\x12&\x017\x03_\x91\xd4\xfb+\xa0\xa4\ +\x22P6\xfbl\xb5\x11\xd8\x0e\xd6\x95$u\xfd$\xe1\ +\x06\x9f\xe7\xfa\x0eL\x02\xaf\xd6_\x97T0\xd8(p\ +\x03D\x99@Iy`\xc5\xec\x95s\xee\xce\x1bj\x03\ +\xd6\x0cv\x0d\x14\x80yI\xd3Y\x09\x17\x80v\xe0\x0d\ +8\xb3^\xe4\xa5\xdc\xf7`\xb1\xaa\x92\x9a\xd3\x80\xf1e\ +\x5c8\xe7\x9e\x00\x9cs/^\xca\xce\x00\x06\xd0\x0f\x1c\ +\xd4\x00%\x8d\x00\xe3\x96\xe6<\x18\xf2S&\xe9HR\ +_\x98p\xcb\xea=p,i\xca6\x9a\x05.\x81\xc7\ +\x0c`\x1e\xa8~\x01\xed\xf6\x96\xcc;`\x07\x980?\ +\x06,\x02\xc3\x19@\x80\xb2\xa4J\x9cp\x19h\x09\x16\ +\x14\xad\x0e|\x03\xf2\x15Ij\xcdQ\xfb2\x00\x06\xad\ +\x0e\xd5\x01\xec\x06N>\x00o\x01\x5c\xe2;\xefo\xbb\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01u\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0b\x00\x00\x00\x10\x08\x06\x00\x00\x00\xc0\xbd\x85~\ +\x00\x00\x01\xcfy\xce=g\xa2!\xc9n\x5c\xc3!\xfc\ +\xc2F\xfc\xc0\xcd\xaaz\xaf\x13\x1eO\xf22\xc9i\x1d\ +\x92\xcc%YJr\x0e&I\xb6c\x11g\xaaj\xc5\ +\x14\x92l\xc6s\x5c\x18p\x19\xb7\xd6\x13BU\xfd\xc6\ +%\xdc\x18p\x0cO\xd6\x13v\x05\xaf\xb1m\x06?\xab\ +j\xb5\xb5\xdc\x87\xa3X\xc6a\xec\xac\xaag\xad\xe6\xdb\ +\x80\x17IN\xb5\x87\x19\xdci\xe7>\xb64\x93\x03X\ +\x19\xf0\x10\x0bIf\xab\xea\x03\x96\xb0\x80/U\xf58\ +\xc9&\xdc\xc5\xed\xa1\xaa\xfe\xe2js\xd3\xfe\x7f\x11\xef\ +Z~\x1d\x0f\xaa\xea\xfb\xd0\x06x\x85!\xc9\x0e|\xc5\ +,\xc6\xed\x9c\xc0S\x18\xba\xa1\x971\xdfo!\xc9A\ +\xbc\xa9\xaa\xb5q\xa0\x11\x8fp\x0f[\xb1\x17Gp\x12\ +WF\xc1d\xcai\xb1\x85k\xed\xdeSUs#\xdf\ +;\xc3.\xec\xef\xf2?=9-\xfe\x88U\xfck]\ +7\xf4\xe40%>\x8f\xb7\xf8\x8cO8\xdb\x93\xff\x01\ +?\x07g7v\xdb#C\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x01\x03\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x13\x00\x00\x00\x11\x08\x06\x00\x00\x00?\x98\x97\xc7\ +\x00\x00\x00\xcaIDAT8\x8d\xed\xd2=J\x03Q\ +\x14@\xe1od2\xa6\x11W\x90F\xec\xd2Y\x84\x08\ +.!\xe0\x02DHe\xe1n\xa6\xb2Nm'$\xa5\ +\x16YA\xba\xb1\xb0\xb1\xc8\x16\x04GL\x8a\x99\xe0\xf8\ +\x08f^\xb4\xf4\xc0\xe5\x15\x17\xce\xbb\x7fIQ\x14\x0f\ +\xb8\xd0\x9e\x15\xae1\x0b\x13)Fx\xc6\x02\x07;D\ +=\x0cq\xba-\x99\x22\xc1\x13n[Tu\x82\x17\x5c\ +\xa1\xdfp\x1ca\x99\xb6\x104\xf9\xa8\xdfA\x1d\xdf\xd8\ +\xd5V\xc8\xab\xaa\x930\xee\xf0\x16+\xfb\x89\xf7\xb0\xcd\ +.&\xf5o1\x9cQ\x0d\xaf\xc9\xa1j\xbb\xe3H\xd9\ +\x14\xf9\xb6\x05\x94\xb8\x8f\x94\x1d#\xff\xcb\x99Eo\xf3\ +_\xf6%\xfb\xfc\xa5\xa7D\xb69\x8dKt\x90\xed)\ +K6\xb29\xceq\xd3H\xae\xf6\xa8\xecq\x0d\xfe\xb1\ +\x1c=z\xc7\xebp\x00\x00\x00\x00IEND\xaeB\ +`\x82\ +\x00\x00\x01)\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x12\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1b\x06/\x5c\ +\x00\x00\x00\xf0IDAT8\x8d\xd5\xd2/K\x83Q\ +\x18\x05\xf0\xdf\xe6\x9a`0h1\xbb&\x13\xee'\x10\ +\xf4KX\xd4l\x10\xecV\x99E\xa3q3\x1b\xd4b\ +5\x18\x9f0\x0d6\x83`S\x04\xa3 \xce\xe0\x8b\xbc\ +\x5c\xdc\xdd\x16=\xed\xdc?\xe7\x1c\xce\xf34\x22bh\ +4\xeeRJ\x9d\xc2\xfd/\x9a\x93<\xfa\x9fB-l\ +\xd7\xf8:^0\xa8\xf8\xdb\xa4B\x8d:\x89\x88\x1e\x06\ +)\xa5\x93\xec|\x07\x07\x05\x9d\xe7V\xc9%\x22\xf6\xd1\ +\xc7\x1c\xeeG\x88\xb5\xd1\xcd\x13]c\x15\xef\x18b\x09\ +\x9f\xb8\xc40\xa5\xb4\xf5\x87Y\x07\x17y\xa2W\x9c\xe3\ +\x0a\x0b\xd8\xc5)\xe6\xb1RJ_\xec(\x22\x9a)\xa5\ +\xaf\x88\xd8\xc3qA\xe7)\x8f\xd9\xab>M\x8db\xd9\ +5\x83\xb1\x89\xc6M\xed\x01\x87\x15\xedOS\xf626\ +#\xe2\xc8\xcf\xd6\xcf\xe0\x0c\xb7x,\x99\xe6B\x1b\x98\ +\xc5G\xc5o\xd0\xc5\x22\xd6*\xf7\x1cm\xb2\xa9\x8d\xc2\ +$\x9b\xfd\x0d1\x8aV\x95q\x1f\x8eP\x00\x00\x00\x00\ +IEND\xaeB`\x82\ +\x00\x00\x02\x01\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x11\x00\x00\x00\x14\x08\x06\x00\x00\x00k\xa0\xd6I\ +\x00\x00\x01\xc8IDAT8\x8d}\xd3\xcf\x8b\xcdQ\ +\x18\xc7\xf1\xd7\xdc\xaeD\xc9(4\xca\x1d\x1b\x16\xc4\xea\ +\xa2\x98\x85\xc5\x10eAhJj\x84\xf8\x0b\xb0\xc4\xca\ +\x86\xa4&c\xd6\xb2\xa04%\xc9\x8f\xe4\xc7$\xc4\xbd\ ++3S\x16\x16D\x94_%\xc5\x8c\xb8\x16\xe7\xf96\ +\xdf\xf9\xba\xdf9\x9b\xcfs\xcey\xce\xfb<\xcfy\x9e\ +\xd3\xd1j\xb5\xb4\x1b\xcdf\x13\x06q\x1c?by\x1f\ +&p-\xef\xdb\xd1h4\xdaBb\xf4c>\x06b\ +~\x13\xfb\xf19\xefT)9\xbc+\xf4\x0a6\x87=\ +\x0b\xbf\x8a\x802\xc8\x06\x5c\xc5JLb\x07:\xb1\x08\ +\xbbs~\x8bq\x06\xab\xdbAN\xa2\x8a\x13\xb9\xb5\x87\ +x\x1f\xba0\xa2\xbb\x8e\xa7\x18+B6bk\xd8}\ +\x11\x0d\x5c\x08\xdd\x84\xb3x\x80\x1e\x0c\xa3U\x84d\xb7\ +\x7f\x88T\xb3\xf9eSo\xb1\x0d\x7f\xf07;\x94\x87\ +\xac\x8b(\xbec\x8b\xf4\x1e}X\x8e\x9f\x18\x0a\xbf/\ +\x91Rov>\x0f9\x1a:\x841\x5c\x8a\xfdc\xb1\ +>\x18\xe0\xf31\xdf\x89&\xb6g}\xd2\x8d\xd7\xb1\xd9\ +\x1d\xe9\xac\xc2\xa8T\xd6\xa5\xf8\x8a\xf5x\x9e\xbbx6\ +\xe6f\x91\x1c\x92*2\x1c\x00\x18\xc7]\xcc\x91\x9a\x0e\ +\xe6\xe1H\x0e2\x81o\x95\x08\xf9@.\xe4\xfc\xb8\x18\ +z0\xf4I\xf8N+HEj\xae\x1a\xde`\xa4\x00\ +\xb9\x15i\xac\xc1\x0a\xe9\x81\xefDZ\xd3 \xbda\xdf\ +@\xf17N\xc6!R\xc5\xe0\x14\x9e\xe5\x9d\xaa\xe8\x0a\ +\xfb\x95\xf6c<\xb4\x06\xf5z\xfd?\x87\x8a\xa9&\xaa\ +\x95@\x96\x85~,\xd9W\xc1\xbd\xb0\xfb\xb1\xa0\xb0\xbf\ +\x04{\xc2\xbe?\x13d\x04\x8f\xa5\xb4nc\xad\xf4\xed\ +{\xa4\x12wJ\x9f\xede\x19\xa4\x1a\xba\x17\x8f\xa4W\ +\x7fQ\xf0\x19\xc5\xe12@\x16\x09\xbc\x8b\x08\xce\xe1-\ +~K\x1d|Zj\x81O3A\xfe\x015\x8de<\ +wk\x85?\x00\x00\x00\x00IEND\xaeB`\x82\ +\ +\x00\x00\x01\xba\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x15\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf9\xda4%\ +\x00\x00\x01\x81IDAT8\x8d\xa5\xd1M\x88\x8ea\ +\x14\xc6\xf1\xdf\xfb6>\x92\x92|Dij4\xa1\xec\ +8\x1b+5\xb1\x18f\xa1f\xc5J1\xc3DVR\ +H!+;\x14\xc9J\xd2lf\xe3\xabdg)\x97\ +\x94\xb2\xb1 e\xc9\xdeJ\x16\x9eW\x8f\xe9\x19\x93q\ +V\xe7>\xd7\xb9\xfe\xf7\xb9\xef\xd3\xb3\x84H\xd2\xc3.\ +L\xe0 f\xaa\xea\xcd@\xef/`\xda\x94d\xef_\ +\xb8}\x5c\xc0e\xacn\x03ah\x01\xd3q\xec\xc1\xcb\ +\x05\xf4Q\x8c\xe1\x1df\xbbn\x9c?e\x1fS\x18O\ +\xb2\xbdC_\x87\xc7\xb8\x86\xdd\xb87\xbf\xa7k\xd2q\ +\x0c7\xf9\x19\x9cj\x01\x97\xe3\x01\xceV\xd5\x93\xa6\xfc\ +m\xd1Iq\xb2\x95\x1fM\xb2\xb6\x01\xf6p\x15\x17\x07\ +\xc0$\x9b;\xfc\x7fB\x93\x0c\xe3@\xab\xb4\x0a\xd3M\ +^\xb8SUo[\xfaX\x92\x1d\x8bM:\xd5Q;\ +\x9dd\xa8\xaa^W\xd5\xe7$\xd3IF\x1am\x16\x97\ +\x92l\xe8\x84&\x19\xc2\xb1\x8e\xd7l\xc1d\xd33\x83\ +\xbb\xb8\x01U\xf5\x03\xd71\x97d\xe5\xc0\xd0kA'\ +1\xd7\x01\x85W\xb8\x8f\xdb\xad\xdaDU=k\xbc7\ +\xb1\x11\x87\xd1kC_`\xff\x02\xd0\xae\xf8\x88\x9dU\ +\xf5=\xc9\x1a|@\xf0\xb4\xdf\x00G\xb1\xef\x1f\x80\xb0\ +\x15\xe7\x9a|\x19>\xf9\xb5\xe4m\x83?\x9d\xd6\xfa\x8a\ +\x7f\x88\xf3IF\xaa\xea+\xae\xe0=FzIV\xe0\ +\x0b\xd6/\x01\x0a\x8f\xaa\xea\x10\xbf\x97=\xd6Kr\x04\ +\x0f\x97\x08\x1c\xc4xU=\x1f\x1c\xfa8\xf1\x9f@\xb8\ +\xd5\xbc\x18\xfc\x049\xa0\x7f\x85\xe9\xdfLL\x00\x00\x00\ +\x00IEND\xaeB`\x82\ +\x00\x00\x01\xb8\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x11\x00\x00\x00\x12\x08\x06\x00\x00\x00\xbd\xf95T\ +\x00\x00\x01\x7fIDAT8\x8d\x95\xd3;hTQ\ +\x10\xc6\xf1\xdf\xbd((jv;\x05+\xc1\xad\x04!\ +0\x8d\x8d\x16\x82\x88H*A\xd2\xd9\xf8\xa8\x04\x1b5\ +\x82\x9d\x06;\xed\xc4B\x10\x0b\xedl$\xb0`e\x93\ +\x22\x0e\xda\x1bS\x89/\x10\x82\x16\xc1g\xb4\xc8\x89\x5c\ +\xd7\xbb\xbbf\xe0p\x98\xef|\xe7\xcf\xcc\x1cN\x95\x99\ +\xbd\x88X\xb4\xc1\xc8\xccI\x5cA\xa7\xca\xcc{\x98\xc6\ +\x02nF\xc4\xa31\x97\x8f\xe1\x01:E\xba[ef\ +\x0f/\x1b\xbek\x11qu\x0ch\x19\xdd\x92\xee\xadK\ ++\xcbE\xf8\x85'c\x00w\x0a\xe03>F\xc4R\ +]\xce\xfa\x98\xc3\x0f\xf43\xb3;\x04p\x02\xa7\xf18\ +\x22:8\x05\xeb\x903\x11q\x1c\xfb\xf0\xd5\xda|\x06\ +\x01{p\x1f\xd7#b\x0a\x22b\x0e\xaa\x16\xf3v<\ +\xc3+\xac\xcf\xa6\xc6C\xcc\xb4\x0d\xfe\x1fH\x03\xf6\x0d\ +\x9b\x1b\xd2\xdb\x88\xd8\xdd\xe6\xad\xdb\xc4\x12+\x03\xf9\xf7\ +a\xc6Q\x90\xff\x8eQ\x90\xc1\xb3m\x1b\x82d\xe6I\ +\xac\xe2ic\xc9\xcc\x1bm\xfe\xb6\xd7\x99\xc5y\xf4\x22\ +\xe2]C\xdf\x82\xf7\xd6\x9e\xffhD\xac\xfeUIf\ +\x1e){\x1f\x971\xdd\x04@D|\xc1A\x1c\xc6R\ +f\xee\xcc\xcc\x99?\x95d\xe6\xa7R~\x17\xb7\x22\xe2\ +\xc2\xb0\xfe3\xf3\x1cn\x17\xffJD\xec\xa8\xca\x97~\ +^<\x1f\x22b\xd70@\x81L\xe05&\x8at\xa0\ +\xc6\xc5\x86gkf\x1e\x1a\x05\xc1$\x16\xf1\xb3\xe4\x97\ +\xaa\xcc\x9c\xc7\x1b\xccF\xc4\x8b1\x80fE\x9bp\x16\ +\xfb\x7f\x03$D\x8d]u\xca0\xbc\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x04\xe6\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a Set object h\ +eight 1\x0d\ +\x0a \ +\x0d\x0a \x0d\x0a \x0d\x0a \ + \x0d\ +\x0a \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x01h\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0c\x00\x00\x00\x10\x08\x06\x00\x00\x00\x22a\x9e\x07\ +\x00\x00\x01/IDAT(\x91m\xd2!H\x9dQ\ +\x14\x07\xf0\xdf\xf7=\xf0\xa5!\x82\x93\x85\x85\x81E\x96\ +\x0c\x074mma\xb0\xf0\x94\x19\xb5Y\xc4<\x1cC\ +\x04\xe3\xb3\x8a\x0b\xa6\x85\x85\x19\x1e\x0c\x8b\xc50\xb4\xdd\ +&cF\x8b\x08o,\x092\xa6\x8c\x85w\x95\x8f\xbb\ +\xef\xa4{\xcf\xff\x9c\xff\xff\xfc\xcf\xbd\x95\x96H)=\ +\xc5\x17\xbc\x89\x88_M\xacn)\xee`\x0f\x17\xd8(\ +\xf1\xaaQ8\x89>z\xe8\xe0;\xa6\xf1\x0d[\x11q\ +\xf6\xa0\x90R\x9a\xc1\x0f,c\x1cG\x111\x8f)\xec\ +\xe3 \xa5\xb4\x04UJ\xa9\x9b\xe5\x9f4\x94\xb7#b\ +\xb3\xa1>\x85\x13\xf4j\xac`\xa2\x18\xf5\xb2y\x89\x88\ +!\xde\xe1C\x8d\xb7\xe8\x16\x0d\xbfK\xb38\xc4\x5c\xdd\ +\xc2\xde\x1a\x11q{o\xfa\xbc\x05\xaf\xcaD\xf6qS\ +\x1b\xed\xfc\xba\xc0\x1f\xb7\x90\xac\xe1\xa0\x8e\x88S\x1c\xe3\ +O\x03|V\xb0/b\x01;UN\x8c\xe13^\xe1\ +\x11\xce\xf2\xf99V3\xc1bD\x5cV\x05\xd3\x1c\xd6\ +\xf1\x1a?q\x87M\x0c\x22\xe2o\xab\xb9\xdc\xd8\xc7\x0b\ +|\x8a\x88\xdd&\xf6\xdf\xe7\xcb\xf1\x1e_\xf1\xb1\x04\xee\ +=\xcc\xe2\xa5\xd1\x9bt\x8d\xfeP'/b\x98G\xbb\ +\xc2\xe0\x1f\x06\xdbU3\x9dA\x19\x09\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x01O\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x13\x00\x00\x00\x12\x08\x06\x00\x00\x00\xb9\x0c\xe5i\ +\x00\x00\x01\x16IDAT8\x8d\xe5\xd4=/DA\ +\x14\xc6\xf1\xdf\xae\xcd\x8d\xd7H\xd8\x88\xc6&tJ\x89\ +B\x22\xdb)t(\x84\x8f\xe0\xa5\xf09T:\x85\x1a\ +A\xa3Pjt\xbe\x00\xb2\x05\xd1\xa0\x14B\x16\xbb\x89\ +(\xee\xacl.\xb9w\xb3JOu\xe6\xccs\xfe9\ +3g2\xb9J\xa5r\x8e)\xed\xe9\x18\xcb\xa8A\x01\ +\xe3a\xe3\x05\xcf(\xe2!\x050\x84W\xf4a\x01\x07\ +\x0d`!\x18\xea(a\x04\xdb(\xa7\xc0\xf6\xb0\x8f+\ +\x9c\x06\xe0!\x96\xf2\xc1P\xc5S\xabg\x0b\xba\xc5l\ +\xa8\x9b\xc7Q>\xdd\x9f\xa9kl\x84x\xae\xf0\x8b\xa1\ +\x88\xc5\x14@)\xb1\xde\xc5\x16\x06\x92\xb0{\x9ca\x06\ +\x11z\x9a\xf6\xde\xf0\x8eK\xf1}5\xf4)\x1e\xc8\x0f\ +\xd8#VS\xbaJU\x12\xd6\x8b\x15td\xd4U\xb1\ +\x83\x8f\xe6dr\x00\xa3Xo\xa1\x895Lfu\x06\ +w\xd8\xcc\x80\x95\x91K&\xff\xfa4\xfe3\xac\x0b\x83\ +m2\x22\xf1\x0f\xf2=\xcdH\xfc\xedT\xd1\x89\x9b\x0c\ +\xc00&\xc4\xef\xb1?\xd4\xd4\x0a\xb8\xc0t\x00F\xc1\ +<\xd6BG\xddMq\x1d'_\xecY0\xc6f\xd8\ +\x84\x84\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01/\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x08\x00\x00\x00\x10\x08\x06\x00\x00\x00+\x8a>}\ +\x00\x00\x00\xf6IDAT(\x91m\xd0=+\x86\x01\ +\x18\xc5\xf1\xdf\xf3\x84\xb2\xb0\x89\xf4$\xe4\xa5\x8c\xae\x12\ +\x8b\x922\xd9\x18\x14\xa3\x85\xd5\x8a\xcf`\x90\xcd\xcb&\ +e\x91\xcc>\xc0UH^V\x93A\x8aE\x8a\xb0\xdc\ +wn\xe5l\xd7\xe9\x7f\xaeN\xa7\xa6Pf\x0e\xe1\x1c\ +w\x98\x8e\x88/\xa8\xfb\xd5\x22v\xd0\x8e\xd1\xd2\xac\x02\ +\x9fh\xa0\x05\x1f\xa5\xd9T\x01\x0ep\x8dK\x5c\xfd\xf7\ +\xe1\x15\x0f\xb8\x8d\x88\xef\xff\x80u\x0cb&3;\xff\ +\x00\x99\xb9\x899\x1c\xe1\x19W\x999\x09\xb5\x02\xb8,\ +\xfa\xacb\x1e\xb3h\xc5[Sfv\xa1\x1f\x138\xc4\ +MD\xf4\x14\xc1\xaezQn\x19Cx/v\x00\x11\ +\xf1X+\x8f\xcc\x5c\xc2\x02\xc60\x1c\x11O\xd5\x92\x0d\ +\xac\xe1\x0c7\xb8\xc8\xcc6\xa8gf3\xb6\xd1\x8bc\ +\xec\xa3\x03\x93\xe5\x92\x1b\xf8\xc2xD\x85\x9fX\x87\xdbU\x95uc\x11\x97\xca~\x0f\ +L\xe2\x03:\xaa\xc8&p\x00\x9f\xb1'~o\xc5\xd1\ + \xf8\x1e\x1d\xec\xca\x93\xd5\xd1\x17\xfey\xf4\x87?\x8d\ +\xe7-\x84u\xdc\xc0\xee\xc8\xe9\xc7\xeb\x88\x1dA_\x0d\ +\xab\xf1\x02\x9bsU\x5c\xc1}\xcc\xa0\x177q(\xda\ +k\xc5(\xae\xe3G\x863\xd8\x88\xf7\xf8\xaa\x1dk\xd1\ +\x89_xT\x10\xef\xc5\x02F2I\xa7\x078\x8b\xb7\ +\xb9\xc4\x83\xb8\x16\xf6\x02\x8e\xe3[.\xe72\xb6b2\ +\x93\xf6iTZ\xceV\xec\x97\xa6\xda\x90\xb4|\x22\xad\ +\xc2>i\x00+8-i9\x90I\xda\xac\xa0\x81\xc1\ +hk\x18\x87\xb1\x1c\xb1\xf1x\x9f\xf5g\xff\xaeb\x1e\ +\xe7h_\x8d\xa6\xa4\xcdX\xb4\x9e?\x9f\x8b\xd1\xd2\x89\ + \xf9K\xe3<\xd9;\xec\x95v\xa8\xec\xd4j\xd2d\ +{\xf2dE\xe7\xb4\x05C\xd2\x94\x8ap\x17\x17\x8a\x02\ +Ed$]f\xc3\xde\x86\x0d\xd2\xad~\x91$(-\ +\xb9\x0a\xc3\xb8\x855x\xa9\xfd\xbc\xfe\x8bl\x08\xa7\xa2\ +\xca)l\xafJ\xfe\x0dZUP\xe3\xae\x0f\xedJ\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xfc\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x17\x00\x00\x00\x12\x08\x06\x00\x00\x00\xb0\xe7E\x13\ +\x00\x00\x01\xc3IDAT8\x8d\xad\xd4\xcd\x8bNa\ +\x1c\xc6\xf1\xcf\x19/y\x9d\x85\x97\x14)\xa4\xcc\xc8\x82\ +\xe6\x96\x14EY(Rd\xa3\x89$\x8d\x05\xe5e\x1a\ +ec\x83\xb2\xb2\x90\x8d\x8d\x05v\x96vVVV\xd7\ +)\x7f\x02\xf2\x0f\xc8[\x8a\x1e\x8b\xe7\x9czL\xe7\x99\ +\x19q\xd5\xa9\xfb\xba\xbb\x7f\xdf\xdf\xd5\xef>\xe7T\xbd\ +^\xcfBT\xd7u\x85\xb3\x18\xc7G|\xc6\xb3\x89\x89\ +\x89\xa1\x80\xc5\x0b\x22\xf7u\x1d\xebJ)\xb7 \xc9X\ +\xb3\xf7`X\xc1HUU\xba\x9e\xba\xaeo\xd7u\xbd\ +\xac\xf5\xf8\x81WIv'9\x84\xefX?\xac\xbe\xaa\ +*#]\x1d\x93\x8cb\xa6\x19\xc3\x1f*\xa5\xbc\xc5[\ +\x9c\xd4\x1f\xcfPu\xc2q\x0e\xab0\x9d\xa4=\xf3\x0b\ +cI.b\x0b\xde\xe9\xcf}\xe1\xf0$\x15.7v\ +\x07\x8e5\xeb\xc7\xf8\x86\x8d8\x82Q<\x9b\x0b\xdeu\ +\xa1\x8716\xe0o\xe2e)\xa5\x87\xa7\x1daV\x96\ +R\xbe.(9\xae\xcc\xf2\x07\x92\xec\x9d#\xe0\xda$\ +\x17\xe6\x85'\xd9\x8c\x13\x1d\xe7ff\x9d\xdb\xde6,\ +\xa5|\xc0\xd2$\xa7\xe7K~\x09\x8b:\xe0\xa7\x92l\ +m\xc1x\x8d'I\xda\xb1>\xc6T\x92\x83\x9d\xf0$\ +K1\xd5\x01\xd64\xbc\x91dg\x03\xde\x84]\xb8\xd1\ +\xa4\xef\xe1\x1a^$\x19o\x8b\xaa\x01\xf8$\x9e\x0f\x81\ +\xc3W\xfd\x0fg\xdd\xc0\xde7\xec,\xa5\xbco\x18\xf7\ +0\x893\xf898\x96\xcb\xe6\xd6\xcaY`X\x81G\ +\x03\xfe.\x96\xe0\x0d\x0e\x8e4\x1d\xf7`\xff<\xf0a\ +:\x9e\xe4d\xb3\xde\xd7\xc0a[\x9b|\xf6\xeb\xf7\xb7\ +z\x98du)\xe55\xb6\xe1\x0e6TI\xd6\xe8\xff\ +#\x96\xffc\x83\x07\xa5\x94\xe9\xd6$Y5\x82\xf3\xff\ +\x01\x0cW\x93\xecnM)\xe5\xcbb\xfd\xdb\xfd\xf4\x1f\ +\xe0p\x1fG[\xf3\x1b\xdb,\x90K\x95\x7f\xdek\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xb8\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x15\x00\x00\x00\x0e\x08\x06\x00\x00\x00\xc0\x06W\xce\ +\x00\x00\x01\x7fIDAT8\x8d\x8d\xd3\xbdk\x15Q\ +\x10\xc6\xe1g\xaf\x17EQ\xd1\x18E\x04\xb1\xd2F\xcb\ +\x01k\x0b\xed\x82\xa4NH\xa1EH\xa7]\x08\xa2H\ +\x04M\xa5\x85 \x8a \xe2_ X(\x08\x8aU\xc0\ +)\xc5\xc2&\x85\xe2g\xe7G\x22\x92\x0f\x8b\xdd\xab\xeb\ +\x82\xbbw\xba3\xf3\xce\xef\xcc9\xe7=\x85Fd\xe6\ +fLa\x0cG\xb1\x17[\xd1\xaf$k\xf8\x81\x0fx\ +\x81k\x11\xb1Tg\x145\xd8q\xdc\xc11\xf4\x9a\x9b\ +u\xc4\x1bLF\xc4\xcb?\xd0\xcc\xbc\x8b\xb3\x0d\xe1\x0a\ +>\xe3#~V\xb9-\xd5\xe4\xfb\xb0\xa3\xa1\xdf\xc0\xa5\ +\x88\x98/2\xf3\x02\xe6k\xa0\x07\xb8\x12\x11o\xdbF\ +\xcb\xcc]\x98\xab\x86\x19\xa9\x95\xc6\x8b\xcc\xfc\x86\xedx\ +\x84\xd3\x11\xb1>\xd4\x81\xff\xdd`\x12\xd71\x8awE\ +fn`-\x22\xfa\xed\xad\x9d\xe0Sx\x82\xf5\xc1\x83\ +l\xca\xcc\xd7\x999\xd2\xd27h\x9e\xce\xccC\xb5\xf5\ +\x91\xcc|\x8e\xc7U\xaa(2sYi\x19\xf8\x8a=\ +\x11\xb1\xda\x00\xf5q\x15\xd3\xca\x07\x9a\xc5+,(m\ +W\x8f\xef}\xdc\xc6\xb9*\xb1\x13\x13\xb8_\xc1F\x95\ +6\x1b\xf3\xd7\xa7p\x11\xdb\xfes\x98\x87\xbd\x888\x8f\ +\xa7\xb5\xe4\xfe\x0a8\xa3\xb4\xd4x\x03\xa8\x05\xf8\x1eg\ +z\x10\x11'1\x83/\xaa\xab\x88\x88[\xb8\xa7\xf4_\ +W\xac\xe2\x19\x0eG\xc4\xaf\xa2K\x9d\x99\x07\xb1\x88\x03\ +\x8d\xd22\x96\x94_\xf5rD|\x1a\x14:\xa15\xf8\ +\x14n`7f#ba\xd8\xdea\xe073\xf3D\ +\x9b\xe67\xefB~?\xd8\xe7\x90V\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x02\x0e\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x12\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1b\x06/\x5c\ +\x00\x00\x01\xd5IDAT8\x8dm\xd3M\x88\xceQ\ +\x14\x06\xf0\xdf;\xc6\x82)\xb10jl|5\x92\x8f\ +1u\x94\x90\x95\x95\x9a\x94\x12j2\x09[\x1a6,\ +H6\xa3|\x84\xcd\xa4h\xb2\xd1\x88\x99\x85\xaf\x86\xa4\ +\x88l\x8ef\x8a\x15K\xb3@hR\x94\x91X\xbc\xd7\ +\xf4\xce\xbf9u\xeb\x9e\xf3\x9c\x9es\xces\xef\xa9e\ +\xe6\x1cl\xc3hD\x8c\x9b\xc12\xb3\x0fK\xd0\x1d\x11\ +\x7ff\xca\xa9e\xe6\x03\xcc\xc5j\xb4G\xc4Df\x1e\ +\xc2A\xec\x88\x88\x8f\x999\x86\xa5h-g/\x1eF\ +\xc4\xdb\xffDMh\xc7\x1d\xfc\xc5\xfc\x12_\x8cuh\ +)\xfe0\xceD\xc4/\xdc,\xd8\x93\xc6\x8e\x9a\xb0\x1f\ +[\xf1\x1c\x9b!\x22N\xe3\x18\x96\x97\xbce\xf8Z\xee\ +\xdf\xb0\x16\x9f\xa7\x11E\xc4\x8b\x88\xd8\x8d\xe38\x95\x99\ +\x07\x0a6\x80\xdd\xe5\xde\x82\xef\x99\xb9\x08kp\xf6\x7f\ +\xd1)\x8d\x1a\x9d\xcc\x5c\x88\x07\x18\x88\x88\xfe\xcc<\x82\ +\x11\x5cB?\x8e\xe2bD\xdc\xcd\xcc\x1az\x8b\xb6\xe7\ +\xa6\x11\x15\xb2\x16\xdc\xc6c\x5c+z\xacG'\x86\x22\ +b\xa4\xe4m\xc1u\x0caC\xad\x04[\xd1\x19\x11\x8f\ +\x8a?\x1b]\x111\xdcP`\x05\xc6\x11\xd8\xa4\xfee\ +6b\x14\xef\x9bK\xde\x05tg\xe63L\x94Xo\ +\xa5\xd9\x85\xb8\x887x\x8dA\xcc\xc3J\xdco$\x1a\ +\xc3\x95\x88\xf8\xdd\xd0EG\xe9b\x01\xda\xcah7*\ +\x05\xdeR\x11\xbb\xa2\xd5N\xec\xc2>u\x91g\xe1G\ +\x19\xe9\x9e\xfa\x83L\x15m\x9a\x81\xa0\x96\x99'\xd1\x83\ +\x9e\x92<\x88=\x11q\x19?q\xb5\xe83e\xd5\xe7\ +\xefB\x1f>\xa8\xaf\xc7d\x03\xf6\xb2h\xf4N\xfdC\ +\xdej\xdc\xbbf\xd3m;V\xa9/\xe7d\x05\x9b\xc0\ +y\xf5\xbd\xec\xa8.ou\xb4\xc3h\x8b\x88\xb1\xea\xc8\ +\xea{\xd8\x87/XQ\x05\xa7uT\xf4\xf84\x03\x09\ +\x9c\xc0\x19<\xc5\xab*\xf8\x0f\xdd\xdb\x9d,w\xcc\x1f\ +\xb4\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x04\xe9\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\ +\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x00\x06\ +bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\ +\x00\x09oFFs\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x5c\ +]\xb9S\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\ +\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\ +\x07\xdf\x0c\x12\x0e\x1b\x0b$&IW\x00\x00\x00\x09v\ +pAg\x00\x00\x03@\x00\x00\x00 \x00\xbe\xe6\x89Z\ +\x00\x00\x03\xaeIDATX\xc3\xed\xd6]\x88UU\ +\x14\x07\xf0\xdf\x98\x8e\x96\xa8QIjA\x1f\x04BD\ +\xd0KQ\xf8\x12\x91\x11=\x04\xf5b'\x85\x8e\x88\x8d\ +i\xa7\x14\x02\xc3\xc2\xbe\xa0\xcc,8f\x1aa\xa7/\ +N\x11J\x05RP\xe1C\x1f\x86\x0f\xbe\x98(\x94\x1a\ +F\x94\x85Y\x98\xcd\x80:\xde\x1e\xf6\xbe\xce\x9e\xdb\xbd\ +3:MO\xb5\xe0p\xf7\xddg\x9d\xb5\xfe{}\xfc\ +\xf7\xe2\xbf.]\xcdE\x96\x17Oa\xc5(\xdb\x9f]\ +W\xe5'C)\x8cM\xd6+p\x02\x07p\xec\x1f:\ +\x9e\x82\x19\xb8\x1a\xa7\x0d\x00\x0e\xd4Uy\xc5h\x1c=\ +\xcb\x8b\x06N\x0e\xa7\xd7\x0a\xe0X\x07cS\xb0\x11\x1b\ +\xeb\xaa\xfch\x04``a\x8c\xcc\xea\xba*;\x02h\ +~\xb0\x16S\xf1\x07&`\x12n\xc7\xac,/\xde\xc5\ +\xf7Q\xb5\x0b\xefa:\xaeCo]\x95/%\xa6\x9a\ +5\xb6\x04k\x84\x14\x8f\xc9\xf2bU\x13D[\x00\x98\ +\x17\xd1\xf6cL\xb2\x7f!\xee\x8b\xc0\xc6\xe3\xec\x08\xe6\ +Z,C\x1fR\x00?dy\xb1\x00O\xa3;>\xcb\ +\xb0j\xc8\x08\xe0!\xcc\xc48\xfc\x88\x8bp\x17.\xc0\ +Y\xf1\x99\x8f\x8b\xb1\x07G\xe2i\x8f&6\x1e\x8b\xfa\ +\xeb\x92\xbdo1o\xd8\x14\xd4U\xf9J\x9b\xb4lC\ +\x95\xa4d\x0dz\xea\xaa\xdc\x89\x9dh\xad\x8d?\xf1L\ +\xf2\xffg\xcc\xaf\xabr{\xaa\xd4\xa9\x06\xee\xc0e\x06\ +\xaa\xb8\x0b\xbf\xe1~\xac\xc6\xb9B\x9b\xbd\x9d\xe5\xc5-\ +uU~\x99\xe5\xc5R\xf4\xd5U\xb9!\xcb\x8b\x1e<\ +\x19\xc16\x9d\xcf\xa9\xab\xf2\x8bV_\x9dR\xb0\x14\xb3\ +\xda\xec7\xe2\xd3\x1f\xd30\x11\x1fdy\xb1\x0c+\x85\ +\x22\xdb\x80\xf5-a\x9f\xdf\xce\xf9P\x006\xe2\xb3\xe8\ +,\x95cx\x1d7\x09\xc56\x1e\xe7\xc7P/\xc0\x8c\ +,/~O\xf4\x8f\x0a9\xdf\xde\xc1O\xc7\x1ax\xcd\ +\xd0\xf2j\x96\x17\xbdX+\x14\xdat\xbc)tLw\ +\xd4\xd9\x8f|(\xe7\x1d\x01dyq'.\xd5\x99\xc9\ +\xba\xf0\x0b\x1eF)\xb4\xe3\x84\xe4\xfd^<\x81\xc3\xc3\ +\x1c\xa4c\x0a\x1e\xd4\xbe\x06Ri\x08\xfd\xbcI\xe0\x8d\ +T\xd6\xc7\xc8\xcc\xc6\xae\x91\x00x\x03\xdb\x87\x89\xc0.\ +\xa1\x1d\x97\xb7y\xbfR`\xbfm#\x8a@;\x1eh\ +\x95,/\x16\x09\x0c\xd7\x94\xc3\x02qM\xc2d\xa10\ +\x17c\xdf\x19\x03\xc8\xf2b\xb9p\x95\xa6\x11\xe8\xc2q\ +!\xbf\x87\xf0\x82\xd0\x05\xf0\x13\xee\xc1%x\xd6\x00O\ +\xbc\x95\xe5\xc5\xaduU~~\xa6)\xb8M\xfb\x1a8\ +.\xe4\xbe;\xd9\xfbF\xa0\xdd\x0f\xe3\xbbE\x06\xc8j\ +\x22\xde\xcf\xf2ba]\x95\x9b\xcf\x04\xc0\xa3\xf1\x04M\ +\x1eh\xa0W`\xc7\xc7\x13\x00G1\x17\x07\xe3o\x1f\ +\xb6\xc6\xc8\xad\x13:\xe3<\xbc\x98\xe5Eo\xbb\xab<\ +\x1d\xc9\x1a\xd8SW\xe5\x95\x1d\xd2r\xaf\xc0rM\xd9\ +\x8f\xb9uU~\xd5F\x17z\x84V\x9c\x1a\xb7\x8f\x08\ +t<\x08D[\x00\x09\x0f4\x84Ke\x1c\x9e3\x90\ +\xf3}x$\x86\xf9S\xc9< \xcc\x077\xe0F\xec\ +\x10Z\xb2\xf9\xddA,I\xd31\x1c\x0f\xf4\x0b\xfc\xde\ +\x9d\x80=\x89\xcbQ\xc7\xbd9\x06\xe6\x81\x13\xd8\x1d\xed\ +.\xc6\xddx@ \xabnL\xc3\xcbY^l\x1en\ + i\xf2@\xbfPd\xd7\x08\x83\xc8^a.\x98\x8e\ +\x9b\xe3\xfb\xdd\x06\xcf\x03\xdf\x09\x83\x0bL\xab\xab\xf2\xf9\ +,/~\x8d\xe9\xeb\x12\xae\xf0S\x8eN\x8b\x07\xb2\xbc\ +\x98\x1c\xc3\xbb\xa5\xae\xca\x1dq{K\xa2\xf2\xb5d\x1e\ +\xc8\xf2\xe2\xfa\xb8lD{\x9b\xb2\xbc\x18\x8b\xee\xba*\ +7\xa5\xb6[\x01\x8cI\x8ahr\xb2\xdf\x10\xfa\xfbP\ +\x043\x94\x1c\xc19q}\xaa\xc6\xea\xaa|'\xda\x1d\ +$\xad\x00f\xc6b\x1c-\x19D\xe5i\xe8\xdb\x01\xc8\ +p\x95\xbf\xcf\x00#\x95\x06>\x1e\xc5\xc3\xfc/\xff\x8e\ +\xfc\x05\x99&,\xa0:Dw\xab\x00\x00\x00%tE\ +Xtdate:create\x0020\ +15-12-18T14:27:1\ +1+00:00YM\xe3\xc6\x00\x00\x00%t\ +EXtdate:modify\x002\ +015-12-18T14:27:\ +11+00:00(\x10[z\x00\x00\x00\x00\ +IEND\xaeB`\x82\ +\x00\x00\x04~\ +\x00\ +\x00\x01\x00\x01\x00\x10\x10\x00\x00\x00\x00 \x00h\x04\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\ +\x00\x01\x00 \x00\x00\x00\x00\x00@\x04\x00\x00\x13\x0b\x00\ +\x00\x13\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +>\x00\x00\x00\xa1\x00\x00\x00\xd6\x00\x00\x00\xeb\x00\x00\x00\ +\xec\x00\x00\x00\xd8\x00\x00\x00\xa7\x00\x00\x00G\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x9e\x02\x022\ +\xff\x04\x10\x81\xff\x03#\xaa\xff\x02,\xbb\xff\x02-\xbc\ +\xff\x03$\xac\xff\x03\x12\x85\xff\x02\x028\xff\x00\x00\x00\ +\xac\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x0c\x00\x00\x00\xbb\x03\x08\x5c\xff\x02/\xcb\ +\xff\x00;\xca\xff\x00:\xca\xff\x009\xca\xff\x009\xca\ +\xff\x00:\xca\xff\x00;\xca\xff\x022\xcb\xff\x03\x09b\ +\xff\x00\x00\x00\xc9\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x98\x04\x08^\xff\x015\xca\xff\x00:\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x00:\xca\xff\x018\xca\ +\xff\x03\x09c\xff\x00\x00\x00\xaf\x00\x00\x00\x00\x00\x00\x00\ +0\x00\x00\x00\xfd\x02.\xc8\xff\x00:\xca\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x00:\xca\ +\xff\x022\xcb\xff\x02\x027\xff\x00\x00\x00E\x00\x00\x00\ +\x92\x04\x0eu\xff\x00:\xca\xff\x008\xca\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x00;\xca\xff\x03\x13\x87\xff\x00\x00\x00\xaa\x00\x00\x00\ +\xce\x02\x1f\xa4\xff\x00:\xc9\xff\x008\xc9\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x008\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x00:\xca\xff\x03%\xaf\xff\x00\x00\x00\xdc\x00\x00\x00\ +\xea\x02)\xb9\xff\x01:\xca\xff\x088\xd1\xff\x098\xd1\ +\xff\x078\xd0\xff\x058\xce\xff\x038\xcd\xff\x028\xcc\ +\xff\x018\xca\xff\x008\xca\xff\x008\xca\xff\x008\xca\ +\xff\x009\xca\xff\x02-\xbd\xff\x00\x00\x00\xed\x00\x00\x00\ +\xe8\x02)\xb8\xff\x0d:\xd6\xff\x139\xdb\xff\x118\xd9\ +\xff\x0f8\xd7\xff\x0d8\xd6\xff\x0b8\xd4\xff\x0a8\xd3\ +\xff\x088\xd1\xff\x068\xcf\xff\x048\xce\xff\x028\xcb\ +\xff\x009\xc9\xff\x02-\xbd\xff\x00\x00\x00\xed\x00\x00\x00\ +\xcb\x06\x1d\xa5\xff\x1b;\xe2\xff\x1a9\xe2\xff\x189\xe0\ +\xff\x179\xde\xff\x159\xdd\xff\x139\xdb\xff\x129\xda\ +\xff\x108\xd8\xff\x0e8\xd7\xff\x0c8\xd5\xff\x0a8\xd3\ +\xff\x01:\xcb\xff\x03$\xad\xff\x00\x00\x00\xd9\x00\x00\x00\ +\x8b\x07\x0dr\xff\x22;\xe8\xff\x22:\xe9\xff 9\xe7\ +\xff\x1e9\xe5\xff\x1c9\xe3\xff\x1b9\xe2\xff\x199\xe0\ +\xff\x179\xdf\xff\x169\xdd\xff\x149\xdc\xff\x139\xdb\ +\xff\x07;\xd0\xff\x03\x11\x82\xff\x00\x00\x00\xa3\x00\x00\x00\ +(\x00\x00\x00\xfa\x1c+\xd7\xff,;\xf2\xff(:\xee\ +\xff&:\xec\xff$:\xeb\xff#:\xe9\xff!:\xe7\ +\xff\x1f9\xe6\xff\x1d9\xe4\xff\x1c9\xe3\xff\x1b;\xe2\ +\xff\x090\xd1\xff\x02\x02/\xff\x00\x00\x00;\x00\x00\x00\ +\x00\x00\x00\x00\x88\x07\x08[\xff,5\xf1\xff3;\xf8\ +\xff.;\xf3\xff,:\xf1\xff*:\xf0\xff(:\xee\ +\xff':\xed\xff%:\xeb\xff&;\xec\xff\x1c6\xe3\ +\xff\x04\x08]\xff\x00\x00\x00\x9f\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x05\x00\x00\x00\xaa\x08\x08[\xff)+\xe2\ +\xff8;\xfc\xff7;\xfc\xff4;\xf9\xff2;\xf8\ +\xff1;\xf6\xff/;\xf5\xff#/\xe5\xff\x06\x08`\ +\xff\x00\x00\x00\xbb\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x88\x00\x00\x00\ +\xf6\x0e\x0ex\xff\x1e\x1f\xb8\xff)+\xda\xff)+\xdb\ +\xff\x1d!\xba\xff\x0e\x0f~\xff\x00\x00\x00\xfb\x00\x00\x00\ +\x96\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ ++\x00\x00\x00\x8a\x00\x00\x00\xc8\x00\x00\x00\xe7\x00\x00\x00\ +\xe8\x00\x00\x00\xcb\x00\x00\x00\x91\x00\x00\x003\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0f\x00\ +\x00\xc0\x03\x00\x00\x80\x01\x00\x00\x80\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\ +\x00\x80\x01\x00\x00\xc0\x03\x00\x00\xf0\x0f\x00\x00\ +\x00\x00\x03F\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a source contr\ +ol connected\x0d\x0a \x0d\x0a \ + \x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x03J\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a source contr\ +ol - not setup\x0d\x0a \x0d\x0a\ + \x0d\x0a \x0d\ +\x0a\x0d\x0a\ +\x00\x00\x04~\ +\x00\ +\x00\x01\x00\x01\x00\x10\x10\x00\x00\x00\x00 \x00h\x04\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\ +\x00\x01\x00 \x00\x00\x00\x00\x00@\x04\x00\x00\x13\x0b\x00\ +\x00\x13\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x1a\x1c\ +>\x1c\x0f\x1b\xa1\x1c\x13\x1b\xd6\x1b\x1e\x1b\xeb\x19\x1d\x1a\ +\xec\x17\x0f\x17\xd8\x13\x05\x11\xa7\x0d\x09\x0cG\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00###\x0a#\x19\x22\x9e!$\x22\ +\xff\x18q!\xff\x0d\xb6\x1e\xff\x09\xd0\x1d\xff\x09\xd2\x1d\ +\xff\x0d\xba\x1f\xff\x16x \xff\x18!\x19\xff\x10\x05\x0f\ +\xac\x0f\x0f\x0f\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00%&%\x0c'\x1b&\xbb\x1fK$\xff\x09\xcd\x1d\ +\xff\x00\xfd\x19\xff\x00\xfb\x18\xff\x00\xf7\x18\xff\x00\xf6\x18\ +\xff\x00\xfa\x18\xff\x00\xff\x19\xff\x0a\xd7\x1f\xff\x18P\x1e\ +\xff\x12\x06\x11\xc9\x04\x05\x05\x13\x00\x00\x00\x00\x00\x00\x00\ +\x00-#,\x98\x22I&\xff\x03\xe6\x1a\xff\x00\xfb\x18\ +\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\ +\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf9\x18\xff\x05\xf1\x1c\ +\xff\x18P\x1e\xff\x11\x06\x11\xaf\x00\x00\x00\x00.,.\ +0+.+\xfd\x08\xc8\x1c\xff\x00\xfb\x18\xff\x00\xf0\x19\ +\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\ +\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xfa\x18\ +\xff\x09\xd7\x1e\xff\x19\x22\x1a\xff\x0c\x09\x0cE3&1\ +\x92\x1dk%\xff\x00\xfb\x18\xff\x00\xf0\x19\xff\x00\xf0\x18\ +\xff\x00\xf0\x18\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\ +\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\ +\xff\x00\xff\x19\xff\x16x \xff\x14\x07\x13\xaa3'2\ +\xce\x0f\xa5\x1f\xff\x00\xfc\x17\xff\x00\xf0\x17\xff\x01\xf0\x1a\ +\xff\x02\xf0\x1b\xff\x00\xf0\x19\xff\x00\xf0\x18\xff\x00\xf0\x18\ +\xff\x00\xf0\x18\xff\x00\xf0\x19\xff\x00\xf0\x19\xff\x00\xf0\x19\ +\xff\x00\xfa\x18\xff\x0c\xba\x1f\xff\x1a\x12\x19\xdc5/4\ +\xea\x07\xc2\x1a\xff\x03\xf9\x1b\xff!\xf27\xff%\xf2:\ +\xff\x1e\xf24\xff\x16\xf1-\xff\x0f\xf1&\xff\x08\xf0!\ +\xff\x04\xf0\x1c\xff\x01\xf0\x1a\xff\x00\xf0\x18\xff\x00\xf0\x18\ +\xff\x00\xf7\x18\xff\x08\xd1\x1d\xff\x1b\x1f\x1c\xed616\ +\xe8\x07\xc1\x1b\xff:\xfcO\xffT\xf5e\xffH\xf4Z\ +\xffA\xf4S\xff9\xf4M\xff1\xf3F\xff*\xf2?\ +\xff\x22\xf27\xff\x1a\xf10\xff\x12\xf1*\xff\x08\xf0 \ +\xff\x00\xf7\x17\xff\x08\xd0\x1d\xff\x1d \x1d\xed8.7\ +\xcb \xa1.\xffs\xff\x82\xffr\xf6\x80\xffi\xf6x\ +\xffb\xf6q\xffZ\xf6k\xffS\xf5d\xffL\xf5]\ +\xffD\xf4W\xff=\xf4P\xff6\xf3I\xff-\xf2A\ +\xff\x06\xfb\x1f\xff\x0b\xb5\x1d\xff \x17\x1f\xd9;2:\ +\x8b3i9\xff\x91\xff\x9d\xff\x95\xfa\x9f\xff\x8b\xf8\x96\ +\xff\x83\xf8\x8f\xff{\xf7\x88\xfft\xf7\x82\xffm\xf7{\ +\xffe\xf6t\xff^\xf6n\xffV\xf5g\xffS\xf5d\ +\xff\x1d\xfe5\xff\x13q\x1d\xff\x1f\x12\x1e\xa3><=\ +(262\xfa\x83\xc4\x8a\xff\xbf\xff\xc7\xff\xac\xfa\xb4\ +\xff\xa5\xfa\xae\xff\x9d\xf9\xa7\xff\x96\xf9\xa0\xff\x8e\xf9\x99\ +\xff\x87\xf8\x92\xff\x7f\xf7\x8c\xffx\xf7\x85\xfft\xff\x83\ +\xff(\xce9\xff\x1f' \xff!\x1e!;\x00\x00\x00\ +\x00>;>\x88DME\xff\xbd\xe3\xc2\xff\xdb\xff\xdf\ +\xff\xc7\xfd\xcc\xff\xbe\xfb\xc4\xff\xb6\xfb\xbe\xff\xaf\xfa\xb7\ +\xff\xa8\xfa\xb0\xff\xa1\xfa\xaa\xff\xa3\xff\xad\xffx\xeb\x84\ +\xff$M(\xff'\x1f&\x9f\x00\x00\x00\x00\x00\x00\x00\ +\x00<=<\x05979\xaaLPL\xff\xba\xc7\xbb\ +\xff\xf1\xff\xf4\xff\xee\xff\xf0\xff\xe2\xff\xe6\xff\xda\xff\xdf\ +\xff\xd5\xff\xda\xff\xcd\xff\xd3\xff\x9b\xd0\xa0\xff9M;\ +\xff#\x1d#\xbb\x0d\x0e\x0d\x0b\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00CCC\x04<;<\x889:9\ +\xf6lnl\xff\xa4\xa8\xa4\xff\xc3\xcb\xc4\xff\xc1\xcd\xc2\ +\xff\xa2\xb0\xa3\xffitj\xff333\xfb,'+\ +\x96$$$\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BBB\ ++555\x8a111\xc8444\xe7333\ +\xe8-+-\xcb,*,\x911113\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0f\x00\ +\x00\xc0\x03\x00\x00\x80\x01\x00\x00\x80\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\ +\x00\x80\x01\x00\x00\xc0\x03\x00\x00\xf0\x0f\x00\x00\ +\x00\x00\x03L\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a source contr\ +ol - warning v2<\ +/title>\x0d\x0a \ +\x0d\x0a \ +\x0d\x0a \x0d\x0a\x0d\x0a\ +\x00\x00\x04~\ +\x00\ +\x00\x01\x00\x01\x00\x10\x10\x00\x00\x00\x00 \x00h\x04\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\ +\x00\x01\x00 \x00\x00\x00\x00\x00@\x04\x00\x00\x13\x0b\x00\ +\x00\x13\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +>\x00\x00\x00\xa1\x00\x00\x00\xd6\x00\x00\x00\xeb\x00\x00\x00\ +\xec\x00\x00\x00\xd8\x00\x00\x00\xa7\x00\x00\x00G\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x9e\x0148\ +\xff\x02\x8c\x92\xff\x01\xbf\xc1\xff\x01\xd5\xd4\xff\x01\xd6\xd5\ +\xff\x01\xc2\xc3\xff\x01\x91\x97\xff\x01;@\xff\x00\x00\x00\ +\xac\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x0c\x00\x00\x00\xbb\x01bh\xff\x01\xe7\xe6\ +\xff\x00\xec\xe6\xff\x00\xec\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xec\xe6\xff\x00\xec\xe6\xff\x01\xe8\xe6\xff\x01io\ +\xff\x00\x00\x00\xc9\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x98\x02cj\xff\x00\xea\xe6\xff\x00\xec\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xec\xe6\xff\x01\xeb\xe6\ +\xff\x01jp\xff\x00\x00\x00\xaf\x00\x00\x00\x00\x00\x00\x00\ +0\x00\x00\x00\xfd\x01\xe4\xe4\xff\x00\xec\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xec\xe6\ +\xff\x01\xe8\xe6\xff\x019>\xff\x00\x00\x00E\x00\x00\x00\ +\x92\x02~\x84\xff\x00\xec\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xec\xe6\xff\x01\x93\x9a\xff\x00\x00\x00\xaa\x00\x00\x00\ +\xce\x02\xb7\xba\xff\x00\xec\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xec\xe6\xff\x01\xc6\xc6\xff\x00\x00\x00\xdc\x00\x00\x00\ +\xea\x01\xd3\xd3\xff\x00\xec\xe6\xff\x04\xeb\xe9\xff\x04\xeb\xe9\ +\xff\x03\xeb\xe9\xff\x02\xeb\xe8\xff\x02\xeb\xe7\xff\x01\xeb\xe7\ +\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\xff\x00\xeb\xe6\ +\xff\x00\xeb\xe6\xff\x01\xd7\xd6\xff\x00\x00\x00\xed\x00\x00\x00\ +\xe8\x01\xd0\xd1\xff\x06\xec\xec\xff\x09\xeb\xee\xff\x08\xeb\xed\ +\xff\x07\xeb\xec\xff\x06\xeb\xeb\xff\x05\xeb\xeb\xff\x05\xeb\xea\ +\xff\x04\xeb\xe9\xff\x03\xeb\xe8\xff\x02\xeb\xe8\xff\x01\xeb\xe7\ +\xff\x00\xeb\xe6\xff\x01\xd7\xd6\xff\x00\x00\x00\xed\x00\x00\x00\ +\xcb\x03\xb4\xb9\xff\x0d\xec\xf1\xff\x0d\xeb\xf1\xff\x0c\xeb\xf0\ +\xff\x0b\xeb\xef\xff\x0a\xeb\xef\xff\x09\xeb\xee\xff\x08\xeb\xed\ +\xff\x07\xeb\xed\xff\x07\xeb\xec\xff\x06\xeb\xeb\xff\x05\xeb\xea\ +\xff\x01\xec\xe6\xff\x01\xc2\xc4\xff\x00\x00\x00\xd9\x00\x00\x00\ +\x8b\x03x\x7f\xff\x10\xec\xf4\xff\x10\xec\xf4\xff\x0f\xec\xf3\ +\xff\x0e\xec\xf3\xff\x0e\xeb\xf2\xff\x0d\xeb\xf1\xff\x0c\xeb\xf1\ +\xff\x0b\xeb\xf0\xff\x0a\xeb\xef\xff\x09\xeb\xee\xff\x09\xeb\xee\ +\xff\x03\xec\xe9\xff\x01\x8d\x93\xff\x00\x00\x00\xa3\x00\x00\x00\ +(\x00\x00\x00\xfa\x0d\xd9\xe5\xff\x15\xec\xf9\xff\x13\xec\xf7\ +\xff\x12\xec\xf6\xff\x11\xec\xf5\xff\x10\xec\xf5\xff\x10\xec\xf4\ +\xff\x0f\xec\xf3\xff\x0e\xeb\xf2\xff\x0d\xeb\xf2\xff\x0d\xec\xf1\ +\xff\x04\xe7\xe9\xff\x0115\xff\x00\x00\x00;\x00\x00\x00\ +\x00\x00\x00\x00\x88\x03^e\xff\x15\xe9\xf8\xff\x18\xec\xfb\ +\xff\x16\xec\xf9\xff\x15\xec\xf9\xff\x14\xec\xf8\xff\x13\xec\xf7\ +\xff\x12\xec\xf6\xff\x12\xec\xf6\xff\x12\xec\xf6\xff\x0d\xea\xf1\ +\xff\x02ci\xff\x00\x00\x00\x9f\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x05\x00\x00\x00\xaa\x03]d\xff\x13\xd9\xea\ +\xff\x1a\xec\xfe\xff\x1a\xec\xfd\xff\x19\xec\xfc\xff\x18\xec\xfb\ +\xff\x17\xec\xfb\xff\x17\xec\xfa\xff\x11\xe3\xf1\xff\x03cj\ +\xff\x00\x00\x00\xbb\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x88\x00\x00\x00\ +\xf6\x06w\x81\xff\x0e\xb2\xc0\xff\x13\xd1\xe2\xff\x13\xd2\xe2\ +\xff\x0e\xb6\xc3\xff\x07~\x88\xff\x00\x00\x00\xfb\x00\x00\x00\ +\x96\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ ++\x00\x00\x00\x8a\x00\x00\x00\xc8\x00\x00\x00\xe7\x00\x00\x00\ +\xe8\x00\x00\x00\xcb\x00\x00\x00\x91\x00\x00\x003\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0f\x00\ +\x00\xc0\x03\x00\x00\x80\x01\x00\x00\x80\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\ +\x00\x80\x01\x00\x00\xc0\x03\x00\x00\xf0\x0f\x00\x00\ +\x00\x00\x04~\ +\x00\ +\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00 \x00h\x04\x00\ +\x00\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\ +\x00\x01\x00 \x00\x00\x00\x00\x00@\x04\x00\x00\x13\x0b\x00\ +\x00\x13\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|||\ +>|||\xa1|||\xd6|||\xeb|||\ +\xec|||\xd8|||\xa7|||G\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00|||\x0a|||\x9e\x89\x89\x89\ +\xff\x9e\x9e\x9e\xff\xa8\xa8\xa8\xff\xac\xac\xac\xff\xad\xad\xad\ +\xff\xa9\xa9\xa9\xff\x9f\x9f\x9f\xff\x8b\x8b\x8b\xff|||\ +\xac|||\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00|||\x0c|||\xbb\x94\x94\x94\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\x96\x96\x96\ +\xff|||\xc9|||\x13\x00\x00\x00\x00\x00\x00\x00\ +\x00|||\x98\x95\x95\x95\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\x96\x96\x96\xff|||\xaf\x00\x00\x00\x00|||\ +0|||\xfd\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\x8a\x8a\x8a\xff|||E|||\ +\x92\x9b\x9b\x9b\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\x9f\x9f\x9f\xff|||\xaa|||\ +\xce\xa6\xa6\xa6\xff\xaf\xaf\xaf\xff\xaf\xaf\xaf\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xaa\xaa\xaa\xff|||\xdc|||\ +\xea\xac\xac\xac\xff\xb0\xb0\xb0\xff\xb3\xb3\xb3\xff\xb4\xb4\xb4\ +\xff\xb3\xb3\xb3\xff\xb2\xb2\xb2\xff\xb1\xb1\xb1\xff\xb1\xb1\xb1\ +\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\xff\xb0\xb0\xb0\ +\xff\xb0\xb0\xb0\xff\xad\xad\xad\xff|||\xed|||\ +\xe8\xac\xac\xac\xff\xb6\xb6\xb6\xff\xb9\xb9\xb9\xff\xb8\xb8\xb8\ +\xff\xb7\xb7\xb7\xff\xb6\xb6\xb6\xff\xb5\xb5\xb5\xff\xb4\xb4\xb4\ +\xff\xb3\xb3\xb3\xff\xb2\xb2\xb2\xff\xb2\xb2\xb2\xff\xb0\xb0\xb0\ +\xff\xaf\xaf\xaf\xff\xad\xad\xad\xff|||\xed|||\ +\xcb\xa8\xa8\xa8\xff\xbd\xbd\xbd\xff\xbc\xbc\xbc\xff\xbb\xbb\xbb\ +\xff\xbb\xbb\xbb\xff\xba\xba\xba\xff\xb9\xb9\xb9\xff\xb8\xb8\xb8\ +\xff\xb7\xb7\xb7\xff\xb6\xb6\xb6\xff\xb5\xb5\xb5\xff\xb4\xb4\xb4\ +\xff\xb0\xb0\xb0\xff\xa9\xa9\xa9\xff|||\xd9|||\ +\x8b\x9b\x9b\x9b\xff\xc0\xc0\xc0\xff\xc0\xc0\xc0\xff\xbf\xbf\xbf\ +\xff\xbe\xbe\xbe\xff\xbd\xbd\xbd\xff\xbd\xbd\xbd\xff\xbc\xbc\xbc\ +\xff\xbb\xbb\xbb\xff\xba\xba\xba\xff\xb9\xb9\xb9\xff\xb9\xb9\xb9\ +\xff\xb3\xb3\xb3\xff\x9e\x9e\x9e\xff|||\xa3|||\ +(|||\xfa\xba\xba\xba\xff\xc5\xc5\xc5\xff\xc3\xc3\xc3\ +\xff\xc2\xc2\xc2\xff\xc1\xc1\xc1\xff\xc1\xc1\xc1\xff\xc0\xc0\xc0\ +\xff\xbf\xbf\xbf\xff\xbe\xbe\xbe\xff\xbd\xbd\xbd\xff\xbd\xbd\xbd\ +\xff\xb4\xb4\xb4\xff\x88\x88\x88\xff|||;\x00\x00\x00\ +\x00|||\x88\x95\x95\x95\xff\xc5\xc5\xc5\xff\xc8\xc8\xc8\ +\xff\xc6\xc6\xc6\xff\xc5\xc5\xc5\xff\xc4\xc4\xc4\xff\xc3\xc3\xc3\ +\xff\xc3\xc3\xc3\xff\xc2\xc2\xc2\xff\xc2\xc2\xc2\xff\xbd\xbd\xbd\ +\xff\x95\x95\x95\xff|||\x9f\x00\x00\x00\x00\x00\x00\x00\ +\x00|||\x05|||\xaa\x95\x95\x95\xff\xc0\xc0\xc0\ +\xff\xcb\xcb\xcb\xff\xca\xca\xca\xff\xc9\xc9\xc9\xff\xc8\xc8\xc8\ +\xff\xc7\xc7\xc7\xff\xc7\xc7\xc7\xff\xc0\xc0\xc0\xff\x96\x96\x96\ +\xff|||\xbb|||\x0b\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00|||\x04|||\x88|||\ +\xf6\x9e\x9e\x9e\xff\xb3\xb3\xb3\xff\xbe\xbe\xbe\xff\xbf\xbf\xbf\ +\xff\xb3\xb3\xb3\xff\xa0\xa0\xa0\xff|||\xfb|||\ +\x96|||\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|||\ ++|||\x8a|||\xc8|||\xe7|||\ +\xe8|||\xcb|||\x91|||3\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0f\x00\ +\x00\xc0\x03\x00\x00\x80\x01\x00\x00\x80\x01\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\ +\x00\x80\x01\x00\x00\xc0\x03\x00\x00\xf0\x0f\x00\x00\ +\x00\x00\x03D\ +<\ +?xml version=\x221.\ +0\x22 encoding=\x22UTF\ +-8\x22?>\x0d\x0a\x0d\x0a source contr\ +ol error v2\x0d\x0a \x0d\x0a \ + \x0d\ +\x0a \x0d\x0a\x0d\x0a\ +" + +qt_resource_name = b"\ +\x00\x0f\ +\x04T\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x000\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04S\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x005\x00.\x00p\x00n\x00g\ +\x00\x09\ +\x0a\xc5\xacG\ +\x00w\ +\x00a\x00t\x00e\x00r\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x06Z\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x000\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04_\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x009\x00.\x00p\x00n\x00g\ +\x00\x16\ +\x0eE\x9e\x87\ +\x00e\ +\x00r\x00r\x00o\x00r\x00_\x00r\x00e\x00p\x00o\x00r\x00t\x00_\x00e\x00r\x00r\x00o\ +\x00r\x00.\x00s\x00v\x00g\ +\x00\x09\ +\x08\xbcm\xc2\ +\x00s\ +\x00t\x00a\x00t\x00u\x00s\x00b\x00a\x00r\ +\x00\x0f\ +\x04P\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x004\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04\x5c\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x008\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00\xf0&'\ +\x00e\ +\x00r\x00r\x00o\x00r\x00_\x00r\x00e\x00p\x00o\x00r\x00t\x00_\x00w\x00a\x00r\x00n\ +\x00i\x00n\x00g\x00.\x00s\x00v\x00g\ +\x00\x18\ +\x0c\x85t\xe7\ +\x00e\ +\x00r\x00r\x00o\x00r\x00_\x00r\x00e\x00p\x00o\x00r\x00t\x00_\x00c\x00o\x00m\x00m\ +\x00e\x00n\x00t\x00.\x00s\x00v\x00g\ +\x00\x0a\ +\x03\xd6;g\ +\x00M\ +\x00a\x00i\x00n\x00W\x00i\x00n\x00d\x00o\x00w\ +\x00\x0f\ +\x04U\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x003\x00.\x00p\x00n\x00g\ +\x00\x09\ +\x0c\xe6\xbd#\ +\x00v\ +\x00i\x00e\x00w\x00p\x00a\x00n\x00e\x00s\ +\x00\x0f\ +\x04a\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x007\x00.\x00p\x00n\x00g\ +\x00\x07\ +\x0a\xc9\xa6S\ +\x00c\ +\x00u\x00r\x00s\x00o\x00r\x00s\ +\x00\x15\ +\x06S\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x007\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04R\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x002\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04^\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x006\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x06P\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x006\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04W\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x001\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04S\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x005\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x06a\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x005\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04T\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x000\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04P\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x004\x00.\x00p\x00n\x00g\ +\x00\x03\ +\x00\x00x\xc3\ +\x00r\ +\x00e\x00s\ +\x00\x14\ +\x07\x22u\xc7\ +\x00a\ +\x00r\x00h\x00i\x00t\x00y\x00p\x00e\x00_\x00t\x00r\x00e\x00e\x00_\x000\x003\x00.\ +\x00p\x00n\x00g\ +\x00\x05\ +\x00O\xa6S\ +\x00I\ +\x00c\x00o\x00n\x00s\ +\x00\x15\ +\x06^\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x004\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04U\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x003\x00.\x00p\x00n\x00g\ +\x00\x14\ +\x07!u\xc7\ +\x00a\ +\x00r\x00h\x00i\x00t\x00y\x00p\x00e\x00_\x00t\x00r\x00e\x00e\x00_\x000\x002\x00.\ +\x00p\x00n\x00g\ +\x00\x15\ +\x06_\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x003\ +\x00.\x00p\x00n\x00g\ +\x00\x0b\ +\x0f\x08B\x1e\ +\x00A\ +\x00p\x00p\x00l\x00i\x00c\x00a\x00t\x00i\x00o\x00n\ +\x00\x0f\ +\x04R\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x002\x00.\x00p\x00n\x00g\ +\x00\x14\ +\x07(u\xc7\ +\x00a\ +\x00r\x00h\x00i\x00t\x00y\x00p\x00e\x00_\x00t\x00r\x00e\x00e\x00_\x000\x001\x00.\ +\x00p\x00n\x00g\ +\x00\x0f\ +\x04a\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x007\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x06\x5c\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x002\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x04W\x95'\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x005\x00_\x000\x001\x00.\x00p\x00n\x00g\ +\x00\x14\ +\x07'u\xc7\ +\x00a\ +\x00r\x00h\x00i\x00t\x00y\x00p\x00e\x00_\x00t\x00r\x00e\x00e\x00_\x000\x000\x00.\ +\x00p\x00n\x00g\ +\x00\x0f\ +\x04^\x95\xc7\ +\x00b\ +\x00m\x00p\x000\x000\x000\x000\x006\x00_\x000\x006\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x06]\x7fG\ +\x00p\ +\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00_\x00t\x00r\x00e\x00e\x00_\x000\x001\ +\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x0e\x0f\xf3\xff\ +\x00o\ +\x003\x00d\x00e\x00_\x00e\x00d\x00i\x00t\x00o\x00r\x00.\x00i\x00c\x00o\ +\x00\x14\ +\x0b\x1b&\xb6\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00_\x00D\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\ +\x00t\x00i\x00f\ +\x00\x17\ +\x0c\x9a\xdb\xc7\ +\x00l\ +\x00o\x00c\x00k\x00_\x00c\x00i\x00r\x00c\x00l\x00e\x00_\x00d\x00e\x00f\x00a\x00u\ +\x00l\x00t\x00.\x00s\x00v\x00g\ +\x00\x10\ +\x07T\xb1'\ +\x00D\ +\x00e\x00f\x00a\x00u\x00l\x00t\x00_\x00o\x00p\x00e\x00n\x00.\x00s\x00v\x00g\ +\x00\x19\ +\x0e\xd2\x13\xe7\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00H\x00a\x00n\x00d\x00l\x00e\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00.\x00s\x00v\x00g\ +\x00\x0e\ +\x0b\xd8M\x87\ +\x00l\ +\x00a\x00y\x00e\x00r\x00_\x00i\x00c\x00o\x00n\x00.\x00s\x00v\x00g\ +\x00\x15\ +\x01\xaf\x1c'\ +\x00E\ +\x00n\x00t\x00i\x00t\x00y\x00_\x00N\x00o\x00t\x00_\x00A\x00c\x00t\x00i\x00v\x00e\ +\x00.\x00s\x00v\x00g\ +\x00\x19\ +\x05\xf2\xd2\x07\ +\x00v\ +\x00i\x00s\x00_\x00o\x00n\x00_\x00N\x00o\x00t\x00T\x00r\x00a\x00n\x00s\x00p\x00a\ +\x00r\x00e\x00n\x00t\x00.\x00s\x00v\x00g\ +\x00\x1c\ +\x05A\x11\x07\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00H\x00a\x00n\x00d\x00l\x00e\x00_\x00E\x00d\x00i\x00t\ +\x00o\x00r\x00_\x00O\x00n\x00l\x00y\x00.\x00s\x00v\x00g\ +\x00\x16\ +\x0c>\x8f\xc7\ +\x00v\ +\x00i\x00s\x00_\x00c\x00i\x00r\x00c\x00l\x00e\x00_\x00d\x00e\x00f\x00a\x00u\x00l\ +\x00t\x00.\x00s\x00v\x00g\ +\x00%\ +\x0f-\x08'\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00H\x00a\x00n\x00d\x00l\x00e\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00E\x00d\x00i\x00t\x00o\x00r\x00_\x00O\x00n\x00l\x00y\ +\x00.\x00s\x00v\x00g\ +\x00\x0a\ +\x01\xb91\x87\ +\x00l\ +\x00o\x00c\x00k\x00e\x00d\x00.\x00s\x00v\x00g\ +\x00\x0f\ +\x05bA^\ +\x00E\ +\x00y\x00e\x00_\x00O\x00p\x00e\x00n\x00_\x00H\x00i\x00d\x00d\x00e\x00n\ +\x00\x1b\ +\x05K\x05V\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00_\x00P\x00a\x00r\x00t\x00i\x00a\x00l\x00_\x00E\ +\x00n\x00a\x00b\x00l\x00e\x00d\x00.\x00t\x00i\x00f\ +\x00\x0c\ +\x0f^26\ +\x00E\ +\x00y\x00e\x00_\x00O\x00p\x00e\x00n\x00.\x00t\x00i\x00f\ +\x00\x10\ +\x03\xcaZG\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00.\x00s\x00v\x00g\ +\x00\x16\ +\x06\x8e\x16\xc7\ +\x00E\ +\x00n\x00t\x00i\x00t\x00y\x00_\x00E\x00d\x00i\x00t\x00o\x00r\x00_\x00O\x00n\x00l\ +\x00y\x00.\x00s\x00v\x00g\ +\x00\x1b\ +\x04\xe2\x14'\ +\x00l\ +\x00o\x00c\x00k\x00_\x00c\x00i\x00r\x00c\x00l\x00e\x00_\x00t\x00r\x00a\x00n\x00s\ +\x00p\x00a\x00r\x00e\x00n\x00t\x00.\x00s\x00v\x00g\ +\x00\x0d\ +\x01m\xcf\x96\ +\x00E\ +\x00y\x00e\x00_\x00S\x00l\x00a\x00s\x00h\x00.\x00t\x00i\x00f\ +\x00\x14\ +\x0e\x00\x9e6\ +\x00E\ +\x00y\x00e\x00_\x00P\x00a\x00r\x00t\x00i\x00a\x00l\x00_\x00O\x00p\x00e\x00n\x00.\ +\x00t\x00i\x00f\ +\x00\x07\ +\x0c\xf8ZG\ +\x00E\ +\x00y\x00e\x00.\x00s\x00v\x00g\ +\x00\x14\ +\x07T)\xf6\ +\x00E\ +\x00y\x00e\x00_\x00S\x00l\x00a\x00s\x00h\x00_\x00H\x00i\x00d\x00d\x00e\x00n\x00.\ +\x00t\x00i\x00f\ +\x00\x1a\ +\x00\xe3\x5c\xa7\ +\x00v\ +\x00i\x00s\x00_\x00c\x00i\x00r\x00c\x00l\x00e\x00_\x00t\x00r\x00a\x00n\x00s\x00p\ +\x00a\x00r\x00e\x00n\x00t\x00.\x00s\x00v\x00g\ +\x00/\ +\x0a\xf8TG\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00E\x00d\x00i\x00t\x00o\x00r\x00_\x00O\x00n\x00l\x00y\ +\x00_\x00U\x00n\x00s\x00a\x00v\x00a\x00b\x00l\x00e\x00.\x00s\x00v\x00g\ +\x00\x13\ +\x09+\x00\xf6\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00_\x00E\x00n\x00a\x00b\x00l\x00e\x00d\x00.\x00t\ +\x00i\x00f\ +\x00\x19\ +\x07,\xf6\x07\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00.\x00s\x00v\x00g\ +\x00.\ +\x07\x84Qg\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00N\x00o\x00t\x00_\x00A\x00c\x00t\x00i\x00v\x00e\x00_\ +\x00U\x00n\x00s\x00a\x00v\x00a\x00b\x00l\x00e\x00.\x00s\x00v\x00g\ +\x00%\ +\x08\xc2\x87g\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00E\x00d\x00i\x00t\x00o\x00r\x00_\x00O\x00n\x00l\x00y\ +\x00.\x00s\x00v\x00g\ +\x00\x19\ +\x0e\x89\x90\x16\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00_\x00E\x00n\x00a\x00b\x00l\x00e\x00d\x00_\x00H\ +\x00o\x00v\x00e\x00r\x00.\x00t\x00i\x00f\ +\x00\x1c\ +\x0bd6G\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00E\x00d\x00i\x00t\ +\x00o\x00r\x00_\x00O\x00n\x00l\x00y\x00.\x00s\x00v\x00g\ +\x00\x0b\ +\x052\xac\xa7\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00.\x00s\x00v\x00g\ +\x00\x10\ +\x08Y\x11\xa7\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00H\x00a\x00n\x00d\x00l\x00e\x00.\x00s\x00v\x00g\ +\x00$\ +\x05\xcb\xc5\xc7\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00N\x00o\x00t\x00_\x00A\x00c\x00t\x00i\x00v\x00e\x00.\ +\x00s\x00v\x00g\ +\x00\x1b\ +\x0e\xf5\xfe\xc7\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00N\x00o\x00t\x00_\ +\x00A\x00c\x00t\x00i\x00v\x00e\x00.\x00s\x00v\x00g\ +\x00\x0c\ +\x0e=1\x87\ +\x00u\ +\x00n\x00l\x00o\x00c\x00k\x00e\x00d\x00.\x00s\x00v\x00g\ +\x00\x1a\ +\x00\x9cw\x07\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00U\x00n\x00s\x00a\ +\x00v\x00a\x00b\x00l\x00e\x00.\x00s\x00v\x00g\ +\x00\x1a\ +\x0a\xe9\xdc\x96\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00_\x00D\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00_\ +\x00H\x00o\x00v\x00e\x00r\x00.\x00t\x00i\x00f\ +\x00\x0a\ +\x00\xb5\xd1\xa7\ +\x00E\ +\x00n\x00t\x00i\x00t\x00y\x00.\x00s\x00v\x00g\ +\x00#\ +\x08\x0b\xd5\x87\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00E\x00n\x00t\x00i\x00t\x00y\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00U\x00n\x00s\x00a\x00v\x00a\x00b\x00l\x00e\x00.\x00s\ +\x00v\x00g\ +\x00\x13\ +\x0e\x1c\x0e\xf6\ +\x00E\ +\x00y\x00e\x00_\x00S\x00l\x00a\x00s\x00h\x00_\x00H\x00o\x00v\x00e\x00r\x00.\x00t\ +\x00i\x00f\ +\x00\x1b\ +\x03\x98\x0c'\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00H\x00a\x00n\x00d\x00l\x00e\x00_\x00N\x00o\x00t\x00_\ +\x00A\x00c\x00t\x00i\x00v\x00e\x00.\x00s\x00v\x00g\ +\x00\x17\ +\x03\xf8\xf5g\ +\x00l\ +\x00o\x00c\x00k\x00_\x00o\x00n\x00_\x00t\x00r\x00a\x00n\x00s\x00p\x00a\x00r\x00e\ +\x00n\x00t\x00.\x00s\x00v\x00g\ +\x00\x1a\ +\x00\xb2\x86\xa7\ +\x00l\ +\x00o\x00c\x00k\x00_\x00o\x00n\x00_\x00N\x00o\x00t\x00T\x00r\x00a\x00n\x00s\x00p\ +\x00a\x00r\x00e\x00n\x00t\x00.\x00s\x00v\x00g\ +\x00\x08\ +\x00\x95Ug\ +\x00v\ +\x00i\x00s\x00b\x00.\x00s\x00v\x00g\ +\x00\x15\ +\x0c\x87\x8f\xd6\ +\x00E\ +\x00y\x00e\x00_\x00P\x00a\x00r\x00t\x00i\x00a\x00l\x00_\x00S\x00l\x00a\x00s\x00h\ +\x00.\x00t\x00i\x00f\ +\x00\x0f\ +\x09\xcbq\xa7\ +\x00v\ +\x00i\x00s\x00b\x00_\x00h\x00i\x00d\x00d\x00e\x00n\x00.\x00s\x00v\x00g\ +\x00\x12\ +\x02{\xf5\x96\ +\x00E\ +\x00y\x00e\x00_\x00O\x00p\x00e\x00n\x00_\x00H\x00o\x00v\x00e\x00r\x00.\x00t\x00i\ +\x00f\ +\x00$\ +\x0a9L\xa7\ +\x00S\ +\x00l\x00i\x00c\x00e\x00_\x00H\x00a\x00n\x00d\x00l\x00e\x00_\x00M\x00o\x00d\x00i\ +\x00f\x00i\x00e\x00d\x00_\x00N\x00o\x00t\x00_\x00A\x00c\x00t\x00i\x00v\x00e\x00.\ +\x00s\x00v\x00g\ +\x00\x16\ +\x05\x5c\xa1g\ +\x00v\ +\x00i\x00s\x00_\x00o\x00n\x00_\x00t\x00r\x00a\x00n\x00s\x00p\x00a\x00r\x00e\x00n\ +\x00t\x00.\x00s\x00v\x00g\ +\x00\x12\ +\x0cS?'\ +\x00D\ +\x00e\x00f\x00a\x00u\x00l\x00t\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00.\x00s\x00v\ +\x00g\ +\x00\x1c\ +\x0d\x1b}6\ +\x00P\ +\x00a\x00d\x00l\x00o\x00c\x00k\x00_\x00P\x00a\x00r\x00t\x00i\x00a\x00l\x00_\x00D\ +\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00t\x00i\x00f\ +\x00\x12\ +\x06\xdb\x9dg\ +\x00P\ +\x00r\x00e\x00f\x00e\x00r\x00e\x00n\x00c\x00e\x00s\x00_\x000\x001\x00.\x00p\x00n\ +\x00g\ +\x00\x0a\ +\x08v\x9cg\ +\x00G\ +\x00l\x00o\x00b\x00a\x00l\x00.\x00s\x00v\x00g\ +\x00\x0a\ +\x0c\x8dj\xa7\ +\x00C\ +\x00a\x00m\x00e\x00r\x00a\x00.\x00s\x00v\x00g\ +\x00\x12\ +\x06\xd8\x9dg\ +\x00P\ +\x00r\x00e\x00f\x00e\x00r\x00e\x00n\x00c\x00e\x00s\x00_\x000\x000\x00.\x00p\x00n\ +\x00g\ +\x00\x0c\ +\x0d\x08\xc6'\ +\x00V\ +\x00i\x00e\x00w\x00p\x00o\x00r\x00t\x00.\x00s\x00v\x00g\ +\x00\x12\ +\x06\xe1\x9dg\ +\x00P\ +\x00r\x00e\x00f\x00e\x00r\x00e\x00n\x00c\x00e\x00s\x00_\x000\x003\x00.\x00p\x00n\ +\x00g\ +\x00\x0a\ +\x00k\xd7\xa7\ +\x00M\ +\x00o\x00t\x00i\x00o\x00n\x00.\x00s\x00v\x00g\ +\x00\x12\ +\x06\xde\x9dg\ +\x00P\ +\x00r\x00e\x00f\x00e\x00r\x00e\x00n\x00c\x00e\x00s\x00_\x000\x002\x00.\x00p\x00n\ +\x00g\ +\x00\x09\ +\x09\xba\xcf\xa7\ +\x00D\ +\x00e\x00b\x00u\x00g\x00.\x00s\x00v\x00g\ +\x00\x10\ +\x03l\x17\xc7\ +\x00E\ +\x00x\x00p\x00e\x00r\x00i\x00m\x00e\x00n\x00t\x00a\x00l\x00.\x00s\x00v\x00g\ +\x00\x09\ +\x02\xc6\xc0\xc7\ +\x00F\ +\x00i\x00l\x00e\x00s\x00.\x00s\x00v\x00g\ +\x00\x0a\ +\x04o\x98\xe7\ +\x00G\ +\x00i\x00z\x00m\x00o\x00s\x00.\x00s\x00v\x00g\ +\x00\x07\ +\x0f\x07J\x02\ +\x00h\ +\x00i\x00t\x00.\x00c\x00u\x00r\ +\x00\x0e\ +\x06\x0c\xfb\x82\ +\x00a\ +\x00r\x00r\x00o\x00w\x00_\x00d\x00o\x00w\x00n\x00.\x00c\x00u\x00r\ +\x00\x0b\ +\x06\x89\xd9\x82\ +\x00c\ +\x00u\x00r\x00s\x00o\x00r\x001\x00.\x00c\x00u\x00r\ +\x00\x0b\ +\x06\x80\xd9\x82\ +\x00c\ +\x00u\x00r\x00s\x00o\x00r\x002\x00.\x00c\x00u\x00r\ +\x00\x17\ +\x06h\xad\xa2\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00s\x00o\x00_\x00s\x00e\x00l\x00_\x00p\x00l\ +\x00u\x00s\x00.\x00c\x00u\x00r\ +\x00\x0d\ +\x08\x8c:\xe2\ +\x00l\ +\x00e\x00f\x00t\x00r\x00i\x00g\x00h\x00t\x00.\x00c\x00u\x00r\ +\x00\x0e\ +\x03\x0c\x0f\xc2\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00H\x00i\x00t\x00.\x00c\x00u\x00r\ +\x00\x12\ +\x03\xfe\x1c\x82\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00s\x00m\x00o\x00o\x00t\x00h\x00.\x00c\x00u\ +\x00r\ +\x00\x11\ +\x06\x8d\xde\x82\ +\x00a\ +\x00r\x00r\x00o\x00w\x00_\x00u\x00p\x00r\x00i\x00g\x00h\x00t\x00.\x00c\x00u\x00r\ +\ +\x00\x0e\ +\x02\xc2\xa5\x82\ +\x00a\ +\x00r\x00r\x00_\x00a\x00d\x00d\x00k\x00e\x00y\x00.\x00c\x00u\x00r\ +\x00\x15\ +\x07\x0a7B\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00g\x00e\x00t\x00h\x00e\x00i\x00g\x00h\x00t\ +\x00.\x00c\x00u\x00r\ +\x00\x10\ +\x08\x83\x1e\x22\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00p\x00l\x00u\x00s\x00.\x00c\x00u\x00r\ +\x00\x11\ +\x0d\xbf%b\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00r\x00o\x00t\x00a\x00t\x00e\x00.\x00c\x00u\x00r\ +\ +\x00\x0f\ +\x07\xb5h\x82\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00m\x00o\x00v\x00e\x00.\x00c\x00u\x00r\ +\x00\x13\ +\x00.\xa8\x22\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00l\x00i\x00n\x00k\x00n\x00o\x00w\x00.\x00c\ +\x00u\x00r\ +\x00\x10\ +\x07\x0b\x1e\x82\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00l\x00i\x00n\x00k\x00.\x00c\x00u\x00r\ +\x00\x11\ +\x01\x93\x0c\x22\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00m\x00i\x00n\x00u\x00s\x00.\x00c\x00u\x00r\ +\ +\x00\x13\ +\x0aQ\xeab\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00D\x00r\x00a\x00g\x00I\x00t\x00e\x00m\x00.\x00c\ +\x00u\x00r\ +\x00\x0c\ +\x0b\xd0gb\ +\x00a\ +\x00r\x00r\x00o\x00w\x00_\x00u\x00p\x00.\x00c\x00u\x00r\ +\x00\x0f\ +\x0eO\xc8\x02\ +\x00p\ +\x00i\x00c\x00k\x00_\x00c\x00u\x00r\x00s\x00o\x00r\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x0el\xec\xa2\ +\x00c\ +\x00u\x00r\x000\x000\x000\x000\x001\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x0em\xec\xa2\ +\x00c\ +\x00u\x00r\x000\x000\x000\x000\x002\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x0en\xec\xa2\ +\x00c\ +\x00u\x00r\x000\x000\x000\x000\x003\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x05\xaa\xdb\xa2\ +\x00h\ +\x00a\x00n\x00d\x00D\x00r\x00a\x00g\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x0eo\xec\xa2\ +\x00c\ +\x00u\x00r\x000\x000\x000\x000\x004\x00.\x00c\x00u\x00r\ +\x00\x10\ +\x03y\xfd\xc2\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00s\x00c\x00a\x00l\x00e\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x0ep\xec\xa2\ +\x00c\ +\x00u\x00r\x000\x000\x000\x000\x005\x00.\x00c\x00u\x00r\ +\x00\x0c\ +\x02\xaeA\x82\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00.\x00c\x00u\x00r\ +\x00\x15\ +\x0eb\xe8\x22\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00s\x00o\x00_\x00s\x00e\x00l\x00e\x00c\x00t\ +\x00.\x00c\x00u\x00r\ +\x00\x13\ +\x096M\x02\ +\x00a\ +\x00r\x00r\x00o\x00w\x00_\x00d\x00o\x00w\x00n\x00r\x00i\x00g\x00h\x00t\x00.\x00c\ +\x00u\x00r\ +\x00\x13\ +\x0f\xbas\x02\ +\x00p\ +\x00o\x00i\x00n\x00t\x00e\x00r\x00_\x00f\x00l\x00a\x00t\x00t\x00e\x00n\x00.\x00c\ +\x00u\x00r\ +\x00\x0c\ +\x0fy\xb7\xc7\ +\x00m\ +\x00a\x00x\x00i\x00m\x00i\x00z\x00e\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x0a5o'\ +\x00h\ +\x00i\x00d\x00e\x00_\x00h\x00e\x00l\x00p\x00e\x00r\x00s\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x0a\x9d~\xc7\ +\x00d\ +\x00i\x00s\x00p\x00l\x00a\x00y\x00_\x00i\x00n\x00f\x00o\x00.\x00p\x00n\x00g\ +\x00\x17\ +\x04\xb0\xfe\xc7\ +\x00e\ +\x00d\x00i\x00t\x00w\x00i\x00t\x00h\x00b\x00u\x00t\x00t\x00o\x00n\x00_\x00d\x00a\ +\x00r\x00k\x00.\x00p\x00n\x00g\ +\x00\x08\ +\x06b\x87\xf3\ +\x00t\ +\x00o\x00o\x00l\x00b\x00a\x00r\x00s\ +\x00\x1d\ +\x08\xa8\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x005\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x98q\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x004\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x000\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x004\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00N\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x006\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xd5\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x002\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xa7\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x004\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x005\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x003\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00C\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x005\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xd4\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x001\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\xecq\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x001\x000\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00$\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x002\x000\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xa6\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x003\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x9aq\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x002\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x002\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x002\x00.\x00p\x00n\x00g\ +\x00\x1e\ +\x076,\x87\ +\x00p\ +\x00r\x00o\x00c\x00e\x00d\x00u\x00r\x00a\x00l\x00m\x00a\x00t\x00e\x00r\x00i\x00a\ +\x00l\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xd3\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x000\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xa5\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x002\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x9fq\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x001\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x007\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x001\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00E\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x003\x00.\x00p\x00n\x00g\ +\x00\x13\ +\x0c\xd0\x1e\x87\ +\x00m\ +\x00i\x00s\x00c\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x000\x00.\x00p\ +\x00n\x00g\ +\x00\x1d\ +\x08\xa4\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x001\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x9cq\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x000\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x004\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x000\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xdb\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x008\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00B\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x002\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x97q\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x009\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00?\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x009\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x0f\xf1A\xe7\ +\x00O\ +\x00b\x00j\x00S\x00e\x00l\x00e\x00c\x00t\x00i\x00o\x00n\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xa3\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x000\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xda\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x007\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00G\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x001\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xac\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x009\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x94q\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x008\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00<\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x008\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xd9\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x006\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00#\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x002\x005\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00D\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x000\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xab\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x008\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x99q\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x007\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00A\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x007\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00O\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x009\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xd8\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x005\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00 \xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x002\x004\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xaa\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x007\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x96q\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x006\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00>\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x006\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00L\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x008\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x9d|'\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x003\ +\x00.\x00s\x00v\x00g\ +\x00\x1d\ +\x08\xd7\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x004\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00%\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x002\x003\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xa9\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x000\x006\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x0e\x9bq\xa7\ +\x00o\ +\x00b\x00j\x00e\x00c\x00t\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00-\x000\x005\ +\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x003\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x001\x005\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00Q\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x000\x007\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x08\xd6\xaf\xe7\ +\x00s\ +\x00t\x00a\x00n\x00d\x00a\x00r\x00d\x00_\x00v\x00i\x00e\x00w\x00s\x00_\x00t\x00o\ +\x00o\x00l\x00b\x00a\x00r\x00-\x001\x003\x00.\x00p\x00n\x00g\ +\x00\x18\ +\x00\x22\xce\x87\ +\x00e\ +\x00d\x00i\x00t\x00_\x00m\x00o\x00d\x00e\x00_\x00t\x00o\x00o\x00l\x00b\x00a\x00r\ +\x00-\x002\x002\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x0c\x99\xf5\x1f\ +\x00b\ +\x00a\x00l\x00l\x00_\x00o\x00f\x00f\x00l\x00i\x00n\x00e\x00.\x00i\x00c\x00o\ +\x00\x1c\ +\x0d\xbb\x8bG\ +\x00s\ +\x00o\x00u\x00r\x00c\x00e\x00_\x00c\x00o\x00n\x00t\x00r\x00o\x00l\x00_\x00c\x00o\ +\x00n\x00n\x00e\x00c\x00t\x00e\x00d\x00.\x00s\x00v\x00g\ +\x00\x1c\ +\x03ZJ\xe7\ +\x00s\ +\x00o\x00u\x00r\x00c\x00e\x00_\x00c\x00o\x00n\x00t\x00r\x00o\x00l\x00-\x00n\x00o\ +\x00t\x00_\x00s\x00e\x00t\x00u\x00p\x00.\x00s\x00v\x00g\ +\x00\x0f\ +\x0a\x0d'\xdf\ +\x00b\ +\x00a\x00l\x00l\x00_\x00o\x00n\x00l\x00i\x00n\x00e\x00.\x00i\x00c\x00o\ +\x00\x1d\ +\x03\xe3zG\ +\x00s\ +\x00o\x00u\x00r\x00c\x00e\x00_\x00c\x00o\x00n\x00t\x00r\x00o\x00l\x00-\x00w\x00a\ +\x00r\x00n\x00i\x00n\x00g\x00_\x00v\x002\x00.\x00s\x00v\x00g\ +\x00\x10\ +\x0c\xa1\xe6\x1f\ +\x00b\ +\x00a\x00l\x00l\x00_\x00p\x00e\x00n\x00d\x00i\x00n\x00g\x00.\x00i\x00c\x00o\ +\x00\x11\ +\x08tl?\ +\x00b\ +\x00a\x00l\x00l\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00i\x00c\x00o\ +\ +\x00\x1b\ +\x00\x22\x12'\ +\x00s\ +\x00o\x00u\x00r\x00c\x00e\x00_\x00c\x00o\x00n\x00t\x00r\x00o\x00l\x00_\x00e\x00r\ +\x00r\x00o\x00r\x00_\x00v\x002\x00.\x00s\x00v\x00g\ +" + +qt_resource_struct = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00)\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x03\xa8\x00\x02\x00\x00\x00\x0c\x00\x00\x00\xc5\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x03\xe2\x00\x02\x00\x00\x002\x00\x00\x00\x93\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x01F\x00\x00\x00\x00\x00\x01\x00\x00\xb19\ +\x00\x00\x01y+\x8f\x93\xf2\ +\x00\x00\x01\xb2\x00\x02\x00\x00\x00\x01\x00\x00\x00Y\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x03\x84\x00\x00\x00\x00\x00\x01\x00\x00\xc6s\ +\x00\x00\x01x\xc7F\xf7\x89\ +\x00\x00\x00\xfe\x00\x00\x00\x00\x00\x01\x00\x00\xad\x12\ +\x00\x00\x01x\xc7F\xf7\xee\ +\x00\x00\x04\xc0\x00\x00\x00\x00\x00\x01\x00\x00\xceG\ +\x00\x00\x01x\xc7F\xf7\xd7\ +\x00\x00\x02p\x00\x00\x00\x00\x00\x01\x00\x00\xbec\ +\x00\x00\x01x\xc7F\xf8\x08\ +\x00\x00\x03\x0c\x00\x00\x00\x00\x00\x01\x00\x00\xc3\xef\ +\x00\x00\x01x\xc7F\xf8\x04\ +\x00\x00\x00$\x00\x00\x00\x00\x00\x01\x00\x00\x01\x88\ +\x00\x00\x01x\xc7F\xf7\x88\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01x\xc7F\xf7\xd9\ +\x00\x00\x03`\x00\x00\x00\x00\x00\x01\x00\x00\xc5\xe4\ +\x00\x00\x01x\xc7F\xf85\ +\x00\x00\x04\x22\x00\x00\x00\x00\x00\x01\x00\x00\xca\x80\ +\x00\x00\x01x\xc7F\xf7\xa9\ +\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x00\xba{\ +\x00\x00\x01x\xc7F\xf7\xed\ +\x00\x00\x05f\x00\x00\x00\x00\x00\x01\x00\x00\xd6\xb3\ +\x00\x00\x01x\xc7F\xf7\x85\ +\x00\x00\x02\xe8\x00\x00\x00\x00\x00\x01\x00\x00\xc3E\ +\x00\x00\x01x\xc7F\xf88\ +\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x00\xae\x8b\ +\x00\x00\x01x\xc7F\xf7\x81\ +\x00\x00\x02\x94\x00\x00\x00\x00\x00\x01\x00\x00\xbfR\ +\x00\x00\x01x\xc7F\xf7|\ +\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x00\xd9\x9f\ +\x00\x00\x01x\xc7F\xf6\xe8\ +\x00\x00\x00\x90\x00\x00\x00\x00\x00\x01\x00\x00\xa6\x97\ +\x00\x00\x01x\xc7F\xf7}\ +\x00\x00\x02\x08\x00\x00\x00\x00\x00\x01\x00\x00\xbb\x91\ +\x00\x00\x01x\xc7F\xf7\x82\ +\x00\x00\x05\x12\x00\x00\x00\x00\x00\x01\x00\x00\xd0\xa7\ +\x00\x00\x01x\xc7F\xf6\xeb\ +\x00\x00\x02\xb8\x00\x00\x00\x00\x00\x01\x00\x00\xc2a\ +\x00\x00\x01x\xc7F\xf8\x07\ +\x00\x00\x02@\x00\x00\x00\x00\x00\x01\x00\x00\xbd\x8a\ +\x00\x00\x01x\xc7F\xf8\x13\ +\x00\x00\x00`\x00\x00\x00\x00\x00\x01\x00\x00\xa5\xc3\ +\x00\x00\x01x\xc7F\xf8\x17\ +\x00\x00\x056\x00\x00\x00\x00\x00\x01\x00\x00\xd5\xb3\ +\x00\x00\x01x\xc7F\xf7\xf5\ +\x00\x00\x05\xdc\x00\x00\x00\x00\x00\x01\x00\x00\xdd\xf0\ +\x00\x00\x01x\xc7F\xf8&\ +\x00\x00\x03\xf2\x00\x00\x00\x00\x00\x01\x00\x00\xc9M\ +\x00\x00\x01x\xc7F\xf7\xf1\ +\x00\x00\x04t\x00\x00\x00\x00\x00\x01\x00\x00\xcd>\ +\x00\x00\x01x\xc7F\xf7\xf2\ +\x00\x00\x030\x00\x00\x00\x00\x00\x01\x00\x00\xc4\xe2\ +\x00\x00\x01x\xc7F\xf7\xf3\ +\x00\x00\x04F\x00\x00\x00\x00\x00\x01\x00\x00\xccj\ +\x00\x00\x01x\xc7F\xf8\x11\ +\x00\x00\x03\xb4\x00\x00\x00\x00\x00\x01\x00\x00\xc8y\ +\x00\x00\x01x\xc7F\xf8\x12\ +\x00\x00\x05\x8a\x00\x00\x00\x00\x00\x01\x00\x00\xd8\xcb\ +\x00\x00\x01x\xc7F\xf8\x15\ +\x00\x00\x04\xe4\x00\x00\x00\x00\x00\x01\x00\x00\xcf\xd3\ +\x00\x00\x01x\xc7F\xf8\x16\ +\x00\x00\x00\xe6\x00\x02\x00\x00\x00\x01\x00\x00\x00P\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x03\xf4\ +\x00\x00\x01x\xc7G\x01\xc4\ +\x00\x00\x02,\x00\x02\x00\x00\x00\x01\x00\x00\x000\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x01|\x00\x00\x00\x00\x00\x01\x00\x00\xb3\xeb\ +\x00\x00\x01y+\x8f\x93\xef\ +\x00\x00\x01\xf0\x00\x02\x00\x00\x00\x04\x00\x00\x00,\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\xb4\x00\x00\x00\x00\x00\x01\x00\x00\xa9\x0c\ +\x00\x00\x01y+\x8f\x93\xf0\ +\x00\x00\x04\xa4\x00\x02\x00\x00\x00\x01\x00\x00\x00*\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x03\xa8\x00\x02\x00\x00\x00\x01\x00\x00\x00+\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x06\x0c\x00\x01\x00\x00\x00\x01\x00\x00\xde\xc4\ +\x00\x00\x01y\x1f\xa4\xb3Q\ +\x00\x00\x16\x86\x00\x00\x00\x00\x00\x01\x00\x05\x97+\ +\x00\x00\x01x\xc7F\xed\xa9\ +\x00\x00\x16:\x00\x00\x00\x00\x00\x01\x00\x05\x95\x7f\ +\x00\x00\x01x\xc7F\xf0\x97\ +\x00\x00\x16`\x00\x00\x00\x00\x00\x01\x00\x05\x96S\ +\x00\x00\x01x\xc7F\xeb4\ +\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x94\x8c\ +\x00\x00\x01x\xc7F\xf0\x87\ +\x00\x00\x03\xa8\x00\x02\x00\x00\x00\x1f\x00\x00\x001\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x13\xb4\x00\x00\x00\x00\x00\x01\x00\x05\x80\xf9\ +\x00\x00\x01x\xc7D\x85\xca\ +\x00\x00\x14\x06\x00\x00\x00\x00\x00\x01\x00\x05\x83\x8d\ +\x00\x00\x01x\xc7D\x85\xcc\ +\x00\x00\x15v\x00\x00\x00\x00\x00\x01\x00\x05\x90T\ +\x00\x00\x01x\xc7D\x85\xc3\ +\x00\x00\x12\xf0\x00\x00\x00\x00\x00\x01\x00\x05z\x87\ +\x00\x00\x01x\xc7D\x83\xc5\ +\x00\x00\x12|\x00\x00\x00\x00\x00\x01\x00\x05w\x97\ +\x00\x00\x01x\xc7D\x85\xbb\ +\x00\x00\x152\x00\x00\x00\x00\x00\x01\x00\x05\x8d\xc0\ +\x00\x00\x01x\xc7D\x85e\ +\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05x\xe1\ +\x00\x00\x01x\xc7D\x85\xd0\ +\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\x8b,\ +\x00\x00\x01x\xc7D\x84\xb4\ +\x00\x00\x11\xce\x00\x01\x00\x00\x00\x01\x00\x05s\x95\ +\x00\x00\x01x\xc7D\x83\xca\ +\x00\x00\x12(\x00\x00\x00\x00\x00\x01\x00\x05u\xe9\ +\x00\x00\x01x\xc7D\x85\xd2\ +\x00\x00\x12\x0c\x00\x01\x00\x00\x00\x01\x00\x05u>\ +\x00\x00\x01x\xc7D\x84\x12\ +\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05s\xf4\ +\x00\x00\x01x\xc7D\x84\x12\ +\x00\x00\x12\xc8\x00\x01\x00\x00\x00\x01\x00\x05z+\ +\x00\x00\x01x\xc7D\x83\xcf\ +\x00\x00\x13\x12\x00\x00\x00\x00\x00\x01\x00\x05{\xd1\ +\x00\x00\x01x\xc7D\x85\xc7\ +\x00\x00\x13\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x82C\ +\x00\x00\x01x\xc7D\x85\xc8\ +\x00\x00\x13\x90\x00\x00\x00\x00\x00\x01\x00\x05\x7f\xaf\ +\x00\x00\x01x\xc7D\x85d\ +\x00\x00\x13B\x00\x00\x00\x00\x00\x01\x00\x05}\x1b\ +\x00\x00\x01x\xc7D\x85\xce\ +\x00\x00\x12\x5c\x00\x01\x00\x00\x00\x01\x00\x05w3\ +\x00\x00\x01x\xc7D\x85-\ +\x00\x00\x15\xc4\x00\x01\x00\x00\x00\x01\x00\x05\x92\xe8\ +\x00\x00\x01x\xc7D\x83\xcb\ +\x00\x00\x14.\x00\x01\x00\x00\x00\x01\x00\x05\x84\xd7\ +\x00\x00\x01x\xc7D\x85\xb9\ +\x00\x00\x14Z\x00\x01\x00\x00\x00\x01\x00\x05\x85\xa4\ +\x00\x00\x01x\xc7D\x83\xcc\ +\x00\x00\x13h\x00\x00\x00\x00\x00\x01\x00\x05~e\ +\x00\x00\x01x\xc7D\x85d\ +\x00\x00\x14x\x00\x00\x00\x00\x00\x01\x00\x05\x86\x04\ +\x00\x00\x01x\xc7D\x85\xb6\ +\x00\x00\x15\x94\x00\x00\x00\x00\x00\x01\x00\x05\x91\x9e\ +\x00\x00\x01x\xc7D\x85\xd4\ +\x00\x00\x14\x9c\x00\x00\x00\x00\x00\x01\x00\x05\x87N\ +\x00\x00\x01x\xc7D\x84\x0b\ +\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\x88\x98\ +\x00\x00\x01x\xc7D\x84\x0d\ +\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\x89\xe2\ +\x00\x00\x01x\xc7D\x84\x0e\ +\x00\x00\x15\x14\x00\x00\x00\x00\x00\x01\x00\x05\x8cv\ +\x00\x00\x01x\xc7D\x84\x0e\ +\x00\x00\x15X\x00\x00\x00\x00\x00\x01\x00\x05\x8f\x0a\ +\x00\x00\x01x\xc7D\x84\x0f\ +\x00\x00\x11\xba\x00\x01\x00\x00\x00\x01\x00\x05s6\ +\x00\x00\x01x\xc7D\x84\xb5\ +\x00\x00\x15\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x93B\ +\x00\x00\x01x\xc7D\x85\xc5\ +\x00\x00\x03\xa8\x00\x02\x00\x00\x00\x08\x00\x00\x00Q\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00$\x98\x00\x00\x00\x00\x00\x01\x00\x06+\x0e\ +\x00\x00\x01y+\x8f\x94\x0d\ +\x00\x00#\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x16\xea\ +\x00\x00\x01y+\x8f\x94\x0b\ +\x00\x00$\x0a\x00\x00\x00\x00\x00\x01\x00\x06\x1e\xba\ +\x00\x00\x01y+\x8f\x94\x0b\ +\x00\x00$p\x00\x00\x00\x00\x00\x01\x00\x06&\x8c\ +\x00\x00\x01x\xc7F\xf7d\ +\x00\x00#\xe6\x00\x00\x00\x00\x00\x01\x00\x06\x1a8\ +\x00\x00\x01x\xc7F\xf6\xff\ +\x00\x00#D\x00\x00\x00\x00\x00\x01\x00\x06\x0f\x1e\ +\x00\x00\x01x\xc7F\xf6\xed\ +\x00\x00$J\x00\x00\x00\x00\x00\x01\x00\x06\x22\x0a\ +\x00\x00\x01x\xc7F\xf6\xf0\ +\x00\x00#j\x00\x00\x00\x00\x00\x01\x00\x06\x13\xa0\ +\x00\x00\x01y+\x8f\x94\x0c\ +\x00\x00\x16\xba\x00\x02\x00\x00\x009\x00\x00\x00Z\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00 :\x00\x00\x00\x00\x00\x01\x00\x05\xf1\xf4\ +\x00\x00\x01x\xc7F\xf0|\ +\x00\x00#\x0e\x00\x00\x00\x00\x00\x01\x00\x06\x0a1\ +\x00\x00\x01x\xc7F\xed\xa1\ +\x00\x00\x1e\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xe2\xb6\ +\x00\x00\x01x\xc7F\xef\xf0\ +\x00\x00\x19\x08\x00\x00\x00\x00\x00\x01\x00\x05\xaf\x1d\ +\x00\x00\x01x\xc7F\xf0\xa1\ +\x00\x00!\xbc\x00\x00\x00\x00\x00\x01\x00\x05\xff\xfd\ +\x00\x00\x01x\xc7F\xf0W\ +\x00\x00\x17@\x00\x00\x00\x00\x00\x01\x00\x05\x9d\xdd\ +\x00\x00\x01x\xc7F\xed\xd6\ +\x00\x00\x19\xae\x00\x00\x00\x00\x00\x01\x00\x05\xb2\xec\ +\x00\x00\x01x\xc7F\xe9{\ +\x00\x00\x22b\x00\x00\x00\x00\x00\x01\x00\x06\x04c\ +\x00\x00\x01x\xc7F\xef\xab\ +\x00\x00\x1b\xde\x00\x00\x00\x00\x00\x01\x00\x05\xc5\xad\ +\x00\x00\x01x\xc7F\xcb~\ +\x00\x00\x18,\x00\x00\x00\x00\x00\x01\x00\x05\xa5\x22\ +\x00\x00\x01x\xc7F\xf0\x90\ +\x00\x00\x1a\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xba\xbf\ +\x00\x00\x01x\xc7F\xe9@\ +\x00\x00\x1e<\x00\x00\x00\x00\x00\x01\x00\x05\xdcu\ +\x00\x00\x01x\xc7F\xf0=\ +\x00\x00 \xe0\x00\x00\x00\x00\x00\x01\x00\x05\xf6-\ +\x00\x00\x01x\xc7F\xf0\x03\ +\x00\x00\x1c\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd0B\ +\x00\x00\x01x\xc7F\xed\xd8\ +\x00\x00\x1f\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xec\xea\ +\x00\x00\x01x\xc7F\xed\xda\ +\x00\x00\x1cT\x00\x00\x00\x00\x00\x01\x00\x05\xcb\x82\ +\x00\x00\x01x\xc7F\xcc\x09\ +\x00\x00\x18b\x00\x00\x00\x00\x00\x01\x00\x05\xa6\x14\ +\x00\x00\x01x\xc7F\xe9f\ +\x00\x00\x1e\xe8\x00\x00\x00\x00\x00\x01\x00\x05\xe4c\ +\x00\x00\x01x\xc7F\xe9\x86\ +\x00\x00\x1b\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xbc\x0d\ +\x00\x00\x01x\xc7F\xcb\xfe\ +\x00\x00\x1d\x96\x00\x00\x00\x00\x00\x01\x00\x05\xd7\x13\ +\x00\x00\x01x\xc7F\xe9\x82\ +\x00\x00!\x16\x00\x00\x00\x00\x00\x01\x00\x05\xf7\xeb\ +\x00\x00\x01x\xc7F\xe7N\ +\x00\x00\x17v\x00\x00\x00\x00\x00\x01\x00\x05\x9f\xff\ +\x00\x00\x01x\xc7F\xe97\ +\x00\x00\x1f\xc4\x00\x00\x00\x00\x00\x01\x00\x05\xee\xfa\ +\x00\x00\x01x\xc7F\xe7\xa5\ +\x00\x00\x22\x98\x00\x00\x00\x00\x00\x01\x00\x06\x06c\ +\x00\x00\x01x\xc7F\xe6\xb8\ +\x00\x00\x19\xe4\x00\x00\x00\x00\x00\x01\x00\x05\xb4\x0b\ +\x00\x00\x01x\xc7F\xef\xeb\ +\x00\x00\x1d\x16\x00\x00\x00\x00\x00\x01\x00\x05\xd3\xe3\ +\x00\x00\x01x\xc7F\xef\xe2\ +\x00\x00\x1bn\x00\x00\x00\x00\x00\x01\x00\x05\xc1)\ +\x00\x00\x01x\xc7F\xef\x93\ +\x00\x00\x1af\x00\x00\x00\x00\x00\x01\x00\x05\xb7h\ +\x00\x00\x01x\xc7F\xef\xee\ +\x00\x00\x19>\x00\x00\x00\x00\x00\x01\x00\x05\xaf\xef\ +\x00\x00\x01x\xc7F\xf00\ +\x00\x00\x17\xec\x00\x00\x00\x00\x00\x01\x00\x05\xa2\xf2\ +\x00\x00\x01x\xc7F\xed\xd3\ +\x00\x00\x16\xd0\x00\x00\x00\x00\x00\x01\x00\x05\x9b\x19\ +\x00\x00\x01x\xc7F\xef\xe6\ +\x00\x00!\xf2\x00\x00\x00\x00\x00\x01\x00\x06\x01P\ +\x00\x00\x01x\xc7F\xf0f\ +\x00\x00 p\x00\x00\x00\x00\x00\x01\x00\x05\xf2\xfb\ +\x00\x00\x01x\xc7F\xf0k\ +\x00\x00\x1f\x1e\x00\x00\x00\x00\x00\x01\x00\x05\xe5k\ +\x00\x00\x01x\xc7F\xed\xc2\ +\x00\x00\x1d\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xd8!\ +\x00\x00\x01x\xc7F\xf0\x0e\ +\x00\x00\x1a&\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xde\ +\x00\x00\x01x\xc7F\xf0%\ +\x00\x00\x18\x98\x00\x00\x00\x00\x00\x01\x00\x05\xa7M\ +\x00\x00\x01x\xc7F\xf0I\ +\x00\x00\x17\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa1s\ +\x00\x00\x01x\xc7F\xf0,\ +\x00\x00\x22\xce\x00\x00\x00\x00\x00\x01\x00\x06\x08\x1f\ +\x00\x00\x01x\xc7F\xed\xdd\ +\x00\x00!|\x00\x00\x00\x00\x00\x01\x00\x05\xfe\x91\ +\x00\x00\x01x\xc7F\xf04\ +\x00\x00\x1f\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xf0{\ +\x00\x00\x01x\xc7F\xf0*\ +\x00\x00\x1er\x00\x00\x00\x00\x00\x01\x00\x05\xdd\xd6\ +\x00\x00\x01x\xc7F\xed\xc6\ +\x00\x00\x1dV\x00\x00\x00\x00\x00\x01\x00\x05\xd5\xb7\ +\x00\x00\x01x\xc7F\xf0@\ +\x00\x00\x1c\x14\x00\x00\x00\x00\x00\x01\x00\x05\xca)\ +\x00\x00\x01x\xc7F\xf0K\ +\x00\x00\x1bB\x00\x00\x00\x00\x00\x01\x00\x05\xbe&\ +\x00\x00\x01x\xc7F\xed\xcb\ +\x00\x00\x1e\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xd9\xa3\ +\x00\x00\x01x\xc7F\xed\xc8\ +\x00\x00 \xb0\x00\x00\x00\x00\x00\x01\x00\x05\xf4(\ +\x00\x00\x01x\xc7F\xef\xa6\ +\x00\x00\x1c\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x7f\ +\x00\x00\x01x\xc7F\xed\xce\ +\x00\x00\x17\x10\x00\x00\x00\x00\x00\x01\x00\x05\x9c\xd8\ +\x00\x00\x01x\xc7F\xf0\x80\ +\x00\x00\x1f^\x00\x00\x00\x00\x00\x01\x00\x05\xeb\x8c\ +\x00\x00\x01x\xc7F\xf0:\ +\x00\x00\x19~\x00\x00\x00\x00\x00\x01\x00\x05\xb1p\ +\x00\x00\x01x\xc7F\xf0(\ +\x00\x00\x222\x00\x00\x00\x00\x00\x01\x00\x06\x02\x83\ +\x00\x00\x01x\xc7F\xef\xdd\ +\x00\x00\x1b\xae\x00\x00\x00\x00\x00\x01\x00\x05\xc3\x18\ +\x00\x00\x01x\xc7F\xed\xd1\ +\x00\x00!L\x00\x00\x00\x00\x00\x01\x00\x05\xf9\xa7\ +\x00\x00\x01y+\x8f\x93{\ +\x00\x00\x1a\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xb92\ +\x00\x00\x01x\xc7F\xf0\x0c\ +\x00\x00\x18\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa5\ +\x00\x00\x01x\xc7F\xed\xa4\ +\x00\x00\x1c\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xd2\x83\ +\x00\x00\x01x\xc7F\xe9<\ +\x00\x00\x0e\xba\x00\x00\x00\x00\x00\x01\x00\x04(&\ +\x00\x00\x01y+\x8f\x94\x12\ +\x00\x00\x0d\x0a\x00\x00\x00\x00\x00\x01\x00\x03\x91\x87\ +\x00\x00\x01y+\x8f\x93\xdc\ +\x00\x00\x0e\x80\x00\x00\x00\x00\x00\x01\x00\x04#\x0e\ +\x00\x00\x01y+\x8f\x94\x08\ +\x00\x00\x0d~\x00\x00\x00\x00\x00\x01\x00\x03\xf4\x82\ +\x00\x00\x01y+\x8f\x93\xcd\ +\x00\x00\x09\xf6\x00\x00\x00\x00\x00\x01\x00\x032\x17\ +\x00\x00\x01y+\x8f\x94\x10\ +\x00\x00\x09f\x00\x01\x00\x00\x00\x01\x00\x02i(\ +\x00\x00\x01x\xc7F\xf5\xfe\ +\x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x01\x87\xf3\ +\x00\x00\x01y+\x8f\x93\xce\ +\x00\x00\x08:\x00\x00\x00\x00\x00\x01\x00\x01\xa3\x1b\ +\x00\x00\x01y+\x8f\x94\x0a\ +\x00\x00\x0f$\x00\x00\x00\x00\x00\x01\x00\x04jD\ +\x00\x00\x01x\xc7F\xf6G\ +\x00\x00\x0e\x10\x00\x00\x00\x00\x00\x01\x00\x04\x15\xa7\ +\x00\x00\x01y+\x8f\x93\xe3\ +\x00\x00\x08\xd2\x00\x00\x00\x00\x00\x01\x00\x02^f\ +\x00\x00\x01y+\x8f\x93\xd6\ +\x00\x00\x0eL\x00\x00\x00\x00\x00\x01\x00\x04\x1d\xe2\ +\x00\x00\x01y+\x8f\x94\x09\ +\x00\x00\x09*\x00\x00\x00\x00\x00\x01\x00\x02f\x89\ +\x00\x00\x01y+\x8f\x94\x07\ +\x00\x00\x0c \x00\x00\x00\x00\x00\x01\x00\x03u!\ +\x00\x00\x01y+\x8f\x93\xd5\ +\x00\x00\x07z\x00\x00\x00\x00\x00\x01\x00\x01\x96/\ +\x00\x00\x01y+\x8f\x93\xdf\ +\x00\x00\x08x\x00\x00\x00\x00\x00\x01\x00\x01\xbe\xd0\ +\x00\x00\x01x\xc7F\xf6*\ +\x00\x00\x0f\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x87Q\ +\x00\x00\x01y+\x8f\x94\x12\ +\x00\x00\x08T\x00\x00\x00\x00\x00\x01\x00\x01\xa9(\ +\x00\x00\x01x\xc7F\xf61\ +\x00\x00\x0cb\x00\x00\x00\x00\x00\x01\x00\x03\x81c\ +\x00\x00\x01y+\x8f\x93\xda\ +\x00\x00\x07B\x00\x00\x00\x00\x00\x01\x00\x01\x8c\x97\ +\x00\x00\x01y+\x8f\x94\x11\ +\x00\x00\x08\xf8\x00\x00\x00\x00\x00\x01\x00\x02b\x14\ +\x00\x00\x01y+\x8f\x93\xce\ +\x00\x00\x0a\xc0\x00\x00\x00\x00\x00\x01\x00\x03N\xe6\ +\x00\x00\x01y+\x8f\x93\xd8\ +\x00\x00\x09\xc8\x00\x01\x00\x00\x00\x01\x00\x03\x12$\ +\x00\x00\x01x\xc7F\xf5\xf8\ +\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x01|\xed\ +\x00\x00\x01y+\x8f\x93\xcc\ +\x00\x00\x0a\xf8\x00\x00\x00\x00\x00\x01\x00\x03R\x98\ +\x00\x00\x01y+\x8f\x93\xdb\ +\x00\x00\x0d\x98\x00\x00\x00\x00\x00\x01\x00\x03\xf8 \ +\x00\x00\x01y+\x8f\x93\xdc\ +\x00\x00\x0c<\x00\x00\x00\x00\x00\x01\x00\x03|B\ +\x00\x00\x01y+\x8f\x93\xde\ +\x00\x00\x0bZ\x00\x00\x00\x00\x00\x01\x00\x03W)\ +\x00\x00\x01y+\x8f\x93\xd8\ +\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x039\x16\ +\x00\x00\x01x\xc7F\xf60\ +\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x01\x00\x04`\xcc\ +\x00\x00\x01y+\x8f\x94\x13\ +\x00\x00\x0fN\x00\x00\x00\x00\x00\x01\x00\x04\x7f\x0c\ +\x00\x00\x01y+\x8f\x93\xe2\ +\x00\x00\x0dD\x00\x00\x00\x00\x00\x01\x00\x03\x95\x12\ +\x00\x00\x01x\xc7F\xf6\x18\ +\x00\x00\x0a0\x00\x00\x00\x00\x00\x01\x00\x034\xb6\ +\x00\x00\x01y+\x8f\x93\xd9\ +\x00\x00\x060\x00\x00\x00\x00\x00\x01\x00\x01\x1a\xc3\ +\x00\x00\x01x\xc7F\xf6\x1b\ +\x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03p\xae\ +\x00\x00\x01y+\x8f\x93\xd7\ +\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x84\xba\ +\x00\x00\x01y+\x8f\x93\xfd\ +\x00\x00\x07\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x9bR\ +\x00\x00\x01y+\x8f\x94\x0f\ +\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\x90\xf8\ +\x00\x00\x01y+\x8f\x93\xcb\ +\x00\x00\x0e\xd0\x00\x01\x00\x00\x00\x01\x00\x04/\x99\ +\x00\x00\x01x\xc7F\xf5\xf3\ +\x00\x00\x06^\x00\x00\x00\x00\x00\x01\x00\x01zK\ +\x00\x00\x01y+\x8f\x94\x06\ +\x00\x00\x09\xb4\x00\x00\x00\x00\x00\x01\x00\x03\x0c\xa3\ +\x00\x00\x01y+\x8f\x93\xd0\ +\x00\x00\x0f\xf8\x00\x00\x00\x00\x00\x01\x00\x04\x93\xb2\ +\x00\x00\x01x\xc7F\xf5\xf0\ +\x00\x00\x09\x86\x00\x00\x00\x00\x00\x01\x00\x02\x8c\x97\ +\x00\x00\x01x\xc7F\xf6$\ +\x00\x00\x0d\xe4\x00\x01\x00\x00\x00\x01\x00\x03\xfb\xab\ +\x00\x00\x01x\xc7F\xf6\x04\ +\x00\x00\x0c\xec\x00\x00\x00\x00\x00\x01\x00\x03\x8a\xa7\ +\x00\x00\x01y+\x8f\x94\x0e\ +\x00\x00\x0b\xaa\x00\x00\x00\x00\x00\x01\x00\x03[\xa0\ +\x00\x00\x01x\xc7F\xf6L\ +\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x7f\x95\ +\x00\x00\x01y+\x8f\x93\xe0\ +\x00\x00\x0c\xb0\x00\x00\x00\x00\x00\x01\x00\x03\x86\x07\ +\x00\x00\x01y+\x8f\x93\xdd\ +\x00\x00\x07\xea\x00\x00\x00\x00\x00\x01\x00\x01\x9d\xf4\ +\x00\x00\x01y+\x8f\x93\xe1\ +\x00\x00\x08\xb4\x00\x00\x00\x00\x00\x01\x00\x02H\xcc\ +\x00\x00\x01x\xc7F\xf6D\ +\x00\x00\x11\x06\x00\x00\x00\x00\x00\x01\x00\x05@\xf3\ +\x00\x00\x01y+\x8f\x93\xd4\ +\x00\x00\x11\x88\x00\x00\x00\x00\x00\x01\x00\x05k\xae\ +\x00\x00\x01y+\x8f\x93\xd1\ +\x00\x00\x11b\x00\x00\x00\x00\x00\x01\x00\x05Yl\ +\x00\x00\x01y+\x8f\x93\xcf\ +\x00\x00\x11\xa0\x00\x00\x00\x00\x00\x01\x00\x05o\xc9\ +\x00\x00\x01y+\x8f\x93\xd2\ +\x00\x00\x10\x94\x00\x00\x00\x00\x00\x01\x00\x059\xf7\ +\x00\x00\x01x\xc7F\xf8/\ +\x00\x00\x106\x00\x00\x00\x00\x00\x01\x00\x05!\xc2\ +\x00\x00\x01x\xc7F\xf8,\ +\x00\x00\x11 \x00\x00\x00\x00\x00\x01\x00\x05F\xfd\ +\x00\x00\x01x\xc7F\xf83\ +\x00\x00\x10\xdc\x00\x00\x00\x00\x00\x01\x00\x05@%\ +\x00\x00\x01x\xc7F\xf8(\ +\x00\x00\x10`\x00\x00\x00\x00\x00\x01\x00\x05\x22O\ +\x00\x00\x01y+\x8f\x93\xd3\ +\x00\x00\x11J\x00\x00\x00\x00\x00\x01\x00\x05G\xa1\ +\x00\x00\x01y+\x8f\x93\xca\ +\x00\x00\x10z\x00\x00\x00\x00\x00\x01\x00\x0509\ +\x00\x00\x01y+\x8f\x93\xc9\ +\x00\x00\x10\xbe\x00\x00\x00\x00\x00\x01\x00\x05:\xa6\ +\x00\x00\x01y+\x8f\x93\xe3\ +" + +def qInitResources(): + QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h index ab9e65b896..d67260de3e 100644 --- a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h +++ b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h @@ -152,8 +152,11 @@ struct AssetValidationTest { AZ::SettingsRegistry::Register(&m_registry); - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(m_registry); + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + m_registry.Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); + // Set the engine root to the temporary directory and re-update the runtime file paths auto enginePathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/engine_path"; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp index 04b015a66f..bf584c4d63 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp @@ -108,11 +108,11 @@ namespace ImageProcessingAtomEditor { readableString = "PC"; } - else if (platformStrLowerCase == "es3") + else if (platformStrLowerCase == "android") { readableString = "Android"; } - else if (platformStrLowerCase == "osx_gl") + else if (platformStrLowerCase == "mac") { readableString = "macOS"; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h index 28c1e78bb6..ef0e7f9619 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h @@ -12,7 +12,7 @@ #pragma once #define AZ_TRAIT_IMAGEPROCESSING_BESSEL_FUNCTION_FIRST_ORDER j1 -#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "osx_gl" +#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "mac" #define AZ_TRAIT_IMAGEPROCESSING_DEFINE_DIRECT3D_CONSTANTS 1 #define AZ_TRAIT_IMAGEPROCESSING_PVRTEXLIB_USE_WINDLL_IMPORT 0 #define AZ_TRAIT_IMAGEPROCESSING_SQUISH_DO_NOT_USE_FASTCALL 1 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h index 28c1e78bb6..ef0e7f9619 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h @@ -12,7 +12,7 @@ #pragma once #define AZ_TRAIT_IMAGEPROCESSING_BESSEL_FUNCTION_FIRST_ORDER j1 -#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "osx_gl" +#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "mac" #define AZ_TRAIT_IMAGEPROCESSING_DEFINE_DIRECT3D_CONSTANTS 1 #define AZ_TRAIT_IMAGEPROCESSING_PVRTEXLIB_USE_WINDLL_IMPORT 0 #define AZ_TRAIT_IMAGEPROCESSING_SQUISH_DO_NOT_USE_FASTCALL 1 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings index 0417122033..7bce3041b9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /bumptype=none /M=62,18,32,83,50,50 /preset=Diffuse_highQ /mipgentype=kaiser /reduce="es3:0,ios:3,osx_gl:0,pc:4,provo:1" /ser=0 +/autooptimizefile=0 /bumptype=none /M=62,18,32,83,50,50 /preset=Diffuse_highQ /mipgentype=kaiser /reduce="android:0,ios:3,mac:0,pc:4,provo:1" /ser=0 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset index 0b68493198..3a5122f18e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset @@ -25,7 +25,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", "Name": "Albedo", "RGB_Weight": "CIEXYZ", @@ -67,7 +67,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", "Name": "Albedo", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset index 3773857e0a..692ef99b1c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset @@ -23,7 +23,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}", "Name": "AlbedoWithCoverage", "RGB_Weight": "CIEXYZ", @@ -61,7 +61,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}", "Name": "AlbedoWithCoverage", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset index 530e36038d..4ebe773f0e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset @@ -23,7 +23,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}", "Name": "AlbedoWithGenericAlpha", "RGB_Weight": "CIEXYZ", @@ -61,7 +61,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}", "Name": "AlbedoWithGenericAlpha", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset index 6d6c156683..6049ef5bd4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset @@ -23,7 +23,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", "Name": "AlbedoWithOpacity", "RGB_Weight": "CIEXYZ", @@ -61,7 +61,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", "Name": "AlbedoWithOpacity", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset index 4e69ae67f2..56dec20f3e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset @@ -17,7 +17,7 @@ "PixelFormat": "BC4" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", "Name": "AmbientOcclusion", "SourceColor": "Linear", @@ -43,7 +43,7 @@ ], "PixelFormat": "EAC_R11" }, - "osx_gl": { + "mac": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", "Name": "AmbientOcclusion", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset index f37acd2f9d..2280a06302 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset @@ -11,7 +11,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", @@ -25,7 +25,7 @@ "PixelFormat": "EAC_R11", "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset index 46327e87ed..5f0480cee7 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset @@ -15,7 +15,7 @@ "IsColorChart": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", "Name": "ColorChart", "SourceColor": "Linear", @@ -37,7 +37,7 @@ "PixelFormat": "R8G8B8X8", "IsColorChart": true }, - "osx_gl": { + "mac": { "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", "Name": "ColorChart", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset index fe87f49426..abdf6501be 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset @@ -30,7 +30,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{2174E04B-73BB-4DF1-8961-4900DC3C9D72}", "Name": "ConvolvedCubemap", "SourceColor": "Linear", @@ -82,7 +82,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{2174E04B-73BB-4DF1-8961-4900DC3C9D72}", "Name": "ConvolvedCubemap", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset index 2c47f9eaed..f1e43e74b1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset @@ -18,7 +18,7 @@ "NumberResidentMips": 255 }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", "Name": "Decal_AlbedoWithOpacity", "FileMasks": [ @@ -46,7 +46,7 @@ // Decal Texture Arrays need all mips available immediately for packing. "NumberResidentMips": 255 }, - "osx_gl": { + "mac": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", "Name": "Decal_AlbedoWithOpacity", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset index 23ec2347cd..991692c5cc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset @@ -18,7 +18,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", "Name": "Detail_MergedAlbedoNormalsSmoothness", "SourceColor": "Linear", @@ -46,7 +46,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", "Name": "Detail_MergedAlbedoNormalsSmoothness", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset index 3145c5cf8a..fec11218e8 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset @@ -17,7 +17,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", "SourceColor": "Linear", @@ -43,7 +43,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset index 86ba9d74c0..520e4ae193 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset @@ -28,7 +28,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{D7B4BEA6-6427-4295-B61B-62776D0056DE}", "Name": "Displacement", "SourceColor": "Linear", @@ -77,7 +77,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{D7B4BEA6-6427-4295-B61B-62776D0056DE}", "Name": "Displacement", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset index 5a98d2cd30..ffb16482fd 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset @@ -18,7 +18,7 @@ "DiscardAlpha": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}", "Name": "Emissive", "RGB_Weight": "CIEXYZ", @@ -46,7 +46,7 @@ "PixelFormat": "ASTC_6x6", "DiscardAlpha": true }, - "osx_gl": { + "mac": { "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}", "Name": "Emissive", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset index 9cf32093d1..33d7babf00 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset @@ -11,7 +11,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{0D26B387-2FBA-456D-AB8E-613020BCC7F8}", "Name": "Gradient", "SourceColor": "Linear", @@ -25,7 +25,7 @@ "DestColor": "Linear", "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{0D26B387-2FBA-456D-AB8E-613020BCC7F8}", "Name": "Gradient", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset index c77c77b988..f06682be42 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset @@ -18,7 +18,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{B6B04FD3-BD7B-44AC-AD93-6FECD2BD4D76}", "Name": "Greyscale", "SourceColor": "Linear", @@ -46,7 +46,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{B6B04FD3-BD7B-44AC-AD93-6FECD2BD4D76}", "Name": "Greyscale", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset index fb4155a974..8bd6b348d1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset @@ -28,7 +28,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}", "Name": "IBLDiffuse", "FileMasks": [ @@ -74,7 +74,7 @@ "SubId": 3000 } }, - "osx_gl": { + "mac": { "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}", "Name": "IBLDiffuse", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset index 530eb3d048..402fc470eb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset @@ -26,7 +26,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}", "Name": "IBLSkybox", "FileMasks": [ @@ -68,7 +68,7 @@ "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" } }, - "osx_gl": { + "mac": { "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}", "Name": "IBLSkybox", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset index db5a9276bd..d940f425c2 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset @@ -30,7 +30,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ @@ -80,7 +80,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings b/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings index c8d921a8ff..82a57dd614 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings @@ -5,7 +5,7 @@ "ClassData": { "AnalysisFingerprint": "2", "BuildSettings": { - "es3": { + "android": { "GlossScale": 16.0, "GlossBias": 0.0, "Streaming": false, @@ -17,7 +17,7 @@ "Streaming": false, "Enable": true }, - "osx_gl": { + "mac": { "GlossScale": 16.0, "GlossBias": 0.0, "Streaming": false, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset new file mode 100644 index 0000000000..1bb23c6e96 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset @@ -0,0 +1,45 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "FileMasks": ["_lutr32f"], + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "ios": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "osx_gl": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "provo": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset index 6bfb697a5e..183653d111 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset @@ -11,7 +11,7 @@ "PixelFormat": "R16G16" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{D55CBCD3-AF2D-4515-98AB-E278F6B3B5F6}", "Name": "LUT_RG16", "SourceColor": "Linear", @@ -25,7 +25,7 @@ "DestColor": "Linear", "PixelFormat": "R16G16" }, - "osx_gl": { + "mac": { "UUID": "{D55CBCD3-AF2D-4515-98AB-E278F6B3B5F6}", "Name": "LUT_RG16", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset index a010d26a9c..2cf0c6ca0a 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset @@ -12,7 +12,7 @@ "PixelFormat": "R32G32F" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{52470B8B-0798-4E03-B0D3-039D5141CFEC}", "Name": "LUT_RG32F", "SourceColor": "Linear", @@ -26,7 +26,7 @@ "DestColor": "Linear", "PixelFormat": "R32G32F" }, - "osx_gl": { + "mac": { "UUID": "{52470B8B-0798-4E03-B0D3-039D5141CFEC}", "Name": "LUT_RG32F", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset index ca636f486a..9838d532b2 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset @@ -14,7 +14,7 @@ "PixelFormat": "R8G8" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{3791319D-043B-4011-8B6F-3DE96D0C4309}", "Name": "LUT_RG8", "SourceColor": "Linear", @@ -34,7 +34,7 @@ ], "PixelFormat": "R8G8" }, - "osx_gl": { + "mac": { "UUID": "{3791319D-043B-4011-8B6F-3DE96D0C4309}", "Name": "LUT_RG8", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset new file mode 100644 index 0000000000..f36d566d7e --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset @@ -0,0 +1,59 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "ios": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "osx_gl": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "provo": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset new file mode 100644 index 0000000000..367c5101b3 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset @@ -0,0 +1,59 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "ios": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "osx_gl": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "provo": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset index 717ece058d..3a456825bf 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset @@ -12,7 +12,7 @@ "PixelFormat": "R32G32B32A32F" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{AC4C49D4-2C70-425A-8DBF-E7FB2C61CF8D}", "Name": "LUT_RGBA32F", "SourceColor": "Linear", @@ -26,7 +26,7 @@ "DestColor": "Linear", "PixelFormat": "R32G32B32A32F" }, - "osx_gl": { + "mac": { "UUID": "{AC4C49D4-2C70-425A-8DBF-E7FB2C61CF8D}", "Name": "LUT_RGBA32F", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset index 6dbb29f830..49bf33dd84 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset @@ -10,7 +10,7 @@ "DestColor": "Linear" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{3A6BB297-B610-4EA5-8DA4-610FB12B9EC0}", "Name": "LUT_RGBA8", "SourceColor": "Linear", @@ -22,7 +22,7 @@ "SourceColor": "Linear", "DestColor": "Linear" }, - "osx_gl": { + "mac": { "UUID": "{3A6BB297-B610-4EA5-8DA4-610FB12B9EC0}", "Name": "LUT_RGBA8", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset index 9c57f80709..5ce06aaea2 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset @@ -15,7 +15,7 @@ "PixelFormat": "R8G8B8X8" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{B1AC2F76-CB1A-46A8-B92D-B8DFBB564FCF}", "Name": "LayerMask", "SourceColor": "Linear", @@ -37,7 +37,7 @@ ], "PixelFormat": "R8G8B8X8" }, - "osx_gl": { + "mac": { "UUID": "{B1AC2F76-CB1A-46A8-B92D-B8DFBB564FCF}", "Name": "LayerMask", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset index 84294dfbcc..9f4b5bf68d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset @@ -9,7 +9,7 @@ "PixelFormat": "BC1" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", "PixelFormat": "ETC2" @@ -19,7 +19,7 @@ "Name": "LensOptics", "PixelFormat": "ASTC_4x4" }, - "osx_gl": { + "mac": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", "PixelFormat": "BC1" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset index 8c98394d36..ede264a78e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset @@ -14,7 +14,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", @@ -34,7 +34,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset index f64c48eb95..ad0e2ddf06 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset @@ -9,7 +9,7 @@ "PixelFormat": "R8G8B8X8" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", "Name": "LoadingScreen", "PixelFormat": "R8G8B8X8" @@ -19,7 +19,7 @@ "Name": "LoadingScreen", "PixelFormat": "R8G8B8X8" }, - "osx_gl": { + "mac": { "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", "Name": "LoadingScreen", "PixelFormat": "R8G8B8X8" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset index a402a2636c..9370de063d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset @@ -15,7 +15,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, @@ -37,7 +37,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset index f5ecc58d1a..459cd5b1fb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset @@ -14,7 +14,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, @@ -34,7 +34,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset index 104f3b4a39..04307eada4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset @@ -27,7 +27,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{508B21D5-5250-4003-97EC-1CF28D571ACF}", "Name": "Normals", "SourceColor": "Linear", @@ -75,7 +75,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{508B21D5-5250-4003-97EC-1CF28D571ACF}", "Name": "Normals", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset index c513720b68..46e97c3443 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset @@ -19,7 +19,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", "Name": "NormalsFromDisplacement", "SourceColor": "Linear", @@ -49,7 +49,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", "Name": "NormalsFromDisplacement", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset index e773f7d910..b8f6e38ac1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset @@ -25,7 +25,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{6EE749F4-846E-4F7A-878C-F211F85EA59F}", "Name": "NormalsWithSmoothness", "SourceColor": "Linear", @@ -67,7 +67,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{6EE749F4-846E-4F7A-878C-F211F85EA59F}", "Name": "NormalsWithSmoothness", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset index 4cf7af6f29..58bb02cd72 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset @@ -22,7 +22,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", "Name": "NormalsWithSmoothness_Legacy", "SourceColor": "Linear", @@ -58,7 +58,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", "Name": "NormalsWithSmoothness_Legacy", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset index 265379d053..bbd7fd5db9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset @@ -27,7 +27,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{F3D5E572-A3CF-435A-A2AB-75D2B6907847}", "Name": "Opacity", "SourceColor": "Linear", @@ -73,7 +73,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{F3D5E572-A3CF-435A-A2AB-75D2B6907847}", "Name": "Opacity", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset index 03744dee9e..e51848a116 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset @@ -8,7 +8,7 @@ "Name": "ReferenceImage" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}", "Name": "ReferenceImage" }, @@ -16,7 +16,7 @@ "UUID": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}", "Name": "ReferenceImage" }, - "osx_gl": { + "mac": { "UUID": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}", "Name": "ReferenceImage" }, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset index 4d75e7ae1d..d9b9c17d07 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset @@ -13,7 +13,7 @@ "DiscardAlpha": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{46D9F30F-793C-4449-BCEF-12A396E61B2C}", "Name": "ReferenceImage_HDRLinear", "SourceColor": "Linear", @@ -31,7 +31,7 @@ "PixelFormat": "R9G9B9E5", "DiscardAlpha": true }, - "osx_gl": { + "mac": { "UUID": "{46D9F30F-793C-4449-BCEF-12A396E61B2C}", "Name": "ReferenceImage_HDRLinear", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset index 8344102425..8a5a83afa3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset @@ -13,7 +13,7 @@ "DiscardAlpha": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{EEF24422-C8F0-4ECE-B32A-C70DB8129466}", "Name": "ReferenceImage_HDRLinearUncompressed", "SourceColor": "Linear", @@ -31,7 +31,7 @@ "PixelFormat": "R16G16B16A16F", "DiscardAlpha": true }, - "osx_gl": { + "mac": { "UUID": "{EEF24422-C8F0-4ECE-B32A-C70DB8129466}", "Name": "ReferenceImage_HDRLinearUncompressed", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset index 515e9b0512..bf72f21b06 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset @@ -11,7 +11,7 @@ "SuppressEngineReduce": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{02C3D9F5-3637-49BA-A48A-D68D629A4D14}", "Name": "ReferenceImage_Linear", "SourceColor": "Linear", @@ -25,7 +25,7 @@ "DestColor": "Linear", "SuppressEngineReduce": true }, - "osx_gl": { + "mac": { "UUID": "{02C3D9F5-3637-49BA-A48A-D68D629A4D14}", "Name": "ReferenceImage_Linear", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset index 58e283add7..1844e0186e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset @@ -34,7 +34,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", "Name": "Reflectance", "SourceColor": "Linear", @@ -92,7 +92,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", "Name": "Reflectance", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset index e386d08a35..e51cc7122b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset @@ -16,7 +16,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", "Name": "ReflectanceWithSmoothness_Legacy", "FileMasks": [ @@ -40,7 +40,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", "Name": "ReflectanceWithSmoothness_Legacy", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset index 767b0b67eb..07cc39c955 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset @@ -18,7 +18,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", "Name": "Reflectance_Linear", "DestColor": "Linear", @@ -46,7 +46,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", "Name": "Reflectance_Linear", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset index b2bbf905db..f76741148c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset @@ -12,7 +12,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", "Name": "SF_Font", "SourceColor": "Linear", @@ -28,7 +28,7 @@ "SuppressEngineReduce": true, "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", "Name": "SF_Font", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset index 41ba10f55c..aff25dc83d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset @@ -12,7 +12,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", "Name": "SF_Gradient", "SourceColor": "Linear", @@ -28,7 +28,7 @@ "SuppressEngineReduce": true, "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", "Name": "SF_Gradient", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset index e36e42860d..46a32ce5d4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset @@ -13,7 +13,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", "Name": "SF_Image", "SourceColor": "Linear", @@ -31,7 +31,7 @@ "PixelFormat": "PVRTC4", "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", "Name": "SF_Image", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset index fa2fe2ae72..0ba70d2ca3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset @@ -12,7 +12,7 @@ "PixelFormat": "BC1" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", "Name": "SF_Image_nonpower2", "SourceColor": "Linear", @@ -28,7 +28,7 @@ "SuppressEngineReduce": true, "PixelFormat": "PVRTC4" }, - "osx_gl": { + "mac": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", "Name": "SF_Image_nonpower2", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset index 9102bd53bb..4f71855ecf 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset @@ -21,7 +21,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}", "Name": "Skybox", "FileMasks": [ @@ -55,7 +55,7 @@ "RequiresConvolve": false } }, - "osx_gl": { + "mac": { "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}", "Name": "Skybox", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset index 19881f93d7..84a70935f1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset @@ -16,7 +16,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", "Name": "Terrain_Albedo", "SourceColor": "Linear", @@ -40,7 +40,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", "Name": "Terrain_Albedo", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset index 2fcbb012d4..1d83737ef9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset @@ -15,7 +15,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", @@ -37,7 +37,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset index d0dbbcba6f..6e28cafe11 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset @@ -13,7 +13,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", "Name": "Uncompressed", "PixelFormat": "R8G8B8X8", @@ -31,7 +31,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", "Name": "Uncompressed", "PixelFormat": "R8G8B8X8", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset index 01595a3425..6f70e8f14f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset @@ -13,7 +13,7 @@ "FileMasks": [ "_ui" ] }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, @@ -25,7 +25,7 @@ "SuppressEngineReduce": true, "PixelFormat": "ASTC_6x6" }, - "osx_gl": { + "mac": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset index 78c63790ab..39066b242b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset @@ -13,7 +13,7 @@ "FileMasks": [ "_ui" ] }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}", "Name": "UserInterface_Lossless", "SuppressEngineReduce": true, @@ -25,7 +25,7 @@ "SuppressEngineReduce": true, "PixelFormat": "R8G8B8A8" }, - "osx_gl": { + "mac": { "UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}", "Name": "UserInterface_Lossless", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/Shader/Code/CMakeLists.txt b/Gems/Atom/Asset/Shader/Code/CMakeLists.txt index a06aa79d24..dd8afec534 100644 --- a/Gems/Atom/Asset/Shader/Code/CMakeLists.txt +++ b/Gems/Atom/Asset/Shader/Code/CMakeLists.txt @@ -101,3 +101,36 @@ ly_add_target( 3rdParty::SPIRVCross 3rdParty::azslc ) + +################################################################################ +# Tests +################################################################################ +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + + ly_add_target( + NAME Atom_Asset_Shader.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + atom_asset_shader_builders_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + . + Source/Editor + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AtomCore + AZ::AzTest + AZ::AzFramework + AZ::AzToolsFramework + Legacy::CryCommon + Gem::Atom_RPI.Public + Gem::Atom_RHI.Public + Gem::Atom_RPI.Edit + Gem::Atom_Asset_Shader.Static + ) + ly_add_googletest( + NAME Gem::Atom_Asset_Shader.Tests + ) + +endif() diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp index 1e471f8644..14193d774f 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp @@ -60,33 +60,31 @@ namespace AZ void PreprocessorOptions::RemovePredefinedMacros(const AZStd::vector& macroNames) { + for (const auto& macroName : macroNames) + { m_predefinedMacros.erase( AZStd::remove_if( m_predefinedMacros.begin(), m_predefinedMacros.end(), - [&](const AZStd::string& predefinedMacro) - { - for (const auto& macroName : macroNames) + [&](const AZStd::string& predefinedMacro) { + // Haystack, needle, bCaseSensitive + if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true)) { - // Haystack, needle, bCaseSensitive - if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true)) - { - return false; - } - // If found, let's make sure it is not just a substring. - if (predefinedMacro.size() == macroName.size()) - { - return true; - } - // The predefinedMacro can be a string like "macro=value". If we find '=' it is a match. - if (predefinedMacro.c_str()[macroName.size()] == '=') - { - return true; - } return false; } + // If found, let's make sure it is not just a substring. + if (predefinedMacro.size() == macroName.size()) + { + return true; + } + // The predefinedMacro can be a string like "macro=value". If we find '=' it is a match. + if (predefinedMacro.c_str()[macroName.size()] == '=') + { + return true; + } return false; }), m_predefinedMacros.end()); + } } //! Binder helper to Matsui C-Pre-Processor library diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp index 1668b57866..5758db1da2 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp @@ -344,8 +344,7 @@ namespace AZ AZStd::string prependedAzslFilePath = RHI::PrependFile(args); if (prependedAzslFilePath == azslFullPath) { - // For some reason the combined azsl file was not created in the temporary - // directory assigned to this job. + // The specific error is already reported by RHI::PrependFile(). response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; return; } diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index a20b9c869b..c256caac4a 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -736,13 +736,13 @@ namespace AZ { platformId = AzFramework::PlatformId::PC; } - else if (platformIdentifier == "osx_gl") + else if (platformIdentifier == "mac") { - platformId = AzFramework::PlatformId::OSX; + platformId = AzFramework::PlatformId::MAC_ID; } - else if (platformIdentifier == "es3") + else if (platformIdentifier == "android") { - platformId = AzFramework::PlatformId::ES3; + platformId = AzFramework::PlatformId::ANDROID_ID; } else if (platformIdentifier == "ios") { @@ -788,13 +788,13 @@ namespace AZ { platformId = AzFramework::PlatformId::PC; } - else if (platform == "osx_gl") + else if (platform == "mac") { - platformId = AzFramework::PlatformId::OSX; + platformId = AzFramework::PlatformId::MAC_ID; } - else if (platform == "es3") + else if (platform == "android") { - platformId = AzFramework::PlatformId::ES3; + platformId = AzFramework::PlatformId::ANDROID_ID; } else if (platform == "ios") { @@ -886,7 +886,7 @@ namespace AZ } RHI::Ptr BuildPipelineLayoutDescriptorForApi( - const char* builderName, const RPI::ShaderResourceGroupLayoutList& srgLayoutList, const MapOfStringToStageType& shaderEntryPoints, + [[maybe_unused]] const char* builderName, const RPI::ShaderResourceGroupLayoutList& srgLayoutList, const MapOfStringToStageType& shaderEntryPoints, const RHI::ShaderCompilerArguments& shaderCompilerArguments, const RootConstantData& rootConstantData, RHI::ShaderPlatformInterface* shaderPlatformInterface, BindingDependencies& bindingDependencies /*inout*/) { diff --git a/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.cpp b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.cpp new file mode 100644 index 0000000000..276c225e05 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.cpp @@ -0,0 +1,41 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "ShaderBuilderTestFixture.h" + +#include +#include + +namespace UnitTest +{ + void ShaderBuilderTestFixture::SetUp() + { + AllocatorsTestFixture::SetUp(); + + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + + AZ::NameDictionary::Create(); + } + + void ShaderBuilderTestFixture::TearDown() + { + AZ::NameDictionary::Destroy(); + + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + + AllocatorsTestFixture::TearDown(); + } + +} + diff --git a/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.h b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.h new file mode 100644 index 0000000000..450cc6bde5 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.h @@ -0,0 +1,34 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace UnitTest +{ + /** + * Unit test fixture for setting up memory allocation pools and the AZ::Name dictionary. + * In the future will be extended as needed. + */ + class ShaderBuilderTestFixture + : public AllocatorsTestFixture + { + protected: + /////////////////////////////////////////////////////////////////////// + // AllocatorsTestFixture overrides + void SetUp() override; + void TearDown() override; + /////////////////////////////////////////////////////////////////////// + }; +} // namespace UnitTest + diff --git a/Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp b/Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp new file mode 100644 index 0000000000..2e5ee3fc09 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp @@ -0,0 +1,523 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "Common/ShaderBuilderTestFixture.h" + +namespace UnitTest +{ + using namespace AZ; + + struct KeyValueView + { + AZStd::string_view m_key; + AZStd::string_view m_value; + }; + + class SupervariantCmdArgumentTests : public ShaderBuilderTestFixture + { + protected: + static constexpr char MCPP_MACRO1[] = "MACRO1"; + static constexpr char MCPP_VALUE1[] = "VALUE1a"; + static constexpr char MCPP_NEW_VALUE1[] = "VALUE1b"; // Missing A is not a typo + + static constexpr char MCPP_MACRO2[] = "MACRO2"; + static constexpr char MCPP_VALUE2[] = "VALUE2"; + + static constexpr char MCPP_MACRO3[] = "MACRO3"; + static constexpr char MCPP_VALUE3[] = "VALUE3a"; + static constexpr char MCPP_NEW_VALUE3[] = "VALUE3b"; + + static constexpr char MCPP_MACRO4[] = "MACRO4"; + + static constexpr char MCPP_MACRO5[] = "MACRO5"; + + static constexpr char MCPP_MACRO6[] = "MACRO6"; + static constexpr char MCPP_VALUE6[] = "VALUE6"; + + static constexpr char AZSLC_ARG1[] = "--azsl1"; + + static constexpr char AZSLC_ARG2[] = "--azsl2"; + static constexpr char AZSLC_VAL2[] = "open,source"; + static constexpr char AZSLC_NEW_VAL2a[] = "closed,binary"; + static constexpr char AZSLC_NEW_VAL2b[] = "closed,source"; + + static constexpr char AZSLC_ARG3[] = "--azsl3"; + static constexpr char AZSLC_VAL3[] = "blue"; + + static constexpr char AZSLC_ARG4[] = "-azsl4"; + + static constexpr char AZSLC_ARG5[] = "--azsl5"; + static constexpr char AZSLC_VAL5[] = "smith,wick,john,45,-1,-1"; + static constexpr char AZSLC_NEW_VAL5[] = "apple,seed,crisp,-1,2,0"; + + static constexpr char AZSLC_ARG6[] = "--azsl6"; + + static constexpr char AZSLC_ARG7[] = "--azsl7"; + + //! Helper function. + //! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key=Value". + AZStd::vector CreateListOfStringsFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::vector listOfStrings; + for (const auto& keyValue : listOfKeyValues) + { + if (keyValue.m_value.empty()) + { + listOfStrings.push_back(keyValue.m_key); + } + else + { + listOfStrings.push_back(AZStd::string::format("%s=%s", keyValue.m_key.data(), keyValue.m_value.data())); + } + } + return listOfStrings; + } + + //! Helper function. + //! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key1", "Value1", "Key2", "Value2". + AZStd::vector CreateListOfSingleStringsFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::vector listOfStrings; + for (const auto& keyValue : listOfKeyValues) + { + listOfStrings.push_back(keyValue.m_key); + if (!keyValue.m_value.empty()) + { + listOfStrings.push_back(keyValue.m_value); + } + } + return listOfStrings; + } + + //! Helper function. + //! @param outputString: [out] The string " @argName" gets appended to it (The space is intentional). + //! Alternatively, if @argValue is NOT empty, then the string " @argName=@argValue" is + //! appended to it. + //! @param argName: A typical command line argument. "-p" or "--some". + //! @param argValue: A string representing the value that should be appended to @argName. + void AppendCmdLineArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const + { + if (argValue.empty()) + { + outputString += AZStd::string::format(" %s", argName.data()); + } + else + { + outputString += AZStd::string::format(" %s=%s", argName.data(), argValue.data()); + } + } + + //! Helper function. + //! Similar to above, but assumes that @argName refers to just the name of a macro definition so the appended string will always start + //! with "-D". + void AppendMacroDefinitionArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const + { + AppendCmdLineArgument(outputString, AZStd::string::format("-D%s", argName.data()), argValue); + } + + //! A helper made of helpers. + //! Returns a command line string that results of concatenating the input list of {Key, Value} pairs (with '='). + //! Example of a returned string: + //! "key1=value1 key2 key3 key4=value" + AZStd::string CreateCmdLineStringFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::string cmdLineString; + for (const auto& keyValueView : listOfKeyValues) + { + AppendCmdLineArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value); + } + return cmdLineString; + } + + //! A helper made of helpers. + //! Returns a command line string of macro definitions that results of concatenating the input list of {Key, Value} pairs. + //! Example of a returned string: + //! "-Dkey1=value1 -Dkey2 -Dkey3 -Dkey4=value" + AZStd::string CreateMacroDefinitionCmdLineStringFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::string cmdLineString; + for (const auto& keyValueView : listOfKeyValues) + { + AppendMacroDefinitionArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value); + } + return cmdLineString; + } + + //! @param includePaths A List of folder paths + //! @param predefinedMacros A List of strings with format: "name[=value]" + ShaderBuilder::PreprocessorOptions CreatePreprocessorOptions( + AZStd::array_view includePaths, AZStd::array_view predefinedMacros) const + { + ShaderBuilder::PreprocessorOptions preprocessorOptions; + + preprocessorOptions.m_projectIncludePaths.reserve(includePaths.size()); + for (const auto& path : includePaths) + { + preprocessorOptions.m_projectIncludePaths.push_back(path); + } + + preprocessorOptions.m_predefinedMacros.reserve(predefinedMacros.size()); + for (const auto& macro : predefinedMacros) + { + preprocessorOptions.m_predefinedMacros.push_back(macro); + } + + return preprocessorOptions; + } + + //! @param azslcAdditionalFreeArguments: A string representing series of command line arguments for AZSLc. + //! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC. + RHI::ShaderCompilerArguments CreateShaderCompilerArguments( + AZStd::string_view azslcAdditionalFreeArguments, AZStd::string_view dxcAdditionalFreeArguments) const + { + RHI::ShaderCompilerArguments shaderCompilerArguments; + shaderCompilerArguments.m_azslcWarningLevel = 1; + shaderCompilerArguments.m_azslcAdditionalFreeArguments = azslcAdditionalFreeArguments; + shaderCompilerArguments.m_defaultMatrixOrder = RHI::MatrixOrder::Row; + shaderCompilerArguments.m_dxcAdditionalFreeArguments = dxcAdditionalFreeArguments; + + return shaderCompilerArguments; + } + + + //! @param includePaths A List of folder paths + //! @param predefinedMacros A List of strings with format: "name[=value]" + //! @param azslcAdditionalFreeArguments A string representing series of command line arguments for AZSLc. + //! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC. + ShaderBuilder::GlobalBuildOptions CreateGlobalBuildOptions( + AZStd::array_view includePaths, + AZStd::array_view predefinedMacros, + AZStd::string_view azslcAdditionalFreeArguments, + AZStd::string_view dxcAdditionalFreeArguments) const + { + ShaderBuilder::GlobalBuildOptions globalBuildOptions; + globalBuildOptions.m_preprocessorSettings = CreatePreprocessorOptions(includePaths, predefinedMacros); + globalBuildOptions.m_compilerArguments = + CreateShaderCompilerArguments(azslcAdditionalFreeArguments, dxcAdditionalFreeArguments); + return globalBuildOptions; + } + + //! @param name Name of the supervariant. + //! @param plusArguments A string with command line arguments that contains both C-preprocessor macro definitions + //! and other command line arguments for AZSLc. + //! @param minusArguments A string with command line arguments that should be removed from the finalized command line arguments. + //! it can contain both, C-preprocessor macro definitions and other command line arguments for AZSLc. + RPI::ShaderSourceData::SupervariantInfo CreateSupervariantInfo( + AZStd::string_view name, AZStd::string_view plusArguments, AZStd::string_view minusArguments) const + { + RPI::ShaderSourceData::SupervariantInfo supervariantInfo; + supervariantInfo.m_name = name; + supervariantInfo.m_plusArguments = plusArguments; + supervariantInfo.m_minusArguments = minusArguments; + return supervariantInfo; + } + + bool StringContainsAllSubstrings(AZStd::string_view haystack, AZStd::array_view substrings) + { + return AZStd::all_of(AZ_BEGIN_END(substrings), + [&](AZStd::string_view needle) -> bool + { + return (haystack.find(needle) != AZStd::string::npos); + } + ); + } + + bool StringDoesNotContainAnyOneOfTheSubstrings(AZStd::string_view haystack, AZStd::array_view substrings) + { + return AZStd::all_of(AZ_BEGIN_END(substrings), [&](AZStd::string_view needle) -> bool { + return (haystack.find(needle) == AZStd::string::npos); + }); + } + + //! @returns: True if all strings in @substring appear in @vectorOfString. + //! @remark: Keep in mind that this is not the same as saying that all strings in @vectorOfStrings appear in @substrings. + bool VectorContainsAllSubstrings( + AZStd::array_view vectorOfStrings, AZStd::array_view substrings) + { + return AZStd::all_of( + AZ_BEGIN_END(substrings), + [&](AZStd::string_view needle) -> bool { + bool res = AZStd::any_of(AZ_BEGIN_END(vectorOfStrings), + [&](AZStd::string_view haystack) -> bool + { + return haystack.find(needle) != AZStd::string::npos; + } + ); + return res; + } + ); + } + + //! @returns: True only if None of the strings in @vectorOfStrings contains any of the strings in @substrings. + bool VectorDoesNotContainAnyOneOfTheSubstrings(AZStd::array_view vectorOfStrings, AZStd::array_view substrings) + { + return AZStd::all_of(AZ_BEGIN_END(vectorOfStrings), [&](AZStd::string_view haystack) -> bool { + return StringDoesNotContainAnyOneOfTheSubstrings(haystack, substrings); + }); + } + + }; // class SupervariantCmdArgumentTests + + + TEST_F(SupervariantCmdArgumentTests, CommandLineArgumentUtils_ValidateHelperFunctions) + { + // In this test the idea is to validate the static helper functions in AZ::RHI::ShaderCompilerArguments class + // that are useful for command line argument manipulation, etc. + AZStd::vector argumentList = { + {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, + }; + + auto argumentsAsString = CreateCmdLineStringFromListOfKeyValues(argumentList); + auto listOfArgumentNames = AZ::RHI::CommandLineArgumentUtils::GetListOfArgumentNames(argumentsAsString); + + EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(argumentList), [&](const KeyValueView& needle) -> bool { + return (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_key) != listOfArgumentNames.end()) && + // Make sure the values did not make into the expected list of keys. + (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_value) == listOfArgumentNames.end()); + })); + + AZStd::vector listOfArgumentsToRemove = { AZSLC_ARG4, AZSLC_ARG2 }; + auto stringWithRemovedArguments = + AZ::RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString(listOfArgumentsToRemove, argumentsAsString); + EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfArgumentsToRemove), [&](const AZStd::string& needle) -> bool { + return stringWithRemovedArguments.find(needle) == AZStd::string::npos; + })); + + AZStd::vector listOfSurvivingArguments = {AZSLC_ARG1, AZSLC_ARG3, AZSLC_ARG5}; + EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfSurvivingArguments), [&](const AZStd::string& needle) -> bool { + return stringWithRemovedArguments.find(needle) != AZStd::string::npos; + })); + + auto stringWithoutExtraSpaces = + AZ::RHI::CommandLineArgumentUtils::RemoveExtraSpaces(" --arg1 -arg2 --arg3=foo --arg4=bar "); + EXPECT_EQ(stringWithoutExtraSpaces, AZStd::string("--arg1 -arg2 --arg3=foo --arg4=bar")); + + auto stringAsMergedArguments = + AZ::RHI::CommandLineArgumentUtils::MergeCommandLineArguments("--arg1 -arg2 --arg3=foo", "--arg3=bar --arg4"); + EXPECT_EQ(stringAsMergedArguments, AZStd::string("--arg1 -arg2 --arg3=bar --arg4")); + + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-DMACRO")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-D MACRO")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -D MACRO")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -DMACRO --more")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -D MACRO=VALUE --more")); + EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more")); + EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more --DFAKE")); + EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--DFAKE1 --help -p --more --D FAKE2")); + } + + TEST_F(SupervariantCmdArgumentTests, ShaderCompilerArguments_ValidateCommandLineArgumentsMerge) + { + // In this test we validate that AZ::RHI::ShaderCompilerArguments::Merge() works as expected + // by merging AZSLC & DXC arguments giving higher priority to the arguments in the "right". + + auto shaderCompilerArgumentsLeft = CreateShaderCompilerArguments( + "--azsl1 --azsl2=avalue2a -azsl3 --azsl4=avalue4a", + "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3a --dxc4"); + auto shaderCompilerArgumentsRight = CreateShaderCompilerArguments( + "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5", + "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a"); + + shaderCompilerArgumentsLeft.Merge(shaderCompilerArgumentsRight); + EXPECT_EQ(shaderCompilerArgumentsLeft.m_azslcAdditionalFreeArguments, "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5"); + EXPECT_EQ(shaderCompilerArgumentsLeft.m_dxcAdditionalFreeArguments, "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a"); + } + + + TEST_F(SupervariantCmdArgumentTests, SupervariantInfo_ValidateMemberFunctions) + { + // In this test all member functions of the ShaderSourceData::SupervariantInfo class + // are validated. + + AZStd::vector mcppMacrosList = { + {MCPP_MACRO1, MCPP_VALUE1}, + {MCPP_MACRO2, MCPP_VALUE2}, + {MCPP_MACRO3, MCPP_VALUE3}, + {MCPP_MACRO4, ""}, + }; + + AZStd::string argumentsToAddOrReplace; + AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO3, MCPP_NEW_VALUE3); + AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG2, AZSLC_NEW_VAL2a); + AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO1, MCPP_NEW_VALUE1); + AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG5, AZSLC_NEW_VAL5); + AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO5, ""); + AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG6, ""); + + AZStd::string argumentsToRemove; + AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG3, ""); + AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO2, ""); + AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG4, ""); + AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO4, ""); + + auto supervariantInfo = CreateSupervariantInfo("Dummy", argumentsToAddOrReplace, argumentsToRemove); + + auto macroListToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove(); + AZStd::vector macroNamesToRemoveThatMustBePresent = { MCPP_MACRO1, MCPP_MACRO2, MCPP_MACRO3, MCPP_MACRO4, MCPP_MACRO5 }; + EXPECT_EQ(macroListToRemove.size(), macroNamesToRemoveThatMustBePresent.size()); + EXPECT_TRUE( + VectorContainsAllSubstrings(macroListToRemove, macroNamesToRemoveThatMustBePresent) + ); + + auto macroListToAdd = supervariantInfo.GetMacroDefinitionsToAdd(); + AZStd::vector macroNamesToAddThatMustBePresent = {MCPP_MACRO1, MCPP_MACRO3, MCPP_MACRO5}; + EXPECT_EQ(macroListToAdd.size(), macroNamesToAddThatMustBePresent.size()); + EXPECT_TRUE(VectorContainsAllSubstrings(macroListToAdd, macroNamesToAddThatMustBePresent)); + + // The result of GetCustomizedArgumentsForAzslc() is the most important value to test + AZStd::vector freeAzslcArgumentList = { + {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, + }; + AZStd::string azslcArgs = CreateCmdLineStringFromListOfKeyValues(freeAzslcArgumentList); + AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgs); + + AZStd::vector stringsThatMustBePresent = { + AZSLC_ARG1, AZSLC_ARG2, AZSLC_NEW_VAL2a, AZSLC_ARG5, AZSLC_NEW_VAL5, AZSLC_ARG6}; + EXPECT_TRUE(StringContainsAllSubstrings(customizedAzslcArgs, stringsThatMustBePresent)); + + AZStd::vector stringsThatCanNotBePresent = { AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG4, + // Because GetCustomizedArgumentsForAzslc() only returns arguments for AZSLc, none of the macro related + // arguments can be present + MCPP_MACRO1, MCPP_VALUE1, MCPP_NEW_VALUE1, + MCPP_MACRO2, MCPP_VALUE2, + MCPP_MACRO3, MCPP_VALUE3, MCPP_NEW_VALUE3, + MCPP_MACRO4, + MCPP_MACRO5 + }; + + EXPECT_TRUE( + StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, stringsThatCanNotBePresent) + ); + } + + + TEST_F(SupervariantCmdArgumentTests, ShaderAssetBuilder_ValidateInfluenceOfSupervariantInfoOnGlobalBuildOptions) + { + // In this test we validate how the ShaderAssetBuilder configure the commmand line arguments it passes + // to MCPP, AZSLc & DXC. It basically starts with a GlobalBuildOptions, that gets further customized by + // the ShaderCompilerArguments from ShaderSourceData(.shader file) and later further customized + // by each SupervariantInfo in ShaderSourceData. + + // The first step is to define the initial values of the GlobalBuildOptions. + AZStd::vector globalMcppMacrosList = { + {MCPP_MACRO1, MCPP_VALUE1}, + {MCPP_MACRO2, MCPP_VALUE2}, + {MCPP_MACRO3, MCPP_VALUE3}, + {MCPP_MACRO4, ""}, + }; + + AZStd::vector globalAzslArguments = { + {AZSLC_ARG1, ""}, + {AZSLC_ARG2, AZSLC_VAL2}, + {AZSLC_ARG3, AZSLC_VAL3}, + {AZSLC_ARG4, ""}, + {AZSLC_ARG5, AZSLC_VAL5}, + }; + + auto globalBuildOptions = CreateGlobalBuildOptions( + AZStd::vector(), CreateListOfStringsFromListOfKeyValues(globalMcppMacrosList), + CreateCmdLineStringFromListOfKeyValues(globalAzslArguments), + "" /* Don't care about DXC in this test */); + + // The second step is to load the Shader Compiler Arguments from the .shader file. + // These arguments will be merged in @globalBuildOptions, but the .shader arguments have + // higher priority. + AZStd::vector shaderAzslArguments = { + {AZSLC_ARG2, AZSLC_NEW_VAL2a}, + {AZSLC_ARG6, ""}, + }; + auto shaderCompilerArguments = CreateShaderCompilerArguments( + CreateCmdLineStringFromListOfKeyValues(shaderAzslArguments), "" /* Don't care about DXC in this test */); + globalBuildOptions.m_compilerArguments.Merge(shaderCompilerArguments); + + // Let's create the dummy supervariant. It will have some MCPP & AZSLc arguments to be added/replaced AND other MCPP & AZSLc arguments to be removed. + AZStd::vector supervariantAzslArgumentsToAdd = { + {AZSLC_ARG2, AZSLC_NEW_VAL2b}, + {AZSLC_ARG7, ""}, + }; + AZStd::vector supervariantMacroDefinitionsToAdd = { + {MCPP_MACRO1, MCPP_NEW_VALUE1}, + {MCPP_MACRO3, MCPP_NEW_VALUE3}, + {MCPP_MACRO5, ""}, + }; + auto supervariantArgumentsToAdd = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToAdd) + + CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacroDefinitionsToAdd); + + AZStd::vector supervariantAzslArgumentsToRemove = { + {AZSLC_ARG4, ""}, + {AZSLC_ARG1, ""}, + }; + AZStd::vector supervariantMacrosToRemove = { + {MCPP_MACRO2, ""}, + {MCPP_MACRO4, ""}, + }; + auto supervariantArgumentsToRemove = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToRemove) + + CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacrosToRemove); + + //CreateMacroDefinitionCmdLineStringFromListOfKeyValues + auto supervariantInfo = CreateSupervariantInfo("Dummy", + supervariantArgumentsToAdd, // These arguments will be added or replace existing ones. + supervariantArgumentsToRemove); // These arguments must be removed. + + AZStd::vector macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove(); + globalBuildOptions.m_preprocessorSettings.RemovePredefinedMacros(macroDefinitionNamesToRemove); + AZStd::vector macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd(); + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.insert( + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end()); + + // Validate macro definitions that must be present. + EXPECT_TRUE( + VectorContainsAllSubstrings( + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros, + AZStd::vector({MCPP_MACRO1, MCPP_NEW_VALUE1, MCPP_MACRO3, MCPP_NEW_VALUE3, MCPP_MACRO5})) + ); + + // Validate macro definitions that can't be present. + EXPECT_TRUE( + VectorDoesNotContainAnyOneOfTheSubstrings( + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros, + AZStd::vector({MCPP_MACRO2, MCPP_VALUE3, MCPP_MACRO4})) + ); + + AZStd::string azslcArgsFromGlobalBuildOptions = globalBuildOptions.m_compilerArguments.MakeAdditionalAzslcCommandLineString(); + + // The result of GetCustomizedArgumentsForAzslc() is the most important value to test + AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgsFromGlobalBuildOptions); + + EXPECT_TRUE( + StringContainsAllSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToAdd)) + ); + + EXPECT_TRUE( + StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToRemove)) + ); + + EXPECT_TRUE( + StringContainsAllSubstrings(customizedAzslcArgs, AZStd::vector({AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG5, AZSLC_VAL5})) + ); + } + + +} //namespace UnitTest + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); + diff --git a/Gems/Atom/Asset/Shader/Code/atom_asset_shader_builders_tests_files.cmake b/Gems/Atom/Asset/Shader/Code/atom_asset_shader_builders_tests_files.cmake new file mode 100644 index 0000000000..9f22f9f632 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/atom_asset_shader_builders_tests_files.cmake @@ -0,0 +1,16 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/Common/ShaderBuilderTestFixture.h + Tests/Common/ShaderBuilderTestFixture.cpp + Tests/SupervariantCmdArgumentTests.cpp +) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index 954e01c592..4d13663aae 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -1,5 +1,5 @@ { - "description": "Material Type with properties used to define Enhanced PBR material shading model.", + "description": "Material Type with properties used to define Enhanced PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model, with advanced features like subsurface scattering, transmission, and anisotropy.", "propertyLayout": { "version": 3, "groups": [ @@ -32,7 +32,7 @@ "id": "clearCoat", "displayName": "Clear Coat", "description": "Properties for configuring gloss clear coat" - }, + }, { "id": "normal", "displayName": "Normal", @@ -205,6 +205,13 @@ "id": "m_baseColorMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -228,13 +235,6 @@ "type": "ShaderOption", "id": "o_baseColorTextureBlendMode" } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map.", - "type": "Bool", - "defaultValue": true } ], "metallic": [ @@ -261,6 +261,13 @@ "id": "m_metallicMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -272,29 +279,9 @@ "type": "ShaderInput", "id": "m_metallicMapUvIndex" } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true } ], "roughness": [ - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the values", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_roughnessFactor" - } - }, { "id": "textureMap", "displayName": "Texture Map", @@ -305,6 +292,13 @@ "id": "m_roughnessMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -346,11 +340,18 @@ } }, { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true + // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. + "id": "factor", + "displayName": "Factor", + "description": "Controls the roughness value", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "max": 1.0, + "connection": { + "type": "ShaderInput", + "id": "m_roughnessFactor" + } } ], "anisotropy": [ @@ -416,6 +417,13 @@ "id": "m_specularF0Map" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -428,13 +436,7 @@ "id": "m_specularF0MapUvIndex" } }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, + // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { "id": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", @@ -452,11 +454,7 @@ "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderOption", - "id": "o_clearCoat_feature_enabled" - } + "defaultValue": false }, { "id": "factor", @@ -481,6 +479,13 @@ "id": "m_clearCoatInfluenceMap" } }, + { + "id": "useInfluenceMap", + "displayName": " Use Texture", + "description": "Whether to use the texture map, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "influenceMapUv", "displayName": " UV", @@ -493,13 +498,6 @@ "id": "m_clearCoatInfluenceMapUvIndex" } }, - { - "id": "useInfluenceMap", - "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, { "id": "roughness", "displayName": "Roughness", @@ -523,6 +521,13 @@ "id": "m_clearCoatRoughnessMap" } }, + { + "id": "useRoughnessMap", + "displayName": " Use Texture", + "description": "Whether to use the texture map, or just default to the roughness value.", + "type": "Bool", + "defaultValue": true + }, { "id": "roughnessMapUv", "displayName": " UV", @@ -535,13 +540,6 @@ "id": "m_clearCoatRoughnessMapUvIndex" } }, - { - "id": "useRoughnessMap", - "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", - "type": "Bool", - "defaultValue": true - }, { "id": "normalStrength", "displayName": "Normal Strength", @@ -565,9 +563,16 @@ "id": "m_clearCoatNormalMap" } }, + { + "id": "useNormalMap", + "displayName": " Use Texture", + "description": "Whether to use the normal map", + "type": "Bool", + "defaultValue": true + }, { "id": "normalMapUv", - "displayName": "UV", + "displayName": " UV", "description": "Normal texture map UV set", "type": "Enum", "enumIsUv": true, @@ -576,29 +581,9 @@ "type": "ShaderInput", "id": "m_clearCoatNormalMapUvIndex" } - }, - { - "id": "useNormalMap", - "displayName": "Use Texture", - "description": "Whether to use the normal map", - "type": "Bool", - "defaultValue": true } ], "normal": [ - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the values", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "softMax": 2.0, - "connection": { - "type": "ShaderInput", - "id": "m_normalFactor" - } - }, { "id": "textureMap", "displayName": "Texture Map", @@ -609,6 +594,13 @@ "id": "m_normalMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map, or just rely on vertex normals.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -621,13 +613,6 @@ "id": "m_normalMapUvIndex" } }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", - "type": "Bool", - "defaultValue": true - }, { "id": "flipX", "displayName": "Flip X Channel", @@ -649,6 +634,19 @@ "type": "ShaderInput", "id": "m_flipNormalY" } + }, + { + "id": "factor", + "displayName": "Factor", + "description": "Strength factor for scaling the values", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "softMax": 2.0, + "connection": { + "type": "ShaderInput", + "id": "m_normalFactor" + } } ], "opacity": [ @@ -725,7 +723,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -872,18 +870,14 @@ "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderOption", - "id": "o_emissiveEnabled" - } + "defaultValue": false }, { "id": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", - "enumValues": [ "Ev100" ], + "enumValues": ["Ev100"], "defaultValue": "Ev100" }, { @@ -918,34 +912,27 @@ "id": "m_emissiveMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", "description": "Emissive texture map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_emissiveMapUvIndex" } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map.", - "type": "Bool", - "defaultValue": true } ], "parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", "displayName": "Texture Map", @@ -956,13 +943,20 @@ "id": "m_depthMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", "description": "Depth texture map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_parallaxUvIndex" @@ -973,7 +967,7 @@ "displayName": "Heightmap Scale", "description": "The total height of the heightmap in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { @@ -1011,7 +1005,7 @@ "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", "enumValues": [ "Basic", "Steep", "POM", "Relief", "ContactRefinement" ], - "defaultValue": "Basic", + "defaultValue": "POM", "connection": { "type": "ShaderOption", "id": "o_parallax_algorithm" @@ -1055,7 +1049,7 @@ "subsurfaceScattering": [ { "id": "enableSubsurfaceScattering", - "displayName": "Enable Subsurface Scattering", + "displayName": "Subsurface Scattering", "description": "Enable subsurface scattering feature, this will disable metallic and parallax mapping property due to incompatibility", "type": "Bool", "defaultValue": false, @@ -1069,7 +1063,7 @@ "displayName": " Factor", "description": "Strength factor for scaling percentage of subsurface scattering effect applied", "type": "float", - "defaultValue": 0.0, + "defaultValue": 1.0, "min": 0.0, "max": 1.0, "connection": { @@ -1087,25 +1081,25 @@ "id": "m_subsurfaceScatteringInfluenceMap" } }, + { + "id": "useInfluenceMap", + "displayName": " Use Influence Map", + "description": "Whether to use the texture map as influence mask.", + "type": "Bool", + "defaultValue": true + }, { "id": "influenceMapUv", "displayName": " UV", "description": "Influence map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_subsurfaceScatteringInfluenceMapUvIndex" } }, - { - "id": "useInfluenceMap", - "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", - "type": "Bool", - "defaultValue": true - }, { "id": "scatterColor", "displayName": " Scatter color", @@ -1136,11 +1130,16 @@ } }, { - "id": "enableTransmission", - "displayName": "Enable Transmission", - "description": "Enable transmission feature", - "type": "Bool", - "defaultValue": false + "id": "transmissionMode", + "displayName": "Transmission", + "description": "Algorithm used for calculating transmission", + "type": "Enum", + "enumValues": [ "None", "ThickObject", "ThinObject" ], + "defaultValue": "None", + "connection": { + "type": "ShaderOption", + "id": "o_transmission_mode" + } }, { "id": "thickness", @@ -1161,25 +1160,25 @@ "id": "m_transmissionThicknessMap" } }, + { + "id": "useThicknessMap", + "displayName": " Use Thickness Map", + "description": "Whether to use the thickness map", + "type": "Bool", + "defaultValue": true + }, { "id": "thicknessMapUv", "displayName": " UV", "description": "Thickness map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_transmissionThicknessMapUvIndex" } }, - { - "id": "useThicknessMap", - "displayName": " Use Thickness Map", - "description": "Whether to use the thickness map", - "type": "Bool", - "defaultValue": true - }, { "id": "transmissionTint", "displayName": " Transmission Tint", @@ -1187,18 +1186,6 @@ "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, - { - "id": "transmissionMode", - "displayName": " Mode", - "description": "Algorithm used for calculating transmission", - "type": "Enum", - "enumValues": [ "None", "ThickObject", "ThinObject" ], - "defaultValue": "None", - "connection": { - "type": "ShaderOption", - "id": "o_transmission_mode" - } - }, { "id": "transmissionPower", "displayName": " Power", @@ -1287,7 +1274,7 @@ } }, { - "id": "detailMapsMapUv", + "id": "textureMapUv", "displayName": "Detail Map UVs", "description": "Which UV set to use for detail map texture sampling", "type": "Enum", @@ -1388,7 +1375,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1506,7 +1493,7 @@ { "file": "Shaders/Depth/DepthPassTransparentMax.shader", "tag": "DepthPassTransparentMax" - } + } ], "functors": [ { @@ -1549,26 +1536,9 @@ "lightUnitProperty": "emissive.unit", "shaderInput": "m_emissiveIntensity", "ev100Index": 0, - "nitIndex": 1, - "ev100MinMax": [ -10, 20 ], - "nitMinMax": [ 0.001, 100000.0 ] - } - }, - { - // Enable/Disable shader based on different option. - "type": "ShaderEnable", - "args": { - "opacityMode": "opacity.mode", - "parallaxEnable": "parallax.enable", - "parallaxPdoEnable": "parallax.pdo", - "pbrShaderNoEdsIndex": 0, - "pbrShaderWithEdsIndex": 1, - "shadowShaderNoPSIndex": 2, - "shadowShaderWithPSIndex": 3, - "depthShaderNoPSIndex": 4, - "depthShaderWithPSIndex": 5, - "depthShaderTransparentMin": 8, - "depthShaderTransparentMax": 9 + "nitIndex" : 1, + "ev100MinMax": [-10, 20], + "nitMinMax": [0.001, 100000.0] } }, { @@ -1590,120 +1560,40 @@ "tintThickenssShaderInput": "m_transmissionTintThickness" } }, - { - // Reads material properties to determine whether a specific texture map should be sampled at runtime, and sets a shader option accordingly. - // @param textureProperty - which material property contains the texture asset reference (or maybe null) - // @param useTextureProperty - a boolean flag that toggles whether the texture should be sampled (if it's not null) - // @param shaderTags - which shader in the 'shaders' list above is configured by this functor - // @param shaderOption - the name of a shader option in the AZSL file that controls sampling of this texture + { "type": "UseTexture", "args": { "textureProperty": "baseColor.textureMap", - "dependentProperties": ["baseColor.textureMapUv"], "useTextureProperty": "baseColor.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], + "dependentProperties": ["baseColor.textureMapUv", "baseColor.textureBlendMode"], "shaderOption": "o_baseColor_useTexture" } }, { - // See the comment above for details. "type": "UseTexture", "args": { "textureProperty": "metallic.textureMap", - "dependentProperties": ["metallic.textureMapUv"], "useTextureProperty": "metallic.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], + "dependentProperties": ["metallic.textureMapUv"], "shaderOption": "o_metallic_useTexture" } }, { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "roughness.textureMap", - "dependentProperties": ["roughness.textureMapUv"], - "useTextureProperty": "roughness.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_roughness_useTexture" - } - }, - { - // See the comment above for details. "type": "UseTexture", "args": { "textureProperty": "specularF0.textureMap", - "dependentProperties": ["specularF0.textureMapUv"], "useTextureProperty": "specularF0.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], + "dependentProperties": ["specularF0.textureMapUv"], "shaderOption": "o_specularF0_useTexture" } }, { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "clearCoat.influenceMap", - "dependentProperties": ["clearCoat.influenceMapUv"], - "useTextureProperty": "clearCoat.useInfluenceMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_clearCoat_factor_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "clearCoat.roughnessMap", - "dependentProperties": ["clearCoat.roughnessMapUv"], - "useTextureProperty": "clearCoat.useRoughnessMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_clearCoat_roughness_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "clearCoat.normalMap", - "dependentProperties": ["clearCoat.normalMapUv"], - "useTextureProperty": "clearCoat.useNormalMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_clearCoat_normal_useTexture" - } - }, - { - // See the comment above for details. "type": "UseTexture", "args": { "textureProperty": "normal.textureMap", - "dependentProperties": ["normal.textureMapUv"], "useTextureProperty": "normal.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_normal_useTexture" + "dependentProperties": ["normal.textureMapUv", "normal.factor", "normal.flipX", "normal.flipY"], + "shaderOption": "o_normal_useTexture" } }, { @@ -1725,341 +1615,39 @@ } }, { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "emissive.textureMap", - "dependentProperties": ["emissive.textureMapUv"], - "useTextureProperty": "emissive.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_emissive_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "subsurfaceScattering.influenceMap", - "dependentProperties": ["subsurfaceScattering.influenceMapUv"], - "useTextureProperty": "subsurfaceScattering.useInfluenceMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_subsurfaceScattering_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "subsurfaceScattering.thicknessMap", - "dependentProperties": ["subsurfaceScattering.thicknessMapUv"], - "useTextureProperty": "subsurfaceScattering.useThicknessMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_transmission_useTexture" - } - }, - { - // Controls visibility for properties in the editor. - // @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue. - // @param affectedProperties - the properties that are affected by actions. - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "emissive.enable", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "emissive.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "emissive.color", - "emissive.intensity", - "emissive.useTexture", - "emissive.unit" - ] - } - }, - { - // Controls visibility for properties in the editor. - // @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue. - // @param affectedProperties - the properties that are affected by actions. - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "emissive.useTexture", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "emissive.useTexture", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "emissive.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "emissive.textureMap", - "emissive.textureMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "clearCoat.useInfluenceMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.useInfluenceMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.influenceMap", - "clearCoat.influenceMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "clearCoat.useRoughnessMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.useRoughnessMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.roughnessMap", - "clearCoat.roughnessMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "clearCoat.useNormalMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.useNormalMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.normalMap", - "clearCoat.normalMapUv" - ] - } - - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.useInfluenceMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.useInfluenceMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableSubsurfaceScattering", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.influenceMap", - "subsurfaceScattering.influenceMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.useThicknessMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.useThicknessMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.thicknessMap", - "subsurfaceScattering.thicknessMapUv" - ] + "file": "StandardPBR_ClearCoatState.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "clearCoat.enable", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.factor", - "clearCoat.useInfluenceMap", - "clearCoat.roughness", - "clearCoat.useRoughnessMap", - "clearCoat.useNormalMap" - ] + "file": "StandardPBR_ClearCoatEnableFeature.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.transmissionMode", - "triggerValue": "ThickObject", - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.transmissionMode", - "triggerValue": "None", - "visibility": "Hidden" - }, - { - "triggerProperty": "subsurfaceScattering.transmissionMode", - "triggerValue": "ThinObject", - "visibility": "Hidden" - }, - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.transmissionPower", - "subsurfaceScattering.transmissionDistortion", - "subsurfaceScattering.transmissionAttenuation" - ] + "file": "StandardPBR_EmissiveState.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.enableSubsurfaceScattering", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableSubsurfaceScattering", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.subsurfaceScatterFactor", - "subsurfaceScattering.useInfluenceMap", - "subsurfaceScattering.scatterColor", - "subsurfaceScattering.scatterDistance", - "subsurfaceScattering.quality" - ] + "file": "StandardPBR_ParallaxState.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.thickness", - "subsurfaceScattering.useThicknessMap", - "subsurfaceScattering.transmissionTint", - "subsurfaceScattering.transmissionMode", - "subsurfaceScattering.transmissionScale" - ] + "file": "StandardPBR_Roughness.lua" } }, { "type": "Lua", "args": { - "file": "StandardPBR_ParallaxState.lua" + "file": "StandardPBR_SubsurfaceState.lua" } }, { @@ -2069,33 +1657,21 @@ } }, { - "type": "OverrideDrawList", - "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "Blended", - "shaderIndex": 1, - "drawList": "transparent" - } - }, - { - "type": "OverrideDrawList", + "type": "Lua", "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "TintedTransparent", - "shaderIndex": 1, - "drawList": "transparent" + "file": "StandardPBR_HandleOpacityMode.lua" } }, { "type": "Lua", "args": { - "file": "StandardPBR_HandleOpacityMode.lua" + "file": "MaterialInputs/DetailMapsCommonFunctor.lua" } }, { "type": "Lua", "args": { - "file": "MaterialInputs/DetailMapsCommonFunctor.lua" + "file": "StandardPBR_ShaderEnable.lua" } } ], @@ -2104,3 +1680,4 @@ "UV1": "Unwrapped" } } + diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl index db99ee7e24..644473fef9 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl @@ -77,10 +77,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index a91e88afad..a4fcccb5f5 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -117,19 +117,12 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depth) { // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) - || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) - || (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture) || o_detail_normal_useTexture) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- @@ -260,7 +253,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Convert the angle from [0..1] = [0 .. 180 degrees] to radians [0 .. PI] const float anisotropyAngle = MaterialSrg::m_anisotropicAngle * PI; const float anisotropyFactor = MaterialSrg::m_anisotropicFactor; - surface.anisotropy.Init(surface.normal, tangents[0], bitangents[0], anisotropyAngle, anisotropyFactor, surface.roughnessA); + surface.anisotropy.Init(surface.normal, IN.m_tangent, IN.m_bitangent, anisotropyAngle, anisotropyFactor, surface.roughnessA); } // ------- Lighting Data ------- @@ -274,16 +267,16 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Directional light shadow coordinates lightingData.shadowCoords = IN.m_shadowCoords; - // ------- Occlusion ------- - - lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture); - lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture); - // ------- Emissive ------- float2 emissiveUv = IN.m_uv[MaterialSrg::m_emissiveMapUvIndex]; lightingData.emissiveLighting = GetEmissiveInput(MaterialSrg::m_emissiveMap, MaterialSrg::m_sampler, emissiveUv, MaterialSrg::m_emissiveIntensity, MaterialSrg::m_emissiveColor.rgb, o_emissiveEnabled, o_emissive_useTexture); + // ------- Occlusion ------- + + lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture); + lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture); + // ------- Clearcoat ------- // [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl index 80aedcd6f4..7f6be252e2 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl @@ -80,10 +80,10 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { static const float ShadowMapDepthBias = 0.000001; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 456d7cbabe..4fe1f2b364 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -180,17 +180,12 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) float3x3 uvMatrix = CreateIdentity3x3(); // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ( (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) || - (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) + if (o_normal_useTexture || o_detail_normal_useTexture) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } Surface surface; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index 101a03b907..f8c49d579c 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -287,6 +287,13 @@ "id": "m_specularF0Map" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -299,13 +306,7 @@ "id": "m_specularF0MapUvIndex" } }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, + // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { "id": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", @@ -616,7 +617,7 @@ "type": "float", "defaultValue": 6.0, "min": 0.0, - "softMax": 20.0 + "softMax": 20.0 }, { "id": "transmissionDistortion", @@ -909,7 +910,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1011,18 +1012,18 @@ "type": "HandleSubsurfaceScatteringParameters", "args": { "mode": "subsurfaceScattering.transmissionMode", - "scale" : "subsurfaceScattering.transmissionScale", - "power" : "subsurfaceScattering.transmissionPower", - "distortion" : "subsurfaceScattering.transmissionDistortion", - "attenuation" : "subsurfaceScattering.transmissionAttenuation", - "tintColor" : "subsurfaceScattering.transmissionTint", - "thickness" : "subsurfaceScattering.thickness", + "scale": "subsurfaceScattering.transmissionScale", + "power": "subsurfaceScattering.transmissionPower", + "distortion": "subsurfaceScattering.transmissionDistortion", + "attenuation": "subsurfaceScattering.transmissionAttenuation", + "tintColor": "subsurfaceScattering.transmissionTint", + "thickness": "subsurfaceScattering.thickness", "enabled": "subsurfaceScattering.enableSubsurfaceScattering", - "scatterDistanceColor" : "subsurfaceScattering.scatterColor", - "scatterDistanceIntensity" : "subsurfaceScattering.scatterDistance", - "scatterDistanceShaderInput" : "m_scatterDistance", - "parametersShaderInput" : "m_transmissionParams", - "tintThickenssShaderInput" : "m_transmissionTintThickness" + "scatterDistanceColor": "subsurfaceScattering.scatterColor", + "scatterDistanceIntensity": "subsurfaceScattering.scatterDistance", + "scatterDistanceShaderInput": "m_scatterDistance", + "parametersShaderInput": "m_transmissionParams", + "tintThickenssShaderInput": "m_transmissionTintThickness" } }, { @@ -1038,8 +1039,8 @@ "type": "UseTexture", "args": { "textureProperty": "specularF0.textureMap", - "dependentProperties": ["specularF0.textureMapUv"], "useTextureProperty": "specularF0.useTexture", + "dependentProperties": ["specularF0.textureMapUv"], "shaderOption": "o_specularF0_useTexture" } }, diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index a2854e8902..ec1298ae77 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -3,11 +3,6 @@ "propertyLayout": { "version": 3, "groups": [ - { - "id": "general", - "displayName": "General", - "description": "General settings." - }, { "id": "blend", "displayName": "Blend Settings", @@ -29,6 +24,11 @@ "displayName": "Irradiance", "description": "Properties for configuring the irradiance used in global illumination." }, + { + "id": "general", + "displayName": "General", + "description": "General settings." + }, //############################################################################################## // Layer 1 Groups //############################################################################################## @@ -74,8 +74,8 @@ }, { "id": "layer1_parallax", - "displayName": "Layer 1: Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Layer 1: Displacement", + "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { "id": "layer1_uv", @@ -127,8 +127,8 @@ }, { "id": "layer2_parallax", - "displayName": "Layer 2: Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Layer 2: Displacement", + "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { "id": "layer2_uv", @@ -180,8 +180,8 @@ }, { "id": "layer3_parallax", - "displayName": "Layer 3: Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Layer 3: Displacement", + "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { "id": "layer3_uv", @@ -194,18 +194,6 @@ // General Properties //############################################################################################## "general": [ - { - "id": "debugDrawMode", - "displayName": "Debug Draw Mode", - "description": "Enables various debug view features.", - "type": "Enum", - "enumValues": [ "None", "BlendSource", "DepthMaps" ], - "defaultValue": "None", - "connection": { - "type": "ShaderOption", - "id": "o_debugDrawMode" - } - }, { "id": "applySpecularAA", "displayName": "Apply Specular AA", @@ -322,16 +310,16 @@ "displayName": "Blend Source", "description": "The source to use for defining the blend mask. Note VertexColors mode will still use the texture as a fallback if the mesh does not have a COLOR0 stream.", "type": "Enum", - "enumValues": ["TextureMap", "VertexColors"], - "defaultValue": "TextureMap", + "enumValues": ["BlendMaskTexture", "BlendMaskVertexColors", "Displacement", "Displacement_With_BlendMaskTexture", "Displacement_With_BlendMaskVertexColors"], + "defaultValue": "BlendMaskTexture", "connection": { "type": "ShaderOption", - "id": "o_blendSource" + "id": "o_layerBlendSource" } }, { "id": "textureMap", - "displayName": "Blend Mask", + "displayName": "Blend Mask Texture", "description": "RGB image where each channel is the blend mask for one of the three available layers.", "type": "Image", "defaultValue": "Textures/DefaultBlendMask_layers.png", @@ -351,15 +339,44 @@ "type": "ShaderInput", "id": "m_blendMaskUvIndex" } + }, + { + "id": "displacementBlendDistance", + "displayName": "Blend Distance", + "description": "Adjusts how smoothly to transition between layers when displacement blending is enabled.", + "type": "Float", + "defaultValue": 0.0, + "min": 0.0, + "softMax": 0.1, + "step": 0.001, + "connection": { + "type": "ShaderInput", + "id": "m_displacementBlendDistance" + } + }, + { + "id": "debugDrawMode", + "displayName": "Debug Draw Mode", + "description": "Enables various debug view features.", + "type": "Enum", + "enumValues": [ "None", "BlendMask", "Displacement", "FinalBlendWeights" ], + "defaultValue": "None", + "connection": { + "type": "ShaderOption", + "id": "o_debugDrawMode" + } } ], "parallax": [ { + // Note parallax is enabled by default so that as soon as a user hooks up displacement settings they will see some parallax applied. + // The functor that controls parallax will set o_parallax_feature_enabled=false when all the individual layers have no displacement, so + // a default value of true here will not have any initial impact on performance. "id": "enable", "displayName": "Enable", "description": "Whether to enable the parallax feature for this material.", "type": "Bool", - "defaultValue": false + "defaultValue": true }, { "id": "parallaxUv", @@ -391,7 +408,7 @@ "description": "Quality of parallax mapping.", "type": "Enum", "enumValues": [ "Low", "Medium", "High", "Ultra" ], - "defaultValue": "Medium", + "defaultValue": "Low", "connection": { "type": "ShaderOption", "id": "o_parallax_quality" @@ -427,7 +444,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1090,29 +1107,40 @@ } ], "layer1_parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "description": "Displacement texture map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_layer1_m_depthMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, + { + "id": "invert", + "displayName": "Invert", + "description": "Invert the displacement map", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderInput", + "id": "m_layer1_m_depthInverted" + } + }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Scale", + "description": "The total height of the displacement texture map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { @@ -1132,17 +1160,6 @@ "type": "ShaderInput", "id": "m_layer1_m_depthOffset" } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_layer1_m_depthInverted" - } } ], "layer1_uv": [ @@ -1152,7 +1169,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1796,29 +1813,40 @@ } ], "layer2_parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "description": "Displacement texture map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_layer2_m_depthMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, + { + "id": "invert", + "displayName": "Invert", + "description": "Invert the displacement map", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderInput", + "id": "m_layer2_m_depthInverted" + } + }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Scale", + "description": "The total height of the displacement texture map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { @@ -1838,17 +1866,6 @@ "type": "ShaderInput", "id": "m_layer2_m_depthOffset" } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_layer2_m_depthInverted" - } } ], "layer2_uv": [ @@ -1858,7 +1875,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -2502,29 +2519,40 @@ } ], "layer3_parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "description": "Displacement texture map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_layer3_m_depthMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, + { + "id": "invert", + "displayName": "Invert", + "description": "Invert the displacement map", + "type": "Bool", + "defaultValue": true, + "connection": { + "type": "ShaderInput", + "id": "m_layer3_m_depthInverted" + } + }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Scale", + "description": "The total height of the displacement texture map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { @@ -2544,17 +2572,6 @@ "type": "ShaderInput", "id": "m_layer3_m_depthOffset" } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_layer3_m_depthInverted" - } } ], "layer3_uv": [ @@ -2564,7 +2581,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -2699,7 +2716,7 @@ { "type": "Lua", "args": { - "file": "StandardMultilayerPBR_Parallax.lua" + "file": "StandardMultilayerPBR_Displacement.lua" } }, //############################################################################################## @@ -2816,12 +2833,12 @@ } }, { - "type": "Lua", + "type": "UseTexture", "args": { - "file": "StandardMultilayerPBR_ParallaxPerLayer.lua", - "propertyNamePrefix": "layer1_", - "srgNamePrefix": "m_layer1_", - "optionsNamePrefix": "o_layer1_" + "textureProperty": "layer1_parallax.textureMap", + "useTextureProperty": "layer1_parallax.useTexture", + "dependentProperties": ["layer1_parallax.factor", "layer1_parallax.invert"], + "shaderOption": "o_layer1_o_useDepthMap" } }, { @@ -2953,12 +2970,12 @@ } }, { - "type": "Lua", + "type": "UseTexture", "args": { - "file": "StandardMultilayerPBR_ParallaxPerLayer.lua", - "propertyNamePrefix": "layer2_", - "srgNamePrefix": "m_layer2_", - "optionsNamePrefix": "o_layer2_" + "textureProperty": "layer2_parallax.textureMap", + "useTextureProperty": "layer2_parallax.useTexture", + "dependentProperties": ["layer2_parallax.factor", "layer2_parallax.invert"], + "shaderOption": "o_layer2_o_useDepthMap" } }, { @@ -3090,14 +3107,14 @@ } }, { - "type": "Lua", + "type": "UseTexture", "args": { - "file": "StandardMultilayerPBR_ParallaxPerLayer.lua", - "propertyNamePrefix": "layer3_", - "srgNamePrefix": "m_layer3_", - "optionsNamePrefix": "o_layer3_" + "textureProperty": "layer3_parallax.textureMap", + "useTextureProperty": "layer3_parallax.useTexture", + "dependentProperties": ["layer3_parallax.factor", "layer3_parallax.invert"], + "shaderOption": "o_layer3_o_useDepthMap" } - }, + }, { // Maps 2D scale, offset, and rotate properties into a float3x3 transform matrix. "type": "Transform2D", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli index d2314efefe..c20a90c00b 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -15,6 +15,7 @@ #include #include #include +#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" @@ -42,9 +43,19 @@ COMMON_SRG_INPUTS_PARALLAX(prefix) ShaderResourceGroup MaterialSrg : SRG_PerMaterial { - Texture2D m_blendMaskTexture; + Texture2D m_blendMaskTexture; uint m_blendMaskUvIndex; + // When parallax mapping is used, these limit the heightmap intersection search range to the narrowest band possible, to give the best quality result. + // These are also support other calculations related to displacement-based blending, even when parallax is not used. + float m_displacementMin; // The lowest displacement value possible from all layers combined (negative values are below the surface) + float m_displacementMax; // The highest displacement value possible from all layers combined (negative values are below the surface) + + // When displacement-based blending is used, this is the height range where the surface properties of different layers will be blended together. + // We use an absolute value rather than a relative factor because disconnecting this property from the influence of other properties makes per-layer + // displacement adjustments feel more natural if they don't impact the blend distance. + float m_displacementBlendDistance; + // Auto-generate material SRG fields for common inputs for each layer DEFINE_LAYER_SRG_INPUTS(m_layer1_) DEFINE_LAYER_SRG_INPUTS(m_layer2_) @@ -61,10 +72,6 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial uint m_parallaxUvIndex; - // These are used to limit the heightmap intersection search range to the narrowest band possible, to give the best quality result. - float m_displacementMin; // The lowest displacement value possible from all layers combined - float m_displacementMax; // The highest displacement value possible from all layers combined - float3x3 m_uvMatrix; float4 m_pad4; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed. float3x3 m_uvMatrixInverse; @@ -98,11 +105,11 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial option bool o_layer2_enabled; option bool o_layer3_enabled; -enum class DebugDrawMode { None, BlendSource, DepthMaps }; +enum class DebugDrawMode { None, BlendMask, Displacement, FinalBlendWeights }; option DebugDrawMode o_debugDrawMode; -enum class BlendMaskSource { TextureMap, VertexColors, Fallback }; -option BlendMaskSource o_blendSource; +enum class LayerBlendSource { BlendMaskTexture, BlendMaskVertexColors, Displacement, Displacement_With_BlendMaskTexture, Displacement_With_BlendMaskVertexColors, Fallback }; +option LayerBlendSource o_layerBlendSource; // Indicates whether the vertex input struct's "m_optional_blendMask" is bound. If false, it is not safe to read from m_optional_blendMask. // This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_blendMask. @@ -111,53 +118,83 @@ option BlendMaskSource o_blendSource; option bool o_blendMask_isBound; // ------ Blend Utilities ---------------------------------------- - // This is mainly used to pass extra data to the GetDepth callback function during the parallax depth search. // But since we have it, we use it in some other functions as well rather than passing it around. static float3 s_blendMaskFromVertexStream; -//! Returns the BlendMaskSource that will actually be used when rendering (not necessarily the same BlendMaskSource specified by the user) -BlendMaskSource GetFinalBlendMaskSource() +// TODO: Consider storing the result of GetFinalLayerBlendSource() in a static similar to s_blendMaskFromVertexStream. That might give better performance when variants aren't used. + +//! Returns the LayerBlendSource that will actually be used when rendering (not necessarily the same LayerBlendSource specified by the user) +LayerBlendSource GetFinalLayerBlendSource() { - if(o_blendSource == BlendMaskSource::TextureMap) + if(o_layerBlendSource == LayerBlendSource::BlendMaskTexture) + { + return o_layerBlendSource; + } + else if(o_layerBlendSource == LayerBlendSource::BlendMaskVertexColors) + { + if(o_blendMask_isBound) + { + return o_layerBlendSource; + } + else + { + return LayerBlendSource::Fallback; + } + } + else if(o_layerBlendSource == LayerBlendSource::Displacement) + { + return o_layerBlendSource; + } + else if(o_layerBlendSource == LayerBlendSource::Displacement_With_BlendMaskTexture) { - return BlendMaskSource::TextureMap; + return o_layerBlendSource; } - else if(o_blendSource == BlendMaskSource::VertexColors) + else if(o_layerBlendSource == LayerBlendSource::Displacement_With_BlendMaskVertexColors) { if(o_blendMask_isBound) { - return BlendMaskSource::VertexColors; + return o_layerBlendSource; } else { - return BlendMaskSource::TextureMap; + return LayerBlendSource::Displacement; } } else { - return BlendMaskSource::Fallback; + return LayerBlendSource::Fallback; } } -//! Return the raw blend source values directly from the blend mask or vertex colors, depending on the available data and configuration. +//! Return the applicable blend mask values from the blend mask texture or vertex colors, and filters out any that don't apply. //! layer1 is an implicit base layer -//! layer2 is weighted by r -//! layer3 is weighted by g +//! layer2 mask is in the r channel +//! layer3 mask is in the g channel //! b is reserved for perhaps a dedicated puddle layer -float3 GetBlendSourceValues(float2 uv) +//! @param blendSource indicates where to get the blend mask from +//! @param blendMaskUv for sampling a blend mask texture, if that's the blend source +//! @param blendMaskVertexColors the vertex color values to use for the blend mask, if that's the blend source +//! @return the blend mask values, or 0 if there is no blend mask +float3 GetApplicableBlendMaskValues(LayerBlendSource blendSource, float2 blendMaskUv, float3 blendMaskVertexColors) { float3 blendSourceValues = float3(0,0,0); if(o_layer2_enabled || o_layer3_enabled) { - switch(GetFinalBlendMaskSource()) + switch(blendSource) { - case BlendMaskSource::TextureMap: - blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, uv).rgb; + case LayerBlendSource::Displacement: + // In this case the blend mask has no effect, returning (1,1,1) disables any impact of the mask. + blendSourceValues = float3(1,1,1); + break; + case LayerBlendSource::BlendMaskTexture: + case LayerBlendSource::Displacement_With_BlendMaskTexture: + blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, blendMaskUv).rgb; break; - case BlendMaskSource::VertexColors: - blendSourceValues = s_blendMaskFromVertexStream; + case LayerBlendSource::BlendMaskVertexColors: + case LayerBlendSource::Displacement_With_BlendMaskVertexColors: + blendSourceValues = blendMaskVertexColors; break; } @@ -175,29 +212,96 @@ float3 GetBlendSourceValues(float2 uv) return blendSourceValues; } -//! Return the final blend mask values to be used for rendering, based on the available data and configuration. +//! When dealing with masks for displacement-based blending, we sometimes need to push the value below the min displacement to make it disappear. +float GetSubMinDisplacement() +{ + return MaterialSrg::m_displacementMin - 0.001; + //return MaterialSrg::m_displacementMin - max(MaterialSrg::m_displacementBlendDistance, 0.001); +} + +float3 ApplyBlendMaskToDepthValues(float3 blendMaskValues, float3 layerDepthValues, float zeroMaskDisplacement); + +//! Return the final blend weights to be used for rendering, based on the available data and configuration. +//! @param blendSource - indicates where to get the blend mask from +//! @param blendMaskValues - blend mask values as returned by GetApplicableBlendMaskValues() +//! @param layerDepthValues - the depth values for each layer, used if the blend source includes displacement. See GetLayerDepthValues(). +//! Note the blendMaskValues will not be applied here, those should have already been applied to layerDepthValues. +//! @param layerDepthBlendDistance - controls how smoothly to blend layers 2 and 3 with the base layer, when the blend source includes displacement. +//! When layers are close together their weights will be blended together, otherwise the highest layer will have the full weight. //! @return The blend weights for each layer. -//! Even though layer1 not explicitly specified in the blend source data, it is explicitly included with the returned values. +//! Even though layer1 not explicitly specified in the blend mask data, it is explicitly included with the returned values. //! layer1 = r //! layer2 = g //! layer3 = b -float3 GetBlendWeights(float2 uv) +float3 GetBlendWeights(LayerBlendSource blendSource, float3 blendMaskValues, float3 layerDepthValues, float layerDepthBlendDistance) { float3 blendWeights; if(o_layer2_enabled || o_layer3_enabled) { - float3 blendSourceValues = GetBlendSourceValues(uv); - - // Calculate blend weights such that multiplying and adding them with layer data is equivalent - // to lerping between each layer. - // final = lerp(final, layer1, blendWeights.r) - // final = lerp(final, layer2, blendWeights.g) - // final = lerp(final, layer3, blendWeights.b) + if(LayerBlendSource::Displacement == blendSource || + LayerBlendSource::Displacement_With_BlendMaskTexture == blendSource || + LayerBlendSource::Displacement_With_BlendMaskVertexColors == blendSource) + { + // Calculate the blend weights based on displacement values... + + // The inputs are depth values, but we change them to height values to make the code a bit more intuitive. + float3 layerHeightValues = -layerDepthValues; + + float highestPoint = layerHeightValues.x; + if(o_layer2_enabled) + { + highestPoint = max(highestPoint, layerHeightValues.y); + } + if(o_layer3_enabled) + { + highestPoint = max(highestPoint, layerHeightValues.z); + } + + if(layerDepthBlendDistance > 0.0001) + { + float lowestVisiblePoint = highestPoint - layerDepthBlendDistance; + blendWeights = smoothstep(lowestVisiblePoint, highestPoint, layerHeightValues); + + if(!o_layer2_enabled) + { + blendWeights.y = 0.0; + } + + if(!o_layer3_enabled) + { + blendWeights.z = 0.0; + } + } + else + { + blendWeights = float3(layerHeightValues.x >= highestPoint ? 1.0 : 0.0, + layerHeightValues.y >= highestPoint && o_layer2_enabled ? 1.0 : 0.0, + layerHeightValues.z >= highestPoint && o_layer3_enabled ? 1.0 : 0.0); + } + + // Calculate blend weights such that multiplying and adding them with layer data is equivalent + // to lerping between each layer. + // final = lerp(final, layer1, blendWeights.r) + // final = lerp(final, layer2, blendWeights.g) + // final = lerp(final, layer3, blendWeights.b) + + blendWeights.y = (1 - blendWeights.z) * blendWeights.y; + blendWeights.x = 1 - blendWeights.y - blendWeights.z; + } + else + { + // Calculate blend weights such that multiplying and adding them with layer data is equivalent + // to lerping between each layer. + // final = lerp(final, layer1, blendWeights.r) + // final = lerp(final, layer2, blendWeights.g) + // final = lerp(final, layer3, blendWeights.b) + + blendWeights.b = blendMaskValues.g; + blendWeights.g = (1.0 - blendMaskValues.g) * blendMaskValues.r; + blendWeights.r = (1.0 - blendMaskValues.g) * (1.0 - blendMaskValues.r); + } - blendWeights.b = blendSourceValues.g; - blendWeights.g = (1.0 - blendSourceValues.g) * blendSourceValues.r; - blendWeights.r = (1.0 - blendSourceValues.g) * (1.0 - blendSourceValues.r); } else { @@ -207,6 +311,38 @@ float3 GetBlendWeights(float2 uv) return blendWeights; } +float3 GetLayerDepthValues(float2 uv, float2 uv_ddx, float2 uv_ddy); + +//! Return the final blend weights to be used for rendering, based on the available data and configuration. +//! Note this will sample the displacement maps in the case of LayerBlendSource::Displacement. If you have already +//! the layer depth values, use the GetBlendWeights() overload that takes layerDepthValues instead. +float3 GetBlendWeights(LayerBlendSource blendSource, float2 uv, float3 blendMaskVertexColors) +{ + float3 layerDepthValues = float3(0,0,0); + + float3 blendMaskValues = GetApplicableBlendMaskValues(blendSource, uv, blendMaskVertexColors); + + if(blendSource == LayerBlendSource::Displacement || + blendSource == LayerBlendSource::Displacement_With_BlendMaskTexture || + blendSource == LayerBlendSource::Displacement_With_BlendMaskVertexColors) + { + bool useBlendMask = + LayerBlendSource::Displacement_With_BlendMaskTexture == blendSource || + LayerBlendSource::Displacement_With_BlendMaskVertexColors == blendSource; + + layerDepthValues = GetLayerDepthValues(uv, ddx_fine(uv), ddy_fine(uv)); + + if(useBlendMask) + { + // Unlike the GetDepth() callback used for parallax, we don't just shift the values toward GetSubMinDisplacement(), + // we shift extra to ensure that completely masked-out layers are not blended onto upper layers. + layerDepthValues = ApplyBlendMaskToDepthValues(blendMaskValues, layerDepthValues, GetSubMinDisplacement() - MaterialSrg::m_displacementBlendDistance); + } + } + + return GetBlendWeights(blendSource, blendMaskValues, layerDepthValues, MaterialSrg::m_displacementBlendDistance); +} + float BlendLayers(float layer1, float layer2, float layer3, float3 blendWeights) { return dot(float3(layer1, layer2, layer3), blendWeights); @@ -236,55 +372,127 @@ bool ShouldHandleParallaxInDepthShaders() return ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; } -// Callback function for ParallaxMapping.azsli -DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +//! Returns the depth values for each layer. +float3 GetLayerDepthValues(float2 uv, float2 uv_ddx, float2 uv_ddy) { float3 layerDepthValues = float3(0,0,0); - - if(o_layer1_o_useDepthMap) + + // layer1 { - float2 layerUv = uv; - if(MaterialSrg::m_parallaxUvIndex == 0) + if(o_layer1_o_useDepthMap) { - layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy; + float2 layerUv = uv; + if(MaterialSrg::m_parallaxUvIndex == 0) + { + layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy; + } + + layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; + layerDepthValues.r *= MaterialSrg::m_layer1_m_depthFactor; } - layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; - layerDepthValues.r *= MaterialSrg::m_layer1_m_depthFactor; layerDepthValues.r -= MaterialSrg::m_layer1_m_depthOffset; } - if(o_layer2_enabled && o_layer2_o_useDepthMap) + if(o_layer2_enabled) { - float2 layerUv = uv; - if(MaterialSrg::m_parallaxUvIndex == 0) + if(o_layer2_o_useDepthMap) { - layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy; + float2 layerUv = uv; + if(MaterialSrg::m_parallaxUvIndex == 0) + { + layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy; + } + + layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; + layerDepthValues.g *= MaterialSrg::m_layer2_m_depthFactor; } - layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; - layerDepthValues.g *= MaterialSrg::m_layer2_m_depthFactor; layerDepthValues.g -= MaterialSrg::m_layer2_m_depthOffset; + } - if(o_layer3_enabled && o_layer3_o_useDepthMap) + if(o_layer3_enabled) { - float2 layerUv = uv; - if(MaterialSrg::m_parallaxUvIndex == 0) + if(o_layer3_o_useDepthMap) { - layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy; + float2 layerUv = uv; + if(MaterialSrg::m_parallaxUvIndex == 0) + { + layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy; + } + + layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; + layerDepthValues.b *= MaterialSrg::m_layer3_m_depthFactor; } - layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; - layerDepthValues.b *= MaterialSrg::m_layer3_m_depthFactor; layerDepthValues.b -= MaterialSrg::m_layer3_m_depthOffset; + + } + + return layerDepthValues; +} + + +//! Uses a layer blend mask to further displace each layer's surface so that it disappears beyond the other surfaces. +//! Note the blend mask does not apply to the first layer, it is the implicit base layer. Layers 2 and 3 are masked by the r and g channels of the mask. +//! @param blendMaskValues layer mask values as returned by GetApplicableBlendMaskValues() +//! @param layerDepthValues layer depth values as returned by GetLayerDepthValues() +//! @param zeroMaskDisplacement the target displacement value that corresponds to a mask value of 0 +//! @return new layer depth values that have been adjusted according to the layerMaskValues +float3 ApplyBlendMaskToDepthValues(float3 blendMaskValues, float3 layerDepthValues, float zeroMaskDisplacement) +{ + if(o_layer2_enabled || o_layer3_enabled) + { + // We add to the depth value rather than lerp toward m_displacementMin to avoid squashing the topology, but instead lower it out of sight. + + if(o_layer2_enabled) + { + float dropoffRange = MaterialSrg::m_layer2_m_depthOffset - zeroMaskDisplacement; + layerDepthValues.g += dropoffRange * (1-blendMaskValues.r); + } + + if(o_layer3_enabled) + { + float dropoffRange = MaterialSrg::m_layer3_m_depthOffset - zeroMaskDisplacement; + layerDepthValues.b += dropoffRange * (1-blendMaskValues.g); + } } + + return layerDepthValues; +} + +//! Callback function for ParallaxMapping.azsli +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +{ + LayerBlendSource blendSource = GetFinalLayerBlendSource(); + + float3 layerDepthValues = GetLayerDepthValues(uv, uv_ddx, uv_ddy); - // Note, when the blend source is BlendMaskSource::VertexColors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values - // for every UV position when searching for the intersection. This leads to smearing artifacts at the transition point, but these won't be so noticeable as long as + // Note, when the blend source uses the blend mask from the vertex colors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values + // for every UV position when searching for the intersection. This leads to smearing artifacts at the transition point, but these won't be as noticeable if // you have a small depth factor relative to the size of the blend transition. - float3 blendWeights = GetBlendWeights(uv); + float3 blendMaskValues = GetApplicableBlendMaskValues(blendSource, uv, s_blendMaskFromVertexStream); + + bool useBlendMask = + LayerBlendSource::Displacement_With_BlendMaskTexture == blendSource || + LayerBlendSource::Displacement_With_BlendMaskVertexColors == blendSource; + + if(useBlendMask) + { + // Regarding GetSubMinDisplacement(), when a mask of 0 pushes the surface all the way to the bottom, we want that + // to go a little below the min so it will disappear if there is something else right at the min. + + layerDepthValues = ApplyBlendMaskToDepthValues(blendMaskValues, layerDepthValues, GetSubMinDisplacement()); + } + + // When blending the depth together, we don't use MaterialSrg::m_displacementBlendDistance. The intention is that m_displacementBlendDistance + // is for transitioning the appearance of the surface itself, but we still want a distinct change in the heightmap. If someday we want to + // support smoothly blending the depth as well, there is a bit more work to do to get it to play nice with the blend mask code in GetLayerDepthValues(). + float layerDepthBlendDistance = 0.0f; + float3 blendWeightValues = GetBlendWeights(blendSource, blendMaskValues, layerDepthValues, layerDepthBlendDistance); + + float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendWeightValues); - float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendWeights); return DepthResultAbsolute(depth); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl index 178deca8a2..cf4d53e805 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl @@ -84,7 +84,7 @@ VSDepthOutput MainVS(VSInput IN) } else { - OUT.m_blendMask = float3(1,1,1); + OUT.m_blendMask = float3(0,0,0); } return OUT; @@ -103,11 +103,11 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); + s_blendMaskFromVertexStream = IN.m_blendMask; float depth; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua new file mode 100644 index 0000000000..d2bf8f28d3 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua @@ -0,0 +1,195 @@ +-------------------------------------------------------------------------------------- +-- +-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +-- its licensors. +-- +-- For complete copyright and license terms please see the LICENSE at the root of this +-- distribution (the "License"). All use of this software is governed by the License, +-- or, if provided, by the license below or the license accompanying this file. Do not +-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- +-- +---------------------------------------------------------------------------------------------------- + +-- This functor handles general parallax properties that affect the entire material. + +function GetMaterialPropertyDependencies() + return { + "blend.blendSource", + "blend.enableLayer2", + "blend.enableLayer3", + "parallax.enable", + "layer1_parallax.textureMap", + "layer2_parallax.textureMap", + "layer3_parallax.textureMap", + "layer1_parallax.useTexture", + "layer2_parallax.useTexture", + "layer3_parallax.useTexture", + "layer1_parallax.factor", + "layer2_parallax.factor", + "layer3_parallax.factor", + "layer1_parallax.offset", + "layer2_parallax.offset", + "layer3_parallax.offset" + } +end + +function GetShaderOptionDependencies() + return {"o_parallax_feature_enabled"} +end + +-- These values must align with LayerBlendSource in StandardMultilayerPBR_Common.azsli. +LayerBlendSource_BlendMaskTexture = 0 +LayerBlendSource_BlendMaskVertexColors = 1 +LayerBlendSource_Displacement = 2 +LayerBlendSource_Displacement_With_BlendMaskTexture = 3 +LayerBlendSource_Displacement_With_BlendMaskVertexColors = 4 + +function BlendSourceUsesDisplacement(context) + local blendSource = context:GetMaterialPropertyValue_enum("blend.blendSource") + local blendSourceIncludesDisplacement = (blendSource == LayerBlendSource_Displacement or + blendSource == LayerBlendSource_Displacement_With_BlendMaskTexture or + blendSource == LayerBlendSource_Displacement_With_BlendMaskVertexColors) + return blendSourceIncludesDisplacement +end + +function IsParallaxNeededForLayer(context, layerNumber) + local enableLayer = true + if(layerNumber > 1) then -- layer 1 is always enabled, it is the implicit base layer + enableLayer = context:GetMaterialPropertyValue_bool("blend.enableLayer" .. layerNumber) + end + + if not enableLayer then + return false + end + + local parallaxGroupName = "layer" .. layerNumber .. "_parallax." + + local factor = context:GetMaterialPropertyValue_float(parallaxGroupName .. "factor") + local offset = context:GetMaterialPropertyValue_float(parallaxGroupName .. "offset") + + if factor == 0.0 and offset == 0.0 then + return false + end + + local hasTexture = nil ~= context:GetMaterialPropertyValue_Image(parallaxGroupName .. "textureMap") + local useTexture = context:GetMaterialPropertyValue_bool(parallaxGroupName .. "useTexture") + + if not hasTexture or not useTexture then + factorLayer = 0.0 + end + + if factor == 0.0 and offset == 0.0 then + return false + end + + return true +end + +-- Calculates the min and max displacement height values encompassing all enabled layers. +-- @return a table with two values {min,max}. Negative values are below the surface and positive values are above the surface. +function CalcOverallHeightRange(context) + + local heightMinMax = {nil, nil} + + local function GetMergedHeightRange(heightMinMax, offset, factor) + top = offset + bottom = offset - factor + + if(heightMinMax[1] == nil) then + heightMinMax[1] = top + else + heightMinMax[1] = math.max(heightMinMax[1], top) + end + + if(heightMinMax[0] == nil) then + heightMinMax[0] = bottom + else + heightMinMax[0] = math.min(heightMinMax[0], bottom) + end + end + + local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable") + + if(enableParallax or BlendSourceUsesDisplacement(context)) then + local hasTextureLayer1 = nil ~= context:GetMaterialPropertyValue_Image("layer1_parallax.textureMap") + local hasTextureLayer2 = nil ~= context:GetMaterialPropertyValue_Image("layer2_parallax.textureMap") + local hasTextureLayer3 = nil ~= context:GetMaterialPropertyValue_Image("layer3_parallax.textureMap") + + local useTextureLayer1 = context:GetMaterialPropertyValue_bool("layer1_parallax.useTexture") + local useTextureLayer2 = context:GetMaterialPropertyValue_bool("layer2_parallax.useTexture") + local useTextureLayer3 = context:GetMaterialPropertyValue_bool("layer3_parallax.useTexture") + + local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor") + local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor") + local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor") + + if not hasTextureLayer1 or not useTextureLayer1 then factorLayer1 = 0 end + if not hasTextureLayer2 or not useTextureLayer2 then factorLayer2 = 0 end + if not hasTextureLayer3 or not useTextureLayer3 then factorLayer3 = 0 end + + local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset") + local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset") + local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset") + + local enableLayer2 = context:GetMaterialPropertyValue_bool("blend.enableLayer2") + local enableLayer3 = context:GetMaterialPropertyValue_bool("blend.enableLayer3") + + GetMergedHeightRange(heightMinMax, offsetLayer1, factorLayer1) + if(enableLayer2) then GetMergedHeightRange(heightMinMax, offsetLayer2, factorLayer2) end + if(enableLayer3) then GetMergedHeightRange(heightMinMax, offsetLayer3, factorLayer3) end + + else + heightMinMax = {0,0} + end + + return heightMinMax +end + +function Process(context) + local heightMinMax = CalcOverallHeightRange(context) + context:SetShaderConstant_float("m_displacementMin", heightMinMax[0]) + context:SetShaderConstant_float("m_displacementMax", heightMinMax[1]) + + local parallaxFeatureEnabled = context:GetMaterialPropertyValue_bool("parallax.enable") + if parallaxFeatureEnabled then + if not IsParallaxNeededForLayer(context, 1) and + not IsParallaxNeededForLayer(context, 2) and + not IsParallaxNeededForLayer(context, 3) then + parallaxFeatureEnabled = false + end + end + + context:SetShaderOptionValue_bool("o_parallax_feature_enabled", parallaxFeatureEnabled) +end + +function ProcessEditor(context) + local enableParallaxSettings = context:GetMaterialPropertyValue_bool("parallax.enable") + + local parallaxSettingVisibility = MaterialPropertyVisibility_Enabled + if(not enableParallaxSettings) then + parallaxSettingVisibility = MaterialPropertyVisibility_Hidden + end + + context:SetMaterialPropertyVisibility("parallax.parallaxUv", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.algorithm", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.quality", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.pdo", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.showClipping", parallaxSettingVisibility) + + if BlendSourceUsesDisplacement(context) then + context:SetMaterialPropertyVisibility("blend.displacementBlendDistance", MaterialPropertyVisibility_Enabled) + else + context:SetMaterialPropertyVisibility("blend.displacementBlendDistance", MaterialPropertyVisibility_Hidden) + end + + -- We set the displacementBlendDistance slider range to match the range of displacement, so the slider will feel good + -- regardless of how big the overall displacement is. Using a soft max allows the user to exceed the limit if desired, + -- but the main reason for the *soft* max is to avoid impacting the value of displacementBlendDistance which could + -- otherwise lead to edge cases. + local heightMinMax = CalcOverallHeightRange(context) + local totalDisplacementRange = heightMinMax[1] - heightMinMax[0] + context:SetMaterialPropertySoftMaxValue_float("blend.displacementBlendDistance", math.max(totalDisplacementRange, 0.001)) +end + diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 580e5206ab..19efc60a1e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -116,7 +116,7 @@ VSOutput ForwardPassVS(VSInput IN) } else { - OUT.m_blendMask = float3(1,1,1); + OUT.m_blendMask = float3(0,0,0); } // Shadow coords will be calculated in the pixel shader in this case @@ -310,38 +310,45 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float s_blendMaskFromVertexStream = IN.m_blendMask; - // ------- Tangents & Bitangets ------- + LayerBlendSource blendSource = GetFinalLayerBlendSource(); - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_layer1_o_normal_useTexture && MaterialSrg::m_layer1_m_normalMapUvIndex != 0) - || (o_layer2_o_normal_useTexture && MaterialSrg::m_layer2_m_normalMapUvIndex != 0) - || (o_layer3_o_normal_useTexture && MaterialSrg::m_layer3_m_normalMapUvIndex != 0) - || (o_layer1_o_clearCoat_normal_useTexture && MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex != 0) - || (o_layer2_o_clearCoat_normal_useTexture && MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex != 0) - || (o_layer3_o_clearCoat_normal_useTexture && MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex != 0) + // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) + || o_layer1_o_normal_useTexture + || o_layer2_o_normal_useTexture + || o_layer3_o_normal_useTexture + || o_layer1_o_clearCoat_normal_useTexture + || o_layer2_o_clearCoat_normal_useTexture + || o_layer3_o_clearCoat_normal_useTexture ) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Debug Modes ------- - if(o_debugDrawMode == DebugDrawMode::BlendSource) + if(o_debugDrawMode == DebugDrawMode::BlendMask) + { + float3 blendMaskValues = GetApplicableBlendMaskValues(blendSource, IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); + return DebugOutput(blendMaskValues); + } + + if(o_debugDrawMode == DebugDrawMode::Displacement) { - float3 blendSource = GetBlendSourceValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); - return DebugOutput(blendSource); + float startDepth = -MaterialSrg::m_displacementMax; + float stopDepth = -MaterialSrg::m_displacementMin; + float depth = GetNormalizedDepth(startDepth, stopDepth, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); + float height = 1 - saturate(depth); + return DebugOutput(float3(height,height,height)); } - if(o_debugDrawMode == DebugDrawMode::DepthMaps) + if(o_debugDrawMode == DebugDrawMode::FinalBlendWeights) { - float depth = GetNormalizedDepth(-MaterialSrg::m_displacementMax, -MaterialSrg::m_displacementMin, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); - return DebugOutput(float3(depth,depth,depth)); + float3 blendWeights = GetBlendWeights(blendSource, IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); + return DebugOutput(blendWeights); } // ------- Parallax ------- @@ -373,21 +380,27 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Calculate Layer Blend Mask Values ------- // Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax. - float3 blendWeights = GetBlendWeights(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); + float3 blendWeights = GetBlendWeights(blendSource, IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); // ------- Layer 1 (base layer) ----------- ProcessedMaterialInputs lightingInputLayer1; + if(blendWeights.r > 0) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer1_, o_layer1_, blendWeights.r) lightingInputLayer1 = ProcessStandardMaterialInputs(inputs); } + else + { + lightingInputLayer1.InitializeToZero(); + blendWeights.r = 0; + } // ----------- Layer 2 ----------- ProcessedMaterialInputs lightingInputLayer2; - if(o_layer2_enabled) + if(o_layer2_enabled && blendWeights.g > 0) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer2_, o_layer2_, blendWeights.g) @@ -396,12 +409,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float else { lightingInputLayer2.InitializeToZero(); + blendWeights.g = 0; } // ----------- Layer 3 ----------- ProcessedMaterialInputs lightingInputLayer3; - if(o_layer3_enabled) + if(o_layer3_enabled && blendWeights.b > 0) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer3_, o_layer3_, blendWeights.b) @@ -410,6 +424,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float else { lightingInputLayer3.InitializeToZero(); + blendWeights.b = 0; } // ------- Combine all layers --------- @@ -420,12 +435,16 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Combine Normals --------- - float3 normalTS = lightingInputLayer1.m_normalTS; - if(o_layer2_enabled) + float3 normalTS = float3(0,0,1); + if(blendWeights.r > 0) + { + normalTS = lightingInputLayer1.m_normalTS; + } + if(o_layer2_enabled && blendWeights.g > 0) { normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer2.m_normalTS); } - if(o_layer3_enabled) + if(o_layer3_enabled && blendWeights.b > 0) { normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua deleted file mode 100644 index 8880a4b842..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua +++ /dev/null @@ -1,97 +0,0 @@ --------------------------------------------------------------------------------------- --- --- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or --- its licensors. --- --- For complete copyright and license terms please see the LICENSE at the root of this --- distribution (the "License"). All use of this software is governed by the License, --- or, if provided, by the license below or the license accompanying this file. Do not --- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- --- ----------------------------------------------------------------------------------------------------- - --- This functor handles general parallax properties that affect the entire material. - -function GetMaterialPropertyDependencies() - return { - "parallax.enable", - "layer1_parallax.enable", - "layer2_parallax.enable", - "layer3_parallax.enable", - "layer1_parallax.factor", - "layer2_parallax.factor", - "layer3_parallax.factor", - "layer1_parallax.offset", - "layer2_parallax.offset", - "layer3_parallax.offset" - } -end - -function GetShaderOptionDependencies() - return {"o_parallax_feature_enabled"} -end - -function MergeRange(heightMinMax, offset, factor) - top = offset - bottom = offset - factor - - if(heightMinMax[1] == nil) then - heightMinMax[1] = top - else - heightMinMax[1] = math.max(heightMinMax[1], top) - end - - if(heightMinMax[0] == nil) then - heightMinMax[0] = bottom - else - heightMinMax[0] = math.min(heightMinMax[0], bottom) - end -end - -function Process(context) - local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable") - local enable1 = context:GetMaterialPropertyValue_bool("layer1_parallax.enable") - local enable2 = context:GetMaterialPropertyValue_bool("layer2_parallax.enable") - local enable3 = context:GetMaterialPropertyValue_bool("layer3_parallax.enable") - enableParallax = enableParallax and (enable1 or enable2 or enable3) - context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enableParallax) - - if(enableParallax) then - local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor") - local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor") - local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor") - - local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset") - local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset") - local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset") - - local heightMinMax = {nil, nil} - if(enable1) then MergeRange(heightMinMax, offsetLayer1, factorLayer1) end - if(enable2) then MergeRange(heightMinMax, offsetLayer2, factorLayer2) end - if(enable3) then MergeRange(heightMinMax, offsetLayer3, factorLayer3) end - - if(heightMinMax[1] - heightMinMax[0] < 0.0001) then - context:SetShaderOptionValue_bool("o_parallax_feature_enabled", false) - else - context:SetShaderConstant_float("m_displacementMin", heightMinMax[0]) - context:SetShaderConstant_float("m_displacementMax", heightMinMax[1]) - end - end -end - -function ProcessEditor(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - - local visibility = MaterialPropertyVisibility_Enabled - if(not enable) then - visibility = MaterialPropertyVisibility_Hidden - end - - context:SetMaterialPropertyVisibility("parallax.parallaxUv", visibility) - context:SetMaterialPropertyVisibility("parallax.algorithm", visibility) - context:SetMaterialPropertyVisibility("parallax.quality", visibility) - context:SetMaterialPropertyVisibility("parallax.pdo", visibility) - context:SetMaterialPropertyVisibility("parallax.showClipping", visibility) -end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua deleted file mode 100644 index bd56292229..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua +++ /dev/null @@ -1,49 +0,0 @@ --------------------------------------------------------------------------------------- --- --- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or --- its licensors. --- --- For complete copyright and license terms please see the LICENSE at the root of this --- distribution (the "License"). All use of this software is governed by the License, --- or, if provided, by the license below or the license accompanying this file. Do not --- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- --- ----------------------------------------------------------------------------------------------------- - --- This functor handles parallax properties that are specific to a single layer. - -function GetMaterialPropertyDependencies() - return {"parallax.enable", "parallax.textureMap"} -end - -function GetShaderOptionDependencies() - return {"o_useDepthMap"} -end - -function Process(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") - context:SetShaderOptionValue_bool("o_useDepthMap", enable and textureMap ~= nil) -end - -function ProcessEditor(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - - if enable then - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Enabled) - else - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Hidden) - end - - local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") - local visibility = MaterialPropertyVisibility_Enabled - if(not enable or textureMap == nil) then - visibility = MaterialPropertyVisibility_Hidden - end - - context:SetMaterialPropertyVisibility("parallax.factor", visibility) - context:SetMaterialPropertyVisibility("parallax.offset", visibility) - context:SetMaterialPropertyVisibility("parallax.invert", visibility) -end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua index 69df610ab2..778edeea18 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua @@ -24,7 +24,7 @@ function Process(context) local shadowMap = context:GetShaderByTag("Shadowmap") local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS") local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS") - local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS") + local shadowMapWithPS = context:GetShaderByTag("Shadowmap_WithPS") local forwardPass = context:GetShaderByTag("ForwardPass") local shadingAffectsDepth = parallaxEnabled and parallaxPdoEnabled; @@ -34,6 +34,6 @@ function Process(context) forwardPassEDS:SetEnabled(not shadingAffectsDepth) depthPassWithPS:SetEnabled(shadingAffectsDepth) - shadowMapWitPS:SetEnabled(shadingAffectsDepth) + shadowMapWithPS:SetEnabled(shadingAffectsDepth) forwardPass:SetEnabled(shadingAffectsDepth) end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl index 02ccd78735..6c80dc874b 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl @@ -83,7 +83,7 @@ VertexOutput MainVS(VertexInput IN) } else { - OUT.m_blendMask = float3(1,1,1); + OUT.m_blendMask = float3(0,0,0); } return OUT; @@ -102,10 +102,10 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); s_blendMaskFromVertexStream = IN.m_blendMask; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 2b0d09bc5c..ca3e5e1ce4 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -601,7 +601,7 @@ "displayName": "Opacity Mode", "description": "Opacity mode for this texture.", "type": "Enum", - "enumValues": [ "Opaque", "Cutout", "Blended" ], + "enumValues": [ "Opaque", "Cutout", "Blended", "TintedTransparent" ], "defaultValue": "Opaque", "connection": { "type": "ShaderOption", @@ -669,12 +669,12 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", "displayName": "Tile U", - "description": "Scales texture coordinates in V.", + "description": "Scales texture coordinates in U.", "type": "float", "defaultValue": 1.0, "step": 0.1 @@ -879,13 +879,6 @@ } ], "parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", "displayName": "Texture Map", @@ -896,6 +889,13 @@ "id": "m_depthMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture map.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", @@ -913,7 +913,7 @@ "displayName": "Heightmap Scale", "description": "The total height of the heightmap in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { @@ -951,7 +951,7 @@ "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", "enumValues": [ "Basic", "Steep", "POM", "Relief", "ContactRefinement" ], - "defaultValue": "Basic", + "defaultValue": "POM", "connection": { "type": "ShaderOption", "id": "o_parallax_algorithm" @@ -1139,7 +1139,7 @@ "type": "float", "defaultValue": 6.0, "min": 0.0, - "softMax": 20.0 + "softMax": 20.0 }, { "id": "transmissionDistortion", @@ -1170,7 +1170,7 @@ } ], "irradiance": [ - // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader + // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader { "id": "color", "displayName": "Color", @@ -1277,18 +1277,18 @@ "type": "HandleSubsurfaceScatteringParameters", "args": { "mode": "subsurfaceScattering.transmissionMode", - "scale" : "subsurfaceScattering.transmissionScale", - "power" : "subsurfaceScattering.transmissionPower", - "distortion" : "subsurfaceScattering.transmissionDistortion", - "attenuation" : "subsurfaceScattering.transmissionAttenuation", - "tintColor" : "subsurfaceScattering.transmissionTint", - "thickness" : "subsurfaceScattering.thickness", + "scale": "subsurfaceScattering.transmissionScale", + "power": "subsurfaceScattering.transmissionPower", + "distortion": "subsurfaceScattering.transmissionDistortion", + "attenuation": "subsurfaceScattering.transmissionAttenuation", + "tintColor": "subsurfaceScattering.transmissionTint", + "thickness": "subsurfaceScattering.thickness", "enabled": "subsurfaceScattering.enableSubsurfaceScattering", - "scatterDistanceColor" : "subsurfaceScattering.scatterColor", - "scatterDistanceIntensity" : "subsurfaceScattering.scatterDistance", - "scatterDistanceShaderInput" : "m_scatterDistance", - "parametersShaderInput" : "m_transmissionParams", - "tintThickenssShaderInput" : "m_transmissionTintThickness" + "scatterDistanceColor": "subsurfaceScattering.scatterColor", + "scatterDistanceIntensity": "subsurfaceScattering.scatterDistance", + "scatterDistanceShaderInput": "m_scatterDistance", + "parametersShaderInput": "m_transmissionParams", + "tintThickenssShaderInput": "m_transmissionTintThickness" } }, { @@ -1387,15 +1387,6 @@ "file": "StandardPBR_HandleOpacityDoubleSided.lua" } }, - { - "type": "OverrideDrawList", - "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "Blended", - "shaderIndex": 1, - "drawList": "transparent" - } - }, { "type": "Lua", "args": { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl index 28708c12d5..afc93f060e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl @@ -78,10 +78,10 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index bf1ec96b79..10fa3814f3 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -110,19 +110,12 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC) { // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) - || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) - ) + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture)) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- @@ -301,6 +294,24 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular } + else if (o_opacity_mode == OpacityMode::TintedTransparent) + { + // See OpacityMode::Blended above for the basic method. TintedTransparent adds onto the above concept by supporting + // colored alpha. This is currently a very basic calculation that uses the baseColor as a multiplier with strength + // determined by the alpha. We'll modify this later to be more physically accurate and allow surface depth, + // absorption, and interior color to be specified. + // + // The technique uses dual source blending to allow two separate sources to be part of the blending equation + // even though ultimately only a single render target is being written to. m_diffuseColor is render target 0 and + // m_specularColor render target 1, and the blend mode is (dest * source1color) + (source * 1.0). + // + // This means that m_specularColor.rgb (source 1) is multiplied against the destination, then + // m_diffuseColor.rgb (source) is added to that, and the final result is stored in render target 0. + + lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse + lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular + lightingOutput.m_specularColor.rgb = baseColor * (1.0 - lightingOutput.m_diffuseColor.w); + } else { // Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961 diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua index 541b1ac1ce..20d3ee47ae 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua @@ -60,10 +60,13 @@ function Process(context) if(opacityMode == OpacityMode_Blended) then ConfigureAlphaBlending(context:GetShader(ForwardPassIndex)) + context:GetShader(ForwardPassIndex):SetDrawListTagOverride("transparent") elseif(opacityMode == OpacityMode_TintedTransparent) then ConfigureDualSourceBlending(context:GetShader(ForwardPassIndex)) + context:GetShader(ForwardPassIndex):SetDrawListTagOverride("transparent") else ResetAlphaBlending(context:GetShader(ForwardPassIndex)) + context:GetShader(ForwardPassIndex):SetDrawListTagOverride("") -- reset to default draw list end end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua index e6689da327..53d6334f28 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua @@ -13,7 +13,7 @@ ---------------------------------------------------------------------------------------------------- function GetMaterialPropertyDependencies() - return {"parallax.enable", "parallax.textureMap"} + return {"parallax.textureMap", "parallax.useTexture"} end function GetShaderOptionDependencies() @@ -21,27 +21,31 @@ function GetShaderOptionDependencies() end function Process(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") + local useTexture = context:GetMaterialPropertyValue_bool("parallax.useTexture") + local enable = textureMap ~= nil and useTexture context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enable) - context:SetShaderOptionValue_bool("o_useDepthMap", enable and textureMap ~= nil) + context:SetShaderOptionValue_bool("o_useDepthMap", enable) end function ProcessEditor(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") + local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") - if enable then - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Enabled) + if textureMap ~= nil then + context:SetMaterialPropertyVisibility("parallax.useTexture", MaterialPropertyVisibility_Enabled) else - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Hidden) + context:SetMaterialPropertyVisibility("parallax.useTexture", MaterialPropertyVisibility_Hidden) end - local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") + local useTexture = context:GetMaterialPropertyValue_bool("parallax.useTexture") + local visibility = MaterialPropertyVisibility_Enabled - if(not enable or textureMap == nil) then + if(textureMap == nil) then visibility = MaterialPropertyVisibility_Hidden + elseif not useTexture then + visibility = MaterialPropertyVisibility_Disabled end - + context:SetMaterialPropertyVisibility("parallax.factor", visibility) context:SetMaterialPropertyVisibility("parallax.offset", visibility) context:SetMaterialPropertyVisibility("parallax.showClipping", visibility) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua index 2733713122..b245fde3df 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua @@ -13,7 +13,7 @@ ---------------------------------------------------------------------------------------------------- function GetMaterialPropertyDependencies() - return {"opacity.mode", "parallax.enable", "parallax.pdo"} + return {"opacity.mode", "parallax.textureMap", "parallax.useTexture", "parallax.pdo"} end OpacityMode_Opaque = 0 @@ -21,41 +21,61 @@ OpacityMode_Cutout = 1 OpacityMode_Blended = 2 OpacityMode_TintedTransparent = 3 +function TryGetShaderByTag(context, shaderTag) + if context:HasShaderWithTag(shaderTag) then + return context:GetShaderByTag(shaderTag) + else + return nil + end +end + +function TrySetShaderEnabled(shader, enabled) + if shader then + shader:SetEnabled(enabled) + end +end + function Process(context) local opacityMode = context:GetMaterialPropertyValue_enum("opacity.mode") - local parallaxEnabled = context:GetMaterialPropertyValue_bool("parallax.enable") + local displacementMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") + local useDisplacementMap = context:GetMaterialPropertyValue_bool("parallax.useTexture") + local parallaxEnabled = displacementMap ~= nil and useDisplacementMap local parallaxPdoEnabled = context:GetMaterialPropertyValue_bool("parallax.pdo") local depthPass = context:GetShaderByTag("DepthPass") local shadowMap = context:GetShaderByTag("Shadowmap") local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS") - local lowEndForwardEDS = context:GetShaderByTag("LowEndForward_EDS") local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS") - local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS") + local shadowMapWithPS = context:GetShaderByTag("Shadowmap_WithPS") local forwardPass = context:GetShaderByTag("ForwardPass") - local lowEndForward = context:GetShaderByTag("LowEndForward") + + -- Use TryGetShaderByTag because these shaders only exist in StandardPBR but this script is also used for EnhancedPBR + local lowEndForwardEDS = TryGetShaderByTag(context, "LowEndForward_EDS") + local lowEndForward = TryGetShaderByTag(context, "LowEndForward") if parallaxEnabled and parallaxPdoEnabled then depthPass:SetEnabled(false) shadowMap:SetEnabled(false) forwardPassEDS:SetEnabled(false) - lowEndForwardEDS:SetEnabled(false) depthPassWithPS:SetEnabled(true) - shadowMapWitPS:SetEnabled(true) + shadowMapWithPS:SetEnabled(true) forwardPass:SetEnabled(true) - lowEndForward:SetEnabled(true) + + TrySetShaderEnabled(lowEndForwardEDS, false) + TrySetShaderEnabled(lowEndForward, true) else depthPass:SetEnabled(opacityMode == OpacityMode_Opaque) shadowMap:SetEnabled(opacityMode == OpacityMode_Opaque) forwardPassEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) - lowEndForwardEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) depthPassWithPS:SetEnabled(opacityMode == OpacityMode_Cutout) - shadowMapWitPS:SetEnabled(opacityMode == OpacityMode_Cutout) + shadowMapWithPS:SetEnabled(opacityMode == OpacityMode_Cutout) forwardPass:SetEnabled(opacityMode == OpacityMode_Cutout) - lowEndForward:SetEnabled(opacityMode == OpacityMode_Cutout) + + TrySetShaderEnabled(lowEndForwardEDS, (opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) + TrySetShaderEnabled(lowEndForward, opacityMode == OpacityMode_Cutout) end context:GetShaderByTag("DepthPassTransparentMin"):SetEnabled((opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl index 7f24b29700..533df3bb92 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl @@ -81,10 +81,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { static const float ShadowMapDepthBias = 0.000001; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo index 53dbfb2623..72a7948174 100644 --- a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo +++ b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo index 3ae9447621..86d698d24e 100644 --- a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo +++ b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo index da00149fb0..16cb0dd668 100644 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo +++ b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index f1d8fa81be..841cdf67c8 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -33,11 +33,11 @@ set(FILES Materials/Types/StandardMultilayerPBR_Common.azsli Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.shader + Materials/Types/StandardMultilayerPBR_Displacement.lua Materials/Types/StandardMultilayerPBR_ForwardPass.azsl Materials/Types/StandardMultilayerPBR_ForwardPass.shader Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader - Materials/Types/StandardMultilayerPBR_Parallax.lua - Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua + Materials/Types/StandardMultilayerPBR_LayerEnable.lua Materials/Types/StandardMultilayerPBR_ShaderEnable.lua Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.shader diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h index 15b416597d..73ce175d99 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h @@ -14,6 +14,9 @@ #include #include +#include +#include +#include namespace AZ { @@ -23,6 +26,57 @@ namespace AZ using DiffuseProbeGridHandle = AZStd::shared_ptr; + enum class DiffuseProbeGridMode : uint8_t + { + RealTime, + Baked, + AutoSelect + }; + + enum class DiffuseProbeGridTextureNotificationType + { + None, + Ready, + Error + }; + + struct DiffuseProbeGridTexture + { + const AZStd::shared_ptr> m_data; + RHI::Format m_format; + RHI::Size m_size; + }; + + static const char* DiffuseProbeGridIrradianceFileName = "Irradiance_lutrgba16.dds"; + static const char* DiffuseProbeGridDistanceFileName = "Distance_lutrg32f.dds"; + static const char* DiffuseProbeGridRelocationFileName = "Relocation_lutrgba16f.dds"; + static const char* DiffuseProbeGridClassificationFileName = "Classification_lutr32f.dds"; + + using DiffuseProbeGridBakeTexturesCallback = AZStd::function; + + struct DiffuseProbeGridBakedTextures + { + // irradiance and distance images can be used directly + Data::Instance m_irradianceImage; + AZStd::string m_irradianceImageRelativePath; + + Data::Instance m_distanceImage; + AZStd::string m_distanceImageRelativePath; + + // relocation and classification images need to be recreated as RW textures + RHI::ImageDescriptor m_relocationImageDescriptor; + AZStd::array_view m_relocationImageData; + AZStd::string m_relocationImageRelativePath; + + RHI::ImageDescriptor m_classificationImageDescriptor; + AZStd::array_view m_classificationImageData; + AZStd::string m_classificationImageRelativePath; + }; + // DiffuseProbeGridFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom class DiffuseProbeGridFeatureProcessorInterface : public RPI::FeatureProcessor @@ -44,6 +98,29 @@ namespace AZ virtual void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) = 0; virtual void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) = 0; virtual void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) = 0; + virtual void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) = 0; + virtual void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) = 0; + + virtual void BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) = 0; + + // check for and retrieve a new baked texture asset (does not apply to hot-reloaded assets, only initial bakes) + virtual bool CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) = 0; + + virtual bool AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) = 0; + }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h index 101387205f..be761d8e7b 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -26,14 +26,15 @@ namespace AZ , public AzToolsFramework::EditorEntityVisibilityNotificationBus::Handler { public: - using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; - AZ_RTTI((EditorRenderComponentAdapter, "{AAF38BE4-EA2F-408B-9C44-63C7FBAC6B33}", TController, TRuntimeComponent, TConfiguration), BaseClass); + AZ_RTTI( + (EditorRenderComponentAdapter, "{AAF38BE4-EA2F-408B-9C44-63C7FBAC6B33}", TController, TRuntimeComponent, TConfiguration), + BaseClass); static void Reflect(AZ::ReflectContext* context); EditorRenderComponentAdapter() = default; - EditorRenderComponentAdapter(const TConfiguration& config); + explicit EditorRenderComponentAdapter(const TConfiguration& config); // AzToolsFramework::Components::EditorComponentAdapter overrides void Activate() override; @@ -50,7 +51,8 @@ namespace AZ // Convert pre-existing EditorCompnentAdapter based serialized data to EditorRenderComponentAdapter template - static bool ConvertToEditorRenderComponentAdapter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + static bool ConvertToEditorRenderComponentAdapter( + AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl index e3f7ccd44d..633ec0e0ca 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include @@ -19,11 +19,12 @@ namespace AZ { template template - bool EditorRenderComponentAdapter::ConvertToEditorRenderComponentAdapter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + bool EditorRenderComponentAdapter::ConvertToEditorRenderComponentAdapter( + AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { if (classElement.GetVersion() < TVersion) { - // Get the and remove the EditorComponentAdapter base class data that was previpously serialized + // Get the and remove the EditorComponentAdapter base class data that was previously serialized AzToolsFramework::Components::EditorComponentAdapter oldBaseClassData; if (!classElement.FindSubElementAndGetData(AZ_CRC("BaseClass1", 0xd4925735), oldBaseClassData)) @@ -41,8 +42,8 @@ namespace AZ // Replace the old base class data with EditorRenderComponentAdapter EditorRenderComponentAdapter newBaseClassData; - AZ::SerializeContext::DataElementNode& newBaseClassElement = classElement.GetSubElement( - classElement.AddElementWithData(context, "BaseClass1", newBaseClassData)); + AZ::SerializeContext::DataElementNode& newBaseClassElement = + classElement.GetSubElement(classElement.AddElementWithData(context, "BaseClass1", newBaseClassData)); // Overwrite EditorRenderComponentAdapter base class data with retrieved EditorComponentAdapter base class data if (!newBaseClassElement.RemoveElementByName(AZ_CRC("BaseClass1", 0xd4925735))) @@ -62,25 +63,24 @@ namespace AZ { BaseClass::Reflect(context); - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(0) - ; + serializeContext->Class()->Version(0); if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class( - "EditorRenderComponentAdapter", "") + // clang-format off + editContext->Class("EditorRenderComponentAdapter", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + // clang-format on } } } template - EditorRenderComponentAdapter::EditorRenderComponentAdapter(const TConfiguration& config) + EditorRenderComponentAdapter::EditorRenderComponentAdapter( + const TConfiguration& config) : BaseClass(config) { } @@ -103,7 +103,8 @@ namespace AZ bool EditorRenderComponentAdapter::IsVisible() const { bool visible = true; - AzToolsFramework::EditorEntityInfoRequestBus::EventResult(visible, this->GetEntityId(), &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsVisible); + AzToolsFramework::EditorEntityInfoRequestBus::EventResult( + visible, this->GetEntityId(), &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsVisible); return visible; } @@ -114,15 +115,16 @@ namespace AZ } template - void EditorRenderComponentAdapter::OnEntityVisibilityChanged([[maybe_unused]] bool visibility) + void EditorRenderComponentAdapter::OnEntityVisibilityChanged( + [[maybe_unused]] bool visibility) { this->m_controller.Deactivate(); if (this->ShouldActivateController()) { - this->m_controller.Activate(this->GetEntityId()); + AzFramework::Components::ComponentActivateHelper::Activate( + this->m_controller, AZ::EntityComponentIdPair(this->GetEntityId(), this->GetId())); } } - } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 089a6168b1..16f20d7178 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include @@ -114,7 +112,6 @@ namespace AZ ProjectedShadowFeatureProcessor::Reflect(context); SkyBoxFeatureProcessor::Reflect(context); UseTextureFunctor::Reflect(context); - PropertyVisibilityFunctor::Reflect(context); DrawListFunctor::Reflect(context); SubsurfaceTransmissionParameterFunctor::Reflect(context); Transform2DFunctor::Reflect(context); @@ -126,7 +123,6 @@ namespace AZ DisplayMapperPassData::Reflect(context); ConvertEmissiveUnitFunctor::Reflect(context); LookupTableAsset::Reflect(context); - ShaderEnableFunctor::Reflect(context); ReflectionProbeFeatureProcessor::Reflect(context); DecalTextureArrayFeatureProcessor::Reflect(context); SMAAFeatureProcessor::Reflect(context); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index ade4b5b592..9b40097716 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -203,7 +203,7 @@ namespace AZ if (m_shadowingLightHandle.IsValid()) { uint32_t shadowFilterMethod = m_shadowData.at(nullptr).GetData(m_shadowingLightHandle.GetIndex()).m_shadowFilterMethod; - RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(AZ::Name{"o_directional_shadow_filtering_method"}, AZ::RPI::ShaderOptionValue{shadowFilterMethod}); + RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(m_directionalShadowFilteringMethodName, AZ::RPI::ShaderOptionValue{shadowFilterMethod}); const uint32_t cascadeCount = m_shadowData.at(nullptr).GetData(m_shadowingLightHandle.GetIndex()).m_cascadeCount; ShadowProperty& property = m_shadowProperties.GetData(m_shadowingLightHandle.GetIndex()); @@ -656,6 +656,7 @@ namespace AZ CacheRenderPipelineIdsForPersistentView(); SetConfigurationToPasses(); SetCameraViewNameToPass(); + UpdateViewsOfCascadeSegments(); } void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() { @@ -1344,6 +1345,15 @@ namespace AZ } } + void DirectionalLightFeatureProcessor::UpdateViewsOfCascadeSegments() + { + if (m_shadowingLightHandle.IsValid()) + { + const uint16_t cascadeCount = GetCascadeCount(m_shadowingLightHandle); + UpdateViewsOfCascadeSegments(m_shadowingLightHandle, cascadeCount); + } + } + Aabb DirectionalLightFeatureProcessor::CalculateShadowViewAabb( LightHandle handle, const RPI::View* cameraView, diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h index a276ea3f3e..f8c00c859c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h @@ -296,6 +296,8 @@ namespace AZ //! This updates the shadowmap view. void UpdateShadowmapViews(LightHandle handle); + void UpdateViewsOfCascadeSegments(); + //! This calculate shadow view AABB. Aabb CalculateShadowViewAabb( LightHandle handle, @@ -372,6 +374,7 @@ namespace AZ uint32_t m_shadowmapIndexTableBufferNameIndex = 0; Name m_lightTypeName = Name("directional"); + Name m_directionalShadowFilteringMethodName = Name("o_directional_shadow_filtering_method"); static constexpr const char* FeatureProcessorName = "DirectionalLightFeatureProcessor"; }; } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp index 55fa633e5d..7c97af4f79 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp @@ -279,7 +279,7 @@ namespace AZ if (handle.IsValid()) { Quaternion orientation = world.GetRotation(); - Vector3 scale = world.GetScale() * nonUniformScale; + Vector3 scale = world.GetUniformScale() * nonUniformScale; SetDecalHalfSize(handle, scale); SetDecalPosition(handle, world.GetTranslation()); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index febb0b16c5..e783f3b531 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -285,7 +285,7 @@ namespace AZ { if (handle.IsValid()) { - SetDecalHalfSize(handle, nonUniformScale * world.GetScale()); + SetDecalHalfSize(handle, nonUniformScale * world.GetUniformScale()); SetDecalPosition(handle, world.GetTranslation()); SetDecalOrientation(handle, world.GetRotation()); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp index 5be1303434..a55d8fc78c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,11 @@ namespace AZ { namespace Render { + DiffuseProbeGrid::DiffuseProbeGrid() + : m_textureReadback(this) + { + } + DiffuseProbeGrid::~DiffuseProbeGrid() { m_scene->GetCullingScene()->UnregisterCullable(m_cullable); @@ -166,6 +172,84 @@ namespace AZ m_updateRenderObjectSrg = true; } + void DiffuseProbeGrid::SetMode(DiffuseProbeGridMode mode) + { + // handle auto-select + if (mode == DiffuseProbeGridMode::AutoSelect) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + m_mode = (device->GetFeatures().m_rayTracing) ? DiffuseProbeGridMode::RealTime : DiffuseProbeGridMode::Baked; + } + else + { + m_mode = mode; + } + + m_updateTextures = true; + } + + void DiffuseProbeGrid::SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures) + { + AZ_Assert(bakedTextures.m_irradianceImage.get(), "Invalid Irradiance image passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_distanceImage.get(), "Invalid Distance image passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_relocationImageData.size() > 0, "Invalid Relocation image data passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_classificationImageData.size() > 0, "Invalid Classification image data passed to SetBakedTextures"); + + m_bakedIrradianceImage = bakedTextures.m_irradianceImage; + m_bakedDistanceImage = bakedTextures.m_distanceImage; + + m_bakedIrradianceRelativePath = bakedTextures.m_irradianceImageRelativePath; + m_bakedDistanceRelativePath = bakedTextures.m_distanceImageRelativePath; + m_bakedRelocationRelativePath = bakedTextures.m_relocationImageRelativePath; + m_bakedClassificationRelativePath = bakedTextures.m_classificationImageRelativePath; + + m_bakedRelocationImageData.resize(bakedTextures.m_relocationImageData.size()); + memcpy(m_bakedRelocationImageData.data(), bakedTextures.m_relocationImageData.data(), bakedTextures.m_relocationImageData.size()); + + m_bakedClassificationImageData.resize(bakedTextures.m_classificationImageData.size()); + memcpy(m_bakedClassificationImageData.data(), bakedTextures.m_classificationImageData.data(), bakedTextures.m_classificationImageData.size()); + + // create the relocation and distance RW textures now, these are needed for shader compatibility + // (image data is copied in UpdateTextures) + { + m_bakedRelocationImage = RHI::Factory::Get().CreateImage(); + RHI::ImageInitRequest initRequest; + initRequest.m_image = m_bakedRelocationImage.get(); + initRequest.m_descriptor = RHI::ImageDescriptor::Create2D( + RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, + bakedTextures.m_relocationImageDescriptor.m_size.m_width, + bakedTextures.m_relocationImageDescriptor.m_size.m_height, + bakedTextures.m_relocationImageDescriptor.m_format); + + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Relocation image"); + } + + { + m_bakedClassificationImage = RHI::Factory::Get().CreateImage(); + RHI::ImageInitRequest initRequest; + initRequest.m_image = m_bakedClassificationImage.get(); + initRequest.m_descriptor = RHI::ImageDescriptor::Create2D( + RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, + bakedTextures.m_classificationImageDescriptor.m_size.m_width, + bakedTextures.m_classificationImageDescriptor.m_size.m_height, + bakedTextures.m_classificationImageDescriptor.m_format); + + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Classification image"); + } + + m_updateTextures = true; + } + + bool DiffuseProbeGrid::HasValidBakedTextures() const + { + return m_bakedIrradianceImage.get() && + m_bakedDistanceImage.get() && + m_bakedRelocationImage.get() && + m_bakedClassificationImage.get(); + } + uint32_t DiffuseProbeGrid::GetTotalProbeCount() const { return m_probeCountX * m_probeCountY * m_probeCountZ; @@ -188,83 +272,117 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); - // advance to the next image in the frame image array - m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount; + uint32_t probeCountX; + uint32_t probeCountY; + GetTexture2DProbeCount(probeCountX, probeCountY); - // probe raytrace + if (m_mode == DiffuseProbeGridMode::RealTime) { - uint32_t width = m_numRaysPerProbe; - uint32_t height = GetTotalProbeCount(); + // advance to the next image in the frame image array + m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount; - m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + // probe raytrace + { + uint32_t width = m_numRaysPerProbe; + uint32_t height = GetTotalProbeCount(); - RHI::ImageInitRequest request; - request.m_image = m_rayTraceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image"); - } + m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - uint32_t probeCountX; - uint32_t probeCountY; - GetTexture2DProbeCount(probeCountX, probeCountY); + RHI::ImageInitRequest request; + request.m_image = m_rayTraceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image"); + } - // probe irradiance - { - uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2); - uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2); - - m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - - RHI::ImageInitRequest request; - request.m_image = m_irradianceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat); - RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f); - request.m_optimizedClearValue = &clearValue; - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image"); - } + // probe irradiance + { + uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2); + uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2); + + m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_irradianceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat); + RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f); + request.m_optimizedClearValue = &clearValue; + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image"); + } - // probe distance - { - uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2); - uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2); + // probe distance + { + uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2); + uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2); - m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - RHI::ImageInitRequest request; - request.m_image = m_distanceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::DistanceImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image"); - } + RHI::ImageInitRequest request; + request.m_image = m_distanceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::DistanceImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image"); + } - // probe relocation - { - uint32_t width = probeCountX; - uint32_t height = probeCountY; + // probe relocation + { + uint32_t width = probeCountX; + uint32_t height = probeCountY; - m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - RHI::ImageInitRequest request; - request.m_image = m_relocationImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RelocationImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image"); - } + RHI::ImageInitRequest request; + request.m_image = m_relocationImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RelocationImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image"); + } - // probe classification + // probe classification + { + uint32_t width = probeCountX; + uint32_t height = probeCountY; + + m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_classificationImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image"); + } + } + else if (m_mode == DiffuseProbeGridMode::Baked && HasValidBakedTextures()) { - uint32_t width = probeCountX; - uint32_t height = probeCountY; + // copy the baked relocation and classification texture data to the RW textures + // (these need to be RW for shader compatibility) + RHI::ImageSubresourceRange range{ 0, 0, 0 ,0 }; + RHI::ImageSubresourceLayoutPlaced layout; - m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + // relocation + { + m_bakedRelocationImage->GetSubresourceLayouts(range, &layout, nullptr); + + RHI::ImageUpdateRequest updateRequest; + updateRequest.m_image = m_bakedRelocationImage.get(); + updateRequest.m_sourceSubresourceLayout = layout; + updateRequest.m_sourceData = m_bakedRelocationImageData.data(); + updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0); + m_renderData->m_imagePool->UpdateImageContents(updateRequest); + } - RHI::ImageInitRequest request; - request.m_image = m_classificationImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image"); + // classification + { + m_bakedClassificationImage->GetSubresourceLayouts(range, &layout, nullptr); + + RHI::ImageUpdateRequest updateRequest; + updateRequest.m_image = m_bakedClassificationImage.get(); + updateRequest.m_sourceSubresourceLayout = layout; + updateRequest.m_sourceData = m_bakedClassificationImageData.data(); + updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0); + m_renderData->m_imagePool->UpdateImageContents(updateRequest); + } } m_updateTextures = false; @@ -639,16 +757,16 @@ namespace AZ m_renderObjectSrg->SetConstant(constantIndex, m_ambientMultiplier); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeIrradiance")); - m_renderObjectSrg->SetImageView(imageIndex, m_irradianceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetIrradianceImage()->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeDistance")); - m_renderObjectSrg->SetImageView(imageIndex, m_distanceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetDistanceImage()->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeOffsets")); - m_renderObjectSrg->SetImageView(imageIndex, m_relocationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetRelocationImage()->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeStates")); - m_renderObjectSrg->SetImageView(imageIndex, m_classificationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetClassificationImage()->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get()); SetGridConstants(m_renderObjectSrg); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h index e1ca2123a5..ff6ad719cf 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace AZ { @@ -26,11 +27,12 @@ namespace AZ struct DiffuseProbeGridRenderData { + // [GFX TODO][ATOM-15650] Change DiffuseProbeGrid Classification texture to R8_UINT static const RHI::Format RayTraceImageFormat = RHI::Format::R32G32B32A32_FLOAT; static const RHI::Format IrradianceImageFormat = RHI::Format::R16G16B16A16_UNORM; static const RHI::Format DistanceImageFormat = RHI::Format::R32G32_FLOAT; static const RHI::Format RelocationImageFormat = RHI::Format::R16G16B16A16_FLOAT; - static const RHI::Format ClassificationImageFormat = RHI::Format::R8_UINT; + static const RHI::Format ClassificationImageFormat = RHI::Format::R32_FLOAT; // image pool RHI::Ptr m_imagePool; @@ -61,7 +63,7 @@ namespace AZ class DiffuseProbeGrid final { public: - DiffuseProbeGrid() = default; + DiffuseProbeGrid(); ~DiffuseProbeGrid(); void Init(RPI::Scene* scene, DiffuseProbeGridRenderData* diffuseProbeGridRenderData); @@ -96,6 +98,9 @@ namespace AZ bool GetUseDiffuseIbl() const { return m_useDiffuseIbl; } void SetUseDiffuseIbl(bool useDiffuseIbl) { m_useDiffuseIbl = useDiffuseIbl; } + DiffuseProbeGridMode GetMode() const { return m_mode; } + void SetMode(DiffuseProbeGridMode mode); + uint32_t GetNumRaysPerProbe() const { return m_numRaysPerProbe; } uint32_t GetRemainingRelocationIterations() const { return aznumeric_cast(m_remainingRelocationIterations); } @@ -133,11 +138,16 @@ namespace AZ void UpdateRenderObjectSrg(); // textures - const RHI::Ptr& GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; } - const RHI::Ptr& GetIrradianceImage() { return m_irradianceImage[m_currentImageIndex]; } - const RHI::Ptr& GetDistanceImage() { return m_distanceImage[m_currentImageIndex]; } - const RHI::Ptr& GetRelocationImage() { return m_relocationImage[m_currentImageIndex]; } - const RHI::Ptr& GetClassificationImage() { return m_classificationImage[m_currentImageIndex]; } + const RHI::Ptr GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; } + const RHI::Ptr GetIrradianceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_irradianceImage[m_currentImageIndex] : m_bakedIrradianceImage->GetRHIImage(); } + const RHI::Ptr GetDistanceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_distanceImage[m_currentImageIndex] : m_bakedDistanceImage->GetRHIImage(); } + const RHI::Ptr GetRelocationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_relocationImage[m_currentImageIndex] : m_bakedRelocationImage; } + const RHI::Ptr GetClassificationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_classificationImage[m_currentImageIndex] : m_bakedClassificationImage; } + + const AZStd::string& GetBakedIrradianceRelativePath() const { return m_bakedIrradianceRelativePath; } + const AZStd::string& GetBakedDistanceRelativePath() const { return m_bakedDistanceRelativePath; } + const AZStd::string& GetBakedRelocationRelativePath() const { return m_bakedRelocationRelativePath; } + const AZStd::string& GetBakedClassificationRelativePath() const { return m_bakedClassificationRelativePath; } // attachment Ids const RHI::AttachmentId GetRayTraceImageAttachmentId() const { return m_rayTraceImageAttachmentId; } @@ -152,6 +162,12 @@ namespace AZ bool GetIrradianceClearRequired() const { return m_irradianceClearRequired; } void ResetIrradianceClearRequired() { m_irradianceClearRequired = false; } + // texture readback + DiffuseProbeGridTextureReadback& GetTextureReadback() { return m_textureReadback; } + + void SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures); + bool HasValidBakedTextures() const; + static constexpr uint32_t DefaultNumIrradianceTexels = 6; static constexpr uint32_t DefaultNumDistanceTexels = 14; static constexpr int32_t DefaultNumRelocationIterations = 100; @@ -221,7 +237,10 @@ namespace AZ // culling RPI::Cullable m_cullable; - // textures + // grid mode (RealTime or Baked) + DiffuseProbeGridMode m_mode = DiffuseProbeGridMode::RealTime; + + // real-time textures static const uint32_t MaxTextureDimension = 8192; static const uint32_t ImageFrameCount = 3; RHI::Ptr m_rayTraceImage[ImageFrameCount]; @@ -233,6 +252,25 @@ namespace AZ bool m_updateTextures = false; bool m_irradianceClearRequired = true; + // baked textures + Data::Instance m_bakedIrradianceImage; + Data::Instance m_bakedDistanceImage; + RHI::Ptr m_bakedRelocationImage; + RHI::Ptr m_bakedClassificationImage; + + // baked texture relative paths + AZStd::string m_bakedIrradianceRelativePath; + AZStd::string m_bakedDistanceRelativePath; + AZStd::string m_bakedRelocationRelativePath; + AZStd::string m_bakedClassificationRelativePath; + + // baked texture data (only needed for the relocation and classification textures) + AZStd::vector m_bakedRelocationImageData; + AZStd::vector m_bakedClassificationImageData; + + // texture readback + DiffuseProbeGridTextureReadback m_textureReadback; + // Srgs Data::Instance m_rayTraceSrg; Data::Instance m_blendIrradianceSrg; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp index 3df13556d3..2a06dacf3f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp @@ -87,7 +87,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -111,7 +111,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -150,7 +150,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -167,7 +167,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendDistanceSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp index 4e05b8ef31..4818018ea3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp @@ -87,7 +87,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -111,7 +111,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -150,7 +150,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -167,7 +167,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendIrradianceSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp index 59549de331..8821de8a9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp @@ -100,7 +100,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -124,7 +124,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe irradiance image { @@ -153,7 +153,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see line ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -173,7 +173,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { uint32_t probeCountX; uint32_t probeCountY; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp index db85914cee..65f1c2dd5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp @@ -91,7 +91,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -115,7 +115,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -143,7 +143,7 @@ namespace AZ { RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -159,7 +159,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetClassificationSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp index 1aaa06c797..060d51d1d0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp @@ -45,6 +45,7 @@ namespace AZ RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get(); m_diffuseProbeGrids.reserve(InitialProbeGridAllocationSize); + m_realTimeDiffuseProbeGrids.reserve(InitialProbeGridAllocationSize); RHI::BufferPoolDescriptor desc; desc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device; @@ -61,7 +62,7 @@ namespace AZ // image pool { RHI::ImagePoolDescriptor imagePoolDesc; - imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite; + imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead; m_probeGridRenderData.m_imagePool = RHI::Factory::Get().CreateImagePool(); [[maybe_unused]] RHI::ResultCode result = m_probeGridRenderData.m_imagePool->Init(*rhiSystem->GetDevice(), imagePoolDesc); @@ -123,6 +124,32 @@ namespace AZ m_needUpdatePipelineStates = false; } + // check pending textures and connect bus for notifications + for (auto& notificationEntry : m_notifyTextureAssets) + { + if (notificationEntry.m_assetId.IsValid()) + { + // asset already has an assetId + continue; + } + + // query for the assetId + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, + notificationEntry.m_relativePath.c_str(), + azrtti_typeid(), + false); + + if (assetId.IsValid()) + { + notificationEntry.m_assetId = assetId; + notificationEntry.m_asset.Create(assetId, true); + Data::AssetBus::MultiHandler::BusConnect(assetId); + } + } + // if the volumes changed we need to re-sort the probe list if (m_probeGridSortRequired) { @@ -139,6 +166,7 @@ namespace AZ }; AZStd::sort(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), sortFn); + AZStd::sort(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), sortFn); m_probeGridSortRequired = false; } @@ -160,6 +188,9 @@ namespace AZ diffuseProbeGrid->SetExtents(extents); diffuseProbeGrid->SetProbeSpacing(probeSpacing); m_diffuseProbeGrids.push_back(diffuseProbeGrid); + + UpdateRealTimeList(diffuseProbeGrid); + m_probeGridSortRequired = true; return diffuseProbeGrid; @@ -169,6 +200,7 @@ namespace AZ { AZ_Assert(probeGrid.get(), "RemoveProbeGrid called with an invalid handle"); + // remove from main list auto itEntry = AZStd::find_if(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) { return (entry == probeGrid); @@ -176,6 +208,18 @@ namespace AZ AZ_Assert(itEntry != m_diffuseProbeGrids.end(), "RemoveProbeGrid called with a probe grid that is not in the probe list"); m_diffuseProbeGrids.erase(itEntry); + + // remove from side list of real-time grids + itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == probeGrid); + }); + + if (itEntry != m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.erase(itEntry); + } + probeGrid = nullptr; } @@ -247,6 +291,134 @@ namespace AZ probeGrid->SetUseDiffuseIbl(useDiffuseIbl); } + void DiffuseProbeGridFeatureProcessor::BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) + { + AZ_Assert(probeGrid.get(), "BakeTextures called with an invalid handle"); + + AddNotificationEntry(irradianceTextureRelativePath); + AddNotificationEntry(distanceTextureRelativePath); + AddNotificationEntry(relocationTextureRelativePath); + AddNotificationEntry(classificationTextureRelativePath); + + probeGrid->GetTextureReadback().BeginTextureReadback(callback); + } + + void DiffuseProbeGridFeatureProcessor::UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid) + { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::RealTime) + { + // add to side list of real-time grids + auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == diffuseProbeGrid); + }); + + if (itEntry == m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.push_back(diffuseProbeGrid); + } + } + else + { + // remove from side list of real-time grids + auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == diffuseProbeGrid); + }); + + if (itEntry != m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.erase(itEntry); + } + } + } + + void DiffuseProbeGridFeatureProcessor::AddNotificationEntry(const AZStd::string& relativePath) + { + AZStd::string assetPath = relativePath + ".streamingimage"; + + // check to see if this is an existing asset + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, + assetPath.c_str(), + azrtti_typeid(), + false); + + // We only track notifications for new texture assets, meaning assets that are created the first time a DiffuseProbeGrid is baked. + // On subsequent bakes the existing assets are automatically reloaded by the RPI since they are already known by the asset system. + if (!assetId.IsValid()) + { + m_notifyTextureAssets.push_back({ assetPath, assetId }); + } + } + + bool DiffuseProbeGridFeatureProcessor::CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) + { + for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification) + { + if (itNotification->m_relativePath == relativePath) + { + outNotificationType = itNotification->m_notificationType; + if (outNotificationType != DiffuseProbeGridTextureNotificationType::None) + { + outTextureAsset = itNotification->m_asset; + m_notifyTextureAssets.erase(itNotification); + } + + return true; + } + } + + return false; + } + + bool DiffuseProbeGridFeatureProcessor::AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) + { + for (auto& diffuseProbeGrid : m_diffuseProbeGrids) + { + if ((diffuseProbeGrid->GetBakedIrradianceRelativePath() == irradianceTextureRelativePath) || + (diffuseProbeGrid->GetBakedDistanceRelativePath() == distanceTextureRelativePath) || + (diffuseProbeGrid->GetBakedRelocationRelativePath() == relocationTextureRelativePath) || + (diffuseProbeGrid->GetBakedClassificationRelativePath() == classificationTextureRelativePath)) + { + return true; + } + } + + return false; + } + + void DiffuseProbeGridFeatureProcessor::SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) + { + AZ_Assert(probeGrid.get(), "SetMode called with an invalid handle"); + probeGrid->SetMode(mode); + + UpdateRealTimeList(probeGrid); + + m_probeGridSortRequired = true; + } + + void DiffuseProbeGridFeatureProcessor::SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) + { + AZ_Assert(probeGrid.get(), "SetBakedTextures called with an invalid handle"); + probeGrid->SetBakedTextures(bakedTextures); + } + void DiffuseProbeGridFeatureProcessor::CreateBoxMesh() { // vertex positions @@ -418,5 +590,34 @@ namespace AZ } } + void DiffuseProbeGridFeatureProcessor::HandleAssetNotification(Data::Asset asset, DiffuseProbeGridTextureNotificationType notificationType) + { + for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification) + { + if (itNotification->m_assetId == asset.GetId()) + { + // store the texture asset + itNotification->m_asset = Data::static_pointer_cast(asset); + itNotification->m_notificationType = notificationType; + + // stop notifications on this asset + Data::AssetBus::MultiHandler::BusDisconnect(itNotification->m_assetId); + + break; + } + } + } + + void DiffuseProbeGridFeatureProcessor::OnAssetReady(Data::Asset asset) + { + HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Ready); + } + + void DiffuseProbeGridFeatureProcessor::OnAssetError(Data::Asset asset) + { + AZ_Error("ReflectionProbeFeatureProcessor", false, "Failed to load cubemap [%s]", asset.GetHint().c_str()); + + HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Error); + } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h index ad36f8aafa..19e9bf1b1d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h @@ -22,6 +22,7 @@ namespace AZ //! This class manages DiffuseProbeGrids which generate diffuse global illumination class DiffuseProbeGridFeatureProcessor final : public DiffuseProbeGridFeatureProcessorInterface + , private Data::AssetBus::MultiHandler { public: AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessor, "{BCD232F9-1EBF-4D0D-A5F4-84AEC933A93C}", DiffuseProbeGridFeatureProcessorInterface); @@ -46,6 +47,27 @@ namespace AZ void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) override; void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) override; void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) override; + void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) override; + void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) override; + + void BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) override; + + bool CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) override; + + bool AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) override; // FeatureProcessor overrides void Activate() override; @@ -56,12 +78,28 @@ namespace AZ using DiffuseProbeGridVector = AZStd::vector>; DiffuseProbeGridVector& GetProbeGrids() { return m_diffuseProbeGrids; } + // retrieve the side list of probe grids that are using real-time (raytraced) mode + DiffuseProbeGridVector& GetRealTimeProbeGrids() { return m_realTimeDiffuseProbeGrids; } + private: AZ_DISABLE_COPY_MOVE(DiffuseProbeGridFeatureProcessor); // create the box vertex and index streams, which are used to render the probe volumes void CreateBoxMesh(); + // AssetBus::MultiHandler overrides... + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + + // updates the real-time list for a specific probe grid + void UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid); + + // adds a notification entry for a new asset + void AddNotificationEntry(const AZStd::string& relativePath); + + // notifies and removes the notification entry + void HandleAssetNotification(Data::Asset asset, DiffuseProbeGridTextureNotificationType notificationType); + // RPI::SceneNotificationBus::Handler overrides void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override; void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override; @@ -70,10 +108,13 @@ namespace AZ void UpdatePipelineStates(); void UpdatePasses(); - // list of diffuse probe grids + // list of all diffuse probe grids const size_t InitialProbeGridAllocationSize = 64; DiffuseProbeGridVector m_diffuseProbeGrids; + // side list of diffuse probe grids that are in real-time mode (subset of m_diffuseProbeGrids) + DiffuseProbeGridVector m_realTimeDiffuseProbeGrids; + // position structure for the box vertices struct Position { @@ -102,6 +143,17 @@ namespace AZ // indicates the the diffuse probe grid render pipeline state needs to be updated bool m_needUpdatePipelineStates = false; + + // list of texture assets that we need to check during Simulate() to see if they are ready + struct NotifyTextureAssetEntry + { + AZStd::string m_relativePath; + AZ::Data::AssetId m_assetId; + Data::Asset m_asset; + DiffuseProbeGridTextureNotificationType m_notificationType = DiffuseProbeGridTextureNotificationType::None; + }; + typedef AZStd::vector NotifyTextureAssetVector; + NotifyTextureAssetVector m_notifyTextureAssets; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp index b2b5f60660..65d71b8272 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp @@ -136,7 +136,7 @@ namespace AZ } DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -153,9 +153,9 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); - frameGraph.SetEstimatedItemCount(aznumeric_cast(diffuseProbeGridFeatureProcessor->GetProbeGrids().size())); + frameGraph.SetEstimatedItemCount(aznumeric_cast(diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().size())); - for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // TLAS { @@ -260,7 +260,7 @@ namespace AZ rayTracingFeatureProcessor->GetMeshInfoBuffer() && rayTracingFeatureProcessor->GetSubMeshCount()) { - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader // inputs (see line ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -308,7 +308,7 @@ namespace AZ m_rayTracingShaderTable) { // submit the DispatchRaysItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroups[] = { diffuseProbeGrid->GetRayTraceSrg()->GetRHIShaderResourceGroup(), diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp index 2bd6595b71..86a26f002d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp @@ -91,7 +91,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -108,7 +108,7 @@ namespace AZ // create the Relocation Srgs for each DiffuseProbeGrid, and check to see if any grids need relocation bool needRelocation = false; - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { uint32_t rayTracingDataRevision = rayTracingFeatureProcessor->GetRevision(); if (rayTracingDataRevision != m_rayTracingDataRevision) @@ -139,7 +139,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -167,7 +167,7 @@ namespace AZ { RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -187,7 +187,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetRelocationSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp index af6fce6f6a..4f9221a65f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp @@ -79,6 +79,12 @@ namespace AZ params.m_scissorState = scissor; Base::FrameBeginInternal(params); + + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) + { + // process attachment readback + diffuseProbeGrid->GetTextureReadback().FrameBegin(params); + } } void DiffuseProbeGridRenderPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) @@ -88,8 +94,21 @@ namespace AZ for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked && + !diffuseProbeGrid->HasValidBakedTextures()) + { + continue; + } + // probe irradiance image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the irradiance image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetIrradianceImageAttachmentId(), diffuseProbeGrid->GetIrradianceImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeIrradianceImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetIrradianceImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeIrradianceImageViewDescriptor; @@ -100,6 +119,13 @@ namespace AZ // probe distance image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the distance image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetDistanceImageAttachmentId(), diffuseProbeGrid->GetDistanceImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeDistanceImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetDistanceImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeDistanceImageViewDescriptor; @@ -110,6 +136,13 @@ namespace AZ // probe relocation image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the relocation image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetRelocationImageAttachmentId(), diffuseProbeGrid->GetRelocationImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeRelocationImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetRelocationImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeRelocationImageViewDescriptor; @@ -120,6 +153,13 @@ namespace AZ // probe classification image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the classification image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetClassificationImageAttachmentId(), diffuseProbeGrid->GetClassificationImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeClassificationImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetClassificationImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeClassificationImageViewDescriptor; @@ -127,6 +167,8 @@ namespace AZ frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite); } + + diffuseProbeGrid->GetTextureReadback().Update(GetName()); } Base::SetupFrameGraphDependencies(frameGraph); @@ -139,6 +181,12 @@ namespace AZ for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked && + !diffuseProbeGrid->HasValidBakedTextures()) + { + continue; + } + // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() of ShaderResourceGroupData.cpp) diffuseProbeGrid->UpdateRenderObjectSrg(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp new file mode 100644 index 0000000000..bc619cc277 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp @@ -0,0 +1,134 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace Render + { + DiffuseProbeGridTextureReadback::DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid) + : m_diffuseProbeGrid(diffuseProbeGrid) + { + } + + void DiffuseProbeGridTextureReadback::BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback) + { + AZ_Assert(m_readbackState == DiffuseProbeGridReadbackState::Idle, "DiffuseProbeGridTextureReadback is already processing a readback request"); + + m_callback = callback; + m_readbackState = DiffuseProbeGridReadbackState::Irradiance; + } + + void DiffuseProbeGridTextureReadback::Update(const AZ::Name& passName) + { + if (m_readbackState == DiffuseProbeGridReadbackState::Idle || m_readbackState == DiffuseProbeGridReadbackState::Complete) + { + return; + } + + if (m_attachmentReadback.get() && m_attachmentReadback->GetReadbackState() > RPI::AttachmentReadback::ReadbackState::Idle) + { + // still processing previous request + return; + } + + AZStd::string readbackName = AZStd::string::format("DiffuseProbeGridReadback_%s", passName.GetCStr()); + RHI::ImageDescriptor descriptor; + RHI::AttachmentId attachmentId; + RPI::AttachmentReadback::CallbackFunction callbackFunction; + + switch (m_readbackState) + { + case DiffuseProbeGridReadbackState::Irradiance: + descriptor = m_diffuseProbeGrid->GetIrradianceImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetIrradianceImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_irradianceReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Distance; + }; + break; + case DiffuseProbeGridReadbackState::Distance: + descriptor = m_diffuseProbeGrid->GetDistanceImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetDistanceImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_distanceReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Relocation; + }; + break; + case DiffuseProbeGridReadbackState::Relocation: + descriptor = m_diffuseProbeGrid->GetRelocationImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetRelocationImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_relocationReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Classification; + }; + break; + case DiffuseProbeGridReadbackState::Classification: + descriptor = m_diffuseProbeGrid->GetClassificationImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetClassificationImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_classificationReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Complete; + }; + break; + default: + AZ_Assert(false, "Unknown readback state"); + } + + m_attachmentReadback = AZStd::make_shared(AZ::RHI::ScopeId{ "DiffuseProbeGridTextureReadBack" }); + m_attachmentReadback->SetCallback(callbackFunction); + + AZ::RPI::PassAttachment passAttachment; + passAttachment.m_descriptor = descriptor; + passAttachment.m_path = attachmentId; + passAttachment.m_name = readbackName; + passAttachment.m_lifetime = RHI::AttachmentLifetimeType::Imported; + + m_attachmentReadback->ReadPassAttachment(&passAttachment, AZ::Name(readbackName)); + } + + void DiffuseProbeGridTextureReadback::FrameBegin(AZ::RPI::Pass::FramePrepareParams& params) + { + if (m_readbackState == DiffuseProbeGridReadbackState::Idle) + { + return; + } + + if (!m_attachmentReadback.get()) + { + return; + } + + if (m_readbackState == DiffuseProbeGridReadbackState::Complete) + { + // readback of all textures is complete, invoke callback and return to Idle state + m_callback( + { m_irradianceReadbackResult.m_dataBuffer, m_irradianceReadbackResult.m_imageDescriptor.m_format, m_irradianceReadbackResult.m_imageDescriptor.m_size }, + { m_distanceReadbackResult.m_dataBuffer, m_distanceReadbackResult.m_imageDescriptor.m_format, m_distanceReadbackResult.m_imageDescriptor.m_size }, + { m_relocationReadbackResult.m_dataBuffer, m_relocationReadbackResult.m_imageDescriptor.m_format, m_relocationReadbackResult.m_imageDescriptor.m_size }, + { m_classificationReadbackResult.m_dataBuffer, m_classificationReadbackResult.m_imageDescriptor.m_format, m_classificationReadbackResult.m_imageDescriptor.m_size }); + + m_readbackState = DiffuseProbeGridReadbackState::Idle; + m_attachmentReadback.reset(); + return; + } + + m_attachmentReadback->FrameBegin(params); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h new file mode 100644 index 0000000000..1becd6fb3e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h @@ -0,0 +1,60 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseProbeGrid; + + enum class DiffuseProbeGridReadbackState + { + Idle, + Irradiance, + Distance, + Relocation, + Classification, + Complete + }; + + //! This class contains functionality necessary to read back the DiffuseProbeGrid textures, which + //! allows them to be saved as assets to run the DiffuseProbeGrid in non-realtime mode. + class DiffuseProbeGridTextureReadback final + { + public: + DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid); + ~DiffuseProbeGridTextureReadback() = default; + + void BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback); + void Update(const AZ::Name& passName); + void FrameBegin(AZ::RPI::Pass::FramePrepareParams& params); + + private: + + DiffuseProbeGrid* m_diffuseProbeGrid = nullptr; + DiffuseProbeGridReadbackState m_readbackState = DiffuseProbeGridReadbackState::Idle; + AZStd::shared_ptr m_attachmentReadback; + DiffuseProbeGridBakeTexturesCallback m_callback; + + AZ::RPI::AttachmentReadback::ReadbackResult m_irradianceReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_distanceReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_relocationReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_classificationReadbackResult; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp index 32a29cf4d3..2373d0cb00 100644 --- a/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp @@ -12,11 +12,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -58,11 +56,9 @@ namespace AZ } AZ::Render::UseTextureFunctorSourceData::Reflect(context); - AZ::Render::PropertyVisibilityFunctorSourceData::Reflect(context); AZ::Render::DrawListFunctorSourceData::Reflect(context); AZ::Render::Transform2DFunctorSourceData::Reflect(context); AZ::Render::ConvertEmissiveUnitFunctorSourceData::Reflect(context); - AZ::Render::ShaderEnableFunctorSourceData::Reflect(context); AZ::Render::SubsurfaceTransmissionParameterFunctorSourceData::Reflect(context); AZ::Render::EditorLightingPreset::Reflect(context); @@ -104,11 +100,9 @@ namespace AZ } materialFunctorRegistration->RegisterMaterialFunctor("UseTexture", azrtti_typeid()); - materialFunctorRegistration->RegisterMaterialFunctor("UpdatePropertyVisibility", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("OverrideDrawList", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("Transform2D", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("ConvertEmissiveUnit", azrtti_typeid()); - materialFunctorRegistration->RegisterMaterialFunctor("ShaderEnable", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("HandleSubsurfaceScatteringParameters", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("Lua", azrtti_typeid()); diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.cpp deleted file mode 100644 index da8730f1ed..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "PropertyVisibilityFunctor.h" - -namespace AZ -{ - namespace Render - { - void PropertyVisibilityFunctor::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("triggerProperty", &Action::m_triggerPropertyIndex) - ->Field("triggerValue", &Action::m_triggerValue) - ->Field("visibility", &Action::m_visibility) - ; - serializeContext->Class() - ->Version(1) - ->Field("actions", &PropertyVisibilityFunctor::m_actions) - ->Field("affectedProperties", &PropertyVisibilityFunctor::m_affectedProperties) - ; - } - } - - void PropertyVisibilityFunctor::Process(EditorContext& context) - { - bool visibilityApplied = false; - RPI::MaterialPropertyVisibility lastAppliedVisibility; - - for (const auto& action : m_actions) - { - bool willSetVisibility = false; - if (action.m_triggerValue.Is() || action.m_triggerValue.Is() || action.m_triggerValue.Is()) - { - willSetVisibility = action.m_triggerValue == context.GetMaterialPropertyValue(action.m_triggerPropertyIndex); - } - else if (action.m_triggerValue.Is()) - { - willSetVisibility = AZ::IsClose(action.m_triggerValue.GetValue(), - context.GetMaterialPropertyValue(action.m_triggerPropertyIndex), - std::numeric_limits::epsilon()); - } - else // for types Vector2, Vector3, Vector4, Color, Image - { - AZ_Error("PropertyVisibilityFunctor", false, "Unsupported property data type as an enable property."); - } - - if (willSetVisibility) - { - visibilityApplied = true; - lastAppliedVisibility = action.m_visibility; - } - } - - if (visibilityApplied) - { - for (const auto& propertyIndex : m_affectedProperties) - { - context.SetMaterialPropertyVisibility(propertyIndex, lastAppliedVisibility); - } - } - } - - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.h deleted file mode 100644 index d1a7772fc8..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include -#include - -namespace AZ -{ - namespace Render - { - //! Materials can use this functor to control when and how to set the visibility of a group of properties. - class PropertyVisibilityFunctor final - : public RPI::MaterialFunctor - { - friend class PropertyVisibilityFunctorSourceData; - public: - AZ_RTTI(AZ::Render::PropertyVisibilityFunctor, "{2582B36F-FA7C-450F-B46A-39AAE18356A0}", RPI::MaterialFunctor); - - static void Reflect(ReflectContext* context); - - void Process(EditorContext& context) override; - - private: - struct Action - { - AZ_TYPE_INFO(AZ::Render::PropertyVisibilityFunctor::Action, "{5DF4D981-9D0C-4040-A6C5-52E1D0BD876B}"); - - RPI::MaterialPropertyIndex m_triggerPropertyIndex; //! The control property for affected properties. - RPI::MaterialPropertyValue m_triggerValue; //! The trigger value of the control property. - RPI::MaterialPropertyVisibility m_visibility; //! The visibility of affected properties when the trigger value is hit. - }; - // Material property inputs... - AZStd::vector m_actions; //! The actions that describes when and what to do with visibilities. - AZStd::vector m_affectedProperties; //! The properties that are affected by actions. - }; - - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.cpp deleted file mode 100644 index 3baf27de60..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "PropertyVisibilityFunctorSourceData.h" -#include -#include - -#include - -namespace AZ -{ - namespace Render - { - void PropertyVisibilityFunctorSourceData::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("triggerProperty", &ActionSourceData::m_triggerPropertyName) - ->Field("triggerValue", &ActionSourceData::m_triggerValue) - ->Field("visibility", &ActionSourceData::m_visibility) - ; - serializeContext->Class() - ->Version(2) - ->Field("actions", &PropertyVisibilityFunctorSourceData::m_actions) - ->Field("affectedProperties", &PropertyVisibilityFunctorSourceData::m_affectedPropertyNames) - ; - } - } - - RPI::MaterialFunctorSourceData::FunctorResult PropertyVisibilityFunctorSourceData::CreateFunctor(const EditorContext& context) const - { - using namespace RPI; - - RPI::Ptr functor = aznew PropertyVisibilityFunctor; - - functor->m_actions.reserve(m_actions.size()); - - for (const auto& actionSource : m_actions) - { - functor->m_actions.emplace_back(); - PropertyVisibilityFunctor::Action& action = functor->m_actions.back(); - action.m_triggerPropertyIndex = context.FindMaterialPropertyIndex(AZ::Name{ actionSource.m_triggerPropertyName }); - if (action.m_triggerPropertyIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, action.m_triggerPropertyIndex); - - if (!actionSource.m_triggerValue.Resolve(*context.GetMaterialPropertiesLayout(), Name{ actionSource.m_triggerPropertyName })) - { - // Error is reported in Resolve(). - return Failure(); - } - - const MaterialPropertyDescriptor* propertyDescriptor = context.GetMaterialPropertiesLayout()->GetPropertyDescriptor(action.m_triggerPropertyIndex); - // Enum type should resolve further to a unit32_t from the string source. - if (propertyDescriptor->GetDataType() == RPI::MaterialPropertyDataType::Enum) - { - if (!RPI::MaterialUtils::ResolveMaterialPropertyEnumValue( - propertyDescriptor, - Name(actionSource.m_triggerValue.GetValue().GetValue()), - action.m_triggerValue)) - { - return Failure(); - } - } - else - { - action.m_triggerValue = actionSource.m_triggerValue.GetValue(); - } - - action.m_visibility = actionSource.m_visibility; - } - - functor->m_affectedProperties.reserve(m_affectedPropertyNames.size()); - for (const AZStd::string& name : m_affectedPropertyNames) - { - RPI::MaterialPropertyIndex index = context.FindMaterialPropertyIndex(AZ::Name{ name }); - if (index.IsNull()) - { - return Failure(); - } - functor->m_affectedProperties.push_back(index); - } - - return Success(RPI::Ptr(functor)); - } - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.h deleted file mode 100644 index 6b212f78a0..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include "PropertyVisibilityFunctor.h" -#include -#include - -namespace AZ -{ - namespace Render - { - //! Builds a PropertyVisibilityFunctor. - //! Materials can use this functor to control whether a specific property group will be enabled. - class PropertyVisibilityFunctorSourceData final - : public RPI::MaterialFunctorSourceData - { - public: - AZ_RTTI(AZ::Render::PropertyVisibilityFunctorSourceData, "{B44E6929-8FFF-405F-9056-B9B811F97676}", RPI::MaterialFunctorSourceData); - - static void Reflect(ReflectContext* context); - - FunctorResult CreateFunctor(const EditorContext& context) const override; - private: - struct ActionSourceData - { - AZ_TYPE_INFO(AZ::Render::PropertyVisibilityFunctorSourceData::ActionSourceData, "{70E01DA6-0B42-4CCB-AAD0-51980DB43F62}"); - AZStd::string m_triggerPropertyName; //! The control property for affected properties. - RPI::MaterialPropertyValueSourceData m_triggerValue; //! The trigger value of the control property. - RPI::MaterialPropertyVisibility m_visibility; //! The visibility of affected properties when the trigger value is hit. - }; - // Material property inputs... - AZStd::vector m_actions; //! The actions that describes when and what to do with visibilities. - AZStd::vector m_affectedPropertyNames; //! The properties that are affected by actions. - }; - - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.cpp deleted file mode 100644 index dd602cc4ba..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "./ShaderEnableFunctor.h" -#include -#include -#include - -namespace AZ -{ - namespace Render - { - void ShaderEnableFunctor::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(4) - ->Field("opacityModeIndex", &ShaderEnableFunctor::m_opacityModeIndex) - ->Field("parallaxEnableIndex", &ShaderEnableFunctor::m_parallaxEnableIndex) - ->Field("parallaxPdoEnableIndex", &ShaderEnableFunctor::m_parallaxPdoEnableIndex) - ->Field("shadowShaderNoPSIndex", &ShaderEnableFunctor::m_shadowShaderNoPSIndex) - ->Field("shadowShaderWithPSIndex", &ShaderEnableFunctor::m_shadowShaderWithPSIndex) - ->Field("depthShaderNoPSIndex", &ShaderEnableFunctor::m_depthShaderNoPSIndex) - ->Field("depthShaderWithPSIndex", &ShaderEnableFunctor::m_depthShaderWithPSIndex) - ->Field("pbrShaderNoEdsIndex", &ShaderEnableFunctor::m_pbrShaderNoEdsIndex) - ->Field("pbrShaderWithEdsIndex", &ShaderEnableFunctor::m_pbrShaderWithEdsIndex) - ->Field("depthShaderTransparentMin", &ShaderEnableFunctor::m_depthShaderTransparentMin) - ->Field("depthShaderTransparentMax", &ShaderEnableFunctor::m_depthShaderTransparentMax) - ; - } - } - - void ShaderEnableFunctor::Process(RuntimeContext& context) - { - unsigned int opacityMode = context.GetMaterialPropertyValue(m_opacityModeIndex); - bool parallaxEnabled = context.GetMaterialPropertyValue(m_parallaxEnableIndex); - bool parallaxPdoEnabled = context.GetMaterialPropertyValue(m_parallaxPdoEnableIndex); - - if (parallaxEnabled && parallaxPdoEnabled) - { - context.SetShaderEnabled(m_depthShaderNoPSIndex, false); - context.SetShaderEnabled(m_shadowShaderNoPSIndex, false); - context.SetShaderEnabled(m_pbrShaderWithEdsIndex, false); - - context.SetShaderEnabled(m_depthShaderWithPSIndex, true); - context.SetShaderEnabled(m_shadowShaderWithPSIndex, true); - context.SetShaderEnabled(m_pbrShaderNoEdsIndex, true); - } - else - { - context.SetShaderEnabled(m_depthShaderNoPSIndex, opacityMode == OpacityMode::Opaque ); - context.SetShaderEnabled(m_shadowShaderNoPSIndex, opacityMode == OpacityMode::Opaque); - context.SetShaderEnabled(m_pbrShaderWithEdsIndex, opacityMode == OpacityMode::Opaque || opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent); - - context.SetShaderEnabled(m_depthShaderWithPSIndex, opacityMode == OpacityMode::Cutout); - context.SetShaderEnabled(m_shadowShaderWithPSIndex, opacityMode == OpacityMode::Cutout); - context.SetShaderEnabled(m_pbrShaderNoEdsIndex, opacityMode == OpacityMode::Cutout); - } - - context.SetShaderEnabled(m_depthShaderTransparentMin, opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent); - context.SetShaderEnabled(m_depthShaderTransparentMax, opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent); - } - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.h deleted file mode 100644 index ac5f31ba30..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include -#include -#include - -namespace AZ -{ - namespace Render - { - enum OpacityMode - { - Opaque = 0, - Cutout, - Blended, - TintedTransparent, - }; - - //! Select shadow and depth shader based on opacity mode and parallax state - //! Opaque: Enable shader without PS - //! Cutout or Parallax enable: Enable shader with PS - //! Blended: Disable both - //! TintedTransparent: Disable both - class ShaderEnableFunctor final - : public RPI::MaterialFunctor - { - friend class ShaderEnableFunctorSourceData; - public: - AZ_RTTI(ShaderEnableFunctor, "{2079A693-FE4F-46A7-95C0-09D88AC156D0}", RPI::MaterialFunctor); - - static void Reflect(ReflectContext* context); - - void Process(RuntimeContext& context) override; - - private: - RPI::MaterialPropertyIndex m_opacityModeIndex; - RPI::MaterialPropertyIndex m_parallaxEnableIndex; - RPI::MaterialPropertyIndex m_parallaxPdoEnableIndex; - - uint32_t m_shadowShaderNoPSIndex = -1; - uint32_t m_shadowShaderWithPSIndex = -1; - uint32_t m_depthShaderNoPSIndex = -1; - uint32_t m_depthShaderWithPSIndex = -1; - uint32_t m_pbrShaderWithEdsIndex = -1; - uint32_t m_pbrShaderNoEdsIndex = -1; - // The following are used by the light culling system to produce min/max depth bounds - uint32_t m_depthShaderTransparentMin = -1; - uint32_t m_depthShaderTransparentMax = -1; - }; - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.cpp deleted file mode 100644 index 3c16666f82..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "./ShaderEnableFunctorSourceData.h" -#include -#include - -namespace AZ -{ - namespace Render - { - void ShaderEnableFunctorSourceData::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(5) - ->Field("opacityMode", &ShaderEnableFunctorSourceData::m_opacityMode) - ->Field("parallaxEnable", &ShaderEnableFunctorSourceData::m_parallaxEnable) - ->Field("parallaxPdoEnable", &ShaderEnableFunctorSourceData::m_parallaxPdoEnable) - ->Field("shadowShaderNoPSIndex", &ShaderEnableFunctorSourceData::m_shadowShaderNoPSIndex) - ->Field("shadowShaderWithPSIndex", &ShaderEnableFunctorSourceData::m_shadowShaderWithPSIndex) - ->Field("depthShaderNoPSIndex", &ShaderEnableFunctorSourceData::m_depthShaderNoPSIndex) - ->Field("depthShaderWithPSIndex", &ShaderEnableFunctorSourceData::m_depthShaderWithPSIndex) - ->Field("pbrShaderNoEdsIndex", &ShaderEnableFunctorSourceData::m_pbrShaderNoEdsIndex) - ->Field("pbrShaderWithEdsIndex", &ShaderEnableFunctorSourceData::m_pbrShaderWithEdsIndex) - ->Field("depthShaderTransparentMin", &ShaderEnableFunctorSourceData::m_depthShaderTransparentMin) - ->Field("depthShaderTransparentMax", &ShaderEnableFunctorSourceData::m_depthShaderTransparentMax) - ; - } - } - - RPI::MaterialFunctorSourceData::FunctorResult ShaderEnableFunctorSourceData::CreateFunctor(const RuntimeContext& context) const - { - RPI::Ptr functor = aznew ShaderEnableFunctor; - - functor->m_opacityModeIndex = context.FindMaterialPropertyIndex(Name{ m_opacityMode }); - if (functor->m_opacityModeIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, functor->m_opacityModeIndex); - - functor->m_parallaxEnableIndex = context.FindMaterialPropertyIndex(Name{ m_parallaxEnable }); - if (functor->m_parallaxEnableIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, functor->m_parallaxEnableIndex); - - functor->m_parallaxPdoEnableIndex = context.FindMaterialPropertyIndex(Name{ m_parallaxPdoEnable }); - if (functor->m_parallaxPdoEnableIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, functor->m_parallaxPdoEnableIndex); - - if (!context.CheckShaderIndexValid(m_shadowShaderWithPSIndex)) - { - return Failure(); - } - functor->m_shadowShaderWithPSIndex = m_shadowShaderWithPSIndex; - - if (!context.CheckShaderIndexValid(m_shadowShaderNoPSIndex)) - { - return Failure(); - } - functor->m_shadowShaderNoPSIndex = m_shadowShaderNoPSIndex; - - if (!context.CheckShaderIndexValid(m_depthShaderWithPSIndex)) - { - return Failure(); - } - functor->m_depthShaderWithPSIndex = m_depthShaderWithPSIndex; - - if (!context.CheckShaderIndexValid(m_depthShaderNoPSIndex)) - { - return Failure(); - } - functor->m_depthShaderNoPSIndex = m_depthShaderNoPSIndex; - - if (!context.CheckShaderIndexValid(m_pbrShaderNoEdsIndex)) - { - return Failure(); - } - functor->m_pbrShaderNoEdsIndex = m_pbrShaderNoEdsIndex; - - if (!context.CheckShaderIndexValid(m_pbrShaderWithEdsIndex)) - { - return Failure(); - } - functor->m_pbrShaderWithEdsIndex = m_pbrShaderWithEdsIndex; - if (!context.CheckShaderIndexValid(m_depthShaderTransparentMin)) - { - return Failure(); - } - functor->m_depthShaderTransparentMin = m_depthShaderTransparentMin; - if (!context.CheckShaderIndexValid(m_depthShaderTransparentMax)) - { - return Failure(); - } - functor->m_depthShaderTransparentMax = m_depthShaderTransparentMax; - - return Success(RPI::Ptr(functor)); - } - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.h deleted file mode 100644 index 2d00a4a015..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include "./ShaderEnableFunctor.h" -#include - -namespace AZ -{ - namespace Render - { - class ShaderEnableFunctor; - - //! Builds a ShaderEnableFunctor - class ShaderEnableFunctorSourceData final - : public RPI::MaterialFunctorSourceData - { - public: - AZ_RTTI(ShaderEnableFunctorSourceData, "{63775ECB-5C3E-44D3-B175-4537BF76C3A7}", RPI::MaterialFunctorSourceData); - - static void Reflect(ReflectContext* context); - - FunctorResult CreateFunctor(const RuntimeContext& context) const override; - - private: - - AZStd::string m_opacityMode; - AZStd::string m_parallaxEnable; - AZStd::string m_parallaxPdoEnable; - - uint32_t m_shadowShaderNoPSIndex = -1; - uint32_t m_shadowShaderWithPSIndex = -1; - uint32_t m_depthShaderNoPSIndex = -1; - uint32_t m_depthShaderWithPSIndex = -1; - uint32_t m_pbrShaderWithEdsIndex = -1; - uint32_t m_pbrShaderNoEdsIndex = -1; - // The following are used by the light culling system to produce min/max depth bounds - uint32_t m_depthShaderTransparentMin = -1; - uint32_t m_depthShaderTransparentMax = -1; - }; - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 4059d65cbb..117b177f05 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -741,7 +741,7 @@ namespace AZ // retrieve vertex/index buffers RPI::ModelLod::StreamBufferViewList streamBufferViews; - [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, shaderInputContract, meshIndex); + [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, nullptr, shaderInputContract, meshIndex); AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); // note that the element count is the size of the entire buffer, even though this mesh may only diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index c4e9306dc9..7c13daea3b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -318,7 +318,7 @@ namespace AZ { AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); AZ::Transform noScaleTransform = meshTransform; - noScaleTransform.ExtractScale(); + noScaleTransform.ExtractUniformScale(); AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 3497855c07..3e9e316a5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -209,7 +209,7 @@ namespace AZ void ReflectionProbe::SetTransform(const AZ::Transform& transform) { // retrieve previous scale and revert the scale on the inner/outer extents - AZ::Vector3 previousScale = m_transform.GetScale(); + float previousScale = m_transform.GetUniformScale(); m_outerExtents /= previousScale; m_innerExtents /= previousScale; @@ -218,12 +218,12 @@ namespace AZ // avoid scaling the visualization sphere AZ::Transform visualizationTransform = m_transform; - visualizationTransform.ExtractScale(); + visualizationTransform.ExtractUniformScale(); m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, visualizationTransform); // update the inner/outer extents with the new scale - m_outerExtents *= m_transform.GetScale(); - m_innerExtents *= m_transform.GetScale(); + m_outerExtents *= m_transform.GetUniformScale(); + m_innerExtents *= m_transform.GetUniformScale(); m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); @@ -232,14 +232,14 @@ namespace AZ void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents) { - m_outerExtents = outerExtents * m_transform.GetScale(); + m_outerExtents = outerExtents * m_transform.GetUniformScale(); m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents) { - m_innerExtents = innerExtents * m_transform.GetScale(); + m_innerExtents = innerExtents * m_transform.GetUniformScale(); m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); m_updateSrg = true; } diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index acb6e4a287..fb73d0f416 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -231,7 +231,7 @@ namespace AZ AZ_Error("TransformServiceFeatureProcessor", id.IsValid(), "Attempting to get the transform for an invalid handle."); AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromRowMajorFloat12(m_objectToWorldTransforms.at(id.GetIndex()).m_transform); AZ::Transform transform = AZ::Transform::CreateFromMatrix3x4(matrix3x4); - transform.ExtractScale(); + transform.ExtractUniformScale(); return transform; } diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake index 3a749a4b67..4e7cc9dab3 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake @@ -24,16 +24,12 @@ set(FILES Source/Material/ConvertEmissiveUnitFunctorSourceData.h Source/Material/MaterialConverterSystemComponent.cpp Source/Material/MaterialConverterSystemComponent.h - Source/Material/ShaderEnableFunctorSourceData.cpp - Source/Material/ShaderEnableFunctorSourceData.h Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.cpp Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h Source/Material/Transform2DFunctorSourceData.cpp Source/Material/Transform2DFunctorSourceData.h Source/Material/UseTextureFunctorSourceData.cpp Source/Material/UseTextureFunctorSourceData.h - Source/Material/PropertyVisibilityFunctorSourceData.cpp - Source/Material/PropertyVisibilityFunctorSourceData.h Source/Material/DrawListFunctorSourceData.cpp Source/Material/DrawListFunctorSourceData.h ) diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 8926b0c19f..4f98046adb 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -135,6 +135,8 @@ set(FILES Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp Source/DiffuseProbeGrid/DiffuseProbeGrid.h + Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp + Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp Source/DisplayMapper/AcesOutputTransformPass.cpp @@ -153,16 +155,12 @@ set(FILES Source/LookupTable/LookupTableAsset.cpp Source/Material/ConvertEmissiveUnitFunctor.cpp Source/Material/ConvertEmissiveUnitFunctor.h - Source/Material/ShaderEnableFunctor.cpp - Source/Material/ShaderEnableFunctor.h Source/Material/SubsurfaceTransmissionParameterFunctor.cpp Source/Material/SubsurfaceTransmissionParameterFunctor.h Source/Material/Transform2DFunctor.cpp Source/Material/Transform2DFunctor.h Source/Material/UseTextureFunctor.cpp Source/Material/UseTextureFunctor.h - Source/Material/PropertyVisibilityFunctor.cpp - Source/Material/PropertyVisibilityFunctor.h Source/Material/DrawListFunctor.cpp Source/Material/DrawListFunctor.h Source/Math/GaussianMathFilter.h diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h index 2073c48d25..a5d8f53573 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h @@ -13,6 +13,8 @@ #include #include +#include +#include namespace AZ { @@ -30,9 +32,16 @@ namespace AZ static void Reflect(ReflectContext* context); + //! Returns true if either @m_azslcAdditionalFreeArguments or @m_dxcAdditionalFreeArguments contain + //! macro definitions, e.g. "-D MACRO" or "-D MACRO=VALUE" or "-DMACRO", "-DMACRO=VALUE". + //! It is used for validation to forbid macro definitions, because the idea is that this struct + //! is used inside GlobalBuildOptions which has a dedicated variable for macro definitions. + bool HasMacroDefinitionsInCommandLineArguments(); + //! Mix two instances of arguments, by or-ing bools, or by "if different, right hand side wins" void Merge(const ShaderCompilerArguments& right); + //! [GFX TODO] [ATOM-15472] Remove this function. //! Determine whether there is a rebuild-worthy difference in arguments for AZSLc bool HasDifferentAzslcArguments(const ShaderCompilerArguments& right) const; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h index 3e593794af..6f884eb358 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h @@ -110,6 +110,46 @@ namespace AZ AZStd::string BuildFileNameWithExtension(const AZStd::string& shaderSourceFile, const AZStd::string& tempFolder, const char* outputExtension); + + namespace CommandLineArgumentUtils + { + //! @param commandLineString: A string with command line arguments of the form: + //! "- -- --[=] ..." + //! Example: "--use-spaces --namespace=vk -W1" + //! Returns: A list with just the [-|--]: + //! ["-", "--", "--arg3"] + //! For the example shown above it will return this vector: + //! ["--use-spaces", "--namespace", "-W1] + AZStd::vector GetListOfArgumentNames(AZStd::string_view commandLineString); + + //! Takes a list of names of command line arguments and removes those arguments from @commandLineString. + //! The core functionality of this function is that it searches by name in @commandLineString and removes + //! name and value if the name is found. + //! @param listOfArguments: This is a list of strings, usually generated by the helper function + //! ShaderCompilerArguments::GetListOfArgumentNames() + //! @param commandLineString: A single string made of several command line arguments + //! @returns A new string based on @commandLineString but with the matching arguments and their values + //! removed from it. + AZStd::string RemoveArgumentsFromCommandLineString( + AZStd::array_view listOfArguments, AZStd::string_view commandLineString); + + //! @param commandLineString: " --arg1 -arg2 --arg3=foo --arg4=bar " + //! @returns "--arg1 -arg2 --arg3=foo --arg4=bar" + AZStd::string RemoveExtraSpaces(AZStd::string_view commandLineString); + + //! Accepts two arbitrary strings that contain typical command line arguments and returns + //! a new string that combines the arguments were the arguments on the @right have precedence. + //! Example: + //! @param left: "--arg1 -arg2 --arg3=foo" + //! @param right: "--arg3=bar --arg4" + //! @returns: "--arg1 -arg2 --arg3=bar --arg4" + AZStd::string MergeCommandLineArguments(AZStd::string_view left, AZStd::string_view right); + + //! @param commandLineString: A string that contains a series of command line arguments. + //! @returns: true if @commandLineString contains macro definitions, e.g: + //! "-D MACRO" or "-D MACRO=VALUE" or "-DMACRO", "-DMACRO=VALUE". + bool HasMacroDefinitions(AZStd::string_view commandLineString); + } } } diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp index 7304a351d4..09cd1c125e 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp @@ -12,6 +12,9 @@ #include #include +#include + +#include namespace AZ { @@ -49,6 +52,12 @@ namespace AZ } } + bool ShaderCompilerArguments::HasMacroDefinitionsInCommandLineArguments() + { + return CommandLineArgumentUtils::HasMacroDefinitions(m_azslcAdditionalFreeArguments) || + CommandLineArgumentUtils::HasMacroDefinitions(m_dxcAdditionalFreeArguments); + } + void ShaderCompilerArguments::Merge(const ShaderCompilerArguments& right) { if (right.m_azslcWarningLevel != LevelUnset) @@ -56,7 +65,7 @@ namespace AZ m_azslcWarningLevel = right.m_azslcWarningLevel; } m_azslcWarningAsError = m_azslcWarningAsError || right.m_azslcWarningAsError; - m_azslcAdditionalFreeArguments += " " + right.m_azslcAdditionalFreeArguments; + m_azslcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_azslcAdditionalFreeArguments, right.m_azslcAdditionalFreeArguments); m_dxcDisableWarnings = m_dxcDisableWarnings || right.m_dxcDisableWarnings; m_dxcWarningAsError = m_dxcWarningAsError || right.m_dxcWarningAsError; m_dxcDisableOptimizations = m_dxcDisableOptimizations || right.m_dxcDisableOptimizations; @@ -65,13 +74,14 @@ namespace AZ { m_dxcOptimizationLevel = right.m_dxcOptimizationLevel; } - m_dxcAdditionalFreeArguments += " " + right.m_dxcAdditionalFreeArguments; + m_dxcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_dxcAdditionalFreeArguments, right.m_dxcAdditionalFreeArguments); if (right.m_defaultMatrixOrder != MatrixOrder::Default) { m_defaultMatrixOrder = right.m_defaultMatrixOrder; } } + //! [GFX TODO] [ATOM-15472] Remove this function. bool ShaderCompilerArguments::HasDifferentAzslcArguments(const ShaderCompilerArguments& right) const { auto isSet = +[](uint8_t level) { return level != LevelUnset; }; @@ -154,7 +164,7 @@ namespace AZ arguments += " -Zi"; // Generate debug information arguments += " -Zss"; // Compute Shader Hash considering source information } - arguments += m_dxcAdditionalFreeArguments; + arguments += " " + m_dxcAdditionalFreeArguments; return arguments; } } diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp index 00b5dada69..dc0efb7e9a 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp @@ -494,5 +494,64 @@ namespace AZ AzFramework::StringFunc::Path::ReplaceExtension(outputFile, outputExtension); return outputFile; } + + namespace CommandLineArgumentUtils + { + AZStd::vector GetListOfArgumentNames(AZStd::string_view commandLineString) + { + AZStd::vector listOfTokens; + AzFramework::StringFunc::Tokenize(commandLineString, listOfTokens, " \t\n"); + AZStd::vector listOfArguments; + for (const AZStd::string& token : listOfTokens) + { + AZStd::vector splitArguments; + AzFramework::StringFunc::Tokenize(token, splitArguments, "="); + listOfArguments.push_back(splitArguments[0]); + } + return listOfArguments; + } + + AZStd::string RemoveArgumentsFromCommandLineString( + AZStd::array_view listOfArgumentsToRemove, AZStd::string_view commandLineString) + { + AZStd::string customizedArguments = commandLineString; + for (const AZStd::string& azslcArgumentName : listOfArgumentsToRemove) + { + AZStd::string regexStr = AZStd::string::format("%s(=\\S+)?", azslcArgumentName.c_str()); + AZStd::regex replaceRegex(regexStr, AZStd::regex::ECMAScript); + customizedArguments = AZStd::regex_replace(customizedArguments, replaceRegex, ""); + } + return customizedArguments; + } + + AZStd::string RemoveExtraSpaces(AZStd::string_view commandLineString) + { + AZStd::vector argumentList; + AzFramework::StringFunc::Tokenize(commandLineString, argumentList, " \t\n"); + AZStd::string cleanStringWithArguments; + AzFramework::StringFunc::Join(cleanStringWithArguments, argumentList.begin(), argumentList.end(), " "); + return cleanStringWithArguments; + } + + AZStd::string MergeCommandLineArguments(AZStd::string_view left, AZStd::string_view right) + { + auto listOfArgumentNamesFromRight = GetListOfArgumentNames(right); + auto leftWithRightArgumentsRemoved = RemoveArgumentsFromCommandLineString(listOfArgumentNamesFromRight, left); + AZStd::string combinedArguments = AZStd::string::format("%s %s", leftWithRightArgumentsRemoved.c_str(), right.data()); + return RemoveExtraSpaces(combinedArguments); + } + + bool HasMacroDefinitions(AZStd::string_view commandLineString) + { + const AZStd::regex macroRegex(R"((^-D\s*(\w+))|(\s+-D\s*(\w+)))", AZStd::regex::ECMAScript); + + AZStd::smatch match; + if (AZStd::regex_search(commandLineString.data(), match, macroRegex)) + { + return (match.size() >= 1); + } + return false; + } + } //namespace CommandLineArgumentUtils } // namespace RHI } // namespace AZ diff --git a/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp b/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp index 506edc0d1e..afeca6e617 100644 --- a/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp +++ b/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp @@ -60,7 +60,9 @@ namespace UnitTest TEST_F(UtilsTests, LoadFileString_Error_DoesNotExist) { + AZ_TEST_START_TRACE_SUPPRESSION; auto outcome = AZ::RHI::LoadFileString("FileDoesNotExist"); + AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; EXPECT_FALSE(outcome.IsSuccess()); EXPECT_TRUE(outcome.GetError().find("Could not open file") != AZStd::string::npos); EXPECT_TRUE(outcome.GetError().find("FileDoesNotExist") != AZStd::string::npos); @@ -68,7 +70,9 @@ namespace UnitTest TEST_F(UtilsTests, LoadFileBytes_Error_DoesNotExist) { + AZ_TEST_START_TRACE_SUPPRESSION; auto outcome = AZ::RHI::LoadFileBytes("FileDoesNotExist"); + AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; EXPECT_FALSE(outcome.IsSuccess()); EXPECT_TRUE(outcome.GetError().find("Could not open file") != AZStd::string::npos); EXPECT_TRUE(outcome.GetError().find("FileDoesNotExist") != AZStd::string::npos); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 841c71126a..c4666578a7 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -251,7 +251,7 @@ namespace AZ ByProducts& byProducts) const { // Shader compiler executable - static const char* dxcRelativePath = "Builders/DirectXShaderCompilerAz/bin/dxc"; + static const char* dxcRelativePath = "Builders/DirectXShaderCompiler/bin/dxc"; // Output file AZStd::string shaderMSLOutputFile = RHI::BuildFileNameWithExtension(shaderSourceFile, tempFolder, "metal"); diff --git a/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 35ee5a7ec0..0be84dba15 100644 --- a/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -104,7 +104,7 @@ namespace AZ { return WindowsAzslShaderHeader; } - else if (platform.m_identifier == "osx_gl") + else if (platform.m_identifier == "mac") { return MacAzslShaderHeader; } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp index cdacd2578f..791fa006b0 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp @@ -180,6 +180,7 @@ namespace AZ for (uint32_t imageIndex = static_cast(NullDescriptorManager::ImageTypes::General2D); imageIndex < static_cast(NullDescriptorManager::ImageTypes::Count); imageIndex++) { // different options for the images + imageCreateInfo.imageType = (imageIndex >= static_cast(NullDescriptorManager::ImageTypes::General3D)) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; imageCreateInfo.extent = { m_imageNullDescriptor.m_images[imageIndex].m_dimension, m_imageNullDescriptor.m_images[imageIndex].m_dimension, 1 }; imageCreateInfo.samples = m_imageNullDescriptor.m_images[imageIndex].m_sampleCountFlag; imageCreateInfo.format = m_imageNullDescriptor.m_images[imageIndex].m_format; diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli index 2381febed0..9b90c40ab9 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli @@ -225,3 +225,16 @@ float NextRandomFloatUniform(inout uint seed) seed = Xorshift(seed); return (float)seed / 4294967295.0f; } + +//! Returns the inverse of lerp, 't', such that value = lerp(a, b, t), or returns 0 when a == b. +float LerpInverse(float a, float b, float value) +{ + if(abs(a - b) <= EPSILON) + { + return 0.0; + } + else + { + return (value - a) / (b - a); + } +} diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli index 69381ca0fc..e12736dfec 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli @@ -16,7 +16,12 @@ ShaderResourceGroup DrawSrg : SRG_PerDraw { - float4 m_placeholder; // [GFX-TODO] [Atom-1727] Bug in AZSLc, empty SRGs cannot be shader variant fallbacks! // This SRG is unique per draw packet + uint m_uvStreamTangentBitmask; + + uint GetTangentAtUv(uint uvIndex) + { + return (m_uvStreamTangentBitmask >> (4 * uvIndex)) & 0xF; + } } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli index 13e3c652db..62111b3e80 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli @@ -52,6 +52,12 @@ float3 GetTangentSpaceNormal_Unnormalized(float2 normalMapSample, float normalSt // The image build pipeline drops the B channel so we have to reconstruct it here. surfaceNormal.z = sqrt(1 - dot(surfaceNormal.xy, surfaceNormal.xy)); + // Don't allow z to be zero just in case normalStrength approaches 0, to avoid a 0-length normal. + // It doesn't make sense anyway to have a surface with a normal map completely tangential. + // This also addresses the possibility of z being NaN, in the case where x^2+y^2 > 1, so we don't need to call saturate in the sqrt operation above. + // (Note this edge case would be particularly evident in multilayer material types, where the normal map is masked out using normalStrength). + surfaceNormal.z = max(surfaceNormal.z, 0.01); + surfaceNormal.xy *= normalStrength; return surfaceNormal; @@ -190,12 +196,22 @@ void SurfaceGradientNormalMapping_GenerateTB(float2 uv, out float3 tangentWS, ou } //! Utility macro to nest SGBNM setup processes. -#define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents, startIndex) \ +//! We support two UV streams, but only a single stream of tangent/bitangent. +//! By default, the first UV stream is applied and the default tangent/bitangent are used. +//! If anything uses the second UV stream, and it is not a duplication of the first stream, +//! generated tangent/bitangent will be applied. +//! (As it implies, cases may occur where all/none of the UV steams use the default TB.) +//! What tangent/bitangent a UV stream uses is encoded in DrawSrg. +#define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents) \ { \ SurfaceGradientNormalMapping_Init(normal, worldPos, !isFrontFace); \ [unroll] \ - for (int i = startIndex; i < uvSetCount; ++i) \ + for (uint i = 0; i < uvSetCount; ++i) \ { \ + if (DrawSrg::GetTangentAtUv(i) == 0) \ + { \ + continue; \ + } \ SurfaceGradientNormalMapping_GenerateTB(uvSets[i], outTangents[i], outBitangents[i]); \ } \ } diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt index f92213d9d7..d2b7fba071 100644 --- a/Gems/Atom/RPI/Code/CMakeLists.txt +++ b/Gems/Atom/RPI/Code/CMakeLists.txt @@ -69,6 +69,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE AZ::AtomCore AZ::AzToolsFramework + Gem::Atom_RHI.Edit Gem::Atom_RPI.Public ) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index e4b9b91e81..2dd0865688 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -87,6 +87,14 @@ namespace AZ //! Finalize and validate initialization. Any initialization functions should be called before EndInit is called. void EndInit(); + //! Set up the DynamicDrawContext for the input Scene. + //! This should be called after the last frame is done and before any draw calls. + void SetScene(Scene* scene); + + //! Set up the DynamicDrawContext for the input RenderPipeline. + //! This should be called after the last frame is done and before any draw calls. + void SetRenderPipeline(RenderPipeline* pipeline); + //! Return if this DynamicDrawContext is ready to add draw calls bool IsReady(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h index 0aa979d611..70cabca371 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h @@ -98,7 +98,7 @@ namespace AZ //! List of shader options set for this specific draw packet typedef AZStd::pair ShaderOptionPair; typedef AZStd::vector ShaderOptionVector; - ShaderOptionVector m_shaderOptions; + ShaderOptionVector m_shaderOptions; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h index 285cf80aca..5a1c571a26 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h @@ -14,6 +14,7 @@ #include #include +#include #include @@ -108,6 +109,7 @@ namespace AZ const MaterialUvNameMap& materialUvNameMap = {}) const; //! Fills a InputStreamLayout and StreamBufferViewList for the set of streams that satisfy a ShaderInputContract. + // @param uvStreamTangentBitmaskOut a mask processed during UV stream matching, and later to determine which tangent/bitangent stream to use. // @param contract the contract that defines the expected inputs for a shader, used to determine which streams are optional. // @param meshIndex the index of the mesh to search in. // @param materialModelUvMap a map of UV name overrides, which can be supplied to bind a specific mesh stream name to a different material shader stream name. @@ -115,6 +117,7 @@ namespace AZ bool GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, ModelLod::StreamBufferViewList& streamBufferViewsOut, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap = {}, @@ -130,6 +133,8 @@ namespace AZ const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo, Mesh& meshInstance); + StreamInfoList::const_iterator FindFirstUvStreamFromMesh(size_t meshIndex) const; + StreamInfoList::const_iterator FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const; // Finds a mesh vertex input stream that is the best match for a contracted stream channel. @@ -137,12 +142,16 @@ namespace AZ // @param materialModelUvMap a map of UV name overrides, which can be supplied to bind a specific mesh stream name to a different material shader stream name. // @param materialUvNameMap the UV name map that came from a MaterialTypeAsset, which defines the default set of material shader stream names. // @param defaultUv the default UV stream to use if a matching UV stream could not be found. Use FindDefaultUvStream() to populate this. + // @param firstUv the first UV stream from the mesh, which, by design, the tangent/bitangent stream belongs to. + // @param uvStreamTangentIndex a bitset indicating which tangent/bitangent stream (including generated ones) a UV stream will be using. StreamInfoList::const_iterator FindMatchingStream( size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap, const ShaderInputContract::StreamChannelInfo& contractStreamChannel, - StreamInfoList::const_iterator defaultUv) const; + StreamInfoList::const_iterator defaultUv, + StreamInfoList::const_iterator firstUv, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const; // Meshes may share index/stream buffers in an LOD or they may have // unique buffers. Often the asset builder will prioritize shared buffers diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h new file mode 100644 index 0000000000..aa599bc27c --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h @@ -0,0 +1,72 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +#include + +namespace AZ +{ + namespace RPI + { + //! An encoded bitmask for tangent used by UV streams. + //! It contains the information about number of UV streams and which tangent/bitangent is used by each UV stream. + //! See m_mask for more details. + //! The mask will be passed through per draw SRG. + class UvStreamTangentBitmask + { + public: + //! Get the full mask including number of UVs and tangent/bitangent assignment to each UV. + uint32_t GetFullTangentBitmask() const; + + //! Get number of UVs that have tangent/bitangent assigned. + uint32_t GetUvStreamCount() const; + + //! Get tangent/bitangent assignment to the specified UV in the material. + //! @param uvIndex the index of the UV from the material, in default order as in the shader code. + uint32_t GetTangentAtUv(uint32_t uvIndex) const; + + //! Apply the tangent to the next UV, whose index is the same as GetUvStreamCount. + //! @param tangent the tangent/bitangent to be assigned. Ranged in [0, 0xF) + //! It comes from the model in order, e.g. 0 means the first available tangent stream from the model. + //! Specially, value 0xF(=UnassignedTangent) means generated tangent/bitangent will be used in shader. + //! If ranged out of definition, unassigned tangent will be applied. + void ApplyTangent(uint32_t tangent); + + //! Reset the bitmask to clear state. + void Reset(); + + //! The bit mask indicating generated tangent/bitangent will be used. + static constexpr uint32_t UnassignedTangent = 0b1111u; + + //! The variable name defined in the SRG shader code. + static constexpr const char* SrgName = "m_uvStreamTangentBitmask"; + private: + //! Mask composition: + //! The number of UV slots (highest 4 bits) + tangent mask (4 bits each) * 7 + //! e.g. 0x200000F0 means there are 2 UV streams, + //! the first UV stream uses 0th tangent stream (0x0), + //! the second UV stream uses the generated tangent stream (0xF). + uint32_t m_mask = 0; + + //! Bit size in the mask composition. + static constexpr uint32_t BitsPerTangent = 4; + static constexpr uint32_t BitsForUvIndex = 4; + + public: + //! Max UV slots available in this bit mask. + static constexpr uint32_t MaxUvSlots = (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex) / BitsPerTangent; + }; + } +} diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h index 4f24a20f35..8075c6fc5d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h @@ -26,9 +26,9 @@ namespace AZ class ViewportContextManager; //! ViewportContext wraps a native window and represents a minimal viewport - //! in which a scene is rendered on-screen + //! in which a scene is rendered on-screen. //! ViewportContexts are registered on creation to allow consumers to listen to notifications - //! and manage the view stack for a given viewport + //! and manage the view stack for a given viewport. class ViewportContext : public SceneNotificationBus::Handler , public AzFramework::WindowNotificationBus::Handler @@ -61,11 +61,11 @@ namespace AZ //! Gets the current name of this ViewportContext. //! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be - //! renamed via AZ::Interface::Get()->RenameViewportContext. + //! renamed via AZ::RPI::ViewportContextRequests::Get()->RenameViewportContext(...). AZ::Name GetName() const; //! Gets the default view associated with this ViewportContext. - //! Alternatively, use AZ::Interface::Get()->GetCurrentView. + //! Alternatively, use AZ::RPI::ViewportContextRequests::Get()->GetCurrentView(). ViewPtr GetDefaultView(); ConstViewPtr GetDefaultView() const; @@ -99,6 +99,18 @@ namespace AZ //! Notifies consumers when the render scene has changed. void ConnectSceneChangedHandler(SceneChangedEvent::Handler& handler); + using PipelineChangedEvent = AZ::Event; + //! Notifies consumers when the current pipeline associated with our window has changed. + void ConnectCurrentPipelineChangedHandler(PipelineChangedEvent::Handler& handler); + + using ViewChangedEvent = AZ::Event; + //! Notifies consumers when the default view has changed. + void ConnectDefaultViewChangedHandler(ViewChangedEvent::Handler& handler); + + using ViewportIdEvent = AZ::Event; + //! Notifies consumers when this ViewportContext is about to be destroyed. + void ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler); + // ViewportRequestBus interface //! Gets the current camera's view matrix. const AZ::Matrix4x4& GetCameraViewMatrix() const override; @@ -123,12 +135,17 @@ namespace AZ WindowContextSharedPtr m_windowContext; ViewPtr m_defaultView; AzFramework::WindowSize m_viewportSize; + SizeChangedEvent m_sizeChangedEvent; MatrixChangedEvent m_viewMatrixChangedEvent; MatrixChangedEvent::Handler m_onViewMatrixChangedHandler; MatrixChangedEvent m_projectionMatrixChangedEvent; MatrixChangedEvent::Handler m_onProjectionMatrixChangedHandler; SceneChangedEvent m_sceneChangedEvent; + PipelineChangedEvent m_currentPipelineChangedEvent; + ViewChangedEvent m_defaultViewChangedEvent; + ViewportIdEvent m_aboutToBeDestroyedEvent; + ViewportContextManager* m_manager; RenderPipelinePtr m_currentPipeline; Name m_name; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h index 396ba14810..f372f40981 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h @@ -288,6 +288,7 @@ namespace AZ AZStd::size_t GetShaderCount() const; LuaMaterialFunctorShaderItem GetShader(AZStd::size_t index); LuaMaterialFunctorShaderItem GetShaderByTag(const char* shaderTag); + bool HasShaderWithTag(const char* shaderTag); private: diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp index aac81a6e26..376399ff84 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp @@ -11,6 +11,8 @@ */ #include +#include +#include #include #include @@ -57,7 +59,7 @@ namespace AZ bool ShaderSourceData::IsRhiBackendDisabled(const AZ::Name& rhiName) const { - return AZStd::any_of(m_disabledRhiBackends.begin(), m_disabledRhiBackends.end(), [&](const AZStd::string& currentRhiName) + return AZStd::any_of(AZ_BEGIN_END(m_disabledRhiBackends), [&](const AZStd::string& currentRhiName) { return currentRhiName == rhiName.GetStringView(); }); @@ -72,19 +74,32 @@ namespace AZ static void GetListOfMacroDefinitionNames( const AZStd::string& stringWithArguments, AZStd::vector& macroDefinitionNames) { - static const AZStd::regex macroRegex("-D\\s*(\\w+)", AZStd::regex::ECMAScript); + const AZStd::regex macroRegex(R"(-D\s*(\w+))", AZStd::regex::ECMAScript); - AZStd::cmatch match; - if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex)) + AZStd::string hayStack(stringWithArguments); + AZStd::smatch match; + while (AZStd::regex_search(hayStack.c_str(), match, macroRegex)) { // First pattern is always the entire string for (unsigned i = 1; i < match.size(); ++i) { if (match[i].matched) { - macroDefinitionNames.push_back(match[i].str().c_str()); + AZStd::string macroToAdd(match[i].str().c_str()); + const bool isPresent = AZStd::any_of(AZ_BEGIN_END(macroDefinitionNames), + [&](AZStd::string_view macroName) -> bool + { + return macroToAdd == macroName; + } + ); + if (isPresent) + { + continue; + } + macroDefinitionNames.push_back(macroToAdd); } } + hayStack = match.suffix(); } } @@ -103,19 +118,22 @@ namespace AZ static void GetListOfMacroDefinitions( const AZStd::string& stringWithArguments, AZStd::vector& macroDefinitions) { - static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\w+)?)", AZStd::regex::ECMAScript); + const AZStd::regex macroRegex(R"(-D\s*(\w+)(=\w+)?)", AZStd::regex::ECMAScript); - AZStd::cmatch match; - if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex)) + AZStd::string hayStack(stringWithArguments); + AZStd::smatch match; + while (AZStd::regex_search(hayStack.c_str(), match, macroRegex)) { - // First pattern is always the entire string - for (unsigned i = 1; i < match.size(); ++i) + if (match.size() > 1) { - if (match[i].matched) + AZStd::string macro(match[1].str().c_str()); + if (match.size() > 2) { - macroDefinitions.push_back(match[i].str().c_str()); + macro += match[2].str().c_str(); } + macroDefinitions.push_back(macro); } + hayStack = match.suffix(); } } @@ -126,62 +144,27 @@ namespace AZ return parsedMacroDefinitions; } - - // Helper. - // @arguments: A string with command line arguments for a console application of the form: - // "- -- --[=] ..." - // Example: "--use-spaces --namespace=vk" - // Returns: A list with just the [-|--]: - // ["-", "--", "--arg3"] - // For the example shown above it will return this vector: - // ["--use-spaces", "--namespace"] - AZStd::vector GetListOfArgumentNames(const AZStd::string& arguments) - { - AZStd::vector listOfTokens; - AzFramework::StringFunc::Tokenize(arguments, listOfTokens); - AZStd::vector listOfArguments; - for (const AZStd::string& token : listOfTokens) - { - AZStd::vector splitArguments; - AzFramework::StringFunc::Tokenize(token, splitArguments, "="); - listOfArguments.push_back(splitArguments[0]); - } - return listOfArguments; - } - AZStd::string ShaderSourceData::SupervariantInfo::GetCustomizedArgumentsForAzslc( const AZStd::string& initialAzslcCompilerArguments) const { - static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\S+)?)", AZStd::regex::ECMAScript); + const AZStd::regex macroRegex(R"(-D\s*(\w+(=\S+)?))", AZStd::regex::ECMAScript); // We are only concerned with AZSLc arguments. Let's remove the C-Preprocessor macro definitions // from @minusArguments. const AZStd::string minusArguments = AZStd::regex_replace(m_minusArguments, macroRegex, ""); const AZStd::string plusArguments = AZStd::regex_replace(m_plusArguments, macroRegex, ""); AZStd::string azslcArgumentsToRemove = minusArguments + " " + plusArguments; - AZStd::vector azslcArgumentNamesToRemove = GetListOfArgumentNames(azslcArgumentsToRemove); + AZStd::vector azslcArgumentNamesToRemove = RHI::CommandLineArgumentUtils::GetListOfArgumentNames(azslcArgumentsToRemove); // At this moment @azslcArgumentsToRemove contains arguments for AZSLc that can be of the form: // - // --[=] // We need to remove those from @initialAzslcCompilerArguments. - AZStd::string customizedArguments = initialAzslcCompilerArguments; - for (const AZStd::string& azslcArgumentName : azslcArgumentNamesToRemove) - { - AZStd::string regexStr = AZStd::string::format("%s(=\\S+)?", azslcArgumentName.c_str()); - AZStd::regex replaceRegex(regexStr, AZStd::regex::ECMAScript); - customizedArguments = AZStd::regex_replace(customizedArguments, replaceRegex, ""); - } - + AZStd::string customizedArguments = RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString( + azslcArgumentNamesToRemove, initialAzslcCompilerArguments); customizedArguments += " " + plusArguments; - // Will contain the results that will be joined by a space. - // This is used to get a clean string to return without excess spaces. - AZStd::vector argumentList; - AzFramework::StringFunc::Tokenize(customizedArguments, argumentList, " \t\n"); - customizedArguments.clear(); // Need to clear because Join appends. - AzFramework::StringFunc::Join(customizedArguments, argumentList.begin(), argumentList.end(), " "); - return customizedArguments; + return RHI::CommandLineArgumentUtils::RemoveExtraSpaces(customizedArguments); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index b7ba7d4a32..4e4a7a5e71 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -211,6 +211,42 @@ namespace AZ m_rhiPipelineState = m_pipelineState->GetRHIPipelineState(); } + void DynamicDrawContext::SetScene(Scene* scene) + { + AZ_Assert(scene, "SetScene called with an invalid scene"); + if (!scene || m_scene == scene) + { + return; + } + m_scene = scene; + m_drawFilter = RHI::DrawFilterMaskDefaultValue; + // Reinitialize if it was initialized + if (m_initialized) + { + // Report warning if there were some draw data + AZ_Warning( + "DynamicDrawContext", m_cachedDrawItems.size() == 0, + "DynamicDrawContext::SetForScene should be called" + " when there is no cached draw data"); + // Clear some cached data + FrameEnd(); + m_cachedRhiPipelineStates.clear(); + // Reinitialize + EndInit(); + } + } + + void DynamicDrawContext::SetRenderPipeline(RenderPipeline* pipeline) + { + AZ_Assert(pipeline, "SetRenderPipeline called with an invalid pipeline"); + if (!pipeline) + { + return; + } + SetScene(pipeline->GetScene()); + m_drawFilter = pipeline->GetDrawFilterMask(); + } + bool DynamicDrawContext::IsReady() { return m_initialized; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp index 227e096594..1d6e8cdc65 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp @@ -96,12 +96,6 @@ namespace AZ return QueryResultCode::Fail; } - // Limit calling BeginQuery() to the first CommandList in the array. - if (context.GetCommandListIndex() != 0) - { - return QueryResultCode::Success; - } - const auto rhiQueryIndices = GetRhiQueryIndicesFromCurrentFrame(); if (!rhiQueryIndices) { @@ -124,12 +118,6 @@ namespace AZ return QueryResultCode::Fail; } - // Limit calling EndQuery() to the last CommandList in the array. - if (context.GetCommandListIndex() != context.GetCommandListCount() - 1) - { - return QueryResultCode::Success; - } - // Validate that the queries are recorded for the same scope. if (m_cachedScopeId != context.GetScopeId()) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp index d0a277304e..b1265b1228 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp @@ -169,7 +169,7 @@ namespace AZ const AZ::Data::Asset& drawSrgAsset = shader->GetAsset()->GetDrawSrgAsset(); // Set all unspecified shader options to default values, so that we get the most specialized variant possible. - // (because FindVariantStableId treats unspecified options as a request specificlly for a variant that doesn't specify those options) + // (because FindVariantStableId treats unspecified options as a request specifically for a variant that doesn't specify those options) // [GFX TODO][ATOM-3883] We should consider updating the FindVariantStableId algorithm to handle default values for us, and remove this step here. RPI::ShaderOptionGroup shaderOptions = *shaderItem.GetShaderOptions(); shaderOptions.SetUnspecifiedToDefaultValues(); @@ -198,21 +198,6 @@ namespace AZ const ShaderVariantId finalVariantId = shaderOptions.GetShaderVariantId(); const ShaderVariant& variant = r_forceRootShaderVariantUsage ? shader->GetRootVariant() : shader->GetVariant(finalVariantId); - Data::Instance drawSrg; - if (drawSrgAsset) - { - AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "create drawSrg"); - // If the DrawSrg exists we must create and bind it, otherwise the CommandList will fail validation for SRG being null - drawSrg = RPI::ShaderResourceGroup::Create(drawSrgAsset); - - if (!variant.IsFullyBaked() && drawSrgAsset->GetLayout()->HasShaderVariantKeyFallbackEntry()) - { - drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue()); - } - - drawSrg->Compile(); - } - RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; variant.ConfigurePipelineState(pipelineStateDescriptor); @@ -224,9 +209,12 @@ namespace AZ streamBufferViewsPerShader.push_back(); auto& streamBufferViews = streamBufferViewsPerShader.back(); + UvStreamTangentBitmask uvStreamTangentBitmask; + if (!m_modelLod->GetStreamsForMesh( pipelineStateDescriptor.m_inputStreamLayout, streamBufferViews, + &uvStreamTangentBitmask, variant.GetInputContract(), m_modelLodMeshIndex, m_materialModelUvMap, @@ -235,6 +223,32 @@ namespace AZ return false; } + Data::Instance drawSrg; + if (drawSrgAsset) + { + AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "create drawSrg"); + // If the DrawSrg exists we must create and bind it, otherwise the CommandList will fail validation for SRG being null + drawSrg = RPI::ShaderResourceGroup::Create(drawSrgAsset); + + if (!variant.IsFullyBaked() && drawSrgAsset->GetLayout()->HasShaderVariantKeyFallbackEntry()) + { + drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue()); + } + + // Pass UvStreamTangentBitmask to the shader if the draw SRG has it. + { + AZ::Name shaderUvStreamTangentBitmask = AZ::Name(UvStreamTangentBitmask::SrgName); + auto index = drawSrg->FindShaderInputConstantIndex(shaderUvStreamTangentBitmask); + + if (index.IsValid()) + { + drawSrg->SetConstant(index, uvStreamTangentBitmask.GetFullTangentBitmask()); + } + } + + drawSrg->Compile(); + } + // Use the default draw list tag from the shader variant. RHI::DrawListTag drawListTag = shader->GetDrawListTag(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp index 8747dac663..c6a1a51f39 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp @@ -117,6 +117,17 @@ namespace AZ return RHI::ResultCode::Success; } + ModelLod::StreamInfoList::const_iterator ModelLod::FindFirstUvStreamFromMesh(size_t meshIndex) const + { + const Mesh& mesh = m_meshes[meshIndex]; + + auto firstUv = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [](const StreamBufferInfo& info) { + return info.m_semantic.m_name.GetStringView().starts_with(RHI::ShaderSemantic::UvStreamSemantic); + }); + + return firstUv; + } + ModelLod::StreamInfoList::const_iterator ModelLod::FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const { const Mesh& mesh = m_meshes[meshIndex]; @@ -160,7 +171,9 @@ namespace AZ const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap, const ShaderInputContract::StreamChannelInfo& contractStreamChannel, - StreamInfoList::const_iterator defaultUv) const + StreamInfoList::const_iterator defaultUv, + StreamInfoList::const_iterator firstUv, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const { const Mesh& mesh = m_meshes[meshIndex]; auto iter = mesh.m_streamInfo.end(); @@ -184,8 +197,8 @@ namespace AZ // Cost of linear search UV names is low because the size is extremely limited. return uvNamePair.m_shaderInput == contractStreamChannel.m_semantic; }); - const bool IsUv = materialUvIter != materialUvNameMap.end(); - if (IsUv) + const bool isUv = materialUvIter != materialUvNameMap.end(); + if (isUv) { const AZ::Name& materialUvName = materialUvIter->m_uvName; auto modelUvMapIter = materialModelUvMap.find(materialUvIter->m_shaderInput); @@ -224,17 +237,23 @@ namespace AZ }); } - if (iter == mesh.m_streamInfo.end() && IsUv) + if (iter == mesh.m_streamInfo.end() && isUv) { iter = defaultUv; } + if (isUv && uvStreamTangentBitmaskOut) + { + uvStreamTangentBitmaskOut->ApplyTangent(iter == firstUv ? 0 : UvStreamTangentBitmask::UnassignedTangent); + } + return iter; } bool ModelLod::GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, StreamBufferViewList& streamBufferViewsOut, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, @@ -250,11 +269,17 @@ namespace AZ bool success = true; + // Searching for the first UV in the mesh, so it can be used to paired with tangent/bitangent stream + auto firstUv = FindFirstUvStreamFromMesh(meshIndex); auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); + if (uvStreamTangentBitmaskOut) + { + uvStreamTangentBitmaskOut->Reset(); + } for (auto& contractStreamChannel : contract.m_streamChannels) { - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentBitmaskOut); if (iter == mesh.m_streamInfo.end()) { @@ -340,6 +365,7 @@ namespace AZ const Mesh& mesh = m_meshes[meshIndex]; auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); + auto firstUv = FindFirstUvStreamFromMesh(meshIndex); for (auto& contractStreamChannel : contract.m_streamChannels) { @@ -350,7 +376,7 @@ namespace AZ AZ_Assert(contractStreamChannel.m_streamBoundIndicatorIndex.IsValid(), "m_streamBoundIndicatorIndex was invalid for an optional shader input stream"); - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, nullptr); ShaderOptionValue isStreamBound = (iter == mesh.m_streamInfo.end()) ? ShaderOptionValue{0} : ShaderOptionValue{1}; shaderOptions.SetValue(contractStreamChannel.m_streamBoundIndicatorIndex, isStreamBound); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp new file mode 100644 index 0000000000..829207e406 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp @@ -0,0 +1,71 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace RPI + { + uint32_t UvStreamTangentBitmask::GetFullTangentBitmask() const + { + return m_mask; + } + + uint32_t UvStreamTangentBitmask::GetUvStreamCount() const + { + return m_mask >> (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex); + } + + uint32_t UvStreamTangentBitmask::GetTangentAtUv(uint32_t uvIndex) const + { + return (m_mask >> (BitsPerTangent * uvIndex)) & 0b1111u; + } + + void UvStreamTangentBitmask::ApplyTangent(uint32_t tangentIndex) + { + uint32_t currentSlot = GetUvStreamCount(); + if (currentSlot >= MaxUvSlots) + { + AZ_Error("UV Stream", false, "Reaching the max of avaiblable stream slots."); + return; + } + + if (tangentIndex > UnassignedTangent) + { + AZ_Warning( + "UV Stream", false, + "Tangent index must use %d bits as defined in UvStreamTangentIndex::m_flag. Unassigned index will be applied.", + BitsPerTangent); + tangentIndex = UnassignedTangent; + } + + uint32_t clearMask = 0b1111u << (BitsPerTangent * currentSlot); + clearMask = ~clearMask; + + // Clear the writing bits in case + m_mask &= clearMask; + + // Write the bits to the slot + m_mask |= (tangentIndex << (BitsPerTangent * currentSlot)); + + // Increase the index + m_mask += (1u << (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex)); + } + + void UvStreamTangentBitmask::Reset() + { + m_mask = 0; + } + } +} diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp index 9c6a95e582..3a2a556429 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -522,8 +522,11 @@ namespace AZ } }; - ExecuteOnTimestampQuery(beginQuery); - ExecuteOnPipelineStatisticsQuery(beginQuery); + if (context.GetCommandListIndex() == 0) + { + ExecuteOnTimestampQuery(beginQuery); + ExecuteOnPipelineStatisticsQuery(beginQuery); + } } void RenderPass::EndScopeQuery(const RHI::FrameGraphExecuteContext& context) @@ -533,8 +536,23 @@ namespace AZ query->EndQuery(context); }; - ExecuteOnTimestampQuery(endQuery); - ExecuteOnPipelineStatisticsQuery(endQuery); + // This scopy query implmentation should be replaced by + // [ATOM-5407] [RHI][Core] - Add GPU timestamp and pipeline statistic support for scopes + + // For timestamp query, it's okay to execute across different command lists + if (context.GetCommandListIndex() == context.GetCommandListCount() - 1) + { + ExecuteOnTimestampQuery(endQuery); + } + // For all the other types of queries except timestamp, the query start and end has to be in the same command list + // Here only tracks the PipelineStatistics for the first command list due to that we don't know how many queries are + // needed when AddScopeQueryToFrameGraph is called. + // This implementation leads to an issue that we may not get accurate pipeline statistic data + // for passes which were executed with more than one command list + if (context.GetCommandListIndex() == 0) + { + ExecuteOnPipelineStatisticsQuery(endQuery); + } } void RenderPass::ReadbackScopeQueryResults() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp index 6579e6a98d..3fc2bbd197 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp @@ -150,10 +150,7 @@ namespace AZ } } - if (!shaderVariantTreePendingRequests.empty() || !shaderVariantPendingRequests.empty()) - { - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1000)); - } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1000)); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp index 22287238e9..08f8ff0c5e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp @@ -51,6 +51,8 @@ namespace AZ ViewportContext::~ViewportContext() { + m_aboutToBeDestroyedEvent.Signal(m_id); + AzFramework::WindowNotificationBus::Handler::BusDisconnect(); AzFramework::ViewportRequestBus::Handler::BusDisconnect(); @@ -171,6 +173,21 @@ namespace AZ handler.Connect(m_sceneChangedEvent); } + void ViewportContext::ConnectCurrentPipelineChangedHandler(PipelineChangedEvent::Handler& handler) + { + handler.Connect(m_currentPipelineChangedEvent); + } + + void ViewportContext::ConnectDefaultViewChangedHandler(ViewChangedEvent::Handler& handler) + { + handler.Connect(m_defaultViewChangedEvent); + } + + void ViewportContext::ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler) + { + handler.Connect(m_aboutToBeDestroyedEvent); + } + const AZ::Matrix4x4& ViewportContext::GetCameraViewMatrix() const { return GetDefaultView()->GetWorldToViewMatrix(); @@ -214,6 +231,7 @@ namespace AZ m_defaultView = view; UpdatePipelineView(); + m_defaultViewChangedEvent.Signal(view); m_viewMatrixChangedEvent.Signal(view->GetWorldToViewMatrix()); m_projectionMatrixChangedEvent.Signal(view->GetViewToClipMatrix()); @@ -232,6 +250,7 @@ namespace AZ if (!m_currentPipeline) { m_currentPipeline = m_rootScene ? m_rootScene->FindRenderPipelineForWindow(m_windowContext->GetWindowHandle()) : nullptr; + m_currentPipelineChangedEvent.Signal(m_currentPipeline); } if (auto pipeline = GetCurrentPipeline()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp index 7db5f12560..d6421e1337 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp @@ -296,6 +296,7 @@ namespace AZ ->Method("GetShaderCount", &LuaMaterialFunctorRuntimeContext::GetShaderCount) ->Method("GetShader", &LuaMaterialFunctorRuntimeContext::GetShader) ->Method("GetShaderByTag", &LuaMaterialFunctorRuntimeContext::GetShaderByTag) + ->Method("HasShaderWithTag", &LuaMaterialFunctorRuntimeContext::HasShaderWithTag) ; } @@ -424,6 +425,11 @@ namespace AZ return LuaMaterialFunctorShaderItem{nullptr}; } } + + bool LuaMaterialFunctorRuntimeContext::HasShaderWithTag(const char* shaderTag) + { + return m_runtimeContextImpl->m_shaderCollection->HasShaderTag(AZ::Name{shaderTag}); + } void LuaMaterialFunctorEditorContext::LuaMaterialFunctorEditorContext::Reflect(BehaviorContext* behaviorContext) { diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp index 84e8a4ba65..6b9c503ec2 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp @@ -72,7 +72,15 @@ namespace UnitTest return false; } - bool AssetSystemStub::GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) + bool AssetSystemStub::GenerateRelativeSourcePath( + [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, + [[maybe_unused]] AZStd::string& watchFolder) + { + return false; + } + + bool AssetSystemStub::GetFullSourcePathFromRelativeProductPath( + [[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) { return false; } diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h index c6f4ac891f..48609ed0cb 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h @@ -63,6 +63,8 @@ namespace UnitTest const char* GetAbsoluteDevGameFolderPath() override; const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override; + bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override; bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) override; bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override; bool GetSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp index 980a9ac320..1ec14c6169 100644 --- a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -917,6 +918,43 @@ namespace UnitTest } } + TEST_F(ModelTests, UvStream) + { + AZ::RPI::UvStreamTangentBitmask uvStreamTangentBitmask; + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0u); + + uvStreamTangentBitmask.ApplyTangent(1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x10000001); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 1u); + + uvStreamTangentBitmask.ApplyTangent(5u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x20000051); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 2u); + + uvStreamTangentBitmask.ApplyTangent(100u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(2u), AZ::RPI::UvStreamTangentBitmask::UnassignedTangent); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x30000F51); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 3u); + + for (uint32_t i = 3; i < AZ::RPI::UvStreamTangentBitmask::MaxUvSlots; ++i) + { + uvStreamTangentBitmask.ApplyTangent(0u); + } + + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51); + + AZ_TEST_START_TRACE_SUPPRESSION; + uvStreamTangentBitmask.ApplyTangent(0u); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51); + } + // This class creates a Model with one LOD, whose mesh contains 2 planes. Plane 1 is in the XY plane at Z=-0.5, and // plane 2 is in the XY plane at Z=0.5. The two planes each have 9 quads which have been triangulated. It only has // a position and index buffer. diff --git a/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp b/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp index e55ec8012b..98ff6befdd 100644 --- a/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp @@ -159,7 +159,9 @@ namespace UnitTest const uint32_t ResultSize = sizeof(uint64_t); uint64_t mockData; - const RHI::FrameGraphExecuteContext::Descriptor desc = {}; + RHI::FrameGraphExecuteContext::Descriptor desc = {}; + uint64_t dummyCommandList; + desc.m_commandList = reinterpret_cast(&dummyCommandList); RHI::FrameGraphExecuteContext context(desc); RHI::Scope scope; @@ -209,7 +211,9 @@ namespace UnitTest const uint32_t ResultSize = sizeof(uint64_t) * 4u; uint64_t mockData; - const RHI::FrameGraphExecuteContext::Descriptor desc = {}; + RHI::FrameGraphExecuteContext::Descriptor desc = {}; + uint64_t dummyCommandList; + desc.m_commandList = reinterpret_cast(&dummyCommandList); RHI::FrameGraphExecuteContext context(desc); RHI::Scope scope; @@ -273,7 +277,9 @@ namespace UnitTest const uint32_t ResultSize = sizeof(uint64_t) * 2u; uint64_t mockData; - const RHI::FrameGraphExecuteContext::Descriptor desc = {}; + RHI::FrameGraphExecuteContext::Descriptor desc = {}; + uint64_t dummyCommandList; + desc.m_commandList = reinterpret_cast(&dummyCommandList); RHI::FrameGraphExecuteContext context(desc); RHI::Scope scope; diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 0d5c19758b..6242f3f140 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -57,6 +57,7 @@ set(FILES Include/Atom/RPI.Public/Model/ModelLod.h Include/Atom/RPI.Public/Model/ModelLodUtils.h Include/Atom/RPI.Public/Model/ModelSystem.h + Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h Include/Atom/RPI.Public/Pass/AttachmentReadback.h Include/Atom/RPI.Public/Pass/ComputePass.h Include/Atom/RPI.Public/Pass/CopyPass.h @@ -136,6 +137,7 @@ set(FILES Source/RPI.Public/Model/ModelLod.cpp Source/RPI.Public/Model/ModelLodUtils.cpp Source/RPI.Public/Model/ModelSystem.cpp + Source/RPI.Public/Model/UvStreamTangentBitmask.cpp Source/RPI.Public/Pass/AttachmentReadback.cpp Source/RPI.Public/Pass/ComputePass.cpp Source/RPI.Public/Pass/CopyPass.cpp diff --git a/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material b/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material index 4c3a925e52..c9276216eb 100644 --- a/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material +++ b/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material @@ -17,7 +17,6 @@ "textureMap": "TestData/Textures/cc0/Rock030_2K_Normal.jpg" }, "parallax": { - "enable": true, "algorithm": "POM", "factor": 0.03, "quality": "High", diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material index d9a4aabe2a..415bd36dcf 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material @@ -36,7 +36,6 @@ "diffuseTextureMap": "TestData/Textures/cc0/bark1_disp.jpg" }, "layer1_parallax": { - "enable": true, "factor": 0.02500000037252903, "textureMap": "TestData/Textures/cc0/bark1_disp.jpg" }, @@ -80,7 +79,6 @@ "specularTextureMap": "TestData/Textures/cc0/Tiles009_1K_Displacement.jpg" }, "layer2_parallax": { - "enable": true, "factor": 0.01600000075995922, "textureMap": "TestData/Textures/cc0/Lava004_1K_Displacement.jpg" }, @@ -88,6 +86,10 @@ "textureMap": "TestData/Textures/cc0/Lava004_1K_Roughness.jpg" }, "layer2_uv": { + "center": [ + 0.0, + 0.0 + ], "offsetU": 0.5, "offsetV": 0.25 }, @@ -118,7 +120,6 @@ "diffuseTextureMap": "TestData/Textures/cc0/PaintedMetal003_1K_Displacement.jpg" }, "layer3_parallax": { - "enable": true, "factor": 0.004999999888241291, "textureMap": "TestData/Textures/cc0/PaintedMetal003_1K_Displacement.jpg" }, @@ -131,11 +132,15 @@ "factor": 0.47474750876426699 }, "layer3_uv": { + "center": [ + 0.0, + 0.0 + ], "offsetU": 0.11999999731779099, "rotateDegrees": -57.599998474121097 }, "parallax": { - "enable": true + "quality": "Medium" }, "uv": { "center": [ diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material index 0bf4177db9..64adf317a9 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material @@ -15,7 +15,6 @@ "textureMap": "TestData/Textures/cc0/bark1_norm.jpg" }, "layer1_parallax": { - "enable": true, "factor": 0.03999999910593033, "textureMap": "TestData/Textures/cc0/bark1_disp.jpg" }, @@ -32,7 +31,6 @@ "textureMap": "TestData/Textures/cc0/Rock030_2K_Normal.jpg" }, "layer2_parallax": { - "enable": true, "factor": 0.05299999937415123, "offset": -0.024000000208616258, "textureMap": "TestData/Textures/cc0/Rock030_2K_Displacement.jpg" @@ -50,8 +48,8 @@ "textureMap": "TestData/Textures/cc0/Concrete019_1K_Color.jpg" }, "parallax": { - "enable": true, - "pdo": true + "pdo": true, + "quality": "Medium" } } } \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_DepthMaps.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMask.material similarity index 81% rename from Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_DepthMaps.material rename to Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMask.material index d41e116067..ffcbf3ce7e 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_DepthMaps.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMask.material @@ -4,8 +4,8 @@ "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material", "propertyLayoutVersion": 3, "properties": { - "general": { - "debugDrawMode": "DepthMaps" + "blend": { + "debugDrawMode": "BlendMask" } } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendWeights.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendWeights.material new file mode 100644 index 0000000000..8d13ac781f --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendWeights.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "debugDrawMode": "FinalBlendWeights" + } + } +} diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_Displacement.material similarity index 80% rename from Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material rename to Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_Displacement.material index cdd21212c9..7aff50cb56 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_Displacement.material @@ -4,8 +4,8 @@ "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material", "propertyLayoutVersion": 3, "properties": { - "general": { - "debugDrawMode": "BlendSource" + "blend": { + "debugDrawMode": "Displacement" } } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material index ea3ffab467..ea3ea8b519 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material @@ -5,7 +5,10 @@ "propertyLayoutVersion": 3, "properties": { "blend": { - "blendSource": "VertexColors" + "blendSource": "BlendMaskVertexColors" + }, + "parallax": { + "quality": "Medium" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material new file mode 100644 index 0000000000..9163fe0a0c --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material @@ -0,0 +1,81 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "blendSource": "Displacement", + "displacementBlendDistance": 0.008999999612569809, + "enableLayer2": true, + "enableLayer3": true + }, + "layer1_baseColor": { + "textureMap": "TestData/Textures/cc0/Ground033_1K_Color.jpg" + }, + "layer1_normal": { + "textureMap": "TestData/Textures/cc0/Ground033_1K_Normal.jpg" + }, + "layer1_occlusion": { + "diffuseTextureMap": "TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg" + }, + "layer1_parallax": { + "factor": 0.017000000923871995, + "offset": -0.006000000052154064, + "textureMap": "TestData/Textures/cc0/Ground033_1K_Displacement.jpg" + }, + "layer1_roughness": { + "textureMap": "TestData/Textures/cc0/Ground033_1K_Roughness.jpg" + }, + "layer2_baseColor": { + "textureMap": "TestData/Textures/cc0/Rock030_2K_Color.jpg" + }, + "layer2_normal": { + "textureMap": "TestData/Textures/cc0/Rock030_2K_Normal.jpg" + }, + "layer2_occlusion": { + "diffuseTextureMap": "TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg" + }, + "layer2_parallax": { + "factor": 0.03099999949336052, + "offset": 0.0020000000949949028, + "textureMap": "TestData/Textures/cc0/Rock030_2K_Displacement.jpg" + }, + "layer2_roughness": { + "textureMap": "TestData/Textures/cc0/Rock030_2K_Roughness.jpg" + }, + "layer2_uv": { + "center": [ + 0.0, + 0.0 + ], + "offsetU": 0.1599999964237213, + "offsetV": 0.07999999821186066, + "rotateDegrees": 90.0 + }, + "layer3_baseColor": { + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Color.jpg" + }, + "layer3_normal": { + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Normal.jpg" + }, + "layer3_parallax": { + "factor": 0.027000000700354577, + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Displacement.jpg" + }, + "layer3_roughness": { + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Roughness.jpg" + }, + "layer3_uv": { + "center": [ + 0.0, + 0.0 + ], + "scale": 3.4999988079071047 + }, + "parallax": { + "algorithm": "Relief", + "pdo": true + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer2Off.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer2Off.material new file mode 100644 index 0000000000..9413e35128 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer2Off.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "enableLayer2": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer3Off.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer3Off.material new file mode 100644 index 0000000000..93e0b21780 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer3Off.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "enableLayer3": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material new file mode 100644 index 0000000000..6b062f6d1e --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material @@ -0,0 +1,14 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "blendSource": "Displacement_With_BlendMaskTexture" + }, + "layer1_parallax": { + "offset": -0.004000000189989805 + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_AllSameHeight.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_AllSameHeight.material new file mode 100644 index 0000000000..0e6519afd4 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_AllSameHeight.material @@ -0,0 +1,23 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "displacementBlendDistance": 0.0010000000474974514 + }, + "layer1_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + }, + "layer2_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + }, + "layer3_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_NoHeightmaps.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_NoHeightmaps.material new file mode 100644 index 0000000000..b5b5656084 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_NoHeightmaps.material @@ -0,0 +1,20 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material", + "propertyLayoutVersion": 3, + "properties": { + "layer1_parallax": { + "offset": -0.03200000151991844, + "textureMap": "" + }, + "layer2_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + }, + "layer3_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskVertexColors.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskVertexColors.material new file mode 100644 index 0000000000..8461ea429c --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskVertexColors.material @@ -0,0 +1,15 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "blendSource": "Displacement_With_BlendMaskVertexColors", + "displacementBlendDistance": 0.02387000061571598 + }, + "parallax": { + "enable": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_TintedTransparent.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_TintedTransparent.material new file mode 100644 index 0000000000..1716792af1 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_TintedTransparent.material @@ -0,0 +1,23 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "baseColor": { + "color": [ + 0.5906767249107361, + 1.0, + 0.11703670024871826, + 1.0 + ], + "textureMap": "Textures/Default/default_basecolor.tif" + }, + "opacity": { + "alphaSource": "Split", + "factor": 0.75, + "mode": "TintedTransparent", + "textureMap": "TestData/Textures/checker8x8_gray_512.png" + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material index a94d90d04d..ed070d5de2 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material @@ -8,7 +8,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "quality": "High", "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png" diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material index fb862dc5d3..f5ec0e8287 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material @@ -13,13 +13,15 @@ "textureMap": "TestData/Textures/checker8x8_512.png" }, "parallax": { - "algorithm": "POM", - "enable": true, "factor": 0.10000000149011612, "quality": "High", "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png" }, "uv": { + "center": [ + 0.0, + 0.0 + ], "scale": 0.5 } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material index dbaf6cb587..38adbc70cd 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material @@ -6,7 +6,6 @@ "properties": { "subsurfaceScattering": { "enableSubsurfaceScattering": true, - "enableTransmission": true, "scatterDistance": 64.6464614868164, "subsurfaceScatterFactor": 1.0, "thicknessMap": "TestData/Textures/checker8x8_512.png", diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material index 7bf12e5358..b3e69212db 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material @@ -13,7 +13,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.10000000149011612, "quality": "High", "textureMap": "TestData/Objects/cube/cube_diff.tif" diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material index 4b9f233a85..3d52f3b9e6 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material @@ -13,7 +13,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.05000000074505806, "quality": "High", "textureMap": "TestData/Objects/cube/cube_diff.tif" diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material index 7b1f0ba6a9..6d77be5a49 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material @@ -8,18 +8,24 @@ "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, + "detailUV": { + "center": [ + 0.0, + 0.0 + ] + }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png", + "textureMap": "Objects/Lucy/Lucy_Normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png", "textureMapUv": "Unwrapped" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material index 55a01866b5..1a29f392c8 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_bronze_baseColor.png", + "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, "detailLayerGroup": { @@ -19,20 +19,24 @@ "normalDetailStrength": 1.5 }, "detailUV": { + "center": [ + 0.0, + 0.0 + ], "scale": 10.0 }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png", + "textureMap": "Objects/Lucy/Lucy_Normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png", "textureMapUv": "Unwrapped" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material index 6193cf4eed..a69b72b623 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_bronze_baseColor.png", + "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, "detailLayerGroup": { @@ -18,20 +18,24 @@ "normalDetailStrength": 1.5 }, "detailUV": { + "center": [ + 0.0, + 0.0 + ], "scale": 10.0 }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png", + "textureMap": "Objects/Lucy/Lucy_Normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png", "textureMapUv": "Unwrapped" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo b/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo index 2ec31e38ce..1aa896a8d7 100644 --- a/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo +++ b/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg new file mode 100644 index 0000000000..04d871dd2b --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9ec269d03a9552c0ecf24778912fa6190c412b006433db8b7c40e1c0c83e6f7 +size 516915 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Color.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Color.jpg new file mode 100644 index 0000000000..d41658d177 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Color.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b53c4aca020d5833618a5b73b6578c8693dd14e603eb32681e2c7ecc90f59047 +size 1020622 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Displacement.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Displacement.jpg new file mode 100644 index 0000000000..913b35875c --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Displacement.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46ea8a9406ce6df21ce3a6045aac5b1421ce119919ff050951277dd76464ee2c +size 258716 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Normal.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Normal.jpg new file mode 100644 index 0000000000..7c216001a9 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Normal.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac1ad9bea240374bfc6aaaacc42e24eadfbf93c7185617fc1c52e610cb45cb69 +size 1292910 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Roughness.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Roughness.jpg new file mode 100644 index 0000000000..5ac9e78409 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Roughness.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fce266a7445a4d4b5bb3fbf7cb3330e0580b058aeaf2ef7f265a01641dbbdc4 +size 637326 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg new file mode 100644 index 0000000000..772529329e --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca54f762bcddc10ab2d609f10864328cb5a567c43efb8c9bcfe9cb2955db9577 +size 433887 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Color.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Color.jpg new file mode 100644 index 0000000000..8d1d23451d --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Color.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b5f5ff297aef6470045d087cf0c3341f7782e36a750965a052d49d03dd37426 +size 1595449 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Displacement.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Displacement.jpg new file mode 100644 index 0000000000..22304c19dd --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Displacement.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8753448320e0916a70036ef77a06bce746bfa45c14e62b2fbda0987a13bfbb3 +size 277114 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Normal.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Normal.jpg new file mode 100644 index 0000000000..129ecbb1b7 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Normal.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ea23ea8d85d1fffa119e481e6ac85ad3a3096470a5e12f252d37e1b293e5e37 +size 2119669 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Roughness.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Roughness.jpg new file mode 100644 index 0000000000..dc5f8c20a9 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Roughness.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caa6e94f904135461da4d200fc6c21c86dc99cc7d413ea740e678f7758b2b156 +size 555680 diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h similarity index 87% rename from Code/Sandbox/Editor/ModernViewportCameraController.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 39e3c9cbb3..1318deb355 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -12,17 +12,16 @@ #pragma once -#include - #include +#include #include #include #include -namespace SandboxEditor +namespace AtomToolsFramework { class ModernViewportCameraControllerInstance; - class ModernViewportCameraController + class ModularViewportCameraController : public AzFramework::MultiViewportController< ModernViewportCameraControllerInstance, AzFramework::ViewportControllerPriority::DispatchToAllPriorities> { @@ -39,19 +38,19 @@ namespace SandboxEditor }; class ModernViewportCameraControllerInstance final - : public AzFramework::MultiViewportControllerInstanceInterface, - public ModernViewportCameraControllerRequestBus::Handler, + : public AzFramework::MultiViewportControllerInstanceInterface, + public ModularViewportCameraControllerRequestBus::Handler, private AzFramework::ViewportDebugDisplayEventBus::Handler { public: - explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModernViewportCameraController* controller); + explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModularViewportCameraController* controller); ~ModernViewportCameraControllerInstance() override; // MultiViewportControllerInstanceInterface overrides ... bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override; void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override; - // ModernViewportCameraControllerRequestBus overrides ... + // ModularViewportCameraControllerRequestBus overrides ... void InterpolateToTransform(const AZ::Transform& worldFromLocal) override; private: @@ -76,4 +75,4 @@ namespace SandboxEditor AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler; }; -} // namespace SandboxEditor +} // namespace AtomToolsFramework diff --git a/Code/Sandbox/Editor/ModernViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h similarity index 78% rename from Code/Sandbox/Editor/ModernViewportCameraControllerRequestBus.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index 966facc8e9..5b90119372 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -20,11 +20,11 @@ namespace AZ class Transform; } -namespace SandboxEditor +namespace AtomToolsFramework { //! Provides an interface to control the modern viewport camera controller from the Editor. //! @note The bus is addressed by viewport id. - class ModernViewportCameraControllerRequests : public AZ::EBusTraits + class ModularViewportCameraControllerRequests : public AZ::EBusTraits { public: using BusIdType = AzFramework::ViewportId; @@ -35,8 +35,8 @@ namespace SandboxEditor virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0; protected: - ~ModernViewportCameraControllerRequests() = default; + ~ModularViewportCameraControllerRequests() = default; }; - using ModernViewportCameraControllerRequestBus = AZ::EBus; -} // namespace SandboxEditor + using ModularViewportCameraControllerRequestBus = AZ::EBus; +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index a41c8221f0..f2c120dbcd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -98,6 +98,7 @@ namespace AtomToolsFramework AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + float DeviceScalingFactor() override; //! Set interface for providing viewport specific settings (e.g. snapping properties). void SetViewportSettings(const AzToolsFramework::ViewportInteraction::ViewportSettings* viewportSettings); diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp similarity index 91% rename from Code/Sandbox/Editor/ModernViewportCameraController.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 0779542878..6fb3edfa22 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -10,10 +10,9 @@ * */ -#include "ModernViewportCameraController.h" - #include #include +#include #include #include #include @@ -23,7 +22,7 @@ #include #include -namespace SandboxEditor +namespace AtomToolsFramework { // debug void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) @@ -53,12 +52,12 @@ namespace SandboxEditor return viewportContext; } - void ModernViewportCameraController::SetCameraListBuilderCallback(const CameraListBuilder& builder) + void ModularViewportCameraController::SetCameraListBuilderCallback(const CameraListBuilder& builder) { m_cameraListBuilder = builder; } - void ModernViewportCameraController::SetupCameras(AzFramework::Cameras& cameras) + void ModularViewportCameraController::SetupCameras(AzFramework::Cameras& cameras) { if (m_cameraListBuilder) { @@ -67,8 +66,8 @@ namespace SandboxEditor } ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance( - const AzFramework::ViewportId viewportId, ModernViewportCameraController* controller) - : MultiViewportControllerInstanceInterface(viewportId, controller) + const AzFramework::ViewportId viewportId, ModularViewportCameraController* controller) + : MultiViewportControllerInstanceInterface(viewportId, controller) { controller->SetupCameras(m_cameraSystem.m_cameras); @@ -88,12 +87,12 @@ namespace SandboxEditor } AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); - ModernViewportCameraControllerRequestBus::Handler::BusConnect(viewportId); + ModularViewportCameraControllerRequestBus::Handler::BusConnect(viewportId); } ModernViewportCameraControllerInstance::~ModernViewportCameraControllerInstance() { - ModernViewportCameraControllerRequestBus::Handler::BusDisconnect(); + ModularViewportCameraControllerRequestBus::Handler::BusDisconnect(); AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect(); } @@ -182,4 +181,4 @@ namespace SandboxEditor m_transformStart = m_camera.Transform(); m_transformEnd = worldFromLocal; } -} // namespace SandboxEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index cee178c724..35edb3af5b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -313,8 +313,7 @@ namespace AtomToolsFramework // Scale the size by the DPI of the platform to // get the proper size in pixels. const QSize uiWindowSize = size(); - const qreal deficePixelRatio = devicePixelRatioF(); - const QSize windowSize = uiWindowSize * deficePixelRatio; + const QSize windowSize = uiWindowSize * devicePixelRatioF(); const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); @@ -465,6 +464,11 @@ namespace AtomToolsFramework return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection}; } + float RenderViewportWidget::DeviceScalingFactor() + { + return aznumeric_cast(devicePixelRatioF()); + } + AzFramework::ScreenPoint RenderViewportWidget::ViewportCursorScreenPosition() { return AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(m_mousePosition.toPoint()); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake index d8ceccc724..f28ba89b92 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake @@ -24,6 +24,8 @@ set(FILES Include/AtomToolsFramework/Util/MaterialPropertyUtil.h Include/AtomToolsFramework/Util/Util.h Include/AtomToolsFramework/Viewport/RenderViewportWidget.h + Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h + Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h Source/Communication/LocalServer.cpp Source/Communication/LocalSocket.cpp Source/Debug/TraceRecorder.cpp @@ -38,4 +40,5 @@ set(FILES Source/Util/MaterialPropertyUtil.cpp Source/Util/Util.cpp Source/Viewport/RenderViewportWidget.cpp + Source/Viewport/ModularViewportCameraController.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index c7265e0039..b08f5b1165 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -182,9 +183,10 @@ namespace MaterialEditor m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId); m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId); m_shadowCatcherEntity->CreateComponent(azrtti_typeid()); + m_shadowCatcherEntity->CreateComponent(azrtti_typeid()); m_shadowCatcherEntity->Activate(); - AZ::TransformBus::Event(m_shadowCatcherEntity->GetId(), &AZ::TransformBus::Events::SetLocalScale, AZ::Vector3{ 100, 100, 1.0 }); + AZ::NonUniformScaleRequestBus::Event(m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 }); AZ::Data::AssetId shadowCatcherModelAssetId = RPI::AssetUtils::GetAssetIdForProductPath("materialeditor/viewportmodels/plane_1x1.azmodel", RPI::AssetUtils::TraceLevel::Error); AZ::Render::MeshComponentRequestBus::Event(m_shadowCatcherEntity->GetId(), diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp index 6a4bb8c0f4..887019787b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp @@ -71,6 +71,8 @@ namespace MaterialEditor QObject::connect(m_ui->m_materialTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this]() { UpdateMaterialTypeSelection(); }); QObject::connect(m_ui->m_materialTypeComboBox, &QComboBox::currentTextChanged, this, [this]() { UpdateMaterialTypeSelection(); }); + m_ui->m_materialTypeComboBox->model()->sort(0, Qt::AscendingOrder); + // Select the default material type from settings auto settings = AZ::UserSettings::CreateFind(AZ::Crc32("MaterialDocumentSettings"), AZ::UserSettings::CT_GLOBAL); diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material index 25e29b55e5..82b1cdb590 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material @@ -26,7 +26,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material index 336d479b30..03fb0ea5be 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material @@ -31,7 +31,8 @@ "algorithm": "ContactRefinement", "factor": 0.019999999552965165, "quality": "Ultra", - "textureMap": "Materials/Concrete016_8K/Concrete016_8K_Displacement.png" + "textureMap": "Materials/Concrete016_8K/Concrete016_8K_Displacement.png", + "useTexture": false }, "roughness": { "textureMap": "Materials/Concrete016_8K/Concrete016_8K_Roughness.png" diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material index 72610e8bb6..7d8c3d5142 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material @@ -20,7 +20,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.004999999888241291, "quality": "Ultra", "textureMap": "Materials/Fabric001_8K/Fabric001_8K_Displacement.png" diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material index 458ab811b6..493bfab455 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material @@ -20,7 +20,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.0020000000949949028, "pdo": true, "quality": "Medium", diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material index 57163f520d..a2bb2c7704 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material @@ -19,7 +19,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.009999999776482582, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material index 625d872475..f75490c2ad 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material @@ -30,7 +30,8 @@ "factor": 0.02500000037252903, "pdo": true, "quality": "Ultra", - "textureMap": "Materials/Bricks038_8K/Bricks038_8K_Displacement.png" + "textureMap": "Materials/Bricks038_8K/Bricks038_8K_Displacement.png", + "useTexture": false }, "roughness": { "factor": 0.4343433976173401, diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material index e3c6d9eae6..d7e2050dbd 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material @@ -22,7 +22,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material b/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material index ab9f366ff6..d4e2016022 100644 --- a/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material +++ b/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material @@ -19,7 +19,8 @@ }, "parallax": { "factor": 0.0010101000079885126, - "textureMap": "Materials/ConcreteStucco/concrete_stucco_height.jpg" + "textureMap": "Materials/ConcreteStucco/concrete_stucco_height.jpg", + "useTexture": false }, "specularF0": { "factor": 0.5050504803657532 diff --git a/Gems/AtomContent/Sponza/Assets/license.txt b/Gems/AtomContent/Sponza/Assets/license.txt new file mode 100644 index 0000000000..e303d8c767 --- /dev/null +++ b/Gems/AtomContent/Sponza/Assets/license.txt @@ -0,0 +1,8 @@ +The content in this gem "O3DE\Gems\AtomContent\Sponza" is ported +from the original source, and modified for the O3DE Engine and Atom Renderer. + +The original "Crytek Sponza" scene data can be downloaded from the +"McGuire Computer Graphics Archive": https://casual-effects.com/data/ + +The original content is under the "CC BY 3.0" License: +https://creativecommons.org/licenses/by/3.0/ \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material index 1102fb150a..d95e84121c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material @@ -41,7 +41,8 @@ "factor": 0.050999999046325687, "pdo": true, "quality": "High", - "textureMap": "Textures/arch_1k_height.png" + "textureMap": "Textures/arch_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/arch_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material index c1853250d7..710f790419 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material @@ -47,7 +47,8 @@ "factor": 0.03099999949336052, "pdo": true, "quality": "High", - "textureMap": "Textures/background_1k_height.png" + "textureMap": "Textures/background_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/background_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material index a269098b4d..26d64c7db9 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material @@ -46,7 +46,8 @@ "algorithm": "ContactRefinement", "factor": 0.03500000014901161, "quality": "Medium", - "textureMap": "Textures/bricks_1k_height.png" + "textureMap": "Textures/bricks_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/bricks_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material index 94225cd00e..95d08d398b 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material @@ -48,7 +48,8 @@ "factor": 0.019999999552965165, "pdo": true, "quality": "Medium", - "textureMap": "Textures/ceiling_1k_height.png" + "textureMap": "Textures/ceiling_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/ceiling_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material index 8f1cea8649..cc1f685c7c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material @@ -47,7 +47,8 @@ "factor": 0.017000000923871995, "pdo": true, "quality": "High", - "textureMap": "Textures/columnA_1k_height.png" + "textureMap": "Textures/columnA_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/columnA_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material index ac474d7e76..a1e8747f65 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material @@ -46,7 +46,8 @@ "factor": 0.020999999716877939, "pdo": true, "quality": "High", - "textureMap": "Textures/columnB_1k_height.png" + "textureMap": "Textures/columnB_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/columnB_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material index 81fd03fc4a..6edbfde47c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material @@ -47,7 +47,8 @@ "factor": 0.014000000432133675, "pdo": true, "quality": "High", - "textureMap": "Textures/columnC_1k_height.png" + "textureMap": "Textures/columnC_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/columnC_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material index fde599fd4c..1b66a51ec0 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material @@ -38,7 +38,8 @@ "algorithm": "POM", "factor": 0.02500000037252903, "pdo": true, - "textureMap": "Textures/details_1k_height.png" + "textureMap": "Textures/details_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/details_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material index e50e8a0ed2..19010d66e5 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material @@ -41,7 +41,8 @@ "factor": 0.014000000432133675, "pdo": true, "quality": "High", - "textureMap": "Textures/flagpole_1k_height.png" + "textureMap": "Textures/flagpole_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/flagpole_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material index 064a2b24a6..bee92e0edb 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material @@ -43,7 +43,8 @@ "algorithm": "POM", "factor": 0.012000000104308129, "pdo": true, - "textureMap": "Textures/floor_1k_height.png" + "textureMap": "Textures/floor_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/floor_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material index 269e1e5684..ac326ae935 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material @@ -42,7 +42,8 @@ "mode": "Cutout" }, "parallax": { - "textureMap": "Textures/thorn_height.png" + "textureMap": "Textures/thorn_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/thorn_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material index 8dc4852b03..b1f78aa33f 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material @@ -38,7 +38,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.009999999776482582, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material index 0a7246703c..a3e066a438 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material @@ -35,7 +35,8 @@ "algorithm": "ContactRefinement", "factor": 0.019999999552965165, "quality": "Medium", - "textureMap": "Textures/roof_1k_height.png" + "textureMap": "Textures/roof_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/roof_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material index 77adc798a0..dea9aa2a8a 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material @@ -41,7 +41,8 @@ "factor": 0.027000000700354577, "pdo": true, "quality": "High", - "textureMap": "Textures/vase_1k_height.png" + "textureMap": "Textures/vase_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/vase_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material index 22e78f03ae..b2a342dd76 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material @@ -41,7 +41,8 @@ "factor": 0.04600000008940697, "pdo": true, "quality": "High", - "textureMap": "Textures/vaseHanging_1k_height.png" + "textureMap": "Textures/vaseHanging_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/vaseHanging_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material index c773146b51..fba07379c0 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material @@ -45,7 +45,8 @@ "factor": 0.019999999552965165, "pdo": true, "quality": "High", - "textureMap": "Textures/vaseRound_1k_height.png" + "textureMap": "Textures/vaseRound_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/vaseRound_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/stub b/Gems/AtomContent/Sponza/Assets/stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt index c431746b40..0723ca46b7 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt @@ -106,5 +106,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AtomFont Gem::AtomToolsFramework.Editor Gem::AtomViewportDisplayInfo + Gem::AtomViewportDisplayIcons.Editor ) endif() diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h new file mode 100644 index 0000000000..f77e0b0b88 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h @@ -0,0 +1,44 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ::AtomBridge +{ + //! A simple interface for allocating a DynamicDrawContext on-demand for every viewport, based on + //! a registered initialization function. + class PerViewportDynamicDrawInterface + { + public: + AZ_RTTI(PerViewportDynamicDrawInterface, "{1FF054F5-55FF-4ADB-A86D-640B15FA0395}"); + + using DrawContextFactory = AZStd::function)>; + //! Register a named dynamic draw context that can be retrieved on a per-viewport basis. + //! GetNamedDynamicDraw context can be called on a registered context name to retrieve a + //! valid DynamicDrawContext for a given viewport. + virtual void RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) = 0; + + //! Unregister a previously registered named per-viewport dynamic draw context. + //! This will dispose of all dynamic draw contexts currently associated with this name. + virtual void UnregisterDynamicDrawContext(AZ::Name name) = 0; + + //! Get a dynamic draw context associated with the specified viewport based on a factory registered with + //! RegisterNamedDynamicDrawContext. This dynamic draw context will be created if it does not already exist. + virtual RHI::Ptr GetDynamicDrawContextForViewport(AZ::Name name, AzFramework::ViewportId viewportId) = 0; + }; + + using PerViewportDynamicDraw = AZ::Interface; +} // namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp index 9148cdba6f..5116d8a228 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -104,10 +105,12 @@ namespace AZ AzFramework::GameEntityContextRequestBus::BroadcastResult(m_entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect(); + m_dynamicDrawManager = AZStd::make_unique(); } void AtomBridgeSystemComponent::Deactivate() { + m_dynamicDrawManager.reset(); AZ::RPI::ViewportContextManagerNotificationsBus::Handler::BusDisconnect(); RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); // Check if scene is emptry since scene might be released already when running AtomSampleViewer @@ -158,47 +161,8 @@ namespace AZ void AtomBridgeSystemComponent::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { - AZStd::shared_ptr windowContext; - AZ::Render::Bootstrap::DefaultWindowBus::BroadcastResult(windowContext, &AZ::Render::Bootstrap::DefaultWindowInterface::GetDefaultWindowContext); - - if (!windowContext) - { - AZ_Warning("Atom", false, "Cannot initialize Atom because no window context is available"); - return; - } - - AZ::RPI::RenderPipelinePtr renderPipeline = bootstrapScene->GetDefaultRenderPipeline(); - - // If RenderPipeline doesn't have a default view, create a view and make it the default view. - // These settings will be overridden by the editor or game camera. - if (renderPipeline->GetDefaultView() == nullptr) - { - auto viewContextManager = AZ::Interface::Get(); - m_view = AZ::RPI::View::CreateView(AZ::Name("AtomSystem Default View"), RPI::View::UsageCamera); - viewContextManager->PushView(viewContextManager->GetDefaultViewportContextName(), m_view); - const auto& viewport = windowContext->GetViewport(); - const float aspectRatio = viewport.m_maxX / viewport.m_maxY; - - // Note: This is projection assumes a setup for reversed depth - AZ::Matrix4x4 viewToClipMatrix; - AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, aspectRatio, 0.1f, 100.f, true); - - m_view->SetViewToClipMatrix(viewToClipMatrix); - - renderPipeline = bootstrapScene->GetDefaultRenderPipeline(); - renderPipeline->SetDefaultView(m_view); - } - else - { - m_view = renderPipeline->GetDefaultView(); - } - auto auxGeomFP = bootstrapScene->GetFeatureProcessor(); - if (auxGeomFP) - { - auxGeomFP->GetOrCreateDrawQueueForView(m_view.get()); - } - - // Make default AtomDebugDisplayViewportInterface for the scene + AZ_UNUSED(bootstrapScene); + // Make default AtomDebugDisplayViewportInterface AZStd::shared_ptr mainEntityDebugDisplay = AZStd::make_shared(AzFramework::g_defaultSceneEntityDebugDisplayId); m_activeViewportsList[AzFramework::g_defaultSceneEntityDebugDisplayId] = mainEntityDebugDisplay; } diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h index da75d3c35c..4d05ba9285 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h @@ -33,6 +33,7 @@ namespace AZ { // forward declares class AtomDebugDisplayViewportInterface; + class PerViewportDynamicDrawManager; class AtomBridgeSystemComponent : public Component @@ -82,6 +83,7 @@ namespace AZ RPI::ViewPtr m_view = nullptr; AZStd::unordered_map > m_activeViewportsList; + AZStd::unique_ptr m_dynamicDrawManager; }; } } // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp new file mode 100644 index 0000000000..b6ff116a86 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp @@ -0,0 +1,119 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "PerViewportDynamicDrawManager.h" + +#include +#include + +namespace AZ::AtomBridge +{ + PerViewportDynamicDrawManager::PerViewportDynamicDrawManager() + { + PerViewportDynamicDraw::Register(this); + } + + PerViewportDynamicDrawManager::~PerViewportDynamicDrawManager() + { + PerViewportDynamicDraw::Unregister(this); + } + + void PerViewportDynamicDrawManager::RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + const bool alreadyRegistered = m_registeredDrawContexts.find(name) != m_registeredDrawContexts.end(); + AZ_Error("AtomBridge", !alreadyRegistered, "Attempted to call RegisterDynamicDrawContext for already registered name: \"%s\"", name.GetCStr()); + if (alreadyRegistered) + { + return; + } + m_registeredDrawContexts[name] = contextInitializer; + } + + void PerViewportDynamicDrawManager::UnregisterDynamicDrawContext(AZ::Name name) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + auto drawContextFactoryIt = m_registeredDrawContexts.find(name); + const bool registered = drawContextFactoryIt != m_registeredDrawContexts.end(); + AZ_Error("AtomBridge", registered, "Attempted to call UnregisterDynamicDrawContext for unregistered name: \"%s\"", name.GetCStr()); + if (!registered) + { + return; + } + m_registeredDrawContexts.erase(drawContextFactoryIt); + + for (auto& viewportData : m_viewportData) + { + viewportData.second.m_dynamicDrawContexts.erase(name); + } + } + + RHI::Ptr PerViewportDynamicDrawManager::GetDynamicDrawContextForViewport( + AZ::Name name, AzFramework::ViewportId viewportId) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + auto contextFactoryIt = m_registeredDrawContexts.find(name); + if (contextFactoryIt == m_registeredDrawContexts.end()) + { + return nullptr; + } + + auto viewportContextManager = RPI::ViewportContextRequests::Get(); + RPI::ViewportContextPtr viewportContext = viewportContextManager->GetViewportContextById(viewportId); + if (viewportContext == nullptr) + { + return nullptr; + } + + // Get or create a ViewportData if one doesn't already exist + ViewportData& viewportData = m_viewportData[viewportId]; + if (!viewportData.m_initialized) + { + viewportData.m_pipelineChangedHandler = AZ::Event::Handler([this, viewportId](RPI::RenderPipelinePtr pipeline) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + ViewportData& viewportData = m_viewportData[viewportId]; + for (auto& context : viewportData.m_dynamicDrawContexts) + { + context.second->SetRenderPipeline(pipeline.get()); + } + }); + viewportData.m_viewportDestroyedHandler = AZ::Event::Handler([this, viewportId](AzFramework::ViewportId id) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + m_viewportData.erase(id); + }); + + viewportContext->ConnectCurrentPipelineChangedHandler(viewportData.m_pipelineChangedHandler); + viewportContext->ConnectAboutToBeDestroyedHandler(viewportData.m_viewportDestroyedHandler); + + viewportData.m_initialized = true; + } + + RHI::Ptr& context = viewportData.m_dynamicDrawContexts[name]; + if (context == nullptr) + { + auto pipeline = viewportContext->GetCurrentPipeline().get(); + if (pipeline == nullptr) + { + return nullptr; + } + context = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(pipeline); + contextFactoryIt->second(context); + } + + return context; + } +} //namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h new file mode 100644 index 0000000000..e2b442915c --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h @@ -0,0 +1,48 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ::AtomBridge +{ + class PerViewportDynamicDrawManager final : public PerViewportDynamicDrawInterface + { + public: + AZ_TYPE_INFO(PerViewportDynamicDrawManager, "{BED66185-00A7-43F7-BD28-C56BC8E4C535}"); + + PerViewportDynamicDrawManager(); + ~PerViewportDynamicDrawManager(); + + // PerViewportDynamicDrawInterface overrides... + void RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) override; + void UnregisterDynamicDrawContext(AZ::Name name) override; + RHI::Ptr GetDynamicDrawContextForViewport(AZ::Name name, AzFramework::ViewportId viewportId) override; + + private: + struct ViewportData + { + AZStd::unordered_map> m_dynamicDrawContexts; + + // Event handlers + AZ::Event::Handler m_pipelineChangedHandler; + AZ::Event::Handler m_viewportDestroyedHandler; + + // Cached state + bool m_initialized = false; + }; + AZStd::map m_viewportData; + AZStd::unordered_map m_registeredDrawContexts; + AZStd::mutex m_mutexDrawContexts; + }; +} //namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake b/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake index f272d323ae..969030f922 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake +++ b/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake @@ -12,10 +12,13 @@ set(FILES Include/AtomBridge/AtomBridgeBus.h Include/AtomBridge/FlyCameraInputBus.h + Include/AtomBridge/PerViewportDynamicDrawInterface.h Source/AtomBridgeSystemComponent.cpp Source/AtomBridgeSystemComponent.h - Source/FlyCameraInputComponent.cpp - Source/FlyCameraInputComponent.h Source/AtomDebugDisplayViewportInterface.cpp Source/AtomDebugDisplayViewportInterface.h + Source/FlyCameraInputComponent.cpp + Source/FlyCameraInputComponent.h + Source/PerViewportDynamicDrawManager.cpp + Source/PerViewportDynamicDrawManager.h ) diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp index 3c76a9c788..623a931ac9 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp @@ -896,7 +896,7 @@ AZ::RHI::Ptr AZ::AtomFont::GetOrCreateDynamicDrawFo shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false"))); shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true"))); dynamicDraw->InitShaderWithVariant(shader, &shaderOptions); - dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::R8G8B8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}}); + dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::B8G8R8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}}); dynamicDraw->EndInit(); // exclusive lock while writing diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index f8afaa7260..31eb089803 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -54,7 +54,6 @@ #include -static const AZ::Vector2 UiDraw_TextSizeFactor = AZ::Vector2(12.0f, 12.0f); static const int TabCharCount = 4; // set buffer sizes to hold max characters that can be drawn in 1 DrawString call static const size_t MaxVerts = 8 * 1024; // 2048 quads @@ -864,7 +863,7 @@ int AZ::FFont::CreateQuadsForText(const RHI::Viewport& viewport, float x, float if (drawFrame) { ColorB tempColor(255, 255, 255, 255); - uint32_t frameColor = tempColor.pack_abgr8888(); //note: this ends up in r,g,b,a order on little-endian machines + uint32_t frameColor = tempColor.pack_argb8888(); //note: this ends up in r,g,b,a order on little-endian machines Vec2 textSize = GetTextSizeUInternal(viewport, str, asciiMultiLine, ctx); @@ -1122,7 +1121,7 @@ int AZ::FFont::CreateQuadsForText(const RHI::Viewport& viewport, float x, float { ColorB tempColor = color; tempColor.a = ((uint32_t) tempColor.a * alphaBlend) >> 8; - packedColor = tempColor.pack_abgr8888(); //note: this ends up in r,g,b,a order on little-endian machines + packedColor = tempColor.pack_argb8888(); //note: this ends up in r,g,b,a order on little-endian machines } if (ctx.m_drawTextFlags & eDrawText_UseTransform) @@ -1673,6 +1672,12 @@ static void SetCommonContextFlags(AZ::TextDrawContext& ctx, const AzFramework::T { ctx.m_drawTextFlags |= eDrawText_FixedSize; } + + if (params.m_useTransform) + { + ctx.m_drawTextFlags |= eDrawText_UseTransform; + ctx.SetTransform(AZMatrix3x4ToLYMatrix3x4(params.m_transform)); + } } AZ::FFont::DrawParameters AZ::FFont::ExtractDrawParameters(const AzFramework::TextDrawParameters& params, AZStd::string_view text, bool forceCalculateSize) @@ -1696,22 +1701,25 @@ AZ::FFont::DrawParameters AZ::FFont::ExtractDrawParameters(const AzFramework::Te } internalParams.m_ctx.SetBaseState(GS_NODEPTHTEST); internalParams.m_ctx.SetColor(AZColorToLYColorF(params.m_color)); + internalParams.m_ctx.SetEffect(params.m_effectIndex); internalParams.m_ctx.SetCharWidthScale((params.m_monospace || params.m_scaleWithWindow) ? 0.5f : 1.0f); internalParams.m_ctx.EnableFrame(false); internalParams.m_ctx.SetProportional(!params.m_monospace && params.m_scaleWithWindow); internalParams.m_ctx.SetSizeIn800x600(params.m_scaleWithWindow && params.m_virtual800x600ScreenSize); - internalParams.m_ctx.SetSize(AZVec2ToLYVec2(UiDraw_TextSizeFactor * params.m_scale)); + internalParams.m_ctx.SetSize(AZVec2ToLYVec2(AZ::Vector2(params.m_textSizeFactor, params.m_textSizeFactor) * params.m_scale)); internalParams.m_ctx.SetLineSpacing(params.m_lineSpacing); - if (params.m_monospace || !params.m_scaleWithWindow) - { - ScaleCoord(viewport, posX, posY); - } if (params.m_hAlign != AzFramework::TextHorizontalAlignment::Left || params.m_vAlign != AzFramework::TextVerticalAlignment::Top || forceCalculateSize) { + // We align based on the size of the default font effect because we do not want the + // text to move when the font effect is changed + unsigned int effectIndex = internalParams.m_ctx.m_fxIdx; + internalParams.m_ctx.SetEffect(0); Vec2 textSize = GetTextSizeUInternal(viewport, text.data(), params.m_multiline, internalParams.m_ctx); + internalParams.m_ctx.SetEffect(effectIndex); + // If we're using virtual 800x600 coordinates, convert the text size from // pixels to that before using it as an offset. if (internalParams.m_ctx.m_sizeIn800x600) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl new file mode 100644 index 0000000000..c4367efe6b --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl @@ -0,0 +1,77 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + + #include + +ShaderResourceGroup InstanceSrg : SRG_PerDraw +{ + float2 m_viewportSize; + Texture2D m_texture; + + Sampler m_sampler + { + MaxAnisotropy = 16; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +}; + +struct VSInput +{ + float3 m_position : POSITION; + float4 m_color : COLOR0; + float2 m_uv : TEXCOORD0; +}; + +struct VSOutput +{ + float4 m_position : SV_Position; + float4 m_color : COLOR0; + float2 m_uv : TEXCOORD0; +}; + +VSOutput MainVS(VSInput IN) +{ + // Convert from screen space to clip space + float2 posXY = float2(IN.m_position.xy) / InstanceSrg::m_viewportSize * 2.0f - float2(1.0f, 1.0f); + posXY.y *= -1.0f; + float4 posPS = float4(posXY, IN.m_position.z, 1.0f); + + VSOutput OUT; + OUT.m_position = posPS; + OUT.m_color = IN.m_color; + OUT.m_uv = IN.m_uv; + return OUT; +}; + +struct PSOutput +{ + float4 m_color : SV_Target0; +}; + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + float4 tex; + + tex = InstanceSrg::m_texture.Sample(InstanceSrg::m_sampler, IN.m_uv); + float opacity = IN.m_color.a * tex.a; + + // We use pre-multiplied alpha here since it is more flexible. For example, it enables alpha-blended rendering to + // a render target and then alpha blending that render target into another render target + OUT.m_color.rgb = IN.m_color.rgb * tex.rgb * opacity; + + OUT.m_color.a = opacity; + return OUT; +}; diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader new file mode 100644 index 0000000000..601a2664b5 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader @@ -0,0 +1,39 @@ +{ + "Source" : "TexturedIcon", + + "DepthStencilState" : { + "Depth" : { + "Enable" : false, + "CompareFunc" : "Always" + } + }, + + "RasterState" : { + "DepthClipEnable" : false, + "CullMode" : "None" + }, + + "BlendState" : { + "Enable" : true, + "BlendSource" : "One", + "BlendDest" : "AlphaSourceInverse", + "BlendOp" : "Add" + }, + + "DrawList" : "2dpass", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt new file mode 100644 index 0000000000..20a680bce9 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(Code) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt new file mode 100644 index 0000000000..b3e176c7a3 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME AtomViewportDisplayIcons.Editor ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + atomviewportdisplayicons_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework + AZ::AzToolsFramework + AZ::AtomCore + 3rdParty::Qt::Core + 3rdParty::Qt::Gui + 3rdParty::Qt::Svg + Gem::Atom_RHI.Reflect + Gem::Atom_RPI.Public + Gem::Atom_Bootstrap.Headers + Gem::Atom_AtomBridge.Static + ) +endif() diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp new file mode 100644 index 0000000000..5fe8c50350 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -0,0 +1,357 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "AtomViewportDisplayIconsSystemComponent.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace AZ::Render +{ + void AtomViewportDisplayIconsSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("Viewport Display Icons", "Provides an interface for drawing simple icons to the Editor viewport") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) + ->Attribute(Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void AtomViewportDisplayIconsSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("ViewportDisplayIconsService")); + } + + void AtomViewportDisplayIconsSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("ViewportDisplayIconsService")); + } + + void AtomViewportDisplayIconsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC("RPISystem", 0xf2add773)); + required.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99)); + } + + void AtomViewportDisplayIconsSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void AtomViewportDisplayIconsSystemComponent::Activate() + { + AzToolsFramework::EditorViewportIconDisplay::Register(this); + + Bootstrap::NotificationBus::Handler::BusConnect(); + } + + void AtomViewportDisplayIconsSystemComponent::Deactivate() + { + Bootstrap::NotificationBus::Handler::BusDisconnect(); + + auto perViewportDynamicDrawInterface = AtomBridge::PerViewportDynamicDraw::Get(); + if (!perViewportDynamicDrawInterface) + { + return; + } + if (perViewportDynamicDrawInterface) + { + perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName); + } + + AzToolsFramework::EditorViewportIconDisplay::Unregister(this); + } + + void AtomViewportDisplayIconsSystemComponent::DrawIcon(const DrawParameters& drawParameters) + { + // Ensure we have a valid viewport context & dynamic draw interface + auto viewportContext = RPI::ViewportContextRequests::Get()->GetViewportContextById(drawParameters.m_viewport); + if (viewportContext == nullptr) + { + return; + } + + auto perViewportDynamicDrawInterface = + AtomBridge::PerViewportDynamicDraw::Get(); + if (!perViewportDynamicDrawInterface) + { + return; + } + + RHI::Ptr dynamicDraw = + perViewportDynamicDrawInterface->GetDynamicDrawContextForViewport(m_drawContextName, drawParameters.m_viewport); + if (dynamicDraw == nullptr) + { + return; + } + + // Find our icon, falling back on a grey placeholder if its image is unavailable + AZ::Data::Instance image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Grey); + if (auto iconIt = m_iconData.find(drawParameters.m_icon); iconIt != m_iconData.end()) + { + auto& iconData = iconIt->second; + if (iconData.m_image) + { + image = iconData.m_image; + } + } + else + { + return; + } + + // Initialize our shader + AZ::Vector2 viewportSize; + { + AzFramework::WindowSize viewportWindowSize = viewportContext->GetViewportSize(); + viewportSize = AZ::Vector2{aznumeric_cast(viewportWindowSize.m_width), aznumeric_cast(viewportWindowSize.m_height)}; + } + AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); + drawSrg->SetConstant(m_viewportSizeIndex,viewportSize); + drawSrg->SetImageView(m_textureParameterIndex, image->GetImageView()); + drawSrg->Compile(); + + // Scale icons by screen DPI + float scalingFactor = 1.0f; + { + using ViewportRequestBus = AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus; + ViewportRequestBus::EventResult( + scalingFactor, drawParameters.m_viewport, &ViewportRequestBus::Events::DeviceScalingFactor); + } + + AZ::Vector3 screenPosition; + if (drawParameters.m_positionSpace == CoordinateSpace::ScreenSpace) + { + screenPosition = drawParameters.m_position; + } + else if (drawParameters.m_positionSpace == CoordinateSpace::WorldSpace) + { + // Calculate our screen space position using the viewport size + // We want this instead of RenderViewportWidget::WorldToScreen which works in QWidget virtual coordinate space + AzFramework::ScreenPoint position = AzFramework::WorldToScreen( + drawParameters.m_position, viewportContext->GetCameraViewMatrix(), viewportContext->GetCameraProjectionMatrix(), + viewportSize); + screenPosition.SetX(aznumeric_cast(position.m_x)); + screenPosition.SetY(aznumeric_cast(position.m_y)); + } + + struct Vertex + { + float m_position[3]; + AZ::u32 m_color; + float m_uv[2]; + }; + using Indice = AZ::u16; + + // Create a vertex offset from the position to draw from based on the icon size + // Vertex positions are in screen space coordinates + auto createVertex = [&](float offsetX, float offsetY, float u, float v) -> Vertex + { + Vertex vertex; + screenPosition.StoreToFloat3(vertex.m_position); + vertex.m_position[0] += offsetX * drawParameters.m_size.GetX() * scalingFactor; + vertex.m_position[1] += offsetY * drawParameters.m_size.GetY() * scalingFactor; + vertex.m_color = drawParameters.m_color.ToU32(); + vertex.m_uv[0] = u; + vertex.m_uv[1] = v; + return vertex; + }; + + AZStd::array vertices = { + createVertex(-0.5f, -0.5f, 0.f, 0.f), + createVertex(0.5f, -0.5f, 1.f, 0.f), + createVertex(0.5f, 0.5f, 1.f, 1.f), + createVertex(-0.5f, 0.5f, 0.f, 1.f) + }; + AZStd::array indices = {0, 1, 2, 0, 2, 3}; + dynamicDraw->DrawIndexed(&vertices, vertices.size(), &indices, indices.size(), RHI::IndexFormat::Uint16, drawSrg); + } + + QString AtomViewportDisplayIconsSystemComponent::FindAssetPath(const QString& path) const + { + // If we get an absolute path, just use it. + QFileInfo pathInfo(path); + if (pathInfo.isAbsolute()) + { + return path; + } + + bool found = false; + AZStd::vector scanFolders; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + found, &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, scanFolders); + if (!found) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to load asset scan folders"); + return QString(); + } + + for (const auto& folder : scanFolders) + { + QDir dir(folder.data()); + if (dir.exists(path)) + { + return dir.absoluteFilePath(path); + } + } + + return QString(); + } + + QImage AtomViewportDisplayIconsSystemComponent::RenderSvgToImage(const QString& svgPath) const + { + // Set up our SVG renderer + QSvgRenderer renderer(svgPath); + renderer.setAspectRatioMode(Qt::KeepAspectRatio); + + // Set up our target image + QSize size = renderer.defaultSize().expandedTo(MinimumRenderedSvgSize); + QImage image(size, QtImageFormat); + image.fill(0x00000000); + + // Render the SVG + QPainter painter(&image); + renderer.render(&painter); + return image; + } + + AZ::Data::Instance AtomViewportDisplayIconsSystemComponent::ConvertToAtomImage(AZ::Uuid assetId, QImage image) const + { + // Ensure our image is in the correct pixel format so we can memcpy it to our renderer image + image.convertTo(QtImageFormat); + Data::Instance streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); + return RPI::StreamingImage::CreateFromCpuData( + *streamingImagePool.get(), + RHI::ImageDimension::Image2D, + RHI::Size(image.width(), image.height(), 1), + RHI::Format::R8G8B8A8_UNORM_SRGB, + image.bits(), + image.sizeInBytes(), + assetId); + } + + AzToolsFramework::EditorViewportIconDisplayInterface::IconId AtomViewportDisplayIconsSystemComponent::GetOrLoadIconForPath( + AZStd::string_view path) + { + // Check our cache to see if the image is already loaded + auto existingEntryIt = AZStd::find_if(m_iconData.begin(), m_iconData.end(), [&path](const auto& iconData) + { + return iconData.second.m_path == path; + }); + if (existingEntryIt != m_iconData.end()) + { + return existingEntryIt->first; + } + + AZ::Uuid assetId = AZ::Uuid::CreateName(path.data()); + + // Find the asset to load on disk + QString assetPath = FindAssetPath(path.data()); + if (assetPath.isEmpty()) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to locate icon on disk: \"%s\"", path.data()); + return InvalidIconId; + } + + QImage loadedImage; + + AZStd::string extension; + AzFramework::StringFunc::Path::GetExtension(path.data(), extension, false); + // For SVGs, we need to actually rasterize to an image + if (extension == "svg") + { + loadedImage = RenderSvgToImage(assetPath); + } + // For everything else, we can just load it through QImage via its image plugins + else + { + const bool loaded = loadedImage.load(assetPath); + if (!loaded) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to load icon: \"%s\"", assetPath.toUtf8().constData()); + return InvalidIconId; + } + } + + // Cache our loaded icon + IconId id = m_currentId++; + IconData& iconData = m_iconData[id]; + iconData.m_path = path; + iconData.m_image = ConvertToAtomImage(assetId, loadedImage); + return id; + } + + AzToolsFramework::EditorViewportIconDisplayInterface::IconLoadStatus AtomViewportDisplayIconsSystemComponent::GetIconLoadStatus( + IconId icon) + { + auto iconIt = m_iconData.find(icon); + if (iconIt == m_iconData.end()) + { + return IconLoadStatus::Unloaded; + } + if (iconIt->second.m_image) + { + return IconLoadStatus::Loaded; + } + return IconLoadStatus::Error; + } + + void AtomViewportDisplayIconsSystemComponent::OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene) + { + AtomBridge::PerViewportDynamicDraw::Get()->RegisterDynamicDrawContext(m_drawContextName, [](RPI::Ptr drawContext) + { + auto shader = RPI::LoadShader(DrawContextShaderPath); + drawContext->InitShader(shader); + drawContext->InitVertexFormat( + {{"POSITION", RHI::Format::R32G32B32_FLOAT}, + {"COLOR", RHI::Format::R8G8B8A8_UNORM}, + {"TEXCOORD", RHI::Format::R32G32_FLOAT}}); + drawContext->EndInit(); + }); + } +} // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h new file mode 100644 index 0000000000..b44957b51d --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -0,0 +1,82 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace AZ +{ + class TickRequests; + + namespace Render + { + class AtomViewportDisplayIconsSystemComponent + : public AZ::Component + , public AzToolsFramework::EditorViewportIconDisplayInterface + , public AZ::Render::Bootstrap::NotificationBus::Handler + { + public: + AZ_COMPONENT(AtomViewportDisplayIconsSystemComponent, "{AEC1D3E1-1D9A-437A-B4C6-CFAEE620C160}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + protected: + // AZ::Component overrides... + void Activate() override; + void Deactivate() override; + + // AzToolsFramework::EditorViewportIconDisplayInterface overrides... + void DrawIcon(const DrawParameters& drawParameters) override; + IconId GetOrLoadIconForPath(AZStd::string_view path) override; + IconLoadStatus GetIconLoadStatus(IconId icon) override; + + // AZ::Render::Bootstrap::NotificationBus::Handler overrides... + void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override; + + private: + static constexpr const char* DrawContextShaderPath = "Shaders/TexturedIcon.azshader"; + static constexpr QSize MinimumRenderedSvgSize = QSize(128, 128); + static constexpr QImage::Format QtImageFormat = QImage::Format_RGBA8888; + + QString FindAssetPath(const QString& path) const; + QImage RenderSvgToImage(const QString& svgPath) const; + AZ::Data::Instance ConvertToAtomImage(AZ::Uuid assetId, QImage image) const; + + Name m_drawContextName = Name("ViewportIconDisplay"); + bool m_shaderIndexesInitialized = false; + RHI::ShaderInputNameIndex m_textureParameterIndex = "m_texture"; + RHI::ShaderInputNameIndex m_viewportSizeIndex = "m_viewportSize"; + + struct IconData + { + AZStd::string m_path; + AZ::Data::Instance m_image = nullptr; + }; + AZStd::unordered_map m_iconData; + IconId m_currentId = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp new file mode 100644 index 0000000000..db7672186a --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp @@ -0,0 +1,51 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +#include "AtomViewportDisplayIconsSystemComponent.h" + +namespace AZ +{ + namespace Render + { + class AtomViewportDisplayInfoModule + : public AZ::Module + { + public: + AZ_RTTI(AtomViewportDisplayInfoModule, "{8D72F14E-958D-4225-B3BC-C5C87BDDD426}", AZ::Module); + AZ_CLASS_ALLOCATOR(AtomViewportDisplayInfoModule, AZ::SystemAllocator, 0); + + AtomViewportDisplayInfoModule() + : AZ::Module() + { + m_descriptors.insert(m_descriptors.end(), { + AtomViewportDisplayIconsSystemComponent::CreateDescriptor(), + }); + } + + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; + } // namespace Render +} // namespace AZ + +// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM +// The first parameter should be GemName_GemIdLower +// The second should be the fully qualified name of the class above +AZ_DECLARE_MODULE_CLASS(Gem_AtomViewportDisplayInfo, AZ::Render::AtomViewportDisplayInfoModule) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake new file mode 100644 index 0000000000..f02aed0856 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake @@ -0,0 +1,16 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Source/AtomViewportDisplayIconsSystemComponent.cpp + Source/AtomViewportDisplayIconsSystemComponent.h + Source/Module.cpp +) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp index 672c26a9ab..7d830b4ca9 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp @@ -18,10 +18,11 @@ #include #include +#include +#include #include #include #include -#include #include #include @@ -146,7 +147,7 @@ namespace AZ::Render if (m_updateRootPassQuery) { - if (auto rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass()) + if (auto rootPass = viewportContext->GetCurrentPipeline()->GetRootPass()) { rootPass->SetPipelineStatisticsQueryEnabled(displayLevel != AtomBridge::ViewportInfoDisplayState::CompactInfo); m_updateRootPassQuery = false; @@ -226,7 +227,8 @@ namespace AZ::Render void AtomViewportDisplayInfoSystemComponent::DrawPassInfo() { - auto rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass(); + AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + auto rootPass = viewportContext->GetCurrentPipeline()->GetRootPass(); const RPI::PipelineStatisticsResult stats = rootPass->GetLatestPipelineStatisticsResult(); AZStd::function)> containingPassCount = [&containingPassCount](const AZ::RPI::Ptr pass) { diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json deleted file mode 100644 index dd92a99ea9..0000000000 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "gem_name": "AtomLyIntegration_AtomViewportDisplayInfo", - "display_name": "Atom Viewport Display Info Overlay", - "summary": "Provides a diagnostic viewport overlay for the default O3DE Atom viewport.", - "canonical_tags": [ - "Gem" - ], - "user_tags": [ - "AtomLyIntegration", - "AtomViewportDisplayInfo" - ] -} \ No newline at end of file diff --git a/Gems/AtomLyIntegration/CMakeLists.txt b/Gems/AtomLyIntegration/CMakeLists.txt index 35022e643b..ff6800a7ff 100644 --- a/Gems/AtomLyIntegration/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(AtomFont) add_subdirectory(TechnicalArt) add_subdirectory(AtomBridge) add_subdirectory(AtomViewportDisplayInfo) +add_subdirectory(AtomViewportDisplayIcons) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp index 138d619d97..4d43d75406 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.cpp @@ -243,14 +243,14 @@ namespace AZ { // apply offset in world-space finalTransform = m_targetEntityTransform * m_targetBoneTransform; - finalTransform.SetScale(AZ::Vector3::CreateOne()); + finalTransform.SetUniformScale(1.0f); finalTransform *= m_targetOffset; } else if (m_scaleSource == AttachmentConfiguration::ScaleSource::TargetEntityScale) { // apply offset in target-entity-space (ignoring bone scale) AZ::Transform boneNoScale = m_targetBoneTransform; - boneNoScale.SetScale(AZ::Vector3::CreateOne()); + boneNoScale.SetUniformScale(1.0f); finalTransform = m_targetEntityTransform * boneNoScale * m_targetOffset; } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp index 3b50c0a48c..f14340b4c9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp @@ -21,18 +21,42 @@ namespace AZ { namespace Render { + bool EditorAttachmentComponentVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + if (classElement.GetVersion() < 2) + { + float uniformScaleOffset = 1.0f; + + int scaleElementIndex = classElement.FindElement(AZ_CRC_CE("Scale Offset")); + if (scaleElementIndex != -1) + { + AZ::Vector3 oldScaleValue = AZ::Vector3::CreateOne(); + AZ::SerializeContext::DataElementNode& dataElementNode = classElement.GetSubElement(scaleElementIndex); + if (dataElementNode.GetData(oldScaleValue)) + { + uniformScaleOffset = oldScaleValue.GetMaxElement(); + } + classElement.RemoveElement(scaleElementIndex); + } + + classElement.AddElementWithData(context, "Uniform Scale Offset", uniformScaleOffset); + } + + return true; + } + void EditorAttachmentComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() - ->Version(1) + ->Version(2, &EditorAttachmentComponentVersionConverter) ->Field("Target ID", &EditorAttachmentComponent::m_targetId) ->Field("Target Bone Name", &EditorAttachmentComponent::m_targetBoneName) ->Field("Position Offset", &EditorAttachmentComponent::m_positionOffset) ->Field("Rotation Offset", &EditorAttachmentComponent::m_rotationOffset) - ->Field("Scale Offset", &EditorAttachmentComponent::m_scaleOffset) + ->Field("Uniform Scale Offset", &EditorAttachmentComponent::m_uniformScaleOffset) ->Field("Attached Initially", &EditorAttachmentComponent::m_attachedInitially) ->Field("Scale Source", &EditorAttachmentComponent::m_scaleSource); @@ -70,7 +94,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::Min, -AZ::RadToDeg(AZ::Constants::TwoPi)) ->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::TwoPi)) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged) - ->DataElement(0, &EditorAttachmentComponent::m_scaleOffset, "Scale offset", "Local scale offset from target entity") + ->DataElement(0, &EditorAttachmentComponent::m_uniformScaleOffset, "Scale offset", "Local scale offset from target entity") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Min, 0.001f) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged) @@ -128,7 +152,7 @@ namespace AZ { AZ::Transform offset = AZ::ConvertEulerDegreesToTransform(m_rotationOffset); offset.SetTranslation(m_positionOffset); - offset.MultiplyByScale(m_scaleOffset); + offset.MultiplyByUniformScale(m_uniformScaleOffset); return offset; } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h index cac8a71a94..0f44043344 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h @@ -88,7 +88,7 @@ namespace AZ AZ::Vector3 m_rotationOffset = AZ::Vector3::CreateZero(); //! Offset from target entity's scale. - AZ::Vector3 m_scaleOffset = AZ::Vector3::CreateOne(); + float m_uniformScaleOffset = 1.0f; //! Observe scale information from the specified source. AttachmentConfiguration::ScaleSource m_scaleSource = AttachmentConfiguration::ScaleSource::WorldScale; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/CapsuleLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/CapsuleLightDelegate.cpp index 4ffb917f65..4291ce4d97 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/CapsuleLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/CapsuleLightDelegate.cpp @@ -35,7 +35,7 @@ namespace AZ // This equation is based off of the integration of a line segment against a perpendicular normal pointing at the center of the // line segment from some distance away. - float scale = GetTransform().GetScale().GetMaxElement(); + float scale = GetTransform().GetUniformScale(); float h = GetInteriorHeight() * scale; float t2 = lightThreshold * lightThreshold; float h2 = h * h; @@ -54,7 +54,7 @@ namespace AZ const auto endpoints = m_shapeBus->GetCapsulePoints(); GetFeatureProcessor()->SetCapsuleLineSegment(GetLightHandle(), endpoints.m_begin, endpoints.m_end); - float scale = GetTransform().GetScale().GetMaxElement(); + float scale = GetTransform().GetUniformScale(); float radius = m_shapeBus->GetRadius(); GetFeatureProcessor()->SetCapsuleRadius(GetLightHandle(), scale * radius); } @@ -62,7 +62,7 @@ namespace AZ float CapsuleLightDelegate::GetSurfaceArea() const { - float scale = GetTransform().GetScale().GetMaxElement(); + float scale = GetTransform().GetUniformScale(); float radius = m_shapeBus->GetRadius(); float capsArea = 4.0f * Constants::Pi * radius * radius; // both caps make a sphere float sideArea = 2.0f * Constants::Pi * radius * GetInteriorHeight(); // cylindrical area of capsule @@ -77,7 +77,7 @@ namespace AZ float radius = CalculateAttenuationRadius(AreaLightComponentConfig::CutoffIntensity); // Add on the caps for the attenuation radius - float scale = GetTransform().GetScale().GetMaxElement(); + float scale = GetTransform().GetUniformScale(); float height = m_shapeBus->GetHeight() * scale; debugDisplay.SetColor(color); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index 7805a92cd1..8abc790ada 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -48,7 +48,7 @@ namespace AZ::Render float DiskLightDelegate::GetRadius() const { - return m_shapeBus->GetRadius() * GetTransform().GetScale().GetMaxElement(); + return m_shapeBus->GetRadius() * GetTransform().GetUniformScale(); } void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& /*color*/, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp index a40557f2f1..308a4ebd11 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorDirectionalLightComponent.cpp @@ -217,7 +217,7 @@ namespace AZ GetEntityId(), &TransformBus::Events::GetWorldTM); - transform.ExtractScale(); + transform.ExtractUniformScale(); const Vector3 origin = transform.GetTranslation(); const Vector3 originOffset = origin - (transform.TransformVector(forward) * arrowOffset); const Vector3 target = origin - (transform.TransformVector(forward) * (arrowLength + arrowOffset)); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/PolygonLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/PolygonLightDelegate.cpp index c2bcc566e0..0cc01f4066 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/PolygonLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/PolygonLightDelegate.cpp @@ -50,7 +50,7 @@ namespace AZ AZStd::vector vertices = m_shapeBus->GetPolygonPrism()->m_vertexContainer.GetVertices(); Transform transform = GetTransform(); - transform.SetScale(Vector3(transform.GetScale().GetMaxElement())); // Poly Prism only supports uniform scale, so use max element. + transform.SetUniformScale(transform.GetUniformScale()); // Poly Prism only supports uniform scale. AZStd::vector transformedVertices; transformedVertices.reserve(vertices.size()); @@ -73,7 +73,7 @@ namespace AZ twiceArea += vertices.at(i).GetX() * vertices.at(j).GetY(); twiceArea -= vertices.at(i).GetY() * vertices.at(j).GetX(); } - float scale = GetTransform().GetScale().GetMaxElement(); + float scale = GetTransform().GetUniformScale(); return GetAbs(twiceArea * 0.5f * scale * scale); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp index 2666be6f75..6caa8f31b3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp @@ -76,12 +76,12 @@ namespace AZ float QuadLightDelegate::GetWidth() const { - return m_shapeBus->GetQuadWidth() * GetTransform().GetScale().GetX(); + return m_shapeBus->GetQuadWidth() * GetTransform().GetUniformScale(); } float QuadLightDelegate::GetHeight() const { - return m_shapeBus->GetQuadHeight() * GetTransform().GetScale().GetY(); + return m_shapeBus->GetQuadHeight() * GetTransform().GetUniformScale(); } } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp index e3cf1fac78..afb63dce9b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp @@ -50,7 +50,7 @@ namespace AZ float SphereLightDelegate::GetRadius() const { - return m_shapeBus->GetRadius() * GetTransform().GetScale().GetMaxElement(); + return m_shapeBus->GetRadius() * GetTransform().GetUniformScale(); } void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp index 0ddace1f87..5fb835de15 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp @@ -44,7 +44,17 @@ namespace AZ ->Field("AmbientMultiplier", &DiffuseProbeGridComponentConfig::m_ambientMultiplier) ->Field("ViewBias", &DiffuseProbeGridComponentConfig::m_viewBias) ->Field("NormalBias", &DiffuseProbeGridComponentConfig::m_normalBias) - ; + ->Field("EditorMode", &DiffuseProbeGridComponentConfig::m_editorMode) + ->Field("RuntimeMode", &DiffuseProbeGridComponentConfig::m_runtimeMode) + ->Field("BakedIrradianceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureRelativePath) + ->Field("BakedDistanceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureRelativePath) + ->Field("BakedRelocationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureRelativePath) + ->Field("BakedClassificationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureRelativePath) + ->Field("BakedIrradianceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureAsset) + ->Field("BakedDistanceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureAsset) + ->Field("BakedRelocationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureAsset) + ->Field("BakedClassificationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureAsset) + ; } } @@ -110,6 +120,26 @@ namespace AZ m_boxShapeInterface = LmbrCentral::BoxShapeComponentRequestsBus::FindFirstHandler(m_entityId); AZ_Assert(m_boxShapeInterface, "DiffuseProbeGridComponentController was unable to find box shape component"); + // special handling is required if this component is being cloned in the editor: + // check to see if the baked textures are already referenced by another DiffuseProbeGrid + if (m_featureProcessor->AreBakedTexturesReferenced( + m_configuration.m_bakedIrradianceTextureRelativePath, + m_configuration.m_bakedDistanceTextureRelativePath, + m_configuration.m_bakedRelocationTextureRelativePath, + m_configuration.m_bakedClassificationTextureRelativePath)) + { + // clear the baked texture paths and assets, since they belong to the original entity (not the clone) + m_configuration.m_bakedIrradianceTextureRelativePath.clear(); + m_configuration.m_bakedDistanceTextureRelativePath.clear(); + m_configuration.m_bakedRelocationTextureRelativePath.clear(); + m_configuration.m_bakedClassificationTextureRelativePath.clear(); + + m_configuration.m_bakedIrradianceTextureAsset.Reset(); + m_configuration.m_bakedDistanceTextureAsset.Reset(); + m_configuration.m_bakedRelocationTextureAsset.Reset(); + m_configuration.m_bakedClassificationTextureAsset.Reset(); + } + // add this diffuse probe grid to the feature processor const AZ::Transform& transform = m_transformInterface->GetWorldTM(); m_handle = m_featureProcessor->AddProbeGrid(transform, m_configuration.m_extents, m_configuration.m_probeSpacing); @@ -118,11 +148,61 @@ namespace AZ m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias); m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias); + // load the baked texture assets, but only if they are all valid + if (m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() && + m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() && + m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() && + m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid()) + { + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedIrradianceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedDistanceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedRelocationTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedClassificationTextureAsset.GetId()); + + m_configuration.m_bakedIrradianceTextureAsset.QueueLoad(); + m_configuration.m_bakedDistanceTextureAsset.QueueLoad(); + m_configuration.m_bakedRelocationTextureAsset.QueueLoad(); + m_configuration.m_bakedClassificationTextureAsset.QueueLoad(); + } + else if (m_configuration.m_runtimeMode == DiffuseProbeGridMode::Baked || + m_configuration.m_runtimeMode == DiffuseProbeGridMode::AutoSelect || + m_configuration.m_editorMode == DiffuseProbeGridMode::Baked || + m_configuration.m_editorMode == DiffuseProbeGridMode::AutoSelect) + { + AZ_Error("DiffuseProbeGrid", false, "DiffuseProbeGrid mdoe is set to Baked or Auto-Select, but it does not have baked texture assets. Please re-bake this DiffuseProbeGrid."); + } + + m_featureProcessor->SetMode(m_handle, m_configuration.m_runtimeMode); + // set box shape component dimensions from the configuration // this will invoke the OnShapeChanged() handler and set the outer extents on the feature processor m_boxShapeInterface->SetBoxDimensions(m_configuration.m_extents); } + void DiffuseProbeGridComponentController::OnAssetReady(Data::Asset asset) + { + // if all assets are ready we can set the baked texture images + if (m_configuration.m_bakedIrradianceTextureAsset.IsReady() && + m_configuration.m_bakedDistanceTextureAsset.IsReady() && + m_configuration.m_bakedRelocationTextureAsset.IsReady() && + m_configuration.m_bakedClassificationTextureAsset.IsReady()) + { + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedIrradianceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedDistanceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedRelocationTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedClassificationTextureAsset.GetId()); + + UpdateBakedTextures(); + } + } + + void DiffuseProbeGridComponentController::OnAssetError(Data::Asset asset) + { + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + + AZ_Error("DiffuseProbeGrid", false, "Failed to load baked texture [%s], please re-bake this DiffuseProbeGrid.", asset.GetId().ToString().c_str()); + } + void DiffuseProbeGridComponentController::Deactivate() { if (m_featureProcessor) @@ -212,20 +292,96 @@ namespace AZ void DiffuseProbeGridComponentController::SetAmbientMultiplier(float ambientMultiplier) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_ambientMultiplier = ambientMultiplier; m_featureProcessor->SetAmbientMultiplier(m_handle, m_configuration.m_ambientMultiplier); } void DiffuseProbeGridComponentController::SetViewBias(float viewBias) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_viewBias = viewBias; m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias); } void DiffuseProbeGridComponentController::SetNormalBias(float normalBias) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_normalBias = normalBias; m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias); } + + void DiffuseProbeGridComponentController::SetEditorMode(DiffuseProbeGridMode editorMode) + { + if (!m_featureProcessor) + { + return; + } + + // update the configuration and change the DiffuseProbeGrid mode + m_configuration.m_editorMode = editorMode; + m_featureProcessor->SetMode(m_handle, m_configuration.m_editorMode); + } + + void DiffuseProbeGridComponentController::SetRuntimeMode(DiffuseProbeGridMode runtimeMode) + { + if (!m_featureProcessor) + { + return; + } + + // only update the configuration + m_configuration.m_runtimeMode = runtimeMode; + } + + void DiffuseProbeGridComponentController::BakeTextures(DiffuseProbeGridBakeTexturesCallback callback) + { + if (!m_featureProcessor) + { + return; + } + + m_featureProcessor->BakeTextures( + m_handle, + callback, + m_configuration.m_bakedIrradianceTextureRelativePath, + m_configuration.m_bakedDistanceTextureRelativePath, + m_configuration.m_bakedRelocationTextureRelativePath, + m_configuration.m_bakedClassificationTextureRelativePath); + } + + void DiffuseProbeGridComponentController::UpdateBakedTextures() + { + if (!m_featureProcessor) + { + return; + } + + DiffuseProbeGridBakedTextures bakedTextures; + bakedTextures.m_irradianceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedIrradianceTextureAsset); + bakedTextures.m_irradianceImageRelativePath = m_configuration.m_bakedIrradianceTextureRelativePath; + bakedTextures.m_distanceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedDistanceTextureAsset); + bakedTextures.m_distanceImageRelativePath = m_configuration.m_bakedDistanceTextureRelativePath; + bakedTextures.m_relocationImageDescriptor = m_configuration.m_bakedRelocationTextureAsset->GetImageDescriptor(); + bakedTextures.m_relocationImageData = m_configuration.m_bakedRelocationTextureAsset->GetSubImageData(0, 0); + bakedTextures.m_relocationImageRelativePath = m_configuration.m_bakedRelocationTextureRelativePath; + bakedTextures.m_classificationImageDescriptor = m_configuration.m_bakedClassificationTextureAsset->GetImageDescriptor(); + bakedTextures.m_classificationImageData = m_configuration.m_bakedClassificationTextureAsset->GetSubImageData(0, 0); + bakedTextures.m_classificationImageRelativePath = m_configuration.m_bakedClassificationTextureRelativePath; + + m_featureProcessor->SetBakedTextures(m_handle, bakedTextures); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h index 2bcb4132e9..4122a07ba2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h @@ -39,6 +39,19 @@ namespace AZ float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier; float m_viewBias = DefaultDiffuseProbeGridViewBias; float m_normalBias = DefaultDiffuseProbeGridNormalBias; + + DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime; + DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime; + + AZStd::string m_bakedIrradianceTextureRelativePath; + AZStd::string m_bakedDistanceTextureRelativePath; + AZStd::string m_bakedRelocationTextureRelativePath; + AZStd::string m_bakedClassificationTextureRelativePath; + + Data::Asset m_bakedIrradianceTextureAsset; + Data::Asset m_bakedDistanceTextureAsset; + Data::Asset m_bakedRelocationTextureAsset; + Data::Asset m_bakedClassificationTextureAsset; }; class DiffuseProbeGridComponentController final @@ -79,12 +92,24 @@ namespace AZ // ShapeComponentNotificationsBus overrides void OnShapeChanged(ShapeChangeReasons changeReason) override; + // AssetBus overrides + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + // Property handlers bool ValidateProbeSpacing(const AZ::Vector3& newSpacing); void SetProbeSpacing(const AZ::Vector3& probeSpacing); void SetAmbientMultiplier(float ambientMultiplier); void SetViewBias(float viewBias); void SetNormalBias(float normalBias); + void SetEditorMode(DiffuseProbeGridMode editorMode); + void SetRuntimeMode(DiffuseProbeGridMode runtimeMode); + + // Bake the diffuse probe grid textures to assets + void BakeTextures(DiffuseProbeGridBakeTexturesCallback callback); + + // Update the baked texture assets from the configuration + void UpdateBakedTextures(); // box shape component, used for defining the outer extents of the probe area LmbrCentral::BoxShapeComponentRequests* m_boxShapeInterface = nullptr; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp index ae22ec06c9..1e5b959803 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp @@ -16,6 +16,14 @@ #include #include #include +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT +#include +#include +AZ_POP_DISABLE_WARNING namespace AZ { @@ -35,7 +43,9 @@ namespace AZ ->Field("ambientMultiplier", &EditorDiffuseProbeGridComponent::m_ambientMultiplier) ->Field("viewBias", &EditorDiffuseProbeGridComponent::m_viewBias) ->Field("normalBias", &EditorDiffuseProbeGridComponent::m_normalBias) - ; + ->Field("editorMode", &EditorDiffuseProbeGridComponent::m_editorMode) + ->Field("runtimeMode", &EditorDiffuseProbeGridComponent::m_runtimeMode) + ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { @@ -48,25 +58,26 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) - ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing") + ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing (meters between probes)") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateY) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateZ) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) ->ClassElement(AZ::Edit::ClassElements::Group, "Grid Settings") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_ambientMultiplier, "Ambient Multiplier", "Multiplier for the irradiance intensity") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnAmbientMultiplierChanged) - ->Attribute(Edit::Attributes::Decimals, 0) - ->Attribute(Edit::Attributes::Step, 1.0f) + ->Attribute(Edit::Attributes::Decimals, 1) + ->Attribute(Edit::Attributes::Step, 0.1f) ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 10.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_viewBias, "View Bias", "View bias adjustment") @@ -81,6 +92,27 @@ namespace AZ ->Attribute(Edit::Attributes::Step, 0.1f) ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 1.0f) + ->ClassElement(AZ::Edit::ClassElements::EditorData, "Grid mode") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_editorMode, "Editor Mode", "Controls whether the editor uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available") + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEditorModeChanged) + ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)") + ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked") + ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select") + ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_runtimeMode, "Runtime Mode", "Controls whether the runtime uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available") + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnRuntimeModeChanged) + ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)") + ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked") + ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select") + ->ClassElement(AZ::Edit::ClassElements::Group, "Bake Textures") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->UIElement(AZ::Edit::UIHandlers::Button, "Bake Textures", "Bake the Diffuse Probe Grid textures to static assets that will be used when the mode is set to Baked") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") + ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Textures") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid) + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting) ; editContext->Class( @@ -90,12 +122,6 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseProbeGridComponentController::m_configuration, "Configuration", "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; - - editContext->Class( - "DiffuseProbeGridComponentConfig", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; } } @@ -121,15 +147,73 @@ namespace AZ BaseClass::Activate(); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId()); + AZ::TickBus::Handler::BusConnect(); + AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect(); } void EditorDiffuseProbeGridComponent::Deactivate() { + AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect(); + AZ::TickBus::Handler::BusDisconnect(); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(); AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); BaseClass::Deactivate(); } + void EditorDiffuseProbeGridComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if (!m_controller.m_featureProcessor) + { + return; + } + + DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration; + + // set the editor mode, which will override the runtime mode set by the controller + if (!m_editorModeSet) + { + m_controller.m_featureProcessor->SetMode(m_controller.m_handle, configuration.m_editorMode); + m_editorModeSet = true; + } + + CheckTextureAssetNotification(configuration.m_bakedIrradianceTextureRelativePath, configuration.m_bakedIrradianceTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedDistanceTextureRelativePath, configuration.m_bakedDistanceTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedRelocationTextureRelativePath, configuration.m_bakedRelocationTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedClassificationTextureRelativePath, configuration.m_bakedClassificationTextureAsset); + } + + void EditorDiffuseProbeGridComponent::CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset& configurationAsset) + { + Data::Asset textureAsset; + DiffuseProbeGridTextureNotificationType notificationType = DiffuseProbeGridTextureNotificationType::None; + if (m_controller.m_featureProcessor->CheckTextureAssetNotification(relativePath + ".streamingimage", textureAsset, notificationType)) + { + if (notificationType == DiffuseProbeGridTextureNotificationType::Ready) + { + // bake is complete, update configuration with the new baked texture asset + AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid Texture Bake"); + configurationAsset = { textureAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + SetDirty(); + + if (m_controller.m_configuration.m_bakedIrradianceTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedDistanceTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedClassificationTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedRelocationTextureAsset.IsReady()) + { + m_controller.UpdateBakedTextures(); + } + } + else if (notificationType == DiffuseProbeGridTextureNotificationType::Error) + { + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "Diffuse Probe Grid texture failed to bake, please check the Asset Processor for more information.", + QMessageBox::Ok); + } + } + } + AZ::Aabb EditorDiffuseProbeGridComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) { return m_controller.GetAabb(); @@ -140,11 +224,19 @@ namespace AZ return false; } + void EditorDiffuseProbeGridComponent::OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) + { + if ((GetEntityId() == entityId) && !visible) + { + m_editorModeSet = false; + } + } + AZ::Outcome EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingX = *(reinterpret_cast(newValue)); @@ -152,7 +244,7 @@ namespace AZ Vector3 newSpacing(newProbeSpacingX, m_probeSpacingY, m_probeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -162,7 +254,7 @@ namespace AZ { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingY = *(reinterpret_cast(newValue)); @@ -170,7 +262,7 @@ namespace AZ Vector3 newSpacing(m_probeSpacingX, newProbeSpacingY, m_probeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -180,7 +272,7 @@ namespace AZ { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingZ = *(reinterpret_cast(newValue)); @@ -188,7 +280,7 @@ namespace AZ Vector3 newSpacing(m_probeSpacingX, m_probeSpacingY, newProbeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -218,5 +310,226 @@ namespace AZ m_controller.SetNormalBias(m_normalBias); return AZ::Edit::PropertyRefreshLevels::None; } + + AZ::u32 EditorDiffuseProbeGridComponent::OnEditorModeChanged() + { + // this will update the configuration and also change the DiffuseProbeGrid mode + m_controller.SetEditorMode(m_editorMode); + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + + AZ::u32 EditorDiffuseProbeGridComponent::OnRuntimeModeChanged() + { + // this will only update the configuration + m_controller.SetRuntimeMode(m_runtimeMode); + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZ::Outcome EditorDiffuseProbeGridComponent::OnModeChangeValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) + { + DiffuseProbeGridMode newMode = (*(reinterpret_cast(newValue))); + + if (newMode == DiffuseProbeGridMode::Baked || newMode == DiffuseProbeGridMode::AutoSelect) + { + if (!m_controller.m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid()) + { + return AZ::Failure(AZStd::string("Please bake textures before changing the Diffuse Probe Grid to Baked or Auto-Select mode.")); + } + } + + return AZ::Success(); + } + + AZ::u32 EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting() + { + // the Bake button is visible only when the editor mode is set to RealTime + return m_editorMode == DiffuseProbeGridMode::RealTime ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + + AZ::u32 EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid() + { + if (m_bakeInProgress) + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + // retrieve entity visibility + bool isHidden = false; + AzToolsFramework::EditorEntityInfoRequestBus::EventResult( + isHidden, + GetEntityId(), + &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsHidden); + + // the entity must be visible in order to bake + if (isHidden) + { + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "This Diffuse Probe Grid entity is hidden, it must be visible in order to bake textures.", + QMessageBox::Ok); + + return AZ::Edit::PropertyRefreshLevels::None; + } + + DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration; + + // retrieve the source image paths from the configuration + // Note: we need to make sure to use the same source image for each bake + AZStd::string irradianceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedIrradianceTextureRelativePath, DiffuseProbeGridIrradianceFileName); + AZStd::string distanceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedDistanceTextureRelativePath, DiffuseProbeGridDistanceFileName); + AZStd::string relocationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedRelocationTextureRelativePath, DiffuseProbeGridRelocationFileName); + AZStd::string classificationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedClassificationTextureRelativePath, DiffuseProbeGridClassificationFileName); + + // create the full paths + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + AZStd::string irradianceTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true); + AZStd::string distanceTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, distanceTextureRelativePath.c_str(), distanceTextureFullPath, true, true); + AZStd::string relocationTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, relocationTextureRelativePath.c_str(), relocationTextureFullPath, true, true); + AZStd::string classificationTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, classificationTextureRelativePath.c_str(), classificationTextureFullPath, true, true); + + // make sure the folder is created + AZStd::string diffuseProbeGridFolder; + AzFramework::StringFunc::Path::GetFolderPath(irradianceTextureFullPath.data(), diffuseProbeGridFolder); + AZ::IO::SystemFile::CreateDir(diffuseProbeGridFolder.c_str()); + + // check out the files in source control + CheckoutSourceTextureFile(irradianceTextureFullPath); + CheckoutSourceTextureFile(distanceTextureFullPath); + CheckoutSourceTextureFile(relocationTextureFullPath); + CheckoutSourceTextureFile(classificationTextureFullPath); + + // update the configuration + AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid bake"); + configuration.m_bakedIrradianceTextureRelativePath = irradianceTextureRelativePath; + configuration.m_bakedDistanceTextureRelativePath = distanceTextureRelativePath; + configuration.m_bakedRelocationTextureRelativePath = relocationTextureRelativePath; + configuration.m_bakedClassificationTextureRelativePath = classificationTextureRelativePath; + SetDirty(); + + // callback for the texture readback + DiffuseProbeGridBakeTexturesCallback bakeTexturesCallback = [=]( + DiffuseProbeGridTexture irradianceTexture, + DiffuseProbeGridTexture distanceTexture, + DiffuseProbeGridTexture relocationTexture, + DiffuseProbeGridTexture classificationTexture) + { + // irradiance + { + AZ::DdsFile::DdsFileData fileData = { irradianceTexture.m_size, irradianceTexture.m_format, irradianceTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(irradianceTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Irradiance texture .dds file [%s]", irradianceTextureFullPath.c_str()); + } + + // distance + { + AZ::DdsFile::DdsFileData fileData = { distanceTexture.m_size, distanceTexture.m_format, distanceTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(distanceTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Distance texture .dds file [%s]", distanceTextureFullPath.c_str()); + } + + // relocation + { + AZ::DdsFile::DdsFileData fileData = { relocationTexture.m_size, relocationTexture.m_format, relocationTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(relocationTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Relocation texture .dds file [%s]", relocationTextureFullPath.c_str()); + } + + // classification + { + AZ::DdsFile::DdsFileData fileData = { classificationTexture.m_size, classificationTexture.m_format, classificationTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(classificationTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Classification texture .dds file [%s]", classificationTextureFullPath.c_str()); + } + + m_bakeInProgress = false; + }; + + m_bakeInProgress = true; + m_controller.BakeTextures(bakeTexturesCallback); + + while (m_bakeInProgress) + { + QApplication::processEvents(); + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); + } + + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "Successfully baked Diffuse Probe Grid textures.", + QMessageBox::Ok); + + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZStd::string EditorDiffuseProbeGridComponent::ValidateOrCreateNewTexturePath(const AZStd::string& configurationRelativePath, const char* fileSuffix) + { + AZStd::string relativePath = configurationRelativePath; + AZStd::string fullPath; + + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + if (!relativePath.empty()) + { + // test to see if the texture file is actually there, if it was removed we need to + // generate a new filename, otherwise it will cause an error in the asset system + AzFramework::StringFunc::Path::Join(projectPath, configurationRelativePath.c_str(), fullPath, true, true); + + if (!AZ::IO::FileIOBase::GetInstance()->Exists(fullPath.c_str())) + { + // file does not exist, clear the relative path so we generate a new name + relativePath.clear(); + } + } + + // build a new image path if necessary + if (relativePath.empty()) + { + // the file name is a combination of the entity name, a UUID, and the filemask + Entity* entity = GetEntity(); + AZ_Assert(entity, "DiffuseProbeGrid entity is null"); + + AZ::Uuid uuid = AZ::Uuid::CreateRandom(); + AZStd::string uuidString; + uuid.ToString(uuidString); + + relativePath = "DiffuseProbeGrids/" + entity->GetName() + uuidString + fileSuffix; + + // replace any invalid filename characters + auto invalidCharacters = [](char letter) + { + return + letter == ':' || letter == '"' || letter == '\'' || + letter == '{' || letter == '}' || + letter == '<' || letter == '>'; + }; + AZStd::replace_if(relativePath.begin(), relativePath.end(), invalidCharacters, '_'); + } + + return relativePath; + } + + void EditorDiffuseProbeGridComponent::CheckoutSourceTextureFile(const AZStd::string& fullPath) + { + bool checkedOutSuccessfully = false; + using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; + ApplicationBus::BroadcastResult( + checkedOutSuccessfully, + &ApplicationBus::Events::RequestEditForFileBlocking, + fullPath.c_str(), + "Checking out for edit...", + ApplicationBus::Events::RequestEditProgressCallback()); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h index 2a50c47b81..15c46d45ba 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h @@ -12,8 +12,10 @@ #pragma once +#include #include #include +#include #include #include #include @@ -26,6 +28,8 @@ namespace AZ : public EditorRenderComponentAdapter , private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AZ::TickBus::Handler + , private AzToolsFramework::EditorEntityInfoNotificationBus::Handler { public: using BaseClass = EditorRenderComponentAdapter; @@ -41,10 +45,22 @@ namespace AZ void Deactivate() override; private: + + // AZ::TickBus overrides + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + // EditorComponentSelectionRequestsBus overrides AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override; bool SupportsEditorRayIntersect() override; + // EditorEntityInfoNotifications overrides + void OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) override; + + // helper functions + AZStd::string ValidateOrCreateNewTexturePath(const AZStd::string& relativePath, const char* fileSuffix); + void CheckoutSourceTextureFile(const AZStd::string& fullPath); + void CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset& configurationAsset); + // property change notifications AZ::Outcome OnProbeSpacingValidateX(void* newValue, const AZ::Uuid& valueType); AZ::Outcome OnProbeSpacingValidateY(void* newValue, const AZ::Uuid& valueType); @@ -53,6 +69,13 @@ namespace AZ AZ::u32 OnAmbientMultiplierChanged(); AZ::u32 OnViewBiasChanged(); AZ::u32 OnNormalBiasChanged(); + AZ::u32 OnEditorModeChanged(); + AZ::u32 OnRuntimeModeChanged(); + AZ::Outcome OnModeChangeValidate(void* newValue, const AZ::Uuid& valueType); + + // Button handler + AZ::u32 BakeDiffuseProbeGrid(); + AZ::u32 GetBakeDiffuseProbeGridVisibilitySetting(); // properties float m_probeSpacingX = DefaultDiffuseProbeGridSpacing; @@ -61,6 +84,12 @@ namespace AZ float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier; float m_viewBias = DefaultDiffuseProbeGridViewBias; float m_normalBias = DefaultDiffuseProbeGridNormalBias; + DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime; + DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime; + + // flags + bool m_editorModeSet = false; + AZStd::atomic_bool m_bakeInProgress = false; }; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp index 2c44124564..72cc765238 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp @@ -228,7 +228,7 @@ namespace AZ // remove scale Transform worldNoScale = world; - worldNoScale.ExtractScale(); + worldNoScale.ExtractUniformScale(); AZ::Matrix3x4 transformMatrix = AZ::Matrix3x4::CreateFromTransform(worldNoScale); transformMatrix.StoreToRowMajorFloat12(matrix); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/PhysicalSkyComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/PhysicalSkyComponentController.cpp index 4a6b1608a4..192c1ad509 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/PhysicalSkyComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/PhysicalSkyComponentController.cpp @@ -218,7 +218,7 @@ namespace AZ SunPosition PhysicalSkyComponentController::GetSunTransform(const AZ::Transform& world) { Transform worldNoScale = world; - worldNoScale.ExtractScale(); + worldNoScale.ExtractUniformScale(); AZ::Vector3 sunPositionAtom = worldNoScale.TransformVector(AZ::Vector3(0, -1, 0)); // transform Sun from default position // Convert sun position to Y-up coordinate diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp index bad7c2fe38..ef82792f32 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp @@ -32,32 +32,29 @@ namespace AZ void ReleaseResourcesStep::Start() { - m_context->GetData()->m_defaultMaterialAsset.Release(); - m_context->GetData()->m_defaultModelAsset.Release(); - m_context->GetData()->m_materialAsset.Release(); - m_context->GetData()->m_modelAsset.Release(); + auto data = m_context->GetData(); + + data->m_defaultMaterialAsset.Release(); + data->m_defaultModelAsset.Release(); + data->m_materialAsset.Release(); + data->m_modelAsset.Release(); + data->m_lightingPresetAsset.Release(); - if (m_context->GetData()->m_modelEntity) + if (data->m_modelEntity) { - AzFramework::EntityContextRequestBus::Event(m_context->GetData()->m_entityContext->GetContextId(), - &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_context->GetData()->m_modelEntity); - m_context->GetData()->m_modelEntity = nullptr; + AzFramework::EntityContextRequestBus::Event(data->m_entityContext->GetContextId(), + &AzFramework::EntityContextRequestBus::Events::DestroyEntity, data->m_modelEntity); + data->m_modelEntity = nullptr; } - m_context->GetData()->m_frameworkScene->UnsetSubsystem(); - - m_context->GetData()->m_scene->Deactivate(); - m_context->GetData()->m_scene->RemoveRenderPipeline(m_context->GetData()->m_renderPipeline->GetId()); - RPI::RPISystemInterface::Get()->UnregisterScene(m_context->GetData()->m_scene); - - auto sceneSystem = AzFramework::SceneSystemInterface::Get(); - AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation."); - [[maybe_unused]] bool sceneRemovedSuccessfully = sceneSystem->RemoveScene(m_context->GetData()->m_sceneName); - AZ_Assert( - sceneRemovedSuccessfully, "Thumbnail system was unable to remove scene '%s' from the scene system.", - m_context->GetData()->m_sceneName.c_str()); - m_context->GetData()->m_scene = nullptr; - m_context->GetData()->m_renderPipeline = nullptr; + data->m_scene->Deactivate(); + data->m_scene->RemoveRenderPipeline(data->m_renderPipeline->GetId()); + RPI::RPISystemInterface::Get()->UnregisterScene(data->m_scene); + data->m_frameworkScene->UnsetSubsystem(data->m_scene); + data->m_frameworkScene->UnsetSubsystem(data->m_entityContext.get()); + data->m_scene = nullptr; + data->m_frameworkScene = nullptr; + data->m_renderPipeline = nullptr; } } // namespace Thumbnails } // namespace LyIntegration diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/.gitignore new file mode 100644 index 0000000000..f1a223f90e --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/.gitignore @@ -0,0 +1 @@ +pyside2-tools \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py index 6fd8b03e9e..06b00da981 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py @@ -81,8 +81,8 @@ settings = config.get_config_settings() if __name__ == '__main__': """Run this file as main""" - _G_DEBUG = True - _G_TEST_PYSIDE = True + _G_DEBUG = False + _G_TEST_PYSIDE = False _config = get_dccsi_config() _settings = config.get_config_settings() @@ -121,7 +121,6 @@ if __name__ == '__main__': import PySide2 _LOGGER.info(f'PySide2: {PySide2}') - _LOGGER.info(f'QTFORPYTHON_PATH: {_settings.QTFORPYTHON_PATH}') _LOGGER.info(f'LY_BIN_PATH: {_settings.LY_BIN_PATH}') _LOGGER.info(f'QT_PLUGIN_PATH: {_settings.QT_PLUGIN_PATH}') _LOGGER.info(f'QT_QPA_PLATFORM_PLUGIN_PATH: {_settings.QT_QPA_PLATFORM_PLUGIN_PATH}') diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat index 4a64c43029..ab0708defd 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat @@ -68,14 +68,17 @@ IF "%DEV_REL_PATH%"=="" (set DEV_REL_PATH=..\..\..\..) echo DEV_REL_PATH = %DEV_REL_PATH% :: You can define the project name -:: if not defined we just use the DCCsi path as standin -IF "%LY_PROJECT%"=="" ( - for %%a in (%CD%..\..\..) do set LY_PROJECT=%%~na +IF "%LY_PROJECT_NAME%"=="" ( + for %%a in (%CD%..\..\..) do set LY_PROJECT_NAME=%%~na ) +echo LY_PROJECT_NAME = %LY_PROJECT_NAME% + +:: if not defined we just use the DCCsi path as stand-in +IF "%LY_PROJECT%"=="" (set LY_PROJECT=%CD%) echo LY_PROJECT = %LY_PROJECT% :: set up the default project path (dccsi) -:: if not set we lso use the DCCsi path as standin +:: if not set we also use the DCCsi path as stand-in CD /D ..\..\ IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%CD%) echo LY_PROJECT_PATH = %LY_PROJECT_PATH% @@ -88,7 +91,7 @@ pushd %ABS_PATH% :: Change to root Lumberyard dev dir CD /d %LY_PROJECT_PATH%\%DEV_REL_PATH% -set LY_DEV=%CD% +IF "%LY_DEV%"=="" (set LY_DEV=%CD%) echo LY_DEV = %LY_DEV% :: Restore original directory popd diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt index cc090d922f..21882f3d96 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt @@ -29,7 +29,7 @@ A general goal of the DCCsi is be self-maintained, and to not taint the users in So we boostrap additional access to site-packages in our userSetup.py: "C:\Depot\Lumberyard\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\SDK\Maya\Scripts\userSetup.py" -We don't want users to have to install or use Python2.7 although with maya and possibly other dcc tools we don't have that control. Maya still is on Python2.7, so instead of forcing another install of python we can just use mayapy to manage extensions. +We don't want users to have to install or use Python2.7 although with maya and possibly other dcc tools we don't have that control. Maya 2020 and earlier versions are still on Python2.7, so instead of forcing another install of python we can just use mayapy to manage extensions. Pip may already be installed, you can check like so (your maya install path may be different): diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt index 8fd084dac8..ceb5be4dea 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt @@ -4,14 +4,14 @@ # # pip-compile --generate-hashes requirements.txt # -cachetools==3.1.1 \ - --hash=sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae \ - --hash=sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a - # via -r requirements.txt certifi==2020.6.20 \ --hash=sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3 \ --hash=sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41 # via -r requirements.txt +cachetools==3.1.1 \ + --hash=sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae \ + --hash=sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a + # via -r requirements.txt click==7.1.2 \ --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc @@ -68,6 +68,16 @@ unipath==1.1 \ --hash=sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae \ --hash=sha256:e6257e508d8abbfb6ddd8ec357e33589f1f48b1599127f23b017124d90b0fff7 # via -r requirements.txt +qdarkstyle==3.0.2 \ + --hash=sha256:55d149cf5f40ee297397f1818e091118cefb855a4a9c5c38566c47acd2d8c7ae \ + --hash=sha256:7c791535cc20b3cc1e8e1bf6b88dabe53cb0615983df702be83597e73ada2558 + # via -r c:\temp\requirements.txt +qtpy==1.9.0 \ + --hash=sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d \ + --hash=sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea + # via + # -r c:\temp\requirements.txt + # qdarkstyle wincertstore==0.2 \ --hash=sha256:22d5eebb52df88a8d4014d5cf6d1b6c3a5d469e6c3b2e2854f3a003e48872356 \ --hash=sha256:780bd1557c9185c15d9f4221ea7f905cb20b93f7151ca8ccaed9714dce4b327a diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py index 69e4543a59..ab2d5db480 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py @@ -83,13 +83,13 @@ _LY_DEV = os.getenv(constants.ENVAR_LY_DEV, check_stub='engine.json')) # get/set the project name -_LY_PROJECT_TAG = os.getenv(constants.ENVAR_LY_PROJECT, - config_utils.get_current_project(_LY_DEV)) +_LY_PROJECT_NAME = os.getenv(constants.ENVAR_LY_PROJECT, + config_utils.get_current_project().name) # project cache log dir path _DCCSI_LOG_PATH = Path(os.getenv(constants.ENVAR_DCCSI_LOG_PATH, Path(_LY_DEV, - _LY_PROJECT_TAG, + _LY_PROJECT_NAME, 'Cache', 'pc', 'user', 'log', 'logs'))) @@ -223,7 +223,7 @@ if _G_DEBUG: _LOGGER.debug('MODULE_PATH: {}'.format(_MODULE_PATH)) _LOGGER.debug('LY_DEV_PATH: {}'.format(_LY_DEV)) _LOGGER.debug('DCCSI_PATH: {}'.format(_DCCSIG_PATH)) -_LOGGER.debug('LY_PROJECT_TAG: {}'.format(_LY_PROJECT_TAG)) +_LOGGER.debug('LY_PROJECT_TAG: {}'.format(_LY_PROJECT_NAME)) _LOGGER.debug('DCCSI_LOG_PATH: {}'.format(_DCCSI_LOG_PATH)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 9c920a871d..0a0c6b8337 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -137,8 +137,9 @@ def get_dccsi_config(dccsi_dirpath=return_stub_dir()): # ------------------------------------------------------------------------- -def get_current_project(dev_folder=get_stub_check_path()): - """Uses regex in lumberyard Dev\\bootstrap.cfg to retreive project tag str""" +def get_current_project_cfg(dev_folder=get_stub_check_path()): + """Uses regex in lumberyard Dev\\bootstrap.cfg to retreive project tag str + Note: boostrap.cfg will be deprecated. Don't use this method anymore.""" boostrap_filepath = Path(dev_folder, "bootstrap.cfg") if boostrap_filepath.exists(): bootstrap = open(str(boostrap_filepath), "r") @@ -153,6 +154,33 @@ def get_current_project(dev_folder=get_stub_check_path()): # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +def get_current_project(): + """Gets o3de project via .o3de data in user directory""" + + from azpy.constants import PATH_USER_O3DE_BOOTSTRAP + from collections import OrderedDict + from box import Box + + bootstrap_box = None + + try: + bootstrap_box = Box.from_json(filename=PATH_USER_O3DE_BOOTSTRAP, + encoding="utf-8", + errors="strict", + object_pairs_hook=OrderedDict) + except FileExistsError as e: + _LOGGER.error('File does not exist: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + + if bootstrap_box: + # this seems fairly hard coded - what if the data changes? + project_path=Path(bootstrap_box.Amazon.AzCore.Bootstrap.project_path) + return project_path.resolve() + else: + return None +# ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- def bootstrap_dccsi_py_libs(dccsi_dirpath=return_stub_dir()): """Builds and adds local site dir libs based on py version""" @@ -194,7 +222,11 @@ if __name__ == '__main__': _LOGGER.info('LY_DEV: {}'.format(get_stub_check_path('engine.json'))) - _LOGGER.info('LY_PROJECT: {}'.format(get_current_project(get_stub_check_path('bootstrap.cfg')))) + # this will be deprecated and shouldn't work soon (returns None) + _LOGGER.info('LY_PROJECT: {}'.format(get_current_project_cfg(get_stub_check_path('bootstrap.cfg')))) + + # new o3de version + _LOGGER.info('LY_PROJECT: {}'.format(get_current_project())) _LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(bootstrap_dccsi_py_libs(return_stub_dir('dccsi_stub')))) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py index 792f0faee6..e10221d324 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py @@ -26,6 +26,7 @@ So we can make an update here once that is used elsewhere. import os import sys import site +from os.path import expanduser import logging as _logging # for this module to perform standalone @@ -91,6 +92,9 @@ TAG_DIR_DCCSI_SDK = str('SDK') TAG_DIR_LY_BUILD = str('build') TAG_QT_PLUGIN_PATH = str('QT_PLUGIN_PATH') +TAG_O3DE_FOLDER = str('.o3de') +TAG_O3DE_BOOTSTRAP = str('bootstrap.setreg') + # filesystem markers, stub file names. STUB_LY_DEV = str('engine.json') STUB_LY_ROOT_PROJECT = str('ly_project_stub') @@ -221,10 +225,17 @@ TAG_DEFAULT_PY = str('Launch_pyBASE.bat') # config file stuff FILENAME_DEFAULT_CONFIG = str('DCCSI_config.json') +# new o3de related paths +PATH_USER_O3DE = str('{home}\\{o3de}').format(home=expanduser("~"), + o3de=TAG_O3DE_FOLDER) +PATH_USER_O3DE_REGISTRY = str('{0}\\Registry').format(PATH_USER_O3DE) +PATH_USER_O3DE_BOOTSTRAP = str('{reg}\\{file}').format(reg=PATH_USER_O3DE_REGISTRY, + file=TAG_O3DE_BOOTSTRAP) + #python and site-dir TAG_DCCSI_PY_VERSION_MAJOR = str(3) TAG_DCCSI_PY_VERSION_MINOR = str(7) -TAG_DCCSI_PY_VERSION_RELEASE = str(5) +TAG_DCCSI_PY_VERSION_RELEASE = str(10) TAG_PYTHON_EXE = str('python.exe') TAG_TOOLS_DIR = str('Tools\\Python') TAG_PLATFORM = str('windows') @@ -314,6 +325,7 @@ if __name__ == '__main__': _stash_dict['QTFORPYTHON_PATH'] = Path(PATH_QTFORPYTHON_PATH) _stash_dict['QT_PLUGIN_PATH'] = Path(PATH_QT_PLUGIN_PATH) _stash_dict['SAT_INSTALL_PATH'] = Path(PATH_SAT_INSTALL_PATH) + _stash_dict['PATH_USER_O3DE_BOOTSTRAP'] = Path(PATH_USER_O3DE_BOOTSTRAP) # --------------------------------------------------------------------- # py 2 and 3 compatible iter diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down.png deleted file mode 100644 index fa98bc39a3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:afe9402162c5b4527f12c863d389ee9d75b53a1069b7e177497beba389d91d35 -size 525 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_disabled.png deleted file mode 100644 index eaedad9b31..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dc1e37e22cb75f616d6ada02cce006bae7fb1da515b15afea0fc98fcc542a092 -size 547 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_focus.png deleted file mode 100644 index 170beb53b4..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7c9c4c8c5bdc755cc026aa23044f546010c0d1e079ecba34ceb8f0eb9e44bde -size 530 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_pressed.png deleted file mode 100644 index 32b2aac93a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6111d15f1dc946742b00317bda789a5c625333f65a362f38931dabc50afb2067 -size 518 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left.png deleted file mode 100644 index e84d285f63..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ac79e7fbd6be51465e0b685dca32c1236f95ad76ab8c5877ec73d20a1de4365 -size 546 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_disabled.png deleted file mode 100644 index d21aea9e87..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a86de88cf4ee32c352776caf46d5512d27679da8b571ea7e791287495fad4514 -size 569 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_focus.png deleted file mode 100644 index 6315e4d488..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5c5b8427bd1497006b8adbcbc445f11b07ec388a3398fe2997f65bdc56f2644f -size 565 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_pressed.png deleted file mode 100644 index c01c95df2b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:95ad920de52fd198f1af6d569917be20dc24a39dabdbe6c555c812d424bd9736 -size 541 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right.png deleted file mode 100644 index 7dc1534e3c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb5b2d9b40652764f074dcea9856d6748b0efeac57ef58f306efa999f4b411c1 -size 518 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_disabled.png deleted file mode 100644 index 0bdb8963f1..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6a38dcd5b4078df430fa05780844af3e88e0a8e01c1fa910482e4c217354d728 -size 553 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_focus.png deleted file mode 100644 index 9659eeed4d..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f60b2dfce514a6f558134b942f255ddf81ca3dc77b0d899a87f6d4cbac38e26 -size 543 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_pressed.png deleted file mode 100644 index 8e8ae64a87..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:84379a7bd6ffa75692648c6fce328616e8a009d7ea3a83c2288f82ecce37e729 -size 544 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up.png deleted file mode 100644 index 5137aa3c5f..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d7ee7bfb0c60d4687c8a0dcc38a12ee7cacaa2cbb9eb0ae24faa82b992ade445 -size 512 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_disabled.png deleted file mode 100644 index 7c866337ea..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:571b1afb9c2d7e01f56b75e1526dd0a3ffd49a60a4bcb7981ba34823b44a74c0 -size 538 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_focus.png deleted file mode 100644 index a3eaa49ef3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45d805ec94b8144bf121ba74ee96dd27f2f8c0890b2eca5e78df39cb5266f94d -size 530 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_pressed.png deleted file mode 100644 index 168493204d..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d49428a947ea424fabedd5a08e1d78e0bc57dc8a2d5a231b5d9ec06977870f5a -size 518 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_disabled.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_focus.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_pressed.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed.png deleted file mode 100644 index b964f8985a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:56776b4655640d46eb1031b9c19c2341fdfe1201c774a84bc5c2801fbcfefc37 -size 350 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_disabled.png deleted file mode 100644 index ad619682e6..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d48862b1c68efdf376551d1c35d5d3a68e2ce9809e7c2723f42ece7c77fca009 -size 373 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_focus.png deleted file mode 100644 index 8ea7431745..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62cc47f4b7751e22ffe4b26289ecc632b4a2c4e5c33ac79864fcfb32398b1139 -size 380 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_pressed.png deleted file mode 100644 index 54a60293b0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad6e74b57c8876fa28c3a43d1a38369415790507d65d758ea3e77b796c401da2 -size 372 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end.png deleted file mode 100644 index 0fc0630627..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:63324c154ead46027729bcf307ba45fbeb5a8de3ec5e8cef55d315a84a087115 -size 142 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_disabled.png deleted file mode 100644 index 68a6b95488..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a8b091785c84d37de57aacb7fe5a9b854933cc94b9b6f1f6e0b155879c7d6d0 -size 146 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_focus.png deleted file mode 100644 index 84307b3375..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f89a6b105df07325dcf7bdcea2165bb065dd99b648aa12ae4e0c6693e04d0a2 -size 146 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_pressed.png deleted file mode 100644 index 3f63a24d56..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:76f44758619badecf11b3b0d9914612e584ad750a695c942811fe215457b25be -size 146 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line.png deleted file mode 100644 index 7aebe0ec54..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:50a35383d40b4e8a646931e4057cd25045f05a48208e2cb9d9935be76b53bf94 -size 130 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_disabled.png deleted file mode 100644 index f1b83a5734..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba7550922e9d244620f8f9ad76fe546d542764eba02378f81b188dec5fd7438a -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_focus.png deleted file mode 100644 index 5daf190e47..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52bec8528e3c8edd583136d90a37f46bcb45e0d406fc0fb680a8b4d75cfeb731 -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_pressed.png deleted file mode 100644 index d533bb82b9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b8d2c9a8593a52221c91d2a8c2d3cbd837e408a5f6d1dcad6f79328a13a3bcf -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more.png deleted file mode 100644 index d0eb02b7fb..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5087f4f06a9718230e1aec2ba681f3432ecd2640a135b4e90c7b009188ec4c29 -size 155 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_disabled.png deleted file mode 100644 index a457e2822c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dec80a2e8439a0787e10aee70a365e30b5d1f43c29e4c33989cc6cd2f5cac478 -size 162 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_focus.png deleted file mode 100644 index 09b9726550..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39930fd3e240c9ad94d748d6cda73b2943682fc4825edd1240e882be18c06198 -size 162 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_pressed.png deleted file mode 100644 index 31a17b26f0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0d07da719927b26db1c3087bbfe7203510fce077efe9e121935b3aefbb49b95d -size 162 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open.png deleted file mode 100644 index f0f49a375a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:05a1980b268f598ebb3520067679a8beb4fa3f00da9c87dd93be2642718ceb44 -size 354 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_disabled.png deleted file mode 100644 index d46e6138bc..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:047c00f910dd279e871a6329ea533816d8b458063539b8efbe9949d7363996bf -size 375 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_focus.png deleted file mode 100644 index d6c73e877c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6bc79f30e3fdc52ec30eb0e9c6b03d1538f7c8c7855033d24d5f993e8ceb9cc1 -size 367 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_pressed.png deleted file mode 100644 index ba1bb5e27b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa65cf58b8a02bf5f4142ad80de05aba868245c55a790af6ba0230bfd01a2a06 -size 369 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked.png deleted file mode 100644 index d82af2b4ed..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:95c1c1651a13f0562383087549a35a97bbb7899c7d3717d79d4624485b72bf9f -size 452 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_disabled.png deleted file mode 100644 index e96b6ab274..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:80a1d02e6ac7e5d0439b2a077ab8cf82739853ce46ac1556d4035e3bba713242 -size 467 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_focus.png deleted file mode 100644 index abe4bad569..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64d5ed4d01a9778912b98c9147eb67431560acb5805d1d0832a765c441b9ed9b -size 441 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_pressed.png deleted file mode 100644 index 1bab094a68..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f0ffa46106a643a71835056acce66ebe09745a9e6d91fac30ff1caf0589a6677 -size 418 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate.png deleted file mode 100644 index 51d0835feb..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b291eb9180c0e27d1de6ff008ff4259b2c675a65efa94866a66c7c932fc1260 -size 581 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_disabled.png deleted file mode 100644 index 9e13859a93..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c7d3d64e8cb5e2f8bc6620b0d58492a56800fc78fc0229a5fa495d3a43987a1 -size 614 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_focus.png deleted file mode 100644 index aea72c9cde..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43d494685f5d2ed740b69f04fefe0e757626db94685ecc8f4411c6c68a626a5a -size 576 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_pressed.png deleted file mode 100644 index d2c86adef3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7af182d82449663ac37955e45ccc3f8fc86d185649fa9bed24c10167351bd5ee -size 563 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked.png deleted file mode 100644 index bf34be7606..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:232451d0bd9cf1d54c777862030b667cd5078f2f4ff387ec03c44d56eb207c03 -size 397 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_disabled.png deleted file mode 100644 index 596e553c14..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7b54771eaad56ee45f8871248ee1c6b18035aa732f9e4e9257d008a73be04c25 -size 386 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_focus.png deleted file mode 100644 index 96cf982f58..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae293d387fda8a89d68fc3f15db07ef084e3537a033d02351ff918cfcc82ea8a -size 394 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_pressed.png deleted file mode 100644 index 0984a1fd5d..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f8ccf5cb638a090f0e64f6d336e4c6312cfaf53971afd0f00cd16d0c0759f1b4 -size 403 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal.png deleted file mode 100644 index 4d069c17b1..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:256f010c3084112888189bcbea2995a37f8acbf12d61a4a261a94aca797cd964 -size 117 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_disabled.png deleted file mode 100644 index 06465a0a29..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:85522a94ec26125f65dcafc6158665f40ea570e4a08cbdfbbdf5f6772b887eb7 -size 121 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_focus.png deleted file mode 100644 index 5f1332e6dd..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae4766527d9e5a2226107ede231878118538e8be89f2dc2ac92b7c5a68ad0fc6 -size 120 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_pressed.png deleted file mode 100644 index f0f11abeb0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f4bdbb4d207aa40366ad90d363a95d80e2b4a43574ca1ad3256ee6e0617f25e -size 120 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical.png deleted file mode 100644 index 7aebe0ec54..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:50a35383d40b4e8a646931e4057cd25045f05a48208e2cb9d9935be76b53bf94 -size 130 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_disabled.png deleted file mode 100644 index f1b83a5734..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba7550922e9d244620f8f9ad76fe546d542764eba02378f81b188dec5fd7438a -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_focus.png deleted file mode 100644 index 5daf190e47..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52bec8528e3c8edd583136d90a37f46bcb45e0d406fc0fb680a8b4d75cfeb731 -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_pressed.png deleted file mode 100644 index d533bb82b9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b8d2c9a8593a52221c91d2a8c2d3cbd837e408a5f6d1dcad6f79328a13a3bcf -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked.png deleted file mode 100644 index 99c9969237..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:abee85006ecef454df64f65a6aa7dbb85bff9c51f53ed87b47e9f4ef1adefec3 -size 1224 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_disabled.png deleted file mode 100644 index 13daed68f4..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7e22fc4d6bf116cebab4655b6bf81b1c384789b45a96f05d8a4b535e34978cb9 -size 1325 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_focus.png deleted file mode 100644 index e42389445a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3db59af5ba4caa97ac07834e1a919adafdcd610324dacac68cbd2cde551c2397 -size 1293 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_pressed.png deleted file mode 100644 index 4153bb2ed5..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:34bce34b70cf7c24d1e54f87f4225f0c4cf8e7dd8c6fd8a38f81e981bae2a2ce -size 1276 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked.png deleted file mode 100644 index 748ab5998b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f675766febe18edf774b0dd7db11177ca76793c0d2b693b1ffb6a447b79369d2 -size 963 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_disabled.png deleted file mode 100644 index 34230cbb40..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d5af9296f23e58fc7fde5c9b278a801bd51bb3650d0e5f84dd9eb84434308cc -size 1040 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_focus.png deleted file mode 100644 index 3428ad46be..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0b650dda797331b8c23ece7e313babb8e6e9118bd3d53e689a7381a33cb5e00 -size 1032 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_pressed.png deleted file mode 100644 index b60ab09f6c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2dd40885e1d7b1d3f37cfc3afff07fe47db552602bdc46aa9a8ce7a0c8df30db -size 1022 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal.png deleted file mode 100644 index ad5243fcb8..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:054d47979c0879378a6f5e36d9e5b251c31e15610adeed109b8f128115d4b5ec -size 150 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_disabled.png deleted file mode 100644 index 94ef75054c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9eca88a1ebba5d42107da4c3b3af3b52a8de1c76bc1bae8d27190ff5e69f8198 -size 155 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_focus.png deleted file mode 100644 index c4fe22a169..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e11a73fcc79cebd854cbdf3c6539eca99b016440c590b5326f90fa9790e9a69d -size 154 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_pressed.png deleted file mode 100644 index e6d3f5a2c6..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7371a89813b6f4843dc90b4abe866de943ba670057e0802dcc7f0284d9aa079b -size 154 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical.png deleted file mode 100644 index 6f47c7e52c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8e4d88b8da4d94d4ecaa0eda448d18adcad14c5a62d4e6c9d0ffb2673683d855 -size 137 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_disabled.png deleted file mode 100644 index 43b5911860..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e7422317ab56297babc9025f42dd1f7179588ac4e066cb1b976b1bb56efca656 -size 140 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_focus.png deleted file mode 100644 index 0b918dcdc9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b9a1520ad62a2b20f53c0709d643af3e8e0d775891597c4bc05e46ed75617bd9 -size 144 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_pressed.png deleted file mode 100644 index 7b104f52b0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:73b0e2dddb1c22848b9b858975cdaae02f0b7b47696922a863358afa30f81dfa -size 143 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal.png deleted file mode 100644 index e7174cd081..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e2986d7bca86f3359817f002ecf125afab71281561925a6ecbffe844a2be9699 -size 145 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_disabled.png deleted file mode 100644 index b45f02655c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f7909f6aa1843cb2382ea0e49afb10967a331e128cc8103ab8082c3e46a90aa -size 151 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_focus.png deleted file mode 100644 index e2898bd5bf..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4c628767e58d08e929a4bfc3f730e1347b186a44a1b0d4159fa722776a660ea -size 149 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_pressed.png deleted file mode 100644 index 3a71bdc89e..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2bcf16dee85252fbf33d3eb05009b988f4c7b3795c01d66e471712d76def3ab -size 149 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical.png deleted file mode 100644 index 02c38086c4..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5db0c6a32f562204a4dc77c8958ef29e016af621f11891ca1795da808a879288 -size 133 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_disabled.png deleted file mode 100644 index f9b739bb93..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c705854e5a7aae10edb4d0cd28d1217a0b6599845031053d502aa05030ce5134 -size 135 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_focus.png deleted file mode 100644 index 08661141b3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39bd3c687b5bc56d6a62728d3dbfa522f3e1e74ec4de752987768f60c947adf5 -size 139 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_pressed.png deleted file mode 100644 index 5baf760e59..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0966303eb702647e3463ffa644611d817741b1ee0d016ecd56c600e9f04ac114 -size 138 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_disabled.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_focus.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_pressed.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close.png deleted file mode 100644 index dd99b7b8ed..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f7b31e2fb43e9c3dbd9f0e32680422a7d8c8e7ff7cd600e446103e45b0df0523 -size 766 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_disabled.png deleted file mode 100644 index 1f506f9543..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:528a22b6955681f34373fc72a2dfdd19e6255e18c73f0804c09b34ea01c1f0a0 -size 838 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_focus.png deleted file mode 100644 index 244b91f5b8..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bbc009bf89ac37957e2ff6532a9328f71e63f5edb45bd918048c8a69f61a72e2 -size 756 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_pressed.png deleted file mode 100644 index 4a45bda1b2..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cd117b3874dec17ebf78f136784eca821f4d916fe34081187c8c28bc2223f545 -size 745 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip.png deleted file mode 100644 index 0f176f5949..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c2ebc9c32505a0489879f7429034143af2ed48e71d2b2eba449a45c7253a2b7 -size 426 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_disabled.png deleted file mode 100644 index f07d5f0de8..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e7ffdf7643cd2078127953690098b8ea8899428f7906efa778d70422d254f1e -size 447 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_focus.png deleted file mode 100644 index d7271e6e0a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:055a219ffe9015ed50585caa1cedd31cb99c034c41c17f3ff97cc3c9a1b9b68c -size 435 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_pressed.png deleted file mode 100644 index 000da02699..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:776e4b8d509743bb682783085bd139ad8e419a1bbc02a93480b1a4aaffd89041 -size 444 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize.png deleted file mode 100644 index 1846176c40..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ddd70f8069320fb09ee8800cecaa190076baaa4b1c251900220541cc83434bd -size 193 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_disabled.png deleted file mode 100644 index d9df85b122..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4bc0af0fc3119eb066333d3c3e4fbdcc65e23bfdb43b5c5dfd65aae7b6916b3b -size 206 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_focus.png deleted file mode 100644 index 30a7f49ed0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7575b3458ef002ebfe1fece1427dd3170326d35144b0d03f215d091f625f7286 -size 208 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_pressed.png deleted file mode 100644 index 9cd26589f5..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e3dd9475a6db26f45e85d5a688a5e724b7278af4d472caf4b16c1fde346f95db -size 202 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock.png deleted file mode 100644 index 8126d26228..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b07a032c3109f93770c149c4b3199c17cc446e58e50b0d86c4254268a3dc00b0 -size 510 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_disabled.png deleted file mode 100644 index 573cd467ed..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c97ecbc47699b4ec1189831ae2b8f08eed95e96def84ea48ac63597ecd3d40a -size 541 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_focus.png deleted file mode 100644 index 5044d402af..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ed098cc564cc6c45cdf43103f06a670089149cbc4e81a733becd49d6d115d44 -size 519 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_pressed.png deleted file mode 100644 index be8c5637dd..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef37bb94ebabd4d37ed1c8fcd5a095a5d10e2a20a667861d13c772b903c32bb1 -size 523 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/readme.txt deleted file mode 100644 index e100551564..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -LICENSE -https://github.com/ColinDuquesnoy/QDarkStyleSheet/blob/master/LICENSE.rst - -DEPOT -https://github.com/ColinDuquesnoy/QDarkStyleSheet diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qrc b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qrc deleted file mode 100644 index e301854e2c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qrc +++ /dev/null @@ -1,216 +0,0 @@ - - - - rc/arrow_down.png - rc/arrow_down@2x.png - rc/arrow_down_disabled.png - rc/arrow_down_disabled@2x.png - rc/arrow_down_focus.png - rc/arrow_down_focus@2x.png - rc/arrow_down_pressed.png - rc/arrow_down_pressed@2x.png - rc/arrow_left.png - rc/arrow_left@2x.png - rc/arrow_left_disabled.png - rc/arrow_left_disabled@2x.png - rc/arrow_left_focus.png - rc/arrow_left_focus@2x.png - rc/arrow_left_pressed.png - rc/arrow_left_pressed@2x.png - rc/arrow_right.png - rc/arrow_right@2x.png - rc/arrow_right_disabled.png - rc/arrow_right_disabled@2x.png - rc/arrow_right_focus.png - rc/arrow_right_focus@2x.png - rc/arrow_right_pressed.png - rc/arrow_right_pressed@2x.png - rc/arrow_up.png - rc/arrow_up@2x.png - rc/arrow_up_disabled.png - rc/arrow_up_disabled@2x.png - rc/arrow_up_focus.png - rc/arrow_up_focus@2x.png - rc/arrow_up_pressed.png - rc/arrow_up_pressed@2x.png - rc/base_icon.png - rc/base_icon@2x.png - rc/base_icon_disabled.png - rc/base_icon_disabled@2x.png - rc/base_icon_focus.png - rc/base_icon_focus@2x.png - rc/base_icon_pressed.png - rc/base_icon_pressed@2x.png - rc/branch_closed.png - rc/branch_closed@2x.png - rc/branch_closed_disabled.png - rc/branch_closed_disabled@2x.png - rc/branch_closed_focus.png - rc/branch_closed_focus@2x.png - rc/branch_closed_pressed.png - rc/branch_closed_pressed@2x.png - rc/branch_end.png - rc/branch_end@2x.png - rc/branch_end_disabled.png - rc/branch_end_disabled@2x.png - rc/branch_end_focus.png - rc/branch_end_focus@2x.png - rc/branch_end_pressed.png - rc/branch_end_pressed@2x.png - rc/branch_line.png - rc/branch_line@2x.png - rc/branch_line_disabled.png - rc/branch_line_disabled@2x.png - rc/branch_line_focus.png - rc/branch_line_focus@2x.png - rc/branch_line_pressed.png - rc/branch_line_pressed@2x.png - rc/branch_more.png - rc/branch_more@2x.png - rc/branch_more_disabled.png - rc/branch_more_disabled@2x.png - rc/branch_more_focus.png - rc/branch_more_focus@2x.png - rc/branch_more_pressed.png - rc/branch_more_pressed@2x.png - rc/branch_open.png - rc/branch_open@2x.png - rc/branch_open_disabled.png - rc/branch_open_disabled@2x.png - rc/branch_open_focus.png - rc/branch_open_focus@2x.png - rc/branch_open_pressed.png - rc/branch_open_pressed@2x.png - rc/checkbox_checked.png - rc/checkbox_checked@2x.png - rc/checkbox_checked_disabled.png - rc/checkbox_checked_disabled@2x.png - rc/checkbox_checked_focus.png - rc/checkbox_checked_focus@2x.png - rc/checkbox_checked_pressed.png - rc/checkbox_checked_pressed@2x.png - rc/checkbox_indeterminate.png - rc/checkbox_indeterminate@2x.png - rc/checkbox_indeterminate_disabled.png - rc/checkbox_indeterminate_disabled@2x.png - rc/checkbox_indeterminate_focus.png - rc/checkbox_indeterminate_focus@2x.png - rc/checkbox_indeterminate_pressed.png - rc/checkbox_indeterminate_pressed@2x.png - rc/checkbox_unchecked.png - rc/checkbox_unchecked@2x.png - rc/checkbox_unchecked_disabled.png - rc/checkbox_unchecked_disabled@2x.png - rc/checkbox_unchecked_focus.png - rc/checkbox_unchecked_focus@2x.png - rc/checkbox_unchecked_pressed.png - rc/checkbox_unchecked_pressed@2x.png - rc/line_horizontal.png - rc/line_horizontal@2x.png - rc/line_horizontal_disabled.png - rc/line_horizontal_disabled@2x.png - rc/line_horizontal_focus.png - rc/line_horizontal_focus@2x.png - rc/line_horizontal_pressed.png - rc/line_horizontal_pressed@2x.png - rc/line_vertical.png - rc/line_vertical@2x.png - rc/line_vertical_disabled.png - rc/line_vertical_disabled@2x.png - rc/line_vertical_focus.png - rc/line_vertical_focus@2x.png - rc/line_vertical_pressed.png - rc/line_vertical_pressed@2x.png - rc/radio_checked.png - rc/radio_checked@2x.png - rc/radio_checked_disabled.png - rc/radio_checked_disabled@2x.png - rc/radio_checked_focus.png - rc/radio_checked_focus@2x.png - rc/radio_checked_pressed.png - rc/radio_checked_pressed@2x.png - rc/radio_unchecked.png - rc/radio_unchecked@2x.png - rc/radio_unchecked_disabled.png - rc/radio_unchecked_disabled@2x.png - rc/radio_unchecked_focus.png - rc/radio_unchecked_focus@2x.png - rc/radio_unchecked_pressed.png - rc/radio_unchecked_pressed@2x.png - rc/toolbar_move_horizontal.png - rc/toolbar_move_horizontal@2x.png - rc/toolbar_move_horizontal_disabled.png - rc/toolbar_move_horizontal_disabled@2x.png - rc/toolbar_move_horizontal_focus.png - rc/toolbar_move_horizontal_focus@2x.png - rc/toolbar_move_horizontal_pressed.png - rc/toolbar_move_horizontal_pressed@2x.png - rc/toolbar_move_vertical.png - rc/toolbar_move_vertical@2x.png - rc/toolbar_move_vertical_disabled.png - rc/toolbar_move_vertical_disabled@2x.png - rc/toolbar_move_vertical_focus.png - rc/toolbar_move_vertical_focus@2x.png - rc/toolbar_move_vertical_pressed.png - rc/toolbar_move_vertical_pressed@2x.png - rc/toolbar_separator_horizontal.png - rc/toolbar_separator_horizontal@2x.png - rc/toolbar_separator_horizontal_disabled.png - rc/toolbar_separator_horizontal_disabled@2x.png - rc/toolbar_separator_horizontal_focus.png - rc/toolbar_separator_horizontal_focus@2x.png - rc/toolbar_separator_horizontal_pressed.png - rc/toolbar_separator_horizontal_pressed@2x.png - rc/toolbar_separator_vertical.png - rc/toolbar_separator_vertical@2x.png - rc/toolbar_separator_vertical_disabled.png - rc/toolbar_separator_vertical_disabled@2x.png - rc/toolbar_separator_vertical_focus.png - rc/toolbar_separator_vertical_focus@2x.png - rc/toolbar_separator_vertical_pressed.png - rc/toolbar_separator_vertical_pressed@2x.png - rc/transparent.png - rc/transparent@2x.png - rc/transparent_disabled.png - rc/transparent_disabled@2x.png - rc/transparent_focus.png - rc/transparent_focus@2x.png - rc/transparent_pressed.png - rc/transparent_pressed@2x.png - rc/window_close.png - rc/window_close@2x.png - rc/window_close_disabled.png - rc/window_close_disabled@2x.png - rc/window_close_focus.png - rc/window_close_focus@2x.png - rc/window_close_pressed.png - rc/window_close_pressed@2x.png - rc/window_grip.png - rc/window_grip@2x.png - rc/window_grip_disabled.png - rc/window_grip_disabled@2x.png - rc/window_grip_focus.png - rc/window_grip_focus@2x.png - rc/window_grip_pressed.png - rc/window_grip_pressed@2x.png - rc/window_minimize.png - rc/window_minimize@2x.png - rc/window_minimize_disabled.png - rc/window_minimize_disabled@2x.png - rc/window_minimize_focus.png - rc/window_minimize_focus@2x.png - rc/window_minimize_pressed.png - rc/window_minimize_pressed@2x.png - rc/window_undock.png - rc/window_undock@2x.png - rc/window_undock_disabled.png - rc/window_undock_disabled@2x.png - rc/window_undock_focus.png - rc/window_undock_focus@2x.png - rc/window_undock_pressed.png - rc/window_undock_pressed@2x.png - - - style.qss - - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qss b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qss deleted file mode 100644 index 55dfe093d9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qss +++ /dev/null @@ -1,2165 +0,0 @@ -/* --------------------------------------------------------------------------- - - Created by the qtsass compiler v0.1.1 - - The definitions are in the "qdarkstyle.qss._styles.scss" module - - WARNING! All changes made in this file will be lost! - ---------------------------------------------------------------------------- */ -/* QDarkStyleSheet ----------------------------------------------------------- - -This is the main style sheet, the palette has nine colors. - -It is based on three selecting colors, three greyish (background) colors -plus three whitish (foreground) colors. Each set of widgets of the same -type have a header like this: - - ------------------ - GroupName -------- - ------------------ - -And each widget is separated with a header like this: - - QWidgetName ------ - -This makes more easy to find and change some css field. The basic -configuration is described bellow. - - BACKGROUND ----------- - - Light (unpressed) - Normal (border, disabled, pressed, checked, toolbars, menus) - Dark (background) - - FOREGROUND ----------- - - Light (texts/labels) - Normal (not used yet) - Dark (disabled texts) - - SELECTION ------------ - - Light (selection/hover/active) - Normal (selected) - Dark (selected disabled) - -If a stranger configuration is required because of a bugfix or anything -else, keep the comment on the line above so nobody changes it, including the -issue number. - -*/ -/* - -See Qt documentation: - - - https://doc.qt.io/qt-5/stylesheet.html - - https://doc.qt.io/qt-5/stylesheet-reference.html - - https://doc.qt.io/qt-5/stylesheet-examples.html - ---------------------------------------------------------------------------- */ -/* QWidget ---------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QWidget { - background-color: #19232D; - border: 0px solid #32414B; - padding: 0px; - color: #F0F0F0; - selection-background-color: #1464A0; - selection-color: #F0F0F0; -} - -QWidget:disabled { - background-color: #19232D; - color: #787878; - selection-background-color: #14506E; - selection-color: #787878; -} - -QWidget::item:selected { - background-color: #1464A0; -} - -QWidget::item:hover { - background-color: #148CD2; - color: #32414B; -} - -/* QMainWindow ------------------------------------------------------------ - -This adjusts the splitter in the dock widget, not qsplitter -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmainwindow - ---------------------------------------------------------------------------- */ -QMainWindow::separator { - background-color: #32414B; - border: 0px solid #19232D; - spacing: 0px; - padding: 2px; -} - -QMainWindow::separator:hover { - background-color: #505F69; - border: 0px solid #148CD2; -} - -QMainWindow::separator:horizontal { - width: 5px; - margin-top: 2px; - margin-bottom: 2px; - image: url(":/qss_icons/rc/toolbar_separator_vertical.png"); -} - -QMainWindow::separator:vertical { - height: 5px; - margin-left: 2px; - margin-right: 2px; - image: url(":/qss_icons/rc/toolbar_separator_horizontal.png"); -} - -/* QToolTip --------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtooltip - ---------------------------------------------------------------------------- */ -QToolTip { - background-color: #148CD2; - border: 1px solid #19232D; - color: #19232D; - /* Remove padding, for fix combo box tooltip */ - padding: 0px; - /* Remove opacity, fix #174 - may need to use RGBA */ -} - -/* QStatusBar ------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar - ---------------------------------------------------------------------------- */ -QStatusBar { - border: 1px solid #32414B; - /* Fixes Spyder #9120, #9121 */ - background: #32414B; - /* Fixes #205, white vertical borders separating items */ -} - -QStatusBar::item { - border: none; -} - -QStatusBar QToolTip { - background-color: #148CD2; - border: 1px solid #19232D; - color: #19232D; - /* Remove padding, for fix combo box tooltip */ - padding: 0px; - /* Reducing transparency to read better */ - opacity: 230; -} - -QStatusBar QLabel { - /* Fixes Spyder #9120, #9121 */ - background: transparent; -} - -/* QCheckBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcheckbox - ---------------------------------------------------------------------------- */ -QCheckBox { - background-color: #19232D; - color: #F0F0F0; - spacing: 4px; - outline: none; - padding-top: 4px; - padding-bottom: 4px; -} - -QCheckBox:focus { - border: none; -} - -QCheckBox QWidget:disabled { - background-color: #19232D; - color: #787878; -} - -QCheckBox::indicator { - margin-left: 4px; - height: 16px; - width: 16px; -} - -QCheckBox::indicator:unchecked { - image: url(":/qss_icons/rc/checkbox_unchecked.png"); -} - -QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus, QCheckBox::indicator:unchecked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); -} - -QCheckBox::indicator:unchecked:disabled { - image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); -} - -QCheckBox::indicator:checked { - image: url(":/qss_icons/rc/checkbox_checked.png"); -} - -QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus, QCheckBox::indicator:checked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_checked_focus.png"); -} - -QCheckBox::indicator:checked:disabled { - image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); -} - -QCheckBox::indicator:indeterminate { - image: url(":/qss_icons/rc/checkbox_indeterminate.png"); -} - -QCheckBox::indicator:indeterminate:disabled { - image: url(":/qss_icons/rc/checkbox_indeterminate_disabled.png"); -} - -QCheckBox::indicator:indeterminate:focus, QCheckBox::indicator:indeterminate:hover, QCheckBox::indicator:indeterminate:pressed { - image: url(":/qss_icons/rc/checkbox_indeterminate_focus.png"); -} - -/* QGroupBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox - ---------------------------------------------------------------------------- */ -QGroupBox { - font-weight: bold; - border: 1px solid #32414B; - border-radius: 4px; - padding: 4px; - margin-top: 16px; -} - -QGroupBox::title { - subcontrol-origin: margin; - subcontrol-position: top left; - left: 3px; - padding-left: 3px; - padding-right: 5px; - padding-top: 8px; - padding-bottom: 16px; -} - -QGroupBox::indicator { - margin-left: 2px; - height: 12px; - width: 12px; -} - -QGroupBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:focus, QGroupBox::indicator:unchecked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); -} - -QGroupBox::indicator:unchecked:disabled { - image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); -} - -QGroupBox::indicator:checked:hover, QGroupBox::indicator:checked:focus, QGroupBox::indicator:checked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_checked_focus.png"); -} - -QGroupBox::indicator:checked:disabled { - image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); -} - -/* QRadioButton ----------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qradiobutton - ---------------------------------------------------------------------------- */ -QRadioButton { - background-color: #19232D; - color: #F0F0F0; - spacing: 4px; - padding: 0px; - border: none; - outline: none; -} - -QRadioButton:focus { - border: none; -} - -QRadioButton:disabled { - background-color: #19232D; - color: #787878; - border: none; - outline: none; -} - -QRadioButton QWidget { - background-color: #19232D; - color: #F0F0F0; - spacing: 0px; - padding: 0px; - outline: none; - border: none; -} - -QRadioButton::indicator { - border: none; - outline: none; - margin-left: 4px; - height: 16px; - width: 16px; -} - -QRadioButton::indicator:unchecked { - image: url(":/qss_icons/rc/radio_unchecked.png"); -} - -QRadioButton::indicator:unchecked:hover, QRadioButton::indicator:unchecked:focus, QRadioButton::indicator:unchecked:pressed { - border: none; - outline: none; - image: url(":/qss_icons/rc/radio_unchecked_focus.png"); -} - -QRadioButton::indicator:unchecked:disabled { - image: url(":/qss_icons/rc/radio_unchecked_disabled.png"); -} - -QRadioButton::indicator:checked { - border: none; - outline: none; - image: url(":/qss_icons/rc/radio_checked.png"); -} - -QRadioButton::indicator:checked:hover, QRadioButton::indicator:checked:focus, QRadioButton::indicator:checked:pressed { - border: none; - outline: none; - image: url(":/qss_icons/rc/radio_checked_focus.png"); -} - -QRadioButton::indicator:checked:disabled { - outline: none; - image: url(":/qss_icons/rc/radio_checked_disabled.png"); -} - -/* QMenuBar --------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar - ---------------------------------------------------------------------------- */ -QMenuBar { - background-color: #32414B; - padding: 2px; - border: 1px solid #19232D; - color: #F0F0F0; -} - -QMenuBar:focus { - border: 1px solid #148CD2; -} - -QMenuBar::item { - background: transparent; - padding: 4px; -} - -QMenuBar::item:selected { - padding: 4px; - background: transparent; - border: 0px solid #32414B; -} - -QMenuBar::item:pressed { - padding: 4px; - border: 0px solid #32414B; - background-color: #148CD2; - color: #F0F0F0; - margin-bottom: 0px; - padding-bottom: 0px; -} - -/* QMenu ------------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu - ---------------------------------------------------------------------------- */ -QMenu { - border: 0px solid #32414B; - color: #F0F0F0; - margin: 0px; -} - -QMenu::separator { - height: 1px; - background-color: #505F69; - color: #F0F0F0; -} - -QMenu::icon { - margin: 0px; - padding-left: 8px; -} - -QMenu::item { - background-color: #32414B; - padding: 4px 24px 4px 24px; - /* Reserve space for selection border */ - border: 1px transparent #32414B; -} - -QMenu::item:selected { - color: #F0F0F0; -} - -QMenu::indicator { - width: 12px; - height: 12px; - padding-left: 6px; - /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ - /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ -} - -QMenu::indicator:non-exclusive:unchecked { - image: url(":/qss_icons/rc/checkbox_unchecked.png"); -} - -QMenu::indicator:non-exclusive:unchecked:selected { - image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); -} - -QMenu::indicator:non-exclusive:checked { - image: url(":/qss_icons/rc/checkbox_checked.png"); -} - -QMenu::indicator:non-exclusive:checked:selected { - image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); -} - -QMenu::indicator:exclusive:unchecked { - image: url(":/qss_icons/rc/radio_unchecked.png"); -} - -QMenu::indicator:exclusive:unchecked:selected { - image: url(":/qss_icons/rc/radio_unchecked_disabled.png"); -} - -QMenu::indicator:exclusive:checked { - image: url(":/qss_icons/rc/radio_checked.png"); -} - -QMenu::indicator:exclusive:checked:selected { - image: url(":/qss_icons/rc/radio_checked_disabled.png"); -} - -QMenu::right-arrow { - margin: 5px; - image: url(":/qss_icons/rc/arrow_right.png"); - height: 12px; - width: 12px; -} - -/* QAbstractItemView ------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox - ---------------------------------------------------------------------------- */ -QAbstractItemView { - alternate-background-color: #19232D; - color: #F0F0F0; - border: 1px solid #32414B; - border-radius: 4px; -} - -QAbstractItemView QLineEdit { - padding: 2px; -} - -/* QAbstractScrollArea ---------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea - ---------------------------------------------------------------------------- */ -QAbstractScrollArea { - background-color: #19232D; - border: 1px solid #32414B; - border-radius: 4px; - padding: 2px; - /* fix #159 */ - min-height: 1.25em; - /* fix #159 */ - color: #F0F0F0; -} - -QAbstractScrollArea:disabled { - color: #787878; -} - -/* QScrollArea ------------------------------------------------------------ - ---------------------------------------------------------------------------- */ -QScrollArea QWidget QWidget:disabled { - background-color: #19232D; -} - -/* QScrollBar ------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qscrollbar - ---------------------------------------------------------------------------- */ -QScrollBar:horizontal { - height: 16px; - margin: 2px 16px 2px 16px; - border: 1px solid #32414B; - border-radius: 4px; - background-color: #19232D; -} - -QScrollBar:vertical { - background-color: #19232D; - width: 16px; - margin: 16px 2px 16px 2px; - border: 1px solid #32414B; - border-radius: 4px; -} - -QScrollBar::handle:horizontal { - background-color: #787878; - border: 1px solid #32414B; - border-radius: 4px; - min-width: 8px; -} - -QScrollBar::handle:horizontal:hover { - background-color: #148CD2; - border: 1px solid #148CD2; - border-radius: 4px; - min-width: 8px; -} - -QScrollBar::handle:horizontal:focus { - border: 1px solid #1464A0; -} - -QScrollBar::handle:vertical { - background-color: #787878; - border: 1px solid #32414B; - min-height: 8px; - border-radius: 4px; -} - -QScrollBar::handle:vertical:hover { - background-color: #148CD2; - border: 1px solid #148CD2; - border-radius: 4px; - min-height: 8px; -} - -QScrollBar::handle:vertical:focus { - border: 1px solid #1464A0; -} - -QScrollBar::add-line:horizontal { - margin: 0px 0px 0px 0px; - border-image: url(":/qss_icons/rc/arrow_right_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover, QScrollBar::add-line:horizontal:on { - border-image: url(":/qss_icons/rc/arrow_right.png"); - height: 12px; - width: 12px; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical { - margin: 3px 0px 3px 0px; - border-image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { - border-image: url(":/qss_icons/rc/arrow_down.png"); - height: 12px; - width: 12px; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal { - margin: 0px 3px 0px 3px; - border-image: url(":/qss_icons/rc/arrow_left_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on { - border-image: url(":/qss_icons/rc/arrow_left.png"); - height: 12px; - width: 12px; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical { - margin: 3px 0px 3px 0px; - border-image: url(":/qss_icons/rc/arrow_up_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover, QScrollBar::sub-line:vertical:on { - border-image: url(":/qss_icons/rc/arrow_up.png"); - height: 12px; - width: 12px; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal { - background: none; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { - background: none; -} - -QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - background: none; -} - -QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; -} - -/* QTextEdit -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-specific-widgets - ---------------------------------------------------------------------------- */ -QTextEdit { - background-color: #19232D; - color: #F0F0F0; - border-radius: 4px; - border: 1px solid #32414B; -} - -QTextEdit:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QTextEdit:focus { - border: 1px solid #1464A0; -} - -QTextEdit:selected { - background: #1464A0; - color: #32414B; -} - -/* QPlainTextEdit --------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QPlainTextEdit { - background-color: #19232D; - color: #F0F0F0; - border-radius: 4px; - border: 1px solid #32414B; -} - -QPlainTextEdit:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QPlainTextEdit:focus { - border: 1px solid #1464A0; -} - -QPlainTextEdit:selected { - background: #1464A0; - color: #32414B; -} - -/* QSizeGrip -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsizegrip - ---------------------------------------------------------------------------- */ -QSizeGrip { - background: transparent; - width: 12px; - height: 12px; - image: url(":/qss_icons/rc/window_grip.png"); -} - -/* QStackedWidget --------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QStackedWidget { - padding: 2px; - border: 1px solid #32414B; - border: 1px solid #19232D; -} - -/* QToolBar --------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbar - ---------------------------------------------------------------------------- */ -QToolBar { - background-color: #32414B; - border-bottom: 1px solid #19232D; - padding: 2px; - font-weight: bold; - spacing: 2px; -} - -QToolBar QToolButton { - background-color: #32414B; - border: 1px solid #32414B; -} - -QToolBar QToolButton:hover { - border: 1px solid #148CD2; -} - -QToolBar QToolButton:checked { - border: 1px solid #19232D; - background-color: #19232D; -} - -QToolBar QToolButton:checked:hover { - border: 1px solid #148CD2; -} - -QToolBar::handle:horizontal { - width: 16px; - image: url(":/qss_icons/rc/toolbar_move_horizontal.png"); -} - -QToolBar::handle:vertical { - height: 16px; - image: url(":/qss_icons/rc/toolbar_move_vertical.png"); -} - -QToolBar::separator:horizontal { - width: 16px; - image: url(":/qss_icons/rc/toolbar_separator_horizontal.png"); -} - -QToolBar::separator:vertical { - height: 16px; - image: url(":/qss_icons/rc/toolbar_separator_vertical.png"); -} - -QToolButton#qt_toolbar_ext_button { - background: #32414B; - border: 0px; - color: #F0F0F0; - image: url(":/qss_icons/rc/arrow_right.png"); -} - -/* QAbstractSpinBox ------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QAbstractSpinBox { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - /* This fixes 103, 111 */ - padding-top: 2px; - /* This fixes 103, 111 */ - padding-bottom: 2px; - padding-left: 4px; - padding-right: 4px; - border-radius: 4px; - /* min-width: 5px; removed to fix 109 */ -} - -QAbstractSpinBox:up-button { - background-color: transparent #19232D; - subcontrol-origin: border; - subcontrol-position: top right; - border-left: 1px solid #32414B; - border-bottom: 1px solid #32414B; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - margin: 1px; - width: 12px; - margin-bottom: -1px; -} - -QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { - image: url(":/qss_icons/rc/arrow_up_disabled.png"); - height: 8px; - width: 8px; -} - -QAbstractSpinBox::up-arrow:hover { - image: url(":/qss_icons/rc/arrow_up.png"); -} - -QAbstractSpinBox:down-button { - background-color: transparent #19232D; - subcontrol-origin: border; - subcontrol-position: bottom right; - border-left: 1px solid #32414B; - border-top: 1px solid #32414B; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - margin: 1px; - width: 12px; - margin-top: -1px; -} - -QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { - image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 8px; - width: 8px; -} - -QAbstractSpinBox::down-arrow:hover { - image: url(":/qss_icons/rc/arrow_down.png"); -} - -QAbstractSpinBox:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QAbstractSpinBox:focus { - border: 1px solid #1464A0; -} - -QAbstractSpinBox:selected { - background: #1464A0; - color: #32414B; -} - -/* ------------------------------------------------------------------------ */ -/* DISPLAYS --------------------------------------------------------------- */ -/* ------------------------------------------------------------------------ */ -/* QLabel ----------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe - ---------------------------------------------------------------------------- */ -QLabel { - background-color: #19232D; - border: 0px solid #32414B; - padding: 2px; - margin: 0px; - color: #F0F0F0; -} - -QLabel:disabled { - background-color: #19232D; - border: 0px solid #32414B; - color: #787878; -} - -/* QTextBrowser ----------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea - ---------------------------------------------------------------------------- */ -QTextBrowser { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; -} - -QTextBrowser:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; -} - -QTextBrowser:hover, QTextBrowser:!hover, QTextBrowser:selected, QTextBrowser:pressed { - border: 1px solid #32414B; -} - -/* QGraphicsView ---------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QGraphicsView { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; -} - -QGraphicsView:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; -} - -QGraphicsView:hover, QGraphicsView:!hover, QGraphicsView:selected, QGraphicsView:pressed { - border: 1px solid #32414B; -} - -/* QCalendarWidget -------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QCalendarWidget { - border: 1px solid #32414B; - border-radius: 4px; -} - -QCalendarWidget:disabled { - background-color: #19232D; - color: #787878; -} - -/* QLCDNumber ------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QLCDNumber { - background-color: #19232D; - color: #F0F0F0; -} - -QLCDNumber:disabled { - background-color: #19232D; - color: #787878; -} - -/* QProgressBar ----------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qprogressbar - ---------------------------------------------------------------------------- */ -QProgressBar { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; - text-align: center; -} - -QProgressBar:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; - text-align: center; -} - -QProgressBar::chunk { - background-color: #1464A0; - color: #19232D; - border-radius: 4px; -} - -QProgressBar::chunk:disabled { - background-color: #14506E; - color: #787878; - border-radius: 4px; -} - -/* ------------------------------------------------------------------------ */ -/* BUTTONS ---------------------------------------------------------------- */ -/* ------------------------------------------------------------------------ */ -/* QPushButton ------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qpushbutton - ---------------------------------------------------------------------------- */ -QPushButton { - background-color: #505F69; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; - padding: 3px; - outline: none; - /* Issue #194 - Special case of QPushButton inside dialogs, for better UI */ - min-width: 80px; -} - -QPushButton:disabled { - background-color: #32414B; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; - padding: 3px; -} - -QPushButton:checked { - background-color: #32414B; - border: 1px solid #32414B; - border-radius: 4px; - padding: 3px; - outline: none; -} - -QPushButton:checked:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; - padding: 3px; - outline: none; -} - -QPushButton:checked:selected { - background: #1464A0; - color: #32414B; -} - -QPushButton::menu-indicator { - subcontrol-origin: padding; - subcontrol-position: bottom right; - bottom: 4px; -} - -QPushButton:pressed { - background-color: #19232D; - border: 1px solid #19232D; -} - -QPushButton:pressed:hover { - border: 1px solid #148CD2; -} - -QPushButton:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QPushButton:selected { - background: #1464A0; - color: #32414B; -} - -QPushButton:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QPushButton:focus { - border: 1px solid #1464A0; -} - -/* QToolButton ------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton - ---------------------------------------------------------------------------- */ -QToolButton { - background-color: transparent; - border: 1px solid transparent; - border-radius: 4px; - margin: 0px; - padding: 2px; - /* The subcontrols below are used only in the DelayedPopup mode */ - /* The subcontrols below are used only in the MenuButtonPopup mode */ - /* The subcontrol below is used only in the InstantPopup or DelayedPopup mode */ -} - -QToolButton:checked { - background-color: transparent; - border: 1px solid #1464A0; -} - -QToolButton:checked:disabled { - border: 1px solid #14506E; -} - -QToolButton:pressed { - margin: 1px; - background-color: transparent; - border: 1px solid #1464A0; -} - -QToolButton:disabled { - border: none; -} - -QToolButton:hover { - border: 1px solid #148CD2; -} - -QToolButton[popupMode="0"] { - /* Only for DelayedPopup */ - padding-right: 2px; -} - -QToolButton[popupMode="1"] { - /* Only for MenuButtonPopup */ - padding-right: 20px; -} - -QToolButton[popupMode="1"]::menu-button { - border: none; -} - -QToolButton[popupMode="1"]::menu-button:hover { - border: none; - border-left: 1px solid #148CD2; - border-radius: 0; -} - -QToolButton[popupMode="2"] { - /* Only for InstantPopup */ - padding-right: 2px; -} - -QToolButton::menu-button { - padding: 2px; - border-radius: 4px; - border: 1px solid #32414B; - width: 12px; - outline: none; -} - -QToolButton::menu-button:hover { - border: 1px solid #148CD2; -} - -QToolButton::menu-button:checked:hover { - border: 1px solid #148CD2; -} - -QToolButton::menu-indicator { - image: url(":/qss_icons/rc/arrow_down.png"); - height: 8px; - width: 8px; - top: 0; - /* Exclude a shift for better image */ - left: -2px; - /* Shift it a bit */ -} - -QToolButton::menu-arrow { - image: url(":/qss_icons/rc/arrow_down.png"); - height: 8px; - width: 8px; -} - -QToolButton::menu-arrow:hover { - image: url(":/qss_icons/rc/arrow_down_focus.png"); -} - -/* QCommandLinkButton ----------------------------------------------------- - ---------------------------------------------------------------------------- */ -QCommandLinkButton { - background-color: transparent; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; - padding: 0px; - margin: 0px; -} - -QCommandLinkButton:disabled { - background-color: transparent; - color: #787878; -} - -/* ------------------------------------------------------------------------ */ -/* INPUTS - NO FIELDS ----------------------------------------------------- */ -/* ------------------------------------------------------------------------ */ -/* QComboBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox - ---------------------------------------------------------------------------- */ -QComboBox { - border: 1px solid #32414B; - border-radius: 4px; - selection-background-color: #1464A0; - padding-left: 4px; - padding-right: 36px; - /* 4 + 16*2 See scrollbar size */ - /* Fixes #103, #111 */ - min-height: 1.5em; - /* padding-top: 2px; removed to fix #132 */ - /* padding-bottom: 2px; removed to fix #132 */ - /* min-width: 75px; removed to fix #109 */ - /* Needed to remove indicator - fix #132 */ -} - -QComboBox QAbstractItemView { - border: 1px solid #32414B; - border-radius: 0; - background-color: #19232D; - selection-background-color: #1464A0; -} - -QComboBox QAbstractItemView:hover { - background-color: #19232D; - color: #F0F0F0; -} - -QComboBox QAbstractItemView:selected { - background: #1464A0; - color: #32414B; -} - -QComboBox QAbstractItemView:alternate { - background: #19232D; -} - -QComboBox:disabled { - background-color: #19232D; - color: #787878; -} - -QComboBox:hover { - border: 1px solid #148CD2; -} - -QComboBox:focus { - border: 1px solid #1464A0; -} - -QComboBox:on { - selection-background-color: #1464A0; -} - -QComboBox::indicator { - border: none; - border-radius: 0; - background-color: transparent; - selection-background-color: transparent; - color: transparent; - selection-color: transparent; - /* Needed to remove indicator - fix #132 */ -} - -QComboBox::indicator:alternate { - background: #19232D; -} - -QComboBox::item:alternate { - background: #19232D; -} - -QComboBox::item:checked { - font-weight: bold; -} - -QComboBox::item:selected { - border: 0px solid transparent; -} - -QComboBox::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 12px; - border-left: 1px solid #32414B; -} - -QComboBox::down-arrow { - image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 8px; - width: 8px; -} - -QComboBox::down-arrow:on, QComboBox::down-arrow:hover, QComboBox::down-arrow:focus { - image: url(":/qss_icons/rc/arrow_down.png"); -} - -/* QSlider ---------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qslider - ---------------------------------------------------------------------------- */ -QSlider:disabled { - background: #19232D; -} - -QSlider:focus { - border: none; -} - -QSlider::groove:horizontal { - background: #32414B; - border: 1px solid #32414B; - height: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::groove:vertical { - background: #32414B; - border: 1px solid #32414B; - width: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::add-page:vertical { - background: #1464A0; - border: 1px solid #32414B; - width: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::add-page:vertical :disabled { - background: #14506E; -} - -QSlider::sub-page:horizontal { - background: #1464A0; - border: 1px solid #32414B; - height: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::sub-page:horizontal:disabled { - background: #14506E; -} - -QSlider::handle:horizontal { - background: #787878; - border: 1px solid #32414B; - width: 8px; - height: 8px; - margin: -8px 0px; - border-radius: 4px; -} - -QSlider::handle:horizontal:hover { - background: #148CD2; - border: 1px solid #148CD2; -} - -QSlider::handle:horizontal:focus { - border: 1px solid #1464A0; -} - -QSlider::handle:vertical { - background: #787878; - border: 1px solid #32414B; - width: 8px; - height: 8px; - margin: 0 -8px; - border-radius: 4px; -} - -QSlider::handle:vertical:hover { - background: #148CD2; - border: 1px solid #148CD2; -} - -QSlider::handle:vertical:focus { - border: 1px solid #1464A0; -} - -/* QLineEdit -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlineedit - ---------------------------------------------------------------------------- */ -QLineEdit { - background-color: #19232D; - padding-top: 2px; - /* This QLineEdit fix 103, 111 */ - padding-bottom: 2px; - /* This QLineEdit fix 103, 111 */ - padding-left: 4px; - padding-right: 4px; - border-style: solid; - border: 1px solid #32414B; - border-radius: 4px; - color: #F0F0F0; -} - -QLineEdit:disabled { - background-color: #19232D; - color: #787878; -} - -QLineEdit:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QLineEdit:focus { - border: 1px solid #1464A0; -} - -QLineEdit:selected { - background-color: #1464A0; - color: #32414B; -} - -/* QTabWiget -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar - ---------------------------------------------------------------------------- */ -QTabWidget { - padding: 2px; - selection-background-color: #32414B; -} - -QTabWidget QWidget { - /* Fixes #189 */ - border-radius: 4px; -} - -QTabWidget::pane { - border: 1px solid #32414B; - border-radius: 4px; - margin: 0px; - /* Fixes double border inside pane with pyqt5 */ - padding: 0px; -} - -QTabWidget::pane:selected { - background-color: #32414B; - border: 1px solid #1464A0; -} - -/* QTabBar ---------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar - ---------------------------------------------------------------------------- */ -QTabBar { - qproperty-drawBase: 0; - border-radius: 4px; - margin: 0px; - padding: 2px; - border: 0; - /* left: 5px; move to the right by 5px - removed for fix */ -} - -QTabBar::close-button { - border: 0; - margin: 2px; - padding: 2px; - image: url(":/qss_icons/rc/window_close.png"); -} - -QTabBar::close-button:hover { - image: url(":/qss_icons/rc/window_close_focus.png"); -} - -QTabBar::close-button:pressed { - image: url(":/qss_icons/rc/window_close_pressed.png"); -} - -/* QTabBar::tab - selected ------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar - ---------------------------------------------------------------------------- */ -QTabBar::tab { - /* !selected and disabled ----------------------------------------- */ - /* selected ------------------------------------------------------- */ -} - -QTabBar::tab:top:selected:disabled { - border-bottom: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:bottom:selected:disabled { - border-top: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:left:selected:disabled { - border-right: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:right:selected:disabled { - border-left: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:top:!selected:disabled { - border-bottom: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:bottom:!selected:disabled { - border-top: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:left:!selected:disabled { - border-right: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:right:!selected:disabled { - border-left: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:top:!selected { - border-bottom: 2px solid #19232D; - margin-top: 2px; -} - -QTabBar::tab:bottom:!selected { - border-top: 2px solid #19232D; - margin-bottom: 3px; -} - -QTabBar::tab:left:!selected { - border-left: 2px solid #19232D; - margin-right: 2px; -} - -QTabBar::tab:right:!selected { - border-right: 2px solid #19232D; - margin-left: 2px; -} - -QTabBar::tab:top { - background-color: #32414B; - color: #F0F0F0; - margin-left: 2px; - padding-left: 4px; - padding-right: 4px; - padding-top: 2px; - padding-bottom: 2px; - min-width: 5px; - border-bottom: 3px solid #32414B; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} - -QTabBar::tab:top:selected { - background-color: #505F69; - color: #F0F0F0; - border-bottom: 3px solid #1464A0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} - -QTabBar::tab:top:!selected:hover { - border: 1px solid #148CD2; - border-bottom: 3px solid #148CD2; - /* Fixes spyder-ide/spyder#9766 */ - padding-left: 4px; - padding-right: 4px; -} - -QTabBar::tab:bottom { - color: #F0F0F0; - border-top: 3px solid #32414B; - background-color: #32414B; - margin-left: 2px; - padding-left: 4px; - padding-right: 4px; - padding-top: 2px; - padding-bottom: 2px; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - min-width: 5px; -} - -QTabBar::tab:bottom:selected { - color: #F0F0F0; - background-color: #505F69; - border-top: 3px solid #1464A0; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; -} - -QTabBar::tab:bottom:!selected:hover { - border: 1px solid #148CD2; - border-top: 3px solid #148CD2; - /* Fixes spyder-ide/spyder#9766 */ - padding-left: 4px; - padding-right: 4px; -} - -QTabBar::tab:left { - color: #F0F0F0; - background-color: #32414B; - margin-top: 2px; - padding-left: 2px; - padding-right: 2px; - padding-top: 4px; - padding-bottom: 4px; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - min-height: 5px; -} - -QTabBar::tab:left:selected { - color: #F0F0F0; - background-color: #505F69; - border-right: 3px solid #1464A0; -} - -QTabBar::tab:left:!selected:hover { - border: 1px solid #148CD2; - border-right: 3px solid #148CD2; - padding: 0px; -} - -QTabBar::tab:right { - color: #F0F0F0; - background-color: #32414B; - margin-top: 2px; - padding-left: 2px; - padding-right: 2px; - padding-top: 4px; - padding-bottom: 4px; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - min-height: 5px; -} - -QTabBar::tab:right:selected { - color: #F0F0F0; - background-color: #505F69; - border-left: 3px solid #1464A0; -} - -QTabBar::tab:right:!selected:hover { - border: 1px solid #148CD2; - border-left: 3px solid #148CD2; - padding: 0px; -} - -QTabBar QToolButton { - /* Fixes #136 */ - background-color: #32414B; - height: 12px; - width: 12px; -} - -QTabBar QToolButton:pressed { - background-color: #32414B; -} - -QTabBar QToolButton:pressed:hover { - border: 1px solid #148CD2; -} - -QTabBar QToolButton::left-arrow:enabled { - image: url(":/qss_icons/rc/arrow_left.png"); -} - -QTabBar QToolButton::left-arrow:disabled { - image: url(":/qss_icons/rc/arrow_left_disabled.png"); -} - -QTabBar QToolButton::right-arrow:enabled { - image: url(":/qss_icons/rc/arrow_right.png"); -} - -QTabBar QToolButton::right-arrow:disabled { - image: url(":/qss_icons/rc/arrow_right_disabled.png"); -} - -/* QDockWiget ------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QDockWidget { - outline: 1px solid #32414B; - background-color: #19232D; - border: 1px solid #32414B; - border-radius: 4px; - titlebar-close-icon: url(":/qss_icons/rc/window_close.png"); - titlebar-normal-icon: url(":/qss_icons/rc/window_undock.png"); -} - -QDockWidget::title { - /* Better size for title bar */ - padding: 6px; - spacing: 4px; - border: none; - background-color: #32414B; -} - -QDockWidget::close-button { - background-color: #32414B; - border-radius: 4px; - border: none; -} - -QDockWidget::close-button:hover { - image: url(":/qss_icons/rc/window_close_focus.png"); -} - -QDockWidget::close-button:pressed { - image: url(":/qss_icons/rc/window_close_pressed.png"); -} - -QDockWidget::float-button { - background-color: #32414B; - border-radius: 4px; - border: none; -} - -QDockWidget::float-button:hover { - image: url(":/qss_icons/rc/window_undock_focus.png"); -} - -QDockWidget::float-button:pressed { - image: url(":/qss_icons/rc/window_undock_pressed.png"); -} - -/* QTreeView QListView QTableView ----------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtreeview -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview - ---------------------------------------------------------------------------- */ -QTreeView:branch:selected, QTreeView:branch:hover { - background: url(":/qss_icons/rc/transparent.png"); -} - -QTreeView:branch:has-siblings:!adjoins-item { - border-image: url(":/qss_icons/rc/branch_line.png") 0; -} - -QTreeView:branch:has-siblings:adjoins-item { - border-image: url(":/qss_icons/rc/branch_more.png") 0; -} - -QTreeView:branch:!has-children:!has-siblings:adjoins-item { - border-image: url(":/qss_icons/rc/branch_end.png") 0; -} - -QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings { - border-image: none; - image: url(":/qss_icons/rc/branch_closed.png"); -} - -QTreeView:branch:open:has-children:!has-siblings, QTreeView:branch:open:has-children:has-siblings { - border-image: none; - image: url(":/qss_icons/rc/branch_open.png"); -} - -QTreeView:branch:has-children:!has-siblings:closed:hover, QTreeView:branch:closed:has-children:has-siblings:hover { - image: url(":/qss_icons/rc/branch_closed_focus.png"); -} - -QTreeView:branch:open:has-children:!has-siblings:hover, QTreeView:branch:open:has-children:has-siblings:hover { - image: url(":/qss_icons/rc/branch_open_focus.png"); -} - -QTreeView::indicator:checked, -QListView::indicator:checked { - image: url(":/qss_icons/rc/checkbox_checked.png"); -} - -QTreeView::indicator:checked:hover, QTreeView::indicator:checked:focus, QTreeView::indicator:checked:pressed, -QListView::indicator:checked:hover, -QListView::indicator:checked:focus, -QListView::indicator:checked:pressed { - image: url(":/qss_icons/rc/checkbox_checked_focus.png"); -} - -QTreeView::indicator:unchecked, -QListView::indicator:unchecked { - image: url(":/qss_icons/rc/checkbox_unchecked.png"); -} - -QTreeView::indicator:unchecked:hover, QTreeView::indicator:unchecked:focus, QTreeView::indicator:unchecked:pressed, -QListView::indicator:unchecked:hover, -QListView::indicator:unchecked:focus, -QListView::indicator:unchecked:pressed { - image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); -} - -QTreeView::indicator:indeterminate, -QListView::indicator:indeterminate { - image: url(":/qss_icons/rc/checkbox_indeterminate.png"); -} - -QTreeView::indicator:indeterminate:hover, QTreeView::indicator:indeterminate:focus, QTreeView::indicator:indeterminate:pressed, -QListView::indicator:indeterminate:hover, -QListView::indicator:indeterminate:focus, -QListView::indicator:indeterminate:pressed { - image: url(":/qss_icons/rc/checkbox_indeterminate_focus.png"); -} - -QTreeView, -QListView, -QTableView, -QColumnView { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - gridline-color: #32414B; - border-radius: 4px; -} - -QTreeView:disabled, -QListView:disabled, -QTableView:disabled, -QColumnView:disabled { - background-color: #19232D; - color: #787878; -} - -QTreeView:selected, -QListView:selected, -QTableView:selected, -QColumnView:selected { - background-color: #1464A0; - color: #32414B; -} - -QTreeView:hover, -QListView:hover, -QTableView:hover, -QColumnView:hover { - background-color: #19232D; - border: 1px solid #148CD2; -} - -QTreeView::item:pressed, -QListView::item:pressed, -QTableView::item:pressed, -QColumnView::item:pressed { - background-color: #1464A0; -} - -QTreeView::item:selected:hover, -QListView::item:selected:hover, -QTableView::item:selected:hover, -QColumnView::item:selected:hover { - background: #1464A0; - color: #19232D; -} - -QTreeView::item:selected:active, -QListView::item:selected:active, -QTableView::item:selected:active, -QColumnView::item:selected:active { - background-color: #1464A0; -} - -QTreeView::item:!selected:hover, -QListView::item:!selected:hover, -QTableView::item:!selected:hover, -QColumnView::item:!selected:hover { - outline: 0; - color: #148CD2; - background-color: #32414B; -} - -QTableCornerButton::section { - background-color: #19232D; - border: 1px transparent #32414B; - border-radius: 0px; -} - -/* QHeaderView ------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview - ---------------------------------------------------------------------------- */ -QHeaderView { - background-color: #32414B; - border: 0px transparent #32414B; - padding: 0px; - margin: 0px; - border-radius: 0px; -} - -QHeaderView:disabled { - background-color: #32414B; - border: 1px transparent #32414B; - padding: 2px; -} - -QHeaderView::section { - background-color: #32414B; - color: #F0F0F0; - padding: 2px; - border-radius: 0px; - text-align: left; -} - -QHeaderView::section:checked { - color: #F0F0F0; - background-color: #1464A0; -} - -QHeaderView::section:checked:disabled { - color: #787878; - background-color: #14506E; -} - -QHeaderView::section::horizontal { - padding-left: 4px; - padding-right: 4px; - border-left: 1px solid #19232D; -} - -QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { - border-left: 1px solid #32414B; -} - -QHeaderView::section::horizontal:disabled { - color: #787878; -} - -QHeaderView::section::vertical { - padding-left: 4px; - padding-right: 4px; - border-top: 1px solid #19232D; -} - -QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { - border-top: 1px solid #32414B; -} - -QHeaderView::section::vertical:disabled { - color: #787878; -} - -QHeaderView::down-arrow { - /* Those settings (border/width/height/background-color) solve bug */ - /* transparent arrow background and size */ - background-color: #32414B; - border: none; - height: 12px; - width: 12px; - padding-left: 2px; - padding-right: 2px; - image: url(":/qss_icons/rc/arrow_down.png"); -} - -QHeaderView::up-arrow { - background-color: #32414B; - border: none; - height: 12px; - width: 12px; - padding-left: 2px; - padding-right: 2px; - image: url(":/qss_icons/rc/arrow_up.png"); -} - -/* QToolBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbox - ---------------------------------------------------------------------------- */ -QToolBox { - padding: 0px; - border: 0px; - border: 1px solid #32414B; -} - -QToolBox:selected { - padding: 0px; - border: 2px solid #1464A0; -} - -QToolBox::tab { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QToolBox::tab:disabled { - color: #787878; -} - -QToolBox::tab:selected { - background-color: #505F69; - border-bottom: 2px solid #1464A0; -} - -QToolBox::tab:selected:disabled { - background-color: #32414B; - border-bottom: 2px solid #14506E; -} - -QToolBox::tab:!selected { - background-color: #32414B; - border-bottom: 2px solid #32414B; -} - -QToolBox::tab:!selected:disabled { - background-color: #19232D; -} - -QToolBox::tab:hover { - border-color: #148CD2; - border-bottom: 2px solid #148CD2; -} - -QToolBox QScrollArea QWidget QWidget { - padding: 0px; - border: 0px; - background-color: #19232D; -} - -/* QFrame ----------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe -https://doc.qt.io/qt-5/qframe.html#-prop -https://doc.qt.io/qt-5/qframe.html#details -https://stackoverflow.com/questions/14581498/qt-stylesheet-for-hline-vline-color - ---------------------------------------------------------------------------- */ -/* (dot) .QFrame fix #141, #126, #123 */ -.QFrame { - border-radius: 4px; - border: 1px solid #32414B; - /* No frame */ - /* HLine */ - /* HLine */ -} - -.QFrame[frameShape="0"] { - border-radius: 4px; - border: 1px transparent #32414B; -} - -.QFrame[frameShape="4"] { - max-height: 2px; - border: none; - background-color: #32414B; -} - -.QFrame[frameShape="5"] { - max-width: 2px; - border: none; - background-color: #32414B; -} - -/* QSplitter -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsplitter - ---------------------------------------------------------------------------- */ -QSplitter { - background-color: #32414B; - spacing: 0px; - padding: 0px; - margin: 0px; -} - -QSplitter::handle { - background-color: #32414B; - border: 0px solid #19232D; - spacing: 0px; - padding: 1px; - margin: 0px; -} - -QSplitter::handle:hover { - background-color: #787878; -} - -QSplitter::handle:horizontal { - width: 5px; - image: url(":/qss_icons/rc/line_vertical.png"); -} - -QSplitter::handle:vertical { - height: 5px; - image: url(":/qss_icons/rc/line_horizontal.png"); -} - -/* QDateEdit -------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QDateEdit { - selection-background-color: #1464A0; - border-style: solid; - border: 1px solid #32414B; - border-radius: 4px; - /* This fixes 103, 111 */ - padding-top: 2px; - /* This fixes 103, 111 */ - padding-bottom: 2px; - padding-left: 4px; - padding-right: 4px; - min-width: 10px; -} - -QDateEdit:on { - selection-background-color: #1464A0; -} - -QDateEdit::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 12px; - border-left: 1px solid #32414B; -} - -QDateEdit::down-arrow { - image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 8px; - width: 8px; -} - -QDateEdit::down-arrow:on, QDateEdit::down-arrow:hover, QDateEdit::down-arrow:focus { - image: url(":/qss_icons/rc/arrow_down.png"); -} - -QDateEdit QAbstractItemView { - background-color: #19232D; - border-radius: 4px; - border: 1px solid #32414B; - selection-background-color: #1464A0; -} - -/* QAbstractView ---------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QAbstractView:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QAbstractView:selected { - background: #1464A0; - color: #32414B; -} - -/* PlotWidget ------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -PlotWidget { - /* Fix cut labels in plots #134 */ - padding: 0px; -} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py index 86e4b68488..6a3340788b 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py @@ -384,8 +384,7 @@ def stash_env(_SYNTH_ENV_DICT = OrderedDict()): # changed to just make the fallback what is set in boostrap # so now it's less of a fallnack and more correct if not # explicitly set - _LY_PROJECT = os.getenv(ENVAR_LY_PROJECT, - get_current_project(_LY_DEV)) + _LY_PROJECT = os.getenv(ENVAR_LY_PROJECT) _SYNTH_ENV_DICT[ENVAR_LY_PROJECT] = _LY_PROJECT _LY_BUILD_DIR_NAME = os.getenv(ENVAR_LY_BUILD_DIR_NAME, diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py index c62571b2f5..0fa0b6ee67 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py @@ -109,11 +109,11 @@ def init_ly_pyside(LY_DEV=None): 'bin', 'profile').resolve() - # allows to retreive from settings.QTFORPYTHON_PATH - from azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor - QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(LY_DEV)).resolve() - os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH) - site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH + # # allows to retreive from settings.QTFORPYTHON_PATH + # from azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor + # QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(LY_DEV)).resolve() + # os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH) + # site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH QT_PLUGIN_PATH = Path.joinpath(LY_BIN_PATH, 'EditorPlugins').resolve() @@ -131,15 +131,15 @@ def init_ly_pyside(LY_DEV=None): # add Qt binaries to the Windows path to handle findings DLL file dependencies if sys.platform.startswith('win'): - path = os.environ['PATH'] - newPath = '' - newPath += str(LY_BIN_PATH) + os.pathsep - newPath += str(Path.joinpath(QTFORPYTHON_PATH, - 'shiboken2').resolve()) + os.pathsep - newPath += str(Path.joinpath(QTFORPYTHON_PATH, - 'PySide2').resolve()) + os.pathsep - newPath += path - os.environ['PATH']=newPath + # path = os.environ['PATH'] + # newPath = '' + # newPath += str(LY_BIN_PATH) + os.pathsep + # newPath += str(Path.joinpath(QTFORPYTHON_PATH, + # 'shiboken2').resolve()) + os.pathsep + # newPath += str(Path.joinpath(QTFORPYTHON_PATH, + # 'PySide2').resolve()) + os.pathsep + # newPath += path + # os.environ['PATH']=newPath _LOGGER.debug('PySide2 bootstrapped PATH for Windows.') try: @@ -223,8 +223,8 @@ os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(_DCCSI_DEV_MODE) _LY_DEV = azpy.config_utils.get_stub_check_path(in_path=_DCCSIG_PATH, check_stub='engine.json') os.environ["DYNACONF_LY_DEV"] = str(_LY_DEV.resolve()) -_LY_PROJECT = azpy.config_utils.get_current_project(_LY_DEV) -os.environ["DYNACONF_LY_PROJECT"] = _LY_PROJECT +_LY_PROJECT = azpy.config_utils.get_current_project() +os.environ["DYNACONF_LY_PROJECT"] = str(_LY_PROJECT.resolve()) _LY_PROJECT_PATH = Path(_LY_DEV, _LY_PROJECT) os.environ["DYNACONF_LY_PROJECT_PATH"] = str(_LY_PROJECT_PATH) os.environ["DYNACONF_DCCSIG_PATH"] = str(_DCCSIG_PATH) @@ -319,7 +319,7 @@ if __name__ == '__main__': settings.setenv() # doing this will add/set the additional DYNACONF_ envars - _LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) + #_LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) _LOGGER.info('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH)) _LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) _LOGGER.info('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json new file mode 100644 index 0000000000..ca80c62dd0 --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -0,0 +1,17 @@ +{ + "gem_name": "Atom_DccScriptingInterface", + "GemFormatVersion": 4, + "Uuid": "7bf5a77dacd8438bb4966a66b5a678d8", + "Name": "Atom_DccScriptingInterface", + "DisplayName": "Atom DccScriptingInterface (DCCsi)", + "Version": "0.1.0", + "Summary": "A python framework for working with various DCC tools and workflows.", + "Tags": ["DCC","Digital","Content","Creation"], + "IconPath": "preview.png", + "Modules": [ + { + "Name": "Editor", + "Type": "EditorModule" + } + ] +} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt index 2f7626addb..1536b79f3d 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt @@ -36,6 +36,16 @@ unipath==1.1 \ --hash=sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae \ --hash=sha256:e6257e508d8abbfb6ddd8ec357e33589f1f48b1599127f23b017124d90b0fff7 # via -r requirements.txt +qdarkstyle==3.0.2 \ + --hash=sha256:55d149cf5f40ee297397f1818e091118cefb855a4a9c5c38566c47acd2d8c7ae \ + --hash=sha256:7c791535cc20b3cc1e8e1bf6b88dabe53cb0615983df702be83597e73ada2558 + # via -r c:\temp\requirements.txt +qtpy==1.9.0 \ + --hash=sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d \ + --hash=sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea + # via + # -r c:\temp\requirements.txt + # qdarkstyle wincertstore==0.2 \ --hash=sha256:22d5eebb52df88a8d4014d5cf6d1b6c3a5d469e6c3b2e2854f3a003e48872356 \ --hash=sha256:780bd1557c9185c15d9f4221ea7f905cb20b93f7151ca8ccaed9714dce4b327a diff --git a/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp b/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp index f7b54a44d9..b08aa6fa1d 100644 --- a/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp @@ -57,11 +57,11 @@ namespace AudioControlBuilder { atlPlatform = "windows"; } - else if (platform == "es3") + else if (platform == "android") { atlPlatform = "android"; } - else if (platform == "osx_gl") + else if (platform == "mac") { atlPlatform = "mac"; } diff --git a/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h b/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h index ad2ba28269..7fabfb75b3 100644 --- a/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h +++ b/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h @@ -46,7 +46,7 @@ namespace Audio::Wwise ~PlatformMapping() = default; // Serialized Data... - AZStd::string m_assetPlatform; // LY Asset Platform name (i.e. "pc", "osx_gl", "es3", ...) + AZStd::string m_assetPlatform; // LY Asset Platform name (i.e. "pc", "mac", "android", ...) AZStd::string m_altAssetPlatform; // Some platforms can be run using a different asset platform. Useful for builder worker. AZStd::string m_enginePlatform; // LY Engine Platform name (i.e. "Windows", "Mac", "Android", ...) AZStd::string m_wwisePlatform; // Wwise Platform name (i.e. "Windows", "Mac", "Android", ...) diff --git a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json index 22cc632cbd..38f6ff142e 100644 --- a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json +++ b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json @@ -1,5 +1,5 @@ { - "assetPlatform": "es3", + "assetPlatform": "android", "altAssetPlatform": "", "enginePlatform": "Android", "wwisePlatform": "Android", diff --git a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json index 4069d8add7..a996b85150 100644 --- a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json +++ b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json @@ -1,5 +1,5 @@ { - "assetPlatform": "osx_gl", + "assetPlatform": "mac", "altAssetPlatform": "", "enginePlatform": "Mac", "wwisePlatform": "Mac", diff --git a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp index 0336ae8c09..6c4c78c412 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp +++ b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp @@ -68,7 +68,7 @@ namespace Blast auto transform = AZ::Transform::CreateFromQuaternionAndTranslation( m_bodyConfiguration.m_orientation, m_bodyConfiguration.m_position); - transform.MultiplyByScale(AZ::Vector3(m_scale)); + transform.MultiplyByUniformScale(m_scale); AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformInterface::SetWorldTM, transform); diff --git a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp index 5fe1e0ab30..25c0f904e3 100644 --- a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp +++ b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp @@ -202,7 +202,7 @@ namespace Blast if (parentBody) { parentTransform = parentBody->GetTransform(); - parentTransform.MultiplyByScale(AZ::Vector3(m_initialTransform.GetScale().GetMaxElement())); + parentTransform.MultiplyByUniformScale(m_initialTransform.GetUniformScale()); } else { @@ -254,7 +254,7 @@ namespace Blast actorDesc.m_parentCenterOfMass = transform.GetTranslation(); actorDesc.m_parentLinearVelocity = AZ::Vector3::CreateZero(); actorDesc.m_bodyConfiguration = configuration; - actorDesc.m_scale = transform.GetScale().GetMaxElement(); + actorDesc.m_scale = transform.GetUniformScale(); return actorDesc; } diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index 1ae87e2282..00aa12cb84 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -644,19 +644,7 @@ namespace Blast MOCK_METHOD0(GetLocalX, float()); MOCK_METHOD0(GetLocalY, float()); MOCK_METHOD0(GetLocalZ, float()); - MOCK_METHOD1(SetRotation, void(const AZ::Vector3&)); - MOCK_METHOD1(SetRotationX, void(float)); - MOCK_METHOD1(SetRotationY, void(float)); - MOCK_METHOD1(SetRotationZ, void(float)); - MOCK_METHOD1(SetRotationQuaternion, void(const AZ::Quaternion&)); - MOCK_METHOD1(RotateByX, void(float)); - MOCK_METHOD1(RotateByY, void(float)); - MOCK_METHOD1(RotateByZ, void(float)); - MOCK_METHOD0(GetRotationEulerRadians, AZ::Vector3()); - MOCK_METHOD0(GetRotationQuaternion, AZ::Quaternion()); - MOCK_METHOD0(GetRotationX, float()); - MOCK_METHOD0(GetRotationY, float()); - MOCK_METHOD0(GetRotationZ, float()); + MOCK_METHOD1(SetWorldRotationQuaternion, void(const AZ::Quaternion&)); MOCK_METHOD0(GetWorldRotation, AZ::Vector3()); MOCK_METHOD0(GetWorldRotationQuaternion, AZ::Quaternion()); MOCK_METHOD1(SetLocalRotation, void(const AZ::Vector3&)); @@ -666,20 +654,12 @@ namespace Blast MOCK_METHOD1(RotateAroundLocalZ, void(float)); MOCK_METHOD0(GetLocalRotation, AZ::Vector3()); MOCK_METHOD0(GetLocalRotationQuaternion, AZ::Quaternion()); - MOCK_METHOD1(SetScale, void(const AZ::Vector3&)); - MOCK_METHOD1(SetScaleX, void(float)); - MOCK_METHOD1(SetScaleY, void(float)); - MOCK_METHOD1(SetScaleZ, void(float)); - MOCK_METHOD0(GetScale, AZ::Vector3()); - MOCK_METHOD0(GetScaleX, float()); - MOCK_METHOD0(GetScaleY, float()); - MOCK_METHOD0(GetScaleZ, float()); MOCK_METHOD1(SetLocalScale, void(const AZ::Vector3&)); - MOCK_METHOD1(SetLocalScaleX, void(float)); - MOCK_METHOD1(SetLocalScaleY, void(float)); - MOCK_METHOD1(SetLocalScaleZ, void(float)); MOCK_METHOD0(GetLocalScale, AZ::Vector3()); MOCK_METHOD0(GetWorldScale, AZ::Vector3()); + MOCK_METHOD1(SetLocalUniformScale, void(float)); + MOCK_METHOD0(GetLocalUniformScale, float()); + MOCK_METHOD0(GetWorldUniformScale, float()); MOCK_METHOD0(GetParentId, AZ::EntityId()); MOCK_METHOD0(GetParent, TransformInterface*()); MOCK_METHOD1(SetParent, void(AZ::EntityId)); diff --git a/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.cpp b/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.cpp index 1f677d7b6f..1304c7b392 100644 --- a/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.cpp +++ b/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.cpp @@ -274,12 +274,15 @@ namespace DebugDraw AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); - OnTickAabbs(*debugDisplay); - OnTickLines(*debugDisplay); - OnTickObbs(*debugDisplay); - OnTickRays(*debugDisplay); - OnTickSpheres(*debugDisplay); - OnTickText(*debugDisplay); + if (debugDisplay) + { + OnTickAabbs(*debugDisplay); + OnTickLines(*debugDisplay); + OnTickObbs(*debugDisplay); + OnTickRays(*debugDisplay); + OnTickSpheres(*debugDisplay); + OnTickText(*debugDisplay); + } } template diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index 0aef3f9d4f..b2389a3086 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -1157,7 +1157,7 @@ namespace MCommon void RenderUtil::RenderSphere(const AZ::Vector3& position, float radius, const MCore::RGBAColor& color) { // setup the world space matrix of the sphere - AZ::Transform sphereTransform = AZ::Transform::CreateScale(AZ::Vector3(radius, radius, radius)); + AZ::Transform sphereTransform = AZ::Transform::CreateUniformScale(radius); sphereTransform.SetTranslation(position); // render the sphere @@ -1297,18 +1297,6 @@ namespace MCommon } - // render a cube - void RenderUtil::RenderCube(const AZ::Vector3& size, const AZ::Vector3& position, const MCore::RGBAColor& color) - { - // setup the world space matrix of the cube - AZ::Transform cubeTransform = AZ::Transform::CreateScale(size); - cubeTransform.SetTranslation(position); - - // render the cube - RenderCube(color, cubeTransform); - } - - // construct the arrow head mesh used for rendering RenderUtil::UtilMesh* RenderUtil::CreateArrowHead(float height, float radius) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h index 86e41e56ef..b724c28720 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h @@ -297,14 +297,6 @@ namespace MCommon */ void RenderCylinder(float baseRadius, float topRadius, float length, const AZ::Vector3& position, const AZ::Vector3& direction, const MCore::RGBAColor& color); - /** - * Render a cube. - * @param size The size of the cube. - * @param position The position of the center of the cube. - * @param color The desired cube color. - */ - void RenderCube(const AZ::Vector3& size, const AZ::Vector3& position, const MCore::RGBAColor& color); - /** * Render a triangle (CCW). * @param v1 The first corner of the triangle. diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp index 32b779f385..54ccd00535 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp @@ -169,7 +169,6 @@ namespace MCommon if (mXAxisVisible) { renderUtil->RenderLine(mPosition, mPosition + mSignX * AZ::Vector3(mScaledSize.GetX() + 0.5f * mBaseRadius, 0.0f, 0.0f), xAxisColor); - //renderUtil->RenderCube( Vector3(mBaseRadius, mBaseRadius, mBaseRadius), mPosition + mSignX * Vector3(mScaledSize.x+mBaseRadius, 0, 0), ManipulatorColors::mRed ); AZ::Vector3 quadPos = MCore::Project(mPosition + mSignX * AZ::Vector3(mScaledSize.GetX() + mBaseRadius, 0, 0), camera->GetViewProjMatrix(), screenWidth, screenHeight); renderUtil->RenderBorderedRect(static_cast(quadPos.GetX() - 2.0f), static_cast(quadPos.GetX() + 3.0f), static_cast(quadPos.GetY() - 2.0f), static_cast(quadPos.GetY() + 3.0f), ManipulatorColors::mRed, ManipulatorColors::mRed); @@ -186,7 +185,6 @@ namespace MCommon if (mYAxisVisible) { renderUtil->RenderLine(mPosition, mPosition + mSignY * AZ::Vector3(0.0f, mScaledSize.GetY(), 0.0f), yAxisColor); - //renderUtil->RenderCube( Vector3(mBaseRadius, mBaseRadius, mBaseRadius), mPosition + mSignY * Vector3(0, mScaledSize.y+0.5*mBaseRadius, 0), ManipulatorColors::mGreen ); AZ::Vector3 quadPos = MCore::Project(mPosition + mSignY * AZ::Vector3(0, mScaledSize.GetY() + 0.5f * mBaseRadius, 0), camera->GetViewProjMatrix(), screenWidth, screenHeight); renderUtil->RenderBorderedRect(static_cast(quadPos.GetX() - 2.0f), static_cast(quadPos.GetX() + 3.0f), static_cast(quadPos.GetY() - 2.0f), static_cast(quadPos.GetY() + 3.0f), ManipulatorColors::mGreen, ManipulatorColors::mGreen); @@ -203,7 +201,6 @@ namespace MCommon if (mZAxisVisible) { renderUtil->RenderLine(mPosition, mPosition + mSignZ * AZ::Vector3(0.0f, 0.0f, mScaledSize.GetZ()), zAxisColor); - //renderUtil->RenderCube( Vector3(mBaseRadius, mBaseRadius, mBaseRadius), mPosition + mSignZ * Vector3(0, 0, mScaledSize.z+0.5*mBaseRadius), ManipulatorColors::mBlue ); AZ::Vector3 quadPos = MCore::Project(mPosition + mSignZ * AZ::Vector3(0, 0, mScaledSize.GetZ() + 0.5f * mBaseRadius), camera->GetViewProjMatrix(), screenWidth, screenHeight); renderUtil->RenderBorderedRect(static_cast(quadPos.GetX() - 2.0f), static_cast(quadPos.GetX() + 3.0f), static_cast(quadPos.GetY() - 2.0f), static_cast(quadPos.GetY() + 3.0f), ManipulatorColors::mBlue, ManipulatorColors::mBlue); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp index b2322b5379..e3195beba9 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp @@ -183,7 +183,7 @@ namespace EMotionFX { #ifndef EMFX_SCALE_DISABLED mPosition = transform.GetTranslation(); - mScale = transform.GetScale(); + mScale = AZ::Vector3(transform.GetUniformScale()); mRotation = transform.GetRotation(); #else mPosition = transform.GetTranslation(); diff --git a/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h b/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h index 519d44dace..fdead82491 100644 --- a/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h +++ b/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h @@ -58,7 +58,7 @@ namespace MCore AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(emfxTransform.mRotation, emfxTransform.mPosition); EMFX_SCALECODE ( - transform.MultiplyByScale(emfxTransform.mScale); + transform.MultiplyByUniformScale(emfxTransform.mScale.GetMaxElement()); ) return transform; } @@ -386,7 +386,7 @@ namespace MCore AZ::Transform result; result.SetTranslation(translation); result.SetRotation(rotation); - result.SetScale(scale); + result.SetUniformScale(scale.GetMaxElement()); return result; } diff --git a/Gems/EMotionFX/Code/MCore/Source/OBB.cpp b/Gems/EMotionFX/Code/MCore/Source/OBB.cpp index bb73f9413b..e7a0011684 100644 --- a/Gems/EMotionFX/Code/MCore/Source/OBB.cpp +++ b/Gems/EMotionFX/Code/MCore/Source/OBB.cpp @@ -96,9 +96,9 @@ namespace MCore // create the AABB of (box1 in space of box0) const AZ::Transform& mtx = _1in0.mRotation; - AZ::Vector3 transformedAxisX = mtx.GetScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisX())); - AZ::Vector3 transformedAxisY = mtx.GetScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisY())); - AZ::Vector3 transformedAxisZ = mtx.GetScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisZ())); + AZ::Vector3 transformedAxisX = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisX())); + AZ::Vector3 transformedAxisY = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisY())); + AZ::Vector3 transformedAxisZ = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisZ())); float f = transformedAxisX.GetAbs().Dot(mExtents) - box.mExtents.GetX(); if (f > _1in0.mCenter.GetX()) diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 0a64c7b408..8e68c8cb44 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -890,7 +890,7 @@ namespace EMotionFX #if AZ_TRAIT_EMOTIONFX_MAIN_WINDOW_DETACHED emotionFXWindowOptions.detachedWindow = true; #endif - emotionFXWindowOptions.optionalMenuText = "Animation Editor (PREVIEW)"; + emotionFXWindowOptions.optionalMenuText = "Animation Editor"; EditorRequests::Bus::Broadcast(&EditorRequests::RegisterViewPane, EMStudio::MainWindow::GetEMotionFXPaneName(), LyViewPane::CategoryTools, emotionFXWindowOptions, windowCreationFunc); } diff --git a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h index b3acb73e19..446abbf6af 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h @@ -26,7 +26,7 @@ namespace EMotionFX MOCK_METHOD0(DisableSimulation, void()); MOCK_METHOD0(DisableSimulationQueued, void()); - MOCK_METHOD0(IsSimulated, bool()); + MOCK_CONST_METHOD0(IsSimulated, bool()); MOCK_CONST_METHOD1(GetState, void(Physics::RagdollState&)); MOCK_METHOD1(SetState, void(const Physics::RagdollState&)); diff --git a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h index 04a1ca4f81..f8c70a5fee 100644 --- a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h +++ b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h @@ -59,7 +59,16 @@ namespace EMotionFX { public: - ComponentFixtureApp() = default; + ComponentFixtureApp() + { + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); + } + } AZ::ComponentTypeList GetRequiredSystemComponents() const override { diff --git a/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp b/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp index 8045499b4a..8e3a19c1ca 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp @@ -77,7 +77,6 @@ namespace EMotionFX { auto testAssetsPath = AZ::IO::Path(GetEMotionFX().GetAssetCacheFolder()) / "TmpTestAssets"; QString dataDir = QString::fromUtf8(testAssetsPath.c_str(), aznumeric_cast(testAssetsPath.Native().size())); - dataDir += "TmpTestAssets"; if (!QDir(dataDir).exists()) { diff --git a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp index 86628d08a1..440638c61f 100644 --- a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -322,6 +323,13 @@ sys.version void SetUp() override { PythonTestingFixture::SetUp(); + + auto registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.RegisterComponentDescriptor(EditorPythonBindings::PythonSystemComponent::CreateDescriptor()); } diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Util.h b/Gems/GradientSignal/Code/Include/GradientSignal/Util.h index 4e15bdc293..1a8bb00eba 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Util.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Util.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -64,15 +65,15 @@ namespace GradientSignal AZ::LerpInverse(bounds.GetMin().GetZ(), bounds.GetMax().GetZ(), point.GetZ())); } - inline void GetObbParamsFromShape(const AZ::EntityId& entity, AZ::Aabb& bounds, AZ::Transform& worldToBoundsTransform) + inline void GetObbParamsFromShape(const AZ::EntityId& entity, AZ::Aabb& bounds, AZ::Matrix3x4& worldToBoundsTransform) { //get bound and transform data for associated shape bounds = AZ::Aabb::CreateNull(); - worldToBoundsTransform = AZ::Transform::CreateIdentity(); + AZ::Transform transform = AZ::Transform::CreateIdentity(); if (entity.IsValid()) { - LmbrCentral::ShapeComponentRequestsBus::Event(entity, &LmbrCentral::ShapeComponentRequestsBus::Events::GetTransformAndLocalBounds, worldToBoundsTransform, bounds); - worldToBoundsTransform.Invert(); + LmbrCentral::ShapeComponentRequestsBus::Event(entity, &LmbrCentral::ShapeComponentRequestsBus::Events::GetTransformAndLocalBounds, transform, bounds); + worldToBoundsTransform = AZ::Matrix3x4::CreateFromTransform(transform.GetInverse()); } } diff --git a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp index f99a8a8dfb..336f428582 100644 --- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp @@ -334,7 +334,7 @@ namespace GradientSignal AZStd::lock_guard lock(m_cacheMutex); //transforming coordinate into "local" relative space of shape bounds - outUVW = m_shapeTransformInverse.TransformPoint(inPosition); + outUVW = m_shapeTransformInverse * inPosition; if (!m_configuration.m_advancedMode || !m_configuration.m_is3d) { @@ -388,7 +388,7 @@ namespace GradientSignal void GradientTransformComponent::GetGradientEncompassingBounds(AZ::Aabb& bounds) const { bounds = m_shapeBounds; - bounds.ApplyTransform(m_shapeTransformInverse.GetInverse()); + bounds.ApplyMatrix3x4(m_shapeTransformInverse.GetInverseFull()); } void GradientTransformComponent::OnCompositionChanged() @@ -501,10 +501,11 @@ namespace GradientSignal m_shapeBounds = AZ::Aabb::CreateFromMinMax(-m_configuration.m_bounds * 0.5f, m_configuration.m_bounds * 0.5f); //rebuild transform from parameters - AZ::Quaternion rotation; - rotation.SetFromEulerDegrees(m_configuration.m_rotate); - const AZ::Transform shapeTransformFinal(m_configuration.m_translate, rotation, m_configuration.m_scale); - m_shapeTransformInverse = shapeTransformFinal.GetInverse(); + AZ::Matrix3x4 shapeTransformFinal; + shapeTransformFinal.SetFromEulerDegrees(m_configuration.m_rotate); + shapeTransformFinal.SetTranslation(m_configuration.m_translate); + shapeTransformFinal.MultiplyByScale(m_configuration.m_scale); + m_shapeTransformInverse = shapeTransformFinal.GetInverseFull(); } AZ::EntityId GradientTransformComponent::GetShapeEntityId() const diff --git a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h index 8b0bfad672..5955da95c7 100644 --- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h +++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -172,7 +173,7 @@ namespace GradientSignal mutable AZStd::recursive_mutex m_cacheMutex; GradientTransformConfig m_configuration; AZ::Aabb m_shapeBounds = AZ::Aabb::CreateNull(); - AZ::Transform m_shapeTransformInverse = AZ::Transform::CreateIdentity(); + AZ::Matrix3x4 m_shapeTransformInverse = AZ::Matrix3x4::CreateIdentity(); LmbrCentral::DependencyMonitor m_dependencyMonitor; AZStd::atomic_bool m_dirty{ false }; }; diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp index 107b94d119..e16260af4d 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp @@ -169,22 +169,24 @@ namespace LmbrCentral { AZ::TransformNotificationBus::MultiHandler::BusDisconnect(GetEntityId()); { - AZ::Transform currentTM = AZ::Transform::CreateIdentity(); - EBUS_EVENT_ID_RESULT(currentTM, GetEntityId(), AZ::TransformBus, GetWorldTM); - AZ::Vector3 currentScale = currentTM.ExtractScale(); + AZ::Transform sourceTM = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(sourceTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); AZ::Transform targetTM = AZ::Transform::CreateIdentity(); - EBUS_EVENT_ID_RESULT(targetTM, m_targetId, AZ::TransformBus, GetWorldTM); + AZ::TransformBus::EventResult(targetTM, m_targetId, &AZ::TransformBus::Events::GetWorldTM); AZ::Transform lookAtTransform = AZ::Transform::CreateLookAt( - currentTM.GetTranslation(), + sourceTM.GetTranslation(), targetTM.GetTranslation(), m_forwardAxis ); - lookAtTransform.MultiplyByScale(currentScale); + // update the rotation and translation for sourceTM based on lookAtTransform, but leave scale unchanged + sourceTM.SetRotation(lookAtTransform.GetRotation()); + sourceTM.SetTranslation(lookAtTransform.GetTranslation()); EBUS_EVENT_ID(GetEntityId(), AZ::TransformBus, SetWorldTM, lookAtTransform); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTM, sourceTM); } AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); } diff --git a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp index 2830f514fe..a2ee4d986f 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp @@ -251,7 +251,7 @@ namespace LmbrCentral const AZ::Transform& currentTransform, const BoxShapeConfig& configuration, const AZ::Vector3& currentNonUniformScale) { AZ::Transform worldFromLocalNormalized = currentTransform; - const float entityScale = worldFromLocalNormalized.ExtractScale().GetMaxElement(); + const float entityScale = worldFromLocalNormalized.ExtractUniformScale(); m_currentPosition = worldFromLocalNormalized.GetTranslation(); m_scaledDimensions = configuration.m_dimensions * currentNonUniformScale * entityScale; diff --git a/Gems/LmbrCentral/Code/Source/Shape/CapsuleShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/CapsuleShape.cpp index e935ed4009..1026c8addc 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/CapsuleShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/CapsuleShape.cpp @@ -203,7 +203,7 @@ namespace LmbrCentral const AZ::Transform& currentTransform, const CapsuleShapeConfig& configuration, [[maybe_unused]] const AZ::Vector3& currentNonUniformScale) { - const float entityScale = currentTransform.GetScale().GetMaxElement(); + const float entityScale = currentTransform.GetUniformScale(); m_axisVector = currentTransform.GetBasisZ().GetNormalizedSafe() * entityScale; const float internalCylinderHeight = configuration.m_height - configuration.m_radius * 2.0f; diff --git a/Gems/LmbrCentral/Code/Source/Shape/CylinderShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/CylinderShape.cpp index 047261862f..a0ba157fc1 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/CylinderShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/CylinderShape.cpp @@ -273,7 +273,7 @@ namespace LmbrCentral const AZ::Transform& currentTransform, const CylinderShapeConfig& configuration, [[maybe_unused]] const AZ::Vector3& currentNonUniformScale) { - const float entityScale = currentTransform.GetScale().GetMaxElement(); + const float entityScale = currentTransform.GetUniformScale(); m_axisVector = currentTransform.GetBasisZ().GetNormalizedSafe() * entityScale; m_baseCenterPoint = currentTransform.GetTranslation() - m_axisVector * (configuration.m_height * 0.5f); m_axisVector = m_axisVector * configuration.m_height; diff --git a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp index a302a83316..6aff6ed98c 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp @@ -167,7 +167,7 @@ namespace LmbrCentral { m_position = currentTransform.GetTranslation(); m_normal = currentTransform.GetBasisZ().GetNormalized(); - m_radius = configuration.m_radius * currentTransform.GetScale().GetMaxElement(); + m_radius = configuration.m_radius * currentTransform.GetUniformScale(); } const DiskShapeConfig& DiskShape::GetDiskConfiguration() const diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp index c0b49b8e55..e323d58c2a 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp @@ -174,6 +174,6 @@ namespace LmbrCentral AZ::Vector3 EditorBoxShapeComponent::GetBoxScale() { - return AZ::Vector3(m_boxShape.GetCurrentTransform().GetScale().GetMaxElement() * m_boxShape.GetCurrentNonUniformScale()); + return AZ::Vector3(m_boxShape.GetCurrentTransform().GetUniformScale() * m_boxShape.GetCurrentNonUniformScale()); } } // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorSplineComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorSplineComponent.cpp index 212ec49c93..44ed73733c 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorSplineComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorSplineComponent.cpp @@ -349,7 +349,7 @@ namespace LmbrCentral const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) { const auto rayIntersectData = IntersectSpline(m_cachedUniformScaleTransform, src, dir, *m_splineCommon.m_spline); - distance = rayIntersectData.m_rayDistance * m_cachedUniformScaleTransform.GetScale().GetMaxElement(); + distance = rayIntersectData.m_rayDistance * m_cachedUniformScaleTransform.GetUniformScale(); AzFramework::CameraState cameraState; AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::EventResult( diff --git a/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp index 3ea8f2a3d0..4244199b85 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/PolygonPrismShape.cpp @@ -434,8 +434,7 @@ namespace LmbrCentral const AZ::Vector3& nonUniformScale = polygonPrism.GetNonUniformScale(); AZ::Transform worldFromLocalUniformScale = worldFromLocal; - const float entityScale = worldFromLocalUniformScale.ExtractScale().GetMaxElement(); - worldFromLocalUniformScale *= AZ::Transform::CreateScale(AZ::Vector3(entityScale)); + worldFromLocalUniformScale.SetUniformScale(worldFromLocalUniformScale.GetUniformScale()); AZ::Aabb aabb = AZ::Aabb::CreateNull(); // check base of prism @@ -465,8 +464,7 @@ namespace LmbrCentral const size_t vertexCount = vertices.size(); AZ::Transform worldFromLocalWithUniformScale = worldFromLocal; - const float transformScale = worldFromLocalWithUniformScale.ExtractScale().GetMaxElement(); - worldFromLocalWithUniformScale *= AZ::Transform::CreateScale(AZ::Vector3(transformScale)); + worldFromLocalWithUniformScale.SetUniformScale(worldFromLocalWithUniformScale.GetUniformScale()); // transform point to local space // it's fine to invert the transform including scale here, because it won't affect whether the point is inside the prism @@ -530,7 +528,7 @@ namespace LmbrCentral // but inverting any scale in the transform would mess up the distance, so extract that first and apply scale separately to the // prism AZ::Transform worldFromLocalNoScale = worldFromLocal; - const float transformScale = worldFromLocalNoScale.ExtractScale().GetMaxElement(); + const float transformScale = worldFromLocalNoScale.ExtractUniformScale(); const AZ::Vector3 combinedScale = transformScale * nonUniformScale; const float scaledHeight = height * combinedScale.GetZ(); @@ -606,9 +604,9 @@ namespace LmbrCentral } // transform ray into local space - AZ::Transform worldFromLocalNomalized = worldFromLocal; - const float entityScale = worldFromLocalNomalized.ExtractScale().GetMaxElement(); - const AZ::Transform localFromWorldNormalized = worldFromLocalNomalized.GetInverse(); + AZ::Transform worldFromLocalNormalized = worldFromLocal; + const float entityScale = worldFromLocalNormalized.ExtractUniformScale(); + const AZ::Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse(); const float rayLength = 1000.0f; const AZ::Vector3 localSrc = localFromWorldNormalized.TransformPoint(src); const AZ::Vector3 localDir = localFromWorldNormalized.TransformVector(dir); diff --git a/Gems/LmbrCentral/Code/Source/Shape/ShapeDisplay.h b/Gems/LmbrCentral/Code/Source/Shape/ShapeDisplay.h index fb3164960e..5b5cc5fb4d 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/ShapeDisplay.h +++ b/Gems/LmbrCentral/Code/Source/Shape/ShapeDisplay.h @@ -44,8 +44,7 @@ namespace LmbrCentral // only uniform scale is supported in physics so the debug visuals reflect this fact AZ::Transform worldFromLocalWithUniformScale = worldFromLocal; - const AZ::Vector3 scale = worldFromLocalWithUniformScale.ExtractScale(); - worldFromLocalWithUniformScale.MultiplyByScale(AZ::Vector3(scale.GetMaxElement())); + worldFromLocalWithUniformScale.SetUniformScale(worldFromLocalWithUniformScale.GetUniformScale()); debugDisplay.PushMatrix(worldFromLocalWithUniformScale); diff --git a/Gems/LmbrCentral/Code/Source/Shape/SphereShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/SphereShape.cpp index c06d38a612..05472df8be 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/SphereShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/SphereShape.cpp @@ -136,7 +136,7 @@ namespace LmbrCentral [[maybe_unused]] const AZ::Vector3& currentNonUniformScale) { m_position = currentTransform.GetTranslation(); - m_radius = configuration.m_radius * currentTransform.GetScale().GetMaxElement(); + m_radius = configuration.m_radius * currentTransform.GetUniformScale(); } void DrawSphereShape( diff --git a/Gems/LmbrCentral/Code/Source/Shape/TubeShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/TubeShape.cpp index 6ca2e55de8..0db2660e01 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/TubeShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/TubeShape.cpp @@ -187,7 +187,7 @@ namespace LmbrCentral static AZ::Aabb CalculateTubeBounds(const TubeShape& tubeShape, const AZ::Transform& transform) { - const auto maxScale = transform.GetScale().GetMaxElement(); + const auto maxScale = transform.GetUniformScale(); const auto scaledRadiusFn = [&tubeShape, maxScale](const AZ::SplineAddress& splineAddress) { @@ -217,8 +217,7 @@ namespace LmbrCentral } AZ::Transform worldFromLocalUniformScale = m_currentTransform; - const float maxScale = worldFromLocalUniformScale.ExtractScale().GetMaxElement(); - worldFromLocalUniformScale *= AZ::Transform::CreateScale(AZ::Vector3(maxScale)); + worldFromLocalUniformScale.SetUniformScale(worldFromLocalUniformScale.GetUniformScale()); return CalculateTubeBounds(*this, worldFromLocalUniformScale); } @@ -237,45 +236,43 @@ namespace LmbrCentral } AZ::Transform worldFromLocalNormalized = m_currentTransform; - const AZ::Vector3 scale = AZ::Vector3(worldFromLocalNormalized.ExtractScale().GetMaxElement()); + const float scale = worldFromLocalNormalized.ExtractUniformScale(); const AZ::Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse(); - const AZ::Vector3 localPoint = localFromWorldNormalized.TransformPoint(point) * scale.GetReciprocal(); + const AZ::Vector3 localPoint = localFromWorldNormalized.TransformPoint(point) / scale; const auto address = m_spline->GetNearestAddressPosition(localPoint).m_splineAddress; const float radiusSq = powf(m_radius, 2.0f); const float variableRadiusSq = powf(m_variableRadius.GetElementInterpolated(address, Lerpf), 2.0f); - return (m_spline->GetPosition(address) - localPoint).GetLengthSq() < (radiusSq + variableRadiusSq) * - scale.GetMaxElement(); + return (m_spline->GetPosition(address) - localPoint).GetLengthSq() < (radiusSq + variableRadiusSq) * scale; } float TubeShape::DistanceSquaredFromPoint(const AZ::Vector3& point) { AZ::Transform worldFromLocalNormalized = m_currentTransform; - const AZ::Vector3 maxScale = AZ::Vector3(worldFromLocalNormalized.ExtractScale().GetMaxElement()); + const float uniformScale = worldFromLocalNormalized.ExtractUniformScale(); const AZ::Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse(); - const AZ::Vector3 localPoint = localFromWorldNormalized.TransformPoint(point) * maxScale.GetReciprocal(); + const AZ::Vector3 localPoint = localFromWorldNormalized.TransformPoint(point) / uniformScale; const auto splineQueryResult = m_spline->GetNearestAddressPosition(localPoint); const float variableRadius = m_variableRadius.GetElementInterpolated(splineQueryResult.m_splineAddress, Lerpf); - return powf((sqrtf(splineQueryResult.m_distanceSq) - (m_radius + variableRadius)) * maxScale.GetMaxElement(), 2.0f); + return powf((sqrtf(splineQueryResult.m_distanceSq) - (m_radius + variableRadius)) * uniformScale, 2.0f); } bool TubeShape::IntersectRay(const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) { AZ::Transform transformUniformScale = m_currentTransform; - const float maxScale = transformUniformScale.ExtractScale().GetMaxElement(); - transformUniformScale *= AZ::Transform::CreateScale(AZ::Vector3(maxScale)); + transformUniformScale.SetUniformScale(transformUniformScale.GetUniformScale()); const auto splineQueryResult = IntersectSpline(transformUniformScale, src, dir, *m_spline); const float variableRadius = m_variableRadius.GetElementInterpolated( splineQueryResult.m_splineAddress, Lerpf); const float totalRadius = m_radius + variableRadius; - distance = (splineQueryResult.m_rayDistance - totalRadius) * m_currentTransform.GetScale().GetMaxElement(); + distance = (splineQueryResult.m_rayDistance - totalRadius) * m_currentTransform.GetUniformScale(); return static_cast(sqrtf(splineQueryResult.m_distanceSq)) < totalRadius; } diff --git a/Gems/LmbrCentral/Code/Tests/BoxShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/BoxShapeTest.cpp index fd1e4ae4f1..5df486d9a2 100644 --- a/Gems/LmbrCentral/Code/Tests/BoxShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/BoxShapeTest.cpp @@ -262,7 +262,7 @@ namespace UnitTest AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(0.0f, 0.0f, 5.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f)), + AZ::Transform::CreateUniformScale(3.0f), AZ::Vector3(2.0f, 4.0f, 1.0f), entity); bool rayHit = false; @@ -295,7 +295,7 @@ namespace UnitTest { AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateTranslation(AZ::Vector3(2.0f, -5.0f, 3.0f)); - transform.MultiplyByScale(AZ::Vector3(0.5f)); + transform.MultiplyByUniformScale(0.5f); const AZ::Vector3 dimensions(2.2f, 1.8f, 0.4f); const AZ::Vector3 nonUniformScale(0.2f, 2.6f, 1.2f); CreateBoxWithNonUniformScale(transform, dimensions, nonUniformScale, entity); @@ -340,7 +340,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.50f, 0.10f, 0.02f, 0.86f), AZ::Vector3(4.0f, 1.0f, -2.0f)); - transform.MultiplyByScale(AZ::Vector3(1.5f)); + transform.MultiplyByUniformScale(1.5f); const AZ::Vector3 dimensions(1.2f, 0.7f, 2.1f); const AZ::Vector3 nonUniformScale(0.8f, 0.6f, 0.7f); CreateBoxWithNonUniformScale(transform, dimensions, nonUniformScale, entity); @@ -433,7 +433,7 @@ namespace UnitTest AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3::CreateZero()) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f)), + AZ::Transform::CreateUniformScale(3.0f), AZ::Vector3(2.0f, 4.0f, 1.0f), entity); AZ::Aabb aabb; @@ -483,7 +483,7 @@ namespace UnitTest AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::QuarterPi) * AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(9.0f, 11.0f, 13.0f)); - transformIn.MultiplyByScale(AZ::Vector3(3.0f)); + transformIn.MultiplyByUniformScale(3.0f); CreateBox(transformIn, AZ::Vector3(1.5f, 3.5f, 5.5f), entity); AZ::Transform transformOut; @@ -500,7 +500,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.62f, 0.62f, 0.14f, 0.46f), AZ::Vector3(0.8f, -1.2f, 2.7f)); - transformIn.MultiplyByScale(AZ::Vector3(2.0f)); + transformIn.MultiplyByUniformScale(2.0f); const AZ::Vector3 nonUniformScale(1.5f, 2.0f, 0.4f); const AZ::Vector3 boxDimensions(2.0f, 1.7f, 0.5f); CreateBoxWithNonUniformScale(transformIn, nonUniformScale, boxDimensions, entity); @@ -531,7 +531,7 @@ namespace UnitTest AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), AZ::Constants::QuarterPi), AZ::Vector3(23.0f, 12.0f, 40.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f)), + AZ::Transform::CreateUniformScale(3.0f), AZ::Vector3(2.0f, 6.0f, 3.5f), entity); // test some pairs of nearby points which should be just either side of the surface of the box @@ -551,7 +551,7 @@ namespace UnitTest AZ::Transform::CreateTranslation(AZ::Vector3(23.0f, 12.0f, 40.0f)) * AZ::Transform::CreateRotationX(-AZ::Constants::QuarterPi) * AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), AZ::Vector3(4.0f, 7.0f, 3.5f), entity); // test some pairs of nearby points which should be just either side of the surface of the box @@ -588,14 +588,14 @@ namespace UnitTest CreateBox( AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 37.0f, 32.0f)) * AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f, 1.0f, 1.0f)), - AZ::Vector3(4.0f, 2.0f, 10.0f), entity); + AZ::Transform::CreateUniformScale(2.0f), + AZ::Vector3(6.0f, 1.0f, 5.0f), entity); float distance; LmbrCentral::ShapeComponentRequestsBus::EventResult( - distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(3.6356f, 30.636f, 40.0f)); + distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(4.0f, 33.5f, 38.0f)); - EXPECT_NEAR(distance, 3.0f, 1e-2f); + EXPECT_NEAR(distance, 1.45f, 1e-2f); } // distance scaled @@ -606,14 +606,14 @@ namespace UnitTest AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 37.0f, 32.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::HalfPi) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f, 1.0f, 1.0f)), - AZ::Vector3(4.0f, 2.0f, 10.0f), entity); + AZ::Transform::CreateUniformScale(0.5f), + AZ::Vector3(24.0f, 4.0f, 20.0f), entity); float distance; LmbrCentral::ShapeComponentRequestsBus::EventResult( distance, entity.GetId(), &LmbrCentral::ShapeComponentRequests::DistanceFromPoint, AZ::Vector3(10.0f, 37.0f, 48.0f)); - EXPECT_NEAR(distance, 13.0f, 1e-2f); + EXPECT_NEAR(distance, 15.0f, 1e-2f); } TEST_F(BoxShapeTest, DistanceFromPointNonUniformScale) @@ -621,7 +621,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationY(AZ::DegToRad(30.0f)), AZ::Vector3(3.0f, 4.0f, 5.0f)); - transform.MultiplyByScale(AZ::Vector3(2.0f)); + transform.MultiplyByUniformScale(2.0f); const AZ::Vector3 dimensions(2.0f, 3.0f, 1.5f); const AZ::Vector3 nonUniformScale(1.4f, 2.2f, 0.8f); CreateBoxWithNonUniformScale(transform, nonUniformScale, dimensions, entity); @@ -638,7 +638,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.70f, 0.10f, 0.34f, 0.62f), AZ::Vector3(3.0f, -1.0f, 2.0f)); - transform.MultiplyByScale(AZ::Vector3(2.0f)); + transform.MultiplyByUniformScale(2.0f); const AZ::Vector3 dimensions(1.2f, 0.8f, 1.7f); const AZ::Vector3 nonUniformScale(2.4f, 1.3f, 1.8f); CreateBoxWithNonUniformScale(transform, nonUniformScale, dimensions, entity); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp index f082ef74f9..44fe70af80 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp @@ -217,6 +217,9 @@ protected: const char* GetAbsoluteDevGameFolderPath() override { return ""; } const char* GetAbsoluteDevRootFolderPath() override { return ""; } bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) { return true; } + bool GenerateRelativeSourcePath( + [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, + [[maybe_unused]] AZStd::string& watchFolder) { return true; } bool GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) { return true; } bool GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetType& assetType, [[maybe_unused]] const AZStd::string& platformName, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& rootFilePath) { return true; } bool GetSourceInfoBySourcePath([[maybe_unused]] const char* sourcePath, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& watchFolder) { return true; } diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp index 58e7e9e093..470a2abff6 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,12 @@ class LevelBuilderTest protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp index b8911738f7..99213484fc 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,12 @@ namespace UnitTest { void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp index 20dd3db446..49fc06c160 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp @@ -12,6 +12,7 @@ #include "LmbrCentral_precompiled.h" #include +#include #include #include #include @@ -22,6 +23,12 @@ class SeedBuilderTests { void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Gems/LmbrCentral/Code/Tests/CapsuleShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/CapsuleShapeTest.cpp index 9b57cd46c7..3274585e63 100644 --- a/Gems/LmbrCentral/Code/Tests/CapsuleShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/CapsuleShapeTest.cpp @@ -144,7 +144,7 @@ namespace UnitTest CreateCapsule( AZ::Transform::CreateTranslation(AZ::Vector3(-4.0f, -12.0f, -3.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * - AZ::Transform::CreateScale(AZ::Vector3(6.0f)), + AZ::Transform::CreateUniformScale(6.0f), 0.25f, 1.5f, entity); bool rayHit = false; @@ -208,7 +208,7 @@ namespace UnitTest TEST_F(CapsuleShapeTest, GetAabb3) { AZ::Entity entity; - CreateCapsule(AZ::Transform::CreateScale(AZ::Vector3(3.5f)), 2.0f, 4.0f, entity); + CreateCapsule(AZ::Transform::CreateUniformScale(3.5f), 2.0f, 4.0f, entity); AZ::Aabb aabb; LmbrCentral::ShapeComponentRequestsBus::EventResult( @@ -224,7 +224,7 @@ namespace UnitTest AZ::Entity entity; CreateCapsule( AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 20.0f, 0.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f)), 1.0f, 5.0f, entity); + AZ::Transform::CreateUniformScale(2.5f), 1.0f, 5.0f, entity); AZ::Aabb aabb; LmbrCentral::ShapeComponentRequestsBus::EventResult( @@ -255,7 +255,7 @@ namespace UnitTest AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) * AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(-10.0f, -10.0f, 0.0f)); - transformIn.MultiplyByScale(AZ::Vector3(3.0f)); + transformIn.MultiplyByUniformScale(3.0f); CreateCapsule(transformIn, 5.0f, 2.0f, entity); AZ::Transform transformOut; @@ -273,7 +273,7 @@ namespace UnitTest AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) * AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), AZ::Vector3(-10.0f, -10.0f, 0.0f)); - transformIn.MultiplyByScale(AZ::Vector3(3.0f)); + transformIn.MultiplyByUniformScale(3.0f); CreateCapsule(transformIn, 2.0f, 5.0f, entity); AZ::Transform transformOut; @@ -291,7 +291,7 @@ namespace UnitTest AZ::Entity entity; CreateCapsule( AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f, 1.0f, 1.0f)), // test max scale + AZ::Transform::CreateUniformScale(2.5f), 0.5f, 2.0f, entity); bool inside; @@ -309,7 +309,7 @@ namespace UnitTest AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(0.5f)), + AZ::Transform::CreateUniformScale(0.5f), 0.5f, 2.0f, entity); bool inside; @@ -327,7 +327,7 @@ namespace UnitTest AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), 0.5f, 4.0f, entity); float distance; @@ -345,7 +345,7 @@ namespace UnitTest AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), 0.5f, 4.0f, entity); float distance; diff --git a/Gems/LmbrCentral/Code/Tests/CylinderShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/CylinderShapeTest.cpp index 893ed3fcac..115abcf5ff 100644 --- a/Gems/LmbrCentral/Code/Tests/CylinderShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/CylinderShapeTest.cpp @@ -138,7 +138,7 @@ namespace UnitTest { AZ::Transform::CreateTranslation(AZ::Vector3(-14.0f, -14.0f, -1.0f)) * AZ::Transform::CreateRotationY(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationZ(AZ::Constants::HalfPi) * - AZ::Transform::CreateScale(AZ::Vector3(4.0f)), + AZ::Transform::CreateUniformScale(4.0f), 1.0f, 1.25f }, // Result: hit, distance, epsilon { true, 2.5f, 1e-2f } @@ -203,7 +203,7 @@ namespace UnitTest // Test case 2 { // Cylinder: transform, radius, height { AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, -10.0f, 10.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(3.5f)), + AZ::Transform::CreateUniformScale(3.5f), 1.0f, 5.0f }, // AABB: min, max { AZ::Vector3(-13.5f, -13.5f, 1.25f), AZ::Vector3(-6.5f, -6.5f, 18.75f) } }, @@ -236,7 +236,7 @@ namespace UnitTest { AZ::Vector3(-5.0f, -5.0f, -0.5f), AZ::Vector3(5.0f, 5.0f, 0.5f) } }, // Test case 1 { // Cylinder: transform, radius, height - { AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, -10.0f, 10.0f)) * AZ::Transform::CreateScale(AZ::Vector3(3.5f)), + { AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, -10.0f, 10.0f)) * AZ::Transform::CreateUniformScale(3.5f), 5.0f, 5.0f }, // Local bounds: min, max { AZ::Vector3(-5.0f, -5.0f, -2.5f), AZ::Vector3(5.0f, 5.0f, 2.5f) } }, @@ -264,7 +264,7 @@ namespace UnitTest // Test case 0 { // Cylinder: transform, radius, height {AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f, 1.0f, 1.0f)), // test max scale + AZ::Transform::CreateUniformScale(2.5f), 0.5f, 2.0f}, // Point AZ::Vector3(27.0f, 28.5f, 40.0f), @@ -275,7 +275,7 @@ namespace UnitTest {AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(0.5f)), + AZ::Transform::CreateUniformScale(0.5f), 0.5f, 2.0f}, // Point AZ::Vector3(27.0f, 28.155f, 37.82f), @@ -316,7 +316,7 @@ namespace UnitTest { AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), 0.5f, 4.0f }, // Point AZ::Vector3(27.0f, 28.0f, 41.0f), @@ -327,7 +327,7 @@ namespace UnitTest { AZ::Transform::CreateTranslation(AZ::Vector3(27.0f, 28.0f, 38.0f)) * AZ::Transform::CreateRotationX(AZ::Constants::HalfPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), 0.5f, 4.0f }, // Point AZ::Vector3(22.757f, 32.243f, 38.0f), diff --git a/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp index 4a39cd966e..c3683cc33b 100644 --- a/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp @@ -307,7 +307,7 @@ namespace UnitTest AZ::Entity entity; CreateDisk( AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 200.0f, 300.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f)), + AZ::Transform::CreateUniformScale(2.5f), 0.5f, entity); AZ::Aabb aabb; diff --git a/Gems/LmbrCentral/Code/Tests/PolygonPrismShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/PolygonPrismShapeTest.cpp index 46c3da90ab..f12ca69425 100644 --- a/Gems/LmbrCentral/Code/Tests/PolygonPrismShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/PolygonPrismShapeTest.cpp @@ -329,7 +329,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationY(AZ::DegToRad(45.0f)), AZ::Vector3(3.0f, 4.0f, 5.0f)); - transform.MultiplyByScale(AZ::Vector3(1.5f, 1.5f, 1.5f)); + transform.MultiplyByUniformScale(1.5f); const float height = 1.2f; const AZ::Vector3 nonUniformScale(2.0f, 1.2f, 0.5f); const AZStd::vector vertices = @@ -447,7 +447,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationY(AZ::DegToRad(45.0f)), AZ::Vector3(3.0f, 4.0f, 5.0f)); - transform.MultiplyByScale(AZ::Vector3(1.5f, 1.5f, 1.5f)); + transform.MultiplyByUniformScale(1.5f); const float height = 1.2f; const AZ::Vector3 nonUniformScale(2.0f, 1.2f, 0.5f); const AZStd::vector vertices = @@ -608,7 +608,7 @@ namespace UnitTest AZ::Entity entity; CreatePolygonPrism( AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 40.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f)), 2.0f, + AZ::Transform::CreateUniformScale(3.0f), 2.0f, AZStd::vector( { AZ::Vector2(-2.0f, -2.0f), @@ -669,7 +669,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationY(AZ::DegToRad(60.0f)), AZ::Vector3(1.0f, 2.5f, -1.0f)); - transform.MultiplyByScale(AZ::Vector3(2.0f, 2.0f, 2.0f)); + transform.MultiplyByUniformScale(2.0f); const float height = 1.5f; const AZ::Vector3 nonUniformScale(0.5f, 1.5f, 2.0f); @@ -772,7 +772,7 @@ namespace UnitTest AZ::Entity entity; CreatePolygonPrism( AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 40.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(3.0f)), 1.5f, + AZ::Transform::CreateUniformScale(3.0f), 1.5f, AZStd::vector( { AZ::Vector2(-2.0f, -2.0f), @@ -795,7 +795,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationX(AZ::DegToRad(30.0f)), AZ::Vector3(2.0f, -5.0f, 3.0f)); - transform.MultiplyByScale(AZ::Vector3(2.0f, 2.0f, 2.0f)); + transform.MultiplyByUniformScale(2.0f); const float height = 1.2f; const AZ::Vector3 nonUniformScale(1.5f, 0.8f, 2.0f); const AZStd::vector vertices = diff --git a/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp index d70a9344a4..4b6eae4bb8 100644 --- a/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp @@ -188,7 +188,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transformIn = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.46f, 0.34f, 0.02f, 0.82f), AZ::Vector3(1.7f, -0.4f, 2.3f)); - transformIn.MultiplyByScale(AZ::Vector3(2.2f)); + transformIn.MultiplyByUniformScale(2.2f); const AZ::Vector3 nonUniformScale(0.8f, 0.6f, 1.3f); const float width = 0.7f; const float height = 1.3f; @@ -327,7 +327,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.64f, 0.16f, 0.68f, 0.32f), AZ::Vector3(0.4f, -2.3f, -0.9f)); - transform.MultiplyByScale(AZ::Vector3(1.3f)); + transform.MultiplyByUniformScale(1.3f); const AZ::Vector3 nonUniformScale(0.7f, 0.5f, 1.3f); const float width = 0.9f; const float height = 1.3f; @@ -384,7 +384,7 @@ namespace UnitTest AZ::Entity entity; CreateQuad( AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 200.0f, 300.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f)), + AZ::Transform::CreateUniformScale(2.5f), 1.0f, 2.0f, entity); AZ::Aabb aabb; @@ -425,7 +425,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.44f, 0.24f, 0.48f, 0.72f), AZ::Vector3(3.4f, 1.2f, -2.8f)); - transform.MultiplyByScale(AZ::Vector3(1.5f)); + transform.MultiplyByUniformScale(1.5f); const AZ::Vector3 nonUniformScale(1.2f, 1.1f, 0.8f); const float width = 1.2f; const float height = 1.7f; @@ -518,7 +518,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.24f, 0.72f, 0.44f, 0.48f), AZ::Vector3(2.7f, 2.3f, -1.8f)); - transform.MultiplyByScale(AZ::Vector3(1.2f)); + transform.MultiplyByUniformScale(1.2f); const AZ::Vector3 nonUniformScale(0.4f, 2.2f, 1.3f); const float width = 1.6f; const float height = 0.7f; @@ -546,7 +546,7 @@ namespace UnitTest AZ::Entity entity; AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion(0.70f, 0.10f, 0.34f, 0.62f), AZ::Vector3(3.0f, -1.0f, 2.0f)); - transform.MultiplyByScale(AZ::Vector3(2.0f)); + transform.MultiplyByUniformScale(2.0f); const AZ::Vector3 nonUniformScale(2.4f, 1.3f, 1.8f); const float width = 0.8f; const float height = 1.4f; diff --git a/Gems/LmbrCentral/Code/Tests/SphereShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/SphereShapeTest.cpp index 563bb16caa..b5e45f2cda 100644 --- a/Gems/LmbrCentral/Code/Tests/SphereShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/SphereShapeTest.cpp @@ -179,7 +179,7 @@ namespace UnitTest AZ::Entity entity; CreateSphere( AZ::Transform::CreateTranslation(AZ::Vector3(-8.0f, -15.0f, 5.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(5.0f)), + AZ::Transform::CreateUniformScale(5.0f), 0.25f, entity); bool rayHit = false; @@ -240,7 +240,7 @@ namespace UnitTest AZ::Entity entity; CreateSphere( AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 200.0f, 300.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f)), + AZ::Transform::CreateUniformScale(2.5f), 0.5f, entity); AZ::Aabb aabb; @@ -269,7 +269,7 @@ namespace UnitTest TEST_F(SphereShapeTest, GetTransformAndLocalBounds2) { AZ::Entity entity; - AZ::Transform transformIn = AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 200.0f, 300.0f)) * AZ::Transform::CreateScale(AZ::Vector3(2.5f)); + AZ::Transform transformIn = AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 200.0f, 300.0f)) * AZ::Transform::CreateUniformScale(2.5f); CreateSphere(transformIn, 2.0f, entity); AZ::Transform transformOut; @@ -287,7 +287,7 @@ namespace UnitTest AZ::Entity entity; CreateSphere( AZ::Transform::CreateTranslation(AZ::Vector3(-30.0f, -30.0f, 22.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), 1.2f, entity); bool inside; @@ -303,7 +303,7 @@ namespace UnitTest AZ::Entity entity; CreateSphere( AZ::Transform::CreateTranslation(AZ::Vector3(-30.0f, -30.0f, 22.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(1.5f)), + AZ::Transform::CreateUniformScale(1.5f), 1.6f, entity); bool inside; @@ -319,7 +319,7 @@ namespace UnitTest AZ::Entity entity; CreateSphere( AZ::Transform::CreateTranslation(AZ::Vector3(19.0f, 34.0f, 37.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f)), + AZ::Transform::CreateUniformScale(2.0f), 1.0f, entity); float distance; @@ -335,7 +335,7 @@ namespace UnitTest AZ::Entity entity; CreateSphere( AZ::Transform::CreateTranslation(AZ::Vector3(19.0f, 34.0f, 37.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(0.5f)), + AZ::Transform::CreateUniformScale(0.5f), 1.0f, entity); float distance; diff --git a/Gems/LmbrCentral/Code/Tests/TubeShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/TubeShapeTest.cpp index 65388d1fe5..e9d9c40433 100644 --- a/Gems/LmbrCentral/Code/Tests/TubeShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/TubeShapeTest.cpp @@ -139,7 +139,7 @@ namespace UnitTest AZ::Entity entity; CreateTube( AZ::Transform::CreateTranslation(AZ::Vector3(-40.0f, 6.0f, 1.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.5f, 1.0f, 1.0f)), // test max scale + AZ::Transform::CreateUniformScale(2.5f), 1.0f, entity); @@ -232,7 +232,7 @@ namespace UnitTest TEST_F(TubeShapeTest, GetAabb4) { AZ::Entity entity; - CreateTube(AZ::Transform::CreateScale(AZ::Vector3(1.0f, 1.0f, 2.0f)), 1.0f, entity); + CreateTube(AZ::Transform::CreateUniformScale(2.0f), 1.0f, entity); // set variable radius LmbrCentral::TubeShapeComponentRequestsBus::Event( @@ -254,7 +254,7 @@ namespace UnitTest AZ::Entity entity; CreateTube( AZ::Transform::CreateTranslation(AZ::Vector3(37.0f, 36.0f, 32.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(1.0f, 2.0f, 1.0f)), 1.5f, entity); + AZ::Transform::CreateUniformScale(2.0f), 1.5f, entity); // set variable radius LmbrCentral::TubeShapeComponentRequestsBus::Event( @@ -277,7 +277,7 @@ namespace UnitTest AZ::Transform::CreateTranslation(AZ::Vector3(37.0f, 36.0f, 32.0f)) * AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi) * AZ::Transform::CreateRotationY(AZ::Constants::QuarterPi) * - AZ::Transform::CreateScale(AZ::Vector3(0.8f, 1.5f, 1.5f)), 1.5f, entity); + AZ::Transform::CreateUniformScale(1.5f), 1.5f, entity); // set variable radius LmbrCentral::TubeShapeComponentRequestsBus::Event( @@ -302,7 +302,7 @@ namespace UnitTest AZ::Entity entity; CreateTube( AZ::Transform::CreateTranslation(AZ::Vector3(37.0f, 36.0f, 39.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f, 1.5f, 1.5f)), 1.5f, entity); + AZ::Transform::CreateUniformScale(2.0f), 1.5f, entity); LmbrCentral::TubeShapeComponentRequestsBus::Event( entity.GetId(), &LmbrCentral::TubeShapeComponentRequestsBus::Events::SetVariableRadius, 0, 1.0f); @@ -326,7 +326,7 @@ namespace UnitTest AZ::Entity entity; CreateTube( AZ::Transform::CreateTranslation(AZ::Vector3(37.0f, 36.0f, 39.0f)) * - AZ::Transform::CreateScale(AZ::Vector3(2.0f, 1.5f, 1.5f)), 1.5f, entity); + AZ::Transform::CreateUniformScale(2.0f), 1.5f, entity); LmbrCentral::TubeShapeComponentRequestsBus::Event( entity.GetId(), &LmbrCentral::TubeShapeComponentRequestsBus::Events::SetVariableRadius, 0, 1.0f); diff --git a/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo b/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo +++ b/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShine/Code/CMakeLists.txt b/Gems/LyShine/Code/CMakeLists.txt index 732bd1cfd4..796cb22292 100644 --- a/Gems/LyShine/Code/CMakeLists.txt +++ b/Gems/LyShine/Code/CMakeLists.txt @@ -86,7 +86,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::LyShine.Static Legacy::CryCommon Gem::LmbrCentral - Gem::TextureAtlas + Gem::TextureAtlas.Editor Gem::AtomToolsFramework.Static Gem::AtomToolsFramework.Editor ${additional_dependencies} @@ -118,10 +118,10 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AssetBuilderSDK Gem::LyShine.Editor.Static Gem::LmbrCentral - Gem::TextureAtlas + Gem::TextureAtlas.Editor RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor - Gem::TextureAtlas + Gem::TextureAtlas.Editor ) endif() @@ -176,7 +176,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) Legacy::CryCommon AZ::AssetBuilderSDK Gem::LmbrCentral - Gem::TextureAtlas + Gem::TextureAtlas.Editor Gem::LyShine.Editor.Static ) ly_add_googletest( diff --git a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp index 428554a461..2195d2c9d4 100644 --- a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp +++ b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp @@ -30,6 +30,11 @@ namespace ViewportHelpers return isControlledByParent; } + float GetDpiScaledSize(float size) + { + return size * ViewportIcon::GetDpiScaleFactor(); + } + bool IsHorizontallyFit(const AZ::Entity* element) { bool isHorizontallyFit = false; @@ -332,11 +337,12 @@ namespace ViewportHelpers AZ::Vector2 pivotPos; EBUS_EVENT_ID_RESULT(pivotPos, element->GetId(), UiTransformBus, GetViewportSpacePivot); - AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - ((viewportPivot->GetSize().GetY() * 0.5f) + 4.0f)); + float offset = (viewportPivot->GetSize().GetY() * 0.5f) + (GetDpiScaledSize(4.0f)); + AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - offset); draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, 16.0f, 1.0f); + draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, GetDpiScaledSize(16.0f), 1.0f); } } @@ -350,6 +356,6 @@ namespace ViewportHelpers draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(textLabel.c_str(), textPos, 16.0f, 1.0f); + draw2d.DrawText(textLabel.c_str(), textPos, GetDpiScaledSize(16.0f), 1.0f); } } // namespace ViewportHelpers diff --git a/Gems/LyShine/Code/Editor/ViewportIcon.cpp b/Gems/LyShine/Code/Editor/ViewportIcon.cpp index b1866efb00..4be06b2543 100644 --- a/Gems/LyShine/Code/Editor/ViewportIcon.cpp +++ b/Gems/LyShine/Code/Editor/ViewportIcon.cpp @@ -303,7 +303,7 @@ void ViewportIcon::DrawDistanceLine(Draw2dHelper& draw2d, AZ::Vector2 start, AZ: draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(rotation); - draw2d.DrawText(textBuf, textPos, 16.0f, 1.0f); + draw2d.DrawText(textBuf, textPos, 16.0f * ViewportIcon::GetDpiScaleFactor(), 1.0f); } void ViewportIcon::DrawAnchorLinesSplit(Draw2dHelper& draw2d, AZ::Vector2 anchorPos1, AZ::Vector2 anchorPos2, diff --git a/Gems/LyShine/Code/Include/LyShine/Draw2d.h b/Gems/LyShine/Code/Include/LyShine/Draw2d.h index 8270461e73..b83ec4794b 100644 --- a/Gems/LyShine/Code/Include/LyShine/Draw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/Draw2d.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -256,9 +257,8 @@ protected: // types and constants const Draw2dShaderData& shaderData, AZ::RPI::ViewportContextPtr viewportContext) const override; - STextDrawContext m_fontContext; - IFFont* m_font; - AZ::Vector2 m_position; + AzFramework::TextDrawParameters m_drawParameters; + AzFramework::FontId m_fontId; std::string m_string; }; @@ -288,7 +288,7 @@ protected: // member functions void RotatePointsAboutPivot(AZ::Vector2* points, int numPoints, AZ::Vector2 pivot, float angle) const; //! Helper function to render a text string - void DrawTextInternal(const char* textString, IFFont* font, unsigned int effectIndex, + void DrawTextInternal(const char* textString, AzFramework::FontId fontId, unsigned int effectIndex, AZ::Vector2 position, float pointSize, AZ::Color color, float rotation, HAlign horizontalAlignment, VAlign verticalAlignment, int baseState); @@ -298,6 +298,9 @@ protected: // member functions //! Draw or defer a line void DrawOrDeferLine(const DeferredLine* line); + //! Draw or defer a text string + void DrawOrDeferTextString(const DeferredText* text); + //! Draw or defer a rect outline void DrawOrDeferRectOutline(const DeferredRectOutline* outlineRect); @@ -491,7 +494,7 @@ public: // member functions void SetImageBaseState(int state) { m_imageOptions.baseState = state; } //! Set the text font. - void SetTextFont(IFFont* font) { m_textOptions.font = font; } + void SetTextFont(AZStd::string_view fontName) { m_textOptions.fontName = fontName; } //! Set the text font effect index. void SetTextEffectIndex(unsigned int effectIndex) { m_textOptions.effectIndex = effectIndex; } diff --git a/Gems/LyShine/Code/Source/Draw2d.cpp b/Gems/LyShine/Code/Source/Draw2d.cpp index 1a639ea299..6feb47419d 100644 --- a/Gems/LyShine/Code/Source/Draw2d.cpp +++ b/Gems/LyShine/Code/Source/Draw2d.cpp @@ -11,11 +11,13 @@ */ #include "LyShine_precompiled.h" #include "IFont.h" +#include // for SVF_P3F_C4B_T2F which will be removed in a coming PR #include #include #include +#include #include #include @@ -55,7 +57,7 @@ CDraw2d::CDraw2d(AZ::RPI::ViewportContextPtr viewportContext) m_defaultImageOptions.pixelRounding = Rounding::Nearest; m_defaultImageOptions.baseState = g_defaultBaseState; - m_defaultTextOptions.font = (gEnv && gEnv->pCryFont != nullptr) ? gEnv->pCryFont->GetFont("default") : nullptr; + m_defaultTextOptions.fontName = "default"; m_defaultTextOptions.effectIndex = 0; m_defaultTextOptions.color.Set(1.0f, 1.0f, 1.0f); m_defaultTextOptions.horizontalAlignment = HAlign::Left; @@ -283,13 +285,20 @@ void CDraw2d::DrawText(const char* textString, AZ::Vector2 position, float point { TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions; + AzFramework::FontId fontId = AzFramework::InvalidFontId; + AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface::Get(); + if (fontQueryInterface) + { + fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName); + } + // render the drop shadow, if needed if ((actualTextOptions->dropShadowColor.GetA() > 0.0f) && (actualTextOptions->dropShadowOffset.GetX() || actualTextOptions->dropShadowOffset.GetY())) { // calculate the drop shadow pos and render it AZ::Vector2 dropShadowPosition(position + actualTextOptions->dropShadowOffset); - DrawTextInternal(textString, actualTextOptions->font, actualTextOptions->effectIndex, + DrawTextInternal(textString, fontId, actualTextOptions->effectIndex, dropShadowPosition, pointSize, actualTextOptions->dropShadowColor, actualTextOptions->rotation, actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment, @@ -298,7 +307,7 @@ void CDraw2d::DrawText(const char* textString, AZ::Vector2 position, float point // draw the text string AZ::Color textColor = AZ::Color::CreateFromVector3AndFloat(actualTextOptions->color, opacity); - DrawTextInternal(textString, actualTextOptions->font, actualTextOptions->effectIndex, + DrawTextInternal(textString, fontId, actualTextOptions->effectIndex, position, pointSize, textColor, actualTextOptions->rotation, actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment, @@ -398,20 +407,35 @@ void CDraw2d::DrawRectOutlineTextured(AZ::Data::Instance image, //////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Vector2 CDraw2d::GetTextSize(const char* textString, float pointSize, TextOptions* textOptions) { - TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions; - - if (!actualTextOptions->font) + AzFramework::FontDrawInterface* fontDrawInterface = nullptr; + AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface::Get(); + if (fontQueryInterface) + { + TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions; + AzFramework::FontId fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName); + fontDrawInterface = fontQueryInterface->GetFontDrawInterface(fontId); + } + if (!fontDrawInterface) { return AZ::Vector2(0.0f, 0.0f); } - STextDrawContext fontContext; - fontContext.SetEffect(actualTextOptions->effectIndex); - fontContext.SetSizeIn800x600(false); - fontContext.SetSize(vector2f(pointSize, pointSize)); + // Set up draw parameters + AzFramework::TextDrawParameters drawParams; + drawParams.m_drawViewportId = GetViewportContext()->GetId(); + drawParams.m_position = AZ::Vector3(0.0f, 0.0f, 1.0f); + drawParams.m_effectIndex = 0; + drawParams.m_textSizeFactor = pointSize; + drawParams.m_scale = AZ::Vector2(1.0f, 1.0f); + drawParams.m_lineSpacing = 1.0f; + drawParams.m_monospace = false; + drawParams.m_depthTest = false; + drawParams.m_virtual800x600ScreenSize = false; + drawParams.m_scaleWithWindow = false; + drawParams.m_multiline = true; - Vec2 textSize = actualTextOptions->font->GetTextSize(textString, true, fontContext); - return AZ::Vector2(textSize.x, textSize.y); + AZ::Vector2 textSize = fontDrawInterface->GetTextSize(drawParams, textString); + return textSize; } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -559,100 +583,89 @@ void CDraw2d::RotatePointsAboutPivot(AZ::Vector2* points, [[maybe_unused]] int n } //////////////////////////////////////////////////////////////////////////////////////////////////// -void CDraw2d::DrawTextInternal(const char* textString, IFFont* font, unsigned int effectIndex, +void CDraw2d::DrawTextInternal(const char* textString, AzFramework::FontId fontId, unsigned int effectIndex, AZ::Vector2 position, float pointSize, AZ::Color color, float rotation, - HAlign horizontalAlignment, VAlign verticalAlignment, int baseState) + HAlign horizontalAlignment, VAlign verticalAlignment, [[maybe_unused]] int baseState) { - if (!font) - { - return; - } - - STextDrawContext fontContext; - fontContext.SetEffect(effectIndex); - fontContext.SetSizeIn800x600(false); - fontContext.SetSize(vector2f(pointSize, pointSize)); - fontContext.SetColor(ColorF(color.GetR(), color.GetG(), color.GetB(), color.GetA())); - fontContext.m_baseState = baseState; - fontContext.SetOverrideViewProjMatrices(false); - // FFont.cpp uses the alpha value of the color to decide whether to use the color, if the alpha value is zero // (in a ColorB format) then the color set via SetColor is ignored and it usually ends up drawing with an alpha of 1. // This is not what we want so in this case do not draw at all. - if (!fontContext.IsColorOverridden()) + if (AZ::IsClose(color.GetA(), 0.0f)) { return; } - AZ::Vector2 alignedPosition; - if (horizontalAlignment == HAlign::Left && verticalAlignment == VAlign::Top) + // Convert Draw2d alignment to text alignment + AzFramework::TextHorizontalAlignment hAlignment = AzFramework::TextHorizontalAlignment::Left; + switch (horizontalAlignment) { - alignedPosition = position; + case HAlign::Left: + hAlignment = AzFramework::TextHorizontalAlignment::Left; + break; + case HAlign::Center: + hAlignment = AzFramework::TextHorizontalAlignment::Center; + break; + case HAlign::Right: + hAlignment = AzFramework::TextHorizontalAlignment::Right; + break; + default: + AZ_Assert(false, "Attempting to draw text with unsupported horizontal alignment."); + break; } - else - { - // we align based on the size of the default font effect, because we do not want the - // text to move when the font effect is changed - unsigned int fontEffectIndex = fontContext.m_fxIdx; - fontContext.SetEffect(0); - Vec2 textSize = font->GetTextSize(textString, true, fontContext); - fontContext.SetEffect(fontEffectIndex); - alignedPosition = Align(position, AZ::Vector2(textSize.x, textSize.y), horizontalAlignment, verticalAlignment); + AzFramework::TextVerticalAlignment vAlignment = AzFramework::TextVerticalAlignment::Top; + switch (verticalAlignment) + { + case VAlign::Top: + vAlignment = AzFramework::TextVerticalAlignment::Top; + break; + case VAlign::Center: + vAlignment = AzFramework::TextVerticalAlignment::Center; + break; + case VAlign::Bottom: + vAlignment = AzFramework::TextVerticalAlignment::Bottom; + break; + default: + AZ_Assert(false, "Attempting to draw text with unsupported vertical alignment."); + break; } - int flags = 0; + // Set up draw parameters for font interface + AzFramework::TextDrawParameters drawParams; + drawParams.m_drawViewportId = GetViewportContext()->GetId(); + drawParams.m_position = AZ::Vector3(position.GetX(), position.GetY(), 1.0f); + drawParams.m_color = color; + drawParams.m_effectIndex = effectIndex; + drawParams.m_textSizeFactor = pointSize; + drawParams.m_scale = AZ::Vector2(1.0f, 1.0f); + drawParams.m_lineSpacing = 1.0f; //!< Spacing between new lines, as a percentage of m_scale. + drawParams.m_hAlign = hAlignment; + drawParams.m_vAlign = vAlignment; + drawParams.m_monospace = false; + drawParams.m_depthTest = false; + drawParams.m_virtual800x600ScreenSize = false; + drawParams.m_scaleWithWindow = false; + drawParams.m_multiline = true; + if (rotation != 0.0f) { // rotate around the position (if aligned to center will rotate about center etc) float rotRad = DEG2RAD(rotation); - Vec3 pivot(position.GetX(), position.GetY(), 0.0f); - Matrix34A moveToPivotSpaceMat = Matrix34A::CreateTranslationMat(-pivot); - Matrix34A rotMat = Matrix34A::CreateRotationZ(rotRad); - Matrix34A moveFromPivotSpaceMat = Matrix34A::CreateTranslationMat(pivot); + AZ::Vector3 pivot(position.GetX(), position.GetY(), 0.0f); + AZ::Matrix3x4 moveToPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(-pivot); + AZ::Matrix3x4 rotMat = AZ::Matrix3x4::CreateRotationZ(rotRad); + AZ::Matrix3x4 moveFromPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(pivot); - Matrix34A transform = moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat; - fontContext.SetTransform(transform); - flags |= eDrawText_UseTransform; + drawParams.m_transform = moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat; + drawParams.m_useTransform = true; } - // The font system uses these alignment flags to force text to be in the safe zone - // depending on overscan etc - if (horizontalAlignment == HAlign::Center) - { - flags |= eDrawText_Center; - } - else if (horizontalAlignment == HAlign::Right) - { - flags |= eDrawText_Right; - } - - if (verticalAlignment == VAlign::Center) - { - flags |= eDrawText_CenterV; - } - else if (verticalAlignment == VAlign::Bottom) - { - flags |= eDrawText_Bottom; - } + DeferredText newText; + newText.m_drawParameters = drawParams; + newText.m_fontId = fontId; + newText.m_string = textString; - fontContext.SetFlags(flags); - - if (m_deferCalls) - { - DeferredText* newText = new DeferredText; - - newText->m_fontContext = fontContext; - newText->m_font = font; - newText->m_position = alignedPosition; - newText->m_string = textString; - - m_deferredPrimitives.push_back(newText); - } - else - { - font->DrawString(alignedPosition.GetX(), alignedPosition.GetY(), textString, true, fontContext); - } + DrawOrDeferTextString(&newText); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -685,6 +698,20 @@ void CDraw2d::DrawOrDeferLine(const DeferredLine* line) } } +void CDraw2d::DrawOrDeferTextString(const DeferredText* text) +{ + if (m_deferCalls) + { + DeferredText* newText = new DeferredText; + *newText = *text; + m_deferredPrimitives.push_back(newText); + } + else + { + text->Draw(m_dynamicDraw, m_shaderData, GetViewportContext()); + } +} + void CDraw2d::DrawOrDeferRectOutline(const DeferredRectOutline* rectOutline) { if (m_deferCalls) @@ -919,6 +946,15 @@ void CDraw2d::DeferredText::Draw([[maybe_unused]] AZ::RHI::PtrDrawString(m_position.GetX(), m_position.GetY(), m_string.c_str(), true, m_fontContext); + AzFramework::FontDrawInterface* fontDrawInterface = nullptr; + AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface::Get(); + if (fontQueryInterface) + { + fontDrawInterface = fontQueryInterface->GetFontDrawInterface(m_fontId); + if (fontDrawInterface) + { + fontDrawInterface->DrawScreenAlignedText2d(m_drawParameters, m_string.c_str()); + } + } } diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index 679cae3421..fb6dcb2628 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -454,7 +454,6 @@ void CLyShine::Render() GetUiRenderer()->EndUiFrameRender(); -#ifdef LYSHINE_ATOM_TODO // convert debug info to Atom #ifndef _RELEASE if (CV_ui_DisplayElemBounds) { @@ -474,7 +473,6 @@ void CLyShine::Render() m_uiCanvasManager->DebugDisplayDrawCallData(); } #endif -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/LyShine/Code/Source/LyShineDebug.cpp b/Gems/LyShine/Code/Source/LyShineDebug.cpp index 76b4030106..44e0e187ec 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.cpp +++ b/Gems/LyShine/Code/Source/LyShineDebug.cpp @@ -12,6 +12,7 @@ #include "LyShine_precompiled.h" #include "LyShineDebug.h" #include "IConsole.h" +#include "IRenderer.h" #include #include @@ -392,15 +393,15 @@ static void DebugDrawColoredBox(AZ::Vector2 pos, AZ::Vector2 size, AZ::Color col //////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(_RELEASE) -static void DebugDrawStringWithSizeBox(IFFont* font, unsigned int effectIndex, const char* sizeString, +static void DebugDrawStringWithSizeBox(AZStd::string_view font, unsigned int effectIndex, const char* sizeString, const char* testString, AZ::Vector2 pos, float spacing, float size) { CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); - if (font) + if (!font.empty()) { - textOptions.font = font; + textOptions.fontName = font; } textOptions.effectIndex = effectIndex; @@ -427,7 +428,7 @@ static void DebugDrawStringWithSizeBox(IFFont* font, unsigned int effectIndex, c //////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(_RELEASE) -static void DebugDraw2dFontSizes(IFFont* font, unsigned int effectIndex, const char* fontName) +static void DebugDraw2dFontSizes(AZStd::string_view font, unsigned int effectIndex) { CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); @@ -436,7 +437,7 @@ static void DebugDraw2dFontSizes(IFFont* font, unsigned int effectIndex, const c float xSpacing = 20.0f; char buffer[32]; - sprintf_s(buffer, "Font = %s, effect = %d", fontName, effectIndex); + sprintf_s(buffer, "Font = %s, effect = %d", font.data(), effectIndex); draw2d->DrawText(buffer, AZ::Vector2(xOffset, yOffset), 32); yOffset += 40.0f; draw2d->DrawText("NOTE: if the effect includes a drop shadow baked into font then the pixel size", @@ -1441,10 +1442,10 @@ void LyShineDebug::RenderDebug() switch (CV_r_DebugUIDraw2dFont) { case 1: // test font sizes (default font, effect 0) - DebugDraw2dFontSizes(0, 0, "default"); + DebugDraw2dFontSizes("default", 0); break; case 2: // test font sizes (default font, effect 1) - DebugDraw2dFontSizes(0, 1, "default"); + DebugDraw2dFontSizes("default", 1); break; case 3: // test font alignment DebugDraw2dFontAlignment(); diff --git a/Gems/LyShine/Code/Source/LyShineDebug.h b/Gems/LyShine/Code/Source/LyShineDebug.h index ed03fd10b2..e50689710a 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.h +++ b/Gems/LyShine/Code/Source/LyShineDebug.h @@ -14,7 +14,9 @@ #ifndef _RELEASE #include -class ITexture; +#include +#include + #endif //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -66,7 +68,7 @@ public: // static member functions struct DebugInfoTextureUsage { - ITexture* m_texture; + AZ::Data::Instance m_texture; bool m_isClampTextureUsage; int m_numCanvasesUsed; int m_numDrawCallsUsed; diff --git a/Gems/LyShine/Code/Source/RenderGraph.cpp b/Gems/LyShine/Code/Source/RenderGraph.cpp index e5ac6c7b8f..d5a1df7b15 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.cpp +++ b/Gems/LyShine/Code/Source/RenderGraph.cpp @@ -18,6 +18,7 @@ #include #ifndef _RELEASE +#include #include #endif @@ -1115,7 +1116,7 @@ namespace LyShine m_wasBuiltThisFrame = false; - AZStd::set uniqueTextures; + AZStd::set> uniqueTextures; // If we are rendering to the render targets this frame then record the stats for doing that if (m_renderToRenderTargetCount < 2) @@ -1144,13 +1145,11 @@ namespace LyShine } //////////////////////////////////////////////////////////////////////////////////////////////////// - void RenderGraph::GetDebugInfoRenderNodeList(const AZStd::vector& renderNodeList, LyShineDebug::DebugInfoRenderGraph& info, AZStd::set& uniqueTextures) const + void RenderGraph::GetDebugInfoRenderNodeList( + const AZStd::vector& renderNodeList, + LyShineDebug::DebugInfoRenderGraph& info, + AZStd::set>& uniqueTextures) const { - AZ_UNUSED(renderNodeList); - AZ_UNUSED(info); - AZ_UNUSED(uniqueTextures); - -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (convert debug info to use Atom) const PrimitiveListRenderNode* prevPrimListNode = nullptr; bool isFirstNode = true; bool wasLastNodeAMask = false; @@ -1235,7 +1234,6 @@ namespace LyShine isFirstNode = false; } -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1290,13 +1288,6 @@ namespace LyShine void* context, const AZStd::string& indent) const { - AZ_UNUSED(renderNodeList); - AZ_UNUSED(fileHandle); - AZ_UNUSED(reportInfo); - AZ_UNUSED(context); - AZ_UNUSED(indent); - -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (convert debug info to use Atom) AZStd::string logLine; bool previousNodeAlreadyCounted = false; @@ -1355,10 +1346,10 @@ namespace LyShine { for (int i = 0; i < prevPrimListNode->GetNumTextures(); ++i) { - ITexture* texture = prevPrimListNode->GetTexture(i); + AZ::Data::Instance texture = prevPrimListNode->GetTexture(i); if (!texture) { - texture = gEnv->pRenderer->GetWhiteTexture(); + texture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); } bool isClampTextureUsage = prevPrimListNode->GetTextureIsClampMode(i); @@ -1405,17 +1396,19 @@ namespace LyShine for (int i = 0; i < primListRenderNode->GetNumTextures(); ++i) { - ITexture* texture = primListRenderNode->GetTexture(i); + AZ::Data::Instance texture = primListRenderNode->GetTexture(i); if (!texture) { - texture = gEnv->pRenderer->GetWhiteTexture(); + texture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); } bool isClampTextureUsage = primListRenderNode->GetTextureIsClampMode(i); LyShineDebug::DebugInfoTextureUsage* matchingTextureUsage = nullptr; // Write line to logfile for this texture - logLine = AZStd::string::format("%s %s\r\n", indent.c_str(), texture->GetName()); + AZStd::string textureName; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, texture->GetAssetId()); + logLine = AZStd::string::format("%s %s\r\n", indent.c_str(), textureName.c_str()); AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size()); // see if texture is in reportInfo @@ -1459,7 +1452,6 @@ namespace LyShine prevPrimListNode = primListRenderNode; } } -#endif } #endif diff --git a/Gems/LyShine/Code/Source/RenderGraph.h b/Gems/LyShine/Code/Source/RenderGraph.h index f9d16cf8b7..2f1586e857 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.h +++ b/Gems/LyShine/Code/Source/RenderGraph.h @@ -13,7 +13,6 @@ #pragma once #include -#include #include #include #include @@ -294,7 +293,10 @@ namespace LyShine void ValidateGraph(); void GetDebugInfoRenderGraph(LyShineDebug::DebugInfoRenderGraph& info) const; - void GetDebugInfoRenderNodeList(const AZStd::vector& renderNodeList, LyShineDebug::DebugInfoRenderGraph& info, AZStd::set& uniqueTextures) const; + void GetDebugInfoRenderNodeList( + const AZStd::vector& renderNodeList, + LyShineDebug::DebugInfoRenderGraph& info, + AZStd::set>& uniqueTextures) const; void DebugReportDrawCalls(AZ::IO::HandleType fileHandle, LyShineDebug::DebugInfoDrawCallReport& reportInfo, void* context) const; void DebugReportDrawCallsRenderNodeList(const AZStd::vector& renderNodeList, AZ::IO::HandleType fileHandle, diff --git a/Gems/LyShine/Code/Source/UiCanvasManager.cpp b/Gems/LyShine/Code/Source/UiCanvasManager.cpp index 4115feaf22..b64a13280f 100644 --- a/Gems/LyShine/Code/Source/UiCanvasManager.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasManager.cpp @@ -1425,7 +1425,8 @@ void UiCanvasManager::DebugReportDrawCalls(const AZStd::string& name) const if (reportTextureUsage.m_numCanvasesUsed > 1 && reportTextureUsage.m_numDrawCallsWhereExceedingMaxTextures) { - AZStd::string textureName = reportTextureUsage.m_texture->GetName(); + AZStd::string textureName; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, reportTextureUsage.m_texture->GetAssetId()); if (textureName.compare(0, fontTexturePrefix.length(), fontTexturePrefix) != 0) { logLine = AZStd::string::format("%s\r\n", textureName.c_str()); @@ -1457,7 +1458,8 @@ void UiCanvasManager::DebugReportDrawCalls(const AZStd::string& name) const reportTextureUsage.m_lastContextUsed == canvas && reportTextureUsage.m_numDrawCallsWhereExceedingMaxTextures) { - AZStd::string textureName = reportTextureUsage.m_texture->GetName(); + AZStd::string textureName; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, reportTextureUsage.m_texture->GetAssetId()); // exclude font textures if (textureName.compare(0, fontTexturePrefix.length(), fontTexturePrefix) != 0) diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 2a2c950e82..57acbed5d5 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -12,6 +12,7 @@ #include "LyShine_precompiled.h" #include "UiRenderer.h" +#include #include #include #include @@ -24,7 +25,7 @@ #include #include -#include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC MEMBER FUNCTIONS @@ -353,7 +354,6 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) { if (recordingOption > 0) { -#ifdef LYSHINE_ATOM_TODO // compute the total area of all the textures, also create a vector that we can sort by area AZStd::vector textures; int totalArea = 0; @@ -374,15 +374,14 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) return lhs->GetDataSize() > rhs->GetDataSize(); }); - IDraw2d* draw2d = Draw2dHelper::GetDraw2d(); + CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); // setup to render lines of text for the debug display - draw2d->BeginDraw2d(false); float xOffset = 20.0f; float yOffset = 20.0f; - int blackTexture = gEnv->pRenderer->GetBlackTextureId(); + auto blackTexture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Black); float textOpacity = 1.0f; float backgroundRectOpacity = 0.75f; const float lineSpacing = 20.0f; @@ -432,9 +431,6 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) texture->GetWidth(), texture->GetHeight(), texture->GetDataSize(), texture->GetFormatName(), texture->GetName()); WriteLine(buffer, white); } - - draw2d->EndDraw2d(); -#endif } } diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 260bd8278c..888c88586a 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -20,6 +20,8 @@ #include #endif +class ITexture; + //////////////////////////////////////////////////////////////////////////////////////////////////// //! UI render interface // @@ -136,8 +138,6 @@ protected: // attributes #ifndef _RELEASE int m_debugTextureDataRecordLevel = 0; -#ifdef LYSHINE_ATOM_TODO // Convert debug code to Atom - AZStd::unordered_set m_texturesUsedInFrame; -#endif + AZStd::unordered_set m_texturesUsedInFrame; // LYSHINE_ATOM_TODO - convert to RPI::Image #endif }; diff --git a/Gems/LyShine/Code/Source/UiTextComponent.cpp b/Gems/LyShine/Code/Source/UiTextComponent.cpp index 648013108b..72c36ed66d 100644 --- a/Gems/LyShine/Code/Source/UiTextComponent.cpp +++ b/Gems/LyShine/Code/Source/UiTextComponent.cpp @@ -40,6 +40,7 @@ #include "RenderGraph.h" #include +#include namespace { @@ -1076,7 +1077,7 @@ UiTextComponent::InlineImage::InlineImage(const AZStd::string& texturePathname, { m_filepath = texturePathname; AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePath, m_filepath); - m_texture = nullptr; + m_texture.reset(); m_size = AZ::Vector2(0.0f, 0.0f); m_vAlign = vAlign; m_yOffset = yOffset; @@ -1094,24 +1095,11 @@ UiTextComponent::InlineImage::InlineImage(const AZStd::string& texturePathname, else { // Load the texture - uint32 loadTextureFlags = (FT_USAGE_ALLOWREADSRGB | FT_DONT_STREAM); - ITexture* texture = gEnv->pRenderer->EF_LoadTexture(texturePathname.c_str(), loadTextureFlags); - - if (!texture || !texture->IsTextureLoaded()) - { - gEnv->pSystem->Warning( - VALIDATOR_MODULE_SHINE, - VALIDATOR_WARNING, - VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - texturePathname.c_str(), - "No texture file found for image: %s. " - "NOTE: File must be in current project or a gem.", - texturePathname.c_str()); - } - else + m_texture = CDraw2d::LoadTexture(m_filepath); + if (m_texture) { - m_texture = texture; - m_size = AZ::Vector2(static_cast(m_texture->GetWidth()), static_cast(m_texture->GetHeight())); + AZ::RHI::Size size = m_texture->GetDescriptor().m_size; + m_size = AZ::Vector2(size.m_width, size.m_height); } } @@ -1127,17 +1115,6 @@ UiTextComponent::InlineImage::InlineImage(const AZStd::string& texturePathname, //////////////////////////////////////////////////////////////////////////////////////////////////// UiTextComponent::InlineImage::~InlineImage() { - // In order to avoid the texture being deleted while there are still commands on the render - // thread command queue that use it, we queue a command to delete the texture onto the - // command queue. - - if (m_texture && !m_atlas) - { - SResourceAsync* pInfo = new SResourceAsync(); - pInfo->eClassName = eRCN_Texture; - pInfo->pResource = m_texture; - gEnv->pRenderer->ReleaseResourceAsync(pInfo); - } } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1148,13 +1125,6 @@ bool UiTextComponent::InlineImage::OnAtlasLoaded(const TextureAtlasNamespace::Te m_coordinates = atlas->GetAtlasCoordinates(m_filepath); if (m_coordinates.GetWidth() > 0) { - if (m_texture) - { - SResourceAsync* pInfo = new SResourceAsync(); - pInfo->eClassName = eRCN_Texture; - pInfo->pResource = m_texture; - gEnv->pRenderer->ReleaseResourceAsync(pInfo); - } m_atlas = atlas; m_texture = m_atlas->GetTexture(); return true; @@ -1177,25 +1147,7 @@ bool UiTextComponent::InlineImage::OnAtlasUnloaded(const TextureAtlasNamespace:: else { // Load the texture - uint32 loadTextureFlags = (FT_USAGE_ALLOWREADSRGB | FT_DONT_STREAM); - ITexture* texture = gEnv->pRenderer->EF_LoadTexture(m_filepath.c_str(), loadTextureFlags); - - if (!texture || !texture->IsTextureLoaded()) - { - gEnv->pSystem->Warning( - VALIDATOR_MODULE_SHINE, - VALIDATOR_WARNING, - VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - m_filepath.c_str(), - "No texture file found for image: %s. " - "NOTE: File must be in current project or a gem.", - m_filepath.c_str()); - m_texture = nullptr; - } - else - { - m_texture = texture; - } + m_texture = CDraw2d::LoadTexture(m_filepath); } return true; } @@ -1869,7 +1821,7 @@ void UiTextComponent::Render(LyShine::IRenderGraph* renderGraph) UiTransformInterface::RectPointsArray rectPoints; GetTextBoundingBoxPrivate(GetDrawBatchLines(), m_selectionStart, m_selectionEnd, rectPoints); - ITexture* whiteTexture = gEnv->pRenderer->GetWhiteTexture(); + auto systemImage = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); bool isClampTextureMode = true; uint32 packedColor = (m_textSelectionColor.GetA8() << 24) | (m_textSelectionColor.GetR8() << 16) | (m_textSelectionColor.GetG8() << 8) | m_textSelectionColor.GetB8(); @@ -1878,7 +1830,12 @@ void UiTextComponent::Render(LyShine::IRenderGraph* renderGraph) { IRenderer::DynUiPrimitive* primitive = renderGraph->GetDynamicQuadPrimitive(rect.pt, packedColor); primitive->m_next = nullptr; - renderGraph->AddPrimitive(primitive, whiteTexture, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); + + LyShine::RenderGraph* lyRenderGraph = dynamic_cast(renderGraph); + if (lyRenderGraph) + { + lyRenderGraph->AddPrimitiveAtom(primitive, systemImage, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); + } } } @@ -1887,21 +1844,25 @@ void UiTextComponent::Render(LyShine::IRenderGraph* renderGraph) { for (auto batch : m_renderCache.m_imageBatches) { - ITexture* texture = batch->m_texture; + AZ::Data::Instance texture = batch->m_texture; // If the fade value has changed we need to update the alpha values in the vertex colors but we do // not want to touch or recompute the RGB values if (batch->m_cachedPrimitive.m_vertices[0].color.a != finalAlphaByte) { - for (int i=0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { batch->m_cachedPrimitive.m_vertices[i].color.a = finalAlphaByte; } } bool isClampTextureMode = true; - renderGraph->AddPrimitive(&batch->m_cachedPrimitive, texture, - isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); + LyShine::RenderGraph* lyRenderGraph = dynamic_cast(renderGraph); + if (lyRenderGraph) + { + lyRenderGraph->AddPrimitiveAtom(&batch->m_cachedPrimitive, texture, + isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode); + } } } diff --git a/Gems/LyShine/Code/Source/UiTextComponent.h b/Gems/LyShine/Code/Source/UiTextComponent.h index c3983c5454..b23f2a2886 100644 --- a/Gems/LyShine/Code/Source/UiTextComponent.h +++ b/Gems/LyShine/Code/Source/UiTextComponent.h @@ -21,13 +21,14 @@ #include #include #include +#include +#include #include #include #include -#include -#include +#include // Only needed for internal unit-testing #include @@ -91,7 +92,7 @@ public: //types bool OnAtlasLoaded(const TextureAtlasNamespace::TextureAtlas* atlas); bool OnAtlasUnloaded(const TextureAtlasNamespace::TextureAtlas* atlas); - ITexture* m_texture; + AZ::Data::Instance m_texture; AZ::Vector2 m_size; VAlign m_vAlign; float m_yOffset; @@ -616,8 +617,8 @@ private: // types struct RenderCacheImageBatch { - ITexture* m_texture; - IRenderer::DynUiPrimitive m_cachedPrimitive; + AZ::Data::Instance m_texture; + IRenderer::DynUiPrimitive m_cachedPrimitive; }; struct RenderCacheData diff --git a/Gems/LyShine/Code/Source/UiTextInputComponent.cpp b/Gems/LyShine/Code/Source/UiTextInputComponent.cpp index ca0d3fab02..6cfa2c911b 100644 --- a/Gems/LyShine/Code/Source/UiTextInputComponent.cpp +++ b/Gems/LyShine/Code/Source/UiTextInputComponent.cpp @@ -1450,13 +1450,17 @@ void UiTextInputComponent::CheckStartTextInput() EBUS_EVENT_ID_RESULT(textString, m_textEntity, UiTextBus, GetText); options.m_initialText = Utf8SubString(textString, m_textCursorPos, m_textSelectionStartPos); + AZ::EntityId canvasEntityId; + EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId); + + // Calculate height available for virtual keyboard. In game mode, canvas size is the same as viewport size + AZ::Vector2 canvasSize; + EBUS_EVENT_ID_RESULT(canvasSize, canvasEntityId, UiCanvasBus, GetCanvasSize); UiTransformInterface::RectPoints rectPoints; EBUS_EVENT_ID(GetEntityId(), UiTransformBus, GetViewportSpacePoints, rectPoints); const AZ::Vector2 bottomRight = rectPoints.GetAxisAlignedBottomRight(); - options.m_normalizedMinY = bottomRight.GetY() / static_cast(gEnv->pRenderer->GetHeight()); + options.m_normalizedMinY = (canvasSize.GetY() > 0.0f) ? bottomRight.GetY() / canvasSize.GetY() : 0.0f; - AZ::EntityId canvasEntityId; - EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId); EBUS_EVENT_ID_RESULT(options.m_localUserId, canvasEntityId, UiCanvasBus, GetLocalUserIdInputFilter); AzFramework::InputTextEntryRequestBus::Broadcast(&AzFramework::InputTextEntryRequests::TextEntryStart, options); diff --git a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp index 9e57ac26fd..68da6e6009 100644 --- a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp +++ b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,12 @@ protected: m_data->m_stubEnv.pSystem = &m_data->m_mockSystem; gEnv = &m_data->m_stubEnv; + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index 2a3b5fb3cc..f3eb1922fd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -34,11 +34,11 @@ namespace Multiplayer private: void OnRotationChangedEvent(const AZ::Quaternion& rotation); void OnTranslationChangedEvent(const AZ::Vector3& translation); - void OnScaleChangedEvent(const AZ::Vector3& scale); + void OnScaleChangedEvent(float scale); AZ::Event::Handler m_rotationEventHandler; AZ::Event::Handler m_translationEventHandler; - AZ::Event::Handler m_scaleEventHandler; + AZ::Event::Handler m_scaleEventHandler; }; class NetworkTransformComponentController diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h new file mode 100644 index 0000000000..01ae7b1207 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace Multiplayer +{ + //! @class RewindableArray + //! @brief Data structure that has a compile-time upper bound, provides array semantics and supports network serialization + template + class RewindableArray + : public AZStd::array, SIZE> + { + public: + //! Serialization method for array contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer); + + //! Serialization method for array contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @param deltaRecord Bitset delta record used to detect state change during reconciliation + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl new file mode 100644 index 0000000000..6e496ae4ea --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl @@ -0,0 +1,53 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace Multiplayer +{ + template + bool RewindableArray::Serialize(AzNetworking::ISerializer& serializer) + { + for (uint32_t i = 0; i < SIZE; ++i) + { + if(!this[i].Serialize(serializer)) + { + return false; + } + } + + return serializer.IsValid(); + } + + template + bool RewindableArray::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + { + for (uint32_t i = 0; i < SIZE; ++i) + { + if (deltaRecord.GetBit(i)) + { + serializer.ClearTrackedChangesFlag(); + if(!this[i].Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) + { + deltaRecord.SetBit(i, false); + } + } + } + + return serializer.IsValid(); + } +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h new file mode 100644 index 0000000000..c05fb98f72 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h @@ -0,0 +1,127 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace Multiplayer +{ + //! @class RewindableFixedVector + //! @brief Data structure that has a compile-time upper bound, provides vector semantics and supports network serialization + template + class RewindableFixedVector + { + public: + //! Default constructor + constexpr RewindableFixedVector() = default; + + //! Construct and initialize buffer to the provided value + //! @param initialValue initial value to set the internal buffer to + //! @param count initial value to reserve in the vector + constexpr RewindableFixedVector(const TYPE& initialValue, uint32_t count); + + //! Destructor + ~RewindableFixedVector(); + + //! Serialization method for fixed vector contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer); + + //! Serialization method for fixed vector contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @param deltaRecord Bitset delta record used to detect state change during reconciliation + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + + //! Copies elements from the buffer pointed to by Buffer to this FixedSizeVector instance, vector size will be set to BufferSize + //! @param buffer pointer to the buffer to copy + //! @param bufferSize number of elements in the buffer to copy + //! @return bool true on success, false if the input data was too large to fit in the vector + constexpr bool copy_values(const TYPE* buffer, uint32_t bufferSize); + + //! Copy buffer from the provided vector + //! @param RHS instance to copy from + constexpr RewindableFixedVector& operator=(const RewindableFixedVector& rhs); + + //! Equality operator, returns true if the current instance is equal to RHS + //! @param rhs the FixedSizeVector instance to test for equality against + //! @return bool true if equal, false if not + constexpr bool operator ==(const RewindableFixedVector& rhs) const; + + //! Inequality operator, returns true if the current instance is not equal to RHS + //! @param rhs the FixedSizeVector instance to test for inequality against + //! @return bool false if equal, true if not equal + constexpr bool operator !=(const RewindableFixedVector& rhs) const; + + //! Resizes the vector to the requested number of elements, initializing new elements if necessary + //! @param count the number of elements to size the vector to + //! @return bool true on success + constexpr bool resize(uint32_t count); + + //! Resizes the vector to the requested number of elements, without initialization + //! @param count the number of elements to size the vector to + //! @return bool true on success + constexpr bool resize_no_construct(uint32_t count); + + //! Resets the vector, returning it to size 0 + constexpr void clear(); + + //! Const element access + //! @param Index index of the element to return + //! @return const reference to the requested element + constexpr const TYPE& operator[](uint32_t index) const; + + //! Non-const element access + //! @param Index index of the element to return + //! @return non-const reference to the requested element + constexpr TYPE& operator[](uint32_t index); + + //! Pushes a new element to the back of the vector + //! @param Value value to append to the back of this vector + //! @return boolean true on success, false if the vector was full + constexpr bool push_back(const TYPE& value); + + //! Pops the last element off the vector, decreasing the vector's size by one + //! @return bool true on success, false if the vector was empty + constexpr bool pop_back(); + + //! Returns if the vector is empty + //! @return bool true on empty, false if the vector contains valid elements + constexpr bool empty() const; + + //! Gets the last element of the vector + constexpr const TYPE& back() const; + + //! Gets the size of the vector + constexpr uint32_t size() const; + + typedef const RewindableObject* const_iterator; + const_iterator begin() const { return m_container.cbegin(); } + const_iterator end() const { return m_container.cbegin() + aznumeric_cast(size()); } + typedef RewindableObject* iterator; + constexpr iterator begin() { return m_container.begin(); } + constexpr iterator end() { return m_container.begin() + aznumeric_cast(size()); } + + private: + AZStd::array, SIZE> m_container; + // Synchronized value for vector size, prefer using size() locally which checks m_container.size() + RewindableObject m_rewindableSize; + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl new file mode 100644 index 0000000000..5690e51c35 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl @@ -0,0 +1,230 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace Multiplayer +{ + template + constexpr RewindableFixedVector::RewindableFixedVector(const TYPE& initialValue, uint32_t count) + { + m_container.fill(initialValue); + m_rewindableSize = count; + } + + template + RewindableFixedVector::~RewindableFixedVector() + { + ; + } + + template + bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer) + { + if(!m_rewindableSize.Serialize(serializer)) + { + return false; + } + + for (uint32_t idx = 0; idx < size(); ++idx) + { + if(!m_container[idx].Serialize(serializer)) + { + return false; + } + } + + return serializer.IsValid(); + } + + template + bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + { + if (deltaRecord.GetBit(SIZE)) + { + const uint32_t origSize = m_rewindableSize; + if(!m_rewindableSize.Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && origSize == m_rewindableSize) + { + deltaRecord.SetBit(SIZE, false); + } + } + for (uint32_t idx = 0; idx < size(); ++idx) + { + if (deltaRecord.GetBit(idx)) + { + serializer.ClearTrackedChangesFlag(); + if(!m_container[idx].Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) + { + deltaRecord.SetBit(idx, false); + } + } + } + + return serializer.IsValid(); + } + + template + constexpr bool RewindableFixedVector::copy_values(const TYPE* buffer, uint32_t bufferSize) + { + if (!resize(bufferSize)) + { + return false; + } + + for (uint32_t idx = 0; idx < bufferSize; ++idx) + { + m_container[idx] = buffer[idx]; + } + + return true; + } + + template + constexpr RewindableFixedVector& RewindableFixedVector::operator=(const RewindableFixedVector& rhs) + { + resize(rhs.size()); + for (uint32_t idx = 0; idx < size(); ++idx) + { + m_container[idx] = rhs.m_container[idx].Get(); + } + return *this; + } + + template + constexpr bool RewindableFixedVector::operator ==(const RewindableFixedVector& rhs) const + { + return m_container == rhs.m_container && m_rewindableSize == rhs.m_rewindableSize; + } + + template + constexpr bool RewindableFixedVector::operator !=(const RewindableFixedVector& rhs) const + { + return !(*this == rhs); + } + + template + constexpr bool RewindableFixedVector::resize(uint32_t count) + { + if (count > SIZE) + { + return false; + } + + if (count == size()) + { + return true; + } + + if (count > size()) + { + for (uint32_t idx = size(); idx < count; ++idx) + { + m_container[idx] = TYPE(); + } + } + m_rewindableSize = count; + + return true; + } + + template + constexpr bool RewindableFixedVector::resize_no_construct(uint32_t count) + { + if (count > SIZE) + { + return false; + } + + m_rewindableSize = count; + + return true; + } + + template + constexpr void RewindableFixedVector::clear() + { + for (uint32_t idx = 0; idx < SIZE; ++idx) + { + m_container[idx] = TYPE(); + } + m_rewindableSize = 0; + } + + template + constexpr const TYPE& RewindableFixedVector::operator[](uint32_t index) const + { + AZ_Assert(index < size(), "Out of bounds access (requested %u, reserved %u)", index, size()); + return m_container[index].Get(); + } + + template + constexpr TYPE& RewindableFixedVector::operator[](uint32_t index) + { + AZ_Assert(index < size(), "Out of bounds access (requested %u, reserved %u)", index, size()); + return m_container[index].Modify(); + } + + template + constexpr bool RewindableFixedVector::push_back(const TYPE& value) + { + if (size() < SIZE) + { + m_container[m_rewindableSize] = value; + m_rewindableSize = m_rewindableSize + 1; + return true; + } + + return false; + } + + template + constexpr bool RewindableFixedVector::pop_back() + { + if (size() > 0) + { + m_rewindableSize = m_rewindableSize - 1; + m_container[m_rewindableSize] = TYPE(); + return true; + } + + return false; + } + + template + constexpr bool RewindableFixedVector::empty() const + { + return m_rewindableSize.Get() == 0; + } + + template + constexpr const TYPE& RewindableFixedVector::back() const + { + AZ_Assert(size() > 0, "Attempted to get back element of an empty RewindableFixedVector"); + return m_container[m_rewindableSize - 1].Get(); + } + + template + constexpr uint32_t RewindableFixedVector::size() const + { + return m_rewindableSize; + } +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index d5b7d563ab..f7e92bbe26 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -32,7 +32,7 @@ namespace Multiplayer RewindableObject() = default; //! Constructor. - //! @param connectionId the connectionId of the connection that owns the object. + //! @param value base type value to construct from RewindableObject(const BASE_TYPE& value); //! Copy construct from underlying base type. diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 71e81b6bfb..8cf1eeeb58 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -7,13 +7,21 @@ {% macro DeclareNetworkPropertyGetter(Property) %} {% set PropertyName = UpperFirst(Property.attrib['Name']) %} {% if Property.attrib['Container'] == 'Array' %} -const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::k_RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% else %} +const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% endif %} const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const; {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ PropertyName }}AddEvent(AZ::Event::Handler& handler); {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% endif %} const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const; const {{ Property.attrib['Type'] }} &{{ PropertyName }}GetBack() const; uint32_t {{ PropertyName }}GetSize() const; @@ -156,9 +164,17 @@ AZ::Event<{{ Property.attrib['Type'] }}> m_{{ LowerFirst(Property.attrib['Name'] {% macro DeclareNetworkPropertyVars(Component, ReplicateFrom, ReplicateTo) %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if Property.attrib['Container'] == 'Array' %} -AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% if Property.attrib['IsRewindable']|booleanTrue %} +RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% else %} +AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% endif %} {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% else %} AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% endif %} {% elif Property.attrib['IsRewindable']|booleanTrue %} Multiplayer::RewindableObject<{{ Property.attrib['Type'] }}, Multiplayer::RewindHistorySize> m_{{ LowerFirst(Property.attrib['Name']) }} = {{ Property.attrib['Init'] }}; {% else %} @@ -228,6 +244,8 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] } #include #include #include +#include +#include #include {% call(Include) AutoComponentMacros.ParseIncludes(Component) %} #include <{{ Include.attrib['File'] }}> diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 7d5295aabb..6b2c5b199a 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -3,7 +3,11 @@ {% macro LowerFirst(text) %}{{ text[0] | lower}}{{ text[1:] }}{% endmacro %} {% macro DefineNetworkPropertyGet(ClassName, Property, Prefix = '') %} {% if Property.attrib['Container'] == 'Array' %} -const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% else %} +const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -21,7 +25,11 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -110,25 +118,26 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { - int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); - int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - return true; + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value)) + { + int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size(); + int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; + } } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack(const Multiplayer::NetworkInput&) { - if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back()) { - return false; + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; } - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); - return true; + return false; } void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear(const Multiplayer::NetworkInput&) @@ -207,25 +216,27 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { - uint32_t indexToSet = aznumeric_cast(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size()); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); - uint32_t bitIndex = indexToSet + aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - return true; + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value)) + { + uint32_t indexToSet = aznumeric_cast(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size()); + uint32_t bitIndex = indexToSet + aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; + } + return false; } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack() { - if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back()) { - return false; + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; } - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); - return true; + return false; } void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear() @@ -562,12 +573,12 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} {% if networkPropertyCount.value > 0 %} - Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats(); + [[maybe_unused]] Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats(); // We modify the record if we are writing an update so that we don't notify for a change that really didn't change the value (just a duplicated send from the server) [[maybe_unused]] bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if Property.attrib['Container'] != 'None' and Property.attrib['Container'] != 'Object' %} - { /* @todo Implement serialization for Vector and Array Network Properties + { // Serialization for Vector and Array Network Properties const uint32_t firstBit = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); {% if Property.attrib['Container'] == 'Vector' %} const uint32_t lastBit = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}); @@ -575,17 +586,8 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re const uint32_t lastBit = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'End') }}); {% endif %} - AzNetworking::BitsetView deltaRecord(replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, firstBit, lastBit - firstBit + 1); - if (deltaRecord.AnySet()) - { -{% if Property.attrib['Container'] == 'Vector' %} - Multiplayer::SerializableFixedSizeVectorDeltaStruct<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ LowerFirst(Property.attrib['Name']) }}, deltaRecord); -{% else %} - Multiplayer::SerializableFixedSizeArrayDeltaStruct<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ Property.attrib['Name'] }}, deltaRecord); -{% endif %} - serializer.Serialize(deltaStruct, "{{ UpperFirst(Property.attrib['Name']) }}"); - } - */ + AzNetworking::FixedSizeBitsetView deltaRecord(replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, firstBit, lastBit - firstBit + 1); + m_{{ LowerFirst(Property.attrib['Name']) }}.Serialize(serializer, deltaRecord); } {% else %} Multiplayer::SerializeNetworkPropertyHelper @@ -615,7 +617,7 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if (Property.attrib['GenerateEventBindings']|booleanTrue) %} {% if Property.attrib['Container'] != 'None' and Property.attrib['Container'] != 'Object' %} - /* todo Implement NotifyChangesAuthorityToClientProperties for Arrays and Vectors + // NotifyChangesAuthorityToClientProperties for Arrays and Vectors for (uint32_t bitIndex = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}), elementIndex = 0; bitIndex <= static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component, ReplicateFrom, ReplicateTo, Property, 'End') }}); ++bitIndex, ++elementIndex) { if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(bitIndex){% if Property.attrib['Container'] == 'Vector' %} && elementIndex < m_{{ Property.attrib['Name'] }}.GetSize(){% endif %}) @@ -627,7 +629,7 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}))) { m_{{ LowerFirst(Property.attrib['Name']) }}SizeChangedEvent.Signal(m_{{ LowerFirst(Property.attrib['Name']) }}.size()); - } */ + } {% endif %} {% else %} if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}))) @@ -645,7 +647,11 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam {% macro DefineArchetypePropertyGet(Property, ClassType, ClassName, Prefix = '') %} {% if ClassType == '' or Property.attrib['ExportTo'] == ClassType or Property.attrib['ExportTo'] == "Common" %} {% if Property.attrib['Container'] == 'Array' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -656,7 +662,11 @@ const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property. } {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -1472,7 +1482,14 @@ namespace {{ Component.attrib['Namespace'] }} { {% for Property in Component.iter('NetworkProperty') %} {% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} + for ( auto& element: m_{{ LowerFirst(Property.attrib['Name']) }}) + { + element.SetOwningConnectionId(connectionId); + } +{% else %} m_{{ LowerFirst(Property.attrib['Name']) }}.SetOwningConnectionId(connectionId); +{% endif %} {% endif %} {% endfor %} } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml index 96653a607c..a112cde4e6 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml @@ -14,7 +14,7 @@ - + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 0cc4cb131e..682f7ea988 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -32,7 +32,7 @@ namespace Multiplayer NetworkTransformComponent::NetworkTransformComponent() : m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); }) , m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) - , m_scaleEventHandler([this](const AZ::Vector3& scale) { OnScaleChangedEvent(scale); }) + , m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); }) { ; } @@ -68,10 +68,10 @@ namespace Multiplayer GetTransformComponent()->SetWorldTM(worldTm); } - void NetworkTransformComponent::OnScaleChangedEvent(const AZ::Vector3& scale) + void NetworkTransformComponent::OnScaleChangedEvent(float scale) { AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); - worldTm.SetScale(scale); + worldTm.SetUniformScale(scale); GetTransformComponent()->SetWorldTM(worldTm); } @@ -100,7 +100,7 @@ namespace Multiplayer { SetRotation(worldTm.GetRotation()); SetTranslation(worldTm.GetTranslation()); - SetScale(worldTm.GetScale()); + SetScale(worldTm.GetUniformScale()); } } } diff --git a/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp new file mode 100644 index 0000000000..0653dd5e08 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp @@ -0,0 +1,121 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class RewindableContainerTests + : public AllocatorsFixture + { + public: + Multiplayer::NetworkTime m_networkTime; + AZ::LoggerSystemComponent m_loggerComponent; + AZ::TimeSystemComponent m_timeComponent; + }; + + static constexpr uint32_t RewindableContainerSize = 7; + + TEST_F(RewindableContainerTests, BasicVectorTest) + { + Multiplayer::RewindableFixedVector test(0, 0); + + // Test push_back + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + test.push_back(idx); + EXPECT_EQ(idx, test[idx]); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + // Test rewind for all pushed values and overall size + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(idx + 1, test.size()); + EXPECT_EQ(idx, test.back()); + } + + // Test pop_back + test.pop_back(); + EXPECT_EQ(RewindableContainerSize - 1, test.size()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + + // Test iterator + uint32_t iterCount = 0; + auto iter = test.begin(); + while (iter != test.end()) + { + ++iterCount; + ++iter; + } + EXPECT_EQ(RewindableContainerSize - 1, iterCount); + + // Test clear and empty + test.clear(); + EXPECT_EQ(0, test.size()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + EXPECT_TRUE(test.empty()); + + // Test rewind for pop_back and clear + Multiplayer::ScopedAlterTime pop_time(static_cast(RewindableContainerSize), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(RewindableContainerSize - 1, test.size()); + Multiplayer::ScopedAlterTime clear_time(static_cast(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(0, test.size()); + + // Test copy_values and resize_no_construct + test.resize_no_construct(RewindableContainerSize); + test.copy_values(&test[RewindableContainerSize-1], 1); + EXPECT_EQ(1, test.size()); + test.resize_no_construct(RewindableContainerSize); + EXPECT_EQ(test[0], test[RewindableContainerSize - 1]); + } + + TEST_F(RewindableContainerTests, BasicArrayTest) + { + Multiplayer::RewindableArray test; + + test.fill(0); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + // Test push_back + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + test[idx] = idx; + EXPECT_EQ(idx, test[idx].Get()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + // Test rewind for all values and overall size + for (uint32_t idx = 1; idx <= RewindableContainerSize; ++idx) + { + Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + for (uint32_t testIdx = 0; testIdx < RewindableContainerSize; ++testIdx) + { + if (testIdx < idx) + { + EXPECT_EQ(testIdx, test[testIdx].Get()); + } + else + { + EXPECT_EQ(0, test[testIdx].Get()); + } + } + } + } +} diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index eb856a48db..1cfef93240 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -33,6 +33,10 @@ set(FILES Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkTime/INetworkTime.h + Include/Multiplayer/NetworkTime/RewindableArray.h + Include/Multiplayer/NetworkTime/RewindableArray.inl + Include/Multiplayer/NetworkTime/RewindableFixedVector.h + Include/Multiplayer/NetworkTime/RewindableFixedVector.inl Include/Multiplayer/NetworkTime/RewindableObject.h Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/ReplicationWindows/IReplicationWindow.h diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index fe1ca38186..0731c25d3b 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -13,5 +13,6 @@ set(FILES Tests/Main.cpp Tests/IMultiplayerConnectionMock.h Tests/MultiplayerSystemTests.cpp + Tests/RewindableContainerTests.cpp Tests/RewindableObjectTests.cpp ) diff --git a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings index dd621c891f..adbf7d20f9 100644 --- a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings +++ b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings @@ -17,7 +17,7 @@ - + @@ -36,7 +36,7 @@ - + diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings index 8d01ab1ac2..e97c4e452c 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:0,ios:0,osx_gl:0,pc:2,provo:0,wiiu:0" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:0,ios:0,mac:0,pc:2,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings index 93bcddc494..08861692ea 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce="es3:0,ios:0,osx_gl:0,pc:1,provo:0,wiiu:0" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce="android:0,ios:0,mac:0,pc:1,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings index 54586c2db1..48c18e1fe4 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:0,ios:0,osx_gl:0,pc:3,provo:0,wiiu:0" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:0,ios:0,mac:0,pc:3,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PhysX/Code/Editor/DebugDraw.cpp b/Gems/PhysX/Code/Editor/DebugDraw.cpp index fffb1e1350..23a9a3cb44 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.cpp +++ b/Gems/PhysX/Code/Editor/DebugDraw.cpp @@ -555,25 +555,37 @@ namespace PhysX if (meshConfig.GetCachedNativeMesh()) { - const AZ::Transform scaleMatrix = AZ::Transform::CreateScale(meshScale); - debugDisplay.PushMatrix(GetColliderLocalTransform(colliderConfig) * scaleMatrix); + debugDisplay.PushMatrix(GetColliderLocalTransform(colliderConfig)); if (meshConfig.GetMeshType() == Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh) { - DrawTriangleMesh(debugDisplay, colliderConfig, geomIndex); + DrawTriangleMesh(debugDisplay, colliderConfig, geomIndex, meshScale); } else { - DrawConvexMesh(debugDisplay, colliderConfig, geomIndex); + DrawConvexMesh(debugDisplay, colliderConfig, geomIndex, meshScale); } debugDisplay.PopMatrix(); } } - void Collider::DrawTriangleMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, - AZ::u32 geomIndex) const + AZStd::vector ScalePoints(const AZ::Vector3& scale, const AZStd::vector& points) + { + AZStd::vector scaledPoints; + scaledPoints.resize_no_construct(points.size()); + AZStd::transform( + points.begin(), points.end(), scaledPoints.begin(), + [scale](const AZ::Vector3& point) + { + return scale * point; + }); + return scaledPoints; + } + + void Collider::DrawTriangleMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale) const { AZ_Assert(geomIndex < m_geometry.size(), "DrawTriangleMesh: geomIndex is out of range"); @@ -581,10 +593,10 @@ namespace PhysX const AZStd::unordered_map>& triangleIndexesByMaterialSlot = geom.m_triangleIndexesByMaterialSlot; - const AZStd::vector& verts = geom.m_verts; - const AZStd::vector& points = geom.m_points; + AZStd::vector scaledVerts = ScalePoints(meshScale, geom.m_verts); + AZStd::vector scaledPoints = ScalePoints(meshScale, geom.m_points); - if (!verts.empty()) + if (!scaledVerts.empty()) { for (const auto& element : triangleIndexesByMaterialSlot) { @@ -596,30 +608,31 @@ namespace PhysX triangleMeshInfo.m_numTriangles = triangleCount; triangleMeshInfo.m_materialSlotIndex = materialSlot; - debugDisplay.DrawTrianglesIndexed(verts, triangleIndexes + debugDisplay.DrawTrianglesIndexed(scaledVerts, triangleIndexes , CalcDebugColor(colliderConfig, triangleMeshInfo)); } - debugDisplay.DrawLines(points, WireframeColor); + debugDisplay.DrawLines(scaledPoints, WireframeColor); } } - void Collider::DrawConvexMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex) const + void Collider::DrawConvexMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale) const { AZ_Assert(geomIndex < m_geometry.size(), "DrawConvexMesh: geomIndex is out of range"); const GeometryData& geom = m_geometry[geomIndex]; - const AZStd::vector& verts = geom.m_verts; - const AZStd::vector& points = geom.m_points; + AZStd::vector scaledVerts = ScalePoints(meshScale, geom.m_verts); + AZStd::vector scaledPoints = ScalePoints(meshScale, geom.m_points); - if (!verts.empty()) + if (!scaledVerts.empty()) { - const AZ::u32 triangleCount = static_cast(verts.size() / 3); + const AZ::u32 triangleCount = static_cast(scaledVerts.size() / 3); ElementDebugInfo convexMeshInfo; convexMeshInfo.m_numTriangles = triangleCount; - debugDisplay.DrawTriangles(verts, CalcDebugColor(colliderConfig, convexMeshInfo)); - debugDisplay.DrawLines(points, WireframeColor); + debugDisplay.DrawTriangles(scaledVerts, CalcDebugColor(colliderConfig, convexMeshInfo)); + debugDisplay.DrawLines(scaledPoints, WireframeColor); } } @@ -685,7 +698,7 @@ namespace PhysX // Let each collider decide how to scale itself, so extract the scale here. AZ::Transform entityWorldTransformWithoutScale = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(entityWorldTransformWithoutScale, m_entityId, &AZ::TransformInterface::GetWorldTM); - entityWorldTransformWithoutScale.ExtractScale(); + entityWorldTransformWithoutScale.ExtractUniformScale(); auto* physXDebug = AZ::Interface::Get(); if (physXDebug == nullptr) diff --git a/Gems/PhysX/Code/Editor/DebugDraw.h b/Gems/PhysX/Code/Editor/DebugDraw.h index fcff961412..c43634717a 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.h +++ b/Gems/PhysX/Code/Editor/DebugDraw.h @@ -115,11 +115,13 @@ namespace PhysX AzFramework::DebugDisplayRequests& debugDisplay) override; // Internal mesh drawing subroutines - void DrawTriangleMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex) const; + void DrawTriangleMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale = AZ::Vector3::CreateOne()) const; - void DrawConvexMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex) const; + void DrawConvexMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale = AZ::Vector3::CreateOne()) const; void BuildTriangleMesh(physx::PxBase* meshData, AZ::u32 geomIndex) const; diff --git a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp b/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp index 502d60d8d9..e8e93e0d6e 100644 --- a/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp +++ b/Gems/PhysX/Code/Editor/EditorSubComponentModeSnapRotation.cpp @@ -89,7 +89,7 @@ namespace PhysX AZ::Transform worldTransform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult( worldTransform, m_entityComponentId.GetEntityId(), &AZ::TransformInterface::GetWorldTM); - worldTransform.ExtractScale(); + worldTransform.ExtractUniformScale(); AZ::Transform localTransform = AZ::Transform::CreateIdentity(); EditorJointRequestBus::EventResult( diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index e9b9c41da3..87bb702184 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -603,7 +603,7 @@ namespace PhysX } AZ::Transform colliderTransform = GetWorldTM(); - colliderTransform.ExtractScale(); + colliderTransform.ExtractUniformScale(); AzPhysics::StaticRigidBodyConfiguration configuration; configuration.m_orientation = colliderTransform.GetRotation(); configuration.m_position = colliderTransform.GetTranslation(); @@ -868,7 +868,7 @@ namespace PhysX colliderConfigNoOffset.m_rotation = AZ::Quaternion::CreateIdentity(); colliderConfigNoOffset.m_position = AZ::Vector3::CreateZero(); m_colliderDebugDraw.DrawMesh(debugDisplay, colliderConfigNoOffset, m_scaledPrimitive.value(), - GetWorldTM().GetScale() * m_cachedNonUniformScale, shapeIndex); + GetWorldTM().GetUniformScale() * m_cachedNonUniformScale, shapeIndex); } } @@ -1007,7 +1007,7 @@ namespace PhysX AZ::Vector3 EditorColliderComponent::GetBoxScale() { - return GetWorldTM().GetScale(); + return AZ::Vector3(GetWorldTM().GetUniformScale()); } void EditorColliderComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) @@ -1049,7 +1049,7 @@ namespace PhysX void EditorColliderComponent::UpdateShapeConfigurationScale() { auto& shapeConfiguration = m_shapeConfiguration.GetCurrent(); - shapeConfiguration.m_scale = GetWorldTM().ExtractScale() * m_cachedNonUniformScale; + shapeConfiguration.m_scale = GetWorldTM().ExtractUniformScale() * m_cachedNonUniformScale; m_colliderDebugDraw.ClearCachedGeometry(); } diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index efd65181da..f68c4d17d8 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -365,7 +365,7 @@ namespace PhysX } AZ::Transform colliderTransform = GetWorldTM(); - colliderTransform.ExtractScale(); + colliderTransform.ExtractUniformScale(); AzPhysics::RigidBodyConfiguration configuration = m_config; configuration.m_orientation = colliderTransform.GetRotation(); diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 379afc3f2d..692fbf96f3 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -205,7 +205,7 @@ namespace PhysX } AZ::Transform transform = GetWorldTM(); - transform.ExtractScale(); + transform.ExtractUniformScale(); const size_t numPoints = m_geometryCache.m_cachedSamplePoints.size(); for (size_t pointIndex = 0; pointIndex < numPoints; ++pointIndex) { diff --git a/Gems/PhysX/Code/Source/ForceRegion.cpp b/Gems/PhysX/Code/Source/ForceRegion.cpp index c41ae47a0e..2cdd0dea3f 100644 --- a/Gems/PhysX/Code/Source/ForceRegion.cpp +++ b/Gems/PhysX/Code/Source/ForceRegion.cpp @@ -148,7 +148,7 @@ namespace PhysX { m_worldTransform = world; m_regionParams.m_position = world.GetTranslation(); - m_regionParams.m_scale = world.GetScale(); + m_regionParams.m_scale = world.GetUniformScale(); m_regionParams.m_rotation = world.GetRotation(); AZ::EBusReduceResult triggerAabb; triggerAabb.value = AZ::Aabb::CreateNull(); @@ -223,7 +223,7 @@ namespace PhysX , entityId , &AZ::TransformBus::Events::GetWorldTM); regionParams.m_position = worldTransform.GetTranslation(); - regionParams.m_scale = worldTransform.GetScale(); + regionParams.m_scale = worldTransform.GetUniformScale(); regionParams.m_rotation = worldTransform.GetRotation(); LmbrCentral::SplineComponentRequestBus::EventResult(regionParams.m_spline diff --git a/Gems/PhysX/Code/Source/ForceRegionForces.cpp b/Gems/PhysX/Code/Source/ForceRegionForces.cpp index 8ea74c0de7..61679e9cb4 100644 --- a/Gems/PhysX/Code/Source/ForceRegionForces.cpp +++ b/Gems/PhysX/Code/Source/ForceRegionForces.cpp @@ -294,8 +294,7 @@ namespace PhysX rotateInverse.InvertFull(); } - AZ::Vector3 scaleInverse = region.m_scale; - scaleInverse = scaleInverse.GetReciprocal(); + float scaleInverse = 1.0f / region.m_scale; AZ::Vector3 position = entity.m_position + entity.m_velocity * m_lookAhead; AZ::Vector3 localPos = position - region.m_position; diff --git a/Gems/PhysX/Code/Source/ForceRegionForces.h b/Gems/PhysX/Code/Source/ForceRegionForces.h index 206e35c195..6f7eb6b277 100644 --- a/Gems/PhysX/Code/Source/ForceRegionForces.h +++ b/Gems/PhysX/Code/Source/ForceRegionForces.h @@ -36,7 +36,7 @@ namespace PhysX AZ::EntityId m_id; AZ::Vector3 m_position; AZ::Quaternion m_rotation; - AZ::Vector3 m_scale; + float m_scale; AZ::SplinePtr m_spline; AZ::Aabb m_aabb; }; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp index 5249781e31..02ebe7281c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp @@ -274,7 +274,7 @@ namespace PhysX m_queuedDisableSimulation = true; } - bool Ragdoll::IsSimulated() + bool Ragdoll::IsSimulated() const { return m_simulating; } diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h index 7bf807bcc5..7182a3a44c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h @@ -46,7 +46,7 @@ namespace PhysX void EnableSimulationQueued(const Physics::RagdollState& initialState) override; void DisableSimulation() override; void DisableSimulationQueued() override; - bool IsSimulated() override; + bool IsSimulated() const override; void GetState(Physics::RagdollState& ragdollState) const override; void SetState(const Physics::RagdollState& ragdollState) override; void SetStateQueued(const Physics::RagdollState& ragdollState) override; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp index 6fa69c7a24..86c60565a0 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp @@ -105,39 +105,55 @@ namespace PhysX AZ::Vector3 CharacterControllerComponent::GetBasePosition() const { - return IsPhysicsEnabled() ? m_controller->GetBasePosition() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetBasePosition(); + } + return AZ::Vector3::CreateZero(); } void CharacterControllerComponent::SetBasePosition(const AZ::Vector3& position) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetBasePosition(position); + controller->SetBasePosition(position); AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, position); } } AZ::Vector3 CharacterControllerComponent::GetCenterPosition() const { - return IsPhysicsEnabled() ? m_controller->GetCenterPosition() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetCenterPosition(); + } + return AZ::Vector3::CreateZero(); } float CharacterControllerComponent::GetStepHeight() const { - return IsPhysicsEnabled() ? m_controller->GetStepHeight() : 0.0f; + if (auto* controller = GetControllerConst()) + { + return controller->GetStepHeight(); + } + return 0.0f; } void CharacterControllerComponent::SetStepHeight(float stepHeight) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetStepHeight(stepHeight); + controller->SetStepHeight(stepHeight); } } AZ::Vector3 CharacterControllerComponent::GetUpDirection() const { - return IsPhysicsEnabled() ? m_controller->GetUpDirection() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetUpDirection(); + } + return AZ::Vector3::CreateZero(); } void CharacterControllerComponent::SetUpDirection([[maybe_unused]] const AZ::Vector3& upDirection) @@ -147,51 +163,58 @@ namespace PhysX float CharacterControllerComponent::GetSlopeLimitDegrees() const { - return IsPhysicsEnabled() ? m_controller->GetSlopeLimitDegrees() : 0.0f; + if (auto* controller = GetControllerConst()) + { + return controller->GetSlopeLimitDegrees(); + } + return 0.0f; } void CharacterControllerComponent::SetSlopeLimitDegrees(float slopeLimitDegrees) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetSlopeLimitDegrees(slopeLimitDegrees); + controller->SetSlopeLimitDegrees(slopeLimitDegrees); } } float CharacterControllerComponent::GetMaximumSpeed() const { - if (IsPhysicsEnabled()) + if (auto* controller = GetControllerConst()) { - return m_controller->GetMaximumSpeed(); + return controller->GetMaximumSpeed(); } - return 0.0f; } void CharacterControllerComponent::SetMaximumSpeed(float maximumSpeed) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetMaximumSpeed(maximumSpeed); + controller->SetMaximumSpeed(maximumSpeed); } } AZ::Vector3 CharacterControllerComponent::GetVelocity() const { - return IsPhysicsEnabled() ? m_controller->GetVelocity() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetVelocity(); + } + return AZ::Vector3::CreateZero(); } void CharacterControllerComponent::AddVelocity(const AZ::Vector3& velocity) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->AddVelocity(velocity); + controller->AddVelocity(velocity); } } Physics::Character* CharacterControllerComponent::GetCharacter() { - return m_controller; + return GetController(); } void CharacterControllerComponent::EnablePhysics() @@ -206,14 +229,14 @@ namespace PhysX bool CharacterControllerComponent::IsPhysicsEnabled() const { - return m_controller != nullptr; + return GetControllerConst() != nullptr; } AZ::Aabb CharacterControllerComponent::GetAabb() const { - if (m_controller) + if (auto* controller = GetControllerConst()) { - return m_controller->GetAabb(); + return controller->GetAabb(); } return AZ::Aabb::CreateNull(); } @@ -225,94 +248,121 @@ namespace PhysX AzPhysics::SimulatedBodyHandle CharacterControllerComponent::GetSimulatedBodyHandle() const { - if (m_controller) - { - return m_controller->m_bodyHandle; - } - return AzPhysics::InvalidSimulatedBodyHandle; + return m_controllerBodyHandle; } AzPhysics::SceneQueryHit CharacterControllerComponent::RayCast(const AzPhysics::RayCastRequest& request) { - if (m_controller) + if (auto* controller = GetController()) { - return m_controller->RayCast(request); + return controller->RayCast(request); } + return AzPhysics::SceneQueryHit(); } // CharacterControllerRequestBus void CharacterControllerComponent::Resize(float height) { - return m_controller->Resize(height); + if (auto* controller = GetController()) + { + controller->Resize(height); + } } float CharacterControllerComponent::GetHeight() { - return m_controller->GetHeight(); + if (auto* controller = GetController()) + { + return controller->GetHeight(); + } + return 0.0f; } void CharacterControllerComponent::SetHeight(float height) { - return m_controller->SetHeight(height); + if (auto* controller = GetController()) + { + controller->SetHeight(height); + } } float CharacterControllerComponent::GetRadius() { - return m_controller->GetRadius(); + if (auto* controller = GetController()) + { + return controller->GetRadius(); + } + return 0.0f; } void CharacterControllerComponent::SetRadius(float radius) { - return m_controller->SetRadius(radius); + if (auto* controller = GetController()) + { + controller->SetRadius(radius); + } } float CharacterControllerComponent::GetHalfSideExtent() { - return m_controller->GetHalfSideExtent(); + if (auto* controller = GetController()) + { + return controller->GetHalfSideExtent(); + } + return 0.0f; } void CharacterControllerComponent::SetHalfSideExtent(float halfSideExtent) { - return m_controller->SetHalfSideExtent(halfSideExtent); + if (auto* controller = GetController()) + { + controller->SetHalfSideExtent(halfSideExtent); + } } float CharacterControllerComponent::GetHalfForwardExtent() { - return m_controller->GetHalfForwardExtent(); + if (auto* controller = GetController()) + { + return controller->GetHalfForwardExtent(); + } + return 0.0f; } void CharacterControllerComponent::SetHalfForwardExtent(float halfForwardExtent) { - return m_controller->SetHalfForwardExtent(halfForwardExtent); + if (auto* controller = GetController()) + { + controller->SetHalfForwardExtent(halfForwardExtent); + } } // TransformNotificationBus void CharacterControllerComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) { - if (!IsPhysicsEnabled()) + if (auto* controller = GetController()) { - return; + controller->SetBasePosition(world.GetTranslation()); } - - m_controller->SetBasePosition(world.GetTranslation()); } void CharacterControllerComponent::SetCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag) { - if (!IsPhysicsEnabled()) + auto* controller = GetController(); + if (controller == nullptr) { return; } - if (Physics::Utils::FilterTag(m_controller->GetColliderTag(), colliderTag)) + if (Physics::Utils::FilterTag(controller->GetColliderTag(), colliderTag)) { bool success = false; AzPhysics::CollisionLayer collisionLayer; Physics::CollisionRequestBus::BroadcastResult(success, &Physics::CollisionRequests::TryGetCollisionLayerByName, layerName, collisionLayer); if (success) { - m_controller->SetCollisionLayer(collisionLayer); + controller->SetCollisionLayer(collisionLayer); } } } @@ -320,30 +370,33 @@ namespace PhysX AZStd::string CharacterControllerComponent::GetCollisionLayerName() { AZStd::string layerName; - if (!IsPhysicsEnabled()) + auto* controller = GetControllerConst(); + if (controller == nullptr) { return layerName; } - Physics::CollisionRequestBus::BroadcastResult(layerName, &Physics::CollisionRequests::GetCollisionLayerName, m_controller->GetCollisionLayer()); + Physics::CollisionRequestBus::BroadcastResult( + layerName, &Physics::CollisionRequests::GetCollisionLayerName, controller->GetCollisionLayer()); return layerName; } void CharacterControllerComponent::SetCollisionGroup(const AZStd::string& groupName, AZ::Crc32 colliderTag) { - if (!IsPhysicsEnabled()) + auto* controller = GetController(); + if (controller == nullptr) { return; } - if (Physics::Utils::FilterTag(m_controller->GetColliderTag(), colliderTag)) + if (Physics::Utils::FilterTag(controller->GetColliderTag(), colliderTag)) { bool success = false; AzPhysics::CollisionGroup collisionGroup; Physics::CollisionRequestBus::BroadcastResult(success, &Physics::CollisionRequests::TryGetCollisionGroupByName, groupName, collisionGroup); if (success) { - m_controller->SetCollisionGroup(collisionGroup); + controller->SetCollisionGroup(collisionGroup); } } } @@ -351,23 +404,26 @@ namespace PhysX AZStd::string CharacterControllerComponent::GetCollisionGroupName() { AZStd::string groupName; - if (!IsPhysicsEnabled()) + auto* controller = GetControllerConst(); + if (controller == nullptr) { return groupName; } - - Physics::CollisionRequestBus::BroadcastResult(groupName, &Physics::CollisionRequests::GetCollisionGroupName, m_controller->GetCollisionGroup()); + + Physics::CollisionRequestBus::BroadcastResult( + groupName, &Physics::CollisionRequests::GetCollisionGroupName, controller->GetCollisionGroup()); return groupName; } void CharacterControllerComponent::ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) { - if (!IsPhysicsEnabled()) + auto* controller = GetController(); + if (controller == nullptr) { return; } - if (Physics::Utils::FilterTag(m_controller->GetColliderTag(), colliderTag)) + if (Physics::Utils::FilterTag(controller->GetColliderTag(), colliderTag)) { bool success = false; AzPhysics::CollisionLayer collisionLayer; @@ -375,23 +431,43 @@ namespace PhysX if (success) { AzPhysics::CollisionLayer layer(layerName); - AzPhysics::CollisionGroup group = m_controller->GetCollisionGroup(); + AzPhysics::CollisionGroup group = controller->GetCollisionGroup(); group.SetLayer(layer, enabled); - m_controller->SetCollisionGroup(group); + controller->SetCollisionGroup(group); } } } void CharacterControllerComponent::OnPreSimulate(float deltaTime) { - if (m_controller) + if (auto* controller = GetController()) { - m_controller->ApplyRequestedVelocity(deltaTime); - const AZ::Vector3 newPosition = GetBasePosition(); + controller->ApplyRequestedVelocity(deltaTime); + const AZ::Vector3 newPosition = controller->GetBasePosition(); AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, newPosition); } } + const PhysX::CharacterController* CharacterControllerComponent::GetControllerConst() const + { + if (m_controllerBodyHandle == AzPhysics::InvalidSimulatedBodyHandle || m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) + { + return nullptr; + } + + if (auto* sceneInterface = AZ::Interface::Get()) + { + return azdynamic_cast( + sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_controllerBodyHandle)); + } + return nullptr; + } + + PhysX::CharacterController* CharacterControllerComponent::GetController() + { + return const_cast(static_cast(*this).GetControllerConst()); + } + void CharacterControllerComponent::CreateController() { if (IsPhysicsEnabled()) @@ -399,9 +475,8 @@ namespace PhysX return; } - AzPhysics::SceneHandle defaultSceneHandle = AzPhysics::InvalidSceneHandle; - Physics::DefaultWorldBus::BroadcastResult(defaultSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); - if (defaultSceneHandle == AzPhysics::InvalidSceneHandle) + Physics::DefaultWorldBus::BroadcastResult(m_attachedSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); + if (m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) { AZ_Error("PhysX Character Controller Component", false, "Failed to retrieve default scene."); return; @@ -427,11 +502,9 @@ namespace PhysX auto* sceneInterface = AZ::Interface::Get(); if (sceneInterface != nullptr) { - m_controllerBodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, m_characterConfig.get()); - m_controller = azdynamic_cast( - sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, m_controllerBodyHandle)); + m_controllerBodyHandle = sceneInterface->AddSimulatedBody(m_attachedSceneHandle, m_characterConfig.get()); } - if (m_controller == nullptr) + if (m_controllerBodyHandle == AzPhysics::InvalidSimulatedBodyHandle) { AZ_Error("PhysX Character Controller Component", false, "Failed to create character controller."); return; @@ -447,7 +520,7 @@ namespace PhysX DestroyController(); } }); - sceneInterface->RegisterSimulationBodyRemovedHandler(defaultSceneHandle, m_onSimulatedBodyRemovedHandler); + sceneInterface->RegisterSimulationBodyRemovedHandler(m_attachedSceneHandle, m_onSimulatedBodyRemovedHandler); } CharacterControllerRequestBus::Handler::BusConnect(GetEntityId()); @@ -467,24 +540,23 @@ namespace PhysX void CharacterControllerComponent::DisableController() { - if (!IsPhysicsEnabled()) + if (auto* controller = GetController()) { - return; - } + controller->DisablePhysics(); - m_controller->DisablePhysics(); + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->RemoveSimulatedBody(m_attachedSceneHandle, controller->m_bodyHandle); + } - if (auto* sceneInterface = AZ::Interface::Get()) - { - sceneInterface->RemoveSimulatedBody(m_controller->m_sceneOwner, m_controller->m_bodyHandle); + DestroyController(); } - - DestroyController(); } void CharacterControllerComponent::DestroyController() { - m_controller = nullptr; + m_controllerBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; + m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; m_preSimulateHandler.Disconnect(); m_onSimulatedBodyRemovedHandler.Disconnect(); CharacterControllerRequestBus::Handler::BusDisconnect(); diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h index 7c25312b72..54513d52f4 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h @@ -131,6 +131,8 @@ namespace PhysX void ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) override; private: + const PhysX::CharacterController* GetControllerConst() const; + PhysX::CharacterController* GetController(); // Creates the physics character controller in the current default physics scene. // This will do nothing if the controller is already created. void CreateController(); @@ -143,8 +145,8 @@ namespace PhysX AZStd::unique_ptr m_characterConfig; AZStd::shared_ptr m_shapeConfig; - PhysX::CharacterController* m_controller = nullptr; AzPhysics::SimulatedBodyHandle m_controllerBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; + AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; AzPhysics::SystemEvents::OnPresimulateEvent::Handler m_preSimulateHandler; AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler m_onSimulatedBodyRemovedHandler; }; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 4c58187fb8..8da512647f 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -170,63 +170,88 @@ namespace PhysX // RagdollPhysicsBus void RagdollComponent::EnableSimulation(const Physics::RagdollState& initialState) { - m_ragdoll->EnableSimulation(initialState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->EnableSimulation(initialState); + } } void RagdollComponent::EnableSimulationQueued(const Physics::RagdollState& initialState) { - m_ragdoll->EnableSimulationQueued(initialState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->EnableSimulationQueued(initialState); + } } void RagdollComponent::DisableSimulation() { - if (m_ragdoll) + if (auto* ragdoll = GetPhysXRagdoll()) { - m_ragdoll->DisableSimulation(); + ragdoll->DisableSimulation(); } } void RagdollComponent::DisableSimulationQueued() { - if (m_ragdoll) + if (auto* ragdoll = GetPhysXRagdoll()) { - m_ragdoll->DisableSimulationQueued(); + ragdoll->DisableSimulationQueued(); } } Physics::Ragdoll* RagdollComponent::GetRagdoll() { - return m_ragdoll; + return GetPhysXRagdoll(); } void RagdollComponent::GetState(Physics::RagdollState& ragdollState) const { - m_ragdoll->GetState(ragdollState); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + ragdoll->GetState(ragdollState); + } } void RagdollComponent::SetState(const Physics::RagdollState& ragdollState) { - m_ragdoll->SetState(ragdollState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->SetState(ragdollState); + } } void RagdollComponent::SetStateQueued(const Physics::RagdollState& ragdollState) { - m_ragdoll->SetStateQueued(ragdollState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->SetStateQueued(ragdollState); + } } void RagdollComponent::GetNodeState(size_t nodeIndex, Physics::RagdollNodeState& nodeState) const { - m_ragdoll->GetNodeState(nodeIndex, nodeState); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + ragdoll->GetNodeState(nodeIndex, nodeState); + } } void RagdollComponent::SetNodeState(size_t nodeIndex, const Physics::RagdollNodeState& nodeState) { - m_ragdoll->SetNodeState(nodeIndex, nodeState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->SetNodeState(nodeIndex, nodeState); + } } Physics::RagdollNode* RagdollComponent::GetNode(size_t nodeIndex) const { - return m_ragdoll->GetNode(nodeIndex); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + return ragdoll->GetNode(nodeIndex); + } + return nullptr; } void RagdollComponent::EnablePhysics() @@ -245,14 +270,19 @@ namespace PhysX bool RagdollComponent::IsPhysicsEnabled() const { - return m_ragdoll && m_ragdoll->IsSimulated(); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + return ragdoll->IsSimulated(); + } + return false; + } AZ::Aabb RagdollComponent::GetAabb() const { - if (m_ragdoll) + if (const auto* ragdoll = GetPhysXRagdollConst()) { - return m_ragdoll->GetAabb(); + return ragdoll->GetAabb(); } return AZ::Aabb::CreateNull(); } @@ -264,18 +294,14 @@ namespace PhysX AzPhysics::SimulatedBodyHandle RagdollComponent::GetSimulatedBodyHandle() const { - if (m_ragdoll) - { - return m_ragdoll->m_bodyHandle; - } - return AzPhysics::InvalidSimulatedBodyHandle; + return m_ragdollHandle; } AzPhysics::SceneQueryHit RagdollComponent::RayCast(const AzPhysics::RayCastRequest& request) { - if (m_ragdoll) + if (auto* ragdoll = GetPhysXRagdoll()) { - return m_ragdoll->RayCast(request); + return ragdoll->RayCast(request); } return AzPhysics::SceneQueryHit(); } @@ -323,23 +349,24 @@ namespace PhysX AZ::TransformBus::EventResult(entityTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); ragdollConfiguration.m_initialState = GetBindPoseWorld(bindPose, entityTransform); - AzPhysics::SceneHandle defaultSceneHandle = AzPhysics::InvalidSceneHandle; - Physics::DefaultWorldBus::BroadcastResult(defaultSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); + m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; + Physics::DefaultWorldBus::BroadcastResult(m_attachedSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); if (auto* sceneInterface = AZ::Interface::Get()) { - AzPhysics::SimulatedBodyHandle bodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, &ragdollConfiguration); - m_ragdoll = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, bodyHandle)); + m_ragdollHandle = sceneInterface->AddSimulatedBody(m_attachedSceneHandle, &ragdollConfiguration); } - if (m_ragdoll == nullptr) + auto* ragdoll = GetPhysXRagdoll(); + if (ragdoll == nullptr || + m_ragdollHandle == AzPhysics::InvalidSimulatedBodyHandle) { AZ_Error("PhysX Ragdoll Component", false, "Failed to create ragdoll."); return; } - + for (size_t nodeIndex = 0; nodeIndex < numNodes; nodeIndex++) { - if (physx::PxRigidDynamic* pxRigidBody = m_ragdoll->GetPxRigidDynamic(nodeIndex)) + if (physx::PxRigidDynamic* pxRigidBody = ragdoll->GetPxRigidDynamic(nodeIndex)) { pxRigidBody->setSolverIterationCounts(m_positionIterations, m_velocityIterations); } @@ -352,7 +379,7 @@ namespace PhysX for (size_t nodeIndex = 0; nodeIndex < numNodes; nodeIndex++) { - if (const AZStd::shared_ptr& joint = m_ragdoll->GetNode(nodeIndex)->GetJoint()) + if (const AZStd::shared_ptr& joint = ragdoll->GetNode(nodeIndex)->GetJoint()) { if (auto* pxJoint = static_cast(joint->GetNativePointer())) { @@ -374,20 +401,41 @@ namespace PhysX void RagdollComponent::DestroyRagdoll() { - if (m_ragdoll) + if (m_ragdollHandle != AzPhysics::InvalidSimulatedBodyHandle && + m_attachedSceneHandle != AzPhysics::InvalidSceneHandle) { AzFramework::RagdollPhysicsRequestBus::Handler::BusDisconnect(); - AzFramework::RagdollPhysicsNotificationBus::Event(GetEntityId(), - &AzFramework::RagdollPhysicsNotifications::OnRagdollDeactivated); + AzFramework::RagdollPhysicsNotificationBus::Event( + GetEntityId(), &AzFramework::RagdollPhysicsNotifications::OnRagdollDeactivated); if (auto* sceneInterface = AZ::Interface::Get()) { - sceneInterface->RemoveSimulatedBody(m_ragdoll->m_sceneOwner, m_ragdoll->m_bodyHandle); + sceneInterface->RemoveSimulatedBody(m_attachedSceneHandle, m_ragdollHandle); + m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; } - m_ragdoll = nullptr; } } + Ragdoll* RagdollComponent::GetPhysXRagdoll() + { + return const_cast(static_cast(*this).GetPhysXRagdollConst()); + } + + const Ragdoll* RagdollComponent::GetPhysXRagdollConst() const + { + if (m_ragdollHandle == AzPhysics::InvalidSimulatedBodyHandle || + m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) + { + return nullptr; + } + + if (auto* sceneInterface = AZ::Interface::Get()) + { + return azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_ragdollHandle)); + } + return nullptr; + } + // deprecated Cry functions void RagdollComponent::EnterRagdoll() { diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h index a02e9c47fb..2c265b0f73 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h @@ -104,10 +104,13 @@ namespace PhysX private: void CreateRagdoll(const Physics::RagdollConfiguration& ragdollConfiguration); void DestroyRagdoll(); + Ragdoll* GetPhysXRagdoll(); + const Ragdoll* GetPhysXRagdollConst() const; bool IsJointProjectionVisible(); - Ragdoll* m_ragdoll; + AzPhysics::SimulatedBodyHandle m_ragdollHandle = AzPhysics::InvalidSimulatedBodyHandle; + AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; /// Minimum number of position iterations to perform in the PhysX solver. /// Lower iteration counts are less expensive but may behave less realistically. AZ::u32 m_positionIterations = 16; diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp index 3987936b8c..430ef7aef3 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp @@ -196,7 +196,7 @@ namespace PhysX AZ::Transform::CreateFromQuaternionAndTranslation(colliderConfiguration.m_rotation, colliderConfiguration.m_position); AZ::Transform shapeTransform = *m_transform; - shapeTransform.ExtractScale(); + shapeTransform.ExtractUniformScale(); shapeTransform = existingTransform * shapeTransform; diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp index 7069f9a05d..3aeb3ad797 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp @@ -328,7 +328,7 @@ namespace PhysX physx::PxMeshMidPhase::Enum ret = physx::PxMeshMidPhase::eBVH34; // Fallback to 3.3 on Android and iOS platforms since they don't support SSE2, which is required for 3.4 - if (platformIdentifier == "es3" || platformIdentifier == "ios") + if (platformIdentifier == "android" || platformIdentifier == "ios") { ret = physx::PxMeshMidPhase::eBVH33; } diff --git a/Gems/PhysX/Code/Source/RigidBodyComponent.cpp b/Gems/PhysX/Code/Source/RigidBodyComponent.cpp index f770f8408c..40cac1e19e 100644 --- a/Gems/PhysX/Code/Source/RigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/RigidBodyComponent.cpp @@ -201,9 +201,8 @@ namespace PhysX AZ::Quaternion newRotation = AZ::Quaternion::CreateIdentity(); m_interpolator->GetInterpolated(newPosition, newRotation, deltaTime); - AZ::Transform interpolatedTransform = AZ::Transform::CreateFromQuaternionAndTranslation(newRotation, newPosition); - interpolatedTransform.MultiplyByScale(m_initialScale); - AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTM, interpolatedTransform); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldRotationQuaternion, newRotation); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTranslation, newPosition); } } @@ -257,12 +256,8 @@ namespace PhysX } else { - // Maintain scale (this must be precise). - AZ::Transform entityTransform = AZ::Transform::Identity(); - AZ::TransformBus::EventResult(entityTransform, GetEntityId(), &AZ::TransformInterface::GetWorldTM); - transform.MultiplyByScale(m_initialScale); - - AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTM, transform); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldRotationQuaternion, rigidBody->GetOrientation()); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTranslation, rigidBody->GetPosition()); } m_isLastMovementFromKinematicSource = false; } @@ -353,8 +348,6 @@ namespace PhysX m_interpolator = std::make_unique(); m_interpolator->Reset(transform.GetTranslation(), rotation); - m_initialScale = transform.ExtractScale(); - Physics::RigidBodyNotificationBus::Event(GetEntityId(), &Physics::RigidBodyNotificationBus::Events::OnPhysicsEnabled); } diff --git a/Gems/PhysX/Code/Source/RigidBodyComponent.h b/Gems/PhysX/Code/Source/RigidBodyComponent.h index 7b2a34cf37..07fa4fb847 100644 --- a/Gems/PhysX/Code/Source/RigidBodyComponent.h +++ b/Gems/PhysX/Code/Source/RigidBodyComponent.h @@ -161,7 +161,6 @@ namespace PhysX AzPhysics::SimulatedBodyHandle m_rigidBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; - AZ::Vector3 m_initialScale = AZ::Vector3::CreateOne(); bool m_staticTransformAtActivation = false; ///< Whether the transform was static when the component last activated. bool m_isLastMovementFromKinematicSource = false; ///< True when the source of the movement comes from SetKinematicTarget as opposed to coming from a Transform change bool m_rigidBodyTransformNeedsUpdateOnPhysReEnable = false; ///< True if rigid body transform needs to be synced to the entity's when physics is re-enabled diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index a85426d532..55be7c92f7 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -718,7 +718,7 @@ namespace PhysX const float boundsInflationFactor = 1.0f; AZ::Transform overallTransformNoScale = GetColliderWorldTransform(worldTransform, colliderConfiguration.m_position, colliderConfiguration.m_rotation); - overallTransformNoScale.ExtractScale(); + overallTransformNoScale.ExtractUniformScale(); const physx::PxBounds3 bounds = physx::PxGeometryQuery::getWorldBounds(geometryHolder.any(), PxMathConvert(overallTransformNoScale), boundsInflationFactor); @@ -1378,7 +1378,7 @@ namespace PhysX AZ::TransformBus::EventResult(worldTransformWithoutScale , entityId , &AZ::TransformInterface::GetWorldTM); - worldTransformWithoutScale.ExtractScale(); + worldTransformWithoutScale.ExtractUniformScale(); return worldTransformWithoutScale; } @@ -1386,10 +1386,10 @@ namespace PhysX const AZ::Transform& entityWorldTransform) { AZ::Transform jointWorldTransformWithoutScale = jointWorldTransform; - jointWorldTransformWithoutScale.ExtractScale(); + jointWorldTransformWithoutScale.ExtractUniformScale(); AZ::Transform entityWorldTransformWithoutScale = entityWorldTransform; - entityWorldTransformWithoutScale.ExtractScale(); + entityWorldTransformWithoutScale.ExtractUniformScale(); AZ::Transform entityWorldTransformInverse = entityWorldTransformWithoutScale.GetInverse(); return entityWorldTransformInverse * jointWorldTransformWithoutScale; @@ -1399,10 +1399,10 @@ namespace PhysX const AZ::Transform& entityWorldTransform) { AZ::Transform jointLocalTransformWithoutScale = jointLocalTransform; - jointLocalTransformWithoutScale.ExtractScale(); + jointLocalTransformWithoutScale.ExtractUniformScale(); AZ::Transform entityWorldTransformWithoutScale = entityWorldTransform; - entityWorldTransformWithoutScale.ExtractScale(); + entityWorldTransformWithoutScale.ExtractUniformScale(); return entityWorldTransformWithoutScale * jointLocalTransformWithoutScale; } diff --git a/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp b/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp index 25747a55f6..19d069331a 100644 --- a/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp +++ b/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp @@ -68,7 +68,7 @@ namespace PhysXEditorTests AZ::EntityId editorId = editorEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.5f)); + worldTM.SetUniformScale(1.5f); worldTM.SetTranslation(AZ::Vector3(5.0f, 6.0f, 7.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(30.0f))); AZ::TransformBus::Event(editorId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -99,7 +99,7 @@ namespace PhysXEditorTests AZ::EntityId editorId = editorEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.5f)); + worldTM.SetUniformScale(1.5f); worldTM.SetTranslation(AZ::Vector3(5.0f, 6.0f, 7.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(30.0f))); AZ::TransformBus::Event(editorId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -144,7 +144,7 @@ namespace PhysXEditorTests AZ::EntityId capsuleId = editorEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(0.5f)); + worldTM.SetUniformScale(0.5f); worldTM.SetTranslation(AZ::Vector3(3.0f, 1.0f, -4.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationY(AZ::DegToRad(90.0f))); AZ::TransformBus::Event(capsuleId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -176,7 +176,7 @@ namespace PhysXEditorTests AZ::EntityId capsuleId = editorEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(0.5f)); + worldTM.SetUniformScale(0.5f); worldTM.SetTranslation(AZ::Vector3(3.0f, 1.0f, -4.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationY(AZ::DegToRad(90.0f))); AZ::TransformBus::Event(capsuleId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -222,7 +222,7 @@ namespace PhysXEditorTests AZ::EntityId sphereId = editorEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.2f)); + worldTM.SetUniformScale(1.2f); worldTM.SetTranslation(AZ::Vector3(-2.0f, -1.0f, 3.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f))); AZ::TransformBus::Event(sphereId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -254,7 +254,7 @@ namespace PhysXEditorTests AZ::EntityId sphereId = editorEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.2f)); + worldTM.SetUniformScale(1.2f); worldTM.SetTranslation(AZ::Vector3(-2.0f, -1.0f, 3.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f))); AZ::TransformBus::Event(sphereId, &AZ::TransformBus::Events::SetWorldTM, worldTM); diff --git a/Gems/PhysX/Code/Tests/DebugDrawTests.cpp b/Gems/PhysX/Code/Tests/DebugDrawTests.cpp index 5b41c37f34..610ba8d6f6 100644 --- a/Gems/PhysX/Code/Tests/DebugDrawTests.cpp +++ b/Gems/PhysX/Code/Tests/DebugDrawTests.cpp @@ -32,7 +32,7 @@ namespace PhysXEditorTests AZ::EntityId boxId = boxEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.5f)); + worldTM.SetUniformScale(1.5f); worldTM.SetTranslation(AZ::Vector3(5.0f, 6.0f, 7.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(30.0f))); AZ::TransformBus::Event(boxId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -61,7 +61,7 @@ namespace PhysXEditorTests AZ::EntityId boxId = boxEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.2f)); + worldTM.SetUniformScale(1.2f); worldTM.SetTranslation(AZ::Vector3(4.0f, -3.0f, 1.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(45.0f))); AZ::TransformBus::Event(boxId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -91,7 +91,7 @@ namespace PhysXEditorTests AZ::EntityId boxId = boxEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.2f)); + worldTM.SetUniformScale(1.2f); worldTM.SetTranslation(AZ::Vector3(4.0f, -3.0f, 1.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(45.0f))); AZ::TransformBus::Event(boxId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -129,7 +129,7 @@ namespace PhysXEditorTests AZ::EntityId capsuleId = capsuleEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(0.5f)); + worldTM.SetUniformScale(0.5f); worldTM.SetTranslation(AZ::Vector3(3.0f, 1.0f, -4.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationY(AZ::DegToRad(90.0f))); AZ::TransformBus::Event(capsuleId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -158,7 +158,7 @@ namespace PhysXEditorTests AZ::EntityId capsuleId = capsuleEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.4f)); + worldTM.SetUniformScale(1.4f); worldTM.SetTranslation(AZ::Vector3(1.0f, -4.0f, 4.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(45.0f))); AZ::TransformBus::Event(capsuleId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -189,7 +189,7 @@ namespace PhysXEditorTests AZ::EntityId sphereId = sphereEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(1.2f)); + worldTM.SetUniformScale(1.2f); worldTM.SetTranslation(AZ::Vector3(-2.0f, -1.0f, 3.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f))); AZ::TransformBus::Event(sphereId, &AZ::TransformBus::Events::SetWorldTM, worldTM); @@ -218,7 +218,7 @@ namespace PhysXEditorTests AZ::EntityId sphereId = sphereEntity->GetId(); AZ::Transform worldTM; - worldTM.SetScale(AZ::Vector3(0.8f)); + worldTM.SetUniformScale(0.8f); worldTM.SetTranslation(AZ::Vector3(2.0f, -1.0f, 3.0f)); worldTM.SetRotation(AZ::Quaternion::CreateRotationY(AZ::DegToRad(45.0f))); AZ::TransformBus::Event(sphereId, &AZ::TransformBus::Events::SetWorldTM, worldTM); diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp index 043bd60acd..c2e0bbbf5e 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp @@ -93,39 +93,43 @@ namespace PhysXEditorTests editorEntity->CreateComponent(); editorEntity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId); editorEntity->Activate(); - + { - UnitTest::ErrorHandler warningHandler("Negative or zero cylinder dimensions are invalid"); + UnitTest::ErrorHandler dimensionWarningHandler("Negative or zero cylinder dimensions are invalid"); + UnitTest::ErrorHandler colliderWarningHandler("No Collider or Shape information found when creating Rigid body"); LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(), &LmbrCentral::CylinderShapeComponentRequests::SetRadius, radius); // expect 2 warnings //1 if the radius is invalid //2 when re-creating the underlying simulated body - int expectedWarningCount = radius <= 0.f ? 2 : 0; - EXPECT_EQ(warningHandler.GetWarningCount(), expectedWarningCount); + int expectedWarningCount = radius <= 0.f ? 1 : 0; + EXPECT_EQ(dimensionWarningHandler.GetExpectedWarningCount(), expectedWarningCount); + EXPECT_EQ(colliderWarningHandler.GetExpectedWarningCount(), expectedWarningCount); } - + { - UnitTest::ErrorHandler warningHandler("Negative or zero cylinder dimensions are invalid"); + UnitTest::ErrorHandler dimensionWarningHandler("Negative or zero cylinder dimensions are invalid"); + UnitTest::ErrorHandler colliderWarningHandler("No Collider or Shape information found when creating Rigid body"); LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(), &LmbrCentral::CylinderShapeComponentRequests::SetHeight, height); // expect 2 warnings //1 if the radius or height is invalid //2 when re-creating the underlying simulated body - int expectedWarningCount = radius <= 0.f || height <= 0.f ? 2 : 0; - EXPECT_EQ(warningHandler.GetWarningCount(), expectedWarningCount); + int expectedWarningCount = radius <= 0.f || height <= 0.f ? 1 : 0; + EXPECT_EQ(dimensionWarningHandler.GetExpectedWarningCount(), expectedWarningCount); + EXPECT_EQ(colliderWarningHandler.GetExpectedWarningCount(), expectedWarningCount); } EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); - + // since there was no editor rigid body component, the runtime entity should have a static rigid body const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); - + PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); - + // there should be no shapes on the rigid body because the cylinder radius and/or height is invalid EXPECT_EQ(pxRigidStatic->getNbShapes(), 0); } diff --git a/Gems/PhysX/Code/Tests/RigidBodyComponentTests.cpp b/Gems/PhysX/Code/Tests/RigidBodyComponentTests.cpp index eee747d390..4ff88c0b3b 100644 --- a/Gems/PhysX/Code/Tests/RigidBodyComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/RigidBodyComponentTests.cpp @@ -38,8 +38,8 @@ namespace PhysXEditorTests const AZ::Aabb originalAabb = rigidBodyComponent->GetRigidBody()->GetAabb(); // Update the scale - const AZ::Vector3 scale(2.0f); - AZ::TransformBus::Event(editorEntity->GetId(), &AZ::TransformInterface::SetLocalScale, scale); + float scale = 2.0f; + AZ::TransformBus::Event(editorEntity->GetId(), &AZ::TransformInterface::SetLocalUniformScale, scale); // Trigger editor physics world update so EditorRigidBodyComponent can process scale change auto* physicsSystem = AZ::Interface::Get(); @@ -89,8 +89,8 @@ namespace PhysXEditorTests idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, offset); // Update the scale - const AZ::Vector3 scale(2.0f); - AZ::TransformBus::Event(editorEntity->GetId(), &AZ::TransformInterface::SetLocalScale, scale); + float scale = 2.0f; + AZ::TransformBus::Event(editorEntity->GetId(), &AZ::TransformInterface::SetLocalUniformScale, scale); // Update editor world to let updates to be applied physicsSystem->Simulate(0.1f); diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index b00e226078..c8ab158d77 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -241,7 +241,7 @@ namespace PhysXEditorTests SetPolygonPrismHeight(entityId, 2.0f); // update the transform scale and non-uniform scale - AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetScale, AZ::Vector3(2.0f)); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, 2.0f); AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3(0.5f, 1.5f, 2.0f)); EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); @@ -347,6 +347,7 @@ namespace PhysXEditorTests TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithUnsupportedShape_HandledGracefully) { UnitTest::ErrorHandler unsupportedShapeWarningHandler("Unsupported shape"); + UnitTest::ErrorHandler rigidBodyWarningHandler("No Collider or Shape information found when creating Rigid body"); // create an editor entity with a shape collider component and a cylinder shape component // the cylinder shape is not currently supported by the shape collider component @@ -355,10 +356,8 @@ namespace PhysXEditorTests editorEntity->CreateComponent(LmbrCentral::EditorCompoundShapeComponentTypeId); editorEntity->Activate(); - // expect 2 warnings - //1 raised for the unsupported shape - //2 when re-creating the underlying simulated body - EXPECT_EQ(unsupportedShapeWarningHandler.GetWarningCount(), 2); + EXPECT_EQ(unsupportedShapeWarningHandler.GetExpectedWarningCount(), 1); + EXPECT_EQ(rigidBodyWarningHandler.GetExpectedWarningCount(), 1); EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); @@ -435,8 +434,8 @@ namespace PhysXEditorTests &LmbrCentral::BoxShapeComponentRequests::GetBoxDimensions); // update the transform - const AZ::Vector3 scale(2.0f); - AZ::TransformBus::Event(editorEntityId, &AZ::TransformInterface::SetLocalScale, scale); + const float scale = 2.0f; + AZ::TransformBus::Event(editorEntityId, &AZ::TransformInterface::SetLocalUniformScale, scale); const AZ::Vector3 translation(10.0f, 20.0f, 30.0f); AZ::TransformBus::Event(editorEntityId, &AZ::TransformInterface::SetWorldTranslation, translation); @@ -527,10 +526,8 @@ namespace PhysXEditorTests editorParentEntity->Activate(); // set some scale to parent entity - const AZ::Vector3 parentScale(2.0f); - AZ::TransformBus::Event(editorParentEntity->GetId(), - &AZ::TransformInterface::SetLocalScale, - parentScale); + const float parentScale = 2.0f; + AZ::TransformBus::Event(editorParentEntity->GetId(), &AZ::TransformInterface::SetLocalUniformScale, parentScale); // create an editor child entity with a shape collider component and a box shape component EntityPtr editorChildEntity = CreateInactiveEditorEntity("ChildEntity"); diff --git a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings index c2fe2400cb..9da169c456 100644 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings +++ b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings @@ -17,7 +17,7 @@ - + @@ -81,7 +81,7 @@ - + diff --git a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings index c3632028c2..43410a50df 100644 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings +++ b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings @@ -17,7 +17,7 @@ - + @@ -81,7 +81,7 @@ - + diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index 2bb0d8aaf3..ace089b9bd 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace UnitTest @@ -172,6 +173,12 @@ namespace UnitTest void PrefabBuilderTests::SetUp() { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + AZ::ComponentApplication::Descriptor desc; m_app.Start(desc); m_app.CreateReflectionManager(); diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp index 5f96353996..2a89911fda 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +140,12 @@ class SceneBuilderPhasesFixture public: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp index 66fd717508..2287077c89 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,12 @@ class SceneBuilderTests protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); AZ::Debug::TraceMessageBus::Handler::BusConnect(); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp index 9c9297ec1c..6f28cbfe19 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp @@ -512,7 +512,7 @@ namespace ScriptCanvasEditor } } - bool Graph::SanityCheckNodeReplacement(ScriptCanvas::Node* oldNode, ScriptCanvas::Node* newNode, AZStd::unordered_map>& outSlotIdMap) + bool Graph::SanityCheckNodeReplacement(ScriptCanvas::Node* oldNode, ScriptCanvas::Node* newNode, ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport) { auto findReplacementMatch = [](const ScriptCanvas::Slot* oldSlot, const AZStd::vector& newSlots)->ScriptCanvas::SlotId { @@ -529,14 +529,14 @@ namespace ScriptCanvasEditor return {}; }; - oldNode->CustomizeReplacementNode(newNode, outSlotIdMap); - if (!newNode) { AZ_Warning("ScriptCanvas", false, "Replacement node can not be null."); return false; } + oldNode->CustomizeReplacementNode(newNode, nodeUpdateSlotReport.m_oldSlotsToNewSlots); + AZStd::unordered_map> slotNameMap = oldNode->GetReplacementSlotsMap(); const auto newSlots = newNode->GetAllSlots(); @@ -544,16 +544,19 @@ namespace ScriptCanvasEditor bool usingDefaults = true; size_t defaultMatchesFound = 0; + auto& oldSlotsToNewSlots = nodeUpdateSlotReport.m_oldSlotsToNewSlots; + for (auto oldSlot : oldSlots) { const ScriptCanvas::SlotId oldSlotId = oldSlot->GetId(); const AZStd::string oldSlotName = oldSlot->GetName(); - auto slotIdsIter = outSlotIdMap.find(oldSlotId); + + auto slotIdsIter = oldSlotsToNewSlots.find(oldSlotId); auto slotNamesIter = slotNameMap.find(oldSlotName); // For old node slot remapping, we should get: // 1. if old slot name is not static, we should find the mapping in user provided slot id map // 2. if old slot name is static, we should find the mapping in codegen generated map (case 1 can override case 2) - if (slotIdsIter != outSlotIdMap.end()) + if (slotIdsIter != oldSlotsToNewSlots.end()) { for (auto newSlotId : slotIdsIter->second) { @@ -581,9 +584,10 @@ namespace ScriptCanvasEditor if (!newSlotName.empty()) { auto newSlot = newNode->GetSlotByName(newSlotName); + if (!newSlot) { - AZ_Warning("ScriptCanvas", false, "Failed to find slot with name %s in replacement Node(%s).", newSlotName.c_str(), newNode->GetNodeName().c_str()); + AZ_Warning("ScriptCanvas", false, "Failed to find slot with name %s in replacement Node (%s).", newSlotName.c_str(), newNode->GetNodeName().c_str()); return false; } else if (newSlot && oldSlot->GetType() != newSlot->GetType()) @@ -591,10 +595,11 @@ namespace ScriptCanvasEditor AZ_Warning("ScriptCanvas", false, "Failed to map deprecated Node (%s) Slot (%s) to replacement Node (%s) Slot (%s).", oldNode->GetNodeName().c_str(), oldSlot->GetName().c_str(), newNode->GetNodeName().c_str(), newSlot->GetName().c_str()); return false; } + newSlotIds.push_back(newSlot->GetId()); } } - outSlotIdMap.emplace(oldSlot->GetId(), newSlotIds); + oldSlotsToNewSlots.emplace(oldSlot->GetId(), newSlotIds); } else if (slotNameMap.empty()) { @@ -605,7 +610,7 @@ namespace ScriptCanvasEditor { ++defaultMatchesFound; AZStd::vector slotIds{ newSlotId }; - outSlotIdMap.emplace(oldSlot->GetId(), slotIds); + oldSlotsToNewSlots.emplace(oldSlot->GetId(), slotIds); } } else @@ -615,7 +620,7 @@ namespace ScriptCanvasEditor } } - if (usingDefaults && defaultMatchesFound != oldSlots.size()) + if (usingDefaults && oldSlotsToNewSlots.size() != oldSlots.size()) { AZ_Warning("ScriptCanvas", false, "Failed to remap deprecated Node(%s) not all old slots were present in the new node.", oldNode->GetNodeName().c_str()); } @@ -690,8 +695,10 @@ namespace ScriptCanvasEditor } } - AZ::Outcome Graph::ReplaceNodeByConfig(ScriptCanvas::Node* oldNode, const ScriptCanvas::NodeConfiguration& nodeConfig, - ScriptCanvas::ReplacementConnectionMap& remapConnections) + AZ::Outcome Graph::ReplaceNodeByConfig + ( ScriptCanvas::Node* oldNode + , const ScriptCanvas::NodeConfiguration& nodeConfig + , ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport) { auto nodeEntity = oldNode->GetEntity(); if (!nodeEntity) @@ -731,8 +738,8 @@ namespace ScriptCanvasEditor AddNode(newNode->GetEntityId()); ScriptCanvas::NodeUtils::InitializeNode(newNode, nodeConfig); - AZStd::unordered_map> slotIdMap; - rollbackRequired = !SanityCheckNodeReplacement(oldNode, newNode, slotIdMap); + rollbackRequired = !SanityCheckNodeReplacement(oldNode, newNode, nodeUpdateSlotReport); + auto& slotIdMap = nodeUpdateSlotReport.m_oldSlotsToNewSlots; if (rollbackRequired) { @@ -748,25 +755,15 @@ namespace ScriptCanvasEditor else { nodeEntity->Activate(); - newNode->SignalReconfigurationBegin(); - newNode->SetNodeDisabledFlag(oldNode->GetNodeDisabledFlag()); + for (auto slotIdIter : slotIdMap) { ScriptCanvas::Slot* oldSlot = oldNode->GetSlot(slotIdIter.first); const ScriptCanvas::Endpoint oldEndpoint{ nodeEntity->GetId(), oldSlot->GetId() }; if (slotIdIter.second.size() == 0) { - // If remap id is empty, then we should just cache the old slot connection for delete - if (oldSlot->IsInput()) - { - ScriptCanvas::VersioningUtils::CreateRemapConnectionsForTargetEndpoint(*this, oldEndpoint, ScriptCanvas::Endpoint(), remapConnections); - } - else - { - ScriptCanvas::VersioningUtils::CreateRemapConnectionsForSourceEndpoint(*this, oldEndpoint, ScriptCanvas::Endpoint(), remapConnections); - } continue; } @@ -808,35 +805,11 @@ namespace ScriptCanvasEditor ScriptCanvas::VersioningUtils::CopyOldValueToDataSlot(newSlot, oldSlot->GetVariableReference(), oldSlot->FindDatum()); } - - // if old slot is visible, we need to check its connections for remapping - if (oldSlot->IsVisible()) - { - ScriptCanvas::Endpoint newEndpoint; - if (newSlot) - { - newEndpoint = { nodeEntity->GetId(), newSlot->GetId() }; - } - else - { - AZ_Warning("ScriptCanvas", false, "Invalid slot! Unable to create new connection for Node (%s).", newNode->GetNodeName().c_str()); - } - - if (oldSlot->IsInput()) - { - ScriptCanvas::VersioningUtils::CreateRemapConnectionsForTargetEndpoint(*this, oldEndpoint, newEndpoint, remapConnections); - } - else - { - ScriptCanvas::VersioningUtils::CreateRemapConnectionsForSourceEndpoint(*this, oldEndpoint, newEndpoint, remapConnections); - } - } } } - delete oldNode; + delete oldNode; newNode->SignalReconfigurationEnd(); - return AZ::Success(newNode); } } @@ -3634,8 +3607,7 @@ namespace ScriptCanvasEditor AZStd::unordered_map< AZ::EntityId, AZ::EntityId > scriptCanvasToGraphCanvasMapping; - bool graphNeedsDirtying = false; - + bool graphNeedsDirtying = !GetVersion().IsLatest(); { QScopedValueRollback ignoreRequests(m_ignoreSaveRequests, true); @@ -3659,7 +3631,8 @@ namespace ScriptCanvasEditor AZStd::unordered_set deletedNodes; AZStd::unordered_set assetSanitizationSet; AZStd::unordered_set sanityCheckRequiredNodes; - ScriptCanvas::ReplacementConnectionMap remapConnections; + + ScriptCanvas::GraphUpdateSlotReport graphUpdateSlotReport; for (const AZ::EntityId& scriptCanvasNodeId : nodeList) { @@ -3674,12 +3647,15 @@ namespace ScriptCanvasEditor ScriptCanvas::NodeConfiguration nodeConfig = scriptCanvasNode->GetReplacementNodeConfiguration(); if (nodeConfig.IsValid()) { - auto nodeOutcome = ReplaceNodeByConfig(scriptCanvasNode, nodeConfig, remapConnections); + ScriptCanvas::NodeUpdateSlotReport nodeUpdateSlotReport; + auto nodeOutcome = ReplaceNodeByConfig(scriptCanvasNode, nodeConfig, nodeUpdateSlotReport); + if (nodeOutcome.IsSuccess()) { graphNeedsDirtying = true; scriptCanvasNode = nodeOutcome.GetValue(); m_updateStrings.insert(AZStd::string::format("Replaced node (%s)", scriptCanvasNode->GetNodeName().c_str())); + ScriptCanvas::MergeUpdateSlotReport(scriptCanvasNodeId, graphUpdateSlotReport, nodeUpdateSlotReport); } } } @@ -3688,7 +3664,6 @@ namespace ScriptCanvasEditor scriptCanvasToGraphCanvasMapping[scriptCanvasNodeId] = graphCanvasNodeId; auto saveDataIter2 = m_graphCanvasSaveData.find(scriptCanvasNodeId); - if (saveDataIter2 != m_graphCanvasSaveData.end()) { GraphCanvas::EntitySaveDataRequestBus::Event(graphCanvasNodeId, &GraphCanvas::EntitySaveDataRequests::ReadSaveData, (*saveDataIter2->second)); @@ -3699,7 +3674,7 @@ namespace ScriptCanvasEditor GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, graphCanvasNodeId, position, false); - // If the node is deprecated, we want to stomp whatever style it had saved and apply the deperecated style + // If the node is deprecated, we want to stomp whatever style it had saved and apply the deprecated style if (scriptCanvasNode->IsDeprecated()) { GraphCanvas::NodeTitleRequestBus::Event(graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::SetPaletteOverride, "DeprecatedNodeTitlePalette"); @@ -3719,26 +3694,12 @@ namespace ScriptCanvasEditor } } - // Remap connections step should be done before editor processing connections for graph - // - // Delete underlying data conenections. - for (auto remapConnection : remapConnections) - { - RemoveConnection(remapConnection.first); - } - - // Recreate connections in a separate pass to avoid triggering display updates for invalid slot ids. - for (auto remapConnection : remapConnections) + if (!graphUpdateSlotReport.IsEmpty()) { - for (auto newEndpointPair : remapConnection.second) - { - if (newEndpointPair.first.IsValid() && newEndpointPair.second.IsValid()) - { - ConnectByEndpoint(newEndpointPair.first, newEndpointPair.second); - } - } + // currently, it is expected that there are no deleted old slots, those need manual correction + AZ_Error("ScriptCanvas", graphUpdateSlotReport.m_deletedOldSlots.empty(), "Graph upgrade path: If old slots are deleted, manual upgrading is required"); + UpdateConnectionStatus(*this, graphUpdateSlotReport); } - //// AZStd::unordered_set graphCanvasNodesToDelete; diff --git a/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp b/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp index 7762e1d095..581c3775d6 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp @@ -424,24 +424,11 @@ namespace ScriptCanvasEditor EditorGraphUpgradeMachine* sm = GetStateMachine(); auto* graph = sm->m_graph; - // Delete underlying data connections. - for (auto remapConnection : sm->m_replacementConnections) + if (!sm->m_updateReport.IsEmpty()) { - graph->RemoveConnection(remapConnection.first); - } - - // Recreate connections in a separate pass to avoid triggering display updates for invalid slot ids. - for (auto remapConnection : sm->m_replacementConnections) - { - for (auto newEndpointPair : remapConnection.second) - { - if (newEndpointPair.first.IsValid() && newEndpointPair.second.IsValid()) - { - graph->ConnectByEndpoint(newEndpointPair.first, newEndpointPair.second); - - Log("Replaced Connection: %s\n", Helpers::ConnectionToText(graph, newEndpointPair.first, newEndpointPair.second).c_str()); - } - } + // currently, it is expected that there are no deleted old slots, those need manual correction + AZ_Error("ScriptCanvas", sm->m_updateReport.m_deletedOldSlots.empty(), "Graph upgrade path: If old slots are deleted, manual upgrading is required"); + UpdateConnectionStatus(*graph, sm->m_updateReport); } } @@ -455,16 +442,18 @@ namespace ScriptCanvasEditor ScriptCanvas::NodeConfiguration nodeConfig = node->GetReplacementNodeConfiguration(); if (nodeConfig.IsValid()) { - auto nodeOutcome = graph->ReplaceNodeByConfig(node, nodeConfig, sm->m_replacementConnections); + ScriptCanvas::NodeUpdateSlotReport nodeUpdateSlotReport; + auto nodeOutcome = graph->ReplaceNodeByConfig(node, nodeConfig, nodeUpdateSlotReport); if (nodeOutcome.IsSuccess()) { + ScriptCanvas::MergeUpdateSlotReport(node->GetEntityId(), sm->m_updateReport, nodeUpdateSlotReport); + sm->m_allNodes.erase(node); sm->m_outOfDateNodes.erase(node); sm->m_sanityCheckRequiredNodes.erase(node); - sm->m_graphNeedsDirtying = true; - auto replacedNode = nodeOutcome.GetValue(); + auto replacedNode = nodeOutcome.GetValue(); sm->m_allNodes.insert(replacedNode); if (replacedNode->IsOutOfDate(graph->GetVersion())) diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h index 3bd8270629..687d783e63 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h @@ -42,6 +42,7 @@ namespace ScriptCanvas { struct NodeConfiguration; + struct NodeUpdateSlotReport; } namespace ScriptCanvasEditor @@ -361,8 +362,8 @@ namespace ScriptCanvasEditor void HandleFunctionDefinitionExtension(ScriptCanvas::Node* node, GraphCanvas::SlotId graphCanvasSlotId, const GraphCanvas::NodeId& nodeId); //// Version Update code - AZ::Outcome ReplaceNodeByConfig(ScriptCanvas::Node*, const ScriptCanvas::NodeConfiguration&, ScriptCanvas::ReplacementConnectionMap&); - bool SanityCheckNodeReplacement(ScriptCanvas::Node*, ScriptCanvas::Node*, AZStd::unordered_map>&); + AZ::Outcome ReplaceNodeByConfig(ScriptCanvas::Node*, const ScriptCanvas::NodeConfiguration&, ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport); + bool SanityCheckNodeReplacement(ScriptCanvas::Node*, ScriptCanvas::Node*, ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport); bool m_allowVersionUpdate = false; AZStd::unordered_set< AZ::EntityId > m_queuedConvertingNodes; diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h index 791e649e3a..cc5315808e 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h @@ -159,7 +159,7 @@ namespace ScriptCanvasEditor AZStd::unordered_set m_deletedNodes; AZStd::unordered_set m_assetSanitizationSet; - ScriptCanvas::ReplacementConnectionMap m_replacementConnections; + ScriptCanvas::GraphUpdateSlotReport m_updateReport; AZStd::unordered_map< AZ::EntityId, AZ::EntityId > m_scriptCanvasToGraphCanvasMapping; diff --git a/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h b/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h index 74f363f569..06a63fe8b6 100644 --- a/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h +++ b/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h @@ -123,6 +123,11 @@ namespace ScriptCanvasEditor template AzToolsFramework::PropertyHandlerBase* RegisterGenericLineEditHandler(const EditCtrl::PropertyToStringCB& propertyToStringCB, const EditCtrl::StringToPropertyCB& stringToPropertyCB) { + if (!AzToolsFramework::PropertyTypeRegistrationMessages::Bus::FindFirstHandler()) + { + return nullptr; + } + auto propertyHandler(aznew GenericLineEditHandler(propertyToStringCB, stringToPropertyCB)); AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(&AzToolsFramework::PropertyTypeRegistrationMessages::RegisterPropertyType, propertyHandler); return propertyHandler; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja index 089e5fe6ff..fbf2bd5355 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja @@ -75,7 +75,7 @@ public: \ void ConfigureSlots() override; \ bool RequiresDynamicSlotOrdering() const override; \ bool IsDeprecated() const override; \ -{% if deprecationUuid is defined %} NodeConfiguration GetReplacementNodeConfiguration() const override; \ +{% if deprecationUuid is defined %} ScriptCanvas::NodeConfiguration GetReplacementNodeConfiguration() const override; \ {% endif %} using Node::FindDatum; \ {% if Class.attrib['GraphEntryPoint'] is defined %} bool IsEntryPoint() const override { return {%if Class.attrib['GraphEntryPoint'] == "True" %}true{%else%}false{%endif%}; } \ @@ -168,4 +168,4 @@ struct {{ className | replace(' ','') }}Property {% endfor %} -{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Connection.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Connection.h index 155e5bb4c2..e2cd7008c8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Connection.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Connection.h @@ -22,6 +22,7 @@ namespace ScriptCanvas { class Slot; + struct NodeUpdateSlotReport; class Connection : public AZ::Component @@ -64,6 +65,8 @@ namespace ScriptCanvas // GraphNotificationBus void OnNodeRemoved(const ID& nodeId) override; + void UpdateConnectionStatus(NodeUpdateSlotReport& report); + protected: //------------------------------------------------------------------------- static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Contracts/MethodOverloadContract.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Contracts/MethodOverloadContract.cpp index dd223f34ec..0f49fb8b5d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Contracts/MethodOverloadContract.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Contracts/MethodOverloadContract.cpp @@ -33,7 +33,7 @@ namespace ScriptCanvas { if (m_availableIndexes.empty()) { - return -1; + return std::numeric_limits::max(); } return (*m_availableIndexes.begin()); @@ -151,19 +151,22 @@ namespace ScriptCanvas for (const AZ::BehaviorParameter* behaviorParameter : paramTypes.second) { - ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(behaviorParameter->m_typeId); - if (ScriptCanvas::Data::IsValueType(dataType)) + if (behaviorParameter) { - isValueType = true; - } - else if (ScriptCanvas::Data::IsContainerType(dataType)) - { - isContainerType = true; - } + ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(behaviorParameter->m_typeId); + if (ScriptCanvas::Data::IsValueType(dataType)) + { + isValueType = true; + } + else if (ScriptCanvas::Data::IsContainerType(dataType)) + { + isContainerType = true; + } - if (isValueType && isContainerType) - { - break; + if (isValueType && isContainerType) + { + break; + } } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp index 8d24b67b4f..1637a2e71b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp @@ -616,6 +616,34 @@ namespace ScriptCanvas return false; } + + void Graph::RemoveAllConnections() + { + for (auto connectionEntity : m_graphData.m_connections) + { + if (auto connection = connectionEntity ? AZ::EntityUtils::FindFirstDerivedComponent(connectionEntity) : nullptr) + { + if (connection->GetSourceEndpoint().IsValid()) + { + EndpointNotificationBus::Event(connection->GetSourceEndpoint(), &EndpointNotifications::OnEndpointDisconnected, connection->GetTargetEndpoint()); + } + if (connection->GetTargetEndpoint().IsValid()) + { + EndpointNotificationBus::Event(connection->GetTargetEndpoint(), &EndpointNotifications::OnEndpointDisconnected, connection->GetSourceEndpoint()); + } + } + + GraphNotificationBus::Event(GetScriptCanvasId(), &GraphNotifications::OnConnectionRemoved, connectionEntity->GetId()); + } + + for (auto& connectionRef : m_graphData.m_connections) + { + delete connectionRef; + } + + m_graphData.m_connections.clear(); + } + bool Graph::RemoveConnection(const AZ::EntityId& connectionId) { if (connectionId.IsValid()) @@ -752,7 +780,6 @@ namespace ScriptCanvas auto* connectionEntity = aznew AZ::Entity("Connection"); connectionEntity->CreateComponent(sourceEndpoint, targetEndpoint); - AZ::Entity* nodeEntity{}; AZ::ComponentApplicationBus::BroadcastResult(nodeEntity, &AZ::ComponentApplicationRequests::FindEntity, sourceEndpoint.GetNodeId()); auto node = nodeEntity ? AZ::EntityUtils::FindFirstDerivedComponent(nodeEntity) : nullptr; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h index 1862ce4966..49b22db2bf 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h @@ -87,6 +87,7 @@ namespace ScriptCanvas Slot* FindSlot(const Endpoint& endpoint) const override; bool AddConnection(const AZ::EntityId&) override; + void RemoveAllConnections(); bool RemoveConnection(const AZ::EntityId& connectionId) override; AZStd::vector GetConnections() const override; AZStd::vector GetConnectedEndpoints(const Endpoint& firstEndpoint) const override; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h index e34bdb1c71..8426e90515 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h @@ -53,7 +53,9 @@ namespace ScriptCanvas template void AddDefaultInputAndOutputTypeSlot(DatumType&& defaultValue); void AddInputTypeAndOutputTypeSlot(const Data::Type& type); - + + bool IsDeprecated() const override { return true; } + void OnActivate() override; void OnInputChanged(const Datum& input, const SlotId& id) override; void MarkDefaultableInput() override {} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.cpp index d37427af0b..9784cc97c4 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.cpp @@ -347,6 +347,11 @@ namespace ScriptCanvas } } + void Slot::ClearDynamicGroup() + { + m_dynamicGroup = AZ::Crc32{}; + } + void Slot::ConvertToLatentExecutionOut() { if (IsExecution() && IsOutput()) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.h index c65d4efc84..794ce091b1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.h @@ -68,6 +68,8 @@ namespace ScriptCanvas void AddContract(const ContractDescriptor& contractDesc); + void ClearDynamicGroup(); + template T* FindContract() { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index 8dff6da9b1..732455857e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -1036,12 +1036,12 @@ namespace ScriptCanvas if (azrtti_istypeof(&node)) { // todo Add node to these errors - AddError(nullptr, ValidationConstPtr(aznew NotYetImplemented(node.GetEntityId(), AZStd::string::format("NodeableNodeOverloaded doesn't have enough data connected to select a valid overload: %s", node.GetDebugName().data())))); + AddError(nullptr, ValidationConstPtr(aznew Internal::ParseError(node.GetEntityId(), AZStd::string::format("%s: %s", ParseErrors::NodeableNodeOverloadAmbiguous, node.GetDebugName().data())))); } else { // todo Add node to these errors - AddError(nullptr, ValidationConstPtr(aznew NotYetImplemented(node.GetEntityId(), AZStd::string::format("NodeableNode did not construct its internal node: %s", node.GetDebugName().data())))); + AddError(nullptr, ValidationConstPtr(aznew Internal::ParseError(node.GetEntityId(), AZStd::string::format("%s: %s", ParseErrors::NodeableNodeDidNotConstructInternalNodeable, node.GetDebugName().data())))); } } } @@ -2535,7 +2535,7 @@ namespace ScriptCanvas if (IsInfiniteVariableWriteHandlingLoop(*this, variableHandling, variableHandling->m_function, true)) { - AddError(variableHandling->m_function, aznew NotYetImplemented(AZ::EntityId(), ScriptCanvas::ParseErrors::InfiniteLoopWritingToVariable)); + AddError(variableHandling->m_function, aznew Internal::ParseError(AZ::EntityId(), ScriptCanvas::ParseErrors::InfiniteLoopWritingToVariable)); return false; } @@ -3312,7 +3312,7 @@ namespace ScriptCanvas } else { - AddError(execution, aznew NotYetImplemented(execution->GetNodeId(), childOutSlotsOutcome.TakeError())); + AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), childOutSlotsOutcome.TakeError())); } } } @@ -3615,9 +3615,11 @@ namespace ScriptCanvas void AbstractCodeModel::ParseExecutionMultipleOutSyntaxSugar(ExecutionTreePtr execution, const EndpointsResolved& executionOutNodes, const AZStd::vector& outSlots) { + const auto executionNodeId = execution->GetId().m_node ? execution->GetId().m_node->GetEntityId() : AZ::EntityId(); + if (executionOutNodes.size() != outSlots.size()) { - AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarMismatchOutSize); + AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarMismatchOutSize); } if (execution->GetSymbol() != Symbol::Sequence) @@ -3630,13 +3632,13 @@ namespace ScriptCanvas if (!child) { - AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNullChildFound); + AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNullChildFound); return; } if (child->m_execution) { - AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNonNullChildExecutionFound); + AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNonNullChildExecutionFound); return; } @@ -3660,7 +3662,7 @@ namespace ScriptCanvas if (execution->GetChildrenCount() != executionOutNodes.size()) { - AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarChildExecutionRemovedAndNotReplaced); + AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarChildExecutionRemovedAndNotReplaced); return; } } @@ -4187,7 +4189,7 @@ namespace ScriptCanvas } else { - AddError(nullptr, aznew NotYetImplemented(execution->GetNodeId(), dataSlotsOutcome.TakeError())); + AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), dataSlotsOutcome.TakeError())); } } } @@ -4494,13 +4496,13 @@ namespace ScriptCanvas } else { - AddError(execution, aznew NotYetImplemented(execution->GetNodeId(), returnSlotsOutcome.TakeError())); + AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), returnSlotsOutcome.TakeError())); } } } else { - AddError(execution, aznew NotYetImplemented(execution->GetNodeId(), outputSlotsOutcome.TakeError())); + AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), outputSlotsOutcome.TakeError())); } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.cpp index e04f7c1725..cda93c1b36 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.cpp @@ -35,15 +35,6 @@ namespace ScriptCanvas } } - AZStd::unordered_map> ArithmeticExpression::GetReplacementSlotsMap() const - { - AZStd::unordered_map> slotsMap; - slotsMap.emplace(k_evaluateName, AZStd::vector{ "In" }); - slotsMap.emplace(k_outName, AZStd::vector{ "Out" }); - slotsMap.emplace(k_resultName, AZStd::vector{ "Result" }); - return slotsMap; - } - void ArithmeticExpression::OnInit() { { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.h index 351d02f2cc..7c230b359a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.h @@ -72,7 +72,6 @@ namespace ScriptCanvas bool IsDeprecated() const override { return true; } - AZStd::unordered_map> GetReplacementSlotsMap() const override; void CustomizeReplacementNode(Node* replacementNode, AZStd::unordered_map>& outSlotIdMap) const override; protected: diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.cpp index 0b7b6020e3..d730b6bcc9 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.cpp @@ -94,8 +94,6 @@ namespace ScriptCanvas void ForEach::OnInit() { - ResetLoop(); - if (!m_sourceSlot.IsValid()) { DynamicDataSlotConfiguration slotConfiguration; @@ -130,33 +128,6 @@ namespace ScriptCanvas EndpointNotificationBus::Handler::BusConnect({ GetEntityId(), m_sourceSlot }); } - void ForEach::OnInputSignal(const SlotId& slotId) - { - auto inSlotId = ForEachProperty::GetInSlotId(this); - if (slotId == inSlotId || slotId == SlotId{}) - { - if (slotId == inSlotId) - { - if (!InitializeLoop()) - { - // Loop initialization failed - SignalOutput(ForEachProperty::GetFinishedSlotId(this)); - return; - } - } - - if (!m_breakCalled) - { - Iterate(); - } - } - else if (slotId == ForEachProperty::GetBreakSlotId(this)) - { - m_breakCalled = true; - SignalOutput(ForEachProperty::GetFinishedSlotId(this)); - } - } - UpdateResult ForEach::OnUpdateNode() { if (auto continueSlot = GetSlotByNameAndType("Continue", CombinedSlotType::ExecutionIn)) @@ -167,161 +138,6 @@ namespace ScriptCanvas return UpdateResult::DirtyGraph; } - bool ForEach::InitializeLoop() - { - ResetLoop(); - - const Datum* input = FindDatum(m_sourceSlot); - - if (input && !input->Empty()) - { - if (!Data::IsContainerType(input->GetType())) - { - SCRIPTCANVAS_REPORT_ERROR((*this), "Iteration not supported on this type: %s", Data::GetName(m_sourceContainer.GetType()).c_str()); - return false; - } - - // Make a copy of the source datum - m_sourceContainer = *input; - - // Get the size of the container - auto sizeOutcome = BehaviorContextMethodHelper::CallMethodOnDatum(m_sourceContainer, "Size"); - - if (!sizeOutcome) - { - SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get size of container: %s", sizeOutcome.GetError().c_str()); - return false; - } - - Datum sizeResult = sizeOutcome.TakeValue(); - const size_t* sizePtr = sizeResult.GetAs(); - m_size = sizePtr ? *sizePtr : 0; - - if (Data::IsSetContainerType(m_sourceContainer.GetType()) || Data::IsMapContainerType(m_sourceContainer.GetType())) - { - // If it's a map or set, get the vector of keys - auto keysVectorOutcome = BehaviorContextMethodHelper::CallMethodOnDatum(m_sourceContainer, "GetKeys"); - - if (!keysVectorOutcome) - { - SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get vector of keys: %s", keysVectorOutcome.GetError().c_str()); - return false; - } - - m_keysVector = keysVectorOutcome.TakeValue(); - - // Check size of vector of keys for safety - auto keysSizeOutcome = BehaviorContextMethodHelper::CallMethodOnDatum(m_keysVector, "Size"); - - if (!keysSizeOutcome) - { - SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get size of vector of keys: %s", keysSizeOutcome.GetError().c_str()); - return false; - } - - Datum keysSizeResult = keysSizeOutcome.TakeValue(); - const size_t* keysSizePtr = keysSizeResult.GetAs(); - size_t keysSize = keysSizePtr ? *keysSizePtr : 0; - - if (m_size != keysSize) - { - // This shouldn't happen - SCRIPTCANVAS_REPORT_ERROR((*this), "Container size and vector of keys size mismatch."); - return false; - } - } - - return true; - } - - return false; - } - - void ForEach::Iterate() - { - if (m_sourceContainer.Empty() || m_index >= m_size) - { - SignalOutput(ForEachProperty::GetFinishedSlotId(this)); - return; - } - - Datum& container = Data::IsVectorContainerType(m_sourceContainer.GetType()) ? m_sourceContainer : m_keysVector; - - auto keyAtOutcome = BehaviorContextMethodHelper::CallMethodOnDatumUnpackOutcomeSuccess(container, "At", m_index); - - if (!keyAtOutcome) - { - SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get key in container: %s", keyAtOutcome.GetError().c_str()); - return; - } - - Datum keyAtResult = keyAtOutcome.TakeValue(); - - if (!SetPropertySlotData(keyAtResult, k_keySlotIndex)) - { - // Unable to set property slot - SCRIPTCANVAS_REPORT_ERROR((*this), "Unable to set one of the property slots on this node."); - SignalOutput(ForEachProperty::GetFinishedSlotId(this)); - return; - } - - if (Data::IsMapContainerType(m_sourceContainer.GetType())) - { - // If the container is a map, we want to get the value for the current key - auto valueAtOutcome = BehaviorContextMethodHelper::CallMethodOnDatumUnpackOutcomeSuccess(m_sourceContainer, "At", keyAtResult); - - if (!valueAtOutcome) - { - SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get value for key in container: %s", valueAtOutcome.GetError().c_str()); - return; - } - - Datum valueAtResult = valueAtOutcome.TakeValue(); - - if (!SetPropertySlotData(valueAtResult, k_valueSlotIndex)) - { - // Unable to set property slot - SCRIPTCANVAS_REPORT_ERROR((*this), "Unable to set one of the property slots on this node."); - SignalOutput(ForEachProperty::GetFinishedSlotId(this)); - return; - } - } - - ++m_index; - - SignalOutput(ForEachProperty::GetEachSlotId(this)); - } - - bool ForEach::SetPropertySlotData(Datum& atResult, size_t propertyIndex) - { - if (atResult.Empty()) - { - // Something went wrong with the Behavior Context call - SCRIPTCANVAS_REPORT_ERROR((*this), "Behavior Context call failed; unable to retrieve element from container."); - return false; - } - - if (m_propertySlots.size() <= propertyIndex) - { - // Missing a property slot - SCRIPTCANVAS_REPORT_ERROR((*this), "Node in invalid state; missing a property slot."); - return false; - } - - PushOutput(atResult, *GetSlot(m_propertySlots[propertyIndex].m_propertySlotId)); - return true; - } - - void ForEach::ResetLoop() - { - // Reset node state - m_index = 0; - m_size = 0; - m_breakCalled = false; - m_sourceContainer = Datum(); - m_keysVector = Datum(); - } - void ForEach::OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType) { if (dynamicGroup == GetContainerGroupId() && dataType.IsValid()) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.h index 68e75adc80..32bca9eefd 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.h @@ -56,44 +56,29 @@ namespace ScriptCanvas bool IsBreakSlot(const SlotId&) const; - bool IsOutOfDate(const VersionData& graphVersion) const override; - - + bool IsOutOfDate(const VersionData& graphVersion) const override; UpdateResult OnUpdateNode() override; - protected: - ExecutionNameMap GetExecutionNameMap() const override; - - void OnInit() override; - void OnInputSignal(const SlotId&) override; + private: + static const size_t k_keySlotIndex; + static const size_t k_valueSlotIndex; - bool InitializeLoop(); - void Iterate(); - bool SetPropertySlotData(Datum& atResult, size_t propertyIndex); - void ResetLoop(); + static AZ::Crc32 GetContainerGroupId() { return AZ_CRC("ContainerGroup", 0xb81ed451); } - void OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType) override; + void AddPropertySlotsFromType(const Data::Type& dataType); void ClearPropertySlots(); - void AddPropertySlotsFromType(const Data::Type& dataType); - static AZ::Crc32 GetContainerGroupId() { return AZ_CRC("ContainerGroup", 0xb81ed451); } + ExecutionNameMap GetExecutionNameMap() const override; + + void OnInit() override; + void OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType) override; + SlotId m_sourceSlot; AZ::TypeId m_previousTypeId; AZStd::vector m_propertySlots; - - static const size_t k_keySlotIndex; - static const size_t k_valueSlotIndex; - - size_t m_index; - size_t m_size; - - bool m_breakCalled; - - Datum m_sourceContainer; - Datum m_keysVector; }; } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.cpp index cec643d799..678ca60ef3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.cpp @@ -410,6 +410,11 @@ namespace ScriptCanvas return m_asset.GetId(); } + const AZStd::string& FunctionCallNode::GetAssetHint() const + { + return m_asset.GetHint(); + } + AZ::Outcome FunctionCallNode::GetDependencies() const { DependencyReport report; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.h index fe5b453cf3..003bc2581b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.h @@ -70,6 +70,8 @@ namespace ScriptCanvas AZ::Data::AssetId GetAssetId() const; + const AZStd::string& GetAssetHint() const; + const AZStd::string& GetName() const; void Initialize(AZ::Data::AssetId assetId, const ScriptCanvas::Grammar::FunctionSourceId& sourceId); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp index ad556ae093..a849983deb 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp @@ -19,6 +19,32 @@ #include +namespace FunctionDefinitionNodeCpp +{ + void VersionUpdateRemoveDefaultDisplayGroup(ScriptCanvas::Nodes::Core::FunctionDefinitionNode& node) + { + using namespace ScriptCanvas; + using namespace ScriptCanvas::Nodes::Core; + + AZ::SerializeContext* serializeContext{}; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); + if (serializeContext) + { + const auto& classData = serializeContext->FindClassData(azrtti_typeid()); + if (classData && classData->m_version < FunctionDefinitionNode::NodeVersion::RemoveDefaultDisplayGroup) + { + for (auto& slot : node.ModAllSlots()) + { + if (slot->GetType() == CombinedSlotType::DataIn || slot->GetType() == CombinedSlotType::DataOut) + { + slot->ClearDynamicGroup(); + } + } + } + } + } +} + namespace ScriptCanvas { namespace Nodes @@ -113,6 +139,12 @@ namespace ScriptCanvas } } + void FunctionDefinitionNode::OnInit() + { + Nodeling::OnInit(); + FunctionDefinitionNodeCpp::VersionUpdateRemoveDefaultDisplayGroup(*this); + } + void FunctionDefinitionNode::SetupSlots() { auto groupedSlots = GetSlotsWithDisplayGroup(GetSlotDisplayGroup()); @@ -208,7 +240,6 @@ namespace ScriptCanvas slotConfiguration.SetConnectionType(connectionType); slotConfiguration.m_displayGroup = GetDataDisplayGroup(); - slotConfiguration.m_dynamicGroup = GetDataDynamicTypeGroup(); slotConfiguration.m_dynamicDataType = DynamicDataType::Any; slotConfiguration.m_isUserAdded = true; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h index 20b20cb65d..04dc0c7103 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h @@ -29,14 +29,13 @@ namespace ScriptCanvas class FunctionDefinitionNode : public Internal::Nodeling { - private: + public: enum NodeVersion { - Initial = 1 + Initial = 1, + RemoveDefaultDisplayGroup, }; - public: - SCRIPTCANVAS_NODE(FunctionDefinitionNode); FunctionDefinitionNode() = default; @@ -78,14 +77,15 @@ namespace ScriptCanvas static constexpr AZ::Crc32 GetAddNodelingInputDataSlot() { return AZ_CRC_CE("AddNodelingInputDataSlot"); } static constexpr AZ::Crc32 GetAddNodelingOutputDataSlot() { return AZ_CRC_CE("AddNodelingOutputDataSlot"); } - static constexpr AZ::Crc32 GetDataDynamicTypeGroup() { return AZ_CRC_CE("DataGroup"); } - + AZStd::string GetDataDisplayGroup() const { return "DataDisplayGroup"; } SlotId HandleExtension(AZ::Crc32 extensionId) override; void ConfigureVisualExtensions() override; + void OnInit() override; + void OnSetup() override; private: diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp index b6f7bc2972..4a9e8ac3e3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp @@ -208,7 +208,6 @@ namespace ScriptCanvas } } - void Method::InitializeMethod(const MethodConfiguration& config) { m_namespaces = config.m_namespaces ? *config.m_namespaces : m_namespaces; @@ -239,7 +238,11 @@ namespace ScriptCanvas for (size_t argIndex(0), sentinel(config.m_method.GetNumArguments()); argIndex != sentinel; ++argIndex) { SlotId addedSlot = AddMethodInputSlot(config, argIndex); - MethodHelper::SetSlotToDefaultValue(*this, addedSlot, config, argIndex); + + if (addedSlot.IsValid()) + { + MethodHelper::SetSlotToDefaultValue(*this, addedSlot, config, argIndex); + } } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h index 6a53054ddf..829dff2c29 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h @@ -132,6 +132,8 @@ namespace ScriptCanvas const Slot* GetIfBranchSlot(bool branch) const; + AZ_INLINE const AZStd::string& GetLookupName() const { return m_lookupName; } + AZ_INLINE AZStd::recursive_mutex& GetMutex() { return m_mutex; } ConstSlotsOutcome GetSlotsInExecutionThreadByTypeImpl(const Slot& executionSlot, CombinedSlotType targetSlotType, const Slot* executionChildSlot) const override; @@ -160,6 +162,8 @@ namespace ScriptCanvas bool SanityCheckBranchOnResultMethod(const AZ::BehaviorMethod& branchOnResultMethod) const; + AZ_INLINE void SetClassNamePretty(AZStd::string_view classNamePretty) { m_classNamePretty = classNamePretty; } + void SetMethodUnchecked(const AZ::BehaviorMethod* method, const AZ::BehaviorClass* behaviorClass); AZ_INLINE void SetWarnOnMissingFunction(bool enabled) { m_warnOnMissingFunction = enabled; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp index ed7fce7ffc..bfd0bb824d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp @@ -94,7 +94,7 @@ namespace ScriptCanvas { if (!m_updatingDisplay) { - RefreshActiveIndexes(); + RefreshActiveIndexes(true, true); UpdateSlotDisplay(); } } @@ -135,10 +135,8 @@ namespace ScriptCanvas return Data::Type::Invalid(); } - AZ::Outcome MethodOverloaded::GetFunctionCallName(const Slot* slot) const + AZ::Outcome MethodOverloaded::GetFunctionCallName([[maybe_unused]] const Slot* slot) const { - AZ_UNUSED(slot); - AZStd::string overloadName; int activeIndex = GetActiveIndex(); @@ -188,7 +186,7 @@ namespace ScriptCanvas // this prevents repeated updates based on changes to slots Method::InitializeMethod(config); - + SetClassNamePretty(""); RefreshActiveIndexes(); ConfigureContracts(); @@ -197,7 +195,12 @@ namespace ScriptCanvas SlotId MethodOverloaded::AddMethodInputSlot(const MethodConfiguration& config, size_t argumentIndex) { const AZ::BehaviorParameter* argumentPtr = config.m_method.GetArgument(argumentIndex); - AZ_Assert(argumentPtr, "Method: %s had a null argument at index: %d", config.m_lookupName->data(), argumentIndex); + + if (!argumentPtr) + { + return SlotId{}; + } + const auto& argument = *argumentPtr; auto nameAndToolTip = MethodHelper::GetArgumentNameAndToolTip(config, argumentIndex); @@ -507,7 +510,7 @@ namespace ScriptCanvas } } - void MethodOverloaded::RefreshActiveIndexes(bool checkForConnections) + void MethodOverloaded::RefreshActiveIndexes(bool checkForConnections, bool adjustSlots) { DataIndexMapping concreteInputTypes; DataIndexMapping concreteOutputTypes; @@ -519,6 +522,35 @@ namespace ScriptCanvas if (m_overloadSelection.m_availableIndexes.size() == 1) { auto methodOverload = m_overloadConfiguration.m_overloads[(*m_overloadSelection.m_availableIndexes.begin())]; + + if (adjustSlots) + { + const size_t numArguments = methodOverload.first->GetNumArguments(); + const size_t numInputSlots = m_orderedInputSlotIds.size(); + + if (numArguments > numInputSlots) + { + MethodConfiguration config(*methodOverload.first, GetMethodType()); + AZStd::string_view lookupName = GetLookupName(); + config.m_lookupName = &lookupName; + + for (size_t index = numInputSlots; index != numArguments; ++index) + { + AddMethodInputSlot(config, index); + } + } + else if (numArguments < numInputSlots) + { + const size_t removeCount = numInputSlots - numArguments; + // remove extra slots, assuming remaining ones are of valid type (if not valid name) + for (size_t count = 0; count != removeCount; ++count) + { + RemoveSlot(m_orderedInputSlotIds.back()); + m_orderedInputSlotIds.pop_back(); + } + } + } + SetMethodUnchecked(methodOverload.first, methodOverload.second); } } @@ -681,9 +713,6 @@ namespace ScriptCanvas return AZ::Success(); } - } - } - } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h index 1fffbd7eac..517d3dd072 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h @@ -106,7 +106,7 @@ namespace ScriptCanvas void SetupMethodData(const AZ::BehaviorMethod* lookupMethod, const AZ::BehaviorClass* lookupClass); void ConfigureContracts(); - void RefreshActiveIndexes(bool checkForConnections = true); + void RefreshActiveIndexes(bool checkForConnections = true, bool adjustSlots = false); void FindDataIndexMappings(DataIndexMapping& inputMapping, DataIndexMapping& outputMapping, bool checkForConnections) const; void UpdateSlotDisplay(); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/Rotate.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/Rotate.cpp index c510b55314..2f79d4218a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/Rotate.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/Rotate.cpp @@ -47,21 +47,10 @@ namespace ScriptCanvas AZ::Transform currentTransform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(currentTransform, targetEntity, &AZ::TransformInterface::GetWorldTM); - - AZ::Vector3 position = currentTransform.GetTranslation(); - AZ::Quaternion currentRotation = currentTransform.GetRotation(); + currentTransform.SetRotation((rotation * currentTransform.GetRotation().GetNormalized())); - AZ::Quaternion newRotation = (rotation * currentRotation); - newRotation.Normalize(); - - AZ::Transform newTransform = AZ::Transform::CreateIdentity(); - - newTransform.SetScale(currentTransform.GetScale()); - newTransform.SetRotation(newRotation); - newTransform.SetTranslation(position); - - AZ::TransformBus::Event(targetEntity, &AZ::TransformInterface::SetWorldTM, newTransform); + AZ::TransformBus::Event(targetEntity, &AZ::TransformInterface::SetWorldTM, currentTransform); } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/RotateMethod.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/RotateMethod.cpp index 20ea4e1b33..53884f4dd8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/RotateMethod.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Entity/RotateMethod.cpp @@ -44,22 +44,12 @@ namespace ScriptCanvas { AZ::Quaternion rotation = AZ::ConvertEulerDegreesToQuaternion(angles); - AZ::Transform currentTransform = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult(currentTransform, targetEntity, &AZ::TransformInterface::GetWorldTM); + AZ::Transform transform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(transform, targetEntity, &AZ::TransformInterface::GetWorldTM); - AZ::Vector3 position = currentTransform.GetTranslation(); - AZ::Quaternion currentRotation = currentTransform.GetRotation(); + transform.SetRotation((rotation * transform.GetRotation()).GetNormalized()); - AZ::Quaternion newRotation = (rotation * currentRotation); - newRotation.Normalize(); - - AZ::Transform newTransform = AZ::Transform::CreateIdentity(); - - newTransform.CreateScale(currentTransform.ExtractScale()); - newTransform.SetRotation(newRotation); - newTransform.SetTranslation(position); - - AZ::TransformBus::Event(targetEntity, &AZ::TransformInterface::SetWorldTM, newTransform); + AZ::TransformBus::Event(targetEntity, &AZ::TransformInterface::SetWorldTM, transform); } } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.cpp index b8c1e9e400..6f8a23a2ed 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.cpp @@ -393,14 +393,6 @@ namespace ScriptCanvas AZ_UNUSED(sourceType); } - AZStd::unordered_map> OperatorBase::GetReplacementSlotsMap() const - { - AZStd::unordered_map> slotsMap; - slotsMap.emplace("In", AZStd::vector{ "In" }); - slotsMap.emplace("Out", AZStd::vector{ "Out" }); - return slotsMap; - } - void OperatorBase::CustomizeReplacementNode(Node* replacementNode, AZStd::unordered_map>& outSlotIdMap) const { auto newDataInSlots = replacementNode->GetSlotsByType(ScriptCanvas::CombinedSlotType::DataIn); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.h index bc49f3fac6..7091d7fa87 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.h @@ -57,7 +57,6 @@ namespace ScriptCanvas AZStd::vector< SourceSlotConfiguration > m_sourceSlotConfigurations; }; - AZStd::unordered_map> GetReplacementSlotsMap() const override; void CustomizeReplacementNode(Node* replacementNode, AZStd::unordered_map>& outSlotIdMap) const override; using TypeList = AZStd::vector; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Time/Timer.ScriptCanvasGrammar.xml b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Time/Timer.ScriptCanvasGrammar.xml index 21adecb8fe..e1ea8a4b73 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Time/Timer.ScriptCanvasGrammar.xml +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Time/Timer.ScriptCanvasGrammar.xml @@ -8,7 +8,7 @@ Base="ScriptCanvas::Node" Version="2" GeneratePropertyFriend="True" - DeprecationUUID="32A4BEDC-C207-4472-61DE-9A716402620A" + DeprecationUUID="{32A4BEDC-C207-4472-61DE-9A716402620A}" Deprecated="This node has been deprecated in favor of the nodeable form" Description="Provides a time value."> @@ -25,4 +25,4 @@ IsInput="False" IsOutput="True" /> - + \ No newline at end of file diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h index 16ac7e1fa0..def4fa7761 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h @@ -62,6 +62,8 @@ namespace ScriptCanvas constexpr const char* NoChildrenAfterRoot = "No children after parsing function root"; constexpr const char* NoChildrenInExtraction = "No children found in property extraction node"; constexpr const char* NoDataPresent = "Could not construct from graph, no graph data was present"; + constexpr const char* NodeableNodeOverloadAmbiguous = "NodeableNodeOverloaded doesn't have enough data connected to select a valid overload"; + constexpr const char* NodeableNodeDidNotConstructInternalNodeable = "NodeableNode did not construct its internal Nodeable"; constexpr const char* NoInputToForEach = "No Input To For Each Loop"; constexpr const char* NoOutForExecution = "No out slot for execution root"; constexpr const char* NoOutSlotInFunctionDefinitionStart = "No 'Out' slot in start of function definition"; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.cpp index 5bf5f83916..0dc16f721c 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.cpp @@ -12,9 +12,26 @@ #include "VersioningUtils.h" #include +#include namespace ScriptCanvas { + AZStd::vector GraphUpdateSlotReport::Convert(const Endpoint& oldEndpoint) const + { + auto iter = m_oldSlotsToNewSlots.find(oldEndpoint); + return iter != m_oldSlotsToNewSlots.end() ? iter->second : AZStd::vector{ oldEndpoint }; + } + + bool GraphUpdateSlotReport::IsEmpty() const + { + return m_deletedOldSlots.empty() && m_oldSlotsToNewSlots.empty(); + } + + bool NodeUpdateSlotReport::IsEmpty() const + { + return m_deletedOldSlots.empty() && m_oldSlotsToNewSlots.empty(); + } + void VersioningUtils::CopyOldValueToDataSlot(Slot* newSlot, const VariableId& oldVariableReference, const Datum* oldDatum) { if (oldVariableReference.IsValid()) @@ -36,6 +53,99 @@ namespace ScriptCanvas } } + void MergeUpdateSlotReport(const AZ::EntityId& scriptCanvasNodeId, GraphUpdateSlotReport& report, const NodeUpdateSlotReport& source) + { + report.m_deletedOldSlots.reserve(source.m_deletedOldSlots.size()); + + for (auto& slotId : source.m_deletedOldSlots) + { + report.m_deletedOldSlots.insert({ scriptCanvasNodeId, slotId }); + } + + report.m_oldSlotsToNewSlots.reserve(source.m_oldSlotsToNewSlots.size()); + + for (auto& oldToNewIter : source.m_oldSlotsToNewSlots) + { + AZStd::vector newEndpoints; + newEndpoints.reserve(oldToNewIter.second.size()); + + for (auto& targetSlotId : oldToNewIter.second) + { + newEndpoints.push_back({ scriptCanvasNodeId, targetSlotId }); + } + + report.m_oldSlotsToNewSlots[{ scriptCanvasNodeId, oldToNewIter.first}] = AZStd::move(newEndpoints); + } + } + + AZStd::vector> CollectEndpoints(const AZStd::vector& connections, bool logEntityNames) + { + AZStd::vector names; + AZStd::vector> endpoints; + + for (auto& connectionEntity : connections) + { + if (logEntityNames) + { + names.push_back(connectionEntity->GetName()); + } + + if (auto connection = AZ::EntityUtils::FindFirstDerivedComponent(connectionEntity->GetId())) + { + endpoints.push_back(AZStd::make_pair(connection->GetSourceEndpoint(), connection->GetTargetEndpoint())); + } + } + + if (logEntityNames) + { + AZStd::sort(names.begin(), names.end()); + + AZStd::string result = "\nConnection Name list:\n"; + for (auto& name : names) + { + result += "\n"; + result += name; + } + + AZ_TracePrintf("ScriptCanvas", result.c_str()); + } + + return endpoints; + } + + void UpdateConnectionStatus(Graph& graph, const GraphUpdateSlotReport& report) + { + GraphData* graphData = graph.GetGraphData(); + if (!graphData) + { + AZ_Error("ScriptCanvas", false, "Graph was missing graph data to update"); + return; + } + + AZStd::unordered_set oldConnectedSlots; + AZ_TracePrintf("ScriptCanvas", "Connections list before: "); + auto endpoints = CollectEndpoints(graphData->m_connections, true); + graph.RemoveAllConnections(); + + for (auto& iter : endpoints) + { + const AZStd::vector& sources = report.Convert(iter.first); + const AZStd::vector& targets = report.Convert(iter.second); + + for (const auto& source : sources) + { + for (const auto& target : targets) + { + graph.ConnectByEndpoint(source, target); + } + } + } + + graphData->BuildEndpointMap(); + AZ_TracePrintf("ScriptCanvas", "Connections list after: "); + CollectEndpoints(graphData->m_connections, true); + } + void VersioningUtils::CreateRemapConnectionsForSourceEndpoint(const Graph& graph, const Endpoint& oldSourceEndpoint, const Endpoint& newSourceEndpoint, ReplacementConnectionMap& connectionMap) { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.h index 51960e08e7..a9a40305c3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.h @@ -15,6 +15,8 @@ #include #include +#include +#include #include namespace AZ @@ -25,13 +27,36 @@ namespace AZ namespace ScriptCanvas { class Datum; - class Endpoint; class Graph; class Slot; - using ReplacementEndpointPairs = AZStd::unordered_set>; + using ReplacementEndpointPairs = AZStd::unordered_set>; using ReplacementConnectionMap = AZStd::unordered_map; + struct NodeUpdateSlotReport + { + AZStd::unordered_set m_deletedOldSlots; + AZStd::unordered_map> m_oldSlotsToNewSlots; + + bool IsEmpty() const; + }; + + struct GraphUpdateSlotReport + { + AZStd::unordered_set m_deletedOldSlots; + AZStd::unordered_map> m_oldSlotsToNewSlots; + + AZStd::vector Convert(const Endpoint& oldEndpoint) const; + + bool IsEmpty() const; + }; + + void MergeUpdateSlotReport(const AZ::EntityId& scriptCanvasNodeId, GraphUpdateSlotReport& report, const NodeUpdateSlotReport& source); + + AZStd::vector> CollectEndpoints(const AZStd::vector& connections, bool logEntityNames = false); + + void UpdateConnectionStatus(Graph& graph, const GraphUpdateSlotReport& report); + class VersioningUtils { public: diff --git a/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/RotateCameraLookAt.cpp b/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/RotateCameraLookAt.cpp index 2ab7e0bd01..e2e818f3e7 100644 --- a/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/RotateCameraLookAt.cpp +++ b/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/RotateCameraLookAt.cpp @@ -58,19 +58,9 @@ namespace Camera float axisPolarity = m_shouldInvertAxis ? -1.0f : 1.0f; float rotationAmount = axisPolarity * m_rotationAmount; - // remove translation and scale - AZ::Vector3 translation = outLookAtTargetTransform.GetTranslation(); - outLookAtTargetTransform.SetTranslation(AZ::Vector3::CreateZero()); - AZ::Vector3 transformScale = outLookAtTargetTransform.ExtractScale(); - - // perform our rotation - AZ::Transform desiredRotationTransform = AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateFromAxisAngle(outLookAtTargetTransform.GetBasis(m_axisOfRotation), rotationAmount)); - - outLookAtTargetTransform = desiredRotationTransform * outLookAtTargetTransform; - - // return scale and translate - outLookAtTargetTransform.SetScale(transformScale); - outLookAtTargetTransform.SetTranslation(translation); + AZ::Quaternion desiredRotation = AZ::Quaternion::CreateFromAxisAngle( + outLookAtTargetTransform.GetBasis(m_axisOfRotation), rotationAmount); + outLookAtTargetTransform.SetRotation(desiredRotation * outLookAtTargetTransform.GetRotation()); } void RotateCameraLookAt::Activate(AZ::EntityId entityId) diff --git a/Gems/StartingPointInput/Code/Source/InputNode.ScriptCanvasGrammar.xml b/Gems/StartingPointInput/Code/Source/InputNode.ScriptCanvasGrammar.xml index 9d6c555c97..8c8cc34597 100644 --- a/Gems/StartingPointInput/Code/Source/InputNode.ScriptCanvasGrammar.xml +++ b/Gems/StartingPointInput/Code/Source/InputNode.ScriptCanvasGrammar.xml @@ -6,8 +6,10 @@ PreferredClassName="Input Handler" Uuid="{0B0AC61B-4BBA-42BF-BDCD-DAF2D3CA41A8}" Base="ScriptCanvas::Node" - Icon="Icons/ScriptCanvas/Bus.png" + Icon="Editor/Icons/ScriptCanvas/Bus.png" EditAttributes="AZ::Edit::Attributes::Category@Gameplay/Input" + DeprecationUUID="{0A2EB488-5A6A-E166-BB62-23FF81499E33}" + Deprecated="This node has been deprecated in favor of the nodeable form" GraphEntryPoint="True" GeneratePropertyFriend="True" Description="Handle processed input events found in input binding assets"> @@ -25,4 +27,4 @@ IsInput="False" IsOutput="True" /> - + \ No newline at end of file diff --git a/Gems/SurfaceData/Code/Source/Editor/EditorSurfaceDataSystemComponent.cpp b/Gems/SurfaceData/Code/Source/Editor/EditorSurfaceDataSystemComponent.cpp index 4dcc969434..4703977ab4 100644 --- a/Gems/SurfaceData/Code/Source/Editor/EditorSurfaceDataSystemComponent.cpp +++ b/Gems/SurfaceData/Code/Source/Editor/EditorSurfaceDataSystemComponent.cpp @@ -118,6 +118,8 @@ namespace SurfaceData void EditorSurfaceDataSystemComponent::Deactivate() { + m_surfaceTagNameAssets.clear(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); AzToolsFramework::Components::EditorComponentBase::Deactivate(); SurfaceDataTagProviderRequestBus::Handler::BusDisconnect(); diff --git a/Gems/TextureAtlas/Code/CMakeLists.txt b/Gems/TextureAtlas/Code/CMakeLists.txt index b7072321dc..5e29a7ea65 100644 --- a/Gems/TextureAtlas/Code/CMakeLists.txt +++ b/Gems/TextureAtlas/Code/CMakeLists.txt @@ -10,7 +10,7 @@ # ly_add_target( - NAME TextureAtlas ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAME TextureAtlas.Static STATIC NAMESPACE Gem FILES_CMAKE textureatlas_files.cmake @@ -21,4 +21,46 @@ ly_add_target( PRIVATE Legacy::CryCommon AZ::AzFramework + PUBLIC + Gem::Atom_RPI.Public + AZ::AtomCore +) + +ly_add_target( + NAME TextureAtlas ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + textureatlas_module_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + BUILD_DEPENDENCIES + PRIVATE + Legacy::CryCommon + Gem::TextureAtlas.Static ) + +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME TextureAtlas.Editor GEM_MODULE + NAMESPACE Gem + FILES_CMAKE + textureatlas_module_files.cmake + textureatlas_builder_files.cmake + COMPILE_DEFINITIONS + PRIVATE + TEXTUREATLAS_EDITOR + INCLUDE_DIRECTORIES + PUBLIC + Include + BUILD_DEPENDENCIES + PRIVATE + Legacy::CryCommon + AZ::AzCore + AZ::AzFramework + AZ::AssetBuilderSDK + Gem::TextureAtlas.Static + Gem::ImageProcessingAtom.Headers + ) +endif() + diff --git a/Gems/TextureAtlas/Code/Include/TextureAtlas/TextureAtlas.h b/Gems/TextureAtlas/Code/Include/TextureAtlas/TextureAtlas.h index c4e222230d..0703f19c29 100644 --- a/Gems/TextureAtlas/Code/Include/TextureAtlas/TextureAtlas.h +++ b/Gems/TextureAtlas/Code/Include/TextureAtlas/TextureAtlas.h @@ -16,7 +16,8 @@ #include #include -class ITexture; +#include +#include namespace TextureAtlasNamespace { @@ -77,9 +78,9 @@ namespace TextureAtlasNamespace //! Retrieve a coordinate set from the Atlas by its handle virtual AtlasCoordinates GetAtlasCoordinates(const AZStd::string& handle) const = 0; //! Links this atlas to an image pointer - virtual void SetTexture(ITexture* image) = 0; + virtual void SetTexture(AZ::Data::Instance image) = 0; //! Returns the image linked to this atlas - virtual ITexture* GetTexture() const = 0; + virtual AZ::Data::Instance GetTexture() const = 0; //! Returns the width of the atlas virtual int GetWidth() const = 0; //! Returns the height of the atlas diff --git a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.cpp b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.cpp index e8d4948cc9..0cbc653730 100644 --- a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.cpp +++ b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.cpp @@ -10,7 +10,6 @@ * */ -#include "ImageProcessing_precompiled.h" #include "AtlasBuilderComponent.h" #include diff --git a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp index 81e98251cc..ca60d33c0a 100644 --- a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp +++ b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp @@ -10,7 +10,6 @@ * */ -#include "ImageProcessing_precompiled.h" #include "AtlasBuilderWorker.h" #include @@ -19,22 +18,17 @@ #include #include #include +#include #include #include #include #include #include -#include -#include -#include -#include -#include -#include - -#include -#include -#include +#include +#include +#include +#include #include #include @@ -44,13 +38,13 @@ namespace TextureAtlasBuilder { //! Counts leading zeros - uint32 CountLeadingZeros32(uint32 x) + uint32_t CountLeadingZeros32(uint32_t x) { return x == 0 ? 32 : az_clz_u32(x); } //! Integer log2 - uint32 IntegerLog2(uint32 x) + uint32_t IntegerLog2(uint32_t x) { return 31 - CountLeadingZeros32(x); } @@ -113,13 +107,24 @@ namespace TextureAtlasBuilder { bool resolved = false; - // Get full path by appending the relative path to the watch directory - AZStd::string fullPath = watchDirectory; - fullPath.append("/"); - fullPath.append(relativePath); + if (relativePath[0] == '@') + { + // Get full path by resolving the alias at the front of the path + char resolvedPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath(relativePath.c_str(), resolvedPath, AZ_MAX_PATH_LEN); + resolvedFullPathOut = resolvedPath; + resolved = true; + } + else + { + // Get full path by appending the relative path to the watch directory + AZStd::string fullPath = watchDirectory; + fullPath.append("/"); + fullPath.append(relativePath); - // Resolve to canonical path (remove "./" and "../") - resolved = GetCanonicalPathFromFullPath(fullPath, resolvedFullPathOut); + // Resolve to canonical path (remove "./" and "../") + resolved = GetCanonicalPathFromFullPath(fullPath, resolvedFullPathOut); + } return resolved; } @@ -140,23 +145,6 @@ namespace TextureAtlasBuilder return result; } - const ImageProcessing::PresetSettings* GetImageProcessPresetSettings(const AZStd::string& presetName, const AZStd::string& platformIdentifier) - { - // Get the specified presetId - AZ::Uuid presetId = ImageProcessing::BuilderSettingManager::Instance()->GetPresetIdFromName(presetName); - if (presetId.IsNull()) - { - AZ_Error("Texture Editor", false, "Texture Preset %s has no associated UUID.", presetName.c_str()); - return nullptr; - } - - // Get the preset settings for the platform this job is building for - const ImageProcessing::PresetSettings* presetSettings = ImageProcessing::BuilderSettingManager::Instance()->GetPreset( - presetId, platformIdentifier); - - return presetSettings; - } - // Reflect the input parameters void AtlasBuilderInput::Reflect(AZ::ReflectContext* context) { @@ -474,7 +462,7 @@ namespace TextureAtlasBuilder { AZStd::string ext; AzFramework::StringFunc::Path::GetExtension(candidates[i].c_str(), ext, false); - if (ImageProcessing::IsExtensionSupported(ext.c_str()) && ext != "dds") + if (ext != "dds") { bool duplicate = false; for (size_t j = 0; j < paths.size() && !duplicate; ++j) @@ -589,7 +577,7 @@ namespace TextureAtlasBuilder { AddFolderContents(paths, child, valid); } - else if (ImageProcessing::IsExtensionSupported(ext.c_str()) && ext != "dds") + else if (ext != "dds") { AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, child); bool duplicate = false; @@ -652,7 +640,11 @@ namespace TextureAtlasBuilder // We process the same file for all platforms for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms) { - if (ImageProcessing::BuilderSettingManager::Instance()->DoesSupportPlatform(info.m_identifier)) + bool doesSupportPlatform = false; + ImageProcessingAtom::ImageBuilderRequestBus::BroadcastResult(doesSupportPlatform, + &ImageProcessingAtom::ImageBuilderRequests::DoesSupportPlatform, + info.m_identifier); + if (doesSupportPlatform) { AssetBuilderSDK::JobDescriptor descriptor = GetJobDescriptor(request.m_sourceFile, input); descriptor.SetPlatformIdentifier(info.m_identifier.c_str()); @@ -707,12 +699,8 @@ namespace TextureAtlasBuilder // Before we begin, let's make sure we are not meant to abort. AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId); - AZStd::vector productFilepaths; - const AZStd::string path = request.m_fullPath; - bool imageProcessingSuccessful = false; - // read in settings/filepaths AtlasBuilderInput input; input.m_forceSquare = AzFramework::StringFunc::ToBool(request.m_jobDescription.m_jobParameters.find(AZ_CRC("forceSquare"))->second.c_str()); @@ -752,43 +740,37 @@ namespace TextureAtlasBuilder // Default to the TextureAtlas preset which is currently set to use compression for all platforms except for iOS. // Currently the only fully supported compression for iOS is PVRTC which requires the texture to be square and a power of 2. // Due to this limitation, we default to using no compression for iOS until ASTC is fully supported - const AZStd::string defaultPresetName = "TextureAtlas"; + const AZStd::string defaultPresetName = "UserInterface_Compressed"; input.m_presetName = defaultPresetName; } - // Get a preset to use for the output image - const ImageProcessing::PresetSettings* preset = GetImageProcessPresetSettings(input.m_presetName, request.m_platformInfo.m_identifier); - if (preset) - { - // Check the preset's pixel format requirements - const ImageProcessing::PixelFormatInfo* pixelFormatInfo = ImageProcessing::CPixelFormats::GetInstance().GetPixelFormatInfo(preset->m_pixelFormat); - if (pixelFormatInfo && pixelFormatInfo->bSquarePow2) - { - // Override the user config settings to force square and power of 2. - // Otherwise the image conversion process will stretch the image to satisfy these requirements - input.m_forceSquare = true; - input.m_forcePowerOf2 = true; - } - } - else + bool isFormatSquarePow2 = false; + ImageProcessingAtom::ImageBuilderRequestBus::BroadcastResult(isFormatSquarePow2, + &ImageProcessingAtom::ImageBuilderRequests::IsPresetFormatSquarePow2, + input.m_presetName, request.m_platformInfo.m_identifier); + + if (isFormatSquarePow2) { - AZ_Error("AtlasBuilder", false, "Could not find a preset setting for the output image."); - return; + // Override the user config settings to force square and power of 2. + // Otherwise the image conversion process will stretch the image to satisfy these requirements + input.m_forceSquare = true; + input.m_forcePowerOf2 = true; } // Read in images - AZStd::vector images; + AZStd::vector images; AZ::u64 totalArea = 0; int maxArea = input.m_maxDimension * input.m_maxDimension; bool sizeFailure = false; for (int i = 0; i < input.m_filePaths.size() && !jobCancelListener.IsCancelled(); ++i) { - ImageProcessing::IImageObject* inputImage = ImageProcessing::LoadImageFromFile(input.m_filePaths[i]); + ImageProcessingAtom::IImageObjectPtr inputImage; + ImageProcessingAtom::ImageProcessingRequestBus::BroadcastResult(inputImage, &ImageProcessingAtom::ImageProcessingRequests::LoadImage, input.m_filePaths[i]); + // Check if we were able to load the image if (inputImage) { - ImageProcessing::IImageObjectPtr image = ImageProcessing::IImageObjectPtr(inputImage); - images.push_back(image); + images.push_back(inputImage); totalArea += inputImage->GetWidth(0) * inputImage->GetHeight(0); } else @@ -837,8 +819,13 @@ namespace TextureAtlasBuilder // Add white texture if we need to if (input.m_includeWhiteTexture) { - ImageProcessing::IImageObjectPtr texture(ImageProcessing::IImageObject::CreateImage( - cellSize, cellSize, 1, ImageProcessing::EPixelFormat::ePixelFormat_R8G8B8A8)); + ImageProcessingAtom::IImageObjectPtr texture; + ImageProcessingAtom::ImageBuilderRequestBus::BroadcastResult(texture, + &ImageProcessingAtom::ImageBuilderRequests::CreateImage, + aznumeric_cast(cellSize), + aznumeric_cast(cellSize), + 1, + ImageProcessingAtom::EPixelFormat::ePixelFormat_R8G8B8A8); // Make the texture white texture->ClearColor(1, 1, 1, 1); @@ -897,8 +884,8 @@ namespace TextureAtlasBuilder } if (input.m_forcePowerOf2) { - resultWidth = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(resultWidth - 1)))); - resultHeight = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(resultHeight - 1)))); + resultWidth = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(resultWidth - 1)))); + resultHeight = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(resultHeight - 1)))); } else { @@ -918,8 +905,13 @@ namespace TextureAtlasBuilder } // Process texture sheet - ImageProcessing::IImageObjectPtr outImage(ImageProcessing::IImageObject::CreateImage( - resultWidth, resultHeight, 1, ImageProcessing::EPixelFormat::ePixelFormat_R8G8B8A8)); + ImageProcessingAtom::IImageObjectPtr outImage; + ImageProcessingAtom::ImageBuilderRequestBus::BroadcastResult(outImage, + &ImageProcessingAtom::ImageBuilderRequests::CreateImage, + aznumeric_cast(resultWidth), + aznumeric_cast(resultHeight), + 1, + ImageProcessingAtom::EPixelFormat::ePixelFormat_R8G8B8A8); // Clear the sheet outImage->ClearColor(input.m_unusedColor.GetR(), input.m_unusedColor.GetG(), input.m_unusedColor.GetB(), input.m_unusedColor.GetA()); @@ -1010,54 +1002,21 @@ namespace TextureAtlasBuilder // Output texture sheet AZStd::string imageFileName, imageOutputPath; AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.c_str(), imageFileName); - imageFileName += ".dds"; + imageFileName += ".texatlas"; AzFramework::StringFunc::Path::Join( request.m_tempDirPath.c_str(), imageFileName.c_str(), imageOutputPath, true, true); - // Let the ImageProcessor do the rest of the work. - ImageProcessing::TextureSettings textureSettings; - textureSettings.m_preset = preset->m_uuid; - - // Mipmaps for the texture atlas would require more work than the Image Processor does. This is because if we - // let the Image Processor make mipmaps, it might bleed the textures in the atlas together. - textureSettings.m_enableMipmap = false; - - // Check if the ImageBuilder wants to enable streaming - bool isStreaming = ImageProcessing::BuilderSettingManager::Instance() - ->GetBuilderSetting(request.m_platformInfo.m_identifier) - ->m_enableStreaming; - - bool canOverridePreset = false; - ImageProcessing::ImageConvertProcess* process = - new ImageProcessing::ImageConvertProcess(outImage, - textureSettings, - *preset, - false, - isStreaming, - canOverridePreset, - imageOutputPath, - request.m_platformInfo.m_identifier); - - if (process != nullptr) - { - // the process can be stopped if the job is cancelled or the worker is shutting down - while (!process->IsFinished() && !m_isShuttingDown && !jobCancelListener.IsCancelled()) - { - process->UpdateProcess(); - } + AZStd::vector outProducts; + ImageProcessingAtom::ImageBuilderRequestBus::BroadcastResult(outProducts, + &ImageProcessingAtom::ImageBuilderRequests::ConvertImageObject, + outImage, + input.m_presetName, + request.m_platformInfo.m_identifier, + imageOutputPath, + request.m_sourceFileUUID, + request.m_sourceFile); - // get process result - imageProcessingSuccessful = process->IsSucceed(); - process->GetAppendOutputFilePaths(productFilepaths); - - delete process; - } - else - { - imageProcessingSuccessful = false; - } - - if (imageProcessingSuccessful) + if (!outProducts.empty()) { TextureAtlasNamespace::TextureAtlasRequestBus::Broadcast( &TextureAtlasNamespace::TextureAtlasRequests::SaveAtlasToFile, outputPath, output, resultWidth, resultHeight); @@ -1067,27 +1026,23 @@ namespace TextureAtlasBuilder // The Image Processing Gem can produce multiple output files under certain // circumstances, but the texture atlas is not expected to produce such output - if (productFilepaths.size() > 1) + if (outProducts.size() > 1) { AZ_Error("AtlasBuilder", false, "Image processing resulted in multiple output files. Texture atlas is expected to produce one output."); response.m_outputProducts.clear(); return; } - if (productFilepaths.size() > 0) - { - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(productFilepaths[0])); - response.m_outputProducts.back().m_productAssetType = azrtti_typeid(); - response.m_outputProducts.back().m_productSubID = 1; - - // The texatlasidx file is a data file that indicates where the original parts are inside the atlas, - // and this would usually imply that it refers to its dds file in some way or needs it to function. - // The texatlasidx file should be the one that depends on the DDS because its possible to use the DDS - // without the texatlasid, but not the other way around - AZ::Data::AssetId productAssetId(request.m_sourceFileUUID, response.m_outputProducts.back().m_productSubID); - response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_dependencies.push_back(AssetBuilderSDK::ProductDependency(productAssetId, 0)); - response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_dependenciesHandled = true; // We've populated the dependencies immediately above so it's OK to tell the AP we've handled dependencies - } + response.m_outputProducts.push_back(outProducts[0]); + + // The texatlasidx file is a data file that indicates where the original parts are inside the atlas, + // and this would usually imply that it refers to its dds file in some way or needs it to function. + // The texatlasidx file should be the one that depends on the DDS because it's possible to use the DDS + // without the texatlasid, but not the other way around + AZ::Data::AssetId productAssetId(request.m_sourceFileUUID, response.m_outputProducts.back().m_productSubID); + response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_dependencies.push_back(AssetBuilderSDK::ProductDependency(productAssetId, 0)); + response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_dependenciesHandled = true; // We've populated the dependencies immediately above so it's OK to tell the AP we've handled dependencies + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; } } @@ -1315,7 +1270,7 @@ namespace TextureAtlasBuilder if (powerOfTwo) { // Starting dimension needs to be rounded up to the nearest power of two - dimension = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(dimension - 1)))); + dimension = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(dimension - 1)))); } AZStd::vector track; @@ -1363,7 +1318,7 @@ namespace TextureAtlasBuilder if (powerOfTwo) { // Starting dimension needs to be rounded up to the nearest power of two - minWidth = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(minWidth - 1)))); + minWidth = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(minWidth - 1)))); } // Round min width up to the nearest compression unit @@ -1400,7 +1355,7 @@ namespace TextureAtlasBuilder // Find the height of the solution for (int i = 0; i < track.size(); ++i) { - uint32 bottom = static_cast(AZStd::max(0, track[i].GetBottom())); + uint32_t bottom = static_cast(AZStd::max(0, track[i].GetBottom())); if (height < bottom) { height = bottom; @@ -1411,7 +1366,7 @@ namespace TextureAtlasBuilder if (powerOfTwo) { // Starting dimensions need to be rounded up to the nearest power of two - height = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(height - 1)))); + height = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(height - 1)))); } AZ::u32 resultArea = height * width; diff --git a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.h b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.h index 94e2b5b226..36f0c3d486 100644 --- a/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.h +++ b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include #include #include @@ -110,7 +111,7 @@ namespace TextureAtlasBuilder enum class Product { TexatlasidxProduct = 0, - DdsProduct = 1 + StreamingImageProduct = 1 }; //! An asset builder for texture atlases diff --git a/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.cpp b/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.cpp index 70a1900c25..e84e843177 100644 --- a/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.cpp +++ b/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.cpp @@ -120,14 +120,14 @@ namespace TextureAtlasNamespace } // Links this atlas to an image pointer - void TextureAtlasImpl::SetTexture(ITexture* image) + void TextureAtlasImpl::SetTexture(AZ::Data::Instance image) { // We don't need to delete the old value because the pointer is handled elsewhere m_image = image; } // Returns the image linked to this atlas - ITexture* TextureAtlasImpl::GetTexture() const + AZ::Data::Instance TextureAtlasImpl::GetTexture() const { return m_image; } diff --git a/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.h b/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.h index af611c8c7d..22c03e0e3c 100644 --- a/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.h +++ b/Gems/TextureAtlas/Code/Source/TextureAtlasImpl.h @@ -20,7 +20,8 @@ #include "TextureAtlas/TextureAtlas.h" #include "TextureAtlas/TextureAtlasBus.h" -#include +#include +#include namespace TextureAtlasNamespace { @@ -61,10 +62,10 @@ namespace TextureAtlasNamespace AtlasCoordinates GetAtlasCoordinates(const AZStd::string& handle) const override; //! Links this atlas to an image pointer - void SetTexture(ITexture* image) override; + void SetTexture(AZ::Data::Instance image) override; //! Returns the image linked to this atlas - ITexture* GetTexture() const override; + AZ::Data::Instance GetTexture() const override; //! Replaces the mappings of this Texture Atlas Object, with the source's mappings void OverwriteMappings(TextureAtlasImpl* source); @@ -80,7 +81,7 @@ namespace TextureAtlasNamespace private: AZStd::unordered_map m_data; - ITexture* m_image; + AZ::Data::Instance m_image; int m_width; int m_height; }; diff --git a/Gems/TextureAtlas/Code/Source/TextureAtlasModule.cpp b/Gems/TextureAtlas/Code/Source/TextureAtlasModule.cpp index 478a3f8ca6..90114f6e79 100644 --- a/Gems/TextureAtlas/Code/Source/TextureAtlasModule.cpp +++ b/Gems/TextureAtlas/Code/Source/TextureAtlasModule.cpp @@ -16,6 +16,10 @@ #include "TextureAtlasSystemComponent.h" +#ifdef TEXTUREATLAS_EDITOR +#include "Editor/AtlasBuilderComponent.h" +#endif + #include namespace TextureAtlasNamespace @@ -33,6 +37,9 @@ namespace TextureAtlasNamespace // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. m_descriptors.insert(m_descriptors.end(), { TextureAtlasSystemComponent::CreateDescriptor(), +#ifdef TEXTUREATLAS_EDITOR + TextureAtlasBuilder::AtlasBuilderComponent::CreateDescriptor(), //builder component for texture atlas +#endif }); } diff --git a/Gems/TextureAtlas/Code/Source/TextureAtlasSystemComponent.cpp b/Gems/TextureAtlas/Code/Source/TextureAtlasSystemComponent.cpp index 80d4898c41..24a5126df4 100644 --- a/Gems/TextureAtlas/Code/Source/TextureAtlasSystemComponent.cpp +++ b/Gems/TextureAtlas/Code/Source/TextureAtlasSystemComponent.cpp @@ -22,7 +22,25 @@ #include #include -#include +#include + +namespace +{ + AZ::Data::Instance LoadAtlasImage(const AZStd::string& imagePath) + { + // The file may not be in the AssetCatalog at this point if it is still processing or doesn't exist on disk. + // Use GenerateAssetIdTEMP instead of GetAssetIdByPath so that it will return a valid AssetId anyways + AZ::Data::AssetId streamingImageAssetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + streamingImageAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GenerateAssetIdTEMP, + imagePath.c_str()); + + streamingImageAssetId.m_subId = AZ::RPI::StreamingImageAsset::GetImageAssetSubId(); + auto streamingImageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset(streamingImageAssetId, AZ::Data::AssetLoadBehavior::PreLoad); + AZ::Data::Instance image = AZ::RPI::StreamingImage::FindOrCreate(streamingImageAsset); + return image; + } +} namespace TextureAtlasNamespace { @@ -95,27 +113,15 @@ namespace TextureAtlasNamespace // We reload the image here to prevent stuttering in the editor if (iterator->second.m_atlas && iterator->second.m_atlas->GetTexture()) { - SResourceAsync* pInfo = new SResourceAsync(); - pInfo->eClassName = eRCN_Texture; - pInfo->pResource = iterator->second.m_atlas->GetTexture(); - // ToDo: Update to work with Atom? LYN-3680 - // ???->ReleaseResourceAsync(pInfo); + iterator->second.m_atlas->GetTexture().reset(); } - // Reload Texture - AZStd::string imagePath = iterator->second.m_path.substr(0, iterator->second.m_path.find_last_of('.')); - imagePath.append(".dds"); - - // ToDo: Update to work with Atom? LYN-3680 - // uint32 loadTextureFlags = (FT_USAGE_ALLOWREADSRGB | FT_DONT_STREAM); - ITexture* texture = nullptr; - - if (!texture || !texture->IsTextureLoaded()) + AZStd::string imagePath = iterator->second.m_path; + AZ::Data::Instance texture = LoadAtlasImage(imagePath); + if (!texture) { - gEnv->pSystem->Warning(VALIDATOR_MODULE_UNKNOWN, - VALIDATOR_WARNING, - VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - imagePath.c_str(), - "No texture file found for texture atlas: %s. " + AZ_Error("TextureAtlasSystemComponent", + false, + "Failed to find or create an image instance for texture atlas '%s'" "NOTE: File must be in current project or a gem.", imagePath.c_str()); TextureAtlas* temp = iterator->second.m_atlas; @@ -123,6 +129,7 @@ namespace TextureAtlasNamespace TextureAtlasNotificationBus::Broadcast(&TextureAtlasNotifications::OnAtlasUnloaded, temp); return; } + iterator->second.m_atlas->SetTexture(texture); TextureAtlasNotificationBus::Broadcast(&TextureAtlasNotifications::OnAtlasReloaded, iterator->second.m_atlas); break; @@ -186,30 +193,23 @@ namespace TextureAtlasNamespace delete[] buffer; if (loadedAtlas) { - // Get the image path based on the atlas path + // Convert to image path based on the atlas path AZStd::string imagePath = path; - AzFramework::StringFunc::Path::ReplaceExtension(imagePath, "dds"); - - // Load the image in - // ToDo: Update to work with Atom? LYN-3680 - // uint32 loadTextureFlags = (FT_USAGE_ALLOWREADSRGB | FT_DONT_STREAM); - ITexture* texture = nullptr; - - if (!texture || !texture->IsTextureLoaded()) + AzFramework::StringFunc::Path::ReplaceExtension(imagePath, "texatlas"); + AZ::Data::Instance texture = LoadAtlasImage(imagePath); + if (!texture) { - gEnv->pSystem->Warning(VALIDATOR_MODULE_UNKNOWN, - VALIDATOR_WARNING, - VALIDATOR_FLAG_FILE | VALIDATOR_FLAG_TEXTURE, - imagePath.c_str(), - "No texture file found for texture atlas: %s. " + AZ_Error("TextureAtlasSystemComponent", + false, + "Failed to find or create an image instance for texture atlas '%s'" "NOTE: File must be in current project or a gem.", - imagePath.c_str()); + path.c_str()); + delete loadedAtlas; return nullptr; } else { - texture->SetFilter(FILTER_LINEAR); // Add the atlas to the list AtlasInfo info(loadedAtlas, assetPath); ++info.m_refs; @@ -241,11 +241,7 @@ namespace TextureAtlasNamespace // Tell the renderer to release the texture. if (temp.m_atlas && temp.m_atlas->GetTexture()) { - SResourceAsync* pInfo = new SResourceAsync(); - pInfo->eClassName = eRCN_Texture; - pInfo->pResource = temp.m_atlas->GetTexture(); - // ToDo: Update to work with Atom? LYN-3680 - // ???->ReleaseResourceAsync(pInfo); + temp.m_atlas->GetTexture().reset(); } // Delete the atlas SAFE_DELETE(temp.m_atlas); diff --git a/Gems/TextureAtlas/Code/textureatlas_builder_files.cmake b/Gems/TextureAtlas/Code/textureatlas_builder_files.cmake new file mode 100644 index 0000000000..51cb991aa8 --- /dev/null +++ b/Gems/TextureAtlas/Code/textureatlas_builder_files.cmake @@ -0,0 +1,17 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Source/Editor/AtlasBuilderComponent.h + Source/Editor/AtlasBuilderComponent.cpp + Source/Editor/AtlasBuilderWorker.h + Source/Editor/AtlasBuilderWorker.cpp +) diff --git a/Gems/TextureAtlas/Code/textureatlas_files.cmake b/Gems/TextureAtlas/Code/textureatlas_files.cmake index 2da4e9ce1d..c45c1d49a8 100644 --- a/Gems/TextureAtlas/Code/textureatlas_files.cmake +++ b/Gems/TextureAtlas/Code/textureatlas_files.cmake @@ -15,7 +15,6 @@ set(FILES Include/TextureAtlas/TextureAtlasBus.h Include/TextureAtlas/TextureAtlasNotificationBus.h Include/TextureAtlas/TextureAtlas.h - Source/TextureAtlasModule.cpp Source/TextureAtlasSystemComponent.cpp Source/TextureAtlasSystemComponent.h Source/TextureAtlasImpl.h diff --git a/Gems/TextureAtlas/Code/textureatlas_module_files.cmake b/Gems/TextureAtlas/Code/textureatlas_module_files.cmake new file mode 100644 index 0000000000..00e18d92bc --- /dev/null +++ b/Gems/TextureAtlas/Code/textureatlas_module_files.cmake @@ -0,0 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Source/TextureAtlasModule.cpp +) diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo index 95b548a2eb..f808dda121 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo index 2eb5be8e93..54e075dd32 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -64,7 +64,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo index 95b548a2eb..f808dda121 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo index 95b548a2eb..f808dda121 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp b/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp index 597d0ca079..a185c9a601 100644 --- a/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp +++ b/Gems/Vegetation/Code/Source/DynamicSliceInstanceSpawner.cpp @@ -309,7 +309,7 @@ namespace Vegetation // Create a Transform that represents our instance. AZ::Transform world = AZ::Transform::CreateFromQuaternionAndTranslation(instanceData.m_alignment * instanceData.m_rotation, instanceData.m_position); - world.MultiplyByScale(AZ::Vector3(instanceData.m_scale)); + world.MultiplyByUniformScale(instanceData.m_scale); // Request a new dynamic slice instance. AzFramework::SliceInstantiationTicket* ticket = new AzFramework::SliceInstantiationTicket(); diff --git a/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp b/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp index 41d0a4c5e3..cda82c82b0 100644 --- a/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp +++ b/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp @@ -139,7 +139,7 @@ namespace WhiteBox { const AZ::Transform worldTransformWithoutScale = [worldTransform = world]() mutable { - worldTransform.SetScale(AZ::Vector3::CreateOne()); + worldTransform.SetUniformScale(1.0f); return worldTransform; }(); diff --git a/Gems/WhiteBox/Code/Source/Util/WhiteBoxMathUtil.cpp b/Gems/WhiteBox/Code/Source/Util/WhiteBoxMathUtil.cpp index 9d423d5519..840d1f4590 100644 --- a/Gems/WhiteBox/Code/Source/Util/WhiteBoxMathUtil.cpp +++ b/Gems/WhiteBox/Code/Source/Util/WhiteBoxMathUtil.cpp @@ -73,7 +73,7 @@ namespace WhiteBox const AZ::Transform spaceFromLocal = localFromSpace.GetInverse(); const AZ::Vector3 spacePosition = spaceFromLocal.TransformPoint(localPosition); const AZ::Vector3 spaceScaledPosition = - AZ::Transform::CreateScale(AZ::Vector3(scale)).TransformPoint(spacePosition); + AZ::Transform::CreateUniformScale(scale).TransformPoint(spacePosition); return localFromSpace.TransformPoint(spaceScaledPosition); } diff --git a/README.md b/README.md index 333ca795e8..e0442adc16 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,57 @@ Welcome to the Project Spectra Private Preview. This is a confidential pre-release project; your use is subject to the nondisclosure agreement between you (or your organization) and Amazon. Do not disclose the existence of this project, your participation in it, or any of the materials provided, to any unauthorized third party. To request access for a third party, please contact [Royal O'Brien, obriroya@amazon.com](mailto:obriroya@amazon.com). +## Full instructions can be found here: +### https://docs.o3de.org/docs/welcome-guide/setup/setup-from-github/ +(Note: Contact Royal or [Doug Erickson, dougeric@amazon.com](mailto:dougeric@amazon.com) for access) + +## Updates to this readme +May 14, 2021 +- Removed instructions for the 3rdParty zip file and downloader URL. This is no longer a requirement. +- Updated instructions for dependencies +- Links to full documentation + +April 7-13, 2021 +- Updates to the 3rdParty zip file + +March 25, 2021 +- Initial commit for instructions + ## Download and Install This repository uses Git LFS for storing large binary files. You will need to create a Github personal access token to authenticate with the LFS service. +To install Git LFS, download the binary here: https://git-lfs.github.com/. + +After installation, you will need to install the necessary git hooks with this command +``` +git lfs install +``` ### Create a Git Personal Access Token -You will need your personal access token credentials to authenticate when you clone the repository. +You will need your personal access token credentials to authenticate when you clone the repository and when downloading objects from Git LFS [Create a personal access token with the 'repo' scope.](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) +During the clone operation, you will be prompted to enter a password. Your token will be used as the password. You will also be prompted a second time for Git LFS. ### (Recommended) Verify you have a credential manager installed to store your credentials -Recent versions of Git install a credential manager to store your credentials so you don't have to put in the credentials for every request. +Recent versions of Git install a credential manager to store your credentials so you don't have to put in the credentials for every request. + It is highly recommended you check that you have a [credential manager installed and configured](https://github.com/microsoft/Git-Credential-Manager-Core) +For Linux and Mac, use the following commands to store credentials + +Linux: +``` +git config --global credential.helper cache +``` +Mac: +``` +git config --global credential.helper osxkeychain +``` ### Clone the repository @@ -43,67 +77,96 @@ Filtering content: 100% (3853/3853), 621.43 MiB | 881.00 KiB/s, done. ``` -If you have the Git credential manager core installed, you should not be prompted for your credentials anymore. +If you have the Git credential manager core or other credential helpers installed, you should not be prompted for your credentials anymore. ## Building the Engine ### Build Requirements and redistributables +#### Windows -* Visual Studio 2019 16.9.2 (All versions supported, including Community): [https://visualstudio.microsoft.com/downloads/](https://visualstudio.microsoft.com/downloads/) +* Visual Studio 2019 16.9.2 minimum (All versions supported, including Community): [https://visualstudio.microsoft.com/downloads/](https://visualstudio.microsoft.com/downloads/) * Install the following workloads: * Game Development with C++ * MSVC v142 - VS 2019 C++ x64/x86 -* Visual C++ redistributable: [https://visualstudio.microsoft.com/downloads/#other-family](https://visualstudio.microsoft.com/downloads/#other-family) -* FBXSDK for VS2015: [https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2016-1-2](https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2016-1-2) -* WWise - 2019.2.8.7432: [https://www.audiokinetic.com/download/](https://www.audiokinetic.com/download/) -* CMake 3.19.1: [https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi](https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi) + * C++ 2019 redistributable update +* CMake 3.19.1 minimum: [https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi](https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi) + +#### Optional -### Build Steps +* WWise - 2019.2.8.7432 minimum: [https://www.audiokinetic.com/download/](https://www.audiokinetic.com/download/) + * Note: This requires registration and installation of a client to download + * You will also need to set a environment variable: `set LY_WWISE_INSTALL_PATH=` + * For example: `set LY_WWISE_INSTALL_PATH="C:\Program Files (x86)\Audiokinetic\Wwise 2019.2.8.7432"` -1. Download the 3rdParty zip file from here: **[https://d2c171ws20a1rv.cloudfront.net/3rdParty-windows-no-symbols-rev13.zip](https://d2c171ws20a1rv.cloudfront.net/3rdParty-windows-no-symbols-rev13.zip)** -2. Unzip this file into a writable folder. This will also act as a cache location for the 3rdParty downloader by default (configurable with the `LY_PACKAGE_DOWNLOAD_CACHE_LOCATION` environment variable) -3. Install the following redistributables to the following: +### Quick Start Build Steps + +1. Create a writable folder to cache 3rd Party dependencies. You can also use this to store other redistributable SDKs. + + > For the 0.5 branch - Create an empty text file named `3rdParty.txt` in this folder, to allow a legacy CMake validator to pass + +1. Install the following redistributables to the following: - Visual Studio and VC++ redistributable can be installed to any location - - FBXSDK should be installed to `<3rdParty path>\FbxSdk\2016.1.2-az.1`. See the README in this folder for details - - WWise should be installed to: `<3rdParty Path>\Wwise\2019.2.8.7432` - - CMake should be installed to: `<3rdParty Path>\CMake\3.19.1` -4. Add the following environment variables through the command line + - CMake can be installed to any location, as long as it's available in the system path, otherwise it can be installed to: `<3rdParty Path>\CMake\3.19.1` + - WWise can be installed anywhere, but you will need to set an environment variable for CMake to detect it: `set LY_WWISE_INSTALL_PATH=` + +1. Navigate into the repo folder, then download the python runtime with this command + + > For the 0.5 branch - Set this environment variable prior to the `get_python` command below: + > ``` + > set LY_PACKAGE_SERVER_URLS=https://d2c171ws20a1rv.cloudfront.net + > ``` + ``` - set LY_3RDPARTY_PATH= - set LY_PACKAGE_SERVER_URLS="https://d2c171ws20a1rv.cloudfront.net" + python\get_python.bat ``` -5. Configure the source into a solution using this command line, replacing to a path you've created +1. While still within the repo folder, register the engine with this command: ``` - cmake -B -S -G "Visual Studio 16 2019" -DLY_3RDPARTY_PATH=%LY_3RDPARTY_PATH% -DLY_UNITY_BUILD=ON -DLY_PROJECTS=AutomatedTesting + scripts\o3de.bat register --this-engine ``` -6. Alternatively, you can do this through the CMake GUI: +1. Configure the source into a solution using this command line, replacing and <3rdParty cache path> to a path you've created: + ``` + cmake -B -S -G "Visual Studio 16" -DLY_3RDPARTY_PATH=<3rdParty cache path> -DLY_UNITY_BUILD=ON -DLY_PROJECTS=AutomatedTesting + ``` + > Note: Do not use trailing slashes for the <3rdParty cache path> + +1. Alternatively, you can do this through the CMake GUI: 1. Start `cmake-gui.exe` - 2. Select the local path of the repo under "Where is the source code" - 3. Select a path where to build binaries under "Where to build the binaries" - 4. Click "Configure" - 5. Wait for the key values to populate. Fill in the fields that are relevant, including `LY_3RDPARTY_PATH`, `LY_PACKAGE_SERVER_URLS`, and `LY_PROJECTS` - 6. Click "Generate" + 1. Select the local path of the repo under "Where is the source code" + 1. Select a path where to build binaries under "Where to build the binaries" + 1. Click "Configure" + 1. Wait for the key values to populate. Fill in the fields that are relevant, including `LY_3RDPARTY_PATH` and `LY_PROJECTS` + 1. Click "Generate" -7. The configuration of the solution is complete. To build the Editor and AssetProcessor to binaries, run this command inside your repo: +1. The configuration of the solution is complete. To build the Editor and AssetProcessor to binaries, run this command inside your repo: ``` - cmake --build --target AutomatedTesting.GameLauncher AssetProcessor Editor --config profile -- /m + cmake --build --target AutomatedTesting.GameLauncher AssetProcessor Editor --config profile -- /m ``` -8. This will compile after some time and binaries will be available in the build path you've specified +1. This will compile after some time and binaries will be available in the build path you've specified ### Setting up new projects -1. Setup new projects using this command +1. Setup new projects using the `o3de create-project` command. In the 0.5 branch, the project directory must be a subdirectory in the repo folder. ``` - \scripts\o3de.bat create-project --project-path + \scripts\o3de.bat create-project --project-path ``` -2. Once you're ready to build the project, run the same set of commands to configure and build: +1. Register the engine to the project ``` - cmake -B -S -G "Visual Studio 16 2019" -DLY_3RDPARTY_PATH=%LY_3RDPARTY_PATH% -DLY_PROJECTS= -DLY_MONOLITHIC_GAME=1 + \scripts\o3de.bat register --project-path + ``` +1. Once you're ready to build the project, run the same set of commands to configure and build: + ``` + cmake -B -S -G "Visual Studio 16" -DLY_3RDPARTY_PATH=<3rdParty cache path> - cmake --build --target --config profile -- /m + // For the 0.5 branch, you must build a new Editor for each project: + cmake --build --target Editor --config profile -- /m + + // For all other branches, just build the project: + cmake --build --target --config profile -- /m ``` + +For a tutorial on project configuration, see [Creating Projects Using the Command Line](https://docs.o3de.org/docs/welcome-guide/get-started/project-config/creating-projects-using-cli) in the documentation. ## License diff --git a/Registry/AssetProcessorPlatformConfig.setreg b/Registry/AssetProcessorPlatformConfig.setreg index 5f397db06b..2147842da7 100644 --- a/Registry/AssetProcessorPlatformConfig.setreg +++ b/Registry/AssetProcessorPlatformConfig.setreg @@ -24,13 +24,13 @@ "Platform pc": { "tags": "tools,renderer,dx12,vulkan,null" }, - "Platform es3": { + "Platform android": { "tags": "android,mobile,renderer,vulkan" }, "Platform ios": { "tags": "mobile,renderer,metal" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer,metal,null" }, // this is an example of a headless platform that has no renderer. @@ -43,9 +43,9 @@ // To enable any additional platform, just uncomment the appropriate line below. "Platforms": { //"pc": "enabled", - //"es3": "enabled", + //"android": "enabled", //"ios": "enabled", - //"osx_gl": "enabled", + //"mac": "enabled", //"server": "enabled" }, // ---- The number of worker jobs, 0 means use the number of Logical Cores @@ -95,11 +95,11 @@ // "exclude": "(comma seperated platform tags or identifiers)" // } // For example if you want to include a scan folder only for platforms that have the platform tags tools and renderer - // but omit it for platform osx_gl, you will have a scanfolder rule like + // but omit it for platform mac, you will have a scanfolder rule like // "ScanFolder (unique identifier)": { // "watch": "@ROOT@/foo", // "include": "tools, renderer", - // "exclude": "osx_gl" + // "exclude": "mac" // } "ScanFolder Game": { diff --git a/Registry/bootstrap.setreg b/Registry/bootstrap.setreg index b0c954a127..ccbf744232 100644 --- a/Registry/bootstrap.setreg +++ b/Registry/bootstrap.setreg @@ -8,9 +8,9 @@ "ios_remote_filesystem": 0, "mac_remote_filesystem": 0, "assets": "pc", - "android_assets": "es3", + "android_assets": "android", "ios_assets": "ios", - "mac_assets": "osx_gl", + "mac_assets": "mac", "allowed_list": "", "remote_ip": "127.0.0.1", "remote_port": 45643, diff --git a/Registry/gem_autoload.serializecontexttools.setreg b/Registry/gem_autoload.serializecontexttools.setreg index 1f4a8931c5..e7e88dd6a6 100644 --- a/Registry/gem_autoload.serializecontexttools.setreg +++ b/Registry/gem_autoload.serializecontexttools.setreg @@ -9,6 +9,18 @@ }, "PythonAssetBuilder.Editor": { "AutoLoad": false + }, + "AWSCore.Editor": { + "AutoLoad": false + }, + "AWSClientAuth": { + "AutoLoad": false + }, + "AWSClientAuth.Editor": { + "AutoLoad": false + }, + "AWSMetrics": { + "AutoLoad": false } } } diff --git a/Templates/DefaultProject/Template/preview.png b/Templates/DefaultProject/Template/preview.png index 2191a0ebc2..3d4fe78063 100644 --- a/Templates/DefaultProject/Template/preview.png +++ b/Templates/DefaultProject/Template/preview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6 -size 5466 +oid sha256:40949893ed7009eeaa90b7ce6057cb6be9dfaf7b162e3c26ba9dadf985939d7d +size 2038 diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py index 5f6db7ac05..ec4b43b023 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py @@ -16,8 +16,10 @@ import pathlib import warnings from abc import ABCMeta, abstractmethod +import ly_test_tools._internal.pytest_plugin from ly_test_tools.environment.file_system import find_ancestor_file + def _find_engine_root(initial_path): # type: (str) -> str """ @@ -34,11 +36,9 @@ def _find_engine_root(initial_path): # Assumes folder structure similar to: engine_root/dev/Tools/.../ly_test_tools/builtin for _ in range(15): if os.path.exists(os.path.join(current_dir, root_file)): - # The parent of the directory containing the engineroot.txt is the root directory - engine_root = current_dir - return engine_root - # Using an explicit else to avoid aberrant behavior from following filesystem links - else: + # parent of the directory containing root_file + return current_dir + else: # explicit else avoids aberrant behavior from following filesystem links current_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir)) raise OSError(f"Unable to find engine root directory. Verify root file '{root_file}' exists") @@ -50,8 +50,10 @@ def _find_project_json(engine_root, project): Find the project.json file for this project. :return: Full path to the project.json file """ - project_json = find_ancestor_file('project.json') - if not project_json: + # First check relative to defined build directory, for external projects which configure through SDK settings + project_json = find_ancestor_file(target_file_name='project.json', + start_path=ly_test_tools._internal.pytest_plugin.build_directory) + if not project_json: # check internally for a project bundled with the engine project_json = os.path.join(engine_root, project, 'project.json') return project_json @@ -250,9 +252,6 @@ class AbstractResourceLocator(object): """ return os.path.join(self.build_directory(), 'CrySCompileServer') - def bootstrap_config_file(self): - return os.path.join(self.engine_root(), 'bootstrap.cfg') - def asset_processor_config_file(self): return os.path.join(self.engine_root(), 'Registry', 'AssetProcessorPlatformConfig.setreg') diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py index 453e80ac41..4e19a3955a 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py @@ -21,8 +21,8 @@ from ly_test_tools._internal.managers.abstract_resource_locator import AbstractR logger = logging.getLogger(__name__) -CACHE_DIR = 'osx_gl' -CONFIG_FILE = 'system_osx_osx_gl.cfg' +CACHE_DIR = 'mac' +CONFIG_FILE = 'system_osx_mac.cfg' class _MacResourceLocator(AbstractResourceLocator): @@ -33,7 +33,7 @@ class _MacResourceLocator(AbstractResourceLocator): def platform_config_file(self): """ Return the path to the platform config file. - ex. engine_root/dev/system_osx_osx_gl.cfg + ex. engine_root/dev/system_osx_mac.cfg :return: path to the platform config file """ return os.path.join(self.engine_root(), CONFIG_FILE) diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py index 80ac413dbb..db6e7d713d 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py @@ -39,7 +39,7 @@ class _WindowsResourceLocator(AbstractResourceLocator): def platform_config_file(self): """ Return the path to the platform config file. - ex. engine_root/dev/system_osx_osx_gl.cfg + ex. engine_root/dev/system_osx_mac.cfg :return: path to the platform config file """ return os.path.join(self.engine_root(), CONFIG_FILE) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py index 1b0afb3efe..386de62048 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py @@ -123,7 +123,6 @@ class Launcher(object): """ backup_path = self.workspace.settings.get_temp_path() log.debug(f"Performing automatic backup of bootstrap, platform and user settings in path {backup_path}") - self.workspace.settings.backup_bootstrap_settings(backup_path) self.workspace.settings.backup_platform_settings(backup_path) self.workspace.settings.backup_shader_compiler_settings(backup_path) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py index 5507588ae3..8e2b93c20a 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py @@ -36,10 +36,10 @@ DEFAULT_TIMEOUT_HOURS = 8 DEFAULT_TIMEOUT_SECONDS = 300 ASSET_PROCESSOR_PLATFORM_MAP = { - 'android': 'es3', + 'android': 'android', 'ios': 'ios', 'linux': 'linux', # Not fully implemented, see SPEC-2501 - 'mac': 'osx_gl', + 'mac': 'mac', 'windows': 'pc', } @@ -664,8 +664,7 @@ class AssetProcessor(object): make_dir = os.path.join(self._temp_asset_root, copy_dir) if not os.path.isdir(make_dir): os.makedirs(make_dir) - for copyfile_name in ['bootstrap.cfg', - 'Registry/AssetProcessorPlatformConfig.setreg', + for copyfile_name in ['Registry/AssetProcessorPlatformConfig.setreg', os.path.join(self._workspace.project, "project.json"), os.path.join('Assets', 'Engine', 'exclude.filetag')]: shutil.copyfile(os.path.join(self._workspace.paths.engine_root(), copyfile_name), diff --git a/Tools/LyTestTools/ly_test_tools/o3de/settings.py b/Tools/LyTestTools/ly_test_tools/o3de/settings.py index 9677a4d3a3..a1e83abe51 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/settings.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/settings.py @@ -57,14 +57,6 @@ class LySettings(object): """ self._backup_settings(self._resource_locator.platform_config_file(), backup_path) - def backup_bootstrap_settings(self, backup_path=None): - """ - Creates a backup of the bootstrap settings file (~/dev/bootstrap.cfg) in the backup_path. If no path is - provided, it will store in the workspace temp path (the contents of the workspace temp directory are removed - during workspace teardown) - """ - self._backup_settings(self._resource_locator.bootstrap_config_file(), backup_path) - def backup_shader_compiler_settings(self, backup_path=None): self._backup_settings(self._resource_locator.shader_compiler_config_file(), backup_path) @@ -79,14 +71,6 @@ class LySettings(object): """ self._restore_settings(self._resource_locator.platform_config_file(), backup_path) - def restore_bootstrap_settings(self, backup_path=None): - """ - Restores the bootstrap settings file (~/dev/bootstrap.cfg) from its backup. - The backup is stored in the backup_path. - If no backup_path is provided, it will attempt to retrieve the backup from the workspace temp path. - """ - self._restore_settings(self._resource_locator.bootstrap_config_file(), backup_path) - def restore_shader_compiler_settings(self, backup_path=None): self._restore_settings(self._resource_locator.shader_compiler_config_file(), backup_path) diff --git a/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py b/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py index e46fefa461..12286b3dd7 100755 --- a/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py +++ b/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py @@ -158,13 +158,6 @@ class TestAbstractResourceLocator(object): assert mock_abstract_resource_locator.shader_cache() == expected_path - def test_BootstrapConfigFile_IsCalled_ReturnBootstrapConfigFilePath(self): - mock_abstract_resource_locator = abstract_resource_locator.AbstractResourceLocator( - mock_build_directory, mock_project) - expected_path = os.path.join(mock_abstract_resource_locator.engine_root(), 'bootstrap.cfg') - - assert mock_abstract_resource_locator.bootstrap_config_file() == expected_path - def test_AssetProcessorConfigFile_IsCalled_ReturnsAssetProcessorConfigFilePath(self): mock_abstract_resource_locator = abstract_resource_locator.AbstractResourceLocator( mock_build_directory, mock_project) diff --git a/bootstrap.cfg b/bootstrap.cfg deleted file mode 100644 index 858e9093f5..0000000000 --- a/bootstrap.cfg +++ /dev/null @@ -1,12 +0,0 @@ -; This file is deprecated and is only use currently for setting the path when running O3DE in an engine-centric manner -; By engine-centric, what is meant is using CMake to configure from the directory and passing in the LY_PROJECTS value - -project_path=AutomatedTesting - -; The Asset Processor Specific settings are now the /Engine/Registry/bootstrap.setreg settings -; The Engine specific settings can be overridden in order of least precedence to most -; 1. Override the settings in a "/Registry/*.setreg(patch)" file (Shared per Gem Settings) -; 2. Override the settings in a "/Registry/*.setreg(patch)" file (Shared per Project Settings) -; 3. Override the settings in a "/user/Registry/*.setreg(patch)" file (User per Project Settings) -; 4. Override the settings in a "~/.o3de/Registry/*.setreg(patch)" file (User Global Settings) -; Where "~" is %USERPROFILE% on Windows and $HOME on Unix like platforms diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 3cd453b943..3220271b42 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -10,40 +10,41 @@ # # shared by other platforms: -ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) -ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) -ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) -ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) -ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) -ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) -ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) -ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) -ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) -ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) -ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) -ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) -ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) -ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) +ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) +ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) +ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) +ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) +ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) +ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) +ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) +ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) +ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) +ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) +ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) +ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) +ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) +ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) +ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) +ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) +ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) +ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: -ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-linux TARGETS AWSGameLiftServerSDK PACKAGE_HASH a8149a95bd100384af6ade97e2b21a56173740d921e6c3da8188cd51554d39af) -ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-linux TARGETS freetype PACKAGE_HASH 9ad246873067717962c6b780d28a5ce3cef3321b73c9aea746a039c798f52e93) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-linux TARGETS tiff PACKAGE_HASH ae92b4d3b189c42ef644abc5cac865d1fb2eb7cb5622ec17e35642b00d1a0a76) -ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-linux TARGETS AWSNativeSDK PACKAGE_HASH b4db38de49d35a5f7500aed7f4aee5ec511dd3b584ee06fe9097885690191a5d) -ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) -ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-linux TARGETS PhysX PACKAGE_HASH e3ca36106a8dbf1524709f8bb82d520920ebd3ff3a92672d382efff406c75ee3) -ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-linux TARGETS etc2comp PACKAGE_HASH 9283aa5db5bb7fb90a0ddb7a9f3895317c8ebe8044943124bbb3673a41407430) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-linux TARGETS mcpp PACKAGE_HASH 0aa713f3f2c156cb2f17d9b800aed8acf9df5ab167c48b679853ecb040da9a67) -ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux TARGETS mikkelsen PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8) -ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) -ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-linux TARGETS GoogleBenchmark PACKAGE_HASH 4038878f337fc7e0274f0230f71851b385b2e0327c495fc3dd3d1c18a807928d) -ly_associate_package(PACKAGE_NAME unwind-1.2.1-linux TARGETS unwind PACKAGE_HASH 3453265fb056e25432f611a61546a25f60388e315515ad39007b5925dd054a77) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-linux TARGETS Qt PACKAGE_HASH b7d9932647f4b138b3f0b124d70debd250d2a8a6dca52b04dcbe82c6369d48ca) -ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux TARGETS libsamplerate PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261) -ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) +ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-linux TARGETS AWSGameLiftServerSDK PACKAGE_HASH a8149a95bd100384af6ade97e2b21a56173740d921e6c3da8188cd51554d39af) +ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-linux TARGETS freetype PACKAGE_HASH 9ad246873067717962c6b780d28a5ce3cef3321b73c9aea746a039c798f52e93) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-linux TARGETS tiff PACKAGE_HASH ae92b4d3b189c42ef644abc5cac865d1fb2eb7cb5622ec17e35642b00d1a0a76) +ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-linux TARGETS AWSNativeSDK PACKAGE_HASH b4db38de49d35a5f7500aed7f4aee5ec511dd3b584ee06fe9097885690191a5d) +ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) +ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-linux TARGETS PhysX PACKAGE_HASH e3ca36106a8dbf1524709f8bb82d520920ebd3ff3a92672d382efff406c75ee3) +ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-linux TARGETS etc2comp PACKAGE_HASH 9283aa5db5bb7fb90a0ddb7a9f3895317c8ebe8044943124bbb3673a41407430) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-linux TARGETS mcpp PACKAGE_HASH 0aa713f3f2c156cb2f17d9b800aed8acf9df5ab167c48b679853ecb040da9a67) +ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux TARGETS mikkelsen PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8) +ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) +ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-linux TARGETS GoogleBenchmark PACKAGE_HASH 4038878f337fc7e0274f0230f71851b385b2e0327c495fc3dd3d1c18a807928d) +ly_associate_package(PACKAGE_NAME unwind-1.2.1-linux TARGETS unwind PACKAGE_HASH 3453265fb056e25432f611a61546a25f60388e315515ad39007b5925dd054a77) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-linux TARGETS Qt PACKAGE_HASH b7d9932647f4b138b3f0b124d70debd250d2a8a6dca52b04dcbe82c6369d48ca) +ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux TARGETS libsamplerate PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261) +ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) +ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-linux TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 235606f98512c076a1ba84a8402ad24ac21945998abcea264e8e204678efc0ba) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index cf5ecaa15b..f85048d13e 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -10,41 +10,41 @@ # # shared by other platforms: -ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) -ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) -ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) -ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) -ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) -ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) -ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) -ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) -ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) -ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) -ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) -ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-mac TARGETS SPIRVCross PACKAGE_HASH 78c6376ed2fd195b9b1f5fb2b56e5267a32c3aa21fb399e905308de470eb4515) -ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 4e97484f8fcf73fc39f22fc85ae86933a8f2e3ba0748fcec128bce05795035a6) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) -ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) -ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) -ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) +ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) +ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) +ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) +ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) +ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) +ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) +ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) +ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) +ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) +ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) +ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) +ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) +ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) +ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) +ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) +ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) +ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) +ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: -ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-mac-ios TARGETS freetype PACKAGE_HASH 67b4f57aed92082d3fd7c16aa244a7d908d90122c296b0a63f73e0a0b8761977) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-mac-ios TARGETS tiff PACKAGE_HASH a23ae1f8991a29f8e5df09d6d5b00d7768a740f90752cef465558c1768343709) -ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-mac TARGETS AWSNativeSDK PACKAGE_HASH 21920372e90355407578b45ac19580df1463a39a25a867bcd0ffd8b385c8254a) -ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) -ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-mac TARGETS PhysX PACKAGE_HASH 149f5e9b44bd27291b1c4772f5e89a1e0efa88eef73c7e0b188935ed4d0c4a70) -ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-mac TARGETS etc2comp PACKAGE_HASH 1966ab101c89db7ecf30984917e0a48c0d02ee0e4d65b798743842b9469c0818) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-mac TARGETS mcpp PACKAGE_HASH 48a9c5197bf72843fb9ac44825501ee16bbe3e72e086a32b8c9c05bf47db12ab) -ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-mac TARGETS mikkelsen PACKAGE_HASH 83af99ca8bee123684ad254263add556f0cf49486c0b3e32e6d303535714e505) -ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-mac TARGETS googletest PACKAGE_HASH cbf020d5ef976c5db8b6e894c6c63151ade85ed98e7c502729dd20172acae5a8) -ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-mac TARGETS GoogleBenchmark PACKAGE_HASH ad25de0146769c91e179953d845de2bec8ed4a691f973f47e3eb37639381f665) -ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-mac TARGETS OpenSSL PACKAGE_HASH 28adc1c0616ac0482b2a9d7b4a3a3635a1020e87b163f8aba687c501cf35f96c) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-mac TARGETS Qt PACKAGE_HASH 4723ac43b19d4633c3fa4b9642f27c992d30cdc689f769f82869786f1c22a728) -ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) +ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 2bede9a7ef3573027c005e38139237559eebf845c13ffb54c33c5b8675f962e2) +ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-mac TARGETS SPIRVCross PACKAGE_HASH 78c6376ed2fd195b9b1f5fb2b56e5267a32c3aa21fb399e905308de470eb4515) +ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-mac-ios TARGETS freetype PACKAGE_HASH 67b4f57aed92082d3fd7c16aa244a7d908d90122c296b0a63f73e0a0b8761977) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-mac-ios TARGETS tiff PACKAGE_HASH a23ae1f8991a29f8e5df09d6d5b00d7768a740f90752cef465558c1768343709) +ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-mac TARGETS AWSNativeSDK PACKAGE_HASH 21920372e90355407578b45ac19580df1463a39a25a867bcd0ffd8b385c8254a) +ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) +ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-mac TARGETS PhysX PACKAGE_HASH 149f5e9b44bd27291b1c4772f5e89a1e0efa88eef73c7e0b188935ed4d0c4a70) +ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-mac TARGETS etc2comp PACKAGE_HASH 1966ab101c89db7ecf30984917e0a48c0d02ee0e4d65b798743842b9469c0818) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-mac TARGETS mcpp PACKAGE_HASH 48a9c5197bf72843fb9ac44825501ee16bbe3e72e086a32b8c9c05bf47db12ab) +ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-mac TARGETS mikkelsen PACKAGE_HASH 83af99ca8bee123684ad254263add556f0cf49486c0b3e32e6d303535714e505) +ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-mac TARGETS googletest PACKAGE_HASH cbf020d5ef976c5db8b6e894c6c63151ade85ed98e7c502729dd20172acae5a8) +ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-mac TARGETS GoogleBenchmark PACKAGE_HASH ad25de0146769c91e179953d845de2bec8ed4a691f973f47e3eb37639381f665) +ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-mac TARGETS OpenSSL PACKAGE_HASH 28adc1c0616ac0482b2a9d7b4a3a3635a1020e87b163f8aba687c501cf35f96c) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-mac TARGETS Qt PACKAGE_HASH 4723ac43b19d4633c3fa4b9642f27c992d30cdc689f769f82869786f1c22a728) +ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 8fc009c601..fa1326b63d 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -10,49 +10,49 @@ # # shared by other platforms: -ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) -ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) -ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) -ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) -ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) -ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) -ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) -ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) -ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) -ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) -ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) -ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) -ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 2c60297758d73f7833911e5ae3006fe0b10ced6e0b1b54764b33ae2b86e0d41d) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) -ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) -ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) -ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME Blast-1.1.7-rev1-multiplatform TARGETS Blast PACKAGE_HASH 36b8f393bcd25d0f85cfc7a831ebbdac881e6054c4f0735649966aa6aa86e6f0) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) +ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) +ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) +ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) +ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) +ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) +ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) +ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) +ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) +ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) +ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) +ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) +ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) +ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) +ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) +ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) +ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) +ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) +ly_associate_package(PACKAGE_NAME Blast-1.1.7-rev1-multiplatform TARGETS Blast PACKAGE_HASH 36b8f393bcd25d0f85cfc7a831ebbdac881e6054c4f0735649966aa6aa86e6f0) +ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: -ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) -ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-windows TARGETS freetype PACKAGE_HASH 88dedc86ccb8c92f14c2c033e51ee7d828fa08eafd6475c6aa963938a99f4bf3) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.14-windows TARGETS tiff PACKAGE_HASH ab60d1398e4e1e375ec0f1a00cdb1d812a07c0096d827db575ce52dd6d714207) -ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-windows TARGETS AWSNativeSDK PACKAGE_HASH 929873d4252c464620a9d288e41bd5d47c0bd22750aeb3a1caa68a3da8247c48) -ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) -ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-windows TARGETS PhysX PACKAGE_HASH 198bed89d1aae7caaf5dadba24cee56235fe41725d004b64040d4e50d0f3aa1a) -ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-windows TARGETS etc2comp PACKAGE_HASH fc9ae937b2ec0d42d5e7d0e9e8c80e5e4d257673fb33bc9b7d6db76002117123) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-windows TARGETS mcpp PACKAGE_HASH 511672598fa319bfb8db87f965b59abff1620bb7c1dcf7669e039a8acd8d3ff8) -ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-windows TARGETS mikkelsen PACKAGE_HASH 872c4d245a1c86139aa929f2b465b63ea4ea55b04ced50309135dd4597457a4e) -ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-windows TARGETS googletest PACKAGE_HASH 7e8f03ae8a01563124e3daa06386f25a2b311c10bb95bff05cae6c41eff83837) -ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-windows TARGETS GoogleBenchmark PACKAGE_HASH 0c94ca69ae8e7e4aab8e90032b5c82c5964410429f3dd9dbb1f9bf4fe032b1d4) -ly_associate_package(PACKAGE_NAME d3dx12-headers-rev1-windows TARGETS d3dx12 PACKAGE_HASH 088c637159fba4a3e4c0cf08fb4921906fd4cca498939bd239db7c54b5b2f804) -ly_associate_package(PACKAGE_NAME pyside2-qt-5.15.1-rev2-windows TARGETS pyside2 PACKAGE_HASH c90f3efcc7c10e79b22a33467855ad861f9dbd2e909df27a5cba9db9fa3edd0f) -ly_associate_package(PACKAGE_NAME openimageio-2.1.16.0-rev2-windows TARGETS OpenImageIO PACKAGE_HASH 85a2a6cf35cbc4c967c56ca8074babf0955c5b490c90c6e6fd23c78db99fc282) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev2-windows TARGETS Qt PACKAGE_HASH 29966f22ec253dc9904e88ad48fe6b6a669302b2dc7049f2e2bbd4949e79e595) -ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-windows TARGETS libsamplerate PACKAGE_HASH dcf3c11a96f212a52e2c9241abde5c364ee90b0f32fe6eeb6dcdca01d491829f) -ly_associate_package(PACKAGE_NAME OpenMesh-8.1-rev1-windows TARGETS OpenMesh PACKAGE_HASH 1c1df639358526c368e790dfce40c45cbdfcfb1c9a041b9d7054a8949d88ee77) -ly_associate_package(PACKAGE_NAME civetweb-1.8-rev1-windows TARGETS civetweb PACKAGE_HASH 36d0e58a59bcdb4dd70493fb1b177aa0354c945b06c30416348fd326cf323dd4) -ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows TARGETS OpenSSL PACKAGE_HASH 9af1c50343f89146b4053101a7aeb20513319a3fe2f007e356d7ce25f9241040) -ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) +ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) +ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH decc53e97c7ddda9c7f853a30af7808a7b652a912f59ad2cd4bca5d308aae2c4) +ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) +ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-windows TARGETS freetype PACKAGE_HASH 88dedc86ccb8c92f14c2c033e51ee7d828fa08eafd6475c6aa963938a99f4bf3) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.14-windows TARGETS tiff PACKAGE_HASH ab60d1398e4e1e375ec0f1a00cdb1d812a07c0096d827db575ce52dd6d714207) +ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-windows TARGETS AWSNativeSDK PACKAGE_HASH 929873d4252c464620a9d288e41bd5d47c0bd22750aeb3a1caa68a3da8247c48) +ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) +ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-windows TARGETS PhysX PACKAGE_HASH 198bed89d1aae7caaf5dadba24cee56235fe41725d004b64040d4e50d0f3aa1a) +ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-windows TARGETS etc2comp PACKAGE_HASH fc9ae937b2ec0d42d5e7d0e9e8c80e5e4d257673fb33bc9b7d6db76002117123) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-windows TARGETS mcpp PACKAGE_HASH 511672598fa319bfb8db87f965b59abff1620bb7c1dcf7669e039a8acd8d3ff8) +ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-windows TARGETS mikkelsen PACKAGE_HASH 872c4d245a1c86139aa929f2b465b63ea4ea55b04ced50309135dd4597457a4e) +ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-windows TARGETS googletest PACKAGE_HASH 7e8f03ae8a01563124e3daa06386f25a2b311c10bb95bff05cae6c41eff83837) +ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-windows TARGETS GoogleBenchmark PACKAGE_HASH 0c94ca69ae8e7e4aab8e90032b5c82c5964410429f3dd9dbb1f9bf4fe032b1d4) +ly_associate_package(PACKAGE_NAME d3dx12-headers-rev1-windows TARGETS d3dx12 PACKAGE_HASH 088c637159fba4a3e4c0cf08fb4921906fd4cca498939bd239db7c54b5b2f804) +ly_associate_package(PACKAGE_NAME pyside2-qt-5.15.1-rev2-windows TARGETS pyside2 PACKAGE_HASH c90f3efcc7c10e79b22a33467855ad861f9dbd2e909df27a5cba9db9fa3edd0f) +ly_associate_package(PACKAGE_NAME openimageio-2.1.16.0-rev2-windows TARGETS OpenImageIO PACKAGE_HASH 85a2a6cf35cbc4c967c56ca8074babf0955c5b490c90c6e6fd23c78db99fc282) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev2-windows TARGETS Qt PACKAGE_HASH 29966f22ec253dc9904e88ad48fe6b6a669302b2dc7049f2e2bbd4949e79e595) +ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-windows TARGETS libsamplerate PACKAGE_HASH dcf3c11a96f212a52e2c9241abde5c364ee90b0f32fe6eeb6dcdca01d491829f) +ly_associate_package(PACKAGE_NAME OpenMesh-8.1-rev1-windows TARGETS OpenMesh PACKAGE_HASH 1c1df639358526c368e790dfce40c45cbdfcfb1c9a041b9d7054a8949d88ee77) +ly_associate_package(PACKAGE_NAME civetweb-1.8-rev1-windows TARGETS civetweb PACKAGE_HASH 36d0e58a59bcdb4dd70493fb1b177aa0354c945b06c30416348fd326cf323dd4) +ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows TARGETS OpenSSL PACKAGE_HASH 9af1c50343f89146b4053101a7aeb20513319a3fe2f007e356d7ce25f9241040) +ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index 2a65929cf6..b4d6fe308e 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -370,6 +370,7 @@ endfunction() #! ly_add_googletest: Adds a new RUN_TEST using for the specified target using the supplied command or fallback to running # googletest tests through AzTestRunner # \arg:NAME Name to for the test run target +# \arg:TARGET Name of the target module that is being run for tests. If not provided, will default to 'NAME' # \arg:TEST_REQUIRES(optional) List of system resources that are required to run this test. # Only available option is "gpu" # \arg:TEST_SUITE(optional) - "smoke" or "periodic" or "sandbox" - prevents the test from running normally @@ -384,14 +385,20 @@ function(ly_add_googletest) message(FATAL_ERROR "Platform does not support test targets") endif() - set(one_value_args NAME TEST_SUITE) + set(one_value_args NAME TARGET TEST_SUITE) set(multi_value_args TEST_COMMAND COMPONENT) cmake_parse_arguments(ly_add_googletest "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) + if (ly_add_googletest_TARGET) + set(target_name ${ly_add_googletest_TARGET}) + else() + set(target_name ${ly_add_googletest_NAME}) + endif() + # AzTestRunner modules only supports google test libraries, regardless of whether or not # google test suites are supported - set_property(GLOBAL APPEND PROPERTY LY_AZTESTRUNNER_TEST_MODULES "${ly_add_googletest_NAME}") + set_property(GLOBAL APPEND PROPERTY LY_AZTESTRUNNER_TEST_MODULES "${target_name}") if(NOT PAL_TRAIT_TEST_GOOGLE_TEST_SUPPORTED) return() @@ -400,7 +407,7 @@ function(ly_add_googletest) if (ly_add_googletest_TEST_SUITE AND NOT ly_add_googletest_TEST_SUITE STREQUAL "main") # if a suite is specified, we filter to only accept things which match that suite (in c++) - set(non_ide_params "-gtest_filter=*SUITE_${ly_add_googletest_TEST_SUITE}*") + set(non_ide_params "--gtest_filter=*SUITE_${ly_add_googletest_TEST_SUITE}*") else() # otherwise, if its the main suite we only runs things that dont have any of the other suites. # Note: it doesn't do AND, only 'or' - so specifying SUITE_main:REQUIRES_gpu @@ -412,11 +419,11 @@ function(ly_add_googletest) if(NOT ly_add_googletest_TEST_COMMAND) # Use the NAME parameter as the build target - set(build_target ${ly_add_googletest_NAME}) + set(build_target ${target_name}) ly_strip_target_namespace(TARGET ${build_target} OUTPUT_VARIABLE build_target) if(NOT TARGET ${build_target}) - message(FATAL_ERROR "A valid build target \"${build_target}\" for test run \"${ly_add_googletest_NAME}\" has not been found.\ + message(FATAL_ERROR "A valid build target \"${build_target}\" for test run \"${target_name}\" has not been found.\ A valid target via the TARGET parameter or a custom TEST_COMMAND must be supplied") endif() diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index f6a36afc89..8aba6ccb99 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -46,6 +46,7 @@ define_property(TARGET PROPERTY GEM_MODULE # # \arg:NAME name of the target # \arg:STATIC (bool) defines this target to be a static library +# \arg:GEM_STATIC (bool) defines this target to be a static library while also setting the GEM_MODULE property # \arg:SHARED (bool) defines this target to be a dynamic library # \arg:MODULE (bool) defines this target to be a module library # \arg:GEM_MODULE (bool) defines this target to be a module library while also marking the target as a "Gem" via the GEM_MODULE property @@ -76,7 +77,7 @@ define_property(TARGET PROPERTY GEM_MODULE # \arg:AUTOGEN_RULES a set of AutoGeneration rules to be passed to the AzAutoGen expansion system function(ly_add_target) - set(options STATIC SHARED MODULE GEM_MODULE HEADERONLY EXECUTABLE APPLICATION UNKNOWN IMPORTED AUTOMOC AUTOUIC AUTORCC NO_UNITY) + set(options STATIC GEM_STATIC SHARED MODULE GEM_MODULE HEADERONLY EXECUTABLE APPLICATION UNKNOWN IMPORTED AUTOMOC AUTOUIC AUTORCC NO_UNITY) set(oneValueArgs NAME NAMESPACE OUTPUT_SUBDIRECTORY OUTPUT_NAME) set(multiValueArgs FILES_CMAKE GENERATED_FILES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES PLATFORM_INCLUDE_FILES TARGET_PROPERTIES AUTOGEN_RULES) @@ -96,6 +97,10 @@ function(ly_add_target) if(ly_add_target_GEM_MODULE) set(ly_add_target_MODULE ${ly_add_target_GEM_MODULE}) endif() + # If the GEM_STATIC tag is passed mark the target as STATIC + if(ly_add_target_GEM_STATIC) + set(ly_add_target_STATIC ${ly_add_target_GEM_STATIC}) + endif() foreach(file_cmake ${ly_add_target_FILES_CMAKE}) ly_include_cmake_file_list(${file_cmake}) @@ -199,7 +204,7 @@ function(ly_add_target) endif() - if(ly_add_target_GEM_MODULE) + if(ly_add_target_GEM_MODULE OR ly_add_target_GEM_STATIC) set_target_properties(${ly_add_target_NAME} PROPERTIES GEM_MODULE TRUE) endif() @@ -719,3 +724,23 @@ function(ly_project_add_subdirectory project_name) endif() endif() endfunction() + +# given a target name, returns the "real" name of the target if its an alias. +# this function recursively de-aliases +function(ly_de_alias_target target_name output_variable_name) + # its not okay to call get_target_property on a non-existent target + if (NOT TARGET ${target_name}) + message(FATAL_ERROR "ly_de_alias_target called on non-existent target: ${target_name}") + endif() + + while(target_name) + set(de_aliased_target_name ${target_name}) + + get_target_property(target_name ${target_name} ALIASED_TARGET) + endwhile() + + if(NOT de_aliased_target_name) + message(FATAL_ERROR "Empty de_aliased for ${target_name}") + endif() + set(${output_variable_name} ${de_aliased_target_name} PARENT_SCOPE) +endfunction() diff --git a/cmake/Monolithic.cmake b/cmake/Monolithic.cmake index db45c182fd..dfd6816dde 100644 --- a/cmake/Monolithic.cmake +++ b/cmake/Monolithic.cmake @@ -14,7 +14,7 @@ set(LY_MONOLITHIC_GAME FALSE CACHE BOOL "Indicates if the game will be built mon if(LY_MONOLITHIC_GAME) add_compile_definitions(AZ_MONOLITHIC_BUILD) ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE STATIC) - ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE STATIC) + ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE GEM_STATIC) # Disable targets that are not supported with monolithic ly_set(PAL_TRAIT_BUILD_HOST_TOOLS FALSE) ly_set(PAL_TRAIT_BUILD_HOST_GUI_TOOLS FALSE) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index ba610b1883..84bad13687 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -17,6 +17,8 @@ endif() set(LY_INSTALLER_DOWNLOAD_URL "" CACHE STRING "URL embedded into the installer to download additional artifacts") set(LY_INSTALLER_LICENSE_URL "" CACHE STRING "Optionally embed a link to the license instead of raw text") +set(CPACK_DESIRED_CMAKE_VERSION 3.20.2) + # set all common cpack variable overrides first so they can be accessible via configure_file # when the platform specific settings are applied below. additionally, any variable with # the "CPACK_" prefix will automatically be cached for use in any phase of cpack namely @@ -29,15 +31,16 @@ string(TOLOWER ${PROJECT_NAME} _project_name_lower) set(CPACK_PACKAGE_FILE_NAME "${_project_name_lower}_${LY_VERSION_STRING}_installer") set(DEFAULT_LICENSE_NAME "Apache-2.0") -set(DEFAULT_LICENSE_FILE "${CMAKE_SOURCE_DIR}/LICENSE.txt") +set(DEFAULT_LICENSE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt") set(CPACK_RESOURCE_FILE_LICENSE ${DEFAULT_LICENSE_FILE}) set(CPACK_LICENSE_URL ${LY_INSTALLER_LICENSE_URL}) set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_VENDOR}/${CPACK_PACKAGE_VERSION}") -# CMAKE_SOURCE_DIR doesn't equate to anything during execution of pre/post build scripts -set(CPACK_SOURCE_DIR ${CMAKE_SOURCE_DIR}/cmake) +# neither of the SOURCE_DIR variables equate to anything during execution of pre/post build scripts +set(CPACK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CPACK_BINARY_DIR ${CMAKE_BINARY_DIR}/_CPack) # to match other CPack out dirs # attempt to apply platform specific settings ly_get_absolute_pal_filename(pal_dir ${CPACK_SOURCE_DIR}/Platform/${PAL_HOST_PLATFORM_NAME}) @@ -48,6 +51,53 @@ if(NOT CPACK_GENERATOR) return() endif() +# pull down the desired copy of CMake so it can be included in the package +if(NOT (CPACK_CMAKE_PACKAGE_FILE AND CPACK_CMAKE_PACKAGE_HASH)) + message(FATAL_ERROR + "Packaging is missing one or more following properties required to include CMake: " + " CPACK_CMAKE_PACKAGE_FILE, CPACK_CMAKE_PACKAGE_HASH") +endif() + +set(_cmake_package_dest ${CPACK_BINARY_DIR}/${CPACK_CMAKE_PACKAGE_FILE}) + +string(REPLACE "." ";" _version_componets "${CPACK_DESIRED_CMAKE_VERSION}") +list(GET _version_componets 0 _major_version) +list(GET _version_componets 1 _minor_version) + +set(_url_version_tag "v${_major_version}.${_minor_version}") +set(_package_url "https://cmake.org/files/${_url_version_tag}/${CPACK_CMAKE_PACKAGE_FILE}") + +message(STATUS "Ensuring CMake ${CPACK_DESIRED_CMAKE_VERSION} is available for packaging...") +download_file( + URL ${_package_url} + TARGET_FILE ${_cmake_package_dest} + EXPECTED_HASH ${CPACK_CMAKE_PACKAGE_HASH} + RESULTS _results +) +list(GET _results 0 _status_code) + +if (${_status_code} EQUAL 0 AND EXISTS ${_cmake_package_dest}) + message(STATUS "-> Package found and verified!") +else() + file(REMOVE ${_cmake_package_dest}) + list(REMOVE_AT _results 0) + + set(_error_message "An error occurred, code ${_status_code}. URL ${_package_url} - ${_results}") + + if(${_status_code} EQUAL 1) + string(APPEND _error_message + " Please double check the CPACK_CMAKE_PACKAGE_FILE and " + "CPACK_CMAKE_PACKAGE_HASH properties before trying again.") + endif() + + message(FATAL_ERROR ${_error_message}) +endif() + +install(FILES ${_cmake_package_dest} + DESTINATION ./Tools/Redistributables/CMake + COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} +) + # IMPORTANT: required to be included AFTER setting all property overrides include(CPack REQUIRED) diff --git a/cmake/Platform/Android/PAL_android.cmake b/cmake/Platform/Android/PAL_android.cmake index bb4ac5f32b..5f35767f98 100644 --- a/cmake/Platform/Android/PAL_android.cmake +++ b/cmake/Platform/Android/PAL_android.cmake @@ -35,7 +35,7 @@ else() endif() # Set the default asset type for deployment -set(LY_ASSET_DEPLOY_ASSET_TYPE "es3" CACHE STRING "Set the asset type for deployment.") +set(LY_ASSET_DEPLOY_ASSET_TYPE "android" CACHE STRING "Set the asset type for deployment.") # Set the python cmd tool if(PAL_HOST_PLATFORM_NAME_LOWERCASE STREQUAL "windows") diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index f6cf034312..988d36af14 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -35,7 +35,7 @@ else() endif() # Set the default asset type for deployment -set(LY_ASSET_DEPLOY_ASSET_TYPE "osx_gl" CACHE STRING "Set the asset type for deployment.") +set(LY_ASSET_DEPLOY_ASSET_TYPE "mac" CACHE STRING "Set the asset type for deployment.") # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) diff --git a/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs b/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs new file mode 100644 index 0000000000..d4f6c181dd --- /dev/null +++ b/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + Installed OR NOT MANUALPRODUCTFOUND + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/Platform/Windows/Packaging/Template.wxs.in b/cmake/Platform/Windows/Packaging/Template.wxs.in index 2900b96f41..e14e064fbc 100644 --- a/cmake/Platform/Windows/Packaging/Template.wxs.in +++ b/cmake/Platform/Windows/Packaging/Template.wxs.in @@ -17,8 +17,7 @@ - @@ -41,8 +40,24 @@ + + + + + + + NOT Installed Or REINSTALL + + + NOT Installed Or REINSTALL + + + NOT Installed Or REINSTALL + + + diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index ce73e9a07b..db9c7fc906 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -28,6 +28,10 @@ endif() set(CPACK_GENERATOR "WIX") +set(_cmake_package_name "cmake-${CPACK_DESIRED_CMAKE_VERSION}-windows-x86_64") +set(CPACK_CMAKE_PACKAGE_FILE "${_cmake_package_name}.zip") +set(CPACK_CMAKE_PACKAGE_HASH "15a49e2ab81c1822d75b1b1a92f7863f58e31f6d6aac1c4103eef2b071be3112") + # CPack will generate the WiX product/upgrade GUIDs further down the chain if they weren't supplied # however, they are unique for each run. instead, let's do the auto generation here and add it to # the cache for run persistence and have the ability to detect if they are still being used. @@ -83,9 +87,14 @@ set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/produc set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") set(CPACK_WIX_EXTRA_SOURCES + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/PostInstallSetup.wxs" "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Shortcuts.wxs" ) +set(CPACK_WIX_EXTENSIONS + WixUtilExtension +) + set(_embed_artifacts "yes") if(LY_INSTALLER_DOWNLOAD_URL) @@ -101,4 +110,5 @@ endif() set(CPACK_WIX_CANDLE_EXTRA_FLAGS -dCPACK_EMBED_ARTIFACTS=${_embed_artifacts} + -dCPACK_CMAKE_PACKAGE_NAME=${_cmake_package_name} ) diff --git a/cmake/SettingsRegistry.cmake b/cmake/SettingsRegistry.cmake index fd5985a5a1..e1c07f4492 100644 --- a/cmake/SettingsRegistry.cmake +++ b/cmake/SettingsRegistry.cmake @@ -64,18 +64,16 @@ function(ly_get_gem_load_dependencies ly_GEM_LOAD_DEPENDENCIES ly_TARGET) get_target_property(load_dependencies ${ly_TARGET} MANUALLY_ADDED_DEPENDENCIES) if(load_dependencies) foreach(load_dependency ${load_dependencies}) - # Skip wrapping produced when targets are not created in the same directory - if(NOT ${load_dependency} MATCHES "^::@") - get_property(dependency_type TARGET ${load_dependency} PROPERTY TYPE) - get_property(is_gem_target TARGET ${load_dependency} PROPERTY GEM_MODULE SET) - # If the dependency is a "gem module" then add it as a load dependencies - # and recurse into its manually added dependencies - if (is_gem_target) - unset(dependencies) - ly_get_gem_load_dependencies(dependencies ${load_dependency}) - list(APPEND all_gem_load_dependencies ${load_dependency}) - list(APPEND all_gem_load_dependencies ${dependencies}) - endif() + # Skip wrapping produced when targets are not created in the same directory + ly_de_alias_target(${load_dependency} dealias_load_dependency) + get_property(is_gem_target TARGET ${dealias_load_dependency} PROPERTY GEM_MODULE SET) + # If the dependency is a "gem module" then add it as a load dependencies + # and recurse into its manually added dependencies + if (is_gem_target) + unset(dependencies) + ly_get_gem_load_dependencies(dependencies ${dealias_load_dependency}) + list(APPEND all_gem_load_dependencies ${dependencies}) + list(APPEND all_gem_load_dependencies ${dealias_load_dependency}) endif() endforeach() endif() @@ -133,8 +131,8 @@ function(ly_delayed_generate_settings_registry) file(RELATIVE_PATH gem_relative_source_dir ${ly_root_folder_cmake} ${gem_relative_source_dir}) endif() - # Strip target namespace from gem targets before configuring them into the json template - ly_strip_target_namespace(TARGET ${gem_target} OUTPUT_VARIABLE stripped_gem_target) + # De-alias namespace from gem targets before configuring them into the json template + ly_de_alias_target(${gem_target} stripped_gem_target) string(CONFIGURE ${gem_module_template} gem_module_json @ONLY) list(APPEND target_gem_dependencies_names ${gem_module_json}) endforeach() diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index 8a76270df1..eb8361ec18 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -62,7 +62,7 @@ class AndroidDeployment(object): :param deployment_type: The type of deployment (DEPLOY_APK_ONLY, DEPLOY_ASSETS_ONLY, or DEPLOY_BOTH) :param game_name: The name of the game whose assets are being deployed. None if is_test_project is True :param asset_mode: The asset mode of deployment (LOOSE, PAK, VFS). None if is_test_project is True - :param asset_type: The asset type (for android, 'es3'). None if is_test_project is True + :param asset_type: The asset type. None if is_test_project is True :param embedded_assets: Boolean to indicate if the assets are embedded in the APK or not :param is_unit_test: Boolean to indicate if this is a unit test deployment """ diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index f3f6a3acda..9a0e2760f5 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -118,7 +118,7 @@ ASSET_MODE_LOOSE = 'LOOSE' ASSET_MODE_VFS = 'VFS' ALL_ASSET_MODES = [ASSET_MODE_PAK, ASSET_MODE_LOOSE, ASSET_MODE_VFS] ASSET_TYPE_ARGUMENT_NAME = '--asset-type' -DEFAULT_ASSET_TYPE = 'es3' +DEFAULT_ASSET_TYPE = 'android' def wrap_parsed_args(parsed_args): diff --git a/cmake/Tools/Platform/Android/unit_test_android_deployment.py b/cmake/Tools/Platform/Android/unit_test_android_deployment.py index 011033649a..5ee168ca30 100755 --- a/cmake/Tools/Platform/Android/unit_test_android_deployment.py +++ b/cmake/Tools/Platform/Android/unit_test_android_deployment.py @@ -21,7 +21,7 @@ from cmake.Tools.Platform.Android import android_deployment TEST_GAME_NAME = "Foo" TEST_DEV_ROOT = pathlib.Path("Foo") TEST_ASSET_MODE = 'LOOSE' -TEST_ASSET_TYPE = 'es3' +TEST_ASSET_TYPE = 'android' TEST_ANDROID_SDK_PATH = pathlib.Path('c:\\AndroidSDK') TEST_BUILD_DIR = 'android_gradle_test' TEST_DEVICE_ID = '9A201FFAZ000ER' @@ -661,10 +661,10 @@ def test_execute_success(tmpdir, test_config, test_package_name, test_device_sto @pytest.mark.parametrize( "test_game_name, test_config, test_package_name, test_device_storage_path, test_asset_type", [ - pytest.param('game1','profile', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'es3'), - pytest.param('game1','debug', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'es3'), - pytest.param('game2','profile', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'es3'), - pytest.param('game2','debug', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'es3'), + pytest.param('game1','profile', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'android'), + pytest.param('game1','debug', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'android'), + pytest.param('game2','profile', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'android'), + pytest.param('game2','debug', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'android'), pytest.param('game3','profile', 'com.amazon.lumberyard.foo', '/data/fool_storage2', 'pc'), pytest.param('game3','debug', 'com.amazon.lumberyard.foo', '/data/fool_storage2', 'pc'), pytest.param('game4','profile', 'com.amazon.lumberyard.bar', '/data/fool_storage2', 'pc'), diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index b271c59766..8189bb3ca3 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -137,19 +137,32 @@ def get_config_file_values(config_file_path, keys_to_extract): return result_map -def get_bootstrap_values(engine_root, keys_to_extract): +def get_bootstrap_values(bootstrap_dir, keys_to_extract): """ - Extract requested values from the bootstrap.cfg file in the def root folder - :param engine_root: The engine root folder where bootstrap.cfg exists + Extract requested values from the bootstrap.setreg file in the Registry folder + :param bootstrap_dir: The parent directory of the bootstrap.setreg file :param keys_to_extract: The keys to extract into a dictionary :return: Dictionary of keys and its values (for matched keys) """ - bootstrap_file = os.path.join(engine_root, 'bootstrap.cfg') + bootstrap_file = os.path.join(bootstrap_dir, 'bootstrap.setreg') if not os.path.isfile(bootstrap_file): - raise LmbrCmdError("Missing 'bootstrap.cfg' file from engine root ('{}')".format(engine_root), - ERROR_CODE_FILE_NOT_FOUND) + raise logging.error(f'Bootstrap.setreg file {bootstrap_file} does not exist.') + + result_map = {} + with bootstrap_file.open('r') as f: + try: + json_data = json.load(f) + except Exception as e: + logging.error(f'Bootstrap.setreg failed to load: {str(e)}') + else: + for search_key in keys_to_extract: + try: + search_result = json_data["Amazon"]["AzCore"]["Bootstrap"][f'"{search_key}"'] + except KeyError as e: + logging.error(f'Bootstrap.setreg cannot find Amazon:AzCore:Bootstrap:{search_result}: {str(e)}') + else: + result_map[search_key] = search_result - result_map = get_config_file_values(bootstrap_file, keys_to_extract) return result_map diff --git a/cmake/Tools/generate_game_paks.py b/cmake/Tools/generate_game_paks.py deleted file mode 100755 index 6a1ff1e458..0000000000 --- a/cmake/Tools/generate_game_paks.py +++ /dev/null @@ -1,244 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import argparse -import datetime -import logging -import pathlib -import platform -import sys -import os -import subprocess - -ROOT_DEV_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) -if ROOT_DEV_PATH not in sys.path: - sys.path.append(ROOT_DEV_PATH) - -from cmake.Tools import common - -# The location of this python script is not portable relative to the engine root, we determine the engine root based -# on its relative location -DEV_ROOT = os.path.realpath(os.path.join(__file__, '../../..')) - -BOOTSTRAP_CFG = os.path.join(DEV_ROOT, 'bootstrap.cfg') - -EXECUTABLE_EXTN = '.exe' if platform.system() == 'Windows' else '' -RC_NAME = f'rc{EXECUTABLE_EXTN}' -APB_NAME = f'AssetProcessorBatch{EXECUTABLE_EXTN}' - -# Depending on the user request for verbosity, the argument list to subprocess may or may not redirect stdout to NULL -VERBOSE_CALL_ARGS = dict( - shell=True, - cwd=DEV_ROOT -) -NON_VERBOSE_CALL_ARGS = dict( - **VERBOSE_CALL_ARGS, - stdout=subprocess.DEVNULL -) - - -def command_arg(arg): - """ - Work-around for an issue when running subprocess on Linux: subprocess.check_call will take in the argument as an array - but only invokes the first item in the array, ignoring the arguments. As quick fix, we will combine the array into the - full command line and execute it that way on non-windows platforms - """ - if platform.system() == 'Windows': - return arg - else: - return ' '.join(arg) - - -def validate(binfolder, game_name, pak_script): - - # - # Validate the binfolder is relative and contains 'rc' and 'AssetProcessorBatch' - # - if os.path.isabs(binfolder): - raise common.LmbrCmdError("Invalid value for '-b/--binfolder'. It must be a path relative to the engine root folder", - common.ERROR_CODE_ERROR_DIRECTORY) - - binfolder_abs_path = pathlib.Path(DEV_ROOT) / binfolder - if not binfolder_abs_path.is_dir(): - raise common.LmbrCmdError("Invalid value for '-b/--binfolder'. Path does not exist or is not a directory", - common.ERROR_CODE_ERROR_DIRECTORY) - - rc_check = binfolder_abs_path / RC_NAME - if not rc_check.is_file(): - raise common.LmbrCmdError(f"Invalid value for '-b/--binfolder'. Path does not contain {RC_NAME}", - common.ERROR_CODE_ERROR_DIRECTORY) - - apb_check = binfolder_abs_path / APB_NAME - if not apb_check.is_file(): - raise common.LmbrCmdError(f"Invalid value for '-b/--binfolder'. Path does not contain {APB_NAME}", - common.ERROR_CODE_ERROR_DIRECTORY) - - # - # Validate the game name represents a game project within the game engine - # - gamefolder_abs_path = pathlib.Path(DEV_ROOT) / game_name - if not gamefolder_abs_path.is_dir(): - raise common.LmbrCmdError(f"Invalid value for '-g/--game-name'. No game '{game_name} exists.", - common.ERROR_CODE_ERROR_DIRECTORY) - - project_json_path = gamefolder_abs_path / 'project.json' - if not project_json_path.is_file(): - raise common.LmbrCmdError( - f"Invalid value for '-g/--game-name'. Folder '{game_name} is not a valid game project.", - common.ERROR_CODE_FILE_NOT_FOUND) - - if not os.path.isfile(pak_script): - raise common.LmbrCmdError(f'Pak script file {pak_script} does not exist.', - common.ERROR_CODE_FILE_NOT_FOUND) - - -def process(binfolder, game_name, asset_platform, autorun_assetprocessor, recompress, fastest_compression, target, - pak_script, warn_on_assetprocessor_error, verbose): - - logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG if verbose else logging.INFO) - - target_path_root_abs = pathlib.Path(DEV_ROOT) / target - if target_path_root_abs.is_file(): - raise common.LmbrCmdError(f"Target '{target}' already exists as a file.", - common.ERROR_CODE_GENERAL_ERROR) - os.makedirs(target_path_root_abs.absolute(), exist_ok=True) - - target_pak_folder_name = f'{game_name.lower()}_{asset_platform}_paks' - target_pak = target_path_root_abs / target_pak_folder_name - - # Prepare the asset processor batch arguments and execute if requested - if autorun_assetprocessor: - ap_executable = os.path.join(binfolder, APB_NAME) - ap_cmd_args = [ap_executable, - f'/gamefolder={game_name}', - f'/platforms={asset_platform}'] - logging.debug("Calling {}".format(' '.join(ap_cmd_args))) - try: - logging.info(f"Running {APB_NAME} on {game_name}") - start_time = datetime.datetime.now() - - call_args = VERBOSE_CALL_ARGS if verbose else NON_VERBOSE_CALL_ARGS - - subprocess.check_call(command_arg(ap_cmd_args), - **call_args) - - total_time = datetime.datetime.now() - start_time - logging.info(f"Asset Processing Complete. Elapse: {total_time}") - except subprocess.CalledProcessError: - if warn_on_assetprocessor_error: - logging.warning('AssetProcessorBatch reported errors') - else: - raise common.LmbrCmdError("AssetProcessorBatch has one or more failed assets.", - common.ERROR_CODE_GENERAL_ERROR) - - rc_executable = os.path.join(binfolder, RC_NAME) - rc_cmd_args = [rc_executable, - f'/job={pak_script}', - f'/p={asset_platform}', - f'/game={game_name}', - f'/trg={target_pak}'] - if recompress: - rc_cmd_args.append('/recompress=1') - if fastest_compression: - rc_cmd_args.append('/use_fastest=1') - logging.debug("Calling {}".format(' '.join(rc_cmd_args))) - - try: - logging.info(f"Running {APB_NAME} on {game_name}") - start_time = datetime.datetime.now() - - call_args = VERBOSE_CALL_ARGS if verbose else NON_VERBOSE_CALL_ARGS - - subprocess.check_call(command_arg(rc_cmd_args), - **call_args) - - total_time = datetime.datetime.now() - start_time - logging.info(f"Asset Processing Complete. Elapse: {total_time}") - logging.info(f"Pak files for {game_name} written to {target_pak}") - - except subprocess.CalledProcessError as err: - raise common.LmbrCmdError(f"{RC_NAME} returned an error: {str(err)}.", - err.returncode) - - -def main(args): - - parser = argparse.ArgumentParser() - - parser.add_argument('-b', '--binfolder', - help='The relative location of the binary folder that contains the resource compiler and asset processor') - - bootstrap = common.get_bootstrap_values(DEV_ROOT, ['project_path']) - parser.add_argument('-g', '--game-name', - help='The name of the Game whose asset pak will be generated for', - default=bootstrap.get('project_path')) - - parser.add_argument('-p', '--asset-platform', - help='The asset platform type to process') - - parser.add_argument('-a', '--autorun-assetprocessor', - help='Option to automatically invoke asset processor batch on the game before generating the pak', - action='store_true') - - parser.add_argument('-w', '--warn-on-assetprocessor-error', - help='When -a/--autorun-assetprocessor is specified, warn on asset processor failure rather than aborting the process', - action='store_true') - - parser.add_argument('-r', '--recompress', - action='store_true', - help='If present, the ResourceCompiler (RC.exe) will decompress and compress back each PAK file ' - 'found as they are transferred from the cache folder to the game_pc_pak folder.') - parser.add_argument('-fc', '--fastest-compression', - action='store_true', - help='As each file is being added to its PAK file, they will be compressed across all available ' - 'codecs (ZLIB, ZSTD and LZ4) and the one with the fastest decompression time will be ' - 'chosen. The default is to always use ZLIB') - parser.add_argument('--target', - default='Pak', - help='Specify a target folder for the pak files. (Default : Pak)') - parser.add_argument('--pak-script', - default=f'{DEV_ROOT}/{os.path.normpath("Code/Tools/RC/Config/rc/RCJob_Generic_MakePaks.xml")}', - help="The absolute path of the pak script configuration file to use to create the paks.") - - parser.add_argument('-v', '--verbose', - help='Enable debug messages', - action='store_true') - - parsed = parser.parse_args(args) - - validate(binfolder=parsed.binfolder, - game_name=parsed.game_name, - pak_script=parsed.pak_script) - - process(binfolder=parsed.binfolder, - game_name=parsed.game_name, - asset_platform=parsed.asset_platform, - autorun_assetprocessor=parsed.autorun_assetprocessor, - recompress=parsed.recompress, - fastest_compression=parsed.fastest_compression, - target=parsed.target, - pak_script=parsed.pak_script, - warn_on_assetprocessor_error=parsed.warn_on_assetprocessor_error, - verbose=parsed.verbose) - - -if __name__ == '__main__': - try: - if not os.path.isfile(BOOTSTRAP_CFG): - raise common.LmbrCmdError("Invalid dev root, missing bootstrap.cfg.", - common.ERROR_CODE_FILE_NOT_FOUND) - - main(sys.argv[1:]) - exit(0) - - except common.LmbrCmdError as err: - print(str(err), file=sys.stderr) - exit(err.code) diff --git a/cmake/Tools/layout_tool.py b/cmake/Tools/layout_tool.py index 8f573b61f5..69f5b34ae7 100755 --- a/cmake/Tools/layout_tool.py +++ b/cmake/Tools/layout_tool.py @@ -78,19 +78,19 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ if remote_on_check is None: # Validate that if '_connect_to_remote is enabled, that the 'input_remote_ip' is not set to local host if input_remote_connect == '1' and input_remote_ip == LOCAL_HOST: - return _warn("'bootstrap.cfg' is configured to connect to Asset Processor remotely, but the 'remote_ip' " + return _warn("'bootstrap.setreg' is configured to connect to Asset Processor remotely, but the 'remote_ip' " " is configured for LOCAL HOST") else: if remote_on_check: # Verify we are set for remote AP connection if input_remote_ip == LOCAL_HOST: - return _warn(f"'bootstrap.cfg' is not configured for a remote Asset Processor connection (remote_ip={input_remote_ip})") + return _warn(f"'bootstrap.setreg' is not configured for a remote Asset Processor connection (remote_ip={input_remote_ip})") if input_remote_connect != '1': - return _warn(f"'bootstrap.cfg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") + return _warn(f"'bootstrap.setreg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") else: # Verify we are disabled for remote AP connection if input_remote_connect != '0': - return _warn(f"'bootstrap.cfg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") + return _warn(f"'bootstrap.setreg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") return 0 @@ -107,20 +107,15 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ project_name_lower = project_path.lower() layout_path = pathlib.Path(layout_dir) - # Validate bootstrap.cfg exists - bootstrap_file = layout_path / 'bootstrap.cfg' - if not bootstrap_file.is_file(): - warning_count += _warn(f"'bootstrap.cfg' is missing from {str(layout_path)}") - bootstrap_values = None - else: - bootstrap_values = common.get_config_file_values(str(bootstrap_file), [f'{platform_name_lower}_remote_filesystem', - f'{platform_name_lower}_connect_to_remote', - f'{platform_name_lower}_wait_for_connect', - f'{platform_name_lower}_assets', - f'assets', - f'{platform_name_lower}_remote_ip', - f'remote_ip' - ]) + bootstrap_path = layout_path / 'Registry' + bootstrap_values = common.get_bootstrap_values(str(bootstrap_path), [f'{platform_name_lower}_remote_filesystem', + f'{platform_name_lower}_connect_to_remote', + f'{platform_name_lower}_wait_for_connect', + f'{platform_name_lower}_assets', + f'assets', + f'{platform_name_lower}_remote_ip', + f'remote_ip' + ]) # Validate the system_{platform}_{asset type}.cfg exists platform_system_cfg_file = layout_path / f'system_{platform_name_lower}_{asset_type}.cfg' @@ -141,9 +136,9 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ # Validate that the asset type for the platform matches the one set for the build bootstrap_asset_type = bootstrap_values.get(f'{platform_name_lower}_assets') or bootstrap_values.get('assets') if not bootstrap_asset_type: - warning_count += _warn("'bootstrap.cfg' is missing specifications for asset type.") + warning_count += _warn("'bootstrap.setreg' is missing specifications for asset type.") elif bootstrap_asset_type != asset_type: - warning_count += _warn(f"The asset type specified in bootstrap.cfg ({bootstrap_asset_type}) does not match the asset type specified for this deployment({asset_type}).") + warning_count += _warn(f"The asset type specified in bootstrap.setreg ({bootstrap_asset_type}) does not match the asset type specified for this deployment({asset_type}).") # Validate that if '_connect_to_remote is enabled, that the 'remote_ip' is not set to local host warning_count += _validate_remote_ap(remote_ip, remote_connect, None) @@ -211,7 +206,7 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ elif asset_mode == ASSET_MODE_VFS: remote_file_system = bootstrap_values.get(f'{platform_name_lower}_remote_filesystem') or '0' if not remote_file_system != '1': - warning_count += _warn("Remote file system is not configured in bootstrap.cfg for VFS mode.") + warning_count += _warn("Remote file system is not configured in bootstrap.setreg for VFS mode.") else: warning_count += _validate_remote_ap(remote_ip, remote_connect, True) diff --git a/cmake/Tools/unit_test_common.py b/cmake/Tools/unit_test_common.py index 58f89876c3..655c2a32c1 100755 --- a/cmake/Tools/unit_test_common.py +++ b/cmake/Tools/unit_test_common.py @@ -48,60 +48,6 @@ def test_determine_engine_root(tmpdir, engine_json_content, expected_success): assert result is None -TEST_BOOTSTRAP_CONTENT_1 = """ -project_path = Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc ---No Assets -""" - -TEST_BOOTSTRAP_CONTENT_2 = """ -project_path = Game2 - foo = bar -#------------------------- - key1 = value1 -key2 = value2 -assets = pc ---No Assets -""" - - -@pytest.mark.parametrize( - "contents, input_keys, expected_result_map", [ - pytest.param(TEST_BOOTSTRAP_CONTENT_1, ['project_path', 'foo', 'assets'], {'project_path': 'Game1', - 'foo': 'bar', - 'assets': 'pc'}, id="TestFullMatch"), - pytest.param(TEST_BOOTSTRAP_CONTENT_2, ['project_path', 'foo', 'barnone'], {'project_path': 'Game2', - 'foo': 'bar'}, id="TestPartialMatch"), - pytest.param(TEST_BOOTSTRAP_CONTENT_2, ['project_pathnone', 'foonone', 'barnone'], {}, id="TestNoMatch") - ] -) -def test_get_bootstrap_values_success(tmpdir, contents, input_keys, expected_result_map): - - test_dev_root = 'dev' - tmpdir.ensure('{}/bootstrap.cfg'.format(test_dev_root)) - bootstrap_file = tmpdir.join('{}/bootstrap.cfg'.format(test_dev_root)) - bootstrap_file.write(contents) - - bootstrap_file_path = str(tmpdir.join(test_dev_root).realpath()) - - result = common.get_bootstrap_values(bootstrap_file_path, input_keys) - - assert expected_result_map == result - - -def test_get_bootstrap_values_fail(): - try: - bad_file = 'x:\\foo\\bar\\file\\' - common.get_bootstrap_values(bad_file, ['input_keys']) - except common.LmbrCmdError as err: - assert 'Missing' in str(err) - else: - assert False, "Excepted LayoutToolError (missing file)" - - TEST_AP_CONFIG_1 = """ [Platforms] ;pc=enabled @@ -245,7 +191,6 @@ def test_verify_game_project_and_dev_root_success(tmpdir): game_name = 'MyFoo' game_folder = 'myfoo' game_project_json = TEST_GAME_PROJECT_JSON_FORMAT.format(project_name=game_name) - tmpdir.ensure(f'{dev_root}/bootstrap.cfg') tmpdir.ensure(f'{dev_root}/{game_folder}/project.json') project_json_path = tmpdir / dev_root / game_folder / 'project.json' project_json_path.write_text(game_project_json, encoding='ascii') @@ -285,72 +230,6 @@ asset_deploy_type={test_asset_deploy_type} assert result.asset_deploy_type == test_asset_deploy_type -def test_transform_bootstrap_project_path(tmpdir): - - tmpdir.ensure('bootstrap.cfg') - - test_bootstrap_content = """ --- Blah Blah --- Blah Blah - -project_path=OldProject - --- remote_filesystem - enable Virtual File System (VFS) --- This feature allows a remote instance of the game to run off assets --- on the asset processor computers cache instead of deploying them the remote device --- By default it is off and can be overridden for any platform -remote_filesystem=0 -""" - test_src_bootstrap = tmpdir / 'bootstrap.cfg' - test_src_bootstrap.write_text(test_bootstrap_content, encoding='ascii') - - test_dst_bootstrap = tmpdir / 'bootstrap.transformed.cfg' - test_game_name = 'FooBar' - - common.transform_bootstrap_for_project(game_name=test_game_name, - src_bootstrap=str(test_src_bootstrap), - dst_bootstrap=str(test_dst_bootstrap)) - - transformed_text = test_dst_bootstrap.read_text('ascii') - - search_gamename = re.search(r"project_path\s*=\s*(.*)", transformed_text) - assert search_gamename - assert search_gamename.group(1) - assert search_gamename.group(1) == test_game_name - - -def test_transform_bootstrap_project_path_missing(tmpdir): - - tmpdir.ensure('bootstrap.cfg') - - test_bootstrap_content = """ --- Blah Blah --- Blah Blah - --- remote_filesystem - enable Virtual File System (VFS) --- This feature allows a remote instance of the game to run off assets --- on the asset processor computers cache instead of deploying them the remote device --- By default it is off and can be overridden for any platform -remote_filesystem=0 -""" - test_src_bootstrap = tmpdir / 'bootstrap.cfg' - test_src_bootstrap.write_text(test_bootstrap_content, encoding='ascii') - - test_dst_bootstrap = tmpdir / 'bootstrap.transformed.cfg' - test_game_name = 'FooBar' - - common.transform_bootstrap_for_project(game_name=test_game_name, - src_bootstrap=str(test_src_bootstrap), - dst_bootstrap=str(test_dst_bootstrap)) - - transformed_text = test_dst_bootstrap.read_text('ascii') - - search_gamename = re.search(r"project_path\s*=\s*(.*)", transformed_text) - assert search_gamename - assert search_gamename.group(1) - assert search_gamename.group(1) == test_game_name - - def test_cmake_dependency_success(tmpdir): test_module = 'FooBar' diff --git a/cmake/Tools/unit_test_current_project.py b/cmake/Tools/unit_test_current_project.py deleted file mode 100755 index 7db48b62aa..0000000000 --- a/cmake/Tools/unit_test_current_project.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import os -import pytest - -from . import current_project - -TEST_BOOTSTRAP_CONTENT_1 = """ -project_path = Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_2 = """ -project_path=Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_3 = """ -project_path= Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_4 = """ -project_path =Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_5 = """ -project_path = Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" - -@pytest.mark.parametrize( - "contents, expected_result", [ - pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_2, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_3, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_4, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_5, 'Game1'), - ] -) -def test_get_current_project(tmpdir, contents, expected_result): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - bootstrap_file = f'{dev_root}/bootstrap.cfg' - if os.path.isfile(bootstrap_file): - os.unlink(bootstrap_file) - with open(bootstrap_file, 'a') as s: - s.write(contents) - - result = current_project.get_current_project(dev_root) - assert expected_result == result - - -@pytest.mark.parametrize( - "contents, project_to_set, expected_result", [ - pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Test1', 0), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, ' Test2', 0), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Test3 ', 0), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, '/Test4', 1), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, '=Test5', 1), - ] -) -def test_set_current_project(tmpdir, contents, project_to_set, expected_result): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - bootstrap_file = f'{dev_root}/bootstrap.cfg' - if os.path.isfile(bootstrap_file): - os.unlink(bootstrap_file) - with open(bootstrap_file, 'a') as s: - s.write(contents) - - result = current_project.set_current_project(dev_root, project_to_set) - assert expected_result == result - - if result == 0: - project_that_is_set = current_project.get_current_project(dev_root) - print(project_that_is_set) - print(project_to_set) - assert project_to_set.strip() == project_that_is_set \ No newline at end of file diff --git a/cmake/Tools/unit_test_layout_tool.py b/cmake/Tools/unit_test_layout_tool.py index 5be2c23f11..37684654b1 100755 --- a/cmake/Tools/unit_test_layout_tool.py +++ b/cmake/Tools/unit_test_layout_tool.py @@ -212,16 +212,15 @@ def test_create_link_error(): @pytest.mark.parametrize( - "project_path, asset_type, ensure_path, warn_on_missing, expected_result", [ - pytest.param('Foo', 'pc', 'Foo/Cache/pc/bootstrap.cfg', False, 'Foo/Cache/pc'), - pytest.param('Foo', 'pc', 'dev/bootstrap.cfg', True, None), - pytest.param('Foo', 'pc', 'Foo/Cache/es3/bootstrap.cfg', True, None), - pytest.param('Foo', 'pc', 'dev/bootstrap.cfg', False, common.LmbrCmdError), - pytest.param('Foo', 'pc', 'Foo/Cache/es3/bootstrap.cfg', False, common.LmbrCmdError), + "project_path, asset_type, warn_on_missing, expected_result", [ + pytest.param('Foo', 'pc', False, 'Foo/Cache/pc'), + pytest.param('Foo', 'pc', True, None), + pytest.param('Foo', 'pc', True, None), + pytest.param('Foo', 'pc', False, common.LmbrCmdError), + pytest.param('Foo', 'pc', False, common.LmbrCmdError), ] ) -def test_construct_and_validate_cache_game_asset_folder_success(tmpdir, project_path, asset_type, ensure_path, warn_on_missing, expected_result): - tmpdir.ensure(ensure_path) +def test_construct_and_validate_cache_game_asset_folder_success(tmpdir, project_path, asset_type, warn_on_missing, expected_result): if isinstance(expected_result, str): expected_path_realpath = str(tmpdir.join(expected_result).realpath()) elif expected_result == common.LmbrCmdError: @@ -385,7 +384,6 @@ def test_sync_layout_non_vfs_success(tmpdir, mode, existing_game_link, existing_ old_remove_link = layout_tool.remove_link try: # Simple Test Parameters - tmpdir.ensure('engine-root/bootstrap.cfg') engine_root_realpath = str(tmpdir.join('engine-root').realpath()) test_project_path = str(tmpdir.join('Foo').realpath()) test_project_name_lower = 'foo' diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index d0fce80964..fe5e223612 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -99,7 +99,7 @@ "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", "ASSET_PROCESSOR_BINARY": "bin\\profile\\AssetProcessorBatch.exe", "ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode --regset=\"/Amazon/AssetProcessor/Settings/Exclude Android/pattern=.*/DiffuseGlobalIllumination/.*precompiledshader\"", - "ASSET_PROCESSOR_PLATFORMS":"es3" + "ASSET_PROCESSOR_PLATFORMS":"android" } }, "release": { diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index 5d96ae7846..610c6a6514 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest --repeat until-pass:5" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "test_profile_nounity": { @@ -95,7 +95,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest --repeat until-pass:5" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "asset_profile": { @@ -143,7 +143,26 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", - "CTEST_OPTIONS": "-L \"(SUITE_periodic)\"" + "CTEST_OPTIONS": "-L (SUITE_periodic)" + } + }, + "sandbox_test_profile": { + "TAGS": [ + "nightly-incremental", + "nightly-clean", + "weekly-build-metrics" + ], + "PIPELINE_ENV": { + "ON_FAILURE_MARK": "UNSTABLE" + }, + "COMMAND": "build_test_linux.sh", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/linux", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_LY_PROJECTS": "AutomatedTesting", + "CMAKE_TARGET": "all", + "CTEST_OPTIONS": "-L (SUITE_sandbox)" } }, "benchmark_test_profile": { @@ -159,7 +178,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", - "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"" + "CTEST_OPTIONS": "-L (SUITE_benchmark)" } }, "release": { diff --git a/scripts/build/Platform/Mac/build_config.json b/scripts/build/Platform/Mac/build_config.json index f312279fe6..bcaffce880 100644 --- a/scripts/build/Platform/Mac/build_config.json +++ b/scripts/build/Platform/Mac/build_config.json @@ -86,7 +86,7 @@ "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", "ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode", - "ASSET_PROCESSOR_PLATFORMS": "osx_gl" + "ASSET_PROCESSOR_PLATFORMS": "mac" } }, "periodic_test_profile": { diff --git a/scripts/build/lambda/trigger_first_build.py b/scripts/build/lambda/trigger_first_build.py index 6ebe09f7ee..3b4587e737 100755 --- a/scripts/build/lambda/trigger_first_build.py +++ b/scripts/build/lambda/trigger_first_build.py @@ -53,7 +53,7 @@ def lambda_handler(event, context): backoff = 30 status_list = [404] # Retry if the branch doesn't exist yet and provide time for Jenkins to discover it. method_list = ['POST'] - retry_config = Retry(total=retries, backoff_factor=backoff, status_forcelist=status_list, method_whitelist=method_list) + retry_config = Retry(total=retries, backoff_factor=backoff, status_forcelist=status_list, allowed_methods=method_list) session = requests.Session() session.mount('https://', HTTPAdapter(max_retries=retry_config)) diff --git a/scripts/build/package/package.py b/scripts/build/package/package.py index 96b39f0753..4cb09f3918 100755 --- a/scripts/build/package/package.py +++ b/scripts/build/package/package.py @@ -25,9 +25,6 @@ from glob3 import glob def package(options): package_env = PackageEnv(options.platform, options.type, options.package_env) - # Override values in bootstrap.cfg for PC package - override_bootstrap_cfg(package_env) - if not package_env.get('SKIP_BUILD'): print(package_env.get('SKIP_BUILD')) print('SKIP_BUILD is False, running CMake build...') @@ -51,34 +48,6 @@ def get_python_path(package_env): return os.path.join(package_env.get('ENGINE_ROOT'), 'python', 'python.sh') -def override_bootstrap_cfg(package_env): - print('Override values in bootstrap.cfg') - engine_root = package_env.get('ENGINE_ROOT') - bootstrap_path = os.path.join(engine_root, 'bootstrap.cfg') - replace_values = {'project_path':'{}'.format(package_env.get('BOOTSTRAP_CFG_GAME_FOLDER'))} - try: - with open(bootstrap_path, 'r') as bootstrap_cfg: - content = bootstrap_cfg.read() - except: - error('Cannot read file {}'.format(bootstrap_path)) - content = content.split('\n') - new_content = [] - for line in content: - if not line.startswith('--'): - strs = line.split('=') - if len(strs): - key = strs[0].strip(' ') - if key in replace_values: - line = '{}={}'.format(key, replace_values[key]) - new_content.append(line) - try: - with open(bootstrap_path, 'w') as out: - out.write('\n'.join(new_content)) - except: - error('Cannot write to file {}'.format(bootstrap_path)) - print('{} updated with value {}'.format(bootstrap_path, replace_values)) - - def cmake_build(package_env): build_targets = package_env.get('BUILD_TARGETS') for build_target in build_targets: diff --git a/scripts/bundler/gen_shaders.py b/scripts/bundler/gen_shaders.py index 46857179b1..bfb60287af 100644 --- a/scripts/bundler/gen_shaders.py +++ b/scripts/bundler/gen_shaders.py @@ -163,11 +163,11 @@ def add_shaders_types(): shaders.append(gl4) gles3 = _ShaderType('GLES3', 'GLSL_HLSLcc') - gles3.add_configuration('Android', 'es3') + gles3.add_configuration('Android', 'android') shaders.append(gles3) metal = _ShaderType('METAL', 'METAL_LLVM_DXC') - metal.add_configuration('Mac', 'osx_gl') + metal.add_configuration('Mac', 'mac') metal.add_configuration('iOS', 'ios') shaders.append(metal) diff --git a/scripts/project_manager/projects.py b/scripts/project_manager/projects.py index 51db7a6440..51ffc2be91 100755 --- a/scripts/project_manager/projects.py +++ b/scripts/project_manager/projects.py @@ -34,7 +34,7 @@ from cmake.Tools import registration o3de_folder = registration.get_o3de_folder() o3de_logs_folder = registration.get_o3de_logs_folder() -project_manager_log_file_path = o3de_log_folder / "project_manager.log" +project_manager_log_file_path = o3de_logs_folder / "project_manager.log" log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1) formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s') log_file_handler.setFormatter(formatter) diff --git a/scripts/scrubbing/validator.py b/scripts/scrubbing/validator.py index ac7c45554f..4a979da943 100755 --- a/scripts/scrubbing/validator.py +++ b/scripts/scrubbing/validator.py @@ -243,7 +243,7 @@ class Validator(object): validations += 1 counter += 1 - # Trim out whitelisted subdirectories in the current directory if allowed + # Trim out allowlisted subdirectories in the current directory if allowed for name in bypassed_directories: if name in dirnames: dirnames.remove(name) diff --git a/system_android_es3.cfg b/system_android_android.cfg similarity index 93% rename from system_android_es3.cfg rename to system_android_android.cfg index 46ab50f558..d5bfddeb73 100644 --- a/system_android_es3.cfg +++ b/system_android_android.cfg @@ -1,4 +1,4 @@ --- config file used when the android platform is running off 'es3' assets. +-- config file used when the android platform is running off 'android' assets. sys_float_exceptions=0 log_IncludeTime=1 sys_PakLogInvalidFileAccess=1 diff --git a/system_mac_osx_gl.cfg b/system_mac_mac.cfg similarity index 100% rename from system_mac_osx_gl.cfg rename to system_mac_mac.cfg