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.
import pytest
import sys
import os
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSharedTest
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_WhiteBox_AddComponentToEntity(self, request, workspace, editor, launcher_platform):
class TestAutomation(EditorTestSuite):
class test_WhiteBox_AddComponentToEntity(EditorSharedTest):
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
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
self._run_test(request, workspace, editor, test_module)

@ -18,21 +18,17 @@ class Tests():
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.editor as editor
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
import WhiteBoxInit as init
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Report
# open level
helper.init_idle()
helper.open_level("WhiteBox", "EmptyLevel", no_prompt=False)
hydra.open_base_level()
# create white box entity and attach component
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)
Report.result(Tests.white_box_component_enabled, component_enabled)
# close editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
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")
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.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 TestHelper as helper
def check_shape_result(success_fail_tuple, condition):
result = Report.result(success_fail_tuple, condition)
@ -53,8 +49,7 @@ def C29279329_WhiteBox_SetDefaultShape():
}
# open level
helper.init_idle()
helper.open_level("WhiteBox", "EmptyLevel", no_prompt=False)
hydra.open_base_level()
# create white box entity and attach component
white_box_entity = init.create_white_box_entity()
@ -95,10 +90,6 @@ def C29279329_WhiteBox_SetDefaultShape():
Tests.default_shape_sphere,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.SPHERE))
# close editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
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
# 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
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.editor as editor
import azlmbr.entity as entity
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 TestHelper as helper
# open level
helper.init_idle()
helper.open_level("WhiteBox", "EmptyLevel", no_prompt=False)
hydra.open_base_level()
white_box_entity_name = 'WhiteBox-Visibility'
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)
Report.result(Tests.white_box_set_to_invisible, not visible_property)
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
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/std/smart_ptr/make_shared.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/Console/IConsole.h>
// AzFramework
#include <AzFramework/Components/CameraBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/ProjectManager/ProjectManager.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
@ -104,7 +105,6 @@ AZ_POP_DISABLE_WARNING
#include "WaitProgress.h"
#include "ToolBox.h"
#include "LevelInfo.h"
#include "EditorPreferencesDialog.h"
#include "AnimationContext.h"
@ -422,7 +422,6 @@ void CCryEditApp::RegisterActionHandlers()
ON_COMMAND(ID_DISPLAY_GOTOPOSITION, OnDisplayGotoPosition)
ON_COMMAND(ID_FILE_SAVELEVELRESOURCES, OnFileSavelevelresources)
ON_COMMAND(ID_CLEAR_REGISTRY, OnClearRegistryData)
ON_COMMAND(ID_VALIDATELEVEL, OnValidatelevel)
ON_COMMAND(ID_TOOLS_PREFERENCES, OnToolsPreferences)
ON_COMMAND(ID_SWITCHCAMERA_DEFAULTCAMERA, OnSwitchToDefaultCamera)
ON_COMMAND(ID_SWITCHCAMERA_SEQUENCECAMERA, OnSwitchToSequenceCamera)
@ -770,28 +769,6 @@ QString CCryEditApp::ShowWelcomeDialog()
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.
//////////////////////////////////////////////////////////////////////////
@ -1504,7 +1481,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
// We support specifying multiple files in the cmdline by separating them with ';'
AZStd::vector<AZStd::string_view> fileList;
AzFramework::StringFunc::TokenizeVisitor(
AZ::StringFunc::TokenizeVisitor(
fileStr.constData(),
[&fileList](AZStd::string_view elem)
{
@ -1516,7 +1493,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
{
QByteArray pythonArgsStr = cmdInfo.m_pythonArgs.toUtf8();
AZStd::vector<AZStd::string_view> pythonArgs;
AzFramework::StringFunc::TokenizeVisitor(pythonArgsStr.constData(),
AZ::StringFunc::TokenizeVisitor(pythonArgsStr.constData(),
[&pythonArgs](AZStd::string_view elem)
{
pythonArgs.push_back(elem);
@ -1531,7 +1508,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
testcaseList.resize(fileList.size());
{
int i = 0;
AzFramework::StringFunc::TokenizeVisitor(
AZ::StringFunc::TokenizeVisitor(
pythonTestCase.constData(),
[&i, &testcaseList](AZStd::string_view elem)
{
@ -1595,7 +1572,6 @@ bool CCryEditApp::InitInstance()
{
QElapsedTimer startupTimer;
startupTimer.start();
InitDirectory();
// create / attach to the environment:
AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance());
@ -1606,8 +1582,6 @@ bool CCryEditApp::InitInstance()
InitFromCommandLine(cmdInfo);
InitDirectory();
qobject_cast<Editor::EditorQtApplication*>(qApp)->Initialize(); // Must be done after CEditorImpl() is created
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()
{
@ -3827,131 +3793,68 @@ CMainFrame * CCryEditApp::GetMainFrame() const
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)));
tokens.push_back(newElement);
void CCryEditApp::OpenLUAEditor(const char* files)
{
AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath();
currentStr = currentStr.substr(secondQuotePos + 1);
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
firstQuotePos = AZStd::string::npos;
secondQuotePos = 0;
continue;
}
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;
}
}
}
AZStd::string filename = "LuaIDE";
AZ::IO::FixedMaxPath executablePath = AZ::Utils::GetExecutableDirectory();
executablePath /= filename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION;
QStringList argsList;
for (const auto& arg : tokens)
if (!AZ::IO::SystemFile::Exists(executablePath.c_str()))
{
argsList.push_back(QString(arg.c_str()));
AZ_Error("LuaIDE", false, "%s not found", executablePath.c_str());
return;
}
// Launch the process
[[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;
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
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, '|');
for (const auto& file : tokens)
auto ParseFilesList = [&launchCmd](AZStd::string_view filePath)
{
bool fullPathFound = false;
auto GetFullSourcePath = [&launchCmd, &filePath, &fullPathFound]
(AzToolsFramework::AssetSystem::AssetSystemRequest* assetSystemRequests)
{
char resolved[AZ_MAX_PATH_LEN];
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))
AZ::IO::Path assetFullPath;
if(assetSystemRequests->GetFullSourcePathFromRelativeProductPath(filePath, assetFullPath.Native()))
{
AZStd::string current = '\"' + AZStd::string(resolved) + '\"';
AZStd::replace(current.begin(), current.end(), '\\', '/');
resolvedPaths.push_back(current);
fullPathFound = true;
launchCmd.emplace_back("--files");
launchCmd.emplace_back(AZStd::move(assetFullPath.Native()));
}
}
if (!resolvedPaths.empty())
{
for (const auto& resolvedPath : resolvedPaths)
};
AzToolsFramework::AssetSystemRequestBus::Broadcast(AZStd::move(GetFullSourcePath));
// If the full source path could be found through the Asset System, then
// attempt to resolve the path using the FileIO instance
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::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
};
AZ::StringFunc::TokenizeVisitor(files, ParseFilesList, "|");
AZStd::string process = AZStd::string::format("%s%.*s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "LuaIDE"
#if defined(AZ_PLATFORM_WINDOWS)
".exe"
#endif
"%s", argumentQuoteString, aznumeric_cast<int>(exePath.size()), exePath.data(), argumentQuoteString);
processLaunchInfo.m_commandlineParameters = AZStd::move(launchCmd);
AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot.c_str());
StartProcessDetached(process.c_str(), processArgs.c_str());
AZ_VerifyError("LuaIDE", AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo),
"Lua IDE has failed to launch at path %s", executablePath.c_str());
}
void CCryEditApp::PrintAlways(const AZStd::string& output)
@ -4065,11 +3968,6 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
gSettings.Connect();
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
// on Windows 10

@ -138,7 +138,6 @@ public:
RecentFileList* GetRecentFileList();
virtual void AddToRecentFileList(const QString& lpszPathName);
ECreateLevelResult CreateLevel(const QString& levelName, QString& fullyQualifiedLevelName);
static void InitDirectory();
bool FirstInstance(bool bForceNewInstance = false);
void InitFromCommandLine(CEditCommandLineInfo& cmdInfo);
bool CheckIfAlreadyRunning();
@ -159,11 +158,6 @@ public:
// Print to stdout even if there out has been redirected
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
//! \param files A space separated list of aliased paths
void OpenLUAEditor(const char* files);
@ -399,7 +393,6 @@ private:
void OnDisplayGotoPosition();
void OnFileSavelevelresources();
void OnClearRegistryData();
void OnValidatelevel();
void OnToolsPreferences();
void OnSwitchToDefaultCamera();
void OnUpdateSwitchToDefaultCamera(QAction* action);

@ -260,11 +260,6 @@ namespace
namespace
{
void PyStartProcessDetached(const char* process, const char* args)
{
CCryEditApp::instance()->StartProcessDetached(process, args);
}
void PyLaunchLUAEditor(const char* 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_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("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger"));

@ -287,16 +287,16 @@ namespace SandboxEditor
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();
};
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();
};
@ -326,8 +326,8 @@ namespace SandboxEditor
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyMoveCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitScrollDollyCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitMotionDollyCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitFocusCamera);
}
@ -344,7 +344,7 @@ namespace SandboxEditor
m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId());
m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId());
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
m_orbitMotionDollyCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
m_orbitFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
}

@ -56,8 +56,8 @@ namespace SandboxEditor
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_orbitRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_orbitTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyMotionCameraInput> m_orbitDollyMoveCamera;
AZStd::shared_ptr<AzFramework::OrbitScrollDollyCameraInput> m_orbitScrollDollyCamera;
AZStd::shared_ptr<AzFramework::OrbitMotionDollyCameraInput> m_orbitMotionDollyCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera;
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);
// 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.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
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_wait") != 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());
}

@ -1011,9 +1011,6 @@ void MainWindow::InitActions()
am->AddAction(ID_TOOLS_ENABLEFILECHANGEMONITORING, tr("Enable File Change Monitoring"));
am->AddAction(ID_CLEAR_REGISTRY, 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 =
am->AddAction(ID_TOOLS_LOGMEMORYUSAGE, tr("Save Level Statistics"))
.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/Slice/SliceRequestBus.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include <AzToolsFramework/Undo/UndoSystem.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Layer/AddToLayerMenu.h>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/ViewportSelection/EditorHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.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().");
AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusConnect();
m_contextMenuBottomHandler.Setup();
}
void SandboxIntegrationManager::SaveSlice(const bool& QuickPushToFirstLevel)
@ -395,6 +397,8 @@ void SandboxIntegrationManager::GetEntitiesInSlices(
void SandboxIntegrationManager::Teardown()
{
m_contextMenuBottomHandler.Teardown();
AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusDisconnect();
AzFramework::DisplayContextRequestBus::Handler::BusDisconnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
@ -651,12 +655,14 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
if (selected.size() == 0)
{
action = menu->addAction(QObject::tr("Create entity"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_N));
QObject::connect(
action, &QAction::triggered, action,
[this]
{
ContextMenu_NewEntity();
});
}
);
}
// when a single entity is selected, entity is created as its child
else if (selected.size() == 1)
@ -667,6 +673,7 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selectedEntityId) && !selectedEntityIsReadOnly))
{
action = menu->addAction(QObject::tr("Create entity"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_N));
QObject::connect(
action, &QAction::triggered, action,
[selectedEntityId]
@ -694,33 +701,30 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
AzToolsFramework::SetupAddToLayerMenu(menu, flattenedSelection, [this] { return ContextMenu_NewLayer(); });
SetupSliceContextMenu(menu);
}
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)
if (!selected.empty())
{
AZ::EntityId parentEntityId;
AZ::TransformBus::EventResult(parentEntityId, entityId, &AZ::TransformBus::Events::GetParentId);
if (parentEntityId.IsValid() && m_readOnlyEntityPublicInterface->IsReadOnly(parentEntityId))
// 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 : selected)
{
selectionContainsDescendantOfReadOnlyEntity = true;
break;
AZ::EntityId parentEntityId;
AZ::TransformBus::EventResult(parentEntityId, entityId, &AZ::TransformBus::Events::GetParentId);
if (parentEntityId.IsValid() && m_readOnlyEntityPublicInterface->IsReadOnly(parentEntityId))
{
selectionContainsDescendantOfReadOnlyEntity = true;
break;
}
}
}
if (!selectionContainsDescendantOfReadOnlyEntity)
{
action = menu->addAction(QObject::tr("Duplicate"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); });
if (!selectionContainsDescendantOfReadOnlyEntity)
{
action = menu->addAction(QObject::tr("Duplicate"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); });
}
}
}
if (!prefabSystemEnabled)
{
action = menu->addAction(QObject::tr("Delete"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_DeleteSelected(); });
if (selected.size() == 0)
@ -733,20 +737,14 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
if (selected.size() > 0)
{
action = menu->addAction(QObject::tr("Open pinned Inspector"));
QObject::connect(action, &QAction::triggered, action, [this, selected]
{
AzToolsFramework::EntityIdSet pinnedEntities(selected.begin(), selected.end());
OpenPinnedInspector(pinnedEntities);
});
if (selected.size() > 0)
{
action = menu->addAction(QObject::tr("Find in Entity Outliner"));
QObject::connect(action, &QAction::triggered, [selected]
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();
}
}

@ -9,6 +9,8 @@
#ifndef CRYINCLUDE_COMPONENTENTITYEDITORPLUGIN_SANDBOXINTEGRATION_H
#define CRYINCLUDE_COMPONENTENTITYEDITORPLUGIN_SANDBOXINTEGRATION_H
#include "ContextMenuHandlers.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Slice/SliceBus.h>
#include <AzCore/Slice/SliceComponent.h>
@ -273,6 +275,8 @@ private:
};
private:
ContextMenuBottomHandler m_contextMenuBottomHandler;
AZ::Vector2 m_contextMenuViewPoint;
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
ComponentEntityEditorPlugin.h
ComponentEntityEditorPlugin.cpp
ContextMenuHandlers.h
ContextMenuHandlers.cpp
SandboxIntegration.h
SandboxIntegration.cpp
UI/QComponentEntityEditorMainWindow.h

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

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

@ -9,6 +9,7 @@
#pragma once
#include <AzCore/base.h>
#include <AzCore/std/limits.h>
#include <AzCore/std/math.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_integral.h>
@ -20,6 +21,7 @@
#include <limits>
#include <math.h>
#include <utility>
#include <inttypes.h>
// 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.
@ -256,13 +258,13 @@ namespace AZ
struct ClampedIntegralLimits
{
//! If SourceType and ClampType are different, returns the greater value of
//! std::numeric_limits<SourceType>::lowest() and std::numeric_limits<ClampType>::lowest(),
//! otherwise returns std::numeric_limits<SourceType>::lowest().
//! AZStd::numeric_limits<SourceType>::lowest() and AZStd::numeric_limits<ClampType>::lowest(),
//! otherwise returns AZStd::numeric_limits<SourceType>::lowest().
static constexpr SourceType Min();
//! If SourceType and ClampType are different, returns the lesser value of
//! std::numeric_limits<SourceType>::max() and std::numeric_limits<ClampType>::max(),
//! otherwise returns std::numeric_limits<SourceType>::max().
//! AZStd::numeric_limits<SourceType>::max() and AZStd::numeric_limits<ClampType>::max(),
//! otherwise returns AZStd::numeric_limits<SourceType>::max().
static constexpr SourceType Max();
//! 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).
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)
{
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.
@ -431,19 +433,19 @@ namespace AZ
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
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)));
}
//! 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>
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;
}
@ -461,6 +463,44 @@ namespace AZ
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.
template <typename SourceType, typename ClampType>
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
return sizeof(SourceType) < sizeof(ClampType) ?
(std::numeric_limits<SourceType>::lowest)() :
static_cast<SourceType>((std::numeric_limits<ClampType>::lowest)());
(AZStd::numeric_limits<SourceType>::lowest)() :
static_cast<SourceType>((AZStd::numeric_limits<ClampType>::lowest)());
}
}
@ -486,12 +526,12 @@ namespace AZ
if constexpr (sizeof(SourceType) < sizeof(ClampType))
{
// 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))
{
// 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
{
@ -499,13 +539,13 @@ namespace AZ
{
// SourceType and ClampType are the same width, ClampType is signed
// 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
{
// SourceType and ClampType are the same width, ClampType is unsigned
// 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
// LeftType is signed
// 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)
{
@ -604,7 +644,7 @@ namespace AZ
// LeftType < RightType
// LeftType is unsigned
// 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)
{

@ -85,6 +85,16 @@ namespace AZ::Internal
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
{
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{};
@ -209,8 +219,15 @@ namespace AZ::Internal
return {};
}
void InjectSettingToCommandLineBack(AZ::SettingsRegistryInterface& settingsRegistry,
AZStd::string_view path, AZStd::string_view value)
enum class InjectLocation : bool
{
Front,
Back
};
void InjectSettingToCommandLine(AZ::SettingsRegistryInterface& settingsRegistry,
AZStd::string_view path, AZStd::string_view value,
InjectLocation injectLocation = InjectLocation::Front)
{
AZ::CommandLine commandLine;
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine);
@ -219,7 +236,8 @@ namespace AZ::Internal
auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")",
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);
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.
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
settingsRegistry.Set(InternalScanUpEngineRootKey, engineRoot.Native());
if (!engineRoot.empty())
{
settingsRegistry.Set(engineRootKey, engineRoot.Native());
// Inject the engine root at the end of the command line settings
Internal::InjectSettingToCommandLineBack(settingsRegistry, engineRootKey, engineRoot.Native());
// Inject the engine root to the front of the command line settings
Internal::InjectSettingToCommandLine(settingsRegistry, engineRootKey, engineRoot.Native());
return engineRoot;
}
}
@ -258,6 +289,14 @@ namespace AZ::SettingsRegistryMergeUtils
// Step 2 check if the engine_path key has been supplied
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;
}
@ -298,13 +337,28 @@ namespace AZ::SettingsRegistryMergeUtils
if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType)
{
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
settingsRegistry.Set(InternalScanUpProjectRootKey, projectRoot.Native());
if (!projectRoot.empty())
{
settingsRegistry.Set(projectRootKey, projectRoot.c_str());
// Inject the project root at the end of the command line settings
Internal::InjectSettingToCommandLineBack(settingsRegistry, projectRootKey, projectRoot.Native());
// Inject the project root at to the front of the command line settings
Internal::InjectSettingToCommandLine(settingsRegistry, projectRootKey, projectRoot.Native());
return projectRoot;
}
}
@ -312,6 +366,18 @@ namespace AZ::SettingsRegistryMergeUtils
// Step 2 Check the project-path key
// This is the project path root key, as passed from command-line or *.setreg files.
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;
}
@ -325,13 +391,22 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr auto projectCachePathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_cache_path";
// 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
return projectPath / Internal::ProductCacheDirectoryName;
if (projectCachePath.IsRelative())
{
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
@ -344,13 +419,22 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr auto projectUserPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_user_path";
// 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
return projectPath / "user";
if (projectUserPath.IsRelative())
{
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
@ -363,20 +447,37 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr auto projectLogPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_log_path";
// 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 projectUserPath / "log";
return projectLogPath;
}
// 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)
{
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
@ -669,15 +770,6 @@ namespace AZ::SettingsRegistryMergeUtils
if ([[maybe_unused]] constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
!projectPath.empty())
{
if (projectPath.IsRelative())
{
if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectPath.Native());
projectAbsPath.has_value())
{
projectPath = AZStd::move(*projectAbsPath);
}
}
projectPath = projectPath.LexicallyNormal();
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?)"
@ -699,15 +791,6 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
if (!engineRoot.empty())
{
if (engineRoot.IsRelative())
{
if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native());
engineRootAbsPath.has_value())
{
engineRoot = AZStd::move(*engineRootAbsPath);
}
}
engineRoot = engineRoot.LexicallyNormal();
registry.Set(FilePathKey_EngineRootFolder, engineRoot.Native());
}
@ -716,15 +799,6 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal();
if (!projectCachePath.empty())
{
if (projectCachePath.IsRelative())
{
if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native());
projectCacheAbsPath.has_value())
{
projectCachePath = AZStd::move(*projectCacheAbsPath);
}
}
projectCachePath = projectCachePath.LexicallyNormal();
registry.Set(FilePathKey_CacheProjectRootFolder, projectCachePath.Native());
@ -755,15 +829,6 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath);
if (!projectUserPath.empty())
{
if (projectUserPath.IsRelative())
{
if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native());
projectUserAbsPath.has_value())
{
projectUserPath = AZStd::move(*projectUserAbsPath);
}
}
projectUserPath = projectUserPath.LexicallyNormal();
registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
}
@ -771,14 +836,6 @@ namespace AZ::SettingsRegistryMergeUtils
// Log folder
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();
registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native());
}
@ -786,14 +843,6 @@ namespace AZ::SettingsRegistryMergeUtils
// Developer Write Storage folder
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();
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
//! 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.
//! 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
//! 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
//! 1. project_path set in the <engine-root>/bootstrap.cfg file
//! 2. project_path set in a *.setreg/*.setregpatch file
//! 3. project_path found by scanning upwards from the executable directory to the project.json path
//! 4. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=<path>"
//! 1. 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 set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=<path>"
//! or --project_path=<path>
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry);
@ -213,9 +212,15 @@ namespace AZ::SettingsRegistryMergeUtils
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
//! 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>
//! 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
//! example: --regremove "/My/String/Value"
//! only when executeCommands is true are the following options supported:

@ -416,27 +416,27 @@ namespace AZ
Join(fixedOutput, example.begin(), example.end(), ",");
// fixedOutput == "test,string,joining"
*/
template<typename TStringType, typename TConvertableToStringViewIterator, typename TSeparatorString>
template<typename StringType, typename ConvertableToStringViewIterator, typename SeparatorString>
inline void Join(
TStringType& joinTarget,
const TConvertableToStringViewIterator& iteratorBegin,
const TConvertableToStringViewIterator& iteratorEnd,
const TSeparatorString& separator)
StringType& joinTarget,
const ConvertableToStringViewIterator& iteratorBegin,
const ConvertableToStringViewIterator& iteratorEnd,
const SeparatorString& separator)
{
if (iteratorBegin == iteratorEnd)
{
return;
}
using CharType = typename TStringType::value_type;
using CharTraitsType = typename TStringType::traits_type;
using CharType = typename StringType::value_type;
using CharTraitsType = typename StringType::traits_type;
size_t size = joinTarget.size() + AZStd::basic_string_view<CharType, CharTraitsType>(*iteratorBegin).size();
for (auto currentIterator = AZStd::next(iteratorBegin); currentIterator != iteratorEnd; ++currentIterator)
{
size += AZStd::basic_string_view<CharType, CharTraitsType>(*currentIterator).size();
// 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;
}
@ -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
/*! 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(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(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!
template<class Allocator>

@ -656,18 +656,6 @@ namespace UnitTest
fval = AZStd::stof(wfloatStr);
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
AZStd::string str1;
AZStd::to_string(str1, wstr);
@ -978,6 +966,47 @@ namespace UnitTest
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
: public AllocatorsFixture
{

@ -37,8 +37,8 @@ namespace UnitTest
// min/max need to be substantially different to return a useful t value
// Float
const float epsilonF = std::numeric_limits<float>::epsilon();
const float doesntMatterF = std::numeric_limits<float>::signaling_NaN();
const float epsilonF = AZStd::numeric_limits<float>::epsilon();
const float doesntMatterF = AZStd::numeric_limits<float>::signaling_NaN();
float lowerF = 2.3f, upperF = 2.3f;
EXPECT_EQ(0.0f, AZ::LerpInverse(lowerF, upperF, 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);
// Double
const double epsilonD = std::numeric_limits<double>::epsilon();
const double doesntMatterD = std::numeric_limits<double>::signaling_NaN();
const double epsilonD = AZStd::numeric_limits<double>::epsilon();
const double doesntMatterD = AZStd::numeric_limits<double>::signaling_NaN();
double lowerD = 2.3, upperD = 2.3;
EXPECT_EQ(0.0, AZ::LerpInverse(lowerD, upperD, 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(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);
}
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)
{
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 (!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()))
{
return ProjectPathCheckResult::ProjectPathFound;

@ -190,9 +190,67 @@ namespace AzFramework
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)
@ -645,7 +703,7 @@ namespace AzFramework
m_orbitChannelId = orbitChanneId;
}
OrbitDollyScrollCameraInput::OrbitDollyScrollCameraInput()
OrbitScrollDollyCameraInput::OrbitScrollDollyCameraInput()
{
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)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@ -691,7 +749,7 @@ namespace AzFramework
return nextCamera;
}
Camera OrbitDollyScrollCameraInput::StepCamera(
Camera OrbitScrollDollyCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
@ -702,7 +760,7 @@ namespace AzFramework
return nextCamera;
}
OrbitDollyMotionCameraInput::OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId)
OrbitMotionDollyCameraInput::OrbitMotionDollyCameraInput(const InputChannelId& dollyChannelId)
: m_dollyChannelId(dollyChannelId)
{
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)
{
HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this);
return CameraInputUpdatingAfterMotion(*this);
}
Camera OrbitDollyMotionCameraInput::StepCamera(
Camera OrbitMotionDollyCameraInput::StepCamera(
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
@ -727,7 +785,7 @@ namespace AzFramework
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;
}

@ -289,7 +289,17 @@ namespace AzFramework
//! 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.
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.
void Reset();
//! Remove all cameras that were added.
@ -299,9 +309,10 @@ namespace AzFramework
bool Exclusive() const;
private:
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs; //!< Active camera inputs updating the camera (empty initially).
AZStd::vector<AZStd::shared_ptr<CameraInput>>
m_idleCameraInputs; //!< Idle camera inputs not contributing to the update (filled initially).
//! Active camera inputs updating the camera (empty initially).
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
//! 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.
@ -586,10 +597,10 @@ namespace AzFramework
};
//! A camera input to handle discrete scroll events that can modify the camera offset.
class OrbitDollyScrollCameraInput : public CameraInput
class OrbitScrollDollyCameraInput : public CameraInput
{
public:
OrbitDollyScrollCameraInput();
OrbitScrollDollyCameraInput();
// CameraInput overrides ...
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.
class OrbitDollyMotionCameraInput : public CameraInput
class OrbitMotionDollyCameraInput : public CameraInput
{
public:
explicit OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId);
explicit OrbitMotionDollyCameraInput(const InputChannelId& dollyChannelId);
// CameraInput overrides ...
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->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

@ -17,6 +17,7 @@
#include <AzNetworking/Utilities/NetworkCommon.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Math/MathUtils.h>
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
// 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 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 SequenceId fragmentedSequence = connection.m_fragmentQueue.GetNextFragmentedSequenceId();
uint32_t bytesRemaining = packetSize;

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

@ -208,10 +208,6 @@ namespace LegacyFramework
*/
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.
* 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(0, nullptr)
{}
Application::Application(int argc, char** argv)
: ComponentApplication(argc, argv)
{
m_isPrimary = true;
m_desiredExitCode = 0;
@ -191,9 +195,6 @@ namespace LegacyFramework
::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, true);
#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 (GetSerializeContext() == nullptr)
{
@ -206,7 +207,7 @@ namespace LegacyFramework
m_ptrSystemEntity->Activate();
// 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
CreateApplicationComponent();
@ -246,9 +247,6 @@ namespace LegacyFramework
::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, false);
#endif
delete m_ptrCommandLineParser;
m_ptrCommandLineParser = nullptr;
CoreMessageBus::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.
bool Application::EnsureComponentCreated(AZ::Uuid componentCRC)
{

@ -56,6 +56,7 @@ namespace LegacyFramework
using CoreMessageBus::Handler::Run;
virtual int Run(const ApplicationDesc& desc);
Application();
Application(int argc, char** argv);
void CreateReflectionManager() override;
@ -70,7 +71,6 @@ namespace LegacyFramework
const char* GetApplicationName() override;
const char* GetApplicationModule() override;
const char* GetApplicationDirectory() override;
const AzFramework::CommandLine* GetCommandLineParser() override;
void TeardownApplicationComponent() 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...
char m_applicationFilePath[AZ_MAX_PATH_LEN];
ApplicationDesc m_desc;
AzFramework::CommandLine* m_ptrCommandLineParser;
};
}

@ -16,7 +16,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/CommandLine/CommandLine.h>
#include <AzCore/Settings/CommandLine.h>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.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>
AZ_POP_DISABLE_WARNING
#include <AzFramework/StringFunc/StringFunc.h>
#ifndef AZ_PLATFORM_WINDOWS
int __argc = 0;
@ -201,8 +200,11 @@ namespace AzToolsFramework
// enable the built-in stylesheet by default:
bool enableStyleSheet = true;
const AzFramework::CommandLine* comp = nullptr;
EBUS_EVENT_RESULT(comp, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser);
const AZ::CommandLine* comp = nullptr;
AZ::ComponentApplicationBus::Broadcast([&comp](AZ::ComponentApplicationRequests* requests)
{
comp = requests->GetAzCommandLine();
});
if (comp != nullptr)
{
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;
// Create Prefab
@ -291,7 +353,8 @@ namespace AzToolsFramework
[selectedEntities]
{
ContextMenu_CreatePrefab(selectedEntities);
});
}
);
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
if (selectedEntities.size() == 0 ||
selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer)
@ -311,7 +389,8 @@ namespace AzToolsFramework
[]
{
ContextMenu_InstantiatePrefab();
});
}
);
// Instantiate Procedural Prefab
if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs())
@ -324,7 +403,8 @@ namespace AzToolsFramework
[]
{
ContextMenu_InstantiateProceduralPrefab();
});
}
);
}
itemWasShown = true;
@ -335,9 +415,7 @@ namespace AzToolsFramework
menu->addSeparator();
}
itemWasShown = false;
// Edit/Save Prefab
// Save Prefab
{
if (selectedEntities.size() == 1)
{
@ -345,52 +423,6 @@ namespace AzToolsFramework
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
AZ::IO::Path prefabFilePath = s_prefabPublicInterface->GetOwningInstancePrefabPath(selectedEntity);
auto dirtyOutcome = s_prefabPublicInterface->HasUnsavedChanges(prefabFilePath);
@ -405,17 +437,43 @@ namespace AzToolsFramework
[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() &&
@ -424,27 +482,17 @@ namespace AzToolsFramework
!readOnlyEntityInSelection)
{
QAction* deleteAction = menu->addAction(QObject::tr("Delete"));
deleteAction->setShortcut(QKeySequence(Qt::Key_Delete));
QObject::connect(
deleteAction, &QAction::triggered, deleteAction,
[]
{
ContextMenu_DeleteSelected();
});
}
);
}
// 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);
});
}
menu->addSeparator();
}
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()
{
AzToolsFramework::EntityIdList selectedEntityIds;

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

@ -20,6 +20,7 @@
#include <GridMate/Containers/unordered_set.h>
#include <GridMate/Carrier/DriverEvents.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/std/chrono/types.h>
#include <AzCore/std/string/conversions.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*/)
{
// 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)
{

@ -221,18 +221,6 @@ namespace GridMate
void StopWaitForData() override;
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);
bool FreeRIOBuffer(char *buffer);

@ -21,11 +21,11 @@
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/Asset/AssetSystemComponent.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@ -244,7 +244,7 @@ namespace LUAEditor
}
AZStd::vector<AZStd::string> files;
AzFramework::StringFunc::Tokenize(parameters.c_str(), files, ";");
AZ::StringFunc::Tokenize(parameters.c_str(), files, ";");
if (!files.empty())
{
for (const auto& file : files)
@ -669,9 +669,12 @@ namespace LUAEditor
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 forceHide = false;
@ -850,7 +853,7 @@ namespace LUAEditor
DocumentInfo& info = infoEntry.first->second;
info.m_assetId = normalizedAssetId;
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_CanWrite = true;
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."
// 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";
}
@ -961,7 +964,7 @@ namespace LUAEditor
trySaveAs = 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.
docInfoIter->second.m_bSourceControl_Ready = true;
@ -1489,7 +1492,7 @@ namespace LUAEditor
DocumentInfo info;
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_bSourceControl_BusyGettingStats = true;
info.m_bSourceControl_BusyGettingStats = false;
@ -1555,7 +1558,10 @@ namespace LUAEditor
const AZStd::string k_luaScriptFileString = "files";
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 = "";
size_t numSwitchValues = commandLine->GetNumSwitchValues(k_luaScriptFileString);
@ -2448,7 +2454,7 @@ namespace LUAEditor
if (matchFound)
{
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;
finalMessage = match[2].str().c_str();
@ -2467,6 +2473,6 @@ namespace LUAEditor
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
{
Application::Application(int &argc, char **argv) : BaseApplication(argc, argv)
Application::Application(int argc, char **argv)
: BaseApplication(argc, argv)
{
AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
}

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

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

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

@ -179,7 +179,7 @@ namespace ImageProcessingAtom
// Create a context based on the configuration
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);
status = astcenc_context_alloc(&config, threadCount, &context);
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/DDSHeader.h>
#include <AzCore/Math/MathUtils.h>
namespace ImageProcessingAtom
{
CPixelFormats* CPixelFormats::s_instance = nullptr;
@ -370,11 +372,11 @@ namespace ImageProcessingAtom
{
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)
{
outHeight = ((outHeight + pFormatInfo->blockHeight - 1) / pFormatInfo->blockHeight) * pFormatInfo->blockHeight;
outHeight = AZ::RoundUpToMultiple(outHeight, pFormatInfo->blockHeight);
}
}
}
@ -390,10 +392,11 @@ namespace ImageProcessingAtom
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
return (((imageWidth + pFormatInfo->blockWidth - 1) / pFormatInfo->blockWidth) *
((imageHeight + pFormatInfo->blockHeight - 1) / pFormatInfo->blockHeight) * pFormatInfo->bitsPerBlock) / 8;
return (AZ::DivideAndRoundUp(imageWidth, pFormatInfo->blockWidth) *
AZ::DivideAndRoundUp(imageHeight, pFormatInfo->blockHeight) *
pFormatInfo->bitsPerBlock) / 8;
}
bool CPixelFormats::IsFormatSingleChannel(EPixelFormat fmt)

@ -8,6 +8,7 @@
#pragma once
#include <AzCore/base.h>
#include <AzCore/Math/MathUtils.h>
#ifndef AZ_BIT
#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.
*/
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);
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;
if (ValidateConstantAccess(inputIndex, ValidateConstantAccessExpect::Complete, 0, sizeInBytes))
{

@ -41,17 +41,17 @@ namespace AZ
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
{
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
{
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

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

@ -20,7 +20,7 @@ namespace AZ
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 itemCount = AZStd::min(drawList.size() - itemOffset, itemsPerPartition);
return DrawListView(&drawList[itemOffset], itemCount);

@ -281,7 +281,7 @@ namespace AZ
{
srgPool->CompileGroupsBegin();
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 srgCompileEndDesc{"SrgCompileEnd", "Graphics"};
@ -332,7 +332,7 @@ namespace AZ
const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool)
{
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)
{

@ -100,7 +100,7 @@ namespace AZ
// The frame attachment has tight control over lifecycle here.
[[maybe_unused]] const bool isAttach = (!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;

@ -92,7 +92,7 @@ namespace AZ
const uint32_t CommandListCostThreshold =
AZStd::max(
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
@ -145,7 +145,7 @@ namespace AZ
else
{
// 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>();
scopeContextGroup->Init(device, scope, commandListCount, GetJobPolicy());

@ -81,7 +81,7 @@ namespace AZ
const uint32_t CommandListCostThreshold =
AZStd::max(
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
@ -136,7 +136,7 @@ namespace AZ
else
{
// 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>();
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
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.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
#include <QVBoxLayout>
@ -36,8 +40,10 @@ namespace AtomToolsFramework
Q_OBJECT
public:
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;
// InspectorRequestBus::Handler overrides...
@ -74,6 +80,8 @@ namespace AtomToolsFramework
void ExpandAll() override;
void CollapseAll() override;
void SetGroupSettingsPrefix(const AZStd::string& prefix);
protected:
virtual bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const;
virtual void OnGroupExpanded(const AZStd::string& groupName);
@ -81,14 +89,15 @@ namespace AtomToolsFramework
virtual void OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event);
private:
QScopedPointer<Ui::InspectorWidget> m_ui;
struct GroupWidgetPair
{
InspectorGroupHeaderWidget* m_header;
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;
};
} // namespace AtomToolsFramework

@ -11,14 +11,8 @@
#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertyValue.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/any.h>
namespace AzToolsFramework
{
class InstanceDataNode;
}
namespace AtomToolsFramework
{
//! 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,
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
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

@ -9,9 +9,12 @@
#pragma once
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/PlatformDef.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/any.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
#include <QFileInfo>
@ -23,14 +26,6 @@ class QImage;
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&)>;
void LoadImageAsync(const AZStd::string& path, LoadImageAsyncCallback callback);
@ -39,4 +34,64 @@ namespace AtomToolsFramework
QFileInfo GetUniqueFileInfo(const QString& initialPath);
QFileInfo GetDuplicationFileInfo(const QString& initialPath);
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

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

@ -9,6 +9,7 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzFramework/Viewport/ViewportId.h>
namespace AZ
@ -16,6 +17,11 @@ namespace AZ
class Transform;
}
namespace AzFramework
{
class CameraInput;
}
namespace AtomToolsFramework
{
//! 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
//! a negative Z value will move the camera backwards from the pivot.
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:
~ModularViewportCameraControllerRequests() = default;

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

@ -70,9 +70,9 @@ namespace AtomToolsFramework
m_styleManager->initialize(this, engineRootPath);
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 =
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);
connect(&m_timer, &QTimer::timeout, this, [this]()
@ -188,7 +188,7 @@ namespace AtomToolsFramework
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);
ConnectToAssetProcessor();
@ -197,7 +197,7 @@ namespace AtomToolsFramework
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
&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::SourceControlConnectionRequests::EnableSourceControl, enableSourceControl);
@ -238,6 +238,12 @@ namespace AtomToolsFramework
void AtomToolsApplication::Destroy()
{
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();
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();

@ -106,9 +106,9 @@ namespace AtomToolsFramework
const AZStd::vector<AssetBrowserEntry*> entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
const bool promptToOpenMultipleFiles =
GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles", true);
GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles", true);
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())
{

@ -71,11 +71,11 @@ namespace AtomToolsFramework
QListWidgetItem* AssetGridDialog::CreateListItem(const SelectableAsset& selectableAsset)
{
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>(
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>(
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();
m_ui->m_assetList->setGridSize(QSize(

@ -13,6 +13,7 @@
#include <AtomToolsFramework/Document/AtomToolsDocument.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystem.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AtomToolsFrameworkSystemComponent.h>
namespace AtomToolsFramework
@ -23,6 +24,7 @@ namespace AtomToolsFramework
DynamicPropertyGroup::Reflect(context);
AtomToolsDocument::Reflect(context);
AtomToolsDocumentSystem::Reflect(context);
InspectorWidget::Reflect(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
//#include <Document/moc_AtomToolsDocumentMainWindow.cpp>
//#include <AtomToolsFramework/Document/moc_AtomToolsDocumentMainWindow.cpp>

@ -368,7 +368,7 @@ namespace AtomToolsFramework
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)
{
m_documentIdsWithDependencyChanges.clear();
@ -377,7 +377,7 @@ namespace AtomToolsFramework
}
const bool enableHotReloadPrompts =
GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true);
GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true);
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/InspectorGroupWidget.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 <QMenu>
#include <QScrollArea>
#include <QScrollBar>
#include <QSizePolicy>
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)
: QWidget(parent)
, m_ui(new Ui::InspectorWidget)
{
m_ui->setupUi(this);
SetGroupSettingsPrefix("/O3DE/AtomToolsFramework/InspectorWidget");
}
InspectorWidget::~InspectorWidget()
{
SetSettingsObject(m_collapsedGroupSettingName, m_collapsedGroups);
InspectorRequestBus::Handler::BusDisconnect();
}
void InspectorWidget::AddHeading(QWidget* headingWidget)
@ -52,6 +94,7 @@ namespace AtomToolsFramework
delete child;
}
m_groups.clear();
InspectorRequestBus::Handler::BusDisconnect();
}
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
{
AZ_UNUSED(groupName);
return true;
auto stateItr = m_collapsedGroups.find(AZ::Crc32(groupName));
return stateItr == m_collapsedGroups.end();
}
void InspectorWidget::OnGroupExpanded(const AZStd::string& groupName)
{
AZ_UNUSED(groupName);
m_collapsedGroups.erase(AZ::Crc32(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)

@ -6,21 +6,18 @@
*
*/
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Reflect/Image/ImageAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.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/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
namespace AtomToolsFramework
{
@ -223,51 +220,4 @@ namespace AtomToolsFramework
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

@ -8,13 +8,18 @@
#include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingBus.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
@ -178,4 +183,74 @@ namespace AtomToolsFramework
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;
}
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
{
return m_cameraMode == CameraMode::Animation;

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

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

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

@ -64,7 +64,7 @@ namespace MaterialEditor
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");
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/MaterialTypeSourceData.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
@ -17,7 +19,6 @@
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
#include <Window/MaterialEditorWindow.h>
#include <Window/MaterialEditorWindowSettings.h>
#include <Window/MaterialInspector/MaterialInspector.h>
#include <Window/SettingsDialog/SettingsDialog.h>
#include <Window/ViewportSettingsInspector/ViewportSettingsInspector.h>
@ -78,7 +79,20 @@ namespace MaterialEditor
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);
SetDockWidgetVisible("Viewport Settings", false);
@ -98,6 +112,12 @@ namespace MaterialEditor
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)
{
QSize requestedViewportSize = QSize(width, height) / devicePixelRatioF();

@ -9,6 +9,7 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentInspector.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
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);
protected:
// AtomToolsFramework::AtomToolsMainWindowRequestBus::Handler overrides...
void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override;
void LockViewportRenderTargetSize(uint32_t width, uint32_t height) 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 GetOpenDocumentParams(AZStd::string& openPath) override;
void OpenSettings() override;
@ -47,6 +53,7 @@ namespace MaterialEditor
void closeEvent(QCloseEvent* closeEvent) override;
AtomToolsFramework::AtomToolsDocumentInspector* m_materialInspector = {};
MaterialViewportWidget* m_materialViewport = {};
MaterialEditorToolBar* m_toolBar = {};
};

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

@ -27,6 +27,5 @@ namespace MaterialEditor
static void Reflect(AZ::ReflectContext* context);
AZStd::vector<char> m_mainWindowState;
AZStd::unordered_set<AZ::u32> m_inspectorCollapsedGroups;
};
} // 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 <Window/SettingsDialog/SettingsWidget.h>
namespace MaterialEditor
{
SettingsWidget::SettingsWidget(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
{
SetGroupSettingsPrefix("/O3DE/Atom/MaterialEditor/SettingsWidget");
}
SettingsWidget::~SettingsWidget()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void SettingsWidget::Populate()
@ -29,7 +29,6 @@ namespace MaterialEditor
void SettingsWidget::Reset()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
} // namespace MaterialEditor

@ -32,9 +32,7 @@ namespace MaterialEditor
m_viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
m_windowSettings = AZ::UserSettings::CreateFind<MaterialEditorWindowSettings>(
AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL);
SetGroupSettingsPrefix("/O3DE/Atom/MaterialEditor/ViewportSettingsInspector");
MaterialViewportNotificationBus::Handler::BusConnect();
}
@ -43,7 +41,6 @@ namespace MaterialEditor
m_lightingPreset.reset();
m_modelPreset.reset();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void ViewportSettingsInspector::Populate()
@ -144,7 +141,7 @@ namespace MaterialEditor
});
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(
"Model Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow());
@ -270,7 +267,7 @@ namespace MaterialEditor
});
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(
"Lighting Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow());
@ -338,7 +335,6 @@ namespace MaterialEditor
m_viewportSettings->m_displayMapperOperationType = viewportRequests->GetDisplayMapperOperationType();
});
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
@ -433,23 +429,6 @@ namespace MaterialEditor
{
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
#include <Window/ViewportSettingsInspector/moc_ViewportSettingsInspector.cpp>

@ -16,7 +16,6 @@
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <Viewport/MaterialViewportNotificationBus.h>
#include <Viewport/MaterialViewportSettings.h>
#include <Window/MaterialEditorWindowSettings.h>
#endif
namespace MaterialEditor
@ -75,13 +74,9 @@ namespace MaterialEditor
AZStd::string GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) 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::LightingPresetPtr m_lightingPreset;
AZStd::intrusive_ptr<MaterialViewportSettings> m_viewportSettings;
AZStd::intrusive_ptr<MaterialEditorWindowSettings> m_windowSettings;
};
} // namespace MaterialEditor

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

@ -14,8 +14,11 @@
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.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/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/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
@ -24,9 +27,6 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.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
#include <QApplication>
@ -54,7 +54,6 @@ namespace AZ
MaterialPropertyInspector::~MaterialPropertyInspector()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
@ -137,7 +136,6 @@ namespace AZ
m_dirtyPropertyFlags.set();
m_internalEditNotification = {};
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
@ -590,7 +588,7 @@ namespace AZ
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);
}
@ -719,7 +717,7 @@ namespace AZ
// 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
// started and ended
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property)
{
if (m_activeProperty != property)
@ -731,7 +729,7 @@ namespace AZ
void MaterialPropertyInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (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
// 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
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property)
{
if (m_activeProperty == property)

@ -15,10 +15,11 @@
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.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/MaterialTypeAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.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