Compare commits

...

25 Commits

Author SHA1 Message Date
Tom Hulton-Harrop 6ffc2620e4
Updates to EMFX cameras and better support for character follow behavior (#7676)
* initial wip change to help resolve EMFX camera viewport issue

Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>

* some updates to add/remove camera input API

Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>

* add additional comments and remove some optimize off calls

Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>

* improvements to camera behavior in EMFX editor

Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>

* add some tests for add/remove logic for cameras

Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>
4 years ago
Benjamin Jillich d31200ee5c
EMotion FX: Update skeleton transforms when either solid mesh rendering or any of the debug visualizations is enabled (#7747)
* Disabled depth testing for joint orientation debug rendering, so that we can also see them through the solid mesh rendering.
* Update transforms in case solid mesh rendering or any of the debug visualizations are enabled.

Signed-off-by: Benjamin Jillich <jillich@amazon.com>
4 years ago
lumberyard-employee-dm 60adc098da
Fixed implicit conversion to bool in AZStd::to_string. (#7755)
This specifically implies to the value returning to_string overloads

Added a range based StringFunc::Join overload.

Reduced the number of AZStd::to_string calls in the TranslationKey
operator<< function.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
4 years ago
Danilo Aimini b69ae11ec6
Editor | Refactor right-click context menu to improve grouping and surface most used items to the top. (#7742)
* Move "Open pinned Inspector" to separate handler to move it after the Prefab menu items.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Rearrange context menu items, add shortcuts

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Fix pragma once in header

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Revert casing changes that would cause issues in automated testing.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
4 years ago
Andrea 1f4b62dc72
Broganab/white box test conversion (#7600)
* LYN-8773 WhiteBox refactor python automation

Signed-off-by: Broganab <broganab@amazon.com>
4 years ago
Chris Galvan 445385e146
Merge pull request #7617 from Bindless-Chicken/fix-assert-resource
Fix missing format parameter in RHI::Resource
4 years ago
Roman 464b225042
Prevent the character to go out of bound in animation viewport (#7667)
* Prevent the character to go out of bound

Signed-off-by: rhhong <rhhong@amazon.com>

* CR feedback

Signed-off-by: rhhong <rhhong@amazon.com>
4 years ago
Tommy Walton 7de6bc5b23
Add RoundUpToMultiple and DivideAndRoundUp functions to MathUtils.h (#6989)
* Add RoundUpToMultiple and DivideAndRoundUp functions to MathUtils.h

Signed-off-by: Tommy Walton <waltont@amazon.com>

* Rename DivideByMultiple2 back to DivideByMultiple, now that I've confirmed it's not in use in the codebase. RHI::DivideByMultiple can be fully deprecated in favor of AZ::DivideAndRoundUp at a later date, once the deprecation strategy has been finalized.

Signed-off-by: Tommy Walton <waltont@amazon.com>

* Update based on PR feedback

Signed-off-by: Tommy Walton <waltont@amazon.com>

* Switched from std::numeric_limits to AZStd::numeric_limits and updated the header to indicate it works for non-power of two alignments, but that SizeAlignUp is more efficient if the alignment is a power of 2

Signed-off-by: Tommy Walton <waltont@amazon.com>

* Added missing arguments to the assert, and a missing namespace and include that failed to compile on non-unity builds

Signed-off-by: Tommy Walton <waltont@amazon.com>
4 years ago
Guthrie Adams 49dba84fee
Merge pull request #7744 from aws-lumberyard-dev/Atom/guthadam/atom_tools_document_inspector
Atom Tools: Saving tool settings, made inspector configurable, moved to ATF
4 years ago
Steve Pham feb4088446
Python 3.7.12 Update (#7726)
Python 3.7.12 security updates 

Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com>
4 years ago
Guthrie Adams 26c23d9ec0 Fixed non unity compiler issues and reverted a couple of changes
Signed-off-by: Guthrie Adams <guthadam@amazon.com>
4 years ago
Chris Galvan c6ca903e5f
Merge pull request #7731 from aws-lumberyard-dev/cgalvan/FixedToolGemplates
Updated deprecated CMake function usage in PythonToolGem and CppToolGem templates
4 years ago
lumberyard-employee-dm 669caca9cc
Updated Launching of Lua Editor to supply the project-path (#7706)
* Updated Launching of Lua Editor to supply the project-path

The Lua Launch logic also uses the AzFramework ProcessLauncher instead
of Qt. This has the benefit of being able to pass arguments as an array
instead of in a single string. Therefore paths with spaces in them also
work.

Tweak the Settings Registry logic to locate the
project-path/engine-path by scanning upwards for a
project.json/engine.json respectively to inject the found paths to the
front of the command line parameters instead of the back.
This has the effect of making sure that command line parameters for the
project-path/engine-path always takes precedence over scanning upwards
for a project.json/engine.json.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Removed prepend of EngineRoot in CheckProjectPathProvided

The Editor would attempt to validate that the project path supplied via
the command line contained a valid project.json file before contining
the Editor startup flow.

This was taking the engine root path and appending the project path to
it, which works when the project path is absolute

But when the project path is relative it is treated as relative to the
current working directory by the SettingsRegistry, but the logic in the
CheckProjectPathProvided was treating it has relative to the engine
root. Therefore supplying a relative path as part of the Editor.exe
launch command would fail.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Fixed resolving of relative paths to absolute paths in the Editor

The Editor was changing the current working directory to the nearest
ancestor directory containing an `engine.json` file.
This resulted in the ConvertToAbsolutePath function resolving relative
paths to that directory instead of the launch directory of the Editor.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
4 years ago
Chris Galvan 606b4e9ec3
Merge pull request #7749 from aws-lumberyard-dev/cgalvan/RemovedLegacyLevelInfo
Removed legacy Editor `CLevelInfo` class
4 years ago
Chris Galvan b22032e40a Removed legacy Editor LevelInfo class
Signed-off-by: Chris Galvan <chgalvan@amazon.com>
4 years ago
Olex Lozitskiy fdb9c053ce
Added MULTIPLAYER budget. Added profiler markers, visible in PIX. 4 years ago
Benjamin Jillich bb93b2e498
EMotion FX: Minimized distance between wireframe and solid mesh rendering (#7713)
while preventing Z-fighting

Signed-off-by: Benjamin Jillich <jillich@amazon.com>
4 years ago
Benjamin Jillich f4d68a0b0d
EMotion FX: Limiting max values for playspeed, blend-in and blend-out times for the simple motion component (#7720)
GHI #5976 described that for really large numbers the edit field randomly changed digits. This does not happen on the latest version anymore and most likely was caused by a floating point issue that the editor team fixed. We're limiting the maximum values now additionally as in case such large numbers are needed, something is wrong with the global time scale.

Resolves #5976

Signed-off-by: Benjamin Jillich <jillich@amazon.com>
4 years ago
Guthrie Adams 01786b2966 Atom Tools: Saving tool settings, made inspector configurable, moved to ATF
Changed material editor inspector into generic, configurable, document inspector and moved to atom tools framework
Renamed atom tools framework settings registry utility functions
Added more settings registry utility functions to get, set, and save values and objects
Added saving tool settings to atom tools application

Signed-off-by: Guthrie Adams <guthadam@amazon.com>
4 years ago
Olex Lozitskiy f94ec80d9f Improved profile marker names
Signed-off-by: Olex Lozitskiy <5432499+AMZN-Olex@users.noreply.github.com>
4 years ago
Chris Galvan 26190a47eb Fixed the broken links in the template widgets to the UI tools documentation
Signed-off-by: Chris Galvan <chgalvan@amazon.com>
4 years ago
Chris Galvan 8685be877b Replaced deprecated ly_get_list_relative_pal_filename usage with o3de_pal_dir
Signed-off-by: Chris Galvan <chgalvan@amazon.com>
4 years ago
Olex Lozitskiy 4d8c8fdda3 Merge branch 'development' of https://github.com/o3de/o3de into Network/olexl/multiplayer_gem_profiler_markers_cr 4 years ago
AMZN-Olex 4ca71fd96a Added MULTIPLAYER budget. Added profiler markers, visible in PIX.
Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com>
4 years ago
Bindless-Chicken 9da156d0a6 Fix missing format parameter in RHI::Resource
Signed-off-by: Bindless-Chicken <1039134+Bindless-Chicken@users.noreply.github.com>
4 years ago

@ -9,27 +9,17 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
# This suite consists of all test cases that are passing and have been verified. # This suite consists of all test cases that are passing and have been verified.
import pytest import pytest
import sys from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSharedTest
import os
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_main @pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"]) @pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase): class TestAutomation(EditorTestSuite):
def test_WhiteBox_AddComponentToEntity(self, request, workspace, editor, launcher_platform): class test_WhiteBox_AddComponentToEntity(EditorSharedTest):
from .tests import WhiteBox_AddComponentToEntity as test_module from .tests import WhiteBox_AddComponentToEntity as test_module
self._run_test(request, workspace, editor, test_module)
def test_WhiteBox_SetDefaultShape(self, request, workspace, editor, launcher_platform): class test_WhiteBox_SetDefaultShape(EditorSharedTest):
from .tests import WhiteBox_SetDefaultShape as test_module from .tests import WhiteBox_SetDefaultShape as test_module
self._run_test(request, workspace, editor, test_module)
def test_WhiteBox_SetInvisible(self, request, workspace, editor, launcher_platform): class test_WhiteBox_SetInvisible(EditorSharedTest):
from .tests import WhiteBox_SetInvisible as test_module from .tests import WhiteBox_SetInvisible as test_module
self._run_test(request, workspace, editor, test_module)

@ -18,21 +18,17 @@ class Tests():
def C28798177_WhiteBox_AddComponentToEntity(): def C28798177_WhiteBox_AddComponentToEntity():
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.bus as bus import azlmbr.bus as bus
import azlmbr.editor as editor import azlmbr.editor as editor
import azlmbr.legacy.general as general import azlmbr.legacy.general as general
import WhiteBoxInit as init
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper from editor_python_test_tools.utils import Report
# open level # open level
helper.init_idle() hydra.open_base_level()
helper.open_level("WhiteBox", "EmptyLevel", no_prompt=False)
# create white box entity and attach component # create white box entity and attach component
white_box_entity = init.create_white_box_entity() white_box_entity = init.create_white_box_entity()
@ -46,10 +42,6 @@ def C28798177_WhiteBox_AddComponentToEntity():
component_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', white_box_mesh_component) component_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', white_box_mesh_component)
Report.result(Tests.white_box_component_enabled, component_enabled) Report.result(Tests.white_box_component_enabled, component_enabled)
# close editor
helper.close_editor()
if __name__ == "__main__": if __name__ == "__main__":
from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import Report
Report.start_test(C28798177_WhiteBox_AddComponentToEntity) Report.start_test(C28798177_WhiteBox_AddComponentToEntity)

@ -22,17 +22,13 @@ class Tests():
critical_shape_check = ("Default shape has more than 0 sides", "default shape has 0 sides") critical_shape_check = ("Default shape has more than 0 sides", "default shape has 0 sides")
def C29279329_WhiteBox_SetDefaultShape(): def C29279329_WhiteBox_SetDefaultShape():
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import azlmbr.whitebox.api as api
import azlmbr.bus as bus import azlmbr.bus as bus
import azlmbr.legacy.general as general import editor_python_test_tools.hydra_editor_utils as hydra
import WhiteBoxInit as init
from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def check_shape_result(success_fail_tuple, condition): def check_shape_result(success_fail_tuple, condition):
result = Report.result(success_fail_tuple, condition) result = Report.result(success_fail_tuple, condition)
@ -53,8 +49,7 @@ def C29279329_WhiteBox_SetDefaultShape():
} }
# open level # open level
helper.init_idle() hydra.open_base_level()
helper.open_level("WhiteBox", "EmptyLevel", no_prompt=False)
# create white box entity and attach component # create white box entity and attach component
white_box_entity = init.create_white_box_entity() white_box_entity = init.create_white_box_entity()
@ -95,10 +90,6 @@ def C29279329_WhiteBox_SetDefaultShape():
Tests.default_shape_sphere, Tests.default_shape_sphere,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.SPHERE)) white_box_handle.MeshFaceCount() == expected_results.get(shape_types.SPHERE))
# close editor
helper.close_editor()
if __name__ == "__main__": if __name__ == "__main__":
from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import Report
Report.start_test(C29279329_WhiteBox_SetDefaultShape) Report.start_test(C29279329_WhiteBox_SetDefaultShape)

@ -21,25 +21,16 @@ def C28798205_WhiteBox_SetInvisible():
# note: This automated test does not fully replicate the test case in Test Rail as it's # note: This automated test does not fully replicate the test case in Test Rail as it's
# not currently possible using the Hydra API to get an EntityComponentIdPair at runtime, # not currently possible using the Hydra API to get an EntityComponentIdPair at runtime,
# in future game_mode will be activated and a runtime White Box Component queried # in future game_mode will be activated and a runtime White Box Component queried
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.whitebox.api as api
import azlmbr.whitebox
import azlmbr.bus as bus import azlmbr.bus as bus
import azlmbr.editor as editor import azlmbr.editor as editor
import azlmbr.entity as entity import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.legacy.general as general import WhiteBoxInit as init
from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# open level # open level
helper.init_idle() hydra.open_base_level()
helper.open_level("WhiteBox", "EmptyLevel", no_prompt=False)
white_box_entity_name = 'WhiteBox-Visibility' white_box_entity_name = 'WhiteBox-Visibility'
white_box_visibility_path = 'White Box Material|Visible' white_box_visibility_path = 'White Box Material|Visible'
@ -57,9 +48,6 @@ def C28798205_WhiteBox_SetInvisible():
visible_property = hydra.get_component_property_value(white_box_mesh_component, white_box_visibility_path) visible_property = hydra.get_component_property_value(white_box_mesh_component, white_box_visibility_path)
Report.result(Tests.white_box_set_to_invisible, not visible_property) Report.result(Tests.white_box_set_to_invisible, not visible_property)
helper.close_editor()
if __name__ == "__main__": if __name__ == "__main__":
from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import Report
Report.start_test(C28798205_WhiteBox_SetInvisible) Report.start_test(C28798205_WhiteBox_SetInvisible)

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2dfc16cf4e47fc73c40d8a30ce08c34fe7805d369bf6ca69bd0d00ede4509902
size 4542

@ -1,118 +0,0 @@
{
"ContainerEntity": {
"Id": "ContainerEntity",
"Name": "EmptyLevel",
"Components": {
"Component_[10182366347512475253]": {
"$type": "EditorPrefabComponent",
"Id": 10182366347512475253
},
"Component_[12917798267488243668]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12917798267488243668
},
"Component_[3261249813163778338]": {
"$type": "EditorOnlyEntityComponent",
"Id": 3261249813163778338
},
"Component_[3837204912784440039]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 3837204912784440039
},
"Component_[4272963378099646759]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 4272963378099646759,
"Parent Entity": ""
},
"Component_[4848458548047175816]": {
"$type": "EditorVisibilityComponent",
"Id": 4848458548047175816
},
"Component_[5787060997243919943]": {
"$type": "EditorInspectorComponent",
"Id": 5787060997243919943
},
"Component_[7804170251266531779]": {
"$type": "EditorLockComponent",
"Id": 7804170251266531779
},
"Component_[7874177159288365422]": {
"$type": "EditorEntitySortComponent",
"Id": 7874177159288365422
},
"Component_[8018146290632383969]": {
"$type": "EditorEntityIconComponent",
"Id": 8018146290632383969
},
"Component_[8452360690590857075]": {
"$type": "SelectionComponent",
"Id": 8452360690590857075
}
}
},
"Entities": {
"Entity_[273050554979]": {
"Id": "Entity_[273050554979]",
"Name": "DefaultLevelSetup",
"Components": {
"Component_[10017568850356726118]": {
"$type": "EditorEntitySortComponent",
"Id": 10017568850356726118
},
"Component_[13730791873699866292]": {
"$type": "EditorVisibilityComponent",
"Id": 13730791873699866292
},
"Component_[14036484285432839222]": {
"$type": "EditorInspectorComponent",
"Id": 14036484285432839222,
"ComponentOrderEntryArray": [
{
"ComponentId": 9432950532896492451
},
{
"ComponentId": 16094906495473882735,
"SortIndex": 1
}
]
},
"Component_[16744953497953161231]": {
"$type": "EditorOnlyEntityComponent",
"Id": 16744953497953161231
},
"Component_[17819059404707659501]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 17819059404707659501
},
"Component_[2317367007807622931]": {
"$type": "EditorLockComponent",
"Id": 2317367007807622931
},
"Component_[2676812743453687109]": {
"$type": "EditorPendingCompositionComponent",
"Id": 2676812743453687109
},
"Component_[3047355939801335922]": {
"$type": "EditorEntityIconComponent",
"Id": 3047355939801335922
},
"Component_[3687396159953003426]": {
"$type": "SelectionComponent",
"Id": 3687396159953003426
},
"Component_[9432950532896492451]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 9432950532896492451,
"Parent Entity": "ContainerEntity",
"Transform Data": {
"Translate": [
512.0,
512.0,
100.0
]
}
}
}
}
}
}

@ -1,14 +0,0 @@
<Environment>
<Fog ViewDistance="8000" ViewDistanceLowSpec="1000"/>
<Terrain DetailLayersViewDistRatio="1.0" HeightMapAO="0"/>
<EnvState WindVector="1,0,0" BreezeGeneration="0" BreezeStrength="1.f" BreezeMovementSpeed="8.f" BreezeVariation="1.f" BreezeLifeTime="15.f" BreezeCount="4" BreezeSpawnRadius="25.f" BreezeSpread="0.f" BreezeRadius="5.f" ConsoleMergedMeshesPool="2750" ShowTerrainSurface="false" SunShadowsMinSpec="1" SunShadowsAdditionalCascadeMinSpec="0" SunShadowsClipPlaneRange="256.0f" SunShadowsClipPlaneRangeShift="0.0f" UseLayersActivation="0" SunLinkedToTOD="1"/>
<VolFogShadows Enable="0" EnableForClouds="0"/>
<CloudShadows CloudShadowTexture="" CloudShadowSpeed="0,0,0" CloudShadowTiling="1.0" CloudShadowBrightness="1.0" CloudShadowInvert="0"/>
<ParticleLighting AmbientMul="1.0" LightsMul="1.0"/>
<SkyBox Material="EngineAssets/Materials/Sky/Sky" MaterialLowSpec="EngineAssets/Materials/Sky/Sky" Angle="0" Stretching="0.5"/>
<Ocean Material="EngineAssets/Materials/Water/Ocean_default" CausticsDistanceAtten="100.0" CausticDepth="8.0" CausticIntensity="1.0" CausticsTilling="1.0"/>
<OceanAnimation WindDirection="1.0" WindSpeed="4.0" WavesAmount="1.5" WavesSize="0.4" WavesSpeed="1.0"/>
<Moon Latitude="240.0" Longitude="45.0" Size="0.5" Texture="Textures/Skys/Night/half_moon.dds"/>
<DynTexSource Width="256" Height="256"/>
<Total_Illumination_v2 Active="0" IntegrationMode="0" NumberOfBounces="1" DiffuseConeWidth="24" ConeMaxLength="12.0" UseLightProbes="0" InjectionMultiplier="1.0" AmbientOffsetRed="1.0" AmbientOffsetGreen="1.0" AmbientOffsetBlue="1.0" AmbientOffsetBias="0.1" Saturation="0.8" SSAOAmount="0.7"/>
</Environment>

@ -1,7 +0,0 @@
<TerrainTexture TileCountX="1" TileCountY="1" TileResolution="512">
<RGBLayer>
<Tiles>
<tile X="0" Y="0" Size="512"/>
</Tiles>
</RGBLayer>
</TerrainTexture>

@ -1,356 +0,0 @@
<TimeOfDay Time="13.5" TimeStart="13.5" TimeEnd="13.5" TimeAnimSpeed="0">
<Variable Name="Sun color" Color="0.99989021,0.99946922,0.9991194">
<Spline Keys="-0.000628322:(0.783538:0.89627:0.930341):36,0:(0.783538:0.887923:0.921582):36,0.229167:(0.783538:0.879623:0.921582):36,0.25:(0.947307:0.745404:0.577581):36,0.458333:(1:1:1):36,0.5625:(1:1:1):36,0.75:(0.947307:0.745404:0.577581):36,0.770833:(0.783538:0.879623:0.921582):36,1:(0.783538:0.89627:0.930556):36,"/>
</Variable>
<Variable Name="Sun intensity" Value="92366.68">
<Spline Keys="0:1000:36,0.229167:1000:36,0.5:120000:36,0.770833:1000:65572,0.999306:1000:36,"/>
</Variable>
<Variable Name="Sun specular multiplier" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:36,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Fog color" Color="0.27049801,0.47353199,0.83076996">
<Spline Keys="0:(0.00651209:0.00972122:0.0137021):36,0.229167:(0.00604883:0.00972122:0.0137021):36,0.25:(0.270498:0.473532:0.83077):36,0.5:(0.270498:0.473532:0.83077):458788,0.75:(0.270498:0.473532:0.83077):36,0.770833:(0.00604883:0.00972122:0.0137021):36,1:(0.00651209:0.00972122:0.0137021):36,"/>
</Variable>
<Variable Name="Fog color multiplier" Value="1">
<Spline Keys="0:0.5:36,0.229167:0.5:36,0.25:1:36,0.5:1:36,0.75:1:36,0.770833:0.5:36,1:0.5:65572,"/>
</Variable>
<Variable Name="Fog height (bottom)" Value="0">
<Spline Keys="0:0:36,0.25:0:36,0.5:0:36,0.75:0:36,1:0:36,"/>
</Variable>
<Variable Name="Fog layer density (bottom)" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:36,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Fog color (top)" Color="0.597202,0.72305501,0.91309899">
<Spline Keys="0:(0.00699541:0.00972122:0.0122865):36,0.229167:(0.00699541:0.00972122:0.0122865):36,0.25:(0.597202:0.723055:0.913099):36,0.5:(0.597202:0.723055:0.913099):458788,0.75:(0.597202:0.723055:0.913099):36,0.770833:(0.00699541:0.00972122:0.0122865):36,1:(0.00699541:0.00972122:0.0122865):36,"/>
</Variable>
<Variable Name="Fog color (top) multiplier" Value="0.88389361">
<Spline Keys="-4.40702e-06:0.5:36,0.0297507:0.499195:36,0.229167:0.5:36,0.5:1:36,0.770833:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Fog height (top)" Value="100.00001">
<Spline Keys="0:100:36,0.25:100:36,0.5:100:36,0.75:100:65572,1:100:36,"/>
</Variable>
<Variable Name="Fog layer density (top)" Value="9.9999997e-05">
<Spline Keys="0:0.0001:36,0.25:0.0001:36,0.5:0.0001:65572,0.75:0.0001:36,1:0.0001:36,"/>
</Variable>
<Variable Name="Fog color height offset" Value="0">
<Spline Keys="0:0:36,0.25:0:36,0.5:0:36,0.75:0:36,1:0:65572,"/>
</Variable>
<Variable Name="Fog color (radial)" Color="0.78592348,0.52744436,0.17234583">
<Spline Keys="0:(0:0:0):36,0.229167:(0.00439144:0.00367651:0.00334654):36,0.25:(0.838799:0.564712:0.184475):36,0.5:(0.768151:0.514918:0.168269):458788,0.75:(0.838799:0.564712:0.184475):36,0.770833:(0.00402472:0.00334654:0.00303527):36,1:(0:0:0):36,"/>
</Variable>
<Variable Name="Fog color (radial) multiplier" Value="6">
<Spline Keys="0:0:36,0.25:6:36,0.5:6:36,0.75:6:36,1:0:36,"/>
</Variable>
<Variable Name="Fog radial size" Value="0.85000002">
<Spline Keys="0:0:36,0.25:0.85:65572,0.5:0.85:36,0.75:0.85:36,1:0:36,"/>
</Variable>
<Variable Name="Fog radial lobe" Value="0.75">
<Spline Keys="0:0:36,0.25:0.75:36,0.5:0.75:36,0.75:0.75:65572,1:0:36,"/>
</Variable>
<Variable Name="Volumetric fog: Final density clamp" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Volumetric fog: Global density" Value="1.5">
<Spline Keys="0:1.5:36,0.25:1.5:36,0.5:1.5:65572,0.75:1.5:36,1:1.5:36,"/>
</Variable>
<Variable Name="Volumetric fog: Ramp start" Value="25.000002">
<Spline Keys="0:25:36,0.25:25:36,0.5:25:65572,0.75:25:36,1:25:36,"/>
</Variable>
<Variable Name="Volumetric fog: Ramp end" Value="1000.0001">
<Spline Keys="0:1000:36,0.25:1000:36,0.5:1000:65572,0.75:1000:36,1:1000:36,"/>
</Variable>
<Variable Name="Volumetric fog: Ramp influence" Value="0.69999993">
<Spline Keys="0:0.7:36,0.25:0.7:36,0.5:0.7:65572,0.75:0.7:36,1:0.7:36,"/>
</Variable>
<Variable Name="Volumetric fog: Shadow darkening" Value="0.20000002">
<Spline Keys="0:0.2:36,0.25:0.2:36,0.5:0.2:65572,0.75:0.2:36,1:0.2:36,"/>
</Variable>
<Variable Name="Volumetric fog: Shadow darkening sun" Value="0.5">
<Spline Keys="0:0.5:36,0.25:0.5:36,0.5:0.5:65572,0.75:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Volumetric fog: Shadow darkening ambient" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Volumetric fog: Shadow range" Value="0.10000001">
<Spline Keys="0:0.1:36,0.25:0.1:36,0.5:0.1:65572,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog height (bottom)" Value="0">
<Spline Keys="0:0:0,1:0:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog layer density (bottom)" Value="1">
<Spline Keys="0:1:0,1:1:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog height (top)" Value="4000">
<Spline Keys="0:4000:0,1:4000:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog layer density (top)" Value="9.9999997e-05">
<Spline Keys="0:0.0001:0,1:0.0001:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Global fog density" Value="0.1">
<Spline Keys="0:0.1:0,1:0.1:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Ramp start" Value="0">
<Spline Keys="0:0:0,1:0:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Ramp end" Value="0">
<Spline Keys="0:0:0,1:0:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog albedo color (atmosphere)" Color="1,1,1">
<Spline Keys="0:(1:1:1):0,1:(1:1:1):0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Anisotropy factor (atmosphere)" Value="0.60000002">
<Spline Keys="0:0.6:0,1:0.6:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog albedo color (sun radial)" Color="1,1,1">
<Spline Keys="0:(1:1:1):0,1:(1:1:1):0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Anisotropy factor (sun radial)" Value="0.94999999">
<Spline Keys="0:0.95:0,1:0.95:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Blend factor for sun scattering" Value="1">
<Spline Keys="0:1:0,1:1:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Blend mode for sun scattering" Value="0">
<Spline Keys="0:0:0,1:0:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Fog albedo color (entities)" Color="1,1,1">
<Spline Keys="0:(1:1:1):0,1:(1:1:1):0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Anisotropy factor (entities)" Value="0.60000002">
<Spline Keys="0:0.6:0,1:0.6:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Maximum range of ray-marching" Value="64">
<Spline Keys="0:64:0,1:64:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: In-scattering factor" Value="1">
<Spline Keys="0:1:0,1:1:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Extinction factor" Value="0.30000001">
<Spline Keys="0:0.3:0,1:0.3:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Analytical volumetric fog visibility" Value="0.5">
<Spline Keys="0:0.5:0,1:0.5:0,"/>
</Variable>
<Variable Name="Volumetric fog 2: Final density clamp" Value="1">
<Spline Keys="0:1:0,0.5:1:36,1:1:0,"/>
</Variable>
<Variable Name="Sky light: Sun intensity" Color="1,1,1">
<Spline Keys="0:(1:1:1):36,0.25:(1:1:1):36,0.494381:(1:1:1):65572,0.5:(1:1:1):36,0.75:(1:1:1):36,1:(1:1:1):36,"/>
</Variable>
<Variable Name="Sky light: Sun intensity multiplier" Value="200.00002">
<Spline Keys="0:200:36,0.25:200:36,0.5:200:36,0.75:200:36,1:200:36,"/>
</Variable>
<Variable Name="Sky light: Mie scattering" Value="6.779707">
<Spline Keys="0:40:36,0.5:2:36,1:40:36,"/>
</Variable>
<Variable Name="Sky light: Rayleigh scattering" Value="0.20000002">
<Spline Keys="0:0.2:36,0.229167:0.2:36,0.25:1:36,0.291667:0.2:36,0.5:0.2:36,0.729167:0.2:36,0.75:1:36,0.770833:0.2:36,1:0.2:36,"/>
</Variable>
<Variable Name="Sky light: Sun anisotropy factor" Value="-0.99989998">
<Spline Keys="0:-0.9999:36,0.25:-0.9999:36,0.5:-0.9999:65572,0.75:-0.9999:36,1:-0.9999:36,"/>
</Variable>
<Variable Name="Sky light: Wavelength (R)" Value="694">
<Spline Keys="0:694:36,0.25:694:36,0.5:694:65572,0.75:694:36,1:694:36,"/>
</Variable>
<Variable Name="Sky light: Wavelength (G)" Value="596.99994">
<Spline Keys="0:597:36,0.25:597:36,0.5:597:36,0.75:597:36,1:597:36,"/>
</Variable>
<Variable Name="Sky light: Wavelength (B)" Value="488">
<Spline Keys="0:488:36,0.25:488:36,0.5:488:65572,0.75:488:36,1:488:36,"/>
</Variable>
<Variable Name="Night sky: Horizon color" Color="0.27049801,0.39157301,0.52711499">
<Spline Keys="0:(0.270498:0.391573:0.520996):36,0.25:(0.270498:0.391573:0.527115):36,0.5:(0.270498:0.391573:0.527115):262180,0.75:(0.270498:0.391573:0.527115):36,1:(0.270498:0.391573:0.520996):36,"/>
</Variable>
<Variable Name="Night sky: Horizon color multiplier" Value="0">
<Spline Keys="0:0.1:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0.1:36,"/>
</Variable>
<Variable Name="Night sky: Zenith color" Color="0.36130697,0.434154,0.46778399">
<Spline Keys="0:(0.361307:0.434154:0.467784):36,0.25:(0.361307:0.434154:0.467784):36,0.5:(0.361307:0.434154:0.467784):262180,0.75:(0.361307:0.434154:0.467784):36,1:(0.361307:0.434154:0.467784):36,"/>
</Variable>
<Variable Name="Night sky: Zenith color multiplier" Value="0">
<Spline Keys="0:0.02:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0.02:36,"/>
</Variable>
<Variable Name="Night sky: Zenith shift" Value="0.5">
<Spline Keys="0:0.5:36,0.25:0.5:36,0.5:0.5:65572,0.75:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Night sky: Star intensity" Value="0">
<Spline Keys="0:3:36,0.25:0:36,0.5:0:65572,0.75:0:36,0.836647:1.03977:36,1:3:36,"/>
</Variable>
<Variable Name="Night sky: Moon color" Color="1,1,1">
<Spline Keys="0:(1:1:1):36,0.25:(1:1:1):36,0.5:(1:1:1):458788,0.75:(1:1:1):36,1:(1:1:1):36,"/>
</Variable>
<Variable Name="Night sky: Moon color multiplier" Value="0">
<Spline Keys="0:0.4:36,0.25:0:36,0.5:0:36,0.75:0:65572,1:0.4:36,"/>
</Variable>
<Variable Name="Night sky: Moon inner corona color" Color="0.904661,1,1">
<Spline Keys="0:(0.89627:1:1):36,0.25:(0.904661:1:1):36,0.5:(0.904661:1:1):393252,0.75:(0.904661:1:1):36,0.836647:(0.89627:1:1):36,1:(0.89627:1:1):36,"/>
</Variable>
<Variable Name="Night sky: Moon inner corona color multiplier" Value="0">
<Spline Keys="0:0.1:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0.1:36,"/>
</Variable>
<Variable Name="Night sky: Moon inner corona scale" Value="0">
<Spline Keys="0:2:36,0.25:0:36,0.5:0:65572,0.75:0:36,0.836647:0.693178:36,1:2:36,"/>
</Variable>
<Variable Name="Night sky: Moon outer corona color" Color="0.201556,0.22696599,0.25415203">
<Spline Keys="0:(0.198069:0.226966:0.250158):36,0.25:(0.201556:0.226966:0.254152):36,0.5:(0.201556:0.226966:0.254152):36,0.75:(0.201556:0.226966:0.254152):36,1:(0.198069:0.226966:0.250158):36,"/>
</Variable>
<Variable Name="Night sky: Moon outer corona color multiplier" Value="0">
<Spline Keys="0:0.1:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0.1:36,"/>
</Variable>
<Variable Name="Night sky: Moon outer corona scale" Value="0">
<Spline Keys="0:0.01:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0.01:36,"/>
</Variable>
<Variable Name="Cloud shading: Sun light multiplier" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Cloud shading: Sun custom color" Color="0.83076996,0.76815104,0.65837508">
<Spline Keys="0:(0.737911:0.737911:0.737911):36,0.25:(0.83077:0.768151:0.658375):36,0.5:(0.83077:0.768151:0.658375):458788,0.75:(0.83077:0.768151:0.658375):36,1:(0.737911:0.737911:0.737911):36,"/>
</Variable>
<Variable Name="Cloud shading: Sun custom color multiplier" Value="1">
<Spline Keys="0:0.1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cloud shading: Sun custom color influence" Value="0">
<Spline Keys="0:0.5:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0.5:36,"/>
</Variable>
<Variable Name="Sun shafts visibility" Value="0">
<Spline Keys="0:0:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0:36,"/>
</Variable>
<Variable Name="Sun rays visibility" Value="1.5">
<Spline Keys="0:1:36,0.25:1.5:36,0.5:1.5:65572,0.75:1.5:36,1:1:36,"/>
</Variable>
<Variable Name="Sun rays attenuation" Value="1.5">
<Spline Keys="0:0.1:36,0.25:1.5:36,0.5:1.5:65572,0.75:1.5:36,1:0.1:36,"/>
</Variable>
<Variable Name="Sun rays suncolor influence" Value="0.5">
<Spline Keys="0:0.5:36,0.25:0.5:36,0.5:0.5:65572,0.75:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Sun rays custom color" Color="0.66538697,0.83879906,0.94730699">
<Spline Keys="0:(0.665387:0.838799:0.947307):36,0.25:(0.665387:0.838799:0.947307):36,0.5:(0.665387:0.838799:0.947307):458788,0.75:(0.665387:0.838799:0.947307):36,1:(0.665387:0.838799:0.947307):36,"/>
</Variable>
<Variable Name="Ocean fog color" Color="0.0012141101,0.0091340598,0.017642001">
<Spline Keys="0:(0.00121411:0.00913406:0.017642):36,0.25:(0.00121411:0.00913406:0.017642):36,0.5:(0.00121411:0.00913406:0.017642):458788,0.75:(0.00121411:0.00913406:0.017642):36,1:(0.00121411:0.00913406:0.017642):36,"/>
</Variable>
<Variable Name="Ocean fog color multiplier" Value="0.5">
<Spline Keys="0:0.5:36,0.25:0.5:36,0.5:0.5:65572,0.75:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Ocean fog density" Value="0.5">
<Spline Keys="0:0.5:36,0.25:0.5:36,0.5:0.5:65572,0.75:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Static skybox multiplier" Value="1">
<Spline Keys="0:1:0,1:1:0,"/>
</Variable>
<Variable Name="Film curve shoulder scale" Value="2.232213">
<Spline Keys="0:3:36,0.229167:3:36,0.5:2:36,0.770833:3:36,1:3:36,"/>
</Variable>
<Variable Name="Film curve midtones scale" Value="0.88389361">
<Spline Keys="0:0.5:36,0.229167:0.5:36,0.5:1:36,0.770833:0.5:36,1:0.5:36,"/>
</Variable>
<Variable Name="Film curve toe scale" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Film curve whitepoint" Value="4">
<Spline Keys="0:4:36,0.25:4:36,0.5:4:65572,0.75:4:36,1:4:36,"/>
</Variable>
<Variable Name="Saturation" Value="1">
<Spline Keys="0:0.8:36,0.229167:0.8:36,0.5:1:36,0.751391:1:65572,0.770833:0.8:36,1:0.8:36,"/>
</Variable>
<Variable Name="Color balance" Color="1,1,1">
<Spline Keys="0:(1:1:1):36,0.25:(1:1:1):36,0.5:(1:1:1):36,0.75:(1:1:1):36,1:(1:1:1):36,"/>
</Variable>
<Variable Name="Scene key" Value="0.18000002">
<Spline Keys="0:0.18:36,0.25:0.18:36,0.5:0.18:65572,0.75:0.18:36,1:0.18:36,"/>
</Variable>
<Variable Name="Min exposure" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Max exposure" Value="2.6142297">
<Spline Keys="0:2:36,0.229167:2:36,0.5:2.8:36,0.770833:2:36,1:2:36,"/>
</Variable>
<Variable Name="EV Min" Value="4.5">
<Spline Keys="0:4.5:0,1:4.5:0,"/>
</Variable>
<Variable Name="EV Max" Value="17">
<Spline Keys="0:17:0,1:17:0,"/>
</Variable>
<Variable Name="EV Auto compensation" Value="1.5">
<Spline Keys="0:1.5:0,1:1.5:0,"/>
</Variable>
<Variable Name="Bloom amount" Value="0.30899152">
<Spline Keys="0:1:36,0.229167:1:36,0.5:0.1:36,0.770833:1:36,1:1:36,"/>
</Variable>
<Variable Name="Filters: grain" Value="0">
<Spline Keys="0:0.3:65572,0.229167:0.3:36,0.25:0:36,0.5:0:36,0.75:0:36,1:0.3:36,"/>
</Variable>
<Variable Name="Filters: photofilter color" Color="0,0,0">
<Spline Keys="0:(0:0:0):36,0.25:(0:0:0):36,0.5:(0:0:0):458788,0.75:(0:0:0):36,1:(0:0:0):36,"/>
</Variable>
<Variable Name="Filters: photofilter density" Value="0">
<Spline Keys="0:0:36,0.25:0:36,0.5:0:36,0.75:0:36,1:0:36,"/>
</Variable>
<Variable Name="Dof: focus range" Value="500.00003">
<Spline Keys="0:500:36,0.25:500:36,0.5:500:65572,0.75:500:36,1:500:36,"/>
</Variable>
<Variable Name="Dof: blur amount" Value="0.10000001">
<Spline Keys="0:0.1:36,0.25:0.1:36,0.5:0.1:65572,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 0: Bias" Value="0.10000001">
<Spline Keys="0:0.1:36,0.25:0.1:36,0.5:0.1:65572,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 0: Slope Bias" Value="64">
<Spline Keys="0:64:36,0.25:64:36,0.5:64:65572,0.75:64:36,1:64:36,"/>
</Variable>
<Variable Name="Cascade 1: Bias" Value="0.10000001">
<Spline Keys="0:0.1:36,0.25:0.1:36,0.5:0.1:65572,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 1: Slope Bias" Value="23">
<Spline Keys="0:23:36,0.25:23:36,0.5:23:65572,0.75:23:36,1:23:36,"/>
</Variable>
<Variable Name="Cascade 2: Bias" Value="0.10000001">
<Spline Keys="0:0.1:36,0.25:0.1:36,0.5:0.1:65572,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 2: Slope Bias" Value="4">
<Spline Keys="0:4:36,0.25:4:36,0.5:4:65572,0.75:4:36,1:4:36,"/>
</Variable>
<Variable Name="Cascade 3: Bias" Value="0.10000001">
<Spline Keys="0:0.1:36,0.25:0.1:36,0.5:0.1:36,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 3: Slope Bias" Value="1">
<Spline Keys="0:1:36,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Cascade 4: Bias" Value="0.10000001">
<Spline Keys="0:0.1:0,0.25:0.1:36,0.5:0.1:65572,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 4: Slope Bias" Value="1">
<Spline Keys="0:1:0,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Cascade 5: Bias" Value="0.0099999998">
<Spline Keys="0:0.01:0,0.25:0.01:36,0.5:0.01:65572,0.75:0.01:36,1:0.01:36,"/>
</Variable>
<Variable Name="Cascade 5: Slope Bias" Value="1">
<Spline Keys="0:1:0,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Cascade 6: Bias" Value="0.10000001">
<Spline Keys="0:0.1:0,0.25:0.1:36,0.5:0.1:36,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 6: Slope Bias" Value="1">
<Spline Keys="0:1:0,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Cascade 7: Bias" Value="0.10000001">
<Spline Keys="0:0.1:0,0.25:0.1:36,0.5:0.1:36,0.75:0.1:36,1:0.1:36,"/>
</Variable>
<Variable Name="Cascade 7: Slope Bias" Value="1">
<Spline Keys="0:1:0,0.25:1:36,0.5:1:65572,0.75:1:36,1:1:36,"/>
</Variable>
<Variable Name="Shadow jittering" Value="2.4999998">
<Spline Keys="0:5:36,0.25:2.5:36,0.5:2.5:65572,0.75:2.5:36,1:5:0,"/>
</Variable>
<Variable Name="HDR dynamic power factor" Value="0">
<Spline Keys="0:0:36,0.25:0:36,0.5:0:65572,0.75:0:36,1:0:36,"/>
</Variable>
<Variable Name="Sky brightening (terrain occlusion)" Value="0">
<Spline Keys="0:0:36,0.25:0:36,0.5:0:36,0.75:0:36,1:0:36,"/>
</Variable>
<Variable Name="Sun color multiplier" Value="9.999999">
<Spline Keys="0:0.1:36,0.25:10:36,0.5:10:36,0.75:10:36,1:0.1:36,"/>
</Variable>
</TimeOfDay>

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9
size 63

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d85
size 22

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9799510badafd54e2a47574ea7e0112b8cb7eb23facf4dff8adc6f51fa624953
size 7955

@ -43,13 +43,14 @@ AZ_POP_DISABLE_WARNING
#include <AzCore/RTTI/BehaviorContext.h> #include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/smart_ptr/make_shared.h> #include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h> #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h> #include <AzCore/Utils/Utils.h>
#include <AzCore/Console/IConsole.h> #include <AzCore/Console/IConsole.h>
// AzFramework // AzFramework
#include <AzFramework/Components/CameraBus.h> #include <AzFramework/Components/CameraBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h> #include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/ProjectManager/ProjectManager.h> #include <AzFramework/ProjectManager/ProjectManager.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h> #include <AzFramework/Spawnable/RootSpawnableInterface.h>
@ -104,7 +105,6 @@ AZ_POP_DISABLE_WARNING
#include "WaitProgress.h" #include "WaitProgress.h"
#include "ToolBox.h" #include "ToolBox.h"
#include "LevelInfo.h"
#include "EditorPreferencesDialog.h" #include "EditorPreferencesDialog.h"
#include "AnimationContext.h" #include "AnimationContext.h"
@ -422,7 +422,6 @@ void CCryEditApp::RegisterActionHandlers()
ON_COMMAND(ID_DISPLAY_GOTOPOSITION, OnDisplayGotoPosition) ON_COMMAND(ID_DISPLAY_GOTOPOSITION, OnDisplayGotoPosition)
ON_COMMAND(ID_FILE_SAVELEVELRESOURCES, OnFileSavelevelresources) ON_COMMAND(ID_FILE_SAVELEVELRESOURCES, OnFileSavelevelresources)
ON_COMMAND(ID_CLEAR_REGISTRY, OnClearRegistryData) ON_COMMAND(ID_CLEAR_REGISTRY, OnClearRegistryData)
ON_COMMAND(ID_VALIDATELEVEL, OnValidatelevel)
ON_COMMAND(ID_TOOLS_PREFERENCES, OnToolsPreferences) ON_COMMAND(ID_TOOLS_PREFERENCES, OnToolsPreferences)
ON_COMMAND(ID_SWITCHCAMERA_DEFAULTCAMERA, OnSwitchToDefaultCamera) ON_COMMAND(ID_SWITCHCAMERA_DEFAULTCAMERA, OnSwitchToDefaultCamera)
ON_COMMAND(ID_SWITCHCAMERA_SEQUENCECAMERA, OnSwitchToSequenceCamera) ON_COMMAND(ID_SWITCHCAMERA_SEQUENCECAMERA, OnSwitchToSequenceCamera)
@ -770,28 +769,6 @@ QString CCryEditApp::ShowWelcomeDialog()
return levelName; return levelName;
} }
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::InitDirectory()
{
//////////////////////////////////////////////////////////////////////////
// Initializes Root folder of the game.
//////////////////////////////////////////////////////////////////////////
QString szExeFileName = qApp->applicationDirPath();
const static char* s_engineMarkerFile = "engine.json";
while (!QFile::exists(QString("%1/%2").arg(szExeFileName, s_engineMarkerFile)))
{
QDir currentdir(szExeFileName);
if (!currentdir.cdUp())
{
break;
}
szExeFileName = currentdir.absolutePath();
}
QDir::setCurrent(szExeFileName);
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Needed to work with custom memory manager. // Needed to work with custom memory manager.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -1504,7 +1481,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
// We support specifying multiple files in the cmdline by separating them with ';' // We support specifying multiple files in the cmdline by separating them with ';'
AZStd::vector<AZStd::string_view> fileList; AZStd::vector<AZStd::string_view> fileList;
AzFramework::StringFunc::TokenizeVisitor( AZ::StringFunc::TokenizeVisitor(
fileStr.constData(), fileStr.constData(),
[&fileList](AZStd::string_view elem) [&fileList](AZStd::string_view elem)
{ {
@ -1516,7 +1493,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
{ {
QByteArray pythonArgsStr = cmdInfo.m_pythonArgs.toUtf8(); QByteArray pythonArgsStr = cmdInfo.m_pythonArgs.toUtf8();
AZStd::vector<AZStd::string_view> pythonArgs; AZStd::vector<AZStd::string_view> pythonArgs;
AzFramework::StringFunc::TokenizeVisitor(pythonArgsStr.constData(), AZ::StringFunc::TokenizeVisitor(pythonArgsStr.constData(),
[&pythonArgs](AZStd::string_view elem) [&pythonArgs](AZStd::string_view elem)
{ {
pythonArgs.push_back(elem); pythonArgs.push_back(elem);
@ -1531,7 +1508,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
testcaseList.resize(fileList.size()); testcaseList.resize(fileList.size());
{ {
int i = 0; int i = 0;
AzFramework::StringFunc::TokenizeVisitor( AZ::StringFunc::TokenizeVisitor(
pythonTestCase.constData(), pythonTestCase.constData(),
[&i, &testcaseList](AZStd::string_view elem) [&i, &testcaseList](AZStd::string_view elem)
{ {
@ -1595,7 +1572,6 @@ bool CCryEditApp::InitInstance()
{ {
QElapsedTimer startupTimer; QElapsedTimer startupTimer;
startupTimer.start(); startupTimer.start();
InitDirectory();
// create / attach to the environment: // create / attach to the environment:
AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance()); AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance());
@ -1606,8 +1582,6 @@ bool CCryEditApp::InitInstance()
InitFromCommandLine(cmdInfo); InitFromCommandLine(cmdInfo);
InitDirectory();
qobject_cast<Editor::EditorQtApplication*>(qApp)->Initialize(); // Must be done after CEditorImpl() is created qobject_cast<Editor::EditorQtApplication*>(qApp)->Initialize(); // Must be done after CEditorImpl() is created
m_pEditor->Initialize(); m_pEditor->Initialize();
@ -3644,14 +3618,6 @@ void CCryEditApp::OnClearRegistryData()
} }
} }
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnValidatelevel()
{
// TODO: Add your command handler code here
CLevelInfo levelInfo;
levelInfo.Validate();
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnToolsPreferences() void CCryEditApp::OnToolsPreferences()
{ {
@ -3827,131 +3793,68 @@ CMainFrame * CCryEditApp::GetMainFrame() const
return MainWindow::instance()->GetOldMainFrame(); return MainWindow::instance()->GetOldMainFrame();
} }
void CCryEditApp::StartProcessDetached(const char* process, const char* args)
{
// Build the arguments as a QStringList
AZStd::vector<AZStd::string> tokens;
// separate the string based on spaces for paths like "-launch", "lua", "-files";
// also separate the string and keep spaces inside the folder path;
// Ex: C:\dev\Foundation\dev\Cache\AutomatedTesting\pc\automatedtesting\scripts\components\a a\empty.lua;
// Ex: C:\dev\Foundation\dev\Cache\AutomatedTesting\pc\automatedtesting\scripts\components\a a\'empty'.lua;
AZStd::string currentStr(args);
AZStd::size_t firstQuotePos = AZStd::string::npos;
AZStd::size_t secondQuotePos = 0;
AZStd::size_t pos = 0;
while (!currentStr.empty())
{
firstQuotePos = currentStr.find_first_of('\"');
pos = currentStr.find_first_of(" ");
if ((firstQuotePos != AZStd::string::npos) && (firstQuotePos < pos || pos == AZStd::string::npos))
{
secondQuotePos = currentStr.find_first_of('\"', firstQuotePos + 1);
if (secondQuotePos == AZStd::string::npos)
{
AZ_Warning("StartProcessDetached", false, "String tokenize failed, no matching \" found.");
return;
}
AZStd::string newElement(AZStd::string(currentStr.data() + (firstQuotePos + 1), (secondQuotePos - 1))); void CCryEditApp::OpenLUAEditor(const char* files)
tokens.push_back(newElement); {
AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath();
currentStr = currentStr.substr(secondQuotePos + 1); AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
firstQuotePos = AZStd::string::npos; AZStd::string filename = "LuaIDE";
secondQuotePos = 0; AZ::IO::FixedMaxPath executablePath = AZ::Utils::GetExecutableDirectory();
continue; executablePath /= filename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION;
}
else
{
if (pos != AZStd::string::npos)
{
AZStd::string newElement(AZStd::string(currentStr.data() + 0, pos));
tokens.push_back(newElement);
currentStr = currentStr.substr(pos + 1);
}
else
{
tokens.push_back(AZStd::string(currentStr));
break;
}
}
}
QStringList argsList; if (!AZ::IO::SystemFile::Exists(executablePath.c_str()))
for (const auto& arg : tokens)
{ {
argsList.push_back(QString(arg.c_str())); AZ_Error("LuaIDE", false, "%s not found", executablePath.c_str());
return;
} }
// Launch the process AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
[[maybe_unused]] bool startDetachedReturn = QProcess::startDetached(
process,
argsList,
QCoreApplication::applicationDirPath()
);
AZ_Warning("StartProcessDetached", startDetachedReturn, "Failed to start process:%s args:%s", process, args);
}
void CCryEditApp::OpenLUAEditor(const char* files)
{
AZStd::string args = "-launch lua";
if (files && strlen(files) > 0)
{
AZStd::vector<AZStd::string> resolvedPaths;
AZStd::vector<AZStd::string> tokens; AZStd::vector<AZStd::string> launchCmd = { executablePath.String() };
launchCmd.emplace_back("--engine-path");
launchCmd.emplace_back(AZStd::string_view{ enginePath });
launchCmd.emplace_back("--project-path");
launchCmd.emplace_back(AZStd::string_view{ projectPath });
launchCmd.emplace_back("--launch");
launchCmd.emplace_back("lua");
AzFramework::StringFunc::Tokenize(files, tokens, '|'); auto ParseFilesList = [&launchCmd](AZStd::string_view filePath)
{
for (const auto& file : tokens) bool fullPathFound = false;
auto GetFullSourcePath = [&launchCmd, &filePath, &fullPathFound]
(AzToolsFramework::AssetSystem::AssetSystemRequest* assetSystemRequests)
{ {
char resolved[AZ_MAX_PATH_LEN]; AZ::IO::Path assetFullPath;
if(assetSystemRequests->GetFullSourcePathFromRelativeProductPath(filePath, assetFullPath.Native()))
AZStd::string fullPath = Path::GamePathToFullPath(file.c_str()).toUtf8().data();
azstrncpy(resolved, AZ_MAX_PATH_LEN, fullPath.c_str(), fullPath.size());
if (AZ::IO::FileIOBase::GetInstance()->Exists(resolved))
{ {
AZStd::string current = '\"' + AZStd::string(resolved) + '\"'; fullPathFound = true;
AZStd::replace(current.begin(), current.end(), '\\', '/'); launchCmd.emplace_back("--files");
resolvedPaths.push_back(current); launchCmd.emplace_back(AZStd::move(assetFullPath.Native()));
} }
} };
AzToolsFramework::AssetSystemRequestBus::Broadcast(AZStd::move(GetFullSourcePath));
if (!resolvedPaths.empty()) // If the full source path could be found through the Asset System, then
{ // attempt to resolve the path using the FileIO instance
for (const auto& resolvedPath : resolvedPaths) if (!fullPathFound)
{
AZ::IO::FixedMaxPath resolvedFilePath;
if (auto fileIo = AZ::IO::FileIOBase::GetInstance();
fileIo != nullptr && fileIo->ResolvePath(resolvedFilePath, filePath)
&& fileIo->Exists(resolvedFilePath.c_str()))
{ {
args.append(AZStd::string::format(" -files %s", resolvedPath.c_str())); launchCmd.emplace_back("--files");
launchCmd.emplace_back(resolvedFilePath.String());
} }
} }
} };
AZ::StringFunc::TokenizeVisitor(files, ParseFilesList, "|");
AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
AZ_Assert(!engineRoot.empty(), "Unable to query Engine Path");
AZStd::string_view exePath;
AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder);
#if defined(AZ_PLATFORM_LINUX)
// On Linux platforms, launching a process is not done through a shell and its arguments are passed in
// separately. There is no need to wrap the process path in case of spaces in the path
constexpr const char* argumentQuoteString = "";
#else
constexpr const char* argumentQuoteString = "\"";
#endif
AZStd::string process = AZStd::string::format("%s%.*s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "LuaIDE" processLaunchInfo.m_commandlineParameters = AZStd::move(launchCmd);
#if defined(AZ_PLATFORM_WINDOWS)
".exe"
#endif
"%s", argumentQuoteString, aznumeric_cast<int>(exePath.size()), exePath.data(), argumentQuoteString);
AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot.c_str()); AZ_VerifyError("LuaIDE", AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo),
StartProcessDetached(process.c_str(), processArgs.c_str()); "Lua IDE has failed to launch at path %s", executablePath.c_str());
} }
void CCryEditApp::PrintAlways(const AZStd::string& output) void CCryEditApp::PrintAlways(const AZStd::string& output)
@ -4065,11 +3968,6 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
gSettings.Connect(); gSettings.Connect();
auto theApp = AZStd::make_unique<CCryEditApp>(); auto theApp = AZStd::make_unique<CCryEditApp>();
// this does some magic to set the current directory...
{
QCoreApplication app(argc, argv);
CCryEditApp::InitDirectory();
}
// Must be set before QApplication is initialized, so that we support HighDpi monitors, like the Retina displays // Must be set before QApplication is initialized, so that we support HighDpi monitors, like the Retina displays
// on Windows 10 // on Windows 10

@ -138,7 +138,6 @@ public:
RecentFileList* GetRecentFileList(); RecentFileList* GetRecentFileList();
virtual void AddToRecentFileList(const QString& lpszPathName); virtual void AddToRecentFileList(const QString& lpszPathName);
ECreateLevelResult CreateLevel(const QString& levelName, QString& fullyQualifiedLevelName); ECreateLevelResult CreateLevel(const QString& levelName, QString& fullyQualifiedLevelName);
static void InitDirectory();
bool FirstInstance(bool bForceNewInstance = false); bool FirstInstance(bool bForceNewInstance = false);
void InitFromCommandLine(CEditCommandLineInfo& cmdInfo); void InitFromCommandLine(CEditCommandLineInfo& cmdInfo);
bool CheckIfAlreadyRunning(); bool CheckIfAlreadyRunning();
@ -159,11 +158,6 @@ public:
// Print to stdout even if there out has been redirected // Print to stdout even if there out has been redirected
void PrintAlways(const AZStd::string& output); void PrintAlways(const AZStd::string& output);
//! Launches a detached process
//! \param process The path to the process to start
//! \param args Space separated list of arguments to pass to the process on start.
void StartProcessDetached(const char* process, const char* args);
//! Launches the Lua Editor/Debugger //! Launches the Lua Editor/Debugger
//! \param files A space separated list of aliased paths //! \param files A space separated list of aliased paths
void OpenLUAEditor(const char* files); void OpenLUAEditor(const char* files);
@ -399,7 +393,6 @@ private:
void OnDisplayGotoPosition(); void OnDisplayGotoPosition();
void OnFileSavelevelresources(); void OnFileSavelevelresources();
void OnClearRegistryData(); void OnClearRegistryData();
void OnValidatelevel();
void OnToolsPreferences(); void OnToolsPreferences();
void OnSwitchToDefaultCamera(); void OnSwitchToDefaultCamera();
void OnUpdateSwitchToDefaultCamera(QAction* action); void OnUpdateSwitchToDefaultCamera(QAction* action);

@ -260,11 +260,6 @@ namespace
namespace namespace
{ {
void PyStartProcessDetached(const char* process, const char* args)
{
CCryEditApp::instance()->StartProcessDetached(process, args);
}
void PyLaunchLUAEditor(const char* files) void PyLaunchLUAEditor(const char* files)
{ {
CCryEditApp::instance()->OpenLUAEditor(files); CCryEditApp::instance()->OpenLUAEditor(files);
@ -429,7 +424,6 @@ namespace AzToolsFramework
addLegacyGeneral(behaviorContext->Method("idle_wait", PyIdleWait, nullptr, "Waits idling for a given seconds. Primarily used for auto-testing.")); addLegacyGeneral(behaviorContext->Method("idle_wait", PyIdleWait, nullptr, "Waits idling for a given seconds. Primarily used for auto-testing."));
addLegacyGeneral(behaviorContext->Method("idle_wait_frames", PyIdleWaitFrames, nullptr, "Waits idling for a frames. Primarily used for auto-testing.")); addLegacyGeneral(behaviorContext->Method("idle_wait_frames", PyIdleWaitFrames, nullptr, "Waits idling for a frames. Primarily used for auto-testing."));
addLegacyGeneral(behaviorContext->Method("start_process_detached", PyStartProcessDetached, nullptr, "Launches a detached process with an optional space separated list of arguments."));
addLegacyGeneral(behaviorContext->Method("launch_lua_editor", PyLaunchLUAEditor, nullptr, "Launches the Lua editor, may receive a list of space separate file paths, or an empty string to only open the editor.")); addLegacyGeneral(behaviorContext->Method("launch_lua_editor", PyLaunchLUAEditor, nullptr, "Launches the Lua editor, may receive a list of space separate file paths, or an empty string to only open the editor."));
addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger")); addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger"));

@ -287,16 +287,16 @@ namespace SandboxEditor
return SandboxEditor::CameraBoostMultiplier(); return SandboxEditor::CameraBoostMultiplier();
}; };
m_orbitDollyScrollCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>(); m_orbitScrollDollyCamera = AZStd::make_shared<AzFramework::OrbitScrollDollyCameraInput>();
m_orbitDollyScrollCamera->m_scrollSpeedFn = [] m_orbitScrollDollyCamera->m_scrollSpeedFn = []
{ {
return SandboxEditor::CameraScrollSpeed(); return SandboxEditor::CameraScrollSpeed();
}; };
m_orbitDollyMoveCamera = AZStd::make_shared<AzFramework::OrbitDollyMotionCameraInput>(SandboxEditor::CameraOrbitDollyChannelId()); m_orbitMotionDollyCamera = AZStd::make_shared<AzFramework::OrbitMotionDollyCameraInput>(SandboxEditor::CameraOrbitDollyChannelId());
m_orbitDollyMoveCamera->m_motionSpeedFn = [] m_orbitMotionDollyCamera->m_motionSpeedFn = []
{ {
return SandboxEditor::CameraDollyMotionSpeed(); return SandboxEditor::CameraDollyMotionSpeed();
}; };
@ -326,8 +326,8 @@ namespace SandboxEditor
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera); m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera); m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera); m_orbitCamera->m_orbitCameras.AddCamera(m_orbitScrollDollyCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyMoveCamera); m_orbitCamera->m_orbitCameras.AddCamera(m_orbitMotionDollyCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera); m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitFocusCamera); m_orbitCamera->m_orbitCameras.AddCamera(m_orbitFocusCamera);
} }
@ -344,7 +344,7 @@ namespace SandboxEditor
m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId()); m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId());
m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId()); m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId());
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId()); m_orbitMotionDollyCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
m_orbitFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); m_orbitFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
} }

@ -56,8 +56,8 @@ namespace SandboxEditor
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera; AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_orbitRotateCamera; AZStd::shared_ptr<AzFramework::RotateCameraInput> m_orbitRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_orbitTranslateCamera; AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_orbitTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera; AZStd::shared_ptr<AzFramework::OrbitScrollDollyCameraInput> m_orbitScrollDollyCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyMotionCameraInput> m_orbitDollyMoveCamera; AZStd::shared_ptr<AzFramework::OrbitMotionDollyCameraInput> m_orbitMotionDollyCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera; AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_orbitFocusCamera; AZStd::shared_ptr<AzFramework::FocusCameraInput> m_orbitFocusCamera;

@ -1,178 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "LevelInfo.h"
// Qt
#include <QMessageBox>
// Editor
#include "Util/fastlib.h"
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include "QtUI/WaitCursor.h" // for WaitCursor
#include "Include/IErrorReport.h"
#include "Include/IObjectManager.h"
#include "Objects/BaseObject.h"
#include "UsedResources.h"
#include "ErrorReport.h"
//////////////////////////////////////////////////////////////////////////
CLevelInfo::CLevelInfo()
{
m_pReport = GetIEditor()->GetErrorReport();
}
//////////////////////////////////////////////////////////////////////////
void CLevelInfo::SaveLevelResources([[maybe_unused]] const QString& toPath)
{
}
//////////////////////////////////////////////////////////////////////////
void CLevelInfo::Validate()
{
m_pReport->Clear();
m_pReport->SetImmediateMode(false);
m_pReport->SetShowErrors(true);
int nTotalErrors(0);
int nCurrentError(0);
// Here we are appending the current level load errors to the general errors.
// Actually we are inserting them before all others, but this is not important :-).
IErrorReport* poLastLoadedLevelErrorReport = GetIEditor()->GetLastLoadedLevelErrorReport();
if (poLastLoadedLevelErrorReport)
{
nTotalErrors = poLastLoadedLevelErrorReport->GetErrorCount();
for (nCurrentError = 0; nCurrentError < nTotalErrors; ++nCurrentError)
{
m_pReport->ReportError(poLastLoadedLevelErrorReport->GetError(nCurrentError));
}
}
// Validate level.
ValidateObjects();
if (m_pReport->GetErrorCount() == 0)
{
QMessageBox::information(QApplication::activeWindow(), QString(), QObject::tr("No Errors Found"));
}
else
{
m_pReport->Display();
}
}
//////////////////////////////////////////////////////////////////////////
void CLevelInfo::ValidateObjects()
{
WaitCursor cursor;
// Validate all objects
CBaseObjectsArray objects;
GetIEditor()->GetObjectManager()->GetObjects(objects);
int i;
CLogFile::WriteLine("Validating Objects...");
for (i = 0; i < objects.size(); i++)
{
CBaseObject* pObject = objects[i];
m_pReport->SetCurrentValidatorObject(pObject);
pObject->Validate(m_pReport);
m_pReport->SetCurrentValidatorObject(nullptr);
}
CLogFile::WriteLine("Validating Duplicate Objects...");
//////////////////////////////////////////////////////////////////////////
// Find duplicate objects, Same objects with same transform.
// Use simple grid to speed up the check.
//////////////////////////////////////////////////////////////////////////
int gridSize = 256;
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
float worldSize = terrainAabb.GetXExtent();
float fGridToWorld = worldSize / gridSize;
// Put all objects into parition grid.
std::vector<std::list<CBaseObject*> > grid;
grid.resize(gridSize * gridSize);
// Put objects to grid.
for (i = 0; i < objects.size(); i++)
{
CBaseObject* pObject = objects[i];
Vec3 pos = pObject->GetWorldPos();
int px = ftoi(pos.x / fGridToWorld);
int py = ftoi(pos.y / fGridToWorld);
if (px < 0)
{
px = 0;
}
if (py < 0)
{
py = 0;
}
if (px >= gridSize)
{
px = gridSize - 1;
}
if (py >= gridSize)
{
py = gridSize - 1;
}
grid[py * gridSize + px].push_back(pObject);
}
std::list<CBaseObject*>::iterator it1, it2;
// Check objects in grid.
for (i = 0; i < gridSize * gridSize; i++)
{
std::list<CBaseObject*>::iterator first = grid[i].begin();
std::list<CBaseObject*>::iterator last = grid[i].end();
for (it1 = first; it1 != last; ++it1)
{
for (it2 = first; it2 != it1; ++it2)
{
// Check if same object.
CBaseObject* p1 = *it1;
CBaseObject* p2 = *it2;
if (p1 != p2 && p1->GetClassDesc() == p2->GetClassDesc())
{
// Same class.
Quat q1 = p1->GetRotation();
Quat q2 = p2->GetRotation();
if (p1->GetWorldPos() == p2->GetWorldPos() && q1.w == q2.w && IsEquivalent(q1.v, q2.v, 0) && p1->GetScale() == p2->GetScale())
{
// Same transformation
// Check if objects are really same.
if (p1->IsSimilarObject(p2))
{
// Report duplicate objects.
CErrorRecord err;
err.error = QObject::tr("Found multiple objects in the same location (class %1): %2 and %3")
.arg(p1->GetClassDesc()->ClassName(), p1->GetName(), p2->GetName());
err.pObject = p1;
err.severity = CErrorRecord::ESEVERITY_ERROR;
m_pReport->ReportError(err);
}
}
}
}
}
}
}

@ -1,30 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_LEVELINFO_H
#define CRYINCLUDE_EDITOR_LEVELINFO_H
#pragma once
/*! CLevelInfo provides methods for getting information about current level.
*/
class CLevelInfo
{
public:
CLevelInfo();
void Validate();
void SaveLevelResources(const QString& toPath);
private:
void ValidateObjects();
IErrorReport* m_pReport;
};
#endif // CRYINCLUDE_EDITOR_LEVELINFO_H

@ -36,7 +36,7 @@ namespace CryEditPythonBindingsUnitTests
m_app.Start(appDesc); m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests. // in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
m_app.RegisterComponentDescriptor(AzToolsFramework::CryEditPythonHandler::CreateDescriptor()); m_app.RegisterComponentDescriptor(AzToolsFramework::CryEditPythonHandler::CreateDescriptor());
@ -74,7 +74,6 @@ namespace CryEditPythonBindingsUnitTests
EXPECT_TRUE(behaviorContext->m_methods.find("idle_is_enabled") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("idle_is_enabled") != behaviorContext->m_methods.end());
EXPECT_TRUE(behaviorContext->m_methods.find("idle_wait") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("idle_wait") != behaviorContext->m_methods.end());
EXPECT_TRUE(behaviorContext->m_methods.find("idle_wait_frames") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("idle_wait_frames") != behaviorContext->m_methods.end());
EXPECT_TRUE(behaviorContext->m_methods.find("start_process_detached") != behaviorContext->m_methods.end());
EXPECT_TRUE(behaviorContext->m_methods.find("launch_lua_editor") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("launch_lua_editor") != behaviorContext->m_methods.end());
} }

@ -1011,9 +1011,6 @@ void MainWindow::InitActions()
am->AddAction(ID_TOOLS_ENABLEFILECHANGEMONITORING, tr("Enable File Change Monitoring")); am->AddAction(ID_TOOLS_ENABLEFILECHANGEMONITORING, tr("Enable File Change Monitoring"));
am->AddAction(ID_CLEAR_REGISTRY, tr("Clear Registry Data")) am->AddAction(ID_CLEAR_REGISTRY, tr("Clear Registry Data"))
.SetStatusTip(tr("Clear Registry Data")); .SetStatusTip(tr("Clear Registry Data"));
am->AddAction(ID_VALIDATELEVEL, tr("&Check Level for Errors"))
.SetStatusTip(tr("Validate Level"));
am->AddAction(ID_TOOLS_VALIDATEOBJECTPOSITIONS, tr("Check Object Positions"));
QAction* saveLevelStatsAction = QAction* saveLevelStatsAction =
am->AddAction(ID_TOOLS_LOGMEMORYUSAGE, tr("Save Level Statistics")) am->AddAction(ID_TOOLS_LOGMEMORYUSAGE, tr("Save Level Statistics"))
.SetStatusTip(tr("Logs Editor memory usage.")); .SetStatusTip(tr("Logs Editor memory usage."));

@ -0,0 +1,55 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ContextMenuHandlers.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <QAction>
#include <QMenu>
void ContextMenuBottomHandler::Setup()
{
AzToolsFramework::EditorContextMenuBus::Handler::BusConnect();
}
void ContextMenuBottomHandler::Teardown()
{
AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect();
}
int ContextMenuBottomHandler::GetMenuPosition() const
{
return aznumeric_cast<int>(AzToolsFramework::EditorContextMenuOrdering::BOTTOM);
}
void ContextMenuBottomHandler::PopulateEditorGlobalContextMenu(
QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
{
AzToolsFramework::EntityIdList selected;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selected, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
QAction* action = nullptr;
if (selected.size() > 0)
{
action = menu->addAction(QObject::tr("Open pinned Inspector"));
QObject::connect(
action, &QAction::triggered, action,
[selected]
{
AzToolsFramework::EntityIdSet pinnedEntities(selected.begin(), selected.end());
AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequests::OpenPinnedInspector, pinnedEntities);
}
);
menu->addSeparator();
}
}

@ -0,0 +1,23 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
class ContextMenuBottomHandler : private AzToolsFramework::EditorContextMenuBus::Handler
{
public:
void Setup();
void Teardown();
private:
// EditorContextMenu overrides ...
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
int GetMenuPosition() const override;
};

@ -45,17 +45,17 @@
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h> #include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/Slice/SliceRequestBus.h> #include <AzToolsFramework/Slice/SliceRequestBus.h>
#include <AzToolsFramework/Slice/SliceUtilities.h> #include <AzToolsFramework/Slice/SliceUtilities.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponent.h> #include <AzToolsFramework/ToolsComponents/EditorLayerComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h> #include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include <AzToolsFramework/Undo/UndoSystem.h> #include <AzToolsFramework/Undo/UndoSystem.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h> #include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Layer/AddToLayerMenu.h> #include <AzToolsFramework/UI/Layer/AddToLayerMenu.h>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h> #include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h> #include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h> #include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx> #include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/ViewportSelection/EditorHelpers.h> #include <AzToolsFramework/ViewportSelection/EditorHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h> #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <MathConversion.h> #include <MathConversion.h>
@ -212,6 +212,8 @@ void SandboxIntegrationManager::Setup()
AZ_Assert(m_readOnlyEntityPublicInterface, "SandboxIntegrationManager requires an ReadOnlyEntityPublicInterface instance to be present on Setup()."); AZ_Assert(m_readOnlyEntityPublicInterface, "SandboxIntegrationManager requires an ReadOnlyEntityPublicInterface instance to be present on Setup().");
AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusConnect(); AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusConnect();
m_contextMenuBottomHandler.Setup();
} }
void SandboxIntegrationManager::SaveSlice(const bool& QuickPushToFirstLevel) void SandboxIntegrationManager::SaveSlice(const bool& QuickPushToFirstLevel)
@ -395,6 +397,8 @@ void SandboxIntegrationManager::GetEntitiesInSlices(
void SandboxIntegrationManager::Teardown() void SandboxIntegrationManager::Teardown()
{ {
m_contextMenuBottomHandler.Teardown();
AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusDisconnect(); AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusDisconnect();
AzFramework::DisplayContextRequestBus::Handler::BusDisconnect(); AzFramework::DisplayContextRequestBus::Handler::BusDisconnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect(); AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
@ -651,12 +655,14 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
if (selected.size() == 0) if (selected.size() == 0)
{ {
action = menu->addAction(QObject::tr("Create entity")); action = menu->addAction(QObject::tr("Create entity"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_N));
QObject::connect( QObject::connect(
action, &QAction::triggered, action, action, &QAction::triggered, action,
[this] [this]
{ {
ContextMenu_NewEntity(); ContextMenu_NewEntity();
}); }
);
} }
// when a single entity is selected, entity is created as its child // when a single entity is selected, entity is created as its child
else if (selected.size() == 1) else if (selected.size() == 1)
@ -667,6 +673,7 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selectedEntityId) && !selectedEntityIsReadOnly)) if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selectedEntityId) && !selectedEntityIsReadOnly))
{ {
action = menu->addAction(QObject::tr("Create entity")); action = menu->addAction(QObject::tr("Create entity"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_N));
QObject::connect( QObject::connect(
action, &QAction::triggered, action, action, &QAction::triggered, action,
[selectedEntityId] [selectedEntityId]
@ -694,33 +701,30 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
AzToolsFramework::SetupAddToLayerMenu(menu, flattenedSelection, [this] { return ContextMenu_NewLayer(); }); AzToolsFramework::SetupAddToLayerMenu(menu, flattenedSelection, [this] { return ContextMenu_NewLayer(); });
SetupSliceContextMenu(menu); SetupSliceContextMenu(menu);
}
if (!selected.empty()) if (!selected.empty())
{
// Don't allow duplication if any of the selected entities are direct desendants of a read-only entity
bool selectionContainsDescendantOfReadOnlyEntity = false;
for (const auto& entityId : selected)
{ {
AZ::EntityId parentEntityId; // Don't allow duplication if any of the selected entities are direct descendants of a read-only entity
AZ::TransformBus::EventResult(parentEntityId, entityId, &AZ::TransformBus::Events::GetParentId); bool selectionContainsDescendantOfReadOnlyEntity = false;
for (const auto& entityId : selected)
if (parentEntityId.IsValid() && m_readOnlyEntityPublicInterface->IsReadOnly(parentEntityId))
{ {
selectionContainsDescendantOfReadOnlyEntity = true; AZ::EntityId parentEntityId;
break; AZ::TransformBus::EventResult(parentEntityId, entityId, &AZ::TransformBus::Events::GetParentId);
if (parentEntityId.IsValid() && m_readOnlyEntityPublicInterface->IsReadOnly(parentEntityId))
{
selectionContainsDescendantOfReadOnlyEntity = true;
break;
}
} }
}
if (!selectionContainsDescendantOfReadOnlyEntity) if (!selectionContainsDescendantOfReadOnlyEntity)
{ {
action = menu->addAction(QObject::tr("Duplicate")); action = menu->addAction(QObject::tr("Duplicate"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); }); QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); });
}
} }
}
if (!prefabSystemEnabled)
{
action = menu->addAction(QObject::tr("Delete")); action = menu->addAction(QObject::tr("Delete"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_DeleteSelected(); }); QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_DeleteSelected(); });
if (selected.size() == 0) if (selected.size() == 0)
@ -733,20 +737,14 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
if (selected.size() > 0) if (selected.size() > 0)
{ {
action = menu->addAction(QObject::tr("Open pinned Inspector")); action = menu->addAction(QObject::tr("Find in Entity Outliner"));
QObject::connect(action, &QAction::triggered, action, [this, selected] QObject::connect(
{ action, &QAction::triggered,
AzToolsFramework::EntityIdSet pinnedEntities(selected.begin(), selected.end()); [selected]
OpenPinnedInspector(pinnedEntities);
});
if (selected.size() > 0)
{
action = menu->addAction(QObject::tr("Find in Entity Outliner"));
QObject::connect(action, &QAction::triggered, [selected]
{ {
AzToolsFramework::EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnFocusInEntityOutliner, selected); AzToolsFramework::EditorEntityContextNotificationBus::Broadcast(
&EditorEntityContextNotification::OnFocusInEntityOutliner, selected);
}); });
}
menu->addSeparator(); menu->addSeparator();
} }
} }

@ -9,6 +9,8 @@
#ifndef CRYINCLUDE_COMPONENTENTITYEDITORPLUGIN_SANDBOXINTEGRATION_H #ifndef CRYINCLUDE_COMPONENTENTITYEDITORPLUGIN_SANDBOXINTEGRATION_H
#define CRYINCLUDE_COMPONENTENTITYEDITORPLUGIN_SANDBOXINTEGRATION_H #define CRYINCLUDE_COMPONENTENTITYEDITORPLUGIN_SANDBOXINTEGRATION_H
#include "ContextMenuHandlers.h"
#include <AzCore/Component/ComponentApplicationBus.h> #include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Slice/SliceBus.h> #include <AzCore/Slice/SliceBus.h>
#include <AzCore/Slice/SliceComponent.h> #include <AzCore/Slice/SliceComponent.h>
@ -273,6 +275,8 @@ private:
}; };
private: private:
ContextMenuBottomHandler m_contextMenuBottomHandler;
AZ::Vector2 m_contextMenuViewPoint; AZ::Vector2 m_contextMenuViewPoint;
short m_startedUndoRecordingNestingLevel; // used in OnBegin/EndUndo to ensure we only accept undo's we started recording short m_startedUndoRecordingNestingLevel; // used in OnBegin/EndUndo to ensure we only accept undo's we started recording

@ -10,6 +10,8 @@ set(FILES
dllmain.cpp dllmain.cpp
ComponentEntityEditorPlugin.h ComponentEntityEditorPlugin.h
ComponentEntityEditorPlugin.cpp ComponentEntityEditorPlugin.cpp
ContextMenuHandlers.h
ContextMenuHandlers.cpp
SandboxIntegration.h SandboxIntegration.h
SandboxIntegration.cpp SandboxIntegration.cpp
UI/QComponentEntityEditorMainWindow.h UI/QComponentEntityEditorMainWindow.h

@ -86,7 +86,6 @@
#define ID_PHYSICS_RESETPHYSICSSTATE 32938 #define ID_PHYSICS_RESETPHYSICSSTATE 32938
#define ID_GAME_SYNCPLAYER 32941 #define ID_GAME_SYNCPLAYER 32941
#define ID_FILE_SAVELEVELRESOURCES 32942 #define ID_FILE_SAVELEVELRESOURCES 32942
#define ID_VALIDATELEVEL 32943
#define ID_TERRAIN_RESIZE 32944 #define ID_TERRAIN_RESIZE 32944
#define ID_TERRAIN_COLLISION 32960 #define ID_TERRAIN_COLLISION 32960
#define ID_TOOL_FIRST 32972 #define ID_TOOL_FIRST 32972
@ -236,7 +235,6 @@
#define ID_SNAP_TO_GRID_RANGE_END 34106 #define ID_SNAP_TO_GRID_RANGE_END 34106
#define ID_TOOLS_EXPORT_SHORTCUTS 34138 #define ID_TOOLS_EXPORT_SHORTCUTS 34138
#define ID_TOOLS_IMPORT_SHORTCUTS 34139 #define ID_TOOLS_IMPORT_SHORTCUTS 34139
#define ID_TOOLS_VALIDATEOBJECTPOSITIONS 34143
#define ID_TOOLS_BATCH_RENDER 34151 #define ID_TOOLS_BATCH_RENDER 34151
#define ID_TOOLS_SCRIPTHELP 34152 #define ID_TOOLS_SCRIPTHELP 34152
#define ID_TV_MODE_OPENCURVEEDITOR 34153 #define ID_TV_MODE_OPENCURVEEDITOR 34153

@ -532,8 +532,6 @@ set(FILES
ErrorReport.h ErrorReport.h
IconManager.cpp IconManager.cpp
IconManager.h IconManager.h
LevelInfo.cpp
LevelInfo.h
ProcessInfo.cpp ProcessInfo.cpp
ProcessInfo.h ProcessInfo.h
TrackView/AtomOutputFrameCapture.cpp TrackView/AtomOutputFrameCapture.cpp

@ -9,6 +9,7 @@
#pragma once #pragma once
#include <AzCore/base.h> #include <AzCore/base.h>
#include <AzCore/std/limits.h>
#include <AzCore/std/math.h> #include <AzCore/std/math.h>
#include <AzCore/std/typetraits/conditional.h> #include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_integral.h> #include <AzCore/std/typetraits/is_integral.h>
@ -20,6 +21,7 @@
#include <limits> #include <limits>
#include <math.h> #include <math.h>
#include <utility> #include <utility>
#include <inttypes.h>
// We have a separate inline define for math functions. // We have a separate inline define for math functions.
// The performance of these functions is very sensitive to inlining, and some compilers don't deal well with this. // The performance of these functions is very sensitive to inlining, and some compilers don't deal well with this.
@ -256,13 +258,13 @@ namespace AZ
struct ClampedIntegralLimits struct ClampedIntegralLimits
{ {
//! If SourceType and ClampType are different, returns the greater value of //! If SourceType and ClampType are different, returns the greater value of
//! std::numeric_limits<SourceType>::lowest() and std::numeric_limits<ClampType>::lowest(), //! AZStd::numeric_limits<SourceType>::lowest() and AZStd::numeric_limits<ClampType>::lowest(),
//! otherwise returns std::numeric_limits<SourceType>::lowest(). //! otherwise returns AZStd::numeric_limits<SourceType>::lowest().
static constexpr SourceType Min(); static constexpr SourceType Min();
//! If SourceType and ClampType are different, returns the lesser value of //! If SourceType and ClampType are different, returns the lesser value of
//! std::numeric_limits<SourceType>::max() and std::numeric_limits<ClampType>::max(), //! AZStd::numeric_limits<SourceType>::max() and AZStd::numeric_limits<ClampType>::max(),
//! otherwise returns std::numeric_limits<SourceType>::max(). //! otherwise returns AZStd::numeric_limits<SourceType>::max().
static constexpr SourceType Max(); static constexpr SourceType Max();
//! Safely clamps a value of type ValueType to the [Min(), Max()] range as determined by the //! Safely clamps a value of type ValueType to the [Min(), Max()] range as determined by the
@ -375,12 +377,12 @@ namespace AZ
//! Returns a value t where Lerp(a, b, t) == value (or 0 if a == b). //! Returns a value t where Lerp(a, b, t) == value (or 0 if a == b).
inline float LerpInverse(float a, float b, float value) inline float LerpInverse(float a, float b, float value)
{ {
return IsClose(a, b, std::numeric_limits<float>::epsilon()) ? 0.0f : (value - a) / (b - a); return IsClose(a, b, AZStd::numeric_limits<float>::epsilon()) ? 0.0f : (value - a) / (b - a);
} }
inline double LerpInverse(double a, double b, double value) inline double LerpInverse(double a, double b, double value)
{ {
return IsClose(a, b, std::numeric_limits<double>::epsilon()) ? 0.0 : (value - a) / (b - a); return IsClose(a, b, AZStd::numeric_limits<double>::epsilon()) ? 0.0 : (value - a) / (b - a);
} }
//! Returns true if the number provided is even. //! Returns true if the number provided is even.
@ -431,19 +433,19 @@ namespace AZ
AZ_MATH_INLINE float GetFloatQNaN() AZ_MATH_INLINE float GetFloatQNaN()
{ {
return std::numeric_limits<float>::quiet_NaN(); return AZStd::numeric_limits<float>::quiet_NaN();
} }
//! IsCloseMag(x, y, epsilon) returns true if y and x are sufficiently close, taking magnitude of x and y into account in the epsilon //! IsCloseMag(x, y, epsilon) returns true if y and x are sufficiently close, taking magnitude of x and y into account in the epsilon
template<typename T> template<typename T>
AZ_MATH_INLINE bool IsCloseMag(T x, T y, T epsilonValue = std::numeric_limits<T>::epsilon()) AZ_MATH_INLINE bool IsCloseMag(T x, T y, T epsilonValue = AZStd::numeric_limits<T>::epsilon())
{ {
return (AZStd::abs(x - y) <= epsilonValue * GetMax<T>(GetMax<T>(T(1.0), AZStd::abs(x)), AZStd::abs(y))); return (AZStd::abs(x - y) <= epsilonValue * GetMax<T>(GetMax<T>(T(1.0), AZStd::abs(x)), AZStd::abs(y)));
} }
//! ClampIfCloseMag(x, y, epsilon) returns y when x and y are within epsilon of each other (taking magnitude into account). Otherwise returns x. //! ClampIfCloseMag(x, y, epsilon) returns y when x and y are within epsilon of each other (taking magnitude into account). Otherwise returns x.
template<typename T> template<typename T>
AZ_MATH_INLINE T ClampIfCloseMag(T x, T y, T epsilonValue = std::numeric_limits<T>::epsilon()) AZ_MATH_INLINE T ClampIfCloseMag(T x, T y, T epsilonValue = AZStd::numeric_limits<T>::epsilon())
{ {
return IsCloseMag<T>(x, y, epsilonValue) ? y : x; return IsCloseMag<T>(x, y, epsilonValue) ? y : x;
} }
@ -461,6 +463,44 @@ namespace AZ
return (azisfinite(x) != 0); return (azisfinite(x) != 0);
} }
//! Returns the value divided by alignment, where the result is rounded up if the remainder is non-zero.
//! Example: alignment: 4
//! Value: 0 1 2 3 4 5 6 7 8
//! Result: 0 1 1 1 1 2 2 2 2
constexpr uint32_t DivideAndRoundUp(uint32_t value, uint32_t alignment)
{
AZ_Assert(alignment != 0, "0 is an invalid multiple to round to.");
AZ_Assert(
AZStd::numeric_limits<uint32_t>::max() - value >= alignment,
"value '%" PRIu32 "' and alignment '%" PRIu32 "' will overflow when added together during DivideAndRoundUp.", value, alignment);
return (value + alignment - 1) / alignment;
}
constexpr uint64_t DivideAndRoundUp(uint64_t value, uint64_t alignment)
{
AZ_Assert(alignment != 0, "0 is an invalid multiple to round to.");
AZ_Assert(
AZStd::numeric_limits<uint64_t>::max() - value >= alignment,
"value '%" PRIu64 "' and alignment '%" PRIu64 "' will overflow when added together during DivideAndRoundUp.", value, alignment);
return (value + alignment - 1) / alignment;
}
//! Returns the value rounded up to a multiple of alignment.
//! This function will work for non power of two alignments.
//! If your alignment is guaranteed to be a power of two, SizeAlignUp in base.h is a more efficient implementation.
//! Example: roundTo: 4
//! Value: 0 1 2 3 4 5 6 7 8
//! Result: 0 4 4 4 4 8 8 8 8
constexpr uint32_t RoundUpToMultiple(uint32_t value, uint32_t alignment)
{
return DivideAndRoundUp(value, alignment) * alignment;
}
constexpr uint64_t RoundUpToMultiple(uint64_t value, uint64_t alignment)
{
return DivideAndRoundUp(value, alignment) * alignment;
}
//! Returns the maximum value for SourceType as constrained by the numerical range of ClampType. //! Returns the maximum value for SourceType as constrained by the numerical range of ClampType.
template <typename SourceType, typename ClampType> template <typename SourceType, typename ClampType>
constexpr SourceType ClampedIntegralLimits<SourceType, ClampType>::Min() constexpr SourceType ClampedIntegralLimits<SourceType, ClampType>::Min()
@ -474,8 +514,8 @@ namespace AZ
{ {
// Both SourceType and ClampType are signed, take the greater of the lower limits of each type // Both SourceType and ClampType are signed, take the greater of the lower limits of each type
return sizeof(SourceType) < sizeof(ClampType) ? return sizeof(SourceType) < sizeof(ClampType) ?
(std::numeric_limits<SourceType>::lowest)() : (AZStd::numeric_limits<SourceType>::lowest)() :
static_cast<SourceType>((std::numeric_limits<ClampType>::lowest)()); static_cast<SourceType>((AZStd::numeric_limits<ClampType>::lowest)());
} }
} }
@ -486,12 +526,12 @@ namespace AZ
if constexpr (sizeof(SourceType) < sizeof(ClampType)) if constexpr (sizeof(SourceType) < sizeof(ClampType))
{ {
// If SourceType is narrower than ClampType, the upper limit will be SourceType's // If SourceType is narrower than ClampType, the upper limit will be SourceType's
return (std::numeric_limits<SourceType>::max)(); return (AZStd::numeric_limits<SourceType>::max)();
} }
else if constexpr (sizeof(SourceType) > sizeof(ClampType)) else if constexpr (sizeof(SourceType) > sizeof(ClampType))
{ {
// If SourceType is wider than ClampType, the upper limit will be ClampType's // If SourceType is wider than ClampType, the upper limit will be ClampType's
return static_cast<SourceType>((std::numeric_limits<ClampType>::max)()); return static_cast<SourceType>((AZStd::numeric_limits<ClampType>::max)());
} }
else else
{ {
@ -499,13 +539,13 @@ namespace AZ
{ {
// SourceType and ClampType are the same width, ClampType is signed // SourceType and ClampType are the same width, ClampType is signed
// so our upper limit will be ClampType // so our upper limit will be ClampType
return static_cast<SourceType>((std::numeric_limits<ClampType>::max)()); return static_cast<SourceType>((AZStd::numeric_limits<ClampType>::max)());
} }
else else
{ {
// SourceType and ClampType are the same width, ClampType is unsigned // SourceType and ClampType are the same width, ClampType is unsigned
// then our upper limit will be SourceType // then our upper limit will be SourceType
return (std::numeric_limits<SourceType>::max)(); return (AZStd::numeric_limits<SourceType>::max)();
} }
} }
} }
@ -588,7 +628,7 @@ namespace AZ
// LeftTypeSize <= RightTypeSize // LeftTypeSize <= RightTypeSize
// LeftType is signed // LeftType is signed
// RightType is unsigned // RightType is unsigned
RightType max = static_cast<RightType>((std::numeric_limits<LeftType>::max)()); RightType max = static_cast<RightType>((AZStd::numeric_limits<LeftType>::max)());
if (rhs > max) if (rhs > max)
{ {
@ -604,7 +644,7 @@ namespace AZ
// LeftType < RightType // LeftType < RightType
// LeftType is unsigned // LeftType is unsigned
// RightType is signed // RightType is signed
RightType max = static_cast<RightType>((std::numeric_limits<LeftType>::max)()); RightType max = static_cast<RightType>((AZStd::numeric_limits<LeftType>::max)());
if (rhs < 0) if (rhs < 0)
{ {

@ -85,6 +85,16 @@ namespace AZ::Internal
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
{ {
m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} }); m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} });
// Make sure any engine paths read from the manifest are absolute
AZ::IO::FixedMaxPath& recentEnginePath = m_enginePaths.back().m_path;
if (recentEnginePath.IsRelative())
{
if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(recentEnginePath.Native());
engineRootAbsPath.has_value())
{
recentEnginePath = AZStd::move(*engineRootAbsPath);
}
}
} }
AZStd::vector<EngineInfo> m_enginePaths{}; AZStd::vector<EngineInfo> m_enginePaths{};
@ -209,8 +219,15 @@ namespace AZ::Internal
return {}; return {};
} }
void InjectSettingToCommandLineBack(AZ::SettingsRegistryInterface& settingsRegistry, enum class InjectLocation : bool
AZStd::string_view path, AZStd::string_view value) {
Front,
Back
};
void InjectSettingToCommandLine(AZ::SettingsRegistryInterface& settingsRegistry,
AZStd::string_view path, AZStd::string_view value,
InjectLocation injectLocation = InjectLocation::Front)
{ {
AZ::CommandLine commandLine; AZ::CommandLine commandLine;
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine); AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine);
@ -219,7 +236,8 @@ namespace AZ::Internal
auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")", auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")",
aznumeric_cast<int>(path.size()), path.data(), aznumeric_cast<int>(value.size()), value.data()); aznumeric_cast<int>(path.size()), path.data(), aznumeric_cast<int>(value.size()), value.data());
paramContainer.emplace(paramContainer.end(), AZStd::move(projectPathOverride)); auto emplaceIter = injectLocation == InjectLocation::Front ? paramContainer.begin() : paramContainer.end();
paramContainer.emplace(emplaceIter, AZStd::move(projectPathOverride));
commandLine.Parse(paramContainer); commandLine.Parse(paramContainer);
AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine); AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine);
} }
@ -244,13 +262,26 @@ namespace AZ::SettingsRegistryMergeUtils
{ {
// We can scan up from exe directory to find engine.json, use that for engine root if it exists. // We can scan up from exe directory to find engine.json, use that for engine root if it exists.
engineRoot = Internal::ScanUpRootLocator("engine.json"); engineRoot = Internal::ScanUpRootLocator("engine.json");
// The Internal ScanUp Engine Root Key will be set as an absolute path
if (!engineRoot.empty())
{
if (engineRoot.IsRelative())
{
if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native());
engineRootAbsPath.has_value())
{
engineRoot = AZStd::move(*engineRootAbsPath);
}
}
}
// Set the {InternalScanUpEngineRootKey} to make sure this code path isn't called again for this settings registry // Set the {InternalScanUpEngineRootKey} to make sure this code path isn't called again for this settings registry
settingsRegistry.Set(InternalScanUpEngineRootKey, engineRoot.Native()); settingsRegistry.Set(InternalScanUpEngineRootKey, engineRoot.Native());
if (!engineRoot.empty()) if (!engineRoot.empty())
{ {
settingsRegistry.Set(engineRootKey, engineRoot.Native()); settingsRegistry.Set(engineRootKey, engineRoot.Native());
// Inject the engine root at the end of the command line settings // Inject the engine root to the front of the command line settings
Internal::InjectSettingToCommandLineBack(settingsRegistry, engineRootKey, engineRoot.Native()); Internal::InjectSettingToCommandLine(settingsRegistry, engineRootKey, engineRoot.Native());
return engineRoot; return engineRoot;
} }
} }
@ -258,6 +289,14 @@ namespace AZ::SettingsRegistryMergeUtils
// Step 2 check if the engine_path key has been supplied // Step 2 check if the engine_path key has been supplied
if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty()) if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty())
{ {
if (engineRoot.IsRelative())
{
if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native());
engineRootAbsPath.has_value())
{
engineRoot = AZStd::move(*engineRootAbsPath);
}
}
return engineRoot; return engineRoot;
} }
@ -298,13 +337,28 @@ namespace AZ::SettingsRegistryMergeUtils
if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType) if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType)
{ {
projectRoot = Internal::ScanUpRootLocator("project.json"); projectRoot = Internal::ScanUpRootLocator("project.json");
// Convert the path to an absolute path before adding it as a setting to the
// InternalScanUpProjectRootKey
if (!projectRoot.empty())
{
if (projectRoot.IsRelative())
{
if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectRoot.Native());
projectAbsPath.has_value())
{
projectRoot = AZStd::move(*projectAbsPath);
}
}
}
// Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry // Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry
settingsRegistry.Set(InternalScanUpProjectRootKey, projectRoot.Native()); settingsRegistry.Set(InternalScanUpProjectRootKey, projectRoot.Native());
if (!projectRoot.empty()) if (!projectRoot.empty())
{ {
settingsRegistry.Set(projectRootKey, projectRoot.c_str()); settingsRegistry.Set(projectRootKey, projectRoot.c_str());
// Inject the project root at the end of the command line settings // Inject the project root at to the front of the command line settings
Internal::InjectSettingToCommandLineBack(settingsRegistry, projectRootKey, projectRoot.Native()); Internal::InjectSettingToCommandLine(settingsRegistry, projectRootKey, projectRoot.Native());
return projectRoot; return projectRoot;
} }
} }
@ -312,6 +366,18 @@ namespace AZ::SettingsRegistryMergeUtils
// Step 2 Check the project-path key // Step 2 Check the project-path key
// This is the project path root key, as passed from command-line or *.setreg files. // This is the project path root key, as passed from command-line or *.setreg files.
settingsRegistry.Get(projectRoot.Native(), projectRootKey); settingsRegistry.Get(projectRoot.Native(), projectRootKey);
if (!projectRoot.empty())
{
if (projectRoot.IsRelative())
{
if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectRoot.Native());
projectAbsPath.has_value())
{
projectRoot = AZStd::move(*projectAbsPath);
}
}
}
return projectRoot; return projectRoot;
} }
@ -325,13 +391,22 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr auto projectCachePathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_cache_path"; constexpr auto projectCachePathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_cache_path";
// Step 1 Check the project-cache-path key // Step 1 Check the project-cache-path key
if (AZ::IO::FixedMaxPath projectCachePath; settingsRegistry.Get(projectCachePath.Native(), projectCachePathKey)) AZ::IO::FixedMaxPath projectCachePath;
if (!settingsRegistry.Get(projectCachePath.Native(), projectCachePathKey))
{ {
return projectCachePath; // Step 2 Append the "Cache" directory to the project-path
projectCachePath = projectPath / Internal::ProductCacheDirectoryName;
} }
// Step 2 Append the "Cache" directory to the project-path if (projectCachePath.IsRelative())
return projectPath / Internal::ProductCacheDirectoryName; {
if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native());
projectCacheAbsPath.has_value())
{
projectCachePath = AZStd::move(*projectCacheAbsPath);
}
}
return projectCachePath;
} }
//! Set the user directory with the provided path or using <project-path>/user as default //! Set the user directory with the provided path or using <project-path>/user as default
@ -344,13 +419,22 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr auto projectUserPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_user_path"; constexpr auto projectUserPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_user_path";
// Step 1 Check the project-user-path key // Step 1 Check the project-user-path key
if (AZ::IO::FixedMaxPath projectUserPath; settingsRegistry.Get(projectUserPath.Native(), projectUserPathKey)) AZ::IO::FixedMaxPath projectUserPath;
if (!settingsRegistry.Get(projectUserPath.Native(), projectUserPathKey))
{ {
return projectUserPath; // Step 2 Append the "User" directory to the project-path
projectUserPath = projectPath / "user";
} }
// Step 2 Append the "User" directory to the project-path if (projectUserPath.IsRelative())
return projectPath / "user"; {
if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native());
projectUserAbsPath.has_value())
{
projectUserPath = AZStd::move(*projectUserAbsPath);
}
}
return projectUserPath;
} }
//! Set the log directory using the settings registry path or using <project-user-path>/log as default //! Set the log directory using the settings registry path or using <project-user-path>/log as default
@ -363,20 +447,37 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr auto projectLogPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_log_path"; constexpr auto projectLogPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_log_path";
// Step 1 Check the project-user-path key // Step 1 Check the project-user-path key
if (AZ::IO::FixedMaxPath projectLogPath; settingsRegistry.Get(projectLogPath.Native(), projectLogPathKey)) AZ::IO::FixedMaxPath projectLogPath;
if (!settingsRegistry.Get(projectLogPath.Native(), projectLogPathKey))
{
// Step 2 Append the "Log" directory to the project-user-path
projectLogPath = projectUserPath / "log";
}
if (projectLogPath.IsRelative())
{ {
return projectLogPath; if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native()))
{
projectLogPath = AZStd::move(*projectLogAbsPath);
}
} }
// Step 2 Append the "Log" directory to the project-user-path return projectLogPath;
return projectUserPath / "log";
} }
// check for a default write storage path, fall back to the <project-user-path> if not // check for a default write storage path, fall back to the <project-user-path> if not
static AZ::IO::FixedMaxPath FindDevWriteStoragePath(const AZ::IO::FixedMaxPath& projectUserPath) static AZ::IO::FixedMaxPath FindDevWriteStoragePath(const AZ::IO::FixedMaxPath& projectUserPath)
{ {
AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath(); AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath();
return devWriteStorage.has_value() ? *devWriteStorage : projectUserPath; AZ::IO::FixedMaxPath devWriteStoragePath = devWriteStorage.has_value() ? *devWriteStorage : projectUserPath;
if (devWriteStoragePath.IsRelative())
{
if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native()))
{
devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath);
}
}
return devWriteStoragePath;
} }
// check for the project build path, which is a relative path from the project root // check for the project build path, which is a relative path from the project root
@ -669,15 +770,6 @@ namespace AZ::SettingsRegistryMergeUtils
if ([[maybe_unused]] constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path"; if ([[maybe_unused]] constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
!projectPath.empty()) !projectPath.empty())
{ {
if (projectPath.IsRelative())
{
if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectPath.Native());
projectAbsPath.has_value())
{
projectPath = AZStd::move(*projectAbsPath);
}
}
projectPath = projectPath.LexicallyNormal(); projectPath = projectPath.LexicallyNormal();
AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(projectPath.c_str()), AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(projectPath.c_str()),
R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to a valid absolute path?)" R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to a valid absolute path?)"
@ -699,15 +791,6 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry); AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
if (!engineRoot.empty()) if (!engineRoot.empty())
{ {
if (engineRoot.IsRelative())
{
if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native());
engineRootAbsPath.has_value())
{
engineRoot = AZStd::move(*engineRootAbsPath);
}
}
engineRoot = engineRoot.LexicallyNormal(); engineRoot = engineRoot.LexicallyNormal();
registry.Set(FilePathKey_EngineRootFolder, engineRoot.Native()); registry.Set(FilePathKey_EngineRootFolder, engineRoot.Native());
} }
@ -716,15 +799,6 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal(); AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal();
if (!projectCachePath.empty()) if (!projectCachePath.empty())
{ {
if (projectCachePath.IsRelative())
{
if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native());
projectCacheAbsPath.has_value())
{
projectCachePath = AZStd::move(*projectCacheAbsPath);
}
}
projectCachePath = projectCachePath.LexicallyNormal(); projectCachePath = projectCachePath.LexicallyNormal();
registry.Set(FilePathKey_CacheProjectRootFolder, projectCachePath.Native()); registry.Set(FilePathKey_CacheProjectRootFolder, projectCachePath.Native());
@ -755,15 +829,6 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath); AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath);
if (!projectUserPath.empty()) if (!projectUserPath.empty())
{ {
if (projectUserPath.IsRelative())
{
if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native());
projectUserAbsPath.has_value())
{
projectUserPath = AZStd::move(*projectUserAbsPath);
}
}
projectUserPath = projectUserPath.LexicallyNormal(); projectUserPath = projectUserPath.LexicallyNormal();
registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native()); registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
} }
@ -771,14 +836,6 @@ namespace AZ::SettingsRegistryMergeUtils
// Log folder // Log folder
if (AZ::IO::FixedMaxPath projectLogPath = FindProjectLogPath(registry, projectUserPath); !projectLogPath.empty()) if (AZ::IO::FixedMaxPath projectLogPath = FindProjectLogPath(registry, projectUserPath); !projectLogPath.empty())
{ {
if (projectLogPath.IsRelative())
{
if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native()))
{
projectLogPath = AZStd::move(*projectLogAbsPath);
}
}
projectLogPath = projectLogPath.LexicallyNormal(); projectLogPath = projectLogPath.LexicallyNormal();
registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native()); registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native());
} }
@ -786,14 +843,6 @@ namespace AZ::SettingsRegistryMergeUtils
// Developer Write Storage folder // Developer Write Storage folder
if (AZ::IO::FixedMaxPath devWriteStoragePath = FindDevWriteStoragePath(projectUserPath); !devWriteStoragePath.empty()) if (AZ::IO::FixedMaxPath devWriteStoragePath = FindDevWriteStoragePath(projectUserPath); !devWriteStoragePath.empty())
{ {
if (devWriteStoragePath.IsRelative())
{
if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native()))
{
devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath);
}
}
devWriteStoragePath = devWriteStoragePath.LexicallyNormal(); devWriteStoragePath = devWriteStoragePath.LexicallyNormal();
registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native()); registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native());
} }

@ -89,15 +89,14 @@ namespace AZ::SettingsRegistryMergeUtils
//! The algorithm that is used to find the project root is as follows //! The algorithm that is used to find the project root is as follows
//! 1. The first time this function runs it performs an upward scan for a "project.json" file from //! 1. The first time this function runs it performs an upward scan for a "project.json" file from
//! the executable directory and stores that path into an internal key. //! the executable directory and stores that path into an internal key.
//! In the same step it injects the path into the back of the command line parameters //! In the same step it injects the path into the front of the command line parameters
//! using the --regset="{BootstrapSettingsRootKey}/project_path=<path>" value //! using the --regset="{BootstrapSettingsRootKey}/project_path=<path>" value
//! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set //! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set
//! //!
//! The order in which the project path settings are overridden proceeds in the following order //! The order in which the project path settings are overridden proceeds in the following order
//! 1. project_path set in the <engine-root>/bootstrap.cfg file //! 1. project_path set in a *.setreg/*.setregpatch file
//! 2. project_path set in a *.setreg/*.setregpatch file //! 2. project_path found by scanning upwards from the executable directory to the project.json path
//! 3. project_path found by scanning upwards from the executable directory to the project.json path //! 3. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=<path>"
//! 4. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=<path>"
//! or --project_path=<path> //! or --project_path=<path>
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry); AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry);
@ -213,9 +212,15 @@ namespace AZ::SettingsRegistryMergeUtils
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer = nullptr); const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer = nullptr);
//! Adds the settings set through the command line to the Settings Registry. This will also execute any Settings //! Adds the settings set through the command line to the Settings Registry. This will also execute any Settings
//! Registry related arguments. Note that --regset and -regremove will run in the order in which they are parsed //! Registry related arguments. Note that --regset, --regset-file and -regremove will run in the order in which they are parsed
//! --regset <arg> Sets a value in the registry. See MergeCommandLineArgument for options for <arg> //! --regset <arg> Sets a value in the registry. See MergeCommandLineArgument for options for <arg>
//! example: --regset "/My/String/Value=String value set" //! example: --regset "/My/String/Value=String value set"
//! --regset-file <path>[::anchor] Merges the specified file into the Settings registry
//! If the extension is .setregpatch, then JSON Patch will be used to merge the file otherwise JSON Merge Patch will be used
//! `anchor` is a JSON path used to optionally select where to merge the settings underneath, otherwise settings are merged
//! under the root.
//! example: --regset-file="C:/Users/testuser/custom.setreg"
//! example: --regset-file="relative/path/other.setregpatch::/O3DE/settings"
//! --regremove <arg> Removes a value in the registry //! --regremove <arg> Removes a value in the registry
//! example: --regremove "/My/String/Value" //! example: --regremove "/My/String/Value"
//! only when executeCommands is true are the following options supported: //! only when executeCommands is true are the following options supported:

@ -416,27 +416,27 @@ namespace AZ
Join(fixedOutput, example.begin(), example.end(), ","); Join(fixedOutput, example.begin(), example.end(), ",");
// fixedOutput == "test,string,joining" // fixedOutput == "test,string,joining"
*/ */
template<typename TStringType, typename TConvertableToStringViewIterator, typename TSeparatorString> template<typename StringType, typename ConvertableToStringViewIterator, typename SeparatorString>
inline void Join( inline void Join(
TStringType& joinTarget, StringType& joinTarget,
const TConvertableToStringViewIterator& iteratorBegin, const ConvertableToStringViewIterator& iteratorBegin,
const TConvertableToStringViewIterator& iteratorEnd, const ConvertableToStringViewIterator& iteratorEnd,
const TSeparatorString& separator) const SeparatorString& separator)
{ {
if (iteratorBegin == iteratorEnd) if (iteratorBegin == iteratorEnd)
{ {
return; return;
} }
using CharType = typename TStringType::value_type; using CharType = typename StringType::value_type;
using CharTraitsType = typename TStringType::traits_type; using CharTraitsType = typename StringType::traits_type;
size_t size = joinTarget.size() + AZStd::basic_string_view<CharType, CharTraitsType>(*iteratorBegin).size(); size_t size = joinTarget.size() + AZStd::basic_string_view<CharType, CharTraitsType>(*iteratorBegin).size();
for (auto currentIterator = AZStd::next(iteratorBegin); currentIterator != iteratorEnd; ++currentIterator) for (auto currentIterator = AZStd::next(iteratorBegin); currentIterator != iteratorEnd; ++currentIterator)
{ {
size += AZStd::basic_string_view<CharType, CharTraitsType>(*currentIterator).size(); size += AZStd::basic_string_view<CharType, CharTraitsType>(*currentIterator).size();
// Special case for when the separator is just the character type // Special case for when the separator is just the character type
if constexpr (AZStd::is_same_v<AZStd::remove_cvref_t<TSeparatorString>, CharType>) if constexpr (AZStd::is_same_v<AZStd::remove_cvref_t<SeparatorString>, CharType>)
{ {
size += 1; size += 1;
} }
@ -455,6 +455,19 @@ namespace AZ
} }
} }
template<typename StringType, typename Range, typename SeparatorString,
class = AZStd::enable_if_t<AZStd::ranges::input_range<Range> &&
AZStd::convertible_to<AZStd::ranges::range_value_t<Range>,
AZStd::basic_string_view<typename StringType::value_type, typename StringType::traits_type>>
>>
void Join(StringType& joinTarget, Range&& stringViewConvertibleRange, const SeparatorString& separator)
{
Join(joinTarget,
AZStd::ranges::begin(stringViewConvertibleRange),
AZStd::ranges::end(stringViewConvertibleRange),
separator);
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//! StringFunc::NumberFormatting Namespace //! StringFunc::NumberFormatting Namespace
/*! For string functions supporting string representations of numbers /*! For string functions supporting string representations of numbers

@ -302,7 +302,13 @@ namespace AZStd
inline AZStd::string to_string(long long val) { AZStd::string str; to_string(str, val); return str; } inline AZStd::string to_string(long long val) { AZStd::string str; to_string(str, val); return str; }
inline AZStd::string to_string(unsigned long long val) { AZStd::string str; to_string(str, val); return str; } inline AZStd::string to_string(unsigned long long val) { AZStd::string str; to_string(str, val); return str; }
inline AZStd::string to_string(long double val) { AZStd::string str; to_string(str, val); return str; } inline AZStd::string to_string(long double val) { AZStd::string str; to_string(str, val); return str; }
inline AZStd::string to_string(bool val) { AZStd::string str; to_string(str, val); return str; } template<class BoolType>
auto to_string(BoolType value) -> enable_if_t<same_as<remove_cvref_t<BoolType>, bool>, AZStd::string>
{
AZStd::string str;
to_string(str, value);
return str;
}
// In our engine we assume AZStd::string is Utf8 encoded! // In our engine we assume AZStd::string is Utf8 encoded!
template<class Allocator> template<class Allocator>

@ -656,18 +656,6 @@ namespace UnitTest
fval = AZStd::stof(wfloatStr); fval = AZStd::stof(wfloatStr);
AZ_TEST_ASSERT_FLOAT_CLOSE(fval, 2.32f); AZ_TEST_ASSERT_FLOAT_CLOSE(fval, 2.32f);
AZStd::to_string(intStr, 20);
AZ_TEST_ASSERT(intStr == "20");
EXPECT_EQ("20", AZStd::to_string(static_cast<int16_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<uint16_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<int32_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<uint32_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<int64_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<uint64_t>(20)));
EXPECT_EQ("false", AZStd::to_string(false));
EXPECT_EQ("true", AZStd::to_string(true));
// wstring to string // wstring to string
AZStd::string str1; AZStd::string str1;
AZStd::to_string(str1, wstr); AZStd::to_string(str1, wstr);
@ -978,6 +966,47 @@ namespace UnitTest
AZ_TEST_ASSERT(*vecIt++ == "Xiph Xlater 10000"); AZ_TEST_ASSERT(*vecIt++ == "Xiph Xlater 10000");
} }
// Concept to model if AZStd::to_string(<type>) is a valid expression
template<class T, class = void>
constexpr bool IsToStringInvocable = false;
template<class T>
constexpr bool IsToStringInvocable<T, AZStd::void_t<decltype(AZStd::to_string(AZStd::declval<T>()))>> = true;
TEST_F(String, String_to_stringOverload_DoesNotImplicitlyConvertToBool)
{
AZStd::string intStr;
AZStd::to_string(intStr, 20);
EXPECT_EQ("20", intStr);
EXPECT_EQ("20", AZStd::to_string(static_cast<int16_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<uint16_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<int32_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<uint32_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<int64_t>(20)));
EXPECT_EQ("20", AZStd::to_string(static_cast<uint64_t>(20)));
EXPECT_EQ("false", AZStd::to_string(false));
EXPECT_EQ("true", AZStd::to_string(true));
// AZStd::to_string should not be invocable with a char or wchar_t literal
static_assert(!IsToStringInvocable<decltype("NarrowStrLiteral")>);
static_assert(!IsToStringInvocable<decltype(L"WideStrLiteral")>);
// AZStd::to_string should
static_assert(IsToStringInvocable<bool>);
static_assert(IsToStringInvocable<AZ::s8>);
static_assert(IsToStringInvocable<AZ::u8>);
static_assert(IsToStringInvocable<AZ::s16>);
static_assert(IsToStringInvocable<AZ::u16>);
static_assert(IsToStringInvocable<AZ::s32>);
static_assert(IsToStringInvocable<AZ::u32>);
static_assert(IsToStringInvocable<AZ::s64>);
static_assert(IsToStringInvocable<AZ::u64>);
static_assert(IsToStringInvocable<float>);
static_assert(IsToStringInvocable<double>);
static_assert(IsToStringInvocable<long double>);
}
class Regex class Regex
: public AllocatorsFixture : public AllocatorsFixture
{ {

@ -37,8 +37,8 @@ namespace UnitTest
// min/max need to be substantially different to return a useful t value // min/max need to be substantially different to return a useful t value
// Float // Float
const float epsilonF = std::numeric_limits<float>::epsilon(); const float epsilonF = AZStd::numeric_limits<float>::epsilon();
const float doesntMatterF = std::numeric_limits<float>::signaling_NaN(); const float doesntMatterF = AZStd::numeric_limits<float>::signaling_NaN();
float lowerF = 2.3f, upperF = 2.3f; float lowerF = 2.3f, upperF = 2.3f;
EXPECT_EQ(0.0f, AZ::LerpInverse(lowerF, upperF, doesntMatterF)); EXPECT_EQ(0.0f, AZ::LerpInverse(lowerF, upperF, doesntMatterF));
EXPECT_EQ(0.0f, AZ::LerpInverse(0.0f, 0.5f * epsilonF, doesntMatterF)); EXPECT_EQ(0.0f, AZ::LerpInverse(0.0f, 0.5f * epsilonF, doesntMatterF));
@ -48,8 +48,8 @@ namespace UnitTest
EXPECT_NEAR(1.0f, AZ::LerpInverse(1.0f, 1.0f + 5.0f * epsilonF, 1.0f + 5.0f * epsilonF), epsilonF); EXPECT_NEAR(1.0f, AZ::LerpInverse(1.0f, 1.0f + 5.0f * epsilonF, 1.0f + 5.0f * epsilonF), epsilonF);
// Double // Double
const double epsilonD = std::numeric_limits<double>::epsilon(); const double epsilonD = AZStd::numeric_limits<double>::epsilon();
const double doesntMatterD = std::numeric_limits<double>::signaling_NaN(); const double doesntMatterD = AZStd::numeric_limits<double>::signaling_NaN();
double lowerD = 2.3, upperD = 2.3; double lowerD = 2.3, upperD = 2.3;
EXPECT_EQ(0.0, AZ::LerpInverse(lowerD, upperD, doesntMatterD)); EXPECT_EQ(0.0, AZ::LerpInverse(lowerD, upperD, doesntMatterD));
EXPECT_EQ(0.0, AZ::LerpInverse(0.0, 0.5 * epsilonD, doesntMatterD)); EXPECT_EQ(0.0, AZ::LerpInverse(0.0, 0.5 * epsilonD, doesntMatterD));
@ -58,4 +58,128 @@ namespace UnitTest
EXPECT_NEAR(0.6, AZ::LerpInverse(1.0, 1.0 + 5.0 * epsilonD, 1.0 + 3.0 * epsilonD), epsilonD); EXPECT_NEAR(0.6, AZ::LerpInverse(1.0, 1.0 + 5.0 * epsilonD, 1.0 + 3.0 * epsilonD), epsilonD);
EXPECT_NEAR(1.0, AZ::LerpInverse(1.0, 1.0 + 5.0 * epsilonD, 1.0 + 5.0 * epsilonD), epsilonD); EXPECT_NEAR(1.0, AZ::LerpInverse(1.0, 1.0 + 5.0 * epsilonD, 1.0 + 5.0 * epsilonD), epsilonD);
} }
template <typename T>
void TestRoundUpToMultipleIsCorrect()
{
// Example: alignment: 4
// inputValue: 0 1 2 3 4 5 6 7 8 ...
// expectedOutput: 0 4 4 4 4 8 8 8 8 ...
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(0) , static_cast<T>(1)) , 0);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(1) , static_cast<T>(1)) , 1);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(2) , static_cast<T>(1)) , 2);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(0) , static_cast<T>(2)) , 0);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(1) , static_cast<T>(2)) , 2);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(2) , static_cast<T>(2)) , 2);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(3) , static_cast<T>(2)) , 4);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(4) , static_cast<T>(2)) , 4);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(5) , static_cast<T>(2)) , 6);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(0) , static_cast<T>(8)) , 0);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(1) , static_cast<T>(8)) , 8);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(7) , static_cast<T>(8)) , 8);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(8) , static_cast<T>(8)) , 8);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(9) , static_cast<T>(8)) , 16);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(15), static_cast<T>(8)) , 16);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(16), static_cast<T>(8)) , 16);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(17), static_cast<T>(8)) , 24);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(0) , static_cast<T>(13)), 0);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(1) , static_cast<T>(13)), 13);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(9) , static_cast<T>(13)), 13);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(12), static_cast<T>(13)), 13);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(13), static_cast<T>(13)), 13);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(14), static_cast<T>(13)), 26);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(25), static_cast<T>(13)), 26);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(26), static_cast<T>(13)), 26);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(27), static_cast<T>(13)), 39);
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(0), AZStd::numeric_limits<T>::max()), 0);
T aVeryLargeNumberThatStillWontOverflow = AZStd::numeric_limits<T>::max() - 4;
EXPECT_EQ(RoundUpToMultiple(static_cast<T>(1), aVeryLargeNumberThatStillWontOverflow), aVeryLargeNumberThatStillWontOverflow);
EXPECT_EQ(RoundUpToMultiple(aVeryLargeNumberThatStillWontOverflow, static_cast<T>(1)), aVeryLargeNumberThatStillWontOverflow);
}
TEST(RoundUpToMultipleTest, RoundUpToMultipleUInt32_ValidInput_IsCorrect)
{
TestRoundUpToMultipleIsCorrect<uint32_t>();
}
TEST(RoundUpToMultipleTest, RoundUpToMultipleUInt64_ValidInput_IsCorrect)
{
TestRoundUpToMultipleIsCorrect<uint64_t>();
}
template<typename T>
void TestDivideAndRoundUpIsCorrect()
{
//! Example: alignment: 3
//! Value: 0 1 2 3 4 5 6 7 8
//! Result: 0 1 1 1 2 2 2 3 3
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(0), static_cast<T>(3)), 0);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(1), static_cast<T>(3)), 1);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(2), static_cast<T>(3)), 1);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(3), static_cast<T>(3)), 1);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(4), static_cast<T>(3)), 2);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(5), static_cast<T>(3)), 2);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(6), static_cast<T>(3)), 2);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(7), static_cast<T>(3)), 3);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(8), static_cast<T>(3)), 3);
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(0), AZStd::numeric_limits<T>::max()), 0);
T aVeryLargeNumberThatStillWontOverflow = AZStd::numeric_limits<T>::max() - 4;
EXPECT_EQ(DivideAndRoundUp(static_cast<T>(1), aVeryLargeNumberThatStillWontOverflow), static_cast<T>(1));
EXPECT_EQ(DivideAndRoundUp(aVeryLargeNumberThatStillWontOverflow, static_cast<T>(1)), aVeryLargeNumberThatStillWontOverflow);
}
TEST(DivideAndRoundUpTest, DivideAndRoundUpUInt32_ValidInput_IsCorrect)
{
TestDivideAndRoundUpIsCorrect<uint32_t>();
}
TEST(DivideAndRoundUpTest, DivideAndRoundUpUInt64_ValidInput_IsCorrect)
{
TestDivideAndRoundUpIsCorrect<uint64_t>();
}
class RoundUpInvalidInputTestsFixture : public ScopedAllocatorSetupFixture
{
};
TEST_F(RoundUpInvalidInputTestsFixture, DividAndRoundUp_AlignmentZeroUint32_Assert)
{
AZ_TEST_START_TRACE_SUPPRESSION;
DivideAndRoundUp(static_cast<uint32_t>(0), static_cast<uint32_t>(0));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(RoundUpInvalidInputTestsFixture, DividAndRoundUp_AlignmentZeroUint64_Assert)
{
AZ_TEST_START_TRACE_SUPPRESSION;
DivideAndRoundUp(static_cast<uint64_t>(0), static_cast<uint64_t>(0));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(RoundUpInvalidInputTestsFixture, DividAndRoundUp_OverflowUint32_Assert)
{
AZ_TEST_START_TRACE_SUPPRESSION;
DivideAndRoundUp(
static_cast<uint32_t>((AZStd::numeric_limits<uint32_t>::max() / 2) + 1),
static_cast<uint32_t>((AZStd::numeric_limits<uint32_t>::max() / 2) + 1));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(RoundUpInvalidInputTestsFixture, DividAndRoundUp_OverflowUint64_Assert)
{
AZ_TEST_START_TRACE_SUPPRESSION;
DivideAndRoundUp(
static_cast<uint64_t>((AZStd::numeric_limits<uint64_t>::max() / 2) + 1),
static_cast<uint64_t>((AZStd::numeric_limits<uint64_t>::max() / 2) + 1));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
} }

@ -196,6 +196,19 @@ namespace AZ
ASSERT_EQ(joinResult, expectedResult); ASSERT_EQ(joinResult, expectedResult);
} }
TEST_F(StringFuncTest, Join_NonPathJoin_CanJoinRange)
{
AZStd::string result;
AZ::StringFunc::Join(result, AZStd::initializer_list<const char*>{ "1", "2", "3", "4", "3" }, '/');
EXPECT_EQ("1/2/3/4/3", result);
result.clear();
// Try joining with a string literal instead of a char literal
AZ::StringFunc::Join(result, AZStd::initializer_list<const char*>{ "1", "2", "3", "4", "3" }, "/");
EXPECT_EQ("1/2/3/4/3", result);
}
TEST_F(StringFuncTest, Tokenize_SingleDelimeter_Empty) TEST_F(StringFuncTest, Tokenize_SingleDelimeter_Empty)
{ {
AZStd::string input = ""; AZStd::string input = "";

@ -64,7 +64,7 @@ namespace AzFramework::ProjectManager
// If we were able to locate a path to a project, we're done // If we were able to locate a path to a project, we're done
if (!projectRootPath.empty()) if (!projectRootPath.empty())
{ {
AZ::IO::FixedMaxPath projectJsonPath = engineRootPath / projectRootPath / "project.json"; AZ::IO::FixedMaxPath projectJsonPath = projectRootPath / "project.json";
if (AZ::IO::SystemFile::Exists(projectJsonPath.c_str())) if (AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
{ {
return ProjectPathCheckResult::ProjectPathFound; return ProjectPathCheckResult::ProjectPathFound;

@ -190,9 +190,67 @@ namespace AzFramework
return nextCamera; return nextCamera;
} }
void Cameras::AddCamera(AZStd::shared_ptr<CameraInput> cameraInput) bool Cameras::AddCamera(AZStd::shared_ptr<CameraInput> cameraInput)
{ {
m_idleCameraInputs.push_back(AZStd::move(cameraInput)); const auto idleCameraIt = AZStd::find(m_idleCameraInputs.begin(), m_idleCameraInputs.end(), cameraInput);
const auto activeCameraIt = AZStd::find(m_activeCameraInputs.begin(), m_activeCameraInputs.end(), cameraInput);
if (idleCameraIt == m_idleCameraInputs.end() && activeCameraIt == m_activeCameraInputs.end())
{
m_idleCameraInputs.push_back(AZStd::move(cameraInput));
return true;
}
return false;
}
bool Cameras::AddCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs)
{
bool allAdded = true;
for (auto cameraInput : cameraInputs)
{
allAdded = AddCamera(AZStd::move(cameraInput)) && allAdded;
}
return allAdded;
}
bool Cameras::RemoveCamera(const AZStd::shared_ptr<CameraInput>& cameraInput)
{
if (const auto idleCameraIt = AZStd::find(m_idleCameraInputs.begin(), m_idleCameraInputs.end(), cameraInput);
idleCameraIt != m_idleCameraInputs.end())
{
const auto idleIndex = idleCameraIt - m_idleCameraInputs.begin();
using AZStd::swap;
swap(m_idleCameraInputs[idleIndex], m_idleCameraInputs[m_idleCameraInputs.size() - 1]);
m_idleCameraInputs.pop_back();
return true;
}
if (const auto activeCameraIt = AZStd::find(m_activeCameraInputs.begin(), m_activeCameraInputs.end(), cameraInput);
activeCameraIt != m_activeCameraInputs.end())
{
(*activeCameraIt)->Reset();
const auto activeIndex = activeCameraIt - m_idleCameraInputs.begin();
using AZStd::swap;
swap(m_activeCameraInputs[activeIndex], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
m_activeCameraInputs.pop_back();
return true;
}
return false;
}
bool Cameras::RemoveCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs)
{
bool allRemoved = true;
for (const auto& cameraInput : cameraInputs)
{
allRemoved = RemoveCamera(cameraInput) && allRemoved;
}
return allRemoved;
} }
bool Cameras::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta) bool Cameras::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
@ -645,7 +703,7 @@ namespace AzFramework
m_orbitChannelId = orbitChanneId; m_orbitChannelId = orbitChanneId;
} }
OrbitDollyScrollCameraInput::OrbitDollyScrollCameraInput() OrbitScrollDollyCameraInput::OrbitScrollDollyCameraInput()
{ {
m_scrollSpeedFn = []() constexpr m_scrollSpeedFn = []() constexpr
{ {
@ -653,7 +711,7 @@ namespace AzFramework
}; };
} }
bool OrbitDollyScrollCameraInput::HandleEvents( bool OrbitScrollDollyCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta) const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{ {
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event)) if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@ -691,7 +749,7 @@ namespace AzFramework
return nextCamera; return nextCamera;
} }
Camera OrbitDollyScrollCameraInput::StepCamera( Camera OrbitScrollDollyCameraInput::StepCamera(
const Camera& targetCamera, const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta, const float scrollDelta,
@ -702,7 +760,7 @@ namespace AzFramework
return nextCamera; return nextCamera;
} }
OrbitDollyMotionCameraInput::OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId) OrbitMotionDollyCameraInput::OrbitMotionDollyCameraInput(const InputChannelId& dollyChannelId)
: m_dollyChannelId(dollyChannelId) : m_dollyChannelId(dollyChannelId)
{ {
m_motionSpeedFn = []() constexpr m_motionSpeedFn = []() constexpr
@ -711,14 +769,14 @@ namespace AzFramework
}; };
} }
bool OrbitDollyMotionCameraInput::HandleEvents( bool OrbitMotionDollyCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta) const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{ {
HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this); HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this);
return CameraInputUpdatingAfterMotion(*this); return CameraInputUpdatingAfterMotion(*this);
} }
Camera OrbitDollyMotionCameraInput::StepCamera( Camera OrbitMotionDollyCameraInput::StepCamera(
const Camera& targetCamera, const Camera& targetCamera,
const ScreenVector& cursorDelta, const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta, [[maybe_unused]] const float scrollDelta,
@ -727,7 +785,7 @@ namespace AzFramework
return OrbitDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn()); return OrbitDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn());
} }
void OrbitDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId) void OrbitMotionDollyCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
{ {
m_dollyChannelId = dollyChannelId; m_dollyChannelId = dollyChannelId;
} }

@ -289,7 +289,17 @@ namespace AzFramework
//! Add a camera input (behavior) to run in this set of camera inputs. //! Add a camera input (behavior) to run in this set of camera inputs.
//! The camera inputs added here will determine the overall behavior of the camera. //! The camera inputs added here will determine the overall behavior of the camera.
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput); //! @return Returns if the camera was successfully added (if the camera already exists it is not added and AddCamera returns false).
bool AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
//! Add a collection of camera inputs (behaviors) to run in this set of camera inputs.
//! @return Returns if all cameras were added successfully.
bool AddCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs);
//! Remove a camera input (behavior) to stop it running in the set of camera inputs.
//! @return Returns if the camera was removed successfully (if the could not be found RemoveCamera returns false).
bool RemoveCamera(const AZStd::shared_ptr<CameraInput>& cameraInput);
//! Remove a collection of camera inputs (behaviors) to stop them running in the set of camera inputs.
//! @return Returns if all cameras were removed successfully.
bool RemoveCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs);
//! Reset the state of all cameras. //! Reset the state of all cameras.
void Reset(); void Reset();
//! Remove all cameras that were added. //! Remove all cameras that were added.
@ -299,9 +309,10 @@ namespace AzFramework
bool Exclusive() const; bool Exclusive() const;
private: private:
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs; //!< Active camera inputs updating the camera (empty initially). //! Active camera inputs updating the camera (empty initially).
AZStd::vector<AZStd::shared_ptr<CameraInput>> AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
m_idleCameraInputs; //!< Idle camera inputs not contributing to the update (filled initially). //! Idle camera inputs not contributing to the update (filled initially).
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_idleCameraInputs;
}; };
//! Responsible for updating a series of cameras given various inputs. //! Responsible for updating a series of cameras given various inputs.
@ -586,10 +597,10 @@ namespace AzFramework
}; };
//! A camera input to handle discrete scroll events that can modify the camera offset. //! A camera input to handle discrete scroll events that can modify the camera offset.
class OrbitDollyScrollCameraInput : public CameraInput class OrbitScrollDollyCameraInput : public CameraInput
{ {
public: public:
OrbitDollyScrollCameraInput(); OrbitScrollDollyCameraInput();
// CameraInput overrides ... // CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@ -599,10 +610,10 @@ namespace AzFramework
}; };
//! A camera input to handle motion deltas that can modify the camera offset. //! A camera input to handle motion deltas that can modify the camera offset.
class OrbitDollyMotionCameraInput : public CameraInput class OrbitMotionDollyCameraInput : public CameraInput
{ {
public: public:
explicit OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId); explicit OrbitMotionDollyCameraInput(const InputChannelId& dollyChannelId);
// CameraInput overrides ... // CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;

@ -490,4 +490,40 @@ namespace UnitTest
EXPECT_THAT(m_orbitCamera->Ending(), IsFalse()); EXPECT_THAT(m_orbitCamera->Ending(), IsFalse());
EXPECT_THAT(m_orbitCamera->Idle(), IsTrue()); EXPECT_THAT(m_orbitCamera->Idle(), IsTrue());
} }
TEST_F(CameraInputFixture, NewCameraInputCanBeAddedToCameraSystem)
{
auto firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
AzFramework::InputDeviceMouse::Button::Middle, AzFramework::LookPan, AzFramework::TranslatePivotLook);
const bool added =
m_cameraSystem->m_cameras.AddCameras(AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>{ firstPersonPanCamera });
EXPECT_THAT(added, ::testing::IsTrue());
}
TEST_F(CameraInputFixture, ExistingCameraInputCannotBeAddedToCameraSystem)
{
const bool added =
m_cameraSystem->m_cameras.AddCameras(AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>{ m_firstPersonRotateCamera });
EXPECT_THAT(added, ::testing::IsFalse());
}
TEST_F(CameraInputFixture, ExistingCameraInputCanBeRemovedFromCameraSystem)
{
const bool removed = m_cameraSystem->m_cameras.RemoveCameras(
AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>{ m_firstPersonRotateCamera });
EXPECT_THAT(removed, ::testing::IsTrue());
}
TEST_F(CameraInputFixture, NonExistentCameraInputCannotBeRemovedFromCameraSystem)
{
auto firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
AzFramework::InputDeviceMouse::Button::Middle, AzFramework::LookPan, AzFramework::TranslatePivotLook);
const bool removed = m_cameraSystem->m_cameras.RemoveCameras(
AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>{ firstPersonPanCamera });
EXPECT_THAT(removed, ::testing::IsFalse());
}
} // namespace UnitTest } // namespace UnitTest

@ -17,6 +17,7 @@
#include <AzNetworking/Utilities/NetworkCommon.h> #include <AzNetworking/Utilities/NetworkCommon.h>
#include <AzCore/Console/IConsole.h> #include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h> #include <AzCore/Console/ILogger.h>
#include <AzCore/Math/MathUtils.h>
namespace AzNetworking namespace AzNetworking
{ {
@ -539,7 +540,7 @@ namespace AzNetworking
// Each fragmented packet we send adds an extra fragmented packet header, need to deduct that from our chunk size, otherwise we infinitely loop // Each fragmented packet we send adds an extra fragmented packet header, need to deduct that from our chunk size, otherwise we infinitely loop
// SSL encryption can also inflate our payload so we pre-emptively deduct an estimated tax // SSL encryption can also inflate our payload so we pre-emptively deduct an estimated tax
const uint32_t chunkSize = connection.GetConnectionMtu() - net_FragmentedHeaderOverhead - net_SslInflationOverhead; const uint32_t chunkSize = connection.GetConnectionMtu() - net_FragmentedHeaderOverhead - net_SslInflationOverhead;
const uint32_t numChunks = (packetSize + chunkSize - 1) / chunkSize; // We want to round up on the remainder const uint32_t numChunks = AZ::DivideAndRoundUp(packetSize, chunkSize); // We want to round up on the remainder
const uint8_t* chunkStart = packetData; const uint8_t* chunkStart = packetData;
const SequenceId fragmentedSequence = connection.m_fragmentQueue.GetNextFragmentedSequenceId(); const SequenceId fragmentedSequence = connection.m_fragmentQueue.GetNextFragmentedSequenceId();
uint32_t bytesRemaining = packetSize; uint32_t bytesRemaining = packetSize;

@ -8,6 +8,7 @@
#pragma once #pragma once
#include <AzCore/EBus/EBus.h> #include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Outcome/Outcome.h> #include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/any.h> #include <AzCore/std/any.h>
#include <AzCore/std/string/string.h> #include <AzCore/std/string/string.h>

@ -208,10 +208,6 @@ namespace LegacyFramework
*/ */
virtual bool IsRunningInGUIMode() = 0; virtual bool IsRunningInGUIMode() = 0;
/** Retrieve a Command Line Parser object that you can then use to check for values on the command line
*/
virtual const AzFramework::CommandLine* GetCommandLineParser() = 0;
/** (Windows) retrieves the main module of the executable. /** (Windows) retrieves the main module of the executable.
* This is always going to be the main executable except in the situation where the framework may be running as a DLL belonging to another process or program. * This is always going to be the main executable except in the situation where the framework may be running as a DLL belonging to another process or program.
*/ */

@ -97,6 +97,10 @@ namespace LegacyFramework
} }
Application::Application() Application::Application()
: Application(0, nullptr)
{}
Application::Application(int argc, char** argv)
: ComponentApplication(argc, argv)
{ {
m_isPrimary = true; m_isPrimary = true;
m_desiredExitCode = 0; m_desiredExitCode = 0;
@ -191,9 +195,6 @@ namespace LegacyFramework
::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, true); ::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, true);
#endif #endif
m_ptrCommandLineParser = aznew AzFramework::CommandLine();
m_ptrCommandLineParser->Parse(m_desc.m_argc, m_desc.m_argv);
// If we don't have one create a serialize context // If we don't have one create a serialize context
if (GetSerializeContext() == nullptr) if (GetSerializeContext() == nullptr)
{ {
@ -206,7 +207,7 @@ namespace LegacyFramework
m_ptrSystemEntity->Activate(); m_ptrSystemEntity->Activate();
// If we aren't the primary, RunAsAnotherInstance unless we are being forcestarted // If we aren't the primary, RunAsAnotherInstance unless we are being forcestarted
if (!m_isPrimary && !m_ptrCommandLineParser->HasSwitch("forcestart")) if (!m_isPrimary && !m_commandLine.HasSwitch("forcestart"))
{ {
// Required for the application component to handle RunAsAnotherInstance // Required for the application component to handle RunAsAnotherInstance
CreateApplicationComponent(); CreateApplicationComponent();
@ -246,9 +247,6 @@ namespace LegacyFramework
::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, false); ::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, false);
#endif #endif
delete m_ptrCommandLineParser;
m_ptrCommandLineParser = nullptr;
CoreMessageBus::Handler::BusDisconnect(); CoreMessageBus::Handler::BusDisconnect();
FrameworkApplicationMessages::Handler::BusDisconnect(); FrameworkApplicationMessages::Handler::BusDisconnect();
@ -271,11 +269,6 @@ namespace LegacyFramework
} }
} }
const AzFramework::CommandLine* Application::GetCommandLineParser()
{
return m_ptrCommandLineParser;
}
// returns TRUE if the component already existed, FALSE if it had to create one. // returns TRUE if the component already existed, FALSE if it had to create one.
bool Application::EnsureComponentCreated(AZ::Uuid componentCRC) bool Application::EnsureComponentCreated(AZ::Uuid componentCRC)
{ {

@ -56,6 +56,7 @@ namespace LegacyFramework
using CoreMessageBus::Handler::Run; using CoreMessageBus::Handler::Run;
virtual int Run(const ApplicationDesc& desc); virtual int Run(const ApplicationDesc& desc);
Application(); Application();
Application(int argc, char** argv);
void CreateReflectionManager() override; void CreateReflectionManager() override;
@ -70,7 +71,6 @@ namespace LegacyFramework
const char* GetApplicationName() override; const char* GetApplicationName() override;
const char* GetApplicationModule() override; const char* GetApplicationModule() override;
const char* GetApplicationDirectory() override; const char* GetApplicationDirectory() override;
const AzFramework::CommandLine* GetCommandLineParser() override;
void TeardownApplicationComponent() override; void TeardownApplicationComponent() override;
void RunAssetProcessor() override; void RunAssetProcessor() override;
// ------------------------------------------------------------------ // ------------------------------------------------------------------
@ -138,7 +138,6 @@ namespace LegacyFramework
volatile bool m_abortRequested; // if you CTRL+C in a console app, this becomes true. its up to you to check... volatile bool m_abortRequested; // if you CTRL+C in a console app, this becomes true. its up to you to check...
char m_applicationFilePath[AZ_MAX_PATH_LEN]; char m_applicationFilePath[AZ_MAX_PATH_LEN];
ApplicationDesc m_desc; ApplicationDesc m_desc;
AzFramework::CommandLine* m_ptrCommandLineParser;
}; };
} }

@ -16,7 +16,7 @@
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/CommandLine/CommandLine.h> #include <AzCore/Settings/CommandLine.h>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h> #include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkAPI.h> #include <AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkAPI.h>
@ -34,7 +34,6 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // '...' needs to have
#include <QProxyStyle> #include <QProxyStyle>
AZ_POP_DISABLE_WARNING AZ_POP_DISABLE_WARNING
#include <AzFramework/StringFunc/StringFunc.h>
#ifndef AZ_PLATFORM_WINDOWS #ifndef AZ_PLATFORM_WINDOWS
int __argc = 0; int __argc = 0;
@ -201,8 +200,11 @@ namespace AzToolsFramework
// enable the built-in stylesheet by default: // enable the built-in stylesheet by default:
bool enableStyleSheet = true; bool enableStyleSheet = true;
const AzFramework::CommandLine* comp = nullptr; const AZ::CommandLine* comp = nullptr;
EBUS_EVENT_RESULT(comp, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser); AZ::ComponentApplicationBus::Broadcast([&comp](AZ::ComponentApplicationRequests* requests)
{
comp = requests->GetAzCommandLine();
});
if (comp != nullptr) if (comp != nullptr)
{ {
if (comp->HasSwitch("nostyle")) if (comp->HasSwitch("nostyle"))

@ -253,6 +253,68 @@ namespace AzToolsFramework
} }
} }
// Edit/Inspect/Close Prefab
{
if (selectedEntities.size() == 1)
{
AZ::EntityId selectedEntity = selectedEntities[0];
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
{
if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
{
if (s_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(selectedEntity))
{
// Inspect Prefab
QAction* editAction = menu->addAction(QObject::tr("Inspect Procedural Prefab"));
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
editAction->setToolTip(QObject::tr("See the procedural prefab contents in focus mode."));
QObject::connect(
editAction, &QAction::triggered, editAction,
[selectedEntity]
{
ContextMenu_EditPrefab(selectedEntity);
}
);
}
else
{
// Edit Prefab
QAction* editAction = menu->addAction(QObject::tr("Open/Edit Prefab"));
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
QObject::connect(
editAction, &QAction::triggered, editAction,
[selectedEntity]
{
ContextMenu_EditPrefab(selectedEntity);
}
);
}
}
else
{
// Close Prefab
QAction* closeAction = menu->addAction(QObject::tr("Close Prefab"));
closeAction->setShortcut(QKeySequence(Qt::Key_Minus));
closeAction->setToolTip(QObject::tr("Close focus mode for this prefab and move one level up."));
QObject::connect(
closeAction, &QAction::triggered, closeAction,
[]
{
ContextMenu_ClosePrefab();
}
);
}
menu->addSeparator();
}
}
}
bool itemWasShown = false; bool itemWasShown = false;
// Create Prefab // Create Prefab
@ -291,7 +353,8 @@ namespace AzToolsFramework
[selectedEntities] [selectedEntities]
{ {
ContextMenu_CreatePrefab(selectedEntities); ContextMenu_CreatePrefab(selectedEntities);
}); }
);
itemWasShown = true; itemWasShown = true;
} }
@ -299,6 +362,21 @@ namespace AzToolsFramework
} }
} }
// Detach Prefab
if (onlySelectedEntityIsClosedPrefabContainer)
{
AZ::EntityId selectedEntityId = selectedEntities.front();
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
QObject::connect(
detachPrefabAction, &QAction::triggered, detachPrefabAction,
[selectedEntityId]
{
ContextMenu_DetachPrefab(selectedEntityId);
}
);
}
// Instantiate Prefab // Instantiate Prefab
if (selectedEntities.size() == 0 || if (selectedEntities.size() == 0 ||
selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer) selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer)
@ -311,7 +389,8 @@ namespace AzToolsFramework
[] []
{ {
ContextMenu_InstantiatePrefab(); ContextMenu_InstantiatePrefab();
}); }
);
// Instantiate Procedural Prefab // Instantiate Procedural Prefab
if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs()) if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs())
@ -324,7 +403,8 @@ namespace AzToolsFramework
[] []
{ {
ContextMenu_InstantiateProceduralPrefab(); ContextMenu_InstantiateProceduralPrefab();
}); }
);
} }
itemWasShown = true; itemWasShown = true;
@ -335,9 +415,7 @@ namespace AzToolsFramework
menu->addSeparator(); menu->addSeparator();
} }
itemWasShown = false; // Save Prefab
// Edit/Save Prefab
{ {
if (selectedEntities.size() == 1) if (selectedEntities.size() == 1)
{ {
@ -345,52 +423,6 @@ namespace AzToolsFramework
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity)) if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
{ {
if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
{
if (s_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(selectedEntity))
{
// Inspect Prefab
QAction* editAction = menu->addAction(QObject::tr("Inspect Procedural Prefab"));
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
editAction->setToolTip(QObject::tr("See the procedural prefab contents in focus mode."));
QObject::connect(
editAction, &QAction::triggered, editAction,
[selectedEntity]
{
ContextMenu_EditPrefab(selectedEntity);
});
}
else
{
// Edit Prefab
QAction* editAction = menu->addAction(QObject::tr("Open/Edit Prefab"));
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
QObject::connect(
editAction, &QAction::triggered, editAction,
[selectedEntity]
{
ContextMenu_EditPrefab(selectedEntity);
});
}
}
else
{
// Close Prefab
QAction* closeAction = menu->addAction(QObject::tr("Close Prefab"));
closeAction->setShortcut(QKeySequence(Qt::Key_Minus));
closeAction->setToolTip(QObject::tr("Close focus mode for this prefab and move one level up."));
QObject::connect(
closeAction, &QAction::triggered, closeAction,
[]
{
ContextMenu_ClosePrefab();
});
}
// Save Prefab // Save Prefab
AZ::IO::Path prefabFilePath = s_prefabPublicInterface->GetOwningInstancePrefabPath(selectedEntity); AZ::IO::Path prefabFilePath = s_prefabPublicInterface->GetOwningInstancePrefabPath(selectedEntity);
auto dirtyOutcome = s_prefabPublicInterface->HasUnsavedChanges(prefabFilePath); auto dirtyOutcome = s_prefabPublicInterface->HasUnsavedChanges(prefabFilePath);
@ -405,17 +437,43 @@ namespace AzToolsFramework
[selectedEntity] [selectedEntity]
{ {
ContextMenu_SavePrefab(selectedEntity); ContextMenu_SavePrefab(selectedEntity);
}); }
} );
itemWasShown = true; menu->addSeparator();
}
} }
} }
} }
if (itemWasShown) if (!selectedEntities.empty())
{ {
menu->addSeparator(); // Don't allow duplication if any of the selected entities are direct descendants of a read-only entity
bool selectionContainsDescendantOfReadOnlyEntity = false;
for (const auto& entityId : selectedEntities)
{
AZ::EntityId parentEntityId;
AZ::TransformBus::EventResult(parentEntityId, entityId, &AZ::TransformBus::Events::GetParentId);
if (parentEntityId.IsValid() && m_readOnlyEntityPublicInterface->IsReadOnly(parentEntityId))
{
selectionContainsDescendantOfReadOnlyEntity = true;
break;
}
}
if (!selectionContainsDescendantOfReadOnlyEntity)
{
QAction* duplicateAction = menu->addAction(QObject::tr("Duplicate"));
duplicateAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D));
QObject::connect(
duplicateAction, &QAction::triggered, duplicateAction,
[]
{
ContextMenu_Duplicate();
}
);
}
} }
if (!selectedEntities.empty() && if (!selectedEntities.empty() &&
@ -424,27 +482,17 @@ namespace AzToolsFramework
!readOnlyEntityInSelection) !readOnlyEntityInSelection)
{ {
QAction* deleteAction = menu->addAction(QObject::tr("Delete")); QAction* deleteAction = menu->addAction(QObject::tr("Delete"));
deleteAction->setShortcut(QKeySequence(Qt::Key_Delete));
QObject::connect( QObject::connect(
deleteAction, &QAction::triggered, deleteAction, deleteAction, &QAction::triggered, deleteAction,
[] []
{ {
ContextMenu_DeleteSelected(); ContextMenu_DeleteSelected();
}); }
);
} }
// Detach Prefab menu->addSeparator();
if (onlySelectedEntityIsClosedPrefabContainer)
{
AZ::EntityId selectedEntityId = selectedEntities.front();
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
QObject::connect(
detachPrefabAction, &QAction::triggered, detachPrefabAction,
[selectedEntityId]
{
ContextMenu_DetachPrefab(selectedEntityId);
});
}
} }
void PrefabIntegrationManager::OnEscape() void PrefabIntegrationManager::OnEscape()
@ -653,6 +701,12 @@ namespace AzToolsFramework
} }
} }
void PrefabIntegrationManager::ContextMenu_Duplicate()
{
bool handled = true;
AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequests::CloneSelection, handled);
}
void PrefabIntegrationManager::ContextMenu_DeleteSelected() void PrefabIntegrationManager::ContextMenu_DeleteSelected()
{ {
AzToolsFramework::EntityIdList selectedEntityIds; AzToolsFramework::EntityIdList selectedEntityIds;

@ -92,6 +92,7 @@ namespace AzToolsFramework
static void ContextMenu_ClosePrefab(); static void ContextMenu_ClosePrefab();
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity); static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity); static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
static void ContextMenu_Duplicate();
static void ContextMenu_DeleteSelected(); static void ContextMenu_DeleteSelected();
static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity); static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity);

@ -20,6 +20,7 @@
#include <GridMate/Containers/unordered_set.h> #include <GridMate/Containers/unordered_set.h>
#include <GridMate/Carrier/DriverEvents.h> #include <GridMate/Carrier/DriverEvents.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/std/chrono/types.h> #include <AzCore/std/chrono/types.h>
#include <AzCore/std/string/conversions.h> #include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/memorytoascii.h> #include <AzCore/std/string/memorytoascii.h>
@ -1951,7 +1952,7 @@ namespace GridMate
char *SocketDriverCommon::RIOPlatformSocketDriver::AllocRIOBuffer(AZ::u64 bufferSize, AZ::u64 numBuffers, AZ::u64* amountAllocated /*=nullptr*/) char *SocketDriverCommon::RIOPlatformSocketDriver::AllocRIOBuffer(AZ::u64 bufferSize, AZ::u64 numBuffers, AZ::u64* amountAllocated /*=nullptr*/)
{ {
// calculate how much memory we are really asking for, and this must be page aligned. // calculate how much memory we are really asking for, and this must be page aligned.
AZ::u64 totalBufferSize = RoundUp(bufferSize * numBuffers, m_pageSize); AZ::u64 totalBufferSize = AZ::RoundUpToMultiple(bufferSize * numBuffers, m_pageSize);
if (amountAllocated != nullptr) if (amountAllocated != nullptr)
{ {

@ -221,18 +221,6 @@ namespace GridMate
void StopWaitForData() override; void StopWaitForData() override;
private: private:
AZ::u64 RoundUpAndDivide(AZ::u64 Value, AZ::u64 RoundTo) const
{
return ((Value + RoundTo - 1) / RoundTo);
}
AZ::u64 RoundUp(AZ::u64 Value, AZ::u64 RoundTo) const
{
// rounds value up to multiple of RoundTo
// Example: RoundTo: 4
// Value: 0 1 2 3 4 5 6 7 8
// Result: 0 4 4 4 4 8 8 8 8
return RoundUpAndDivide(Value, RoundTo) * RoundTo;
}
char *AllocRIOBuffer(AZ::u64 bufferSize, AZ::u64 numBuffers, AZ::u64* amountAllocated=nullptr); char *AllocRIOBuffer(AZ::u64 bufferSize, AZ::u64 numBuffers, AZ::u64* amountAllocated=nullptr);
bool FreeRIOBuffer(char *buffer); bool FreeRIOBuffer(char *buffer);

@ -21,11 +21,11 @@
#include <AzCore/Settings/SettingsRegistryMergeUtils.h> #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/string/conversions.h> #include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/regex.h> #include <AzCore/std/string/regex.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Asset/AssetSystemBus.h> #include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/Asset/AssetSystemComponent.h> #include <AzFramework/Asset/AssetSystemComponent.h>
#include <AzFramework/Asset/AssetCatalogBus.h> #include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Asset/AssetProcessorMessages.h> #include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@ -244,7 +244,7 @@ namespace LUAEditor
} }
AZStd::vector<AZStd::string> files; AZStd::vector<AZStd::string> files;
AzFramework::StringFunc::Tokenize(parameters.c_str(), files, ";"); AZ::StringFunc::Tokenize(parameters.c_str(), files, ";");
if (!files.empty()) if (!files.empty())
{ {
for (const auto& file : files) for (const auto& file : files)
@ -669,9 +669,12 @@ namespace LUAEditor
return; return;
} }
const AzFramework::CommandLine* commandLine = nullptr; const AZ::CommandLine* commandLine = nullptr;
EBUS_EVENT_RESULT(commandLine, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser); AZ::ComponentApplicationBus::Broadcast([&commandLine](AZ::ComponentApplicationRequests* requests)
{
commandLine = requests->GetAzCommandLine();
});
bool forceShow = false; bool forceShow = false;
bool forceHide = false; bool forceHide = false;
@ -850,7 +853,7 @@ namespace LUAEditor
DocumentInfo& info = infoEntry.first->second; DocumentInfo& info = infoEntry.first->second;
info.m_assetId = normalizedAssetId; info.m_assetId = normalizedAssetId;
info.m_assetName = assetId; info.m_assetName = assetId;
AzFramework::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName); AZ::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName);
info.m_bSourceControl_Ready = true; info.m_bSourceControl_Ready = true;
info.m_bSourceControl_CanWrite = true; info.m_bSourceControl_CanWrite = true;
info.m_bUntitledDocument = false; info.m_bUntitledDocument = false;
@ -945,7 +948,7 @@ namespace LUAEditor
// do not allow SaveAs onto an existing asset, even if it could be checked out and modified "safely." // do not allow SaveAs onto an existing asset, even if it could be checked out and modified "safely."
// end user must check out and modify contents directly if they want this // end user must check out and modify contents directly if they want this
if (AzFramework::StringFunc::Find(newAssetName.c_str(), ".lua") == AZStd::string::npos) if (AZ::StringFunc::Find(newAssetName.c_str(), ".lua") == AZStd::string::npos)
{ {
newAssetName += ".lua"; newAssetName += ".lua";
} }
@ -961,7 +964,7 @@ namespace LUAEditor
trySaveAs = false; trySaveAs = false;
docInfoIter->second.m_bUntitledDocument = false; docInfoIter->second.m_bUntitledDocument = false;
AzFramework::StringFunc::Path::GetFullFileName(newAssetName.c_str(), docInfoIter->second.m_displayName); AZ::StringFunc::Path::GetFullFileName(newAssetName.c_str(), docInfoIter->second.m_displayName);
// when you 'save as' you can write to it, even if it started out not that way. // when you 'save as' you can write to it, even if it started out not that way.
docInfoIter->second.m_bSourceControl_Ready = true; docInfoIter->second.m_bSourceControl_Ready = true;
@ -1489,7 +1492,7 @@ namespace LUAEditor
DocumentInfo info; DocumentInfo info;
info.m_assetName = assetIdLower; info.m_assetName = assetIdLower;
AzFramework::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName); AZ::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName);
info.m_assetId = assetIdLower; info.m_assetId = assetIdLower;
info.m_bSourceControl_BusyGettingStats = true; info.m_bSourceControl_BusyGettingStats = true;
info.m_bSourceControl_BusyGettingStats = false; info.m_bSourceControl_BusyGettingStats = false;
@ -1555,7 +1558,10 @@ namespace LUAEditor
const AZStd::string k_luaScriptFileString = "files"; const AZStd::string k_luaScriptFileString = "files";
const AzFramework::CommandLine* commandLine = nullptr; const AzFramework::CommandLine* commandLine = nullptr;
EBUS_EVENT_RESULT(commandLine, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser); AZ::ComponentApplicationBus::Broadcast([&commandLine](AZ::ComponentApplicationRequests* requests)
{
commandLine = requests->GetAzCommandLine();
});
AZStd::string parameters = ""; AZStd::string parameters = "";
size_t numSwitchValues = commandLine->GetNumSwitchValues(k_luaScriptFileString); size_t numSwitchValues = commandLine->GetNumSwitchValues(k_luaScriptFileString);
@ -2448,7 +2454,7 @@ namespace LUAEditor
if (matchFound) if (matchFound)
{ {
int lineNumber = 0; int lineNumber = 0;
if (AzFramework::StringFunc::LooksLikeInt(match[1].str().c_str(), &lineNumber)) if (AZ::StringFunc::LooksLikeInt(match[1].str().c_str(), &lineNumber))
{ {
errorData->m_lineNumber = lineNumber; errorData->m_lineNumber = lineNumber;
finalMessage = match[2].str().c_str(); finalMessage = match[2].str().c_str();
@ -2467,6 +2473,6 @@ namespace LUAEditor
bool Context::IsLuaAsset(const AZStd::string& assetPath) bool Context::IsLuaAsset(const AZStd::string& assetPath)
{ {
return AzFramework::StringFunc::Path::IsExtension(assetPath.c_str(), ".lua"); return AZ::StringFunc::Path::IsExtension(assetPath.c_str(), ".lua");
} }
} }

@ -27,7 +27,8 @@
namespace LUAEditor namespace LUAEditor
{ {
Application::Application(int &argc, char **argv) : BaseApplication(argc, argv) Application::Application(int argc, char **argv)
: BaseApplication(argc, argv)
{ {
AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect(); AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
} }

@ -17,7 +17,7 @@ namespace LUAEditor
, protected AzToolsFramework::SourceControlNotificationBus::Handler , protected AzToolsFramework::SourceControlNotificationBus::Handler
{ {
public: public:
Application(int &argc, char **argv); Application(int argc, char **argv);
~Application() override; ~Application() override;
protected: protected:

@ -20,8 +20,8 @@
namespace StandaloneTools namespace StandaloneTools
{ {
BaseApplication::BaseApplication(int&, char**) BaseApplication::BaseApplication(int argc, char** argv)
: LegacyFramework::Application() : LegacyFramework::Application(argc, argv)
{ {
AZ::UserSettingsFileLocatorBus::Handler::BusConnect(); AZ::UserSettingsFileLocatorBus::Handler::BusConnect();
} }

@ -25,7 +25,7 @@ namespace StandaloneTools
{ {
public: public:
BaseApplication(int &argc, char **argv); BaseApplication(int argc, char **argv);
~BaseApplication() override; ~BaseApplication() override;
protected: protected:

@ -179,7 +179,7 @@ namespace ImageProcessingAtom
// Create a context based on the configuration // Create a context based on the configuration
astcenc_context* context; astcenc_context* context;
AZ::u32 blockCount = ((srcImage->GetWidth(0)+ dstFormatInfo->blockWidth-1)/dstFormatInfo->blockWidth) * ((srcImage->GetHeight(0) + dstFormatInfo->blockHeight-1)/dstFormatInfo->blockHeight); AZ::u32 blockCount = AZ::DivideAndRoundUp(srcImage->GetWidth(0), dstFormatInfo->blockWidth) * AZ::DivideAndRoundUp(srcImage->GetHeight(0), dstFormatInfo->blockHeight);
AZ::u32 threadCount = AZStd::min(AZStd::thread::hardware_concurrency()/2, blockCount); AZ::u32 threadCount = AZStd::min(AZStd::thread::hardware_concurrency()/2, blockCount);
status = astcenc_context_alloc(&config, threadCount, &context); status = astcenc_context_alloc(&config, threadCount, &context);
AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status)); AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status));

@ -10,6 +10,8 @@
#include <Processing/PixelFormatInfo.h> #include <Processing/PixelFormatInfo.h>
#include <Processing/DDSHeader.h> #include <Processing/DDSHeader.h>
#include <AzCore/Math/MathUtils.h>
namespace ImageProcessingAtom namespace ImageProcessingAtom
{ {
CPixelFormats* CPixelFormats::s_instance = nullptr; CPixelFormats* CPixelFormats::s_instance = nullptr;
@ -370,11 +372,11 @@ namespace ImageProcessingAtom
{ {
if (outWidth % pFormatInfo->blockWidth != 0) if (outWidth % pFormatInfo->blockWidth != 0)
{ {
outWidth = ((outWidth + pFormatInfo->blockWidth - 1) / pFormatInfo->blockWidth) * pFormatInfo->blockWidth; outWidth = AZ::RoundUpToMultiple(outWidth, pFormatInfo->blockWidth);
} }
if (outHeight % pFormatInfo->blockHeight != 0) if (outHeight % pFormatInfo->blockHeight != 0)
{ {
outHeight = ((outHeight + pFormatInfo->blockHeight - 1) / pFormatInfo->blockHeight) * pFormatInfo->blockHeight; outHeight = AZ::RoundUpToMultiple(outHeight, pFormatInfo->blockHeight);
} }
} }
} }
@ -390,10 +392,11 @@ namespace ImageProcessingAtom
return 0; return 0;
} }
// get number of blocks (ceiling round up for block count) and multiply with bits per block. Divided by 8 to get // get number of blocks and multiply with bits per block. Divided by 8 to get
// final byte size // final byte size
return (((imageWidth + pFormatInfo->blockWidth - 1) / pFormatInfo->blockWidth) * return (AZ::DivideAndRoundUp(imageWidth, pFormatInfo->blockWidth) *
((imageHeight + pFormatInfo->blockHeight - 1) / pFormatInfo->blockHeight) * pFormatInfo->bitsPerBlock) / 8; AZ::DivideAndRoundUp(imageHeight, pFormatInfo->blockHeight) *
pFormatInfo->bitsPerBlock) / 8;
} }
bool CPixelFormats::IsFormatSingleChannel(EPixelFormat fmt) bool CPixelFormats::IsFormatSingleChannel(EPixelFormat fmt)

@ -8,6 +8,7 @@
#pragma once #pragma once
#include <AzCore/base.h> #include <AzCore/base.h>
#include <AzCore/Math/MathUtils.h>
#ifndef AZ_BIT #ifndef AZ_BIT
#define AZ_BIT(x) (1u << x) #define AZ_BIT(x) (1u << x)
@ -237,6 +238,7 @@ namespace AZ
} }
/** /**
* O3DE_DEPRECATION_NOTICE(GHI-7407)
* Returns the value divided by alignment, where the result is rounded up if the remainder is non-zero. * Returns the value divided by alignment, where the result is rounded up if the remainder is non-zero.
*/ */
template <typename T> inline T DivideByMultiple(T value, size_t alignment) template <typename T> inline T DivideByMultiple(T value, size_t alignment)

@ -228,7 +228,7 @@ namespace AZ
{ {
AZStd::span<const uint8_t> constantBytes = GetConstantRaw(inputIndex); AZStd::span<const uint8_t> constantBytes = GetConstantRaw(inputIndex);
const size_t elementSize = sizeof(T); const size_t elementSize = sizeof(T);
const size_t elementCount = DivideByMultiple(constantBytes.size(), elementSize); const size_t elementCount = AZ::DivideAndRoundUp(constantBytes.size(), elementSize);
const size_t sizeInBytes = elementCount * elementSize; const size_t sizeInBytes = elementCount * elementSize;
if (ValidateConstantAccess(inputIndex, ValidateConstantAccessExpect::Complete, 0, sizeInBytes)) if (ValidateConstantAccess(inputIndex, ValidateConstantAccessExpect::Complete, 0, sizeInBytes))
{ {

@ -41,17 +41,17 @@ namespace AZ
uint16_t GetNumberOfGroupsX() const uint16_t GetNumberOfGroupsX() const
{ {
return aznumeric_cast<uint16_t>((m_totalNumberOfThreadsX + m_threadsPerGroupX - 1) / m_threadsPerGroupX); return aznumeric_cast<uint16_t>(DivideAndRoundUp(m_totalNumberOfThreadsX, aznumeric_caster(m_threadsPerGroupX)));
} }
uint16_t GetNumberOfGroupsY() const uint16_t GetNumberOfGroupsY() const
{ {
return aznumeric_cast<uint16_t>((m_totalNumberOfThreadsY + m_threadsPerGroupY - 1) / m_threadsPerGroupY); return aznumeric_cast<uint16_t>(DivideAndRoundUp(m_totalNumberOfThreadsY, aznumeric_caster(m_threadsPerGroupY)));
} }
uint16_t GetNumberOfGroupsZ() const uint16_t GetNumberOfGroupsZ() const
{ {
return aznumeric_cast<uint16_t>((m_totalNumberOfThreadsZ + m_threadsPerGroupZ - 1) / m_threadsPerGroupZ); return aznumeric_cast<uint16_t>(DivideAndRoundUp(m_totalNumberOfThreadsZ, aznumeric_caster(m_threadsPerGroupZ)));
} }
// Different platforms require number of groups or number of threads or both in their Dispatch() call // Different platforms require number of groups or number of threads or both in their Dispatch() call

@ -358,8 +358,7 @@ namespace AZ
} }
else else
{ {
const uint32_t bitsPerPixel = RHI::GetFormatSize(imageFormat) * 8; subresourceLayout.m_bytesPerRow = imageSize.m_width * RHI::GetFormatSize(imageFormat);
subresourceLayout.m_bytesPerRow = (imageSize.m_width * bitsPerPixel + 7) / 8; // round up to nearest byte
subresourceLayout.m_rowCount = imageSize.m_height; subresourceLayout.m_rowCount = imageSize.m_height;
subresourceLayout.m_size.m_width = imageSize.m_width; subresourceLayout.m_size.m_width = imageSize.m_width;
subresourceLayout.m_size.m_height = imageSize.m_height; subresourceLayout.m_size.m_height = imageSize.m_height;

@ -20,7 +20,7 @@ namespace AZ
return DrawListView{}; return DrawListView{};
} }
const size_t itemsPerPartition = DivideByMultiple(drawList.size(), partitionCount); const size_t itemsPerPartition = AZ::DivideAndRoundUp(drawList.size(), partitionCount);
const size_t itemOffset = partitionIndex * itemsPerPartition; const size_t itemOffset = partitionIndex * itemsPerPartition;
const size_t itemCount = AZStd::min(drawList.size() - itemOffset, itemsPerPartition); const size_t itemCount = AZStd::min(drawList.size() - itemOffset, itemsPerPartition);
return DrawListView(&drawList[itemOffset], itemCount); return DrawListView(&drawList[itemOffset], itemCount);

@ -281,7 +281,7 @@ namespace AZ
{ {
srgPool->CompileGroupsBegin(); srgPool->CompileGroupsBegin();
const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount(); const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount();
const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob); const uint32_t jobCount = AZ::DivideAndRoundUp(compilesInPool, compilesPerJob);
AZ::TaskDescriptor srgCompileDesc{"SrgCompile", "Graphics"}; AZ::TaskDescriptor srgCompileDesc{"SrgCompile", "Graphics"};
AZ::TaskDescriptor srgCompileEndDesc{"SrgCompileEnd", "Graphics"}; AZ::TaskDescriptor srgCompileEndDesc{"SrgCompileEnd", "Graphics"};
@ -332,7 +332,7 @@ namespace AZ
const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool) const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool)
{ {
const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount(); const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount();
const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob); const uint32_t jobCount = AZ::DivideAndRoundUp(compilesInPool, compilesPerJob);
for (uint32_t i = 0; i < jobCount; ++i) for (uint32_t i = 0; i < jobCount; ++i)
{ {

@ -100,7 +100,7 @@ namespace AZ
// The frame attachment has tight control over lifecycle here. // The frame attachment has tight control over lifecycle here.
[[maybe_unused]] const bool isAttach = (!m_frameAttachment && frameAttachment); [[maybe_unused]] const bool isAttach = (!m_frameAttachment && frameAttachment);
[[maybe_unused]] const bool isDetach = (m_frameAttachment && !frameAttachment); [[maybe_unused]] const bool isDetach = (m_frameAttachment && !frameAttachment);
AZ_Assert(isAttach || isDetach, "The frame attachment for resource '%s' was not assigned properly."); AZ_Assert(isAttach || isDetach, "The frame attachment for resource '%s' was not assigned properly.", GetName().GetCStr());
} }
m_frameAttachment = frameAttachment; m_frameAttachment = frameAttachment;

@ -92,7 +92,7 @@ namespace AZ
const uint32_t CommandListCostThreshold = const uint32_t CommandListCostThreshold =
AZStd::max( AZStd::max(
m_frameGraphExecuterData.m_commandListCostThresholdMin, m_frameGraphExecuterData.m_commandListCostThresholdMin,
RHI::DivideByMultiple(estimatedItemCount, m_frameGraphExecuterData.m_commandListsPerScopeMax)); AZ::DivideAndRoundUp(estimatedItemCount, m_frameGraphExecuterData.m_commandListsPerScopeMax));
/** /**
* Computes a cost heuristic based on the number of items and number of attachments in * Computes a cost heuristic based on the number of items and number of attachments in
@ -145,7 +145,7 @@ namespace AZ
else else
{ {
// And then create a new group for the current scope with dedicated [1, N] command lists. // And then create a new group for the current scope with dedicated [1, N] command lists.
const uint32_t commandListCount = AZStd::max(RHI::DivideByMultiple(totalScopeCost, CommandListCostThreshold), 1u); const uint32_t commandListCount = AZStd::max(AZ::DivideAndRoundUp(totalScopeCost, CommandListCostThreshold), 1u);
FrameGraphExecuteGroup* scopeContextGroup = AddGroup<FrameGraphExecuteGroup>(); FrameGraphExecuteGroup* scopeContextGroup = AddGroup<FrameGraphExecuteGroup>();
scopeContextGroup->Init(device, scope, commandListCount, GetJobPolicy()); scopeContextGroup->Init(device, scope, commandListCount, GetJobPolicy());

@ -81,7 +81,7 @@ namespace AZ
const uint32_t CommandListCostThreshold = const uint32_t CommandListCostThreshold =
AZStd::max( AZStd::max(
m_frameGraphExecuterData.m_commandListCostThresholdMin, m_frameGraphExecuterData.m_commandListCostThresholdMin,
RHI::DivideByMultiple(estimatedItemCount, m_frameGraphExecuterData.m_commandListsPerScopeMax)); AZ::DivideAndRoundUp(estimatedItemCount, m_frameGraphExecuterData.m_commandListsPerScopeMax));
/** /**
* Computes a cost heuristic based on the number of items and number of attachments in * Computes a cost heuristic based on the number of items and number of attachments in
@ -136,7 +136,7 @@ namespace AZ
else else
{ {
// And then create a new group for the current scope with dedicated [1, N] command lists. // And then create a new group for the current scope with dedicated [1, N] command lists.
const uint32_t commandListCount = AZStd::max(RHI::DivideByMultiple(totalScopeCost, CommandListCostThreshold), 1u); const uint32_t commandListCount = AZStd::max(AZ::DivideAndRoundUp(totalScopeCost, CommandListCostThreshold), 1u);
FrameGraphExecuteGroup* scopeContextGroup = AddGroup<FrameGraphExecuteGroup>(); FrameGraphExecuteGroup* scopeContextGroup = AddGroup<FrameGraphExecuteGroup>();
scopeContextGroup->Init(device, scope, commandListCount, GetJobPolicy()); scopeContextGroup->Init(device, scope, commandListCount, GetJobPolicy());

@ -0,0 +1,72 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#endif
namespace AtomToolsFramework
{
//! This is a specialized inspector widget that populates itself by inspecting document reflected object info.
//! Each element of an AtomToolsDocument object info vector will be displayed in a collapsible RPE group in the inspector.
//! Property changes emitted from each RPE will be tracked and used to signal undo/redo events in the document.
class AtomToolsDocumentInspector
: public InspectorWidget
, public AtomToolsDocumentNotificationBus::Handler
, public AzToolsFramework::IPropertyEditorNotify
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AtomToolsDocumentInspector, AZ::SystemAllocator, 0);
AtomToolsDocumentInspector(const AZ::Crc32& toolId, QWidget* parent = nullptr);
~AtomToolsDocumentInspector() override;
//! Set the ID of the document that will be used to populate the inspector
void SetDocumentId(const AZ::Uuid& documentId);
//! Set a prefix string for storing registry settings
void SetDocumentSettingsPrefix(const AZStd::string& prefix);
using NodeIndicatorFunction = AZStd::function<const char*(const AzToolsFramework::InstanceDataNode*)>;
//! Set a function that will be used to determine what, if any, icon should be displayed next to a property in the inspector
void SetIndicatorFunction(const NodeIndicatorFunction& indicatorFunction);
// InspectorRequestBus::Handler overrides...
void Reset() override;
private:
// AtomToolsDocumentNotificationBus::Handler implementation
void OnDocumentObjectInfoChanged(const AZ::Uuid& documentId, const DocumentObjectInfo& objectInfo, bool rebuilt) override;
// AzToolsFramework::IPropertyEditorNotify overrides...
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override {}
void RequestPropertyContextMenu([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, const QPoint&) override {}
void PropertySelectionChanged([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, bool) override {}
const AZ::Crc32 m_toolId = {};
bool m_editInProgress = {};
AZ::Uuid m_documentId = AZ::Uuid::CreateNull();
NodeIndicatorFunction m_nodeIndicatorFunction;
AZStd::string m_documentSettingsPrefix = "/O3DE/AtomToolsFramework/AtomToolsDocumentInspector";
};
} // namespace AtomToolsFramework

@ -9,8 +9,12 @@
#pragma once #pragma once
#if !defined(Q_MOC_RUN) #if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AtomToolsFramework/Inspector/InspectorRequestBus.h> #include <AtomToolsFramework/Inspector/InspectorRequestBus.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QVBoxLayout> #include <QVBoxLayout>
@ -36,8 +40,10 @@ namespace AtomToolsFramework
Q_OBJECT Q_OBJECT
public: public:
AZ_CLASS_ALLOCATOR(InspectorWidget, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR(InspectorWidget, AZ::SystemAllocator, 0);
AZ_RTTI(AtomToolsFramework::InspectorWidget, "{D77A5F5F-0536-4249-916F-328B272E1AAB}");
static void Reflect(AZ::ReflectContext* context);
explicit InspectorWidget(QWidget* parent = nullptr); InspectorWidget(QWidget* parent = nullptr);
~InspectorWidget() override; ~InspectorWidget() override;
// InspectorRequestBus::Handler overrides... // InspectorRequestBus::Handler overrides...
@ -74,6 +80,8 @@ namespace AtomToolsFramework
void ExpandAll() override; void ExpandAll() override;
void CollapseAll() override; void CollapseAll() override;
void SetGroupSettingsPrefix(const AZStd::string& prefix);
protected: protected:
virtual bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const; virtual bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const;
virtual void OnGroupExpanded(const AZStd::string& groupName); virtual void OnGroupExpanded(const AZStd::string& groupName);
@ -81,14 +89,15 @@ namespace AtomToolsFramework
virtual void OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event); virtual void OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event);
private: private:
QScopedPointer<Ui::InspectorWidget> m_ui;
struct GroupWidgetPair struct GroupWidgetPair
{ {
InspectorGroupHeaderWidget* m_header; InspectorGroupHeaderWidget* m_header;
QWidget* m_panel; QWidget* m_panel;
}; };
QScopedPointer<Ui::InspectorWidget> m_ui;
AZStd::string m_collapsedGroupSettingName;
AZStd::unordered_set<AZ::u32> m_collapsedGroups;
AZStd::unordered_map<AZStd::string, GroupWidgetPair> m_groups; AZStd::unordered_map<AZStd::string, GroupWidgetPair> m_groups;
}; };
} // namespace AtomToolsFramework } // namespace AtomToolsFramework

@ -11,14 +11,8 @@
#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h> #include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertyValue.h> #include <Atom/RPI.Reflect/Material/MaterialPropertyValue.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h> #include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/any.h> #include <AzCore/std/any.h>
namespace AzToolsFramework
{
class InstanceDataNode;
}
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
//! Convert an editor property stored in a AZStd::any into a material property value //! Convert an editor property stored in a AZStd::any into a material property value
@ -52,14 +46,4 @@ namespace AtomToolsFramework
[[maybe_unused]] const AZ::Name& propertyId, [[maybe_unused]] const AZ::Name& propertyId,
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition, const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
AZ::RPI::MaterialPropertyValue& propertyValue); AZ::RPI::MaterialPropertyValue& propertyValue);
//! Generate a file path that is relative to either the source asset root or the export path
//! @param exportPath absolute path of the file being saved
//! @param referencePath absolute path of a file that will be treated as an external reference
//! @param relativeToExportPath specifies if the path is relative to the source asset root or the export path
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath = false);
//! Traverse up the instance data node hierarchy to find the containing dynamic property object
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode);
} // namespace AtomToolsFramework } // namespace AtomToolsFramework

@ -9,9 +9,12 @@
#pragma once #pragma once
#include <AzCore/Asset/AssetManager.h> #include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/PlatformDef.h> #include <AzCore/PlatformDef.h>
#include <AzCore/Settings/SettingsRegistry.h> #include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/any.h>
#include <AzCore/std/containers/vector.h> #include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QFileInfo> #include <QFileInfo>
@ -23,14 +26,6 @@ class QImage;
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
template<typename T>
T GetSettingOrDefault(AZStd::string_view path, const T& defaultValue)
{
T result;
auto settingsRegistry = AZ::SettingsRegistry::Get();
return (settingsRegistry && settingsRegistry->Get(result, path)) ? result : defaultValue;
}
using LoadImageAsyncCallback = AZStd::function<void(const QImage&)>; using LoadImageAsyncCallback = AZStd::function<void(const QImage&)>;
void LoadImageAsync(const AZStd::string& path, LoadImageAsyncCallback callback); void LoadImageAsync(const AZStd::string& path, LoadImageAsyncCallback callback);
@ -39,4 +34,64 @@ namespace AtomToolsFramework
QFileInfo GetUniqueFileInfo(const QString& initialPath); QFileInfo GetUniqueFileInfo(const QString& initialPath);
QFileInfo GetDuplicationFileInfo(const QString& initialPath); QFileInfo GetDuplicationFileInfo(const QString& initialPath);
bool LaunchTool(const QString& baseName, const QStringList& arguments); bool LaunchTool(const QString& baseName, const QStringList& arguments);
//! Generate a file path that is relative to either the source asset root or the export path
//! @param exportPath absolute path of the file being saved
//! @param referencePath absolute path of a file that will be treated as an external reference
//! @param relativeToExportPath specifies if the path is relative to the source asset root or the export path
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath = false);
//! Traverse up the instance data hierarchy to find a node containing the corresponding type
template<typename T>
const T* FindAncestorInstanceDataNodeByType(const AzToolsFramework::InstanceDataNode* pNode)
{
// Traverse up the hierarchy from the input node to search for an instance matching the specified type
for (const auto* currentNode = pNode; currentNode; currentNode = currentNode->GetParent())
{
const auto* context = currentNode->GetSerializeContext();
const auto* classData = currentNode->GetClassMetadata();
if (context && classData)
{
if (context->CanDowncast(classData->m_typeId, azrtti_typeid<T>(), classData->m_azRtti, nullptr))
{
return static_cast<const T*>(currentNode->FirstInstance());
}
}
}
return nullptr;
}
template<typename T>
T GetSettingsValue(AZStd::string_view path, const T& defaultValue = {})
{
T result;
auto settingsRegistry = AZ::SettingsRegistry::Get();
return (settingsRegistry && settingsRegistry->Get(result, path)) ? result : defaultValue;
}
template<typename T>
bool SetSettingsValue(AZStd::string_view path, const T& value)
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
return settingsRegistry && settingsRegistry->Set(path, value);
}
template<typename T>
T GetSettingsObject(AZStd::string_view path, const T& defaultValue = {})
{
T result;
auto settingsRegistry = AZ::SettingsRegistry::Get();
return (settingsRegistry && settingsRegistry->GetObject<T>(result, path)) ? result : defaultValue;
}
template<typename T>
bool SetSettingsObject(AZStd::string_view path, const T& value)
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
return settingsRegistry && settingsRegistry->SetObject<T>(path, value);
}
bool SaveSettingsToFile(const AZ::IO::FixedMaxPath& savePath, const AZStd::vector<AZStd::string>& filters);
} // namespace AtomToolsFramework } // namespace AtomToolsFramework

@ -10,6 +10,7 @@
#include <Atom/RPI.Public/ViewportContext.h> #include <Atom/RPI.Public/ViewportContext.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h> #include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzFramework/Viewport/CameraInput.h> #include <AzFramework/Viewport/CameraInput.h>
#include <AzFramework/Viewport/MultiViewportController.h> #include <AzFramework/Viewport/MultiViewportController.h>
@ -120,6 +121,8 @@ namespace AtomToolsFramework
void SetCameraPivotAttached(const AZ::Vector3& pivot) override; void SetCameraPivotAttached(const AZ::Vector3& pivot) override;
void SetCameraPivotDetached(const AZ::Vector3& pivot) override; void SetCameraPivotDetached(const AZ::Vector3& pivot) override;
void SetCameraOffset(const AZ::Vector3& offset) override; void SetCameraOffset(const AZ::Vector3& offset) override;
bool AddCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs) override;
bool RemoveCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs) override;
private: private:
//! Combine the current camera transform with any potential roll from the tracked //! Combine the current camera transform with any potential roll from the tracked

@ -9,6 +9,7 @@
#pragma once #pragma once
#include <AzCore/EBus/EBus.h> #include <AzCore/EBus/EBus.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzFramework/Viewport/ViewportId.h> #include <AzFramework/Viewport/ViewportId.h>
namespace AZ namespace AZ
@ -16,6 +17,11 @@ namespace AZ
class Transform; class Transform;
} }
namespace AzFramework
{
class CameraInput;
}
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
//! Provides an interface to control the modern viewport camera controller from the Editor. //! Provides an interface to control the modern viewport camera controller from the Editor.
@ -54,6 +60,10 @@ namespace AtomToolsFramework
//! @note The offset value is in the current space of the camera, not world space. Setting //! @note The offset value is in the current space of the camera, not world space. Setting
//! a negative Z value will move the camera backwards from the pivot. //! a negative Z value will move the camera backwards from the pivot.
virtual void SetCameraOffset(const AZ::Vector3& offset) = 0; virtual void SetCameraOffset(const AZ::Vector3& offset) = 0;
//! Add one or more camera inputs (behaviors) to run for the current camera.
virtual bool AddCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs) = 0;
//! Remove one or more camera inputs (behaviors) to stop them running for the current camera.
virtual bool RemoveCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs) = 0;
protected: protected:
~ModularViewportCameraControllerRequests() = default; ~ModularViewportCameraControllerRequests() = default;

@ -29,6 +29,7 @@ namespace AtomToolsFramework
~AtomToolsMainWindow(); ~AtomToolsMainWindow();
protected: protected:
// AtomToolsMainWindowRequestBus::Handler overrides...
void ActivateWindow() override; void ActivateWindow() override;
bool AddDockWidget(const AZStd::string& name, QWidget* widget, uint32_t area, uint32_t orientation) override; bool AddDockWidget(const AZStd::string& name, QWidget* widget, uint32_t area, uint32_t orientation) override;
void RemoveDockWidget(const AZStd::string& name) override; void RemoveDockWidget(const AZStd::string& name) override;

@ -70,9 +70,9 @@ namespace AtomToolsFramework
m_styleManager->initialize(this, engineRootPath); m_styleManager->initialize(this, engineRootPath);
const int updateIntervalWhenActive = const int updateIntervalWhenActive =
aznumeric_cast<int>(GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive", 1)); aznumeric_cast<int>(GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive", 1));
const int updateIntervalWhenNotActive = const int updateIntervalWhenNotActive =
aznumeric_cast<int>(GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive", 64)); aznumeric_cast<int>(GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive", 64));
m_timer.setInterval(updateIntervalWhenActive); m_timer.setInterval(updateIntervalWhenActive);
connect(&m_timer, &QTimer::timeout, this, [this]() connect(&m_timer, &QTimer::timeout, this, [this]()
@ -188,7 +188,7 @@ namespace AtomToolsFramework
Base::StartCommon(systemEntity); Base::StartCommon(systemEntity);
const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false); const bool clearLogFile = GetSettingsValue("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false);
m_traceLogger.OpenLogFile(m_targetName + ".log", clearLogFile); m_traceLogger.OpenLogFile(m_targetName + ".log", clearLogFile);
ConnectToAssetProcessor(); ConnectToAssetProcessor();
@ -197,7 +197,7 @@ namespace AtomToolsFramework
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
const bool enableSourceControl = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/EnableSourceControl", true); const bool enableSourceControl = GetSettingsValue("/O3DE/AtomToolsFramework/Application/EnableSourceControl", true);
AzToolsFramework::SourceControlConnectionRequestBus::Broadcast( AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(
&AzToolsFramework::SourceControlConnectionRequests::EnableSourceControl, enableSourceControl); &AzToolsFramework::SourceControlConnectionRequests::EnableSourceControl, enableSourceControl);
@ -238,6 +238,12 @@ namespace AtomToolsFramework
void AtomToolsApplication::Destroy() void AtomToolsApplication::Destroy()
{ {
m_styleManager.reset(); m_styleManager.reset();
// Save application settings to settings registry file
AZ::IO::FixedMaxPath savePath = AZ::Utils::GetProjectPath();
savePath /= AZStd::string::format("user/Registry/%s.setreg", m_targetName.c_str());
SaveSettingsToFile(savePath, { "/O3DE/AtomToolsFramework", AZStd::string::format("/O3DE/Atom/%s", m_targetName.c_str()) });
UnloadSettings(); UnloadSettings();
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();

@ -106,9 +106,9 @@ namespace AtomToolsFramework
const AZStd::vector<AssetBrowserEntry*> entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets(); const AZStd::vector<AssetBrowserEntry*> entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
const bool promptToOpenMultipleFiles = const bool promptToOpenMultipleFiles =
GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles", true); GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles", true);
const AZ::u64 promptToOpenMultipleFilesThreshold = const AZ::u64 promptToOpenMultipleFilesThreshold =
GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFilesThreshold", 10); GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFilesThreshold", 10);
if (promptToOpenMultipleFiles && promptToOpenMultipleFilesThreshold <= entries.size()) if (promptToOpenMultipleFiles && promptToOpenMultipleFilesThreshold <= entries.size())
{ {

@ -71,11 +71,11 @@ namespace AtomToolsFramework
QListWidgetItem* AssetGridDialog::CreateListItem(const SelectableAsset& selectableAsset) QListWidgetItem* AssetGridDialog::CreateListItem(const SelectableAsset& selectableAsset)
{ {
const int itemBorder = aznumeric_cast<int>( const int itemBorder = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/ItemBorder", 4)); AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetGridDialog/ItemBorder", 4));
const int itemSpacing = aznumeric_cast<int>( const int itemSpacing = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/ItemSpacing", 10)); AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetGridDialog/ItemSpacing", 10));
const int headerHeight = aznumeric_cast<int>( const int headerHeight = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/HeaderHeight", 15)); AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetGridDialog/HeaderHeight", 15));
const QSize gridSize = m_ui->m_assetList->gridSize(); const QSize gridSize = m_ui->m_assetList->gridSize();
m_ui->m_assetList->setGridSize(QSize( m_ui->m_assetList->setGridSize(QSize(

@ -13,6 +13,7 @@
#include <AtomToolsFramework/Document/AtomToolsDocument.h> #include <AtomToolsFramework/Document/AtomToolsDocument.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystem.h> #include <AtomToolsFramework/Document/AtomToolsDocumentSystem.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h> #include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AtomToolsFrameworkSystemComponent.h> #include <AtomToolsFrameworkSystemComponent.h>
namespace AtomToolsFramework namespace AtomToolsFramework
@ -23,6 +24,7 @@ namespace AtomToolsFramework
DynamicPropertyGroup::Reflect(context); DynamicPropertyGroup::Reflect(context);
AtomToolsDocument::Reflect(context); AtomToolsDocument::Reflect(context);
AtomToolsDocumentSystem::Reflect(context); AtomToolsDocumentSystem::Reflect(context);
InspectorWidget::Reflect(context);
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context)) if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
{ {

@ -0,0 +1,120 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AtomToolsFramework/Document/AtomToolsDocumentInspector.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
namespace AtomToolsFramework
{
AtomToolsDocumentInspector::AtomToolsDocumentInspector(const AZ::Crc32& toolId, QWidget* parent)
: InspectorWidget(parent)
, m_toolId(toolId)
{
AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
}
AtomToolsDocumentInspector::~AtomToolsDocumentInspector()
{
AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
}
void AtomToolsDocumentInspector::SetDocumentId(const AZ::Uuid& documentId)
{
AddGroupsBegin();
m_documentId = documentId;
AtomToolsDocumentRequestBus::Event(
m_documentId,
[&](AtomToolsDocumentRequests* documentRequests)
{
if (documentRequests->IsOpen())
{
// Create a unique settings prefix string per document using a CRC of the document path
const AZStd::string groupSettingsPrefix = m_documentSettingsPrefix +
AZStd::string::format("/%08x/GroupSettings", aznumeric_cast<AZ::u32>(AZ::Crc32(documentRequests->GetAbsolutePath())));
SetGroupSettingsPrefix(groupSettingsPrefix);
// This will automatically expose all document contents to an inspector with a collapsible group per object.
// In the case of the material editor, this will be one inspector group per property group.
for (auto& objectInfo : documentRequests->GetObjectInfo())
{
// Passing in same main and comparison instance to enable custom value comparison
const AZ::Crc32 groupSaveStateKey(
AZStd::string::format("%s/%s", groupSettingsPrefix.c_str(), objectInfo.m_name.c_str()));
auto propertyGroupWidget = new InspectorPropertyGroupWidget(
objectInfo.m_objectPtr, objectInfo.m_objectPtr, objectInfo.m_objectType, this, this, groupSaveStateKey, {},
m_nodeIndicatorFunction, 0);
AddGroup(objectInfo.m_name, objectInfo.m_displayName, objectInfo.m_description, propertyGroupWidget);
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
}
}
InspectorRequestBus::Handler::BusConnect(m_documentId);
});
AddGroupsEnd();
}
void AtomToolsDocumentInspector::SetDocumentSettingsPrefix(const AZStd::string& prefix)
{
m_documentSettingsPrefix = prefix;
}
void AtomToolsDocumentInspector::SetIndicatorFunction(const NodeIndicatorFunction& indicatorFunction)
{
m_nodeIndicatorFunction = indicatorFunction;
}
void AtomToolsDocumentInspector::Reset()
{
m_documentId = AZ::Uuid::CreateNull();
m_editInProgress = false;
InspectorWidget::Reset();
}
void AtomToolsDocumentInspector::OnDocumentObjectInfoChanged(
[[maybe_unused]] const AZ::Uuid& documentId, const DocumentObjectInfo& objectInfo, bool rebuilt)
{
if (m_documentId == documentId)
{
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
if (rebuilt)
{
RebuildGroup(objectInfo.m_name);
}
else
{
RefreshGroup(objectInfo.m_name);
}
}
}
void AtomToolsDocumentInspector::BeforePropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
if (!m_editInProgress)
{
m_editInProgress = true;
AtomToolsDocumentRequestBus::Event(m_documentId, &AtomToolsDocumentRequestBus::Events::BeginEdit);
}
}
void AtomToolsDocumentInspector::SetPropertyEditingComplete([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
if (m_editInProgress)
{
m_editInProgress = false;
AtomToolsDocumentRequestBus::Event(m_documentId, &AtomToolsDocumentRequestBus::Events::EndEdit);
}
}
} // namespace AtomToolsFramework
//#include <AtomToolsFramework/Document/moc_AtomToolsDocumentInspector.cpp>

@ -500,4 +500,4 @@ namespace AtomToolsFramework
} }
} // namespace AtomToolsFramework } // namespace AtomToolsFramework
//#include <Document/moc_AtomToolsDocumentMainWindow.cpp> //#include <AtomToolsFramework/Document/moc_AtomToolsDocumentMainWindow.cpp>

@ -368,7 +368,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystem::ReopenDocuments() void AtomToolsDocumentSystem::ReopenDocuments()
{ {
const bool enableHotReload = GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReload", true); const bool enableHotReload = GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReload", true);
if (!enableHotReload) if (!enableHotReload)
{ {
m_documentIdsWithDependencyChanges.clear(); m_documentIdsWithDependencyChanges.clear();
@ -377,7 +377,7 @@ namespace AtomToolsFramework
} }
const bool enableHotReloadPrompts = const bool enableHotReloadPrompts =
GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true); GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true);
for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges) for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges)
{ {

@ -6,27 +6,69 @@
* *
*/ */
#include <QMenu>
#include <QScrollArea>
#include <QScrollBar>
#include <QSizePolicy>
#include <AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h> #include <AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h>
#include <AtomToolsFramework/Inspector/InspectorGroupWidget.h> #include <AtomToolsFramework/Inspector/InspectorGroupWidget.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h> #include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Inspector/ui_InspectorWidget.h> #include <Inspector/ui_InspectorWidget.h>
#include <QMenu>
#include <QScrollArea>
#include <QScrollBar>
#include <QSizePolicy>
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
void InspectorWidget::Reflect(AZ::ReflectContext* context)
{
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->RegisterGenericType<AZStd::unordered_set<AZ::u32>>();
serialize->Class<InspectorWidget>()
->Version(0);
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<InspectorRequestBus>("InspectorRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "atomtools")
->Event("ClearHeading", &InspectorRequestBus::Events::ClearHeading)
->Event("Reset", &InspectorRequestBus::Events::Reset)
->Event("AddGroupsBegin", &InspectorRequestBus::Events::AddGroupsBegin)
->Event("AddGroupsEnd", &InspectorRequestBus::Events::AddGroupsEnd)
->Event("SetGroupVisible", &InspectorRequestBus::Events::SetGroupVisible)
->Event("IsGroupVisible", &InspectorRequestBus::Events::IsGroupVisible)
->Event("IsGroupHidden", &InspectorRequestBus::Events::IsGroupHidden)
->Event("RefreshGroup", &InspectorRequestBus::Events::RefreshGroup)
->Event("RebuildGroup", &InspectorRequestBus::Events::RebuildGroup)
->Event("RefreshAll", &InspectorRequestBus::Events::RefreshAll)
->Event("RebuildAll", &InspectorRequestBus::Events::RebuildAll)
->Event("ExpandGroup", &InspectorRequestBus::Events::ExpandGroup)
->Event("CollapseGroup", &InspectorRequestBus::Events::CollapseGroup)
->Event("IsGroupExpanded", &InspectorRequestBus::Events::IsGroupExpanded)
->Event("ExpandAll", &InspectorRequestBus::Events::ExpandAll)
->Event("CollapseAll", &InspectorRequestBus::Events::CollapseAll)
;
}
}
InspectorWidget::InspectorWidget(QWidget* parent) InspectorWidget::InspectorWidget(QWidget* parent)
: QWidget(parent) : QWidget(parent)
, m_ui(new Ui::InspectorWidget) , m_ui(new Ui::InspectorWidget)
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
SetGroupSettingsPrefix("/O3DE/AtomToolsFramework/InspectorWidget");
} }
InspectorWidget::~InspectorWidget() InspectorWidget::~InspectorWidget()
{ {
SetSettingsObject(m_collapsedGroupSettingName, m_collapsedGroups);
InspectorRequestBus::Handler::BusDisconnect();
} }
void InspectorWidget::AddHeading(QWidget* headingWidget) void InspectorWidget::AddHeading(QWidget* headingWidget)
@ -52,6 +94,7 @@ namespace AtomToolsFramework
delete child; delete child;
} }
m_groups.clear(); m_groups.clear();
InspectorRequestBus::Handler::BusDisconnect();
} }
void InspectorWidget::AddGroupsBegin() void InspectorWidget::AddGroupsBegin()
@ -202,20 +245,31 @@ namespace AtomToolsFramework
} }
} }
void InspectorWidget::SetGroupSettingsPrefix(const AZStd::string& prefix)
{
if (!m_collapsedGroupSettingName.empty())
{
SetSettingsObject(m_collapsedGroupSettingName, m_collapsedGroups);
}
m_collapsedGroupSettingName = prefix + "/CollapsedGroups";
m_collapsedGroups = GetSettingsObject<AZStd::unordered_set<AZ::u32>>(m_collapsedGroupSettingName);
}
bool InspectorWidget::ShouldGroupAutoExpanded(const AZStd::string& groupName) const bool InspectorWidget::ShouldGroupAutoExpanded(const AZStd::string& groupName) const
{ {
AZ_UNUSED(groupName); auto stateItr = m_collapsedGroups.find(AZ::Crc32(groupName));
return true; return stateItr == m_collapsedGroups.end();
} }
void InspectorWidget::OnGroupExpanded(const AZStd::string& groupName) void InspectorWidget::OnGroupExpanded(const AZStd::string& groupName)
{ {
AZ_UNUSED(groupName); m_collapsedGroups.erase(AZ::Crc32(groupName));
} }
void InspectorWidget::OnGroupCollapsed(const AZStd::string& groupName) void InspectorWidget::OnGroupCollapsed(const AZStd::string& groupName)
{ {
AZ_UNUSED(groupName); m_collapsedGroups.insert(AZ::Crc32(groupName));
} }
void InspectorWidget::OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event) void InspectorWidget::OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event)

@ -6,21 +6,18 @@
* *
*/ */
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h> #include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Reflect/Image/ImageAsset.h> #include <Atom/RPI.Reflect/Image/ImageAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h> #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h> #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h> #include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/Math/Color.h> #include <AzCore/Math/Color.h>
#include <AzCore/Math/Vector2.h> #include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h> #include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h> #include <AzCore/Math/Vector4.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
@ -223,51 +220,4 @@ namespace AtomToolsFramework
return true; return true;
} }
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath)
{
if (referencePath.empty())
{
return {};
}
if (!relativeToExportPath)
{
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
return assetInfo.m_relativePath;
}
}
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
return AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
}
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode)
{
// Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property
for (const AzToolsFramework::InstanceDataNode* currentNode = pNode; currentNode; currentNode = currentNode->GetParent())
{
const AZ::SerializeContext* context = currentNode->GetSerializeContext();
const AZ::SerializeContext::ClassData* classData = currentNode->GetClassMetadata();
if (context && classData)
{
if (context->CanDowncast(
classData->m_typeId, azrtti_typeid<AtomToolsFramework::DynamicProperty>(), classData->m_azRtti, nullptr))
{
return static_cast<const AtomToolsFramework::DynamicProperty*>(currentNode->FirstInstance());
}
}
}
return nullptr;
}
} // namespace AtomToolsFramework } // namespace AtomToolsFramework

@ -8,13 +8,18 @@
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingBus.h> #include <Atom/ImageProcessing/ImageProcessingBus.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <AtomToolsFramework/Util/Util.h> #include <AtomToolsFramework/Util/Util.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/SystemFile.h> #include <AzCore/IO/SystemFile.h>
#include <AzCore/Jobs/JobFunction.h> #include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h> #include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h> #include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h> #include <AzFramework/API/ApplicationAPI.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h> #include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h> #include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h> #include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h> #include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
@ -178,4 +183,74 @@ namespace AtomToolsFramework
return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str()); return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str());
} }
}
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath)
{
if (referencePath.empty())
{
return {};
}
if (!relativeToExportPath)
{
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
return assetInfo.m_relativePath;
}
}
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
return AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
}
bool SaveSettingsToFile(const AZ::IO::FixedMaxPath& savePath, const AZStd::vector<AZStd::string>& filters)
{
auto registry = AZ::SettingsRegistry::Get();
if (registry == nullptr)
{
AZ_Warning("AtomToolsFramework", false, "Unable to access global settings registry.");
return false;
}
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
dumperSettings.m_includeFilter = [filters](AZStd::string_view path)
{
for (const auto& filter : filters)
{
if (filter.starts_with(path.substr(0, filter.size())))
{
return true;
}
}
return false;
};
AZStd::string stringBuffer;
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "", stringStream, dumperSettings))
{
AZ_Warning("AtomToolsFramework", false, R"(Unable to save changes to the registry file at "%s"\n)", savePath.c_str());
return false;
}
bool saved = false;
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
if (AZ::IO::SystemFile outputFile; outputFile.Open(savePath.c_str(), configurationMode))
{
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
}
AZ_Warning("AtomToolsFramework", saved, R"(Unable to save registry file to path "%s"\n)", savePath.c_str());
return saved;
}
} // namespace AtomToolsFramework

@ -294,6 +294,17 @@ namespace AtomToolsFramework
m_targetCamera.m_offset = offset; m_targetCamera.m_offset = offset;
} }
bool ModularViewportCameraControllerInstance::AddCameras(const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs)
{
return m_cameraSystem.m_cameras.AddCameras(cameraInputs);
}
bool ModularViewportCameraControllerInstance::RemoveCameras(
const AZStd::vector<AZStd::shared_ptr<AzFramework::CameraInput>>& cameraInputs)
{
return m_cameraSystem.m_cameras.RemoveCameras(cameraInputs);
}
bool ModularViewportCameraControllerInstance::IsInterpolating() const bool ModularViewportCameraControllerInstance::IsInterpolating() const
{ {
return m_cameraMode == CameraMode::Animation; return m_cameraMode == CameraMode::Animation;

@ -8,7 +8,7 @@
#include <AzTest/AzTest.h> #include <AzTest/AzTest.h>
#include <Atom/Utils/TestUtils/AssetSystemStub.h> #include <Atom/Utils/TestUtils/AssetSystemStub.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h> #include <AtomToolsFramework/Util/Util.h>
namespace UnitTest namespace UnitTest
{ {

@ -15,6 +15,7 @@ set(FILES
Include/AtomToolsFramework/Communication/LocalSocket.h Include/AtomToolsFramework/Communication/LocalSocket.h
Include/AtomToolsFramework/Debug/TraceRecorder.h Include/AtomToolsFramework/Debug/TraceRecorder.h
Include/AtomToolsFramework/Document/AtomToolsDocument.h Include/AtomToolsFramework/Document/AtomToolsDocument.h
Include/AtomToolsFramework/Document/AtomToolsDocumentInspector.h
Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h
Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h
Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h
@ -51,6 +52,7 @@ set(FILES
Source/Communication/LocalSocket.cpp Source/Communication/LocalSocket.cpp
Source/Debug/TraceRecorder.cpp Source/Debug/TraceRecorder.cpp
Source/Document/AtomToolsDocument.cpp Source/Document/AtomToolsDocument.cpp
Source/Document/AtomToolsDocumentInspector.cpp
Source/Document/AtomToolsDocumentApplication.cpp Source/Document/AtomToolsDocumentApplication.cpp
Source/Document/AtomToolsDocumentMainWindow.cpp Source/Document/AtomToolsDocumentMainWindow.cpp
Source/Document/AtomToolsDocumentSystem.cpp Source/Document/AtomToolsDocumentSystem.cpp

@ -18,6 +18,7 @@
#include <AtomCore/Instance/Instance.h> #include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h> #include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h> #include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/RTTI/BehaviorContext.h> #include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h> #include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>

@ -64,7 +64,7 @@ namespace MaterialEditor
m_ui->m_materialTypeComboBox->model()->sort(0, Qt::AscendingOrder); m_ui->m_materialTypeComboBox->model()->sort(0, Qt::AscendingOrder);
const AZStd::string defaultMaterialType = AtomToolsFramework::GetSettingOrDefault<AZStd::string>( const AZStd::string defaultMaterialType = AtomToolsFramework::GetSettingsValue<AZStd::string>(
"/O3DE/Atom/MaterialEditor/CreateMaterialDialog/DefaultMaterialType", "StandardPBR"); "/O3DE/Atom/MaterialEditor/CreateMaterialDialog/DefaultMaterialType", "StandardPBR");
const int index = m_ui->m_materialTypeComboBox->findText(defaultMaterialType.c_str()); const int index = m_ui->m_materialTypeComboBox->findText(defaultMaterialType.c_str());

@ -9,6 +9,8 @@
#include <Atom/RPI.Edit/Material/MaterialSourceData.h> #include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h> #include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h> #include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h> #include <AtomToolsFramework/Util/Util.h>
#include <AzQtComponents/Components/StyleManager.h> #include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h> #include <AzQtComponents/Components/WindowDecorationWrapper.h>
@ -17,7 +19,6 @@
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h> #include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
#include <Window/MaterialEditorWindow.h> #include <Window/MaterialEditorWindow.h>
#include <Window/MaterialEditorWindowSettings.h> #include <Window/MaterialEditorWindowSettings.h>
#include <Window/MaterialInspector/MaterialInspector.h>
#include <Window/SettingsDialog/SettingsDialog.h> #include <Window/SettingsDialog/SettingsDialog.h>
#include <Window/ViewportSettingsInspector/ViewportSettingsInspector.h> #include <Window/ViewportSettingsInspector/ViewportSettingsInspector.h>
@ -78,7 +79,20 @@ namespace MaterialEditor
QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str())); QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str()));
}); });
AddDockWidget("Inspector", new MaterialInspector(m_toolId), Qt::RightDockWidgetArea, Qt::Vertical); m_materialInspector = new AtomToolsFramework::AtomToolsDocumentInspector(m_toolId, this);
m_materialInspector->SetDocumentSettingsPrefix("/O3DE/Atom/MaterialEditor/MaterialInspector");
m_materialInspector->SetIndicatorFunction(
[](const AzToolsFramework::InstanceDataNode* node)
{
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(node);
if (property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue))
{
return ":/Icons/changed_property.svg";
}
return ":/Icons/blank.png";
});
AddDockWidget("Inspector", m_materialInspector, Qt::RightDockWidgetArea, Qt::Vertical);
AddDockWidget("Viewport Settings", new ViewportSettingsInspector, Qt::LeftDockWidgetArea, Qt::Vertical); AddDockWidget("Viewport Settings", new ViewportSettingsInspector, Qt::LeftDockWidgetArea, Qt::Vertical);
SetDockWidgetVisible("Viewport Settings", false); SetDockWidgetVisible("Viewport Settings", false);
@ -98,6 +112,12 @@ namespace MaterialEditor
OnDocumentOpened(AZ::Uuid::CreateNull()); OnDocumentOpened(AZ::Uuid::CreateNull());
} }
void MaterialEditorWindow::OnDocumentOpened(const AZ::Uuid& documentId)
{
Base::OnDocumentOpened(documentId);
m_materialInspector->SetDocumentId(documentId);
}
void MaterialEditorWindow::ResizeViewportRenderTarget(uint32_t width, uint32_t height) void MaterialEditorWindow::ResizeViewportRenderTarget(uint32_t width, uint32_t height)
{ {
QSize requestedViewportSize = QSize(width, height) / devicePixelRatioF(); QSize requestedViewportSize = QSize(width, height) / devicePixelRatioF();

@ -9,6 +9,7 @@
#pragma once #pragma once
#if !defined(Q_MOC_RUN) #if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentInspector.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h> #include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
@ -35,10 +36,15 @@ namespace MaterialEditor
MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent = 0); MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent = 0);
protected: protected:
// AtomToolsFramework::AtomToolsMainWindowRequestBus::Handler overrides...
void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override; void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override;
void LockViewportRenderTargetSize(uint32_t width, uint32_t height) override; void LockViewportRenderTargetSize(uint32_t width, uint32_t height) override;
void UnlockViewportRenderTargetSize() override; void UnlockViewportRenderTargetSize() override;
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// AtomToolsFramework::AtomToolsDocumentMainWindow overrides...
bool GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath) override; bool GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath) override;
bool GetOpenDocumentParams(AZStd::string& openPath) override; bool GetOpenDocumentParams(AZStd::string& openPath) override;
void OpenSettings() override; void OpenSettings() override;
@ -47,6 +53,7 @@ namespace MaterialEditor
void closeEvent(QCloseEvent* closeEvent) override; void closeEvent(QCloseEvent* closeEvent) override;
AtomToolsFramework::AtomToolsDocumentInspector* m_materialInspector = {};
MaterialViewportWidget* m_materialViewport = {}; MaterialViewportWidget* m_materialViewport = {};
MaterialEditorToolBar* m_toolBar = {}; MaterialEditorToolBar* m_toolBar = {};
}; };

@ -19,7 +19,6 @@ namespace MaterialEditor
serializeContext->Class<MaterialEditorWindowSettings, AZ::UserSettings>() serializeContext->Class<MaterialEditorWindowSettings, AZ::UserSettings>()
->Version(1) ->Version(1)
->Field("mainWindowState", &MaterialEditorWindowSettings::m_mainWindowState) ->Field("mainWindowState", &MaterialEditorWindowSettings::m_mainWindowState)
->Field("inspectorCollapsedGroups", &MaterialEditorWindowSettings::m_inspectorCollapsedGroups)
; ;
if (auto editContext = serializeContext->GetEditContext()) if (auto editContext = serializeContext->GetEditContext())

@ -27,6 +27,5 @@ namespace MaterialEditor
static void Reflect(AZ::ReflectContext* context); static void Reflect(AZ::ReflectContext* context);
AZStd::vector<char> m_mainWindowState; AZStd::vector<char> m_mainWindowState;
AZStd::unordered_set<AZ::u32> m_inspectorCollapsedGroups;
}; };
} // namespace MaterialEditor } // namespace MaterialEditor

@ -1,159 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <Window/MaterialInspector/MaterialInspector.h>
namespace MaterialEditor
{
MaterialInspector::MaterialInspector(const AZ::Crc32& toolId, QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
, m_toolId(toolId)
{
m_windowSettings = AZ::UserSettings::CreateFind<MaterialEditorWindowSettings>(
AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL);
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
}
MaterialInspector::~MaterialInspector()
{
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void MaterialInspector::Reset()
{
m_documentPath.clear();
m_documentId = AZ::Uuid::CreateNull();
m_activeProperty = {};
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
bool MaterialInspector::ShouldGroupAutoExpanded(const AZStd::string& groupName) const
{
auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupName));
return stateItr == m_windowSettings->m_inspectorCollapsedGroups.end();
}
void MaterialInspector::OnGroupExpanded(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupName));
}
void MaterialInspector::OnGroupCollapsed(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupName));
}
void MaterialInspector::OnDocumentOpened(const AZ::Uuid& documentId)
{
AddGroupsBegin();
m_documentId = documentId;
bool isOpen = false;
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
isOpen, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsOpen);
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
m_documentPath, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
if (!m_documentId.IsNull() && isOpen)
{
// This will automatically expose all document contents to an inspector with a collapsible group per object. In the case of the
// material editor, this will be one inspector group per property group.
AZStd::vector<AtomToolsFramework::DocumentObjectInfo> objects;
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
objects, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetObjectInfo);
for (auto& objectInfo : objects)
{
// Passing in same main and comparison instance to enable custom value comparison for highlighting modified properties
auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget(
objectInfo.m_objectPtr, objectInfo.m_objectPtr, objectInfo.m_objectType, this, this,
GetGroupSaveStateKey(objectInfo.m_name), {},
[this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0);
AddGroup(objectInfo.m_name, objectInfo.m_displayName, objectInfo.m_description, propertyGroupWidget);
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
}
AtomToolsFramework::InspectorRequestBus::Handler::BusConnect(m_documentId);
}
AddGroupsEnd();
}
AZ::Crc32 MaterialInspector::GetGroupSaveStateKey(const AZStd::string& groupName) const
{
return AZ::Crc32(AZStd::string::format("MaterialInspector::PropertyGroup::%s::%s", m_documentPath.c_str(), groupName.c_str()));
}
bool MaterialInspector::IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const
{
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(node);
return property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
}
const char* MaterialInspector::GetInstanceNodePropertyIndicator(const AzToolsFramework::InstanceDataNode* node) const
{
if (IsInstanceNodePropertyModifed(node))
{
return ":/Icons/changed_property.svg";
}
return ":/Icons/blank.png";
}
void MaterialInspector::OnDocumentObjectInfoChanged(
[[maybe_unused]] const AZ::Uuid& documentId, const AtomToolsFramework::DocumentObjectInfo& objectInfo, bool rebuilt)
{
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
if (rebuilt)
{
RebuildGroup(objectInfo.m_name);
}
else
{
RefreshGroup(objectInfo.m_name);
}
}
void MaterialInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
// This function is called before every single property change whether it's a button click or dragging a slider. We only want to
// begin tracking undo state for the first change in the sequence, when the user begins to drag the slider.
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
if (!m_activeProperty && property)
{
m_activeProperty = property;
AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::BeginEdit);
}
}
void MaterialInspector::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode)
{
// If tracking has started and editing has completed then we can stop tracking undue state for this sequence of changes.
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
if (m_activeProperty && m_activeProperty == property)
{
AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::EndEdit);
m_activeProperty = {};
}
}
} // namespace MaterialEditor
#include <Window/MaterialInspector/moc_MaterialInspector.cpp>

@ -1,71 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <Window/MaterialEditorWindowSettings.h>
#endif
namespace MaterialEditor
{
//! Provides controls for viewing and editing document settings.
class MaterialInspector
: public AtomToolsFramework::InspectorWidget
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
, public AzToolsFramework::IPropertyEditorNotify
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MaterialInspector, AZ::SystemAllocator, 0);
MaterialInspector(const AZ::Crc32& toolId, QWidget* parent = nullptr);
~MaterialInspector() override;
// AtomToolsFramework::InspectorRequestBus::Handler overrides...
void Reset() override;
protected:
bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const override;
void OnGroupExpanded(const AZStd::string& groupName) override;
void OnGroupCollapsed(const AZStd::string& groupName) override;
private:
AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const;
bool IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const;
const char* GetInstanceNodePropertyIndicator(const AzToolsFramework::InstanceDataNode* node) const;
// AtomToolsDocumentNotificationBus::Handler implementation
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OnDocumentObjectInfoChanged(
const AZ::Uuid& documentId, const AtomToolsFramework::DocumentObjectInfo& objectInfo, bool rebuilt) override;
// AzToolsFramework::IPropertyEditorNotify overrides...
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override {}
void RequestPropertyContextMenu([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, const QPoint&) override {}
void PropertySelectionChanged([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, bool) override {}
const AZ::Crc32 m_toolId = {};
// Tracking the property that is activiley being edited in the inspector
const AtomToolsFramework::DynamicProperty* m_activeProperty = {};
AZ::Uuid m_documentId = AZ::Uuid::CreateNull();
AZStd::string m_documentPath;
AZStd::intrusive_ptr<MaterialEditorWindowSettings> m_windowSettings;
};
} // namespace MaterialEditor

@ -6,19 +6,19 @@
* *
*/ */
#include <Window/SettingsDialog/SettingsWidget.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h> #include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <Window/SettingsDialog/SettingsWidget.h>
namespace MaterialEditor namespace MaterialEditor
{ {
SettingsWidget::SettingsWidget(QWidget* parent) SettingsWidget::SettingsWidget(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent) : AtomToolsFramework::InspectorWidget(parent)
{ {
SetGroupSettingsPrefix("/O3DE/Atom/MaterialEditor/SettingsWidget");
} }
SettingsWidget::~SettingsWidget() SettingsWidget::~SettingsWidget()
{ {
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
} }
void SettingsWidget::Populate() void SettingsWidget::Populate()
@ -29,7 +29,6 @@ namespace MaterialEditor
void SettingsWidget::Reset() void SettingsWidget::Reset()
{ {
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset(); AtomToolsFramework::InspectorWidget::Reset();
} }
} // namespace MaterialEditor } // namespace MaterialEditor

@ -32,9 +32,7 @@ namespace MaterialEditor
m_viewportSettings = m_viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL); AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
m_windowSettings = AZ::UserSettings::CreateFind<MaterialEditorWindowSettings>( SetGroupSettingsPrefix("/O3DE/Atom/MaterialEditor/ViewportSettingsInspector");
AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL);
MaterialViewportNotificationBus::Handler::BusConnect(); MaterialViewportNotificationBus::Handler::BusConnect();
} }
@ -43,7 +41,6 @@ namespace MaterialEditor
m_lightingPreset.reset(); m_lightingPreset.reset();
m_modelPreset.reset(); m_modelPreset.reset();
MaterialViewportNotificationBus::Handler::BusDisconnect(); MaterialViewportNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
} }
void ViewportSettingsInspector::Populate() void ViewportSettingsInspector::Populate()
@ -144,7 +141,7 @@ namespace MaterialEditor
}); });
const int itemSize = aznumeric_cast<int>( const int itemSize = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/ModelItemSize", 180)); AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/ModelItemSize", 180));
AtomToolsFramework::AssetGridDialog dialog( AtomToolsFramework::AssetGridDialog dialog(
"Model Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow()); "Model Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow());
@ -270,7 +267,7 @@ namespace MaterialEditor
}); });
const int itemSize = aznumeric_cast<int>( const int itemSize = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/LightingItemSize", 180)); AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/LightingItemSize", 180));
AtomToolsFramework::AssetGridDialog dialog( AtomToolsFramework::AssetGridDialog dialog(
"Lighting Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow()); "Lighting Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow());
@ -338,7 +335,6 @@ namespace MaterialEditor
m_viewportSettings->m_displayMapperOperationType = viewportRequests->GetDisplayMapperOperationType(); m_viewportSettings->m_displayMapperOperationType = viewportRequests->GetDisplayMapperOperationType();
}); });
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset(); AtomToolsFramework::InspectorWidget::Reset();
} }
@ -433,23 +429,6 @@ namespace MaterialEditor
{ {
return AZ::Crc32(AZStd::string::format("ViewportSettingsInspector::PropertyGroup::%s", groupName.c_str())); return AZ::Crc32(AZStd::string::format("ViewportSettingsInspector::PropertyGroup::%s", groupName.c_str()));
} }
bool ViewportSettingsInspector::ShouldGroupAutoExpanded(const AZStd::string& groupName) const
{
auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupName));
return stateItr == m_windowSettings->m_inspectorCollapsedGroups.end();
}
void ViewportSettingsInspector::OnGroupExpanded(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupName));
}
void ViewportSettingsInspector::OnGroupCollapsed(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupName));
}
} // namespace MaterialEditor } // namespace MaterialEditor
#include <Window/ViewportSettingsInspector/moc_ViewportSettingsInspector.cpp> #include <Window/ViewportSettingsInspector/moc_ViewportSettingsInspector.cpp>

@ -16,7 +16,6 @@
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h> #include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <Viewport/MaterialViewportNotificationBus.h> #include <Viewport/MaterialViewportNotificationBus.h>
#include <Viewport/MaterialViewportSettings.h> #include <Viewport/MaterialViewportSettings.h>
#include <Window/MaterialEditorWindowSettings.h>
#endif #endif
namespace MaterialEditor namespace MaterialEditor
@ -75,13 +74,9 @@ namespace MaterialEditor
AZStd::string GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) const; AZStd::string GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) const;
AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const; AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const;
bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const override;
void OnGroupExpanded(const AZStd::string& groupName) override;
void OnGroupCollapsed(const AZStd::string& groupName) override;
AZ::Render::ModelPresetPtr m_modelPreset; AZ::Render::ModelPresetPtr m_modelPreset;
AZ::Render::LightingPresetPtr m_lightingPreset; AZ::Render::LightingPresetPtr m_lightingPreset;
AZStd::intrusive_ptr<MaterialViewportSettings> m_viewportSettings; AZStd::intrusive_ptr<MaterialViewportSettings> m_viewportSettings;
AZStd::intrusive_ptr<MaterialEditorWindowSettings> m_windowSettings;
}; };
} // namespace MaterialEditor } // namespace MaterialEditor

@ -46,8 +46,6 @@ set(FILES
Source/Window/ToolBar/ModelPresetComboBox.cpp Source/Window/ToolBar/ModelPresetComboBox.cpp
Source/Window/ToolBar/LightingPresetComboBox.h Source/Window/ToolBar/LightingPresetComboBox.h
Source/Window/ToolBar/LightingPresetComboBox.cpp Source/Window/ToolBar/LightingPresetComboBox.cpp
Source/Window/MaterialInspector/MaterialInspector.h
Source/Window/MaterialInspector/MaterialInspector.cpp
Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h
Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp
) )

@ -14,8 +14,11 @@
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h> #include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.h> #include <Atom/RPI.Edit/Material/MaterialUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h> #include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h> #include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h> #include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h> #include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h> #include <AtomToolsFramework/Util/Util.h>
@ -24,9 +27,6 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h> #include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h> #include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QApplication> #include <QApplication>
@ -54,7 +54,6 @@ namespace AZ
MaterialPropertyInspector::~MaterialPropertyInspector() MaterialPropertyInspector::~MaterialPropertyInspector()
{ {
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect(); AZ::EntitySystemBus::Handler::BusDisconnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect(); EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
@ -137,7 +136,6 @@ namespace AZ
m_dirtyPropertyFlags.set(); m_dirtyPropertyFlags.set();
m_internalEditNotification = {}; m_internalEditNotification = {};
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset(); AtomToolsFramework::InspectorWidget::Reset();
} }
@ -590,7 +588,7 @@ namespace AZ
bool MaterialPropertyInspector::IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const bool MaterialPropertyInspector::IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const
{ {
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(node); const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(node);
return property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue); return property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
} }
@ -719,7 +717,7 @@ namespace AZ
// This function is called continuously anytime a property changes until the edit has completed // This function is called continuously anytime a property changes until the edit has completed
// Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has // Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has
// started and ended // started and ended
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode); const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property) if (property)
{ {
if (m_activeProperty != property) if (m_activeProperty != property)
@ -731,7 +729,7 @@ namespace AZ
void MaterialPropertyInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) void MaterialPropertyInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{ {
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode); const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property) if (property)
{ {
if (m_activeProperty == property) if (m_activeProperty == property)
@ -748,7 +746,7 @@ namespace AZ
// As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been // As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been
// completed but they are not being called following that pattern. when this function executes the changes to the property // completed but they are not being called following that pattern. when this function executes the changes to the property
// are ready to be committed or reverted // are ready to be committed or reverted
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode); const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property) if (property)
{ {
if (m_activeProperty == property) if (m_activeProperty == property)

@ -15,10 +15,11 @@
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h> #include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.h> #include <Atom/RPI.Edit/Material/MaterialUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h> #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h> #include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h> #include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h> #include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzFramework/API/ApplicationAPI.h> #include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h> #include <AzToolsFramework/API/ToolsApplicationAPI.h>

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

Loading…
Cancel
Save