Merge branch 'main' into SPEC6476_ReflectNetworkProperties

This commit is contained in:
Gene Walters
2021-05-13 22:04:21 -07:00
535 changed files with 3833 additions and 21336 deletions
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:45b58009dc2f9340e08cafd5e68f142d15d77e19e7cef142933f2dff3cfb9293
size 38352
@@ -68,7 +68,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/scripting/TestSuite_Active.py
TIMEOUT 3000
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -123,6 +123,21 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
)
endif()
## Prefab ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PrefabTests
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/prefab/TestSuite_Main.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
)
endif()
## Editor Python Bindings ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
@@ -0,0 +1,76 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
# fmt:off
class Tests():
find_empty_entity = ("Entity: 'EmptyEntity' found", "Entity: 'EmptyEntity' *not* found in level")
empty_entity_pos = ("'EmptyEntity' position is at the expected position", "'EmptyEntity' position is *not* at the expected position")
find_pxentity = ("Entity: 'EntityWithPxCollider' found", "Entity: 'EntityWithPxCollider' *not* found in level")
pxentity_component = ("Entity: 'EntityWithPxCollider' has a Physx Collider", "Entity: 'EntityWithPxCollider' does *not* have a Physx Collider")
# fmt:on
def PrefabLevel_OpensLevelWithEntities():
"""
Opens the level that contains 2 entities, "EmptyEntity" and "EntityWithPxCollider".
This test makes sure that both entities exist after opening the level and that:
- EmptyEntity is at Position: (10, 20, 30)
- EntityWithPxCollider has a PhysXCollider component
"""
import os
import sys
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.entity as entity
import azlmbr.bus as bus
from azlmbr.math import Vector3
EXPECTED_EMPTY_ENTITY_POS = Vector3(10.00, 20.0, 30.0)
helper.init_idle()
helper.open_level("prefab", "PrefabLevel_OpensLevelWithEntities")
def find_entity(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entityIds = entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter)
if entityIds[0].IsValid():
return entityIds[0]
return None
#Checks for an entity called "EmptyEntity"
helper.wait_for_condition(lambda: find_entity("EmptyEntity").IsValid(), 5.0)
empty_entity_id = find_entity("EmptyEntity")
Report.result(Tests.find_empty_entity, empty_entity_id.IsValid())
# Checks if the EmptyEntity is in the correct position and if it fails, it will provide the expected postion and the actual postion of the entity in the Editor log
empty_entity_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", empty_entity_id)
is_at_position = empty_entity_pos.IsClose(EXPECTED_EMPTY_ENTITY_POS)
Report.result(Tests.empty_entity_pos, is_at_position)
if not is_at_position:
Report.info(f'Expected position: {EXPECTED_EMPTY_ENTITY_POS.ToString()}, actual position: {empty_entity_pos.ToString()}')
#Checks for an entity called "EntityWithPxCollider" and if it has the PhysX Collider component
pxentity = find_entity("EntityWithPxCollider")
Report.result(Tests.find_pxentity, pxentity.IsValid())
pxcollider_id = hydra.get_component_type_id("PhysX Collider")
hasComponent = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'HasComponentOfType', pxentity, pxcollider_id)
Report.result(Tests.pxentity_component, hasComponent)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test (PrefabLevel_OpensLevelWithEntities)
@@ -0,0 +1,35 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append (os.path.dirname (os.path.abspath (__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def _run_prefab_test(self, request, workspace, editor, test_module):
self._run_test(request, workspace, editor, test_module, ["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
def test_PrefabLevel_OpensLevelWithEntities(self, request, workspace, editor, launcher_platform):
from . import PrefabLevel_OpensLevelWithEntities as test_module
self._run_prefab_test(request, workspace, editor, test_module)
@@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
@@ -7,10 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92568856
Test Case Title: Multiple Entities can be targeted in the Debugger tool
"""
@@ -25,20 +21,20 @@ class Tests():
GENERAL_WAIT = 0.5 # seconds
def Debugging_TargetMultipleEntities():
def Debugger_HappyPath_TargetMultipleEntities():
"""
Summary:
Multiple Entities can be targeted in the Debugger tool
Expected Behavior:
Selected files can be checked for logging.
Multiple selected files can be checked for logging.
Upon checking, checkboxes of the parent folders change to either full or partial check.
Test Steps:
1) Create temp level
2) Create two entities with scriptcanvas components
3) Set values for scriptcanvas
4) Open Script Canvas window and get sc opbject
4) Open Script Canvas window and get sc object
5) Open Debugging(Logging) window
6) Click on Entities tab in logging window
7) Verify if the scriptcanvas exist under entities
@@ -53,7 +49,6 @@ def Debugging_TargetMultipleEntities():
:return: None
"""
from PySide2 import QtWidgets
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
@@ -140,4 +135,4 @@ if __name__ == "__main__":
imports.init()
from utils import Report
Report.start_test(Debugging_TargetMultipleEntities)
Report.start_test(Debugger_HappyPath_TargetMultipleEntities)
@@ -7,29 +7,25 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92569137
Test Case Title: Multiple Graphs can be targeted in the Debugger tool
"""
# fmt: off
class Tests():
select_multiple_targets = ("Multiple targets are selected", "Multiple targets are not selected")
select_multiple_targets = ("Multiple targets are selected", "Multiple targets are not selected")
# fmt: on
GENERAL_WAIT = 0.5 # seconds
def Debugging_TargetMultipleGraphs():
def Debugger_HappyPath_TargetMultipleGraphs():
"""
Summary:
Multiple Graphs can be targeted in the Debugger tool
Expected Behavior:
Selected files can be checked for logging.
Multiple elected files can be checked for logging.
Upon checking, checkboxes of the parent folders change to either full or partial check.
Test Steps:
@@ -49,7 +45,6 @@ def Debugging_TargetMultipleGraphs():
:return: None
"""
from PySide2 import QtWidgets
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
@@ -106,7 +101,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(Debugging_TargetMultipleGraphs)
Report.start_test(Debugger_HappyPath_TargetMultipleGraphs)
@@ -7,12 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92569049
Test Case Title: Edit > Undo undoes the last action
Test case ID: T92569051
Test Case Title: Edit > Redo redoes the last undone action
"""
@@ -24,7 +18,7 @@ class Tests():
# fmt: on
def EditMenu_UndoRedo():
def EditMenu_Default_UndoRedo():
"""
Summary:
Edit > Undo undoes the last action
@@ -33,8 +27,8 @@ def EditMenu_UndoRedo():
redo it and verify if the variable is created again.
Expected Behavior:
The last action is undone.
The last undone action is redone.
The last action is undone upon selecting Undo.
The last undone action is redone upon selecting Redo.
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -44,7 +38,7 @@ def EditMenu_UndoRedo():
5) Create new variable
6) Verify if the variable is created initially
7) Trigger Undo action and verify if variable is removed in Variable Manager
8) Trigger Redo action and verify if variable is readded in Variable Manager
8) Trigger Redo action and verify if variable is re-added in Variable Manager
9) Close SC window
Note:
@@ -54,13 +48,12 @@ def EditMenu_UndoRedo():
:return: None
"""
from PySide2 import QtWidgets, QtCore
import azlmbr.legacy.general as general
import pyside_utils
import azlmbr.legacy.general as general
# 1) Open Script Canvas window
general.idle_enable(True)
general.open_pane("Script Canvas")
@@ -115,7 +108,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(EditMenu_UndoRedo)
Report.start_test(EditMenu_Default_UndoRedo)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92562978
Test Case Title: Script Canvas Component can be added to an entity
"""
@@ -23,13 +20,13 @@ class Tests():
# fmt: on
def Entity_AddScriptCanvasComponent():
def Entity_HappyPath_AddScriptCanvasComponent():
"""
Summary:
verify if Script Canvas component can be added to Entity without any issue
Script Canvas Component can be added to an entity
Expected Behavior:
Script Canvas Component is added to the entity successfully without issue.
Script Canvas Component is added to the entity successfully without issue
Test Steps:
1) Create temp level
@@ -46,7 +43,6 @@ def Entity_AddScriptCanvasComponent():
:return: None
"""
from utils import TestHelper as helper
from utils import Tracer
from editor_entity_utils import EditorEntity
@@ -84,4 +80,4 @@ if __name__ == "__main__":
imports.init()
from utils import Report
Report.start_test(Entity_AddScriptCanvasComponent)
Report.start_test(Entity_HappyPath_AddScriptCanvasComponent)
@@ -7,22 +7,15 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92569037
Test Case Title: File > New Script creates a new script
Test case ID: T92569039
Test Case Title: File > Open opens the Open... dialog
"""
import os
import sys
from PySide2 import QtWidgets
import azlmbr.legacy.general as general
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.utils import Report
import azlmbr.legacy.general as general
# fmt: off
class Tests():
new_action = "File->New action working as expected"
@@ -33,10 +26,11 @@ class Tests():
GENERAL_WAIT = 0.5 # seconds
class TestFileMenuNewOpen:
class TestFileMenuDefaultNewOpen:
"""
Summary:
When clicked on File->New, new script opens and File->Open should open the FileBrowser
When clicked on File->New, new script opens
File->Open should open the FileBrowser
Expected Behavior:
New and Open actions should work as expected.
@@ -92,5 +86,5 @@ class TestFileMenuNewOpen:
general.close_pane("Script Canvas")
test = TestFileMenuNewOpen()
test = TestFileMenuDefaultNewOpen()
test.run_test()
@@ -7,24 +7,17 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92563070
Test Case Title: Graphs can be closed by clicking X on the Graph name tab
Test case ID: T92563068
Test Case Title: Save Prompt: User is prompted to save a graph on close after
creating a new graph
"""
import os
import sys
from PySide2 import QtWidgets
import azlmbr.legacy.general as general
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Report
import azlmbr.legacy.general as general
# fmt: off
class Tests():
new_graph = "New graph created"
@@ -36,14 +29,15 @@ class Tests():
GENERAL_WAIT = 0.5 # seconds
class TestGraphCloseSavePrompt:
class TestGraphClose_Default_SavePrompt:
"""
Summary:
The graph is closed when x button is clicked.
Save Prompt is opened before closing.
Expected Behavior:
New and Open actions should work as expected.
The Graph is closed.
Upon closing the graph, User is prompted whether or not to save changes.
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -104,5 +98,5 @@ class TestGraphCloseSavePrompt:
general.close_pane("Script Canvas")
test = TestGraphCloseSavePrompt()
test = TestGraphClose_Default_SavePrompt()
test.run_test()
@@ -7,12 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92569079
Test Case Title: View > Zoom In zooms the graph in
Test case ID: T92569081
Test Case Title: View > Zoom In zooms the graph out
"""
@@ -26,7 +20,7 @@ class Tests():
GENERAL_WAIT = 0.5 # seconds
def Graph_ZoomInZoomOut():
def Graph_HappyPath_ZoomInZoomOut():
"""
Summary:
The graph can be zoomed in and zoomed out.
@@ -91,7 +85,7 @@ def Graph_ZoomInZoomOut():
zin = pyside_utils.find_child_by_pattern(sc_main, {"objectName": "action_ZoomIn", "type": QtWidgets.QAction})
zin.trigger()
result = helper.wait_for_condition(
lambda: curr_m11 < graphics_view.transform().m11() and curr_m22 < graphics_view.transform().m22(), GENERAL_WAIT,
lambda: curr_m11 < graphics_view.transform().m11() and curr_m22 < graphics_view.transform().m22(), GENERAL_WAIT
)
Report.result(Tests.zoom_in, result)
@@ -100,7 +94,7 @@ def Graph_ZoomInZoomOut():
zout = pyside_utils.find_child_by_pattern(sc_main, {"objectName": "action_ZoomOut", "type": QtWidgets.QAction})
zout.trigger()
result = helper.wait_for_condition(
lambda: curr_m11 > graphics_view.transform().m11() and curr_m22 > graphics_view.transform().m22(), GENERAL_WAIT,
lambda: curr_m11 > graphics_view.transform().m11() and curr_m22 > graphics_view.transform().m22(), GENERAL_WAIT
)
Report.result(Tests.zoom_out, result)
@@ -112,7 +106,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(Graph_ZoomInZoomOut)
Report.start_test(Graph_HappyPath_ZoomInZoomOut)
@@ -13,5 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
def init():
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../EditorPythonTestTools/editor_python_test_tools')
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../automatedtesting_shared")
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../EditorPythonTestTools/editor_python_test_tools")
@@ -7,11 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92568942
Test Case Title: Clicking the "+" button and selecting "New Script Event" opens the
Asset Editor with a new Script Event asset
"""
from PySide2 import QtWidgets
@@ -35,11 +30,10 @@ GENERAL_WAIT = 0.5 # seconds
class TestAssetEditor_NewScriptEvent:
"""
Summary:
Clicking the "+" button in Node Palette and creating New Script Event opens Asset Editor
Verifying logic flow of the "+" button on the Script Canvas pane's Node Palette is as expected
Expected Behavior:
Clicking the "+" button and selecting "New Script Event" opens the Asset Editor with a
new Script Event asset
Clicking the "+" button and selecting "New Script Event" opens the Asset Editor with a new Script Event asset
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92562988
Test Case Title: Left-click/double click expands and collapses node categories
"""
@@ -123,6 +120,8 @@ def NodeCategory_ExpandOnClick():
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(NodeCategory_ExpandOnClick)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92568982
Test Case Title: Renaming variables in the Node Inspector
"""
@@ -24,7 +21,7 @@ class Tests():
GENERAL_WAIT = 0.5 # seconds
def NodeInspector_RenameVariable():
def NodeInspector_HappyPath_VariableRenames():
"""
Summary:
Renaming variables in the Node Inspector, renames the actual variable.
@@ -50,16 +47,16 @@ def NodeInspector_RenameVariable():
:return: None
"""
TEST_NAME = "test name"
from PySide2 import QtWidgets, QtCore, QtTest
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
import pyside_utils
from utils import TestHelper as helper
import azlmbr.legacy.general as general
TEST_NAME = "test name"
def open_tool(sc, dock_widget_name, pane_name):
if sc.findChild(QtWidgets.QDockWidget, dock_widget_name) is None:
action = pyside_utils.find_child_by_pattern(sc, {"text": pane_name, "type": QtWidgets.QAction})
@@ -120,12 +117,10 @@ def NodeInspector_RenameVariable():
general.close_pane("Script Canvas")
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(NodeInspector_RenameVariable)
Report.start_test(NodeInspector_HappyPath_VariableRenames)
@@ -7,30 +7,27 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92568940
Test Case Title: Categories and Nodes can be selected
"""
# fmt: off
class Tests():
category_selected = ("Category can be selected", "Category cannot be selected")
node_selected = ("Node can be selected", "Node cannot be selected")
category_selected = ("Category can be selected", "Category cannot be selected")
node_selected = ("Node can be selected", "Node cannot be selected")
# fmt: on
GENERAL_WAIT = 0.5 # seconds
def NodePalette_SelectNode():
def NodePalette_HappyPath_CanSelectNode():
"""
Summary:
Categories and Nodes can be selected
Expected Behavior:
When clicked on Node Palette, nodes and categories can be selected.
A category can be selected inside the Node Palette
A Node can be selected inside the Node Palette
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -53,11 +50,12 @@ def NodePalette_SelectNode():
NODE = "Find Path To Entity"
from PySide2 import QtWidgets
import azlmbr.legacy.general as general
import pyside_utils
from utils import TestHelper as helper
import azlmbr.legacy.general as general
# 1) Open Script Canvas window (Tools > Script Canvas)
general.idle_enable(True)
general.open_pane("Script Canvas")
@@ -97,7 +95,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(NodePalette_SelectNode)
Report.start_test(NodePalette_HappyPath_CanSelectNode)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92562993
Test Case Title: Clicking the X button on the Search Box clears the currently entered string
"""
@@ -20,14 +17,14 @@ class Tests():
# fmt: on
def NodePalette_ClearSelection():
def NodePalette_HappyPath_ClearSelection():
"""
Summary:
We enter some string in the Node Palette Search box, and click on the X button to verify if the
search string got cleared.
Clicking the X button on the Search Box clears the currently entered string
Expected Behavior:
Clicking the X button on the Search Box clears the currently entered string
After entering a string value into the Node Palette's search box and click on
the X button, the search box should be cleared
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -44,15 +41,13 @@ def NodePalette_ClearSelection():
:return: None
"""
from PySide2 import QtWidgets
import pyside_utils
from utils import TestHelper as helper
import azlmbr.legacy.general as general
import pyside_utils
TEST_STRING = "Test String"
# 1) Open Script Canvas window (Tools > Script Canvas)
@@ -90,4 +85,4 @@ if __name__ == "__main__":
imports.init()
from utils import Report
Report.start_test(NodePalette_ClearSelection)
Report.start_test(NodePalette_HappyPath_ClearSelection)
@@ -0,0 +1,117 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
# fmt: off
class Tests():
node_duplicated = ("Successfully duplicated node", "Failed to duplicate the node")
# fmt: on
def Node_HappyPath_DuplicateNode():
"""
Summary:
Duplicating node in graph
Expected Behavior:
Upon selecting a node and pressing Ctrl+D, the node will be duplicated
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
2) Open a new graph
3) Add node to graph
4) Duplicate node
5) Verify the node was duplicated6) Verify the node was duplicated
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import editor_python_test_tools.pyside_utils as pyside_utils
import azlmbr.legacy.general as general
WAIT_FRAMES = 200
NODE_NAME = "Print"
NODE_CATEGORY = "Debug"
EXPECTED_STRING = f"{NODE_NAME} - {NODE_CATEGORY} (2 Selected)"
def command_line_input(command_str):
cmd_action = pyside_utils.find_child_by_pattern(
sc_main, {"objectName": "action_ViewCommandLine", "type": QtWidgets.QAction}
)
cmd_action.trigger()
textbox = sc.findChild(QtWidgets.QLineEdit, "commandText")
QtTest.QTest.keyClicks(textbox, command_str)
QtTest.QTest.keyClick(textbox, Qt.Key_Enter, Qt.NoModifier)
def grab_title_text():
scroll_area = node_inspector.findChild(QtWidgets.QScrollArea, "")
QtTest.QTest.keyClick(graph, "a", Qt.ControlModifier, WAIT_FRAMES)
background = scroll_area.findChild(QtWidgets.QFrame, "Background")
title = background.findChild(QtWidgets.QLabel, "Title")
text = title.findChild(QtWidgets.QLabel, "Title")
return text.text()
# 1) Open Script Canvas window (Tools > Script Canvas)
general.idle_enable(True)
general.open_pane("Script Canvas")
helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0)
# 2) Open a new graph
editor_window = pyside_utils.get_editor_main_window()
sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas")
sc_main = sc.findChild(QtWidgets.QMainWindow)
create_new_graph = pyside_utils.find_child_by_pattern(
sc_main, {"objectName": "action_New_Script", "type": QtWidgets.QAction}
)
if sc.findChild(QtWidgets.QDockWidget, "NodeInspector") is None:
action = pyside_utils.find_child_by_pattern(sc, {"text": "Node Inspector", "type": QtWidgets.QAction})
action.trigger()
node_inspector = sc.findChild(QtWidgets.QDockWidget, "NodeInspector")
create_new_graph.trigger()
# 3) Add node
command_line_input("add_node Print")
# 4) Duplicate node
graph_view = sc.findChild(QtWidgets.QFrame, "graphicsViewFrame")
graph = graph_view.findChild(QtWidgets.QWidget, "")
# There are currently no utilities available to directly duplicate the node,
# therefore the node is selected using CTRL+A on the graph to select
# it and then CTRL+D to duplicate
sc_main.activateWindow()
QtTest.QTest.keyClick(graph, "a", Qt.ControlModifier, WAIT_FRAMES)
QtTest.QTest.keyClick(graph, "d", Qt.ControlModifier, WAIT_FRAMES)
# 5) Verify the node was duplicated
# As direct interaction with node is not available the text on the label
# inside the Node Inspector is validated showing two nodes exist
after_dup = grab_title_text()
Report.result(Tests.node_duplicated, after_dup == EXPECTED_STRING)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(Node_HappyPath_DuplicateNode)
@@ -7,33 +7,30 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: C1702821 // C1702832
Test Case Title: Retain visibility, size and location upon Script Canvas restart
"""
# fmt: off
class Tests():
relaunch_sc = ("Script Canvas window is relaunched", "Failed to relaunch Script Canvas window")
test_panes_visible = ("All the test panes are opened", "Failed to open one or more test panes")
close_pane_1 = ("Test pane 1 is closed", "Failed to close test pane 1")
visiblity_retained = ("Test pane retained its visiblity on SC restart", "Failed to retain visiblity of test pane on SC restart")
resize_pane_3 = ("Test pane 3 resized successfully", "Failed to resize Test pane 3")
size_retained = ("Test pane retained its size on SC restart", "Failed to retain size of test pane on SC restart")
location_changed = ("Location of test pane 2 changed successfully", "Failed to change locatio of test pane 2")
location_retained = ("Test pane retained its location on SC restart", "Failed to retain location of test pane on SC restart")
relaunch_sc = ("Script Canvas window is relaunched", "Failed to relaunch Script Canvas window")
test_panes_visible = ("All the test panes are opened", "Failed to open one or more test panes")
close_pane_1 = ("Test pane 1 is closed", "Failed to close test pane 1")
visibility_retained = ("Test pane retained its visibility on SC restart", "Failed to retain visibility of test pane on SC restart")
resize_pane_3 = ("Test pane 3 resized successfully", "Failed to resize Test pane 3")
size_retained = ("Test pane retained its size on SC restart", "Failed to retain size of test pane on SC restart")
location_changed = ("Location of test pane 2 changed successfully", "Failed to change location of test pane 2")
location_retained = ("Test pane retained its location on SC restart", "Failed to retain location of test pane on SC restart")
# fmt: on
def Pane_RetainOnSCRestart():
def Pane_Default_RetainOnSCRestart():
"""
Summary:
The Script Canvas window is opened to verify if Script canvas panes can retain its visibility, size and location
upon ScriptCanvas restart.
Expected Behavior:
The ScriptCanvas pane retain it's visiblity, size and location upon ScriptCanvas restart.
The ScriptCanvas pane retain it's visibility, size and location upon ScriptCanvas restart.
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -42,7 +39,7 @@ def Pane_RetainOnSCRestart():
4) Change dock location of test pane 2
5) Resize test pane 3
6) Relaunch Script Canvas
7) Verify if test pane 1 retain its visiblity
7) Verify if test pane 1 retain its visibility
8) Verify if location of test pane 2 is retained
9) Verify if size of test pane 3 is retained
10) Restore default layout and close SC window
@@ -55,6 +52,10 @@ def Pane_RetainOnSCRestart():
:return: None
"""
# Pyside imports
from PySide2 import QtCore, QtWidgets
from PySide2.QtCore import Qt
# Helper imports
from utils import Report
from utils import TestHelper as helper
@@ -63,11 +64,6 @@ def Pane_RetainOnSCRestart():
# Open 3D Engine Imports
import azlmbr.legacy.general as general
# Pyside imports
from PySide2 import QtCore, QtWidgets
from PySide2.QtCore import Qt
# Constants
TEST_PANE_1 = "NodePalette" # test visibility
TEST_PANE_2 = "VariableManager" # test location
TEST_PANE_3 = "NodeInspector" # test size
@@ -128,10 +124,10 @@ def Pane_RetainOnSCRestart():
sc_visible = helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0)
Report.result(Tests.relaunch_sc, sc_visible)
# 7) Verify if test pane 1 retain its visiblity
# 7) Verify if test pane 1 retain its visibility
editor_window = pyside_utils.get_editor_main_window()
sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas")
Report.result(Tests.visiblity_retained, not find_pane(sc, TEST_PANE_1).isVisible())
Report.result(Tests.visibility_retained, not find_pane(sc, TEST_PANE_1).isVisible())
# 8) Verify if location of test pane 2 is retained
sc_main = sc.findChild(QtWidgets.QMainWindow)
@@ -156,7 +152,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(Pane_RetainOnSCRestart)
Report.start_test(Pane_Default_RetainOnSCRestart)
@@ -7,24 +7,21 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: C1702824
Test Case Title: Docking
"""
# fmt: off
class Tests():
pane_opened = ("Pane is opened successfully", "Failed to open pane")
dock_pane = ("Pane is docked successfully", "Failed to dock Pane into one or more allowed area")
pane_opened = ("Pane is opened successfully", "Failed to open pane")
dock_pane = ("Pane is docked successfully", "Failed to dock Pane into one or more allowed area")
# fmt: on
def Docking_Pane():
def Pane_HappyPath_DocksProperly():
"""
Summary:
The Script Canvas window is opened to verify if Script canvas panes can be docked into
every possible area of Script Canvas main window.
The Script Canvas window is opened to verify if Script canvas panes can be docked into every
possible area of Script Canvas main window. (top, bottom, right and left sides of the window)
Expected Behavior:
The pane docks successfully.
@@ -43,12 +40,6 @@ def Docking_Pane():
:return: None
"""
# Helper imports
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import editor_python_test_tools.pyside_utils as pyside_utils
@@ -110,7 +101,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(Docking_Pane)
Report.start_test(Pane_HappyPath_DocksProperly)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: C1702834 // C1702823
Test Case Title: Opening pane // Closing pane
"""
@@ -21,13 +18,13 @@ class Tests():
# fmt: on
def Opening_Closing_Pane():
def Pane_HappyPath_OpenCloseSuccessfully():
"""
Summary:
The Script Canvas window is opened to verify if Script canvas panes can be opened and closed.
The Script Canvas window is opened to verify if Script Canvas panes can be opened and closed.
Expected Behavior:
The pane opens and closes successfully.
The panes open and close successfully.
Test Steps:
1) Open Script Canvas window (Tools > Script Canvas)
@@ -113,7 +110,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(Opening_Closing_Pane)
Report.start_test(Pane_HappyPath_OpenCloseSuccessfully)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: C1702829
Test Case Title: Resizing pane
"""
@@ -20,10 +17,10 @@ class Tests():
# fmt: on
def Resizing_Pane():
def Pane_HappyPath_ResizesProperly():
"""
Summary:
The Script Canvas window is opened to verify if Script canvas panes can be resized and scaled
The Script Canvas window is opened to verify if Script Canvas panes can be resized and scaled
Expected Behavior:
The pane is resized and scaled appropriately.
@@ -32,7 +29,7 @@ def Resizing_Pane():
1) Open Script Canvas window (Tools > Script Canvas)
2) Restore default layout
3) Make sure pane is opened
4) Resize pane
4) Resize pane and verify change
5) Restore default layout
6) Close Script Canvas window
@@ -44,6 +41,8 @@ def Resizing_Pane():
:return: None
"""
from PySide2 import QtWidgets
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import editor_python_test_tools.pyside_utils as pyside_utils
@@ -51,9 +50,6 @@ def Resizing_Pane():
# Open 3D Engine imports
import azlmbr.legacy.general as general
# Pyside imports
from PySide2 import QtWidgets
PANE_WIDGET = "NodePalette"
SCALE_INT = 10
@@ -86,7 +82,7 @@ def Resizing_Pane():
Report.result(Tests.open_pane, pane.isVisible())
# 4) Resize pane
# 4) Resize pane and verify change
initial_size = pane.frameSize()
pane.resize(initial_size.width() + SCALE_INT, initial_size.height() + SCALE_INT)
new_size = pane.frameSize()
@@ -109,4 +105,4 @@ if __name__ == "__main__":
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(Resizing_Pane)
Report.start_test(Pane_HappyPath_ResizesProperly)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: C1702825 // C1702831
Test Case Title: Undocking // Closing script canvas with the pane floating
"""
@@ -21,7 +18,7 @@ class Tests():
# fmt: on
def UnDockedPane_CloseSCWindow():
def Pane_Undocked_ClosesSuccessfully():
"""
Summary:
The Script Canvas window is opened with one of the pane undocked.
@@ -45,6 +42,8 @@ def UnDockedPane_CloseSCWindow():
:return: None
"""
from PySide2 import QtWidgets
# Helper imports
from utils import Report
from utils import TestHelper as helper
@@ -53,9 +52,6 @@ def UnDockedPane_CloseSCWindow():
# Open 3D Engine imports
import azlmbr.legacy.general as general
# Pyside imports
from PySide2 import QtWidgets
TEST_PANE = "NodePalette" # Chosen most commonly used pane
def click_menu_option(window, option_text):
@@ -120,7 +116,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(UnDockedPane_CloseSCWindow)
Report.start_test(Pane_Undocked_ClosesSuccessfully)
@@ -7,29 +7,26 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92569253 // T92569254
Test Case Title: On Entity Activated // On Entity Deactivated
"""
# fmt: off
class Tests():
level_created = ("Successfully created temp level", "Failed to create temp level")
controller_exists = ("Successfully found controller entity", "Failed to find controller entity")
activated_exists = ("Successfully found activated entity", "Failed to find activated entity")
deactivated_exists = ("Successfully found deactivated entity","Failed to find deactivated entity")
start_states_correct = ("Start states set up successfully", "Start states set up incorrectly")
game_mode_entered = ("Successfully entered game mode" "Failed to enter game mode")
lines_found = ("Successfully found expected prints", "Failed to find expected prints")
game_mode_exited = ("Successfully exited game mode" "Failed to exit game mode")
level_created = ("Successfully created temp level", "Failed to create temp level")
controller_exists = ("Successfully found controller entity", "Failed to find controller entity")
activated_exists = ("Successfully found activated entity", "Failed to find activated entity")
deactivated_exists = ("Successfully found deactivated entity", "Failed to find deactivated entity")
start_states_correct = ("Start states set up successfully", "Start states set up incorrectly")
game_mode_entered = ("Successfully entered game mode" "Failed to enter game mode")
lines_found = ("Successfully found expected prints", "Failed to find expected prints")
game_mode_exited = ("Successfully exited game mode" "Failed to exit game mode")
# fmt: on
def OnEntityActivatedDeactivated_PrintMessage():
def ScriptCanvasComponent_OnEntityActivatedDeactivated_PrintMessage():
"""
Summary:
Verify that the On Entity Activation node is working as expected
Verify that the On Entity Activated/On Entity Deactivated nodes are working as expected
Expected Behavior:
Upon entering game mode, the Controller entity will wait 1 second and then activate the ActivationTest
@@ -54,9 +51,9 @@ def OnEntityActivatedDeactivated_PrintMessage():
"""
import os
from utils import TestHelper as helper
from editor_entity_utils import EditorEntity as Entity
from utils import Report
from utils import TestHelper as helper
from utils import Tracer
import azlmbr.legacy.general as general
@@ -68,33 +65,45 @@ def OnEntityActivatedDeactivated_PrintMessage():
controller_dict = {
"name": "Controller",
"status": "active",
"path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "controller.scriptcanvas")
"path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "controller.scriptcanvas"),
}
activated_dict = {
"name": "ActivationTest",
"status": "inactive",
"path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "activator.scriptcanvas")
"path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "activator.scriptcanvas"),
}
deactivated_dict = {
"name": "DeactivationTest",
"status": "active",
"path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "deactivator.scriptcanvas")
"path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "deactivator.scriptcanvas"),
}
def get_asset(asset_path):
return azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, "GetAssetIdByPath", asset_path, azlmbr.math.Uuid(), False)
return azlmbr.asset.AssetCatalogRequestBus(
azlmbr.bus.Broadcast, "GetAssetIdByPath", asset_path, azlmbr.math.Uuid(), False
)
def setup_level():
def create_editor_entity(entity_dict:dict, entity_to_activate:EditorEntity=None, entity_to_deactivate:EditorEntity=None) -> EditorEntity:
def create_editor_entity(
entity_dict: dict, entity_to_activate: EditorEntity = None, entity_to_deactivate: EditorEntity = None
) -> EditorEntity:
entity = Entity.create_editor_entity(entity_dict["name"])
entity.set_start_status(entity_dict["status"])
sc_component = entity.add_component("Script Canvas")
sc_component.set_component_property_value("Script Canvas Asset|Script Canvas Asset", get_asset(entity_dict["path"]))
sc_component.set_component_property_value(
"Script Canvas Asset|Script Canvas Asset", get_asset(entity_dict["path"])
)
if entity_dict["name"] == "Controller":
sc_component.get_property_tree()
sc_component.set_component_property_value("Properties|Variable Fields|Variables|[0]|Name,Value|Datum|Datum|EntityToActivate", entity_to_activate.id)
sc_component.set_component_property_value("Properties|Variable Fields|Variables|[1]|Name,Value|Datum|Datum|EntityToDeactivate", entity_to_deactivate.id)
sc_component.set_component_property_value(
"Properties|Variable Fields|Variables|[0]|Name,Value|Datum|Datum|EntityToActivate",
entity_to_activate.id,
)
sc_component.set_component_property_value(
"Properties|Variable Fields|Variables|[1]|Name,Value|Datum|Datum|EntityToDeactivate",
entity_to_deactivate.id,
)
return entity
activated = create_editor_entity(activated_dict)
@@ -110,7 +119,7 @@ def OnEntityActivatedDeactivated_PrintMessage():
Report.critical_result(test_tuple, entity.id.IsValid())
return entity
def validate_start_state(entity:EditorEntity, expected_state:str):
def validate_start_state(entity: EditorEntity, expected_state: str):
"""
Validate that the starting state of the entity is correct, if it isn't then attempt to rectify and recheck.
:return: bool: Whether state is set as expected
@@ -176,8 +185,8 @@ def OnEntityActivatedDeactivated_PrintMessage():
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
imports.init()
from utils import Report
Report.start_test(OnEntityActivatedDeactivated_PrintMessage)
Report.start_test(ScriptCanvasComponent_OnEntityActivatedDeactivated_PrintMessage)
@@ -7,12 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: C92569165, C92569167, C92569168, C92569170
Test Case Title: Tools > Node Palette toggles the Node Palette
Tools > Node Inspector toggles the Node Inspector
Tools > Bookmarks toggles the Bookmarks
Tools > Variable Manager toggles the Variable Manager
"""
@@ -33,7 +27,7 @@ class Tests():
# fmt: on
def Toggle_ScriptCanvasTools():
def ScriptCanvasTools_Toggle_OpenCloseSuccess():
"""
Summary:
Toggle Node Palette, Node Inspector, Bookmarks and Variable Manager in Script Canvas.
@@ -58,6 +52,8 @@ def Toggle_ScriptCanvasTools():
:return: None
"""
from PySide2 import QtWidgets
from utils import Report
from utils import TestHelper as helper
import pyside_utils
@@ -65,9 +61,6 @@ def Toggle_ScriptCanvasTools():
# Open 3D Engine imports
import azlmbr.legacy.general as general
# Pyside imports
from PySide2 import QtWidgets
def click_menu_option(window, option_text):
action = pyside_utils.find_child_by_pattern(window, {"text": option_text, "type": QtWidgets.QAction})
action.trigger()
@@ -126,7 +119,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(Toggle_ScriptCanvasTools)
Report.start_test(ScriptCanvasTools_Toggle_OpenCloseSuccess)
@@ -7,10 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92562986
Test Case Title: Changing the assigned Script Canvas Asset on an entity properly updates
level functionality
"""
@@ -24,7 +20,7 @@ class Tests():
# fmt: on
def ScriptCanvas_ChangingAssets():
def ScriptCanvas_ChangingAssets_ComponentStable():
"""
Summary:
Changing the assigned Script Canvas Asset on an entity properly updates level functionality
@@ -57,7 +53,6 @@ def ScriptCanvas_ChangingAssets():
import azlmbr.math as math
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.paths as paths
LEVEL_NAME = "tmp_level"
ASSET_1 = os.path.join("scriptcanvas", "ScriptCanvas_TwoComponents0.scriptcanvas")
@@ -83,7 +78,6 @@ def ScriptCanvas_ChangingAssets():
Report.result(Tests.found_lines, find_expected_line(EXP_LINE))
helper.exit_game_mode(Tests.game_mode_exited)
# 1) Create temp level
general.idle_enable(True)
result = general.create_level_no_prompt(LEVEL_NAME, 128, 1, 512, True)
@@ -112,4 +106,4 @@ if __name__ == "__main__":
imports.init()
from utils import Report
Report.start_test(ScriptCanvas_ChangingAssets)
Report.start_test(ScriptCanvas_ChangingAssets_ComponentStable)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92563190
Test Case Title: A single Entity with two Script Canvas components works properly
"""
@@ -26,7 +23,7 @@ class LogLines:
expected_lines = ["Greetings from the first script", "Greetings from the second script"]
def ScriptCanvas_TwoComponents():
def ScriptCanvas_TwoComponents_InteractSuccessfully():
"""
Summary:
A test entity contains two Script Canvas components with different unique script canvas files.
@@ -57,6 +54,7 @@ def ScriptCanvas_TwoComponents():
import hydra_editor_utils as hydra
from utils import Report
from utils import Tracer
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.asset as asset
@@ -111,7 +109,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(ScriptCanvas_TwoComponents)
Report.start_test(ScriptCanvas_TwoComponents_InteractSuccessfully)
@@ -7,26 +7,23 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92563191
Test Case Title: Two Entities can use the same Graph asset successfully at RunTime
"""
# fmt: off
class Tests():
level_created = ("New level created", "New level not created")
level_created = ("New level created successfully", "New level failed to create")
game_mode_entered = ("Game Mode successfully entered", "Game mode failed to enter")
game_mode_exited = ("Game Mode successfully exited", "Game mode failed to exited")
found_lines = ("Expected log lines were found", "Expected log lines were not found")
# fmt: on
def ScriptCanvas_TwoEntities():
def ScriptCanvas_TwoEntities_UseSimultaneously():
"""
Summary:
Two Entities can use the same Graph asset successfully at RunTime. The script canvas asset
attached to the enties will print the respective entity names.
attached to the entities will print the respective entity names.
Expected Behavior:
When game mode is entered, respective strings of different entities should be printed.
@@ -48,9 +45,10 @@ def ScriptCanvas_TwoEntities():
import os
import hydra_editor_utils as hydra
from utils import TestHelper as helper
from utils import Tracer
import hydra_editor_utils as hydra
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.asset as asset
@@ -103,4 +101,4 @@ if __name__ == "__main__":
imports.init()
from utils import Report
Report.start_test(ScriptCanvas_TwoEntities)
Report.start_test(ScriptCanvas_TwoEntities_UseSimultaneously)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92569013
Test Case Title: Script Event file can be created
"""
@@ -117,7 +114,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(CreateScriptEventFile)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92567320
Test Case Title: Script Events: Can send and receive a script event successfully
"""
@@ -23,7 +20,7 @@ class Tests():
# fmt: on
def ScriptEvents_SendReceiveSuccessfully():
def ScriptEvents_Default_SendReceiveSuccessfully():
"""
Summary:
An entity exists in the level that contains a Script Canvas component. In the graph is both a Send Event
@@ -103,7 +100,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(ScriptEvents_SendReceiveSuccessfully)
Report.start_test(ScriptEvents_Default_SendReceiveSuccessfully)
@@ -7,9 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92567321
Test Case Title: Script Events: Can send and receive a script event across multiple entities successfully
"""
@@ -24,10 +21,11 @@ class Tests():
# fmt: on
def ScriptEvents_SendReceiveAcrossMultiple():
def ScriptEvents_HappyPath_SendReceiveAcrossMultiple():
"""
Summary:
EntityA and EntityB will be created in a level. Attached to both will be a Script Canvas component. The Script Event created for the test will be sent from EntityA to EntityB.
EntityA and EntityB will be created in a level. Attached to both will be a Script Canvas component.
The Script Event created for the test will be sent from EntityA to EntityB.
Expected Behavior:
The output of the Script Event should be printed to the console
@@ -49,7 +47,7 @@ def ScriptEvents_SendReceiveAcrossMultiple():
:return: None
"""
import os
from editor_entity_utils import EditorEntity as Entity
from utils import Report
from utils import TestHelper as helper
@@ -65,20 +63,19 @@ def ScriptEvents_SendReceiveAcrossMultiple():
"assetA": os.path.join("ScriptCanvas", f"{ASSET_PREFIX}A.scriptcanvas"),
"assetB": os.path.join("ScriptCanvas", f"{ASSET_PREFIX}B.scriptcanvas"),
}
sc_for_entities = {
"EntityA": asset_paths["assetA"],
"EntityB": asset_paths["assetB"]
}
sc_for_entities = {"EntityA": asset_paths["assetA"], "EntityB": asset_paths["assetB"]}
EXPECTED_LINES = ["Incoming Message Received"]
def get_asset(asset_path):
return azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, "GetAssetIdByPath", asset_path, azlmbr.math.Uuid(), False)
return azlmbr.asset.AssetCatalogRequestBus(
azlmbr.bus.Broadcast, "GetAssetIdByPath", asset_path, azlmbr.math.Uuid(), False
)
def create_editor_entity(name, sc_asset):
entity = Entity.create_editor_entity(name)
sc_comp = entity.add_component("Script Canvas")
sc_comp.set_component_property_value("Script Canvas Asset|Script Canvas Asset", get_asset(sc_asset))
Report.critical_result(Tests.__dict__[name.lower()+"_created"], entity.id.isValid())
Report.critical_result(Tests.__dict__[name.lower() + "_created"], entity.id.isValid())
def locate_expected_lines(line_list: list):
found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints]
@@ -112,8 +109,8 @@ def ScriptEvents_SendReceiveAcrossMultiple():
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
imports.init()
from utils import Report
Report.start_test(ScriptEvents_SendReceiveAcrossMultiple)
Report.start_test(ScriptEvents_HappyPath_SendReceiveAcrossMultiple)
@@ -106,7 +106,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(ScriptEvents_ReturnSetType_Successfully)
@@ -29,79 +29,73 @@ TEST_DIRECTORY = os.path.dirname(__file__)
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
@pytest.mark.test_case_id("C1702834", "C1702823")
def test_Opening_Closing_Pane(self, request, workspace, editor, launcher_platform):
from . import Opening_Closing_Pane as test_module
def test_Pane_HappyPath_OpenCloseSuccessfully(self, request, workspace, editor, launcher_platform):
from . import Pane_HappyPath_OpenCloseSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("C1702824")
def test_Docking_Pane(self, request, workspace, editor, launcher_platform):
from . import Docking_Pane as test_module
def test_Pane_HappyPath_DocksProperly(self, request, workspace, editor, launcher_platform):
from . import Pane_HappyPath_DocksProperly as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("C1702829")
def test_Resizing_Pane(self, request, workspace, editor, launcher_platform):
from . import Resizing_Pane as test_module
def test_Pane_HappyPath_ResizesProperly(self, request, workspace, editor, launcher_platform):
from . import Pane_HappyPath_ResizesProperly as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92563190")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_ScriptCanvas_TwoComponents(self, request, workspace, editor, launcher_platform, level):
def test_ScriptCanvas_TwoComponents_InteractSuccessfully(self, request, workspace, editor, launcher_platform, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvas_TwoComponents as test_module
from . import ScriptCanvas_TwoComponents_InteractSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92562986")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_ScriptCanvas_ChangingAssets(self, request, workspace, editor, launcher_platform, project, level):
def test_ScriptCanvas_ChangingAssets_ComponentStable(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvas_ChangingAssets as test_module
from . import ScriptCanvas_ChangingAssets_ComponentStable as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92569079", "T92569081")
def test_Graph_ZoomInZoomOut(self, request, workspace, editor, launcher_platform):
from . import Graph_ZoomInZoomOut as test_module
def test_Graph_HappyPath_ZoomInZoomOut(self, request, workspace, editor, launcher_platform):
from . import Graph_HappyPath_ZoomInZoomOut as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92568940")
def test_NodePalette_SelectNode(self, request, workspace, editor, launcher_platform):
from . import NodePalette_SelectNode as test_module
def test_NodePalette_HappyPath_CanSelectNode(self, request, workspace, editor, launcher_platform):
from . import NodePalette_HappyPath_CanSelectNode as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92569253")
@pytest.mark.test_case_id("T92569254")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_OnEntityActivatedDeactivated_PrintMessage(self, request, workspace, editor, launcher_platform, project, level):
def test_ScriptCanvasComponent_OnEntityActivatedDeactivated_PrintMessage(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import OnEntityActivatedDeactivated_PrintMessage as test_module
from . import ScriptCanvasComponent_OnEntityActivatedDeactivated_PrintMessage as test_module
self._run_test(request, workspace, editor, test_module)
<<<<<<< HEAD
def test_NodePalette_HappyPath_ClearSelection(self, request, workspace, editor, launcher_platform, project):
from . import NodePalette_HappyPath_ClearSelection as test_module
=======
@pytest.mark.test_case_id("T92562993")
def test_NodePalette_ClearSelection(self, request, workspace, editor, launcher_platform, project):
from . import NodePalette_ClearSelection as test_module
>>>>>>> main
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92563191")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_ScriptCanvas_TwoEntities(self, request, workspace, editor, launcher_platform, project, level):
def test_ScriptCanvas_TwoEntities_UseSimultaneously(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvas_TwoEntities as test_module
from . import ScriptCanvas_TwoEntities_UseSimultaneously as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92569013")
def test_AssetEditor_CreateScriptEventFile(self, request, workspace, editor, launcher_platform, project):
def test_ScriptEvent_HappyPath_CreatedWithoutError(self, request, workspace, editor, launcher_platform, project):
def teardown():
file_system.delete(
[os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True
@@ -110,77 +104,72 @@ class TestAutomation(TestAutomationBase):
file_system.delete(
[os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True
)
from . import AssetEditor_CreateScriptEventFile as test_module
from . import ScriptEvent_HappyPath_CreatedWithoutError as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92569165", "T92569167", "T92569168", "T92569170")
def test_Toggle_ScriptCanvasTools(self, request, workspace, editor, launcher_platform):
from . import Toggle_ScriptCanvasTools as test_module
def test_ScriptCanvasTools_Toggle_OpenCloseSuccess(self, request, workspace, editor, launcher_platform):
from . import ScriptCanvasTools_Toggle_OpenCloseSuccess as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92568982")
def test_NodeInspector_RenameVariable(self, request, workspace, editor, launcher_platform, project):
from . import NodeInspector_RenameVariable as test_module
def test_NodeInspector_HappyPath_VariableRenames(self, request, workspace, editor, launcher_platform, project):
from . import NodeInspector_HappyPath_VariableRenames as test_module
self._run_test(request, workspace, editor, test_module)
def test_Debugger_HappyPath_TargetMultipleGraphs(self, request, workspace, editor, launcher_platform, project):
from . import Debugger_HappyPath_TargetMultipleGraphs as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92569137")
def test_Debugging_TargetMultipleGraphs(self, request, workspace, editor, launcher_platform, project):
from . import Debugging_TargetMultipleGraphs as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92568856")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_Debugging_TargetMultipleEntities(self, request, workspace, editor, launcher_platform, project, level):
def test_Debugger_HappyPath_TargetMultipleEntities(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import Debugging_TargetMultipleEntities as test_module
from . import Debugger_HappyPath_TargetMultipleEntities as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92569049", "T92569051")
def test_EditMenu_UndoRedo(self, request, workspace, editor, launcher_platform, project):
from . import EditMenu_UndoRedo as test_module
def test_EditMenu_Default_UndoRedo(self, request, workspace, editor, launcher_platform, project):
from . import EditMenu_Default_UndoRedo as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("C1702825", "C1702831")
def test_UnDockedPane_CloseSCWindow(self, request, workspace, editor, launcher_platform):
from . import UnDockedPane_CloseSCWindow as test_module
def test_Pane_Undocked_ClosesSuccessfully(self, request, workspace, editor, launcher_platform):
from . import Pane_Undocked_ClosesSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92562978")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_Entity_AddScriptCanvasComponent(self, request, workspace, editor, launcher_platform, project, level):
def test_Entity_HappyPath_AddScriptCanvasComponent(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import Entity_AddScriptCanvasComponent as test_module
from . import Entity_HappyPath_AddScriptCanvasComponent as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("C1702821", "C1702832")
def test_Pane_RetainOnSCRestart(self, request, workspace, editor, launcher_platform):
from . import Pane_RetainOnSCRestart as test_module
def test_Pane_Default_RetainOnSCRestart(self, request, workspace, editor, launcher_platform):
from . import Pane_Default_RetainOnSCRestart as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92567321")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_ScriptEvents_SendReceiveAcrossMultiple(self, request, workspace, editor, launcher_platform, project, level):
def test_ScriptEvents_HappyPath_SendReceiveAcrossMultiple(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptEvents_SendReceiveAcrossMultiple as test_module
from . import ScriptEvents_HappyPath_SendReceiveAcrossMultiple as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.test_case_id("T92567320")
@pytest.mark.parametrize("level", ["tmp_level"])
def test_ScriptEvents_SendReceiveSuccessfully(self, request, workspace, editor, launcher_platform, project, level):
def test_ScriptEvents_Default_SendReceiveSuccessfully(self, request, workspace, editor, launcher_platform, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptEvents_SendReceiveSuccessfully as test_module
from . import ScriptEvents_Default_SendReceiveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -195,7 +184,7 @@ class TestAutomation(TestAutomationBase):
def test_NodeCategory_ExpandOnClick(self, request, workspace, editor, launcher_platform):
from . import NodeCategory_ExpandOnClick as test_module
self._run_test(request, workspace, editor, test_module)
def test_NodePalette_SearchText_Deletion(self, request, workspace, editor, launcher_platform):
from . import NodePalette_SearchText_Deletion as test_module
self._run_test(request, workspace, editor, test_module)
@@ -204,6 +193,10 @@ class TestAutomation(TestAutomationBase):
from . import VariableManager_UnpinVariableType_Works as test_module
self._run_test(request, workspace, editor, test_module)
def test_Node_HappyPath_DuplicateNode(self, request, workspace, editor, launcher_platform):
from . import Node_HappyPath_DuplicateNode as test_module
self._run_test(request, workspace, editor, test_module)
# NOTE: We had to use hydra_test_utils.py, as TestAutomationBase run_test method
# fails because of pyside_utils import
@pytest.mark.SUITE_periodic
@@ -214,18 +207,16 @@ class TestScriptCanvasTests(object):
The following tests use hydra_test_utils.py to launch the editor and validate the results.
"""
@pytest.mark.test_case_id("T92569037", "T92569039")
def test_FileMenu_New_Open(self, request, editor, launcher_platform):
def test_FileMenu_Default_NewAndOpen(self, request, editor, launcher_platform):
expected_lines = [
"File->New action working as expected: True",
"File->Open action working as expected: True",
]
hydra.launch_and_validate_results(
request, TEST_DIRECTORY, editor, "FileMenu_New_Open.py", expected_lines, auto_test_mode=False, timeout=60,
request, TEST_DIRECTORY, editor, "FileMenu_Default_NewAndOpen.py", expected_lines, auto_test_mode=False, timeout=60,
)
@pytest.mark.test_case_id("T92568942")
def test_AssetEditor_NewScriptEvent(self, request, editor, launcher_platform):
def test_NewScriptEventButton_HappyPath_ContainsSCCategory(self, request, editor, launcher_platform):
expected_lines = [
"New Script event action found: True",
"Asset Editor opened: True",
@@ -236,14 +227,13 @@ class TestScriptCanvasTests(object):
request,
TEST_DIRECTORY,
editor,
"AssetEditor_NewScriptEvent.py",
"NewScriptEventButton_HappyPath_ContainsSCCategory.py",
expected_lines,
auto_test_mode=False,
timeout=60,
)
@pytest.mark.test_case_id("T92563068", "T92563070")
def test_GraphClose_SavePrompt(self, request, editor, launcher_platform):
def test_GraphClose_Default_SavePrompt(self, request, editor, launcher_platform):
expected_lines = [
"New graph created: True",
"Save prompt opened as expected: True",
@@ -253,14 +243,13 @@ class TestScriptCanvasTests(object):
request,
TEST_DIRECTORY,
editor,
"GraphClose_SavePrompt.py",
"GraphClose_Default_SavePrompt.py",
expected_lines,
auto_test_mode=False,
timeout=60,
)
@pytest.mark.test_case_id("T92564789", "T92568873")
def test_VariableManager_CreateDeleteVars(self, request, editor, launcher_platform):
def test_VariableManager_Default_CreateDeleteVars(self, request, editor, launcher_platform):
var_types = ["Boolean", "Color", "EntityID", "Number", "String", "Transform", "Vector2", "Vector3", "Vector4"]
expected_lines = [f"Success: {var_type} variable is created" for var_type in var_types]
expected_lines.extend([f"Success: {var_type} variable is deleted" for var_type in var_types])
@@ -268,7 +257,7 @@ class TestScriptCanvasTests(object):
request,
TEST_DIRECTORY,
editor,
"VariableManager_CreateDeleteVars.py",
"VariableManager_Default_CreateDeleteVars.py",
expected_lines,
auto_test_mode=False,
timeout=60,
@@ -7,18 +7,13 @@ distribution (the "License"). All use of this software is governed by the Licens
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Test case ID: T92564789
Test Case Title: Each Variable type can be created
Test case ID: T92568873
Test Case Title: Each Variable type can be deleted
"""
def VariableManager_CreateDeleteVars():
def VariableManager_Default_CreateDeleteVars():
"""
Summary:
Each variable type can be created and deleted in variable manager.
Creating and deleting each type of variable in the Variable Manager pane
Expected Behavior:
Each variable type can be created and deleted in variable manager.
@@ -41,15 +36,13 @@ def VariableManager_CreateDeleteVars():
"""
from PySide2 import QtWidgets, QtCore, QtTest
from PySide2.QtCore import Qt
from utils import TestHelper as helper
import pyside_utils
import azlmbr.legacy.general as general
import pyside_utils
def generate_test_tuple(var_type, action):
return (f"{var_type} variable is {action}d", f"{var_type} variable is not {action}d")
@@ -118,4 +111,4 @@ if __name__ == "__main__":
imports.init()
from utils import Report
Report.start_test(VariableManager_CreateDeleteVars)
Report.start_test(VariableManager_Default_CreateDeleteVars)
@@ -115,7 +115,6 @@ if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from utils import Report
Report.start_test(VariableManager_UnpinVariableType_Works)
@@ -0,0 +1,171 @@
{
"Source": "Levels/PrefabLevel_OpensLevelWithEntities/PrefabLevel_OpensLevelWithEntities.prefab",
"ContainerEntity": {
"Id": "Entity_[403811863694]",
"Name": "Level",
"Components": {
"Component_[10582285743525614098]": {
"$type": "SelectionComponent",
"Id": 10582285743525614098
},
"Component_[12253783095375428046]": {
"$type": "EditorInspectorComponent",
"Id": 12253783095375428046
},
"Component_[13764860261821571747]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 13764860261821571747,
"Parent Entity": ""
},
"Component_[15844324401733835865]": {
"$type": "EditorEntitySortComponent",
"Id": 15844324401733835865
},
"Component_[1605854641405361768]": {
"$type": "EditorLockComponent",
"Id": 1605854641405361768
},
"Component_[17698173984524983803]": {
"$type": "EditorOnlyEntityComponent",
"Id": 17698173984524983803
},
"Component_[3444251662966224826]": {
"$type": "EditorPendingCompositionComponent",
"Id": 3444251662966224826
},
"Component_[4231768881195179982]": {
"$type": "EditorVisibilityComponent",
"Id": 4231768881195179982
},
"Component_[4722360315410084479]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 4722360315410084479
},
"Component_[7614719100624882952]": {
"$type": "EditorPrefabComponent",
"Id": 7614719100624882952
},
"Component_[9585901769691795481]": {
"$type": "EditorEntityIconComponent",
"Id": 9585901769691795481
}
},
"IsDependencyReady": true
},
"Entities": {
"Entity_[438171602062]": {
"Id": "Entity_[438171602062]",
"Name": "EntityWithPxCollider",
"Components": {
"Component_[11161653124805884473]": {
"$type": "EditorPendingCompositionComponent",
"Id": 11161653124805884473
},
"Component_[13116773315299882093]": {
"$type": "EditorOnlyEntityComponent",
"Id": 13116773315299882093
},
"Component_[15820915681461536711]": {
"$type": "EditorVisibilityComponent",
"Id": 15820915681461536711
},
"Component_[2222061938345834243]": {
"$type": "SelectionComponent",
"Id": 2222061938345834243
},
"Component_[3861913165076405600]": {
"$type": "EditorEntitySortComponent",
"Id": 3861913165076405600
},
"Component_[7118587015611303204]": {
"$type": "EditorLockComponent",
"Id": 7118587015611303204
},
"Component_[7751174327125555504]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 7751174327125555504
},
"Component_[8304730147756374057]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 8304730147756374057,
"Parent Entity": "Entity_[403811863694]",
"Transform Data": {
"Translate": [
0.0,
20.0,
34.0
]
}
},
"Component_[8866353210615920259]": {
"$type": "EditorEntityIconComponent",
"Id": 8866353210615920259
},
"Component_[8988181228601932779]": {
"$type": "EditorInspectorComponent",
"Id": 8988181228601932779
},
"Component_[7103333782129541775]": {
"$type": "EditorColliderComponent",
"Id": 7103333782129541775
}
},
"IsDependencyReady": true
},
"Entity_[532660882574]": {
"Id": "Entity_[532660882574]",
"Name": "EmptyEntity",
"Components": {
"Component_[16437814751543997955]": {
"$type": "EditorEntitySortComponent",
"Id": 16437814751543997955
},
"Component_[16751517102089557119]": {
"$type": "EditorPendingCompositionComponent",
"Id": 16751517102089557119
},
"Component_[16773275259304187949]": {
"$type": "EditorInspectorComponent",
"Id": 16773275259304187949
},
"Component_[17283539636910567200]": {
"$type": "SelectionComponent",
"Id": 17283539636910567200
},
"Component_[250004123617033400]": {
"$type": "EditorLockComponent",
"Id": 250004123617033400
},
"Component_[2791138963683667073]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 2791138963683667073,
"Parent Entity": "Entity_[403811863694]",
"Transform Data": {
"Translate": [
10.0,
20.0,
30.0
]
}
},
"Component_[3296942400051129145]": {
"$type": "EditorEntityIconComponent",
"Id": 3296942400051129145
},
"Component_[3422076964671342434]": {
"$type": "EditorOnlyEntityComponent",
"Id": 3422076964671342434
},
"Component_[3431895414183121731]": {
"$type": "EditorVisibilityComponent",
"Id": 3431895414183121731
},
"Component_[7072085777705148766]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 7072085777705148766
}
},
"IsDependencyReady": true
}
}
}
-6
View File
@@ -1,6 +0,0 @@
#Ignore these directories
SDKs
#ColinB (8/26)- I know there are depot files that this will ignore... But these files should not be
#here, they should all be in 3rdParty... so we will ignore them until I can move them, it should
#be OK for now because they shouldn't change at all anyway.
-8
View File
@@ -9,23 +9,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_pal_tool_dirs(pal_tool_dirs ${CMAKE_CURRENT_LIST_DIR}/Platform)
ly_add_target(
NAME CryCommon STATIC
NAMESPACE Legacy
FILES_CMAKE
crycommon_files.cmake
${pal_dir}/crycommon_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
PLATFORM_INCLUDE_FILES
${pal_dir}/crycommon_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PUBLIC
. # Lots of code without CryCommon/
.. # Dangerous since exports CryEngine's path (client code can do CrySystem/ without depending on that target)
${pal_dir}
${pal_tool_dirs}
BUILD_DEPENDENCIES
PUBLIC
AZ::AzCore
@@ -16,9 +16,6 @@
#include <LyShine/Bus/UiTransformBus.h>
#include <AzCore/Math/Vector2.h>
// forward declarations
class ITexture;
////////////////////////////////////////////////////////////////////////////////////////////////////
//! A sprite is a texture with extra information about how it behaves for 2D drawing
//! Currently a sprite exists on disk as a side car file next to the texture file.
@@ -80,9 +77,6 @@ public: // member functions
//! Set the borders of a given cell within the sprite-sheet.
virtual void SetCellBorders(int cellIndex, Borders borders) = 0;
//! Get the texture for this sprite
virtual ITexture* GetTexture() = 0;
//! Serialize this object for save/load
virtual void Serialize(TSerialize ser) = 0;
@@ -1,43 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <CryMemoryManager.h>
class MemoryManagerMock
: public IMemoryManager
{
public:
MOCK_METHOD1(GetProcessMemInfo,
bool(SProcessMemInfo& minfo));
MOCK_METHOD3(TraceDefineHeap,
HeapHandle(const char* heapName, size_t size, const void* pBase));
MOCK_METHOD6(TraceHeapAlloc,
void(HeapHandle heap, void* mem, size_t size, size_t blockSize, const char* sUsage, const char* sNameHint));
MOCK_METHOD3(TraceHeapFree,
void(HeapHandle heap, void* mem, size_t blockSize));
MOCK_METHOD1(TraceHeapSetColor,
void(uint32 color));
MOCK_METHOD0(TraceHeapGetColor,
uint32());
MOCK_METHOD1(TraceHeapSetLabel,
void(const char* sLabel));
MOCK_METHOD1(CreateCustomMemoryHeapInstance,
ICustomMemoryHeap* const (EAllocPolicy const eAllocPolicy));
MOCK_METHOD3(CreateGeneralExpandingMemoryHeap,
IGeneralMemoryHeap* (size_t upperLimit, size_t reserveSize, const char* sUsage));
MOCK_METHOD3(CreateGeneralMemoryHeap,
IGeneralMemoryHeap* (void* base, size_t sz, const char* sUsage));
MOCK_METHOD2(ReserveAddressRange,
IMemoryAddressRange* (size_t capacity, const char* sName));
MOCK_METHOD2(CreatePageMappingHeap,
IPageMappingHeap* (size_t addressSpace, const char* sName));
};
@@ -1,47 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
struct NetworkMock : public INetwork
{
NetworkMock() : m_gridMate(nullptr)
{
}
GridMate::IGridMate* m_gridMate;
void Release() override {}
void GetMemoryStatistics([[maybe_unused]] ICrySizer* pSizer) override {}
void GetBandwidthStatistics([[maybe_unused]] SBandwidthStats* const pStats) override {}
void GetPerformanceStatistics([[maybe_unused]] SNetworkPerformance* pSizer) override {}
void GetProfilingStatistics([[maybe_unused]] SNetworkProfilingStats* const pStats) override {}
void SyncWithGame([[maybe_unused]] ENetworkGameSync syncType) override {}
const char* GetHostName() override { return "testhostname"; }
GridMate::IGridMate* GetGridMate() override
{
return m_gridMate;
}
ChannelId GetChannelIdForSessionMember([[maybe_unused]] GridMate::GridMember* member) const override { return ChannelId(); }
ChannelId GetServerChannelId() const override { return ChannelId(); }
ChannelId GetLocalChannelId() const override { return ChannelId(); }
CTimeValue GetSessionTime() override { return CTimeValue(); }
void ChangedAspects([[maybe_unused]] EntityId id, [[maybe_unused]] NetworkAspectType aspectBits) override {}
void SetDelegatableAspectMask([[maybe_unused]] NetworkAspectType aspectBits) override {}
void SetObjectDelegatedAspectMask([[maybe_unused]] EntityId entityId, [[maybe_unused]] NetworkAspectType aspects, [[maybe_unused]] bool set) override {}
void DelegateAuthorityToClient([[maybe_unused]] EntityId entityId, [[maybe_unused]] ChannelId clientChannelId) override {}
void InvokeActorRMI([[maybe_unused]] EntityId entityId, [[maybe_unused]] uint8 actorExtensionId, [[maybe_unused]] ChannelId targetChannelFilter, [[maybe_unused]] IActorRMIRep& rep) override {}
void InvokeScriptRMI([[maybe_unused]] ISerializable* serializable, [[maybe_unused]] bool isServerRMI, [[maybe_unused]] ChannelId toChannelId = kInvalidChannelId, [[maybe_unused]] ChannelId avoidChannelId = kInvalidChannelId) override {}
void RegisterActorRMI([[maybe_unused]] IActorRMIRep* rep) override {}
void UnregisterActorRMI([[maybe_unused]] IActorRMIRep* rep) override {}
EntityId LocalEntityIdToServerEntityId([[maybe_unused]] EntityId localId) const override { return EntityId(); }
EntityId ServerEntityIdToLocalEntityId([[maybe_unused]] EntityId serverId, [[maybe_unused]] bool allowForcedEstablishment = false) const override { return EntityId(); }
};
@@ -1,29 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzTest/AzTest.h>
#include <CGFContent.h>
class MockIAssetWriter
: public IAssetWriter
{
public:
~MockIAssetWriter() override = default;
MOCK_METHOD1(WriteCGF,
bool(CContentCGF* content));
MOCK_METHOD2(WriteCHR,
bool(CContentCGF* content, IConvertContext* convertContext));
MOCK_METHOD3(WriteSKIN,
bool(CContentCGF* content, IConvertContext* convertContext, bool exportMorphTargets));
};
@@ -1,11 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -1,14 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
../../WinBase.cpp
)
@@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)
@@ -1,11 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)
@@ -1,11 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -1,14 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
../../WinBase.cpp
)
@@ -1,11 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -1,14 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
../../WinBase.cpp
)
@@ -1,11 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)
@@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)
@@ -1,11 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
@@ -1,14 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
../../WinBase.cpp
)
-1
View File
@@ -168,7 +168,6 @@ namespace CryStringUtils_Internal
// Can be used for ASCII and Unicode (UTF-8/UTF-16/UTF-32), but not for ANSI.
// ? will match exactly one code-point.
// * will match zero or more code-points.
// Note: function moved here from CryCommonTools.
template <template<class> class CharComparator, class CharType>
inline bool MatchesWildcards_Tpl(const CharType* pStr, const CharType* pWild)
{
@@ -1,102 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
namespace Terrain
{
class Viewport2D
{
public:
int m_topLeftX = 0;
int m_topLeftY = 0;
int m_width = 0;
int m_height = 0;
Viewport2D() = default;
Viewport2D(const Viewport2D&) = default;
Viewport2D& operator=(const Viewport2D&) = default;
Viewport2D(int topLeftX, int topLeftY, int width, int height)
: m_topLeftX(topLeftX)
, m_topLeftY(topLeftY)
, m_width(width)
, m_height(height)
{
}
};
// External height map data requests
class HeightmapDataRequestInfo
{
public:
HeightmapDataRequestInfo() = default;
HeightmapDataRequestInfo(const HeightmapDataRequestInfo& rhs) = default;
HeightmapDataRequestInfo& operator=(const HeightmapDataRequestInfo& rhs) = default;
HeightmapDataRequestInfo(int viewportTopLeftX, int viewportTopLeftY, int viewportWidth, int viewportHeight, float metersPerPixel, AZ::Vector2 worldMin, AZ::Vector2 worldMax)
: m_viewport(viewportTopLeftX, viewportTopLeftY, viewportWidth, viewportHeight)
, m_metersPerPixel(metersPerPixel)
, m_worldMin(worldMin)
, m_worldMax(worldMax)
{
}
float GetMetersPerPixel() const
{
return m_metersPerPixel;
}
AZ::Vector2 GetWorldMin() const
{
return m_worldMin;
}
AZ::Vector2 GetWorldMax() const
{
return m_worldMax;
}
AZ::Vector2 GetWorldWidth() const
{
return (m_worldMax - m_worldMin);
}
Viewport2D GetViewport() const
{
return m_viewport;
}
private:
Viewport2D m_viewport;
AZ::Vector2 m_worldMin = AZ::Vector2(0.0f, 0.0f);
AZ::Vector2 m_worldMax = AZ::Vector2(0.0f, 0.0f);
float m_metersPerPixel = 1.0f;
};
class HeightmapDataNotifications
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
virtual void OnTerrainHeightDataChanged(const AZ::Aabb& dirtyRegion) = 0;
};
using HeightmapDataNotificationBus = AZ::EBus<HeightmapDataNotifications>;
}
@@ -1,56 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string_view.h>
class CShader;
namespace Terrain
{
class TerrainDataRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
virtual float GetHeightSynchronous(float x, float y) = 0;
virtual AZ::Vector3 GetNormalSynchronous(float x, float y) = 0;
virtual CShader* GetTerrainHeightGeneratorShader() const = 0;
virtual CShader* GetTerrainMaterialCompositingShader() const = 0;
};
using TerrainDataRequestBus = AZ::EBus<TerrainDataRequests>;
class TerrainShaderRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
virtual void RefreshShader(const AZStd::string_view name, CShader* shader) = 0;
virtual void ReleaseShader(CShader* shader) const = 0;
};
using TerrainShaderRequestBus = AZ::EBus<TerrainShaderRequests>;
}
@@ -1,82 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Vector2.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/EBus/EBus.h>
#include "HeightmapDataBus.h"
class CShader;
namespace Terrain
{
// This interface defines how the renderer can access the terrain system to set up state and gather information before rendering height maps
class TerrainProviderRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// world properties
virtual AZ::Vector3 GetWorldSize() = 0;
virtual AZ::Vector3 GetRegionSize() = 0;
virtual AZ::Vector3 GetWorldOrigin() = 0;
virtual AZ::Vector2 GetHeightRange() = 0;
// utility
virtual void GetRegionIndex(const AZ::Vector2& worldMin, const AZ::Vector2& worldMax, int& regionIndexX, int& regionIndexY) = 0;
virtual float GetHeightAtIndexedPosition([[maybe_unused]] int ix, [[maybe_unused]] int iy) { return 64.0f; }
virtual float GetHeightAtWorldPosition([[maybe_unused]] float fx, [[maybe_unused]] float fy) { return 64.0f; }
virtual unsigned char GetSurfaceTypeAtIndexedPosition([[maybe_unused]] int ix, [[maybe_unused]] int iy) { return 0; }
};
using TerrainProviderRequestBus = AZ::EBus<TerrainProviderRequests>;
// This class exists for the terrain system to inject data into the renderer for generating the GPU-side terrain height map
struct CRETerrainContext
{
// Tract map
virtual void OnTractVersionUpdate() = 0;
CShader* m_currentShader = nullptr;
};
class TerrainProviderNotifications
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// interface to be implemented by the game, invoked by the terrain render element
// pull settings from the world cache, so the next accessors are accurate
virtual void SynchronizeSettings(CRETerrainContext* context) = 0;
};
using TerrainProviderNotificationBus = AZ::EBus<TerrainProviderNotifications>;
}
@@ -1,37 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace Terrain
{
class TerrainRendererRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
// Query state of the terrain renderer
// Returns true once the terrain renderer has fulfilled most data requests and is ready for rendering
virtual bool IsReady() = 0;
};
using TerrainRendererRequestBus = AZ::EBus<TerrainRendererRequests>;
}
@@ -1,122 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
struct IMaterial;
class ITexture;
namespace Terrain
{
struct MacroMaterial
{
// Textures
_smart_ptr<ITexture> m_macroColorMap = nullptr;
_smart_ptr<ITexture> m_macroGlossMap = nullptr;
_smart_ptr<ITexture> m_macroNormalMap = nullptr;
// Material Params
AZ::Color m_macroColorMapColor;
float m_macroGlossMapScale = 1.0f;
float m_macroNormalMapScale = 1.0f;
float m_macroSpecReflectance = 0.03f;
void Clear()
{
m_macroColorMap = nullptr;
m_macroGlossMap = nullptr;
m_macroNormalMap = nullptr;
m_macroColorMapColor = AZ::Color(1.0f);
m_macroGlossMapScale = 1.0f;
m_macroNormalMapScale = 1.0f;
m_macroSpecReflectance = 0.03f;
}
};
struct TerrainMaterialLayer
{
_smart_ptr<IMaterial> m_material = nullptr;
_smart_ptr<ITexture> m_splatTexture = nullptr;
TerrainMaterialLayer(_smart_ptr<IMaterial> material, _smart_ptr<ITexture> splatTexture)
: m_material(material)
, m_splatTexture(splatTexture)
{
}
};
struct RegionMaterials
{
MacroMaterial m_macroMaterial;
AZStd::vector<TerrainMaterialLayer> m_materialLayers;
_smart_ptr<IMaterial> m_defaultMaterial = nullptr;
RegionMaterials& operator=(const RegionMaterials& rhs) = default;
void Clear()
{
m_materialLayers.clear();
m_defaultMaterial = nullptr;
m_macroMaterial.Clear();
}
};
const AZ::u32 kMaxRegionsPerTerrainMaterialRequest = 16;
typedef AZStd::pair<int, int> RegionIndex;
typedef AZStd::fixed_vector<RegionIndex, kMaxRegionsPerTerrainMaterialRequest> RegionIndexVector;
typedef AZStd::fixed_vector<RegionMaterials, kMaxRegionsPerTerrainMaterialRequest> RegionMaterialVector;
enum class RequestResult
{
NoAssetsForRegion,
Loading,
Success
};
class WorldMaterialRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
virtual void LoadWorld(const AZStd::string& worldName, int regionSize) = 0;
virtual RequestResult RequestRegionMaterials(const RegionIndexVector& regions, RegionMaterialVector& outRegionMaterials) = 0;
// Parameters:
// int tileX, tileY : Region tile indices
// Returns:
// bool : If true, region material data is loaded and exists. outMacroMaterial will be modified with the respective macro material data
// If false, no region material data has been loaded or exists for the given tile (may still have invalid material layers).
virtual RequestResult GetMacroMaterial(int tileX, int tileY, MacroMaterial& outMacroMaterial) = 0;
virtual void GetTerrainPOMParameters(float& pomHeightBias, float& pomDisplacement, float& selfShadowStrength) = 0;
//Get the surface type at a given position. If not loaded yet, returns "loadingMaterial".
virtual AZStd::string_view GetSurfaceTypeAtPosition(AZ::Vector2 position) = 0;
};
using WorldMaterialRequestBus = AZ::EBus<WorldMaterialRequests>;
} // namespace Terrain
+3
View File
@@ -12,6 +12,7 @@
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Linux/Mac port support for Win32API calls
#if !defined(WIN32)
#include "platform.h" // Note: This should be first to get consistent debugging definitions
@@ -1667,3 +1668,5 @@ __finddata64_t::~__finddata64_t()
}
}
#endif //defined(APPLE) || defined(LINUX)
#endif // !defined(WIN32)
@@ -249,7 +249,6 @@ set(FILES
platform_impl.cpp
Win32specific.h
Win64specific.h
stl/STLAlignedAlloc.h
LyShine/IDraw2d.h
LyShine/ILyShine.h
LyShine/ISprite.h
@@ -341,11 +340,7 @@ set(FILES
Maestro/Types/AssetBlendKey.h
Maestro/Types/AssetBlends.h
Maestro/Types/SequenceType.h
Terrain/Bus/WorldMaterialRequestsBus.h
Terrain/Bus/TerrainBus.h
Terrain/Bus/TerrainRendererBus.h
Terrain/Bus/HeightmapDataBus.h
Terrain/Bus/TerrainProviderBus.h
StaticInstance.h
Pak/CryPakUtils.h
WinBase.cpp
)
@@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)
@@ -14,12 +14,10 @@ set(FILES
Mocks/IConsoleMock.h
Mocks/ICryPakMock.h
Mocks/ILogMock.h
Mocks/IMemoryManagerMock.h
Mocks/ISystemMock.h
Mocks/ITimerMock.h
Mocks/ICVarMock.h
Mocks/IRendererMock.h
Mocks/ITextureMock.h
Mocks/IRemoteConsoleMock.h
Mocks/MockCGFContent.h
)
@@ -1,115 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implements an aligned allocator for STL
// based on the Mallocator (http://blogs.msdn.com/b/vcblog/archive/2008/08/28/the-mallocator.aspx)
#pragma once
#include <stddef.h> // Required for size_t and ptrdiff_t and NULL
#include <AzCore/Memory/AllocatorBase.h>
namespace stl
{
template <size_t Alignment>
class AlignedAllocator
: public AZ::SimpleSchemaAllocator<AZ::ChildAllocatorSchema<AZ::LegacyAllocator>>
{
public:
AZ_TYPE_INFO(AlignedAllocator, "{DF152D8A-36ED-4A2A-9FA6-734F212716C6}");
using Base = AZ::SimpleSchemaAllocator<AZ::ChildAllocatorSchema<AZ::LegacyAllocator>>;
using Descriptor = Base::Descriptor;
using Schema = AZ::ChildAllocatorSchema<AZ::LegacyAllocator>;
AlignedAllocator()
: Base("AlignedAllocator", "Legacy Cry Aligned Allocator")
{
}
pointer_type Allocate(size_type byteSize, size_type /*alignment*/, int flags /* = 0 */, const char* name /* = 0 */, const char* fileName /* = 0 */, int lineNum /* = 0 */, unsigned int suppressStackRecord /* = 0 */) override
{
return Base::Allocate(byteSize, Alignment, flags, name, fileName, lineNum, suppressStackRecord);
}
void DeAllocate(pointer_type ptr, size_type byteSize, [[maybe_unused]] size_type alignment) override
{
return Base::DeAllocate(ptr, byteSize, Alignment);
}
pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type /*newAlignment*/) override
{
return Base::ReAllocate(ptr, newSize, Alignment);
}
};
template <size_t Alignment>
using aligned_alloc = AZ::AZStdAlloc<AlignedAllocator<Alignment>>;
//////////////////////////////////////////////////////////////////////////
// Defines aligned vector type
//////////////////////////////////////////////////////////////////////////
template <typename T, int AlignSize>
class aligned_vector
: public AZStd::vector<T, aligned_alloc<AlignSize> >
{
public:
typedef aligned_alloc<AlignSize> MyAlloc;
typedef AZStd::vector<T, MyAlloc> MySuperClass;
typedef aligned_vector<T, AlignSize> MySelf;
typedef size_t size_type;
aligned_vector() {}
explicit aligned_vector(const MyAlloc& _Al)
: MySuperClass(_Al) {}
explicit aligned_vector(size_type _Count)
: MySuperClass(_Count) {};
aligned_vector(size_type _Count, const T& _Val)
: MySuperClass(_Count, _Val) {}
aligned_vector(size_type _Count, const T& _Val, const MyAlloc& _Al)
: MySuperClass(_Count, _Val) {}
aligned_vector(const MySelf& _Right)
: MySuperClass(_Right) {};
template<class _Iter>
aligned_vector(_Iter _First, _Iter _Last)
: MySuperClass(_First, _Last) {};
template<class _Iter>
aligned_vector(_Iter _First, _Iter _Last, const MyAlloc& _Al)
: MySuperClass(_First, _Last, _Al) {};
};
template <class Vec>
inline size_t size_of_aligned_vector(const Vec& c)
{
if (!c.empty())
{
// Not really correct as not taking alignment into the account
return c.capacity() * sizeof(typename Vec::value_type);
}
return 0;
}
} // namespace stl
// Specialize for the AlignedAllocator to provide one per module that does not use the
// environment for its storage. Since this allocator just uses LegacyAllocator
// to do the real work, it's fine if there is one of these per cry module
namespace AZ
{
template <size_t Alignment>
class AllocatorInstance<stl::AlignedAllocator<Alignment>> : public Internal::AllocatorInstanceBase<stl::AlignedAllocator<Alignment>, AllocatorStorage::ModuleStoragePolicy<stl::AlignedAllocator<Alignment>>>
{
};
}
+1 -1
View File
@@ -1216,7 +1216,7 @@ bool CSystem::InitShine([[maybe_unused]] const SSystemInitParams& initParams)
if (!m_env.pLyShine)
{
AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, "LYShine System did not initialize correctly. Please check that the LyShine gem is enabled for this project in ProjectConfigurator.");
AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, "LYShine System did not initialize correctly. Please check that the LyShine gem is enabled for this project in *_dependencies.cmake.");
return false;
}
return true;
@@ -12,7 +12,6 @@
#include "CrySystem_precompiled.h"
#include <AzTest/AzTest.h>
#include <AzCore/Memory/AllocatorScope.h>
#include <CryCommon/stl/STLAlignedAlloc.h>
TEST(StringTests, CUT_Strings)
{
@@ -424,21 +423,6 @@ TEST_F(CryPrimitives, CUT_FixedString)
EXPECT_EQ("0123", str5);
}
//////////////////////////////////////////////////////////////////////////
// Unit Testing of aligned_vector
//////////////////////////////////////////////////////////////////////////
TEST_F(CryPrimitives, CUT_AlignedVector)
{
stl::aligned_vector<int, 16> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
EXPECT_TRUE(vec.size() == 3);
EXPECT_TRUE(((INT_PTR)(&vec[0]) % 16) == 0);
}
TEST_F(CryPrimitives, CUT_DynArray)
{
LegacyDynArray<int> a;
-1
View File
@@ -1 +0,0 @@
*.xml
-1
View File
@@ -1 +0,0 @@
*.xml
@@ -269,13 +269,13 @@ namespace AZ
{
for (auto& [assetId, dependentAsset] : m_dependencies)
{
if (dependentAsset->IsReady())
if (dependentAsset->IsReady() || dependentAsset->IsError())
{
HandleReadyAsset(dependentAsset);
}
}
}
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady())
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady() || asset.IsError())
{
HandleReadyAsset(asset);
}
@@ -496,10 +496,10 @@ namespace AZ
m_waitingCount -= 1;
disconnectEbus = true;
if (m_waitingAssets.empty())
{
allReady = true;
}
}
if (m_waitingAssets.empty())
{
allReady = true;
}
}
@@ -510,8 +510,15 @@ namespace AZ
}
}
if (allReady && m_initComplete)
// If there are no assets left to be loaded, trigger the final AssetContainer notification (ready or canceled).
// We guard against prematurely sending it (m_initComplete) because it's possible for assets to get removed from our waiting
// list *while* we're still building up the list, so the list would appear to be empty too soon.
// We also guard against sending it multiple times (m_finalNotificationSent), because in some error conditions, it may be
// possible to try to remove the same asset multiple times, which if it's the last asset, it could trigger multiple
// notifications.
if (allReady && m_initComplete && !m_finalNotificationSent)
{
m_finalNotificationSent = true;
if (m_rootAsset)
{
AssetManagerBus::Broadcast(&AssetManagerBus::Events::OnAssetContainerReady, this);
@@ -137,6 +137,7 @@ namespace AZ
AZStd::atomic_int m_invalidDependencies{ 0 };
AZStd::unordered_set<AZ::Data::AssetId> m_unloadedDependencies;
AZStd::atomic_bool m_initComplete{ false };
AZStd::atomic_bool m_finalNotificationSent{false};
mutable AZStd::recursive_mutex m_preloadMutex;
// AssetId -> List of assets it is still waiting on
@@ -351,6 +351,7 @@ namespace UnitTest
public AZ::Data::AssetCatalog
{
static inline const AZ::Uuid TestAssetId{"{E970B177-5F45-44EB-A2C4-9F29D9A0B2A2}"};
static inline const AZ::Uuid MissingAssetId{"{11111111-1111-1111-1111-111111111111}"};
static inline constexpr AZStd::string_view TestAssetPath = "test";
void SetUp() override
@@ -431,24 +432,40 @@ namespace UnitTest
// AssetCatalogRequestBus implementation
// Minimalist mocks to provide our desired asset path or asset id
AZStd::string GetAssetPathById([[maybe_unused]] const AZ::Data::AssetId& id) override
AZStd::string GetAssetPathById(const AZ::Data::AssetId& id) override
{
return TestAssetPath;
if (id == TestAssetId)
{
return TestAssetPath;
}
return "";
}
AZ::Data::AssetId GetAssetIdByPath(
[[maybe_unused]] const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
[[maybe_unused]] bool autoRegisterIfNotFound) override
{
return TestAssetId;
if (path == TestAssetPath)
{
return TestAssetId;
}
return AZ::Data::AssetId();
}
// Return the mocked-out information for our test asset
AZ::Data::AssetInfo GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& id) override
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
{
AZ::Data::AssetInfo assetInfo;
assetInfo.m_assetId = TestAssetId;
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
assetInfo.m_relativePath = TestAssetPath;
if (id == TestAssetId)
{
assetInfo.m_assetId = TestAssetId;
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
assetInfo.m_relativePath = TestAssetPath;
}
return assetInfo;
}
@@ -456,15 +473,20 @@ namespace UnitTest
// Set the mocked-out asset load to have a 0-byte length so that the load skips I/O and immediately returns success
AZ::Data::AssetStreamInfo GetStreamInfoForLoad(
[[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
{
EXPECT_TRUE(type == AZ::AzTypeInfo<EmptyAsset>::Uuid());
AZ::Data::AssetStreamInfo info;
info.m_dataOffset = 0;
info.m_streamName = TestAssetPath;
info.m_dataLen = 0;
info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
if (id == TestAssetId)
{
info.m_streamName = TestAssetPath;
}
return info;
}
@@ -489,4 +511,27 @@ namespace UnitTest
EXPECT_TRUE(testAsset.IsReady());
}
// This test verifies that even if the asset loading returns immediately with an error, all of the loading code works
// successfully. The test itself loads a missing asset twice - the first time is a non-immediate error, where the error
// isn't reported until the DispatchEvents() call. The second time is an immediate error, because now the asset is already
// registered in an Error state. If the test fails, it will likely get caught in the shutdown of the test class, if any
// assets still exist at the point that the asset handler is unregistered. If they're present, then handling of the immediate
// error didn't work, as it left around extra references to the asset that haven't been cleaned up.
TEST_F(AssetManagerStreamerImmediateCompletionTests, ImmediateAssetError_WorksSuccessfully)
{
AZ::Data::AssetLoadParameters loadParams;
// Attempt to load a missing asset the first time. It will get an error, but not until the DispatchEvents() call happens.
auto testAsset1 = AssetManager::Instance().GetAsset<EmptyAsset>(MissingAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
AZ::Data::AssetManager::Instance().DispatchEvents();
EXPECT_TRUE(testAsset1.IsError());
// While the reference to the missing asset still exists, try to get it again. This will cause a more immediate error in
// the AssetContainer code, which should still get handled correctly. In the failure condition, it will instead leave the
// AssetContainer in a state where it never sends the final OnAssetContainerReady/Canceled message.
auto testAsset2 = AssetManager::Instance().GetAsset<EmptyAsset>(MissingAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
AZ::Data::AssetManager::Instance().DispatchEvents();
EXPECT_TRUE(testAsset2.IsError());
}
} // namespace UnitTest
@@ -37,29 +37,6 @@ namespace AzFramework
void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXSimpleMotionService"));
incompatible.push_back(AZ_CRC_CE("GradientTransformService"));
incompatible.push_back(AZ_CRC_CE("LegacyMeshService"));
incompatible.push_back(AZ_CRC_CE("LookAtService"));
incompatible.push_back(AZ_CRC_CE("SequenceService"));
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
incompatible.push_back(AZ_CRC_CE("GeometryService"));
incompatible.push_back(AZ_CRC_CE("CapsuleShapeService"));
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
incompatible.push_back(AZ_CRC_CE("SplineService"));
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
}
void NonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
@@ -99,6 +99,11 @@ namespace AzPhysics
classElement.RemoveElementByName(AZ_CRC_CE("Property Visibility Flags"));
}
if (classElement.GetVersion() <= 4)
{
classElement.RemoveElementByName(AZ_CRC_CE("Simulated"));
}
return true;
}
}
@@ -110,7 +115,7 @@ namespace AzPhysics
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<RigidBodyConfiguration, AzPhysics::SimulatedBodyConfiguration>()
->Version(4, &Internal::RigidBodyVersionConverter)
->Version(5, &Internal::RigidBodyVersionConverter)
->Field("Initial linear velocity", &RigidBodyConfiguration::m_initialLinearVelocity)
->Field("Initial angular velocity", &RigidBodyConfiguration::m_initialAngularVelocity)
->Field("Linear damping", &RigidBodyConfiguration::m_linearDamping)
@@ -119,7 +124,6 @@ namespace AzPhysics
->Field("Start Asleep", &RigidBodyConfiguration::m_startAsleep)
->Field("Interpolate Motion", &RigidBodyConfiguration::m_interpolateMotion)
->Field("Gravity Enabled", &RigidBodyConfiguration::m_gravityEnabled)
->Field("Simulated", &RigidBodyConfiguration::m_simulated)
->Field("Kinematic", &RigidBodyConfiguration::m_kinematic)
->Field("CCD Enabled", &RigidBodyConfiguration::m_ccdEnabled)
->Field("Compute Mass", &RigidBodyConfiguration::m_computeMass)
@@ -57,7 +57,6 @@ namespace AzPhysics
bool m_startAsleep = false;
bool m_interpolateMotion = false;
bool m_gravityEnabled = true;
bool m_simulated = true;
bool m_kinematic = false;
bool m_ccdEnabled = false; //!< Whether continuous collision detection is enabled.
float m_ccdMinAdvanceCoefficient = 0.15f; //!< Coefficient affecting how granularly time is subdivided in CCD.
@@ -88,13 +88,13 @@ namespace AzPhysics
//! Remove a simulated body from the Scene.z
//! @param sceneHandle A handle to the scene to remove the requested simulated body.
//! @param bodyHandle A handle to the simulated body being removed.
virtual void RemoveSimulatedBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0;
//! @param bodyHandle A handle to the simulated body being removed. This will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBody(SceneHandle sceneHandle, SimulatedBodyHandle& bodyHandle) = 0;
//! Remove a list of simulated bodies from the Scene.
//! @param sceneHandle A handle to the scene to remove the simulated bodies from.
//! @param bodyHandles A list of simulated body handles to be removed.
virtual void RemoveSimulatedBodies(SceneHandle sceneHandle, const SimulatedBodyHandleList& bodyHandles) = 0;
//! @param bodyHandles A list of simulated body handles to be removed. All handles will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBodies(SceneHandle sceneHandle, SimulatedBodyHandleList& bodyHandles) = 0;
//! Enable / Disable simulation of the requested body. By default all bodies added are enabled.
//! Disabling simulation the body will no longer be affected by any forces, collisions, or found with scene queries.
@@ -286,12 +286,12 @@ namespace AzPhysics
virtual SimulatedBodyList GetSimulatedBodiesFromHandle(const SimulatedBodyHandleList& bodyHandles) = 0;
//! Remove a simulated body from the Scene.
//! @param bodyHandle A handle to the simulated body being removed.
virtual void RemoveSimulatedBody(SimulatedBodyHandle bodyHandle) = 0;
//! @param bodyHandle A handle to the simulated body being removed. This will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBody(SimulatedBodyHandle& bodyHandle) = 0;
//! Remove a list of simulated bodies from the Scene.
//! @param bodyHandles A list of simulated body handles to be removed.
virtual void RemoveSimulatedBodies(const SimulatedBodyHandleList& bodyHandles) = 0;
//! @param bodyHandles A list of simulated body handles to be removed. All handles will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
virtual void RemoveSimulatedBodies(SimulatedBodyHandleList& bodyHandles) = 0;
//! Enable / Disable simulation of the requested body. By default all bodies added are enabled.
//! Disabling simulation the body will no longer be affected by any forces, collisions, or found with scene queries.
@@ -62,7 +62,7 @@ namespace AzPhysics
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
virtual AZ::Vector3 GetAngularVelocity() const = 0;
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) const = 0;
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
@@ -15,6 +15,7 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Plane.h>
#include <AzCore/std/numeric.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Windowing/WindowBus.h>
@@ -156,35 +157,25 @@ namespace AzFramework
camera.m_lookAt = transform.GetTranslation() + (camera.Rotation().GetBasisY() * -camera.m_lookDist);
}
static ScreenVector CursorDelta(const AZStd::optional<ScreenPoint>& currentPosition, const AZStd::optional<ScreenPoint>& lastPosition)
{
return currentPosition.has_value() && lastPosition.has_value() ? currentPosition.value() - lastPosition.value()
: ScreenVector(0, 0);
}
bool CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor = AZStd::get_if<CursorEvent>(&event))
{
m_currentCursorPosition = cursor->m_position;
m_cursorState.SetCurrentPosition(cursor->m_position);
}
else if (const auto& scroll = AZStd::get_if<ScrollEvent>(&event))
{
m_scrollDelta = scroll->m_delta;
}
return m_cameras.HandleEvents(event, CursorDelta(m_currentCursorPosition, m_lastCursorPosition), m_scrollDelta);
return m_cameras.HandleEvents(event, m_cursorState.CursorDelta(), m_scrollDelta);
}
Camera CameraSystem::StepCamera(const Camera& targetCamera, const float deltaTime)
{
const auto cursorDelta = CursorDelta(m_currentCursorPosition, m_lastCursorPosition);
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
const auto nextCamera = m_cameras.StepCamera(targetCamera, m_cursorState.CursorDelta(), m_scrollDelta, deltaTime);
const auto nextCamera = m_cameras.StepCamera(targetCamera, cursorDelta, m_scrollDelta, deltaTime);
m_cursorState.Update();
m_scrollDelta = 0.0f;
@@ -236,12 +227,12 @@ namespace AzFramework
}
}
// accumulate
Camera nextCamera = targetCamera;
for (auto& cameraInput : m_activeCameraInputs)
{
nextCamera = cameraInput->StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
const Camera nextCamera = AZStd::accumulate(
AZStd::begin(m_activeCameraInputs), AZStd::end(m_activeCameraInputs), targetCamera,
[cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera) {
acc = camera->StepCamera(acc, cursorDelta, scrollDelta, deltaTime);
return acc;
});
for (int i = 0; i < m_activeCameraInputs.size();)
{
@@ -275,34 +266,42 @@ namespace AzFramework
}
}
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_rotateChannelId)
const ClickDetector::ClickEvent clickEvent = [&event, this] {
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_state == InputChannel::State::Began)
if (input->m_channelId == m_rotateChannelId)
{
m_tryingToBegin = true;
m_moveAccumulator = 0.0f;
}
else if (input->m_state == InputChannel::State::Ended)
{
m_tryingToBegin = false;
EndActivation();
if (input->m_state == InputChannel::State::Began)
{
return ClickDetector::ClickEvent::Down;
}
else if (input->m_state == InputChannel::State::Ended)
{
return ClickDetector::ClickEvent::Up;
}
}
}
}
return ClickDetector::ClickEvent::Nil;
}();
if (m_tryingToBegin)
switch (const auto outcome = m_clickDetector.DetectClick(clickEvent, cursorDelta); outcome)
{
// only allow the action to begin if the mouse has been moved a small amount
m_moveAccumulator += ScreenVectorLength(cursorDelta);
if (m_moveAccumulator > ed_cameraSystemLookDeadzone)
{
BeginActivation();
m_tryingToBegin = false;
}
case ClickDetector::ClickOutcome::Move:
BeginActivation();
break;
case ClickDetector::ClickOutcome::Release:
EndActivation();
break;
default:
// noop
break;
}
}
@@ -324,6 +323,12 @@ namespace AzFramework
return nextCamera;
}
PanCameraInput::PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
{
}
void PanCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
@@ -400,6 +405,11 @@ namespace AzFramework
return TranslationType::Nil;
}
TranslateCameraInput::TranslateCameraInput(TranslationAxesFn translationAxesFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
{
}
void TranslateCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
@@ -574,6 +584,11 @@ namespace AzFramework
return nextCamera;
}
OrbitDollyCursorMoveCameraInput::OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
: m_dollyChannelId(dollyChannelId)
{
}
void OrbitDollyCursorMoveCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
@@ -17,6 +17,8 @@
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/CursorState.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Viewport/ViewportId.h>
@@ -188,26 +190,21 @@ namespace AzFramework
Cameras m_cameras;
private:
CursorState m_cursorState;
float m_scrollDelta = 0.0f;
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
};
class RotateCameraInput : public CameraInput
{
public:
explicit RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
explicit RotateCameraInput(InputChannelId rotateChannelId);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
private:
InputChannelId m_rotateChannelId;
float m_moveAccumulator = 0.0f;
bool m_tryingToBegin = false;
ClickDetector m_clickDetector;
};
struct PanAxes
@@ -240,11 +237,8 @@ namespace AzFramework
class PanCameraInput : public CameraInput
{
public:
PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
{
}
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -283,10 +277,8 @@ namespace AzFramework
class TranslateCameraInput : public CameraInput
{
public:
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
{
}
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
@@ -363,8 +355,7 @@ namespace AzFramework
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
: m_dollyChannelId(dollyChannelId) {}
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -0,0 +1,68 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AzFramework
{
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
{
if (clickEvent == ClickEvent::Down)
{
const auto now = std::chrono::steady_clock::now();
if (m_tryBeginTime)
{
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
if (diff.count() < m_doubleClickInterval)
{
return ClickOutcome::Nil;
}
}
m_detectionState = DetectionState::WaitingForMove;
m_moveAccumulator = 0.0f;
m_tryBeginTime = now;
}
else if (clickEvent == ClickEvent::Up)
{
const auto clickOutcome = [detectionState = m_detectionState] {
if (detectionState == DetectionState::WaitingForMove)
{
return ClickOutcome::Click;
}
if (detectionState == DetectionState::Moved)
{
return ClickOutcome::Release;
}
return ClickOutcome::Nil;
}();
m_detectionState = DetectionState::Nil;
return clickOutcome;
}
if (m_detectionState == DetectionState::WaitingForMove)
{
// only allow the action to begin if the mouse has been moved a small amount
m_moveAccumulator += ScreenVectorLength(cursorDelta);
if (m_moveAccumulator > m_deadZone)
{
m_detectionState = DetectionState::Moved;
return ClickOutcome::Move;
}
}
return ClickOutcome::Nil;
}
} // namespace AzFramework
@@ -0,0 +1,75 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/optional.h>
#include <chrono>
namespace AzFramework
{
struct ScreenVector;
//! Utility class to help detect different types of mouse click (mouse down and up with
//! no movement), mouse move (down and initial move after some threshold) and mouse release
//! (mouse down with movement and then mouse up).
class ClickDetector
{
//! Alias for recording time of mouse down events
using Time = std::chrono::time_point<std::chrono::steady_clock>;
public:
//! Internal representation of click event (map from external event for this when
//! calling DetectClick).
enum class ClickEvent
{
Nil,
Down,
Up
};
//! The type of mouse click.
enum class ClickOutcome
{
Nil, //!< Not recognized.
Move, //!< Initial move after mouse down.
Click, //!< Mouse down and up with no intermediate movement.
Release //!< Mouse down with movement and then mouse up.
};
//! Called from any type of 'handle event' function.
ClickOutcome DetectClick(ClickEvent clickEvent, const ScreenVector& cursorDelta);
void SetDoubleClickInterval(float doubleClickInterval);
private:
//! Internal state of ClickDetector based on incoming events.
enum class DetectionState
{
Nil, //!< Initial state
WaitingForMove, //! Mouse down has happened but mouse hasn't yet moved.
Moved //! Mouse has moved, no longer will be counted as a click.
};
float m_moveAccumulator = 0.0f; //!< How far the mouse has moved after mouse down.
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
DetectionState m_detectionState; //!< Internal state of ClickDetector.
AZStd::optional<Time> m_tryBeginTime; //!< Mouse down time (happens each mouse down, helps with double click handling).
};
inline void ClickDetector::SetDoubleClickInterval(const float doubleClickInterval)
{
m_doubleClickInterval = doubleClickInterval;
}
} // namespace AzFramework
@@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/std/optional.h>
namespace AzFramework
{
//! Utility type to wrap a current and last cursor position.
struct CursorState
{
//! Returns the delta between the current and last cursor position.
[[nodiscard]] ScreenVector CursorDelta() const;
//! Call this in a 'handle event' call to update the most recent cursor position.
void SetCurrentPosition(const ScreenPoint& currentPosition);
//! Call this in an 'update' call to copy the current cursor position to the last
//! cursor position.
void Update();
private:
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
};
inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition)
{
m_currentCursorPosition = currentPosition;
}
inline ScreenVector CursorState::CursorDelta() const
{
return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
: ScreenVector(0, 0);
}
inline void CursorState::Update()
{
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
}
} // namespace AzFramework
@@ -103,6 +103,9 @@ set(FILES
Viewport/CameraState.cpp
Viewport/CameraInput.h
Viewport/CameraInput.cpp
Viewport/ClickDetector.h
Viewport/ClickDetector.cpp
Viewport/CursorState.h
Viewport/DisplayContextRequestBus.h
Entity/BehaviorEntity.cpp
Entity/BehaviorEntity.h
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="20px" viewBox="0 0 22 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Toolbar / Play Console / Simulate Physics</title>
<defs>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
</filter>
<path d="M15.6428742,11.9827626 C14.6770188,10.7687802 14.4956657,9.03975584 15.318317,7.61488202 C16.3923878,5.75453685 18.7712019,5.11713552 20.6315471,6.1912063 C22.4918923,7.26527709 23.1292936,9.64409122 22.0552228,11.5044364 C21.013572,13.3086286 18.7447543,13.9625898 16.9118187,13.0207075 C16.3649862,12.6909949 16.1306352,12.5503038 15.6428742,11.9827626 Z M21.8449657,10.7460039 C22.0595758,10.1342007 22.2373043,8.55906064 21.8449657,9.05833735 C21.4526271,9.55761407 21.2918858,9.96701497 20.8130083,10.4818331 C19.9042528,11.4587922 18.2692551,11.5405181 17.2685207,11.5405181 C16.2677863,11.5405181 17.2685207,12.7159692 18.7447536,12.7159692 C20.2209864,12.7159692 21.4894947,11.7593684 21.8449657,10.7460039 Z M16.197388,14.0090117 C16.3975463,13.8229455 16.701528,13.7946404 16.9039947,13.9824218 C17.1064613,14.1702033 17.0967404,14.4628636 16.9305845,14.6890285 C16.3625338,15.4622373 15.7176919,17.2794303 15.043117,20.0013354 L13.9731652,20.0013354 C13.74245,13.5862059 13.0475403,9.88991503 12.1237705,9.88991503 C11.4442663,9.88991503 10.8362307,11.6219 10.2533824,15.1128617 C10.1857467,15.5179652 9.53567637,19.2951617 9.47653253,20.0013354 L8.32801304,20.0013354 C8.32801304,11.4928776 6.37313749,5.95119082 2.36188186,4.12720757 C2.11050727,4.01290346 1.65264126,3.52516756 2.0367453,2.87964604 C2.42084934,2.23412452 3.17248781,2.63112648 3.42386239,2.7454306 C7.09988702,4.41697884 8.58435154,8.916133 9.2170288,15.2523916 C9.23745765,15.1271094 9.25478369,15.0215631 9.26703535,14.9481819 C9.97359325,10.7162629 10.1786779,8.42129315 12.1237705,8.42129315 C14.0688632,8.42129315 14.4491297,12.5275507 14.8251327,17.6881742 C15.0978485,16.4034552 15.5446067,14.6158341 16.197388,14.0090117 Z M2.0241711,20.6412764 L22.0595758,20.6412764 L20.6246231,21.7592943 L3.70859025,21.7592943 L2.0241711,20.6412764 Z" id="path-2"></path>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Play-Console-v2" transform="translate(-169.000000, -8.000000)">
<g id="Play-Console" transform="translate(100.000000, 0.000000)">
<g id="Icon-/-Toolbar-/-Play-Console-/-Simulate-Physics" transform="translate(68.000000, 6.000000)" filter="url(#filter-1)">
<g>
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-2"></use>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -372,6 +372,7 @@
<file>img/UI20/toolbar/Select.svg</file>
<file>img/UI20/toolbar/select_object.svg</file>
<file>img/UI20/toolbar/Select_terrain.svg</file>
<file>img/UI20/toolbar/Simulate_Physics.svg</file>
<file>img/UI20/toolbar/Simulate_Physics_on_selected_objects.svg</file>
<file>img/UI20/toolbar/Terrain.svg</file>
<file>img/UI20/toolbar/Terrain_Texture.svg</file>
@@ -63,29 +63,6 @@ namespace AzToolsFramework
void EditorNonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXSimpleMotionService"));
incompatible.push_back(AZ_CRC_CE("GradientTransformService"));
incompatible.push_back(AZ_CRC_CE("LegacyMeshService"));
incompatible.push_back(AZ_CRC_CE("LookAtService"));
incompatible.push_back(AZ_CRC_CE("SequenceService"));
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
incompatible.push_back(AZ_CRC_CE("GeometryService"));
incompatible.push_back(AZ_CRC_CE("CapsuleShapeService"));
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
incompatible.push_back(AZ_CRC_CE("SplineService"));
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
}
void EditorNonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
@@ -237,16 +237,15 @@ namespace AzToolsFramework
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
}
static bool IndividualSelect(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
static bool IndividualSelect(const AzFramework::ClickDetector::ClickOutcome clickOutcome)
{
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down;
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click;
}
static bool AdditiveIndividualSelect(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
static bool AdditiveIndividualSelect(
const AzFramework::ClickDetector::ClickOutcome clickOutcome, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click &&
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
!mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
}
@@ -1783,6 +1782,25 @@ namespace AzToolsFramework
m_cachedEntityIdUnderCursor = m_editorHelpers->HandleMouseInteraction(cameraState, mouseInteraction);
const AzFramework::ClickDetector::ClickEvent selectClickEvent = [&mouseInteraction] {
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
{
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
{
return AzFramework::ClickDetector::ClickEvent::Down;
}
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
{
return AzFramework::ClickDetector::ClickEvent::Up;
}
}
return AzFramework::ClickDetector::ClickEvent::Nil;
}();
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
// for entities selected with no bounds of their own (just TransformComponent)
// check selection against the selection indicator aabb
for (AZ::EntityId entityId : m_selectedEntityIds)
@@ -1841,7 +1859,7 @@ namespace AzToolsFramework
if (!m_selectedEntityIds.empty())
{
// select/deselect (add/remove) entities with ctrl held
if (Input::AdditiveIndividualSelect(mouseInteraction))
if (Input::AdditiveIndividualSelect(clickOutcome, mouseInteraction))
{
if (SelectDeselect(entityIdUnderCursor))
{
@@ -2023,7 +2041,7 @@ namespace AzToolsFramework
}
// standard toggle selection
if (Input::IndividualSelect(mouseInteraction))
if (Input::IndividualSelect(clickOutcome))
{
SelectDeselect(entityIdUnderCursor);
}
@@ -2526,7 +2544,7 @@ namespace AzToolsFramework
// create the cluster for changing transform mode
ViewportUi::ViewportUiRequestBus::EventResult(
m_transformModeClusterId, ViewportUi::DefaultViewportId,
&ViewportUi::ViewportUiRequestBus::Events::CreateCluster);
&ViewportUi::ViewportUiRequestBus::Events::CreateCluster, ViewportUi::Alignment::TopLeft);
// create and register the buttons (strings correspond to icons even if the values appear different)
m_translateButtonId = RegisterClusterButton(m_transformModeClusterId, "Move");
@@ -3267,6 +3285,8 @@ namespace AzToolsFramework
const auto modifiers = ViewportInteraction::KeyboardModifiers(
ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers()));
m_cursorState.Update();
HandleAccents(
!m_selectedEntityIds.empty(), m_cachedEntityIdUnderCursor,
modifiers.Ctrl(), m_hoveredEntityId,
@@ -17,6 +17,8 @@
#include <AzCore/std/optional.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Components/CameraBus.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/CursorState.h>
#include <AzToolsFramework/API/EditorCameraBus.h>
#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
@@ -35,37 +37,37 @@ namespace AzToolsFramework
{
class EditorVisibleEntityDataCache;
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; ///< Alias for unordered_set of EntityIds.
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; //!< Alias for unordered_set of EntityIds.
/// Entity related data required by manipulators during action.
//! Entity related data required by manipulators during action.
struct EntityIdManipulatorLookup
{
AZ::Transform m_initial; /// Transform of Entity at mouse down on manipulator.
AZ::Transform m_initial; //!< Transform of Entity at mouse down on manipulator.
};
/// Alias for a mapping between EntityIds and Entity related data required by manipulators.
//! Alias for a mapping between EntityIds and Entity related data required by manipulators.
using EntityIdManipulatorLookups = AZStd::unordered_map<AZ::EntityId, EntityIdManipulatorLookup>;
/// Generic wrapper to handle specific manipulators controlling 1-* entities.
//! Generic wrapper to handle specific manipulators controlling 1-* entities.
struct EntityIdManipulators
{
EntityIdManipulatorLookups m_lookups; ///< Mapping between the EntityId and the transform of the Entity at
///< the point a manipulator started adjusting it.
AZStd::unique_ptr<Manipulators> m_manipulators; ///< The aggregate manipulator currently in use.
EntityIdManipulatorLookups m_lookups; //!< Mapping between the EntityId and the transform of the Entity at
//!< the point a manipulator started adjusting it.
AZStd::unique_ptr<Manipulators> m_manipulators; //!< The aggregate manipulator currently in use.
};
/// Store translation and orientation only (no scale).
//! Store translation and orientation only (no scale).
struct Frame
{
AZ::Vector3 m_translation = AZ::Vector3::CreateZero(); ///< Position of frame.
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); ///< Orientation of frame.
AZ::Vector3 m_translation = AZ::Vector3::CreateZero(); //!< Position of frame.
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); //!< Orientation of frame.
};
/// Temporary manipulator frame used during selection.
//! Temporary manipulator frame used during selection.
struct OptionalFrame
{
/// What part of the transform did we pick (when using ditto on
/// the manipulator). This will depend on the transform mode we're in.
//! What part of the transform did we pick (when using ditto on
//! the manipulator). This will depend on the transform mode we're in.
struct PickType
{
enum : AZ::u8
@@ -83,29 +85,29 @@ namespace AzToolsFramework
bool PickedTranslation() const;
bool PickedOrientation() const;
/// Clear all state associated with the frame.
//! Clear all state associated with the frame.
void Reset();
/// Clear only picked translation state.
//! Clear only picked translation state.
void ResetPickedTranslation();
/// Clear only picked orientation state.
//! Clear only picked orientation state.
void ResetPickedOrientation();
AZ::EntityId m_pickedEntityIdOverride; ///< 'Picked' Entity - frame and parent space relative to this if active.
AZStd::optional<AZ::Vector3> m_translationOverride; ///< Translation override, if set, reset when selection is empty.
AZStd::optional<AZ::Quaternion> m_orientationOverride; ///< Orientation override, if set, reset when selection is empty.
AZ::u8 m_pickTypes = PickType::None; ///< What mode(s) were we in when picking an EntityId override.
AZ::EntityId m_pickedEntityIdOverride; //!< 'Picked' Entity - frame and parent space relative to this if active.
AZStd::optional<AZ::Vector3> m_translationOverride; //!< Translation override, if set, reset when selection is empty.
AZStd::optional<AZ::Quaternion> m_orientationOverride; //!< Orientation override, if set, reset when selection is empty.
AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override.
};
/// What frame/space is the manipulator currently operating in.
//! What frame/space is the manipulator currently operating in.
enum class ReferenceFrame
{
Local, /// The local space of the individual entity.
Parent, /// The parent space of the individual entity (world space if no parent exists).
World, /// World space (space aligned to world axes - identity).
Local, //!< The local space of the individual entity.
Parent, //!< The parent space of the individual entity (world space if no parent exists).
World, //!< World space (space aligned to world axes - identity).
};
/// Entity selection/interaction handling.
/// Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
//! Entity selection/interaction handling.
//! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
class EditorTransformComponentSelection
: public ViewportInteraction::ViewportSelectionRequests
, private EditorEventsBus::Handler
@@ -127,15 +129,15 @@ namespace AzToolsFramework
EditorTransformComponentSelection& operator=(const EditorTransformComponentSelection&) = delete;
virtual ~EditorTransformComponentSelection();
/// Register entity manipulators with the ManipulatorManager.
/// After being registered, the entity manipulators will draw and check for input.
//! Register entity manipulators with the ManipulatorManager.
//! After being registered, the entity manipulators will draw and check for input.
void RegisterManipulator();
/// Unregister entity manipulators with the ManipulatorManager.
/// No longer draw or respond to input.
//! Unregister entity manipulators with the ManipulatorManager.
//! No longer draw or respond to input.
void UnregisterManipulator();
/// ViewportInteraction::ViewportSelectionRequests
/// Intercept all viewport mouse events and respond to inputs.
//! ViewportInteraction::ViewportSelectionRequests
//! Intercept all viewport mouse events and respond to inputs.
bool HandleMouseInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
void DisplayViewportSelection(
@@ -145,9 +147,9 @@ namespace AzToolsFramework
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
/// Add an entity to the current selection
//! Add an entity to the current selection
void AddEntityToSelection(AZ::EntityId entityId);
/// Remove an entity from the current selection
//! Remove an entity from the current selection
void RemoveEntityFromSelection(AZ::EntityId entityId);
private:
@@ -161,8 +163,8 @@ namespace AzToolsFramework
void ClearManipulatorTranslationOverride();
void ClearManipulatorOrientationOverride();
/// Handle an event triggered by the user to clear any manipulator overrides.
/// Delegate to either translation or orientation reset/clear depending on the state we're in.
//! Handle an event triggered by the user to clear any manipulator overrides.
//! Delegate to either translation or orientation reset/clear depending on the state we're in.
void DelegateClearManipulatorOverride();
void ToggleCenterPivotSelection();
@@ -251,63 +253,65 @@ namespace AzToolsFramework
void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale);
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation);
AZ::EntityId m_hoveredEntityId; ///< What EntityId is the mouse currently hovering over (if any).
AZ::EntityId m_cachedEntityIdUnderCursor; ///< Store the EntityId on each mouse move for use in Display.
AZ::EntityId m_editorCameraComponentEntityId; ///< The EditorCameraComponent EntityId if it is set.
EntityIdSet m_selectedEntityIds; ///< Represents the current entities in the selection.
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
EntityIdSet m_selectedEntityIds; //!< Represents the current entities in the selection.
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; ///< A cache of packed EntityData that can be
///< iterated over efficiently without the need
///< to make individual EBus calls.
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; ///< Editor visualization of entities (icons, shapes, debug visuals etc).
EntityIdManipulators m_entityIdManipulators; ///< Mapping from a Manipulator to potentially many EntityIds.
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< A cache of packed EntityData that can be
//!< iterated over efficiently without the need
//!< to make individual EBus calls.
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc).
EntityIdManipulators m_entityIdManipulators; //!< Mapping from a Manipulator to potentially many EntityIds.
EditorBoxSelect m_boxSelect; ///< Type responsible for handling box select.
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; ///< Track adjustments to manipulator translation and orientation (during mouse press/move).
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; ///< What actions are tied to this handler.
ViewportInteraction::KeyboardModifiers m_previousModifiers; ///< What modifiers were held last frame.
EditorContextMenu m_contextMenu; ///< Viewport right click context menu.
OptionalFrame m_pivotOverrideFrame; ///< Has a pivot override been set.
Mode m_mode = Mode::Translation; ///< Manipulator mode - default to translation.
Pivot m_pivotMode = Pivot::Object; ///< Entity pivot mode - default to object (authored root).
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; ///< What reference frame is the Manipulator currently operating in.
Frame m_axisPreview; ///< Axes of entity at the time of mouse down to indicate delta of translation.
bool m_triedToRefresh = false; ///< Did a refresh event occur to recalculate the current Manipulator transform.
bool m_didSetSelectedEntities = false; ///< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
bool m_selectedEntityIdsAndManipulatorsDirty = false; ///< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
bool m_transformChangedInternally = false; ///< Was an OnTransformChanged event triggered internally or not.
ViewportUi::ClusterId m_transformModeClusterId; ///< Id of the Viewport UI cluster for changing transform mode.
ViewportUi::ButtonId m_translateButtonId; ///< Id of the Viewport UI button for translate mode.
ViewportUi::ButtonId m_rotateButtonId; ///< Id of the Viewport UI button for rotate mode.
ViewportUi::ButtonId m_scaleButtonId; ///< Id of the Viewport UI button for scale mode.
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; ///< Event handler for the Viewport UI cluster.
EditorBoxSelect m_boxSelect; //!< Type responsible for handling box select.
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; //!< Track adjustments to manipulator translation and orientation (during mouse press/move).
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; //!< What actions are tied to this handler.
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< What modifiers were held last frame.
EditorContextMenu m_contextMenu; //!< Viewport right click context menu.
OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set.
Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation.
Pivot m_pivotMode = Pivot::Object; //!< Entity pivot mode - default to object (authored root).
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation.
bool m_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
bool m_didSetSelectedEntities = false; //!< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
bool m_selectedEntityIdsAndManipulatorsDirty = false; //!< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
bool m_transformChangedInternally = false; //!< Was an OnTransformChanged event triggered internally or not.
ViewportUi::ClusterId m_transformModeClusterId; //!< Id of the Viewport UI cluster for changing transform mode.
ViewportUi::ButtonId m_translateButtonId; //!< Id of the Viewport UI button for translate mode.
ViewportUi::ButtonId m_rotateButtonId; //!< Id of the Viewport UI button for rotate mode.
ViewportUi::ButtonId m_scaleButtonId; //!< Id of the Viewport UI button for scale mode.
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; //!< Event handler for the Viewport UI cluster.
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
};
/// The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
/// the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
/// and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
//! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
//! the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
//! and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
namespace ETCS
{
/// The result from calculating the entity (transform component) orientation.
/// Does the entity have a parent or not, and what orientation should the manipulator have when
/// displayed at the object pivot (determined by the entity hierarchy and what modifiers are held).
//! The result from calculating the entity (transform component) orientation.
//! Does the entity have a parent or not, and what orientation should the manipulator have when
//! displayed at the object pivot (determined by the entity hierarchy and what modifiers are held).
struct PivotOrientationResult
{
AZ::Quaternion m_worldOrientation;
AZ::EntityId m_parentId;
};
/// Calculate the orientation for an individual entity based on the incoming reference frame.
/// Note: If the entity is in a hierarchy the Parent reference frame will return the orientation of the parent.
//! Calculate the orientation for an individual entity based on the incoming reference frame.
//! Note: If the entity is in a hierarchy the Parent reference frame will return the orientation of the parent.
PivotOrientationResult CalculatePivotOrientation(AZ::EntityId entityId, ReferenceFrame referenceFrame);
/// Calculate the orientation for a group of entities based on the incoming reference frame.
//! Calculate the orientation for a group of entities based on the incoming reference frame.
template<typename EntityIdMap>
PivotOrientationResult CalculatePivotOrientationForEntityIds(
const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
/// Calculate the orientation for a group of entities based on the incoming
/// reference frame with possible pivot override.
//! Calculate the orientation for a group of entities based on the incoming
//! reference frame with possible pivot override.
template<typename EntityIdMap>
PivotOrientationResult CalculateSelectionPivotOrientation(
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame,
@@ -41,6 +41,28 @@ namespace AzToolsFramework::ViewportUi::Internal
}
}
static Qt::Alignment GetQtAlignment(Alignment align)
{
switch (align)
{
case Alignment::TopRight:
return Qt::AlignTop | Qt::AlignRight;
case Alignment::TopLeft:
return Qt::AlignTop | Qt::AlignLeft;
case Alignment::BottomRight:
return Qt::AlignBottom | Qt::AlignRight;
case Alignment::BottomLeft:
return Qt::AlignBottom | Qt::AlignLeft;
case Alignment::Top:
return Qt::AlignTop;
case Alignment::Bottom:
return Qt::AlignBottom;
}
AZ_Assert(false, "ViewportUI", "Unhandled ViewportUI Alignment %d", static_cast<int>(align));
return Qt::AlignTop;
}
ViewportUiDisplay::ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay)
: m_renderOverlay(renderOverlay)
, m_uiMainWindow(parent)
@@ -56,7 +78,7 @@ namespace AzToolsFramework::ViewportUi::Internal
UnparentWidgets(m_viewportUiElements);
}
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup)
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup, const Alignment align)
{
if (!buttonGroup.get())
{
@@ -66,7 +88,7 @@ namespace AzToolsFramework::ViewportUi::Internal
auto viewportUiCluster = AZStd::make_shared<ViewportUiCluster>(buttonGroup);
auto id = AddViewportUiElement(viewportUiCluster);
buttonGroup->SetViewportUiElementId(id);
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
PositionViewportUiElementAnchored(id, GetQtAlignment(align));
}
void ViewportUiDisplay::AddClusterButton(
@@ -94,7 +116,7 @@ namespace AzToolsFramework::ViewportUi::Internal
}
}
void ViewportUiDisplay::AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup)
void ViewportUiDisplay::AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup, const Alignment align)
{
if (!buttonGroup.get())
{
@@ -104,7 +126,7 @@ namespace AzToolsFramework::ViewportUi::Internal
auto viewportUiSwitcher = AZStd::make_shared<ViewportUiSwitcher>(buttonGroup);
auto id = AddViewportUiElement(viewportUiSwitcher);
buttonGroup->SetViewportUiElementId(id);
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
PositionViewportUiElementAnchored(id, GetQtAlignment(align));
}
void ViewportUiDisplay::AddSwitcherButton(const ViewportUiElementId clusterId, Button* button)
@@ -56,12 +56,12 @@ namespace AzToolsFramework::ViewportUi::Internal
ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay);
~ViewportUiDisplay();
void AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup);
void AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup, Alignment align);
void AddClusterButton(ViewportUiElementId clusterId, Button* button);
void RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId);
void UpdateCluster(const ViewportUiElementId clusterId);
void AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup);
void AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup, Alignment align);
void AddSwitcherButton(ViewportUiElementId switcherId, Button* button);
void RemoveSwitcherButton(ViewportUiElementId switcherId, ButtonId buttonId);
void UpdateSwitcher(ViewportUiElementId switcherId);
@@ -30,18 +30,18 @@ namespace AzToolsFramework::ViewportUi
ViewportUiRequestBus::Handler::BusDisconnect();
}
const ClusterId ViewportUiManager::CreateCluster()
const ClusterId ViewportUiManager::CreateCluster(const Alignment align)
{
auto buttonGroup = AZStd::make_shared<Internal::ButtonGroup>();
m_viewportUi->AddCluster(buttonGroup);
m_viewportUi->AddCluster(buttonGroup, align);
return RegisterNewCluster(buttonGroup);
}
const SwitcherId ViewportUiManager::CreateSwitcher()
const SwitcherId ViewportUiManager::CreateSwitcher(const Alignment align)
{
auto buttonGroup = AZStd::make_shared<Internal::ButtonGroup>();
m_viewportUi->AddSwitcher(buttonGroup);
m_viewportUi->AddSwitcher(buttonGroup, align);
return RegisterNewSwitcher(buttonGroup);
}
@@ -31,8 +31,8 @@ namespace AzToolsFramework::ViewportUi
~ViewportUiManager() = default;
// ViewportUiRequestBus ...
const ClusterId CreateCluster() override;
const SwitcherId CreateSwitcher() override;
const ClusterId CreateCluster(Alignment align) override;
const SwitcherId CreateSwitcher(Alignment align) override;
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override;
const ButtonId CreateClusterButton(ClusterId clusterId, const AZStd::string& icon) override;
@@ -41,15 +41,26 @@ namespace AzToolsFramework::ViewportUi
String
};
//! Used to anchor widgets to a specific side of the viewport.
enum class Alignment
{
TopRight,
TopLeft,
BottomRight,
BottomLeft,
Top,
Bottom
};
//! Viewport requests to interact with the Viewport UI. Viewport UI refers to the entire UI overlay (one per viewport).
//! Each widget on the Viewport UI is referred to as an element.
class ViewportUiRequests
{
public:
//! Creates and registers a cluster with the Viewport UI system.
virtual const ClusterId CreateCluster() = 0;
virtual const ClusterId CreateCluster(Alignment align) = 0;
//! Creates and registers a switcher with the Viewport UI system.
virtual const SwitcherId CreateSwitcher() = 0;
virtual const SwitcherId CreateSwitcher(Alignment align) = 0;
//! Sets the active button of the cluster. This is the button which will display as highlighted.
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
//! Sets the active button of the switcher. This is the button which has a text label.
@@ -72,7 +72,7 @@ namespace UnitTest
TEST_F(ViewportUiDisplayTestFixture, RemoveViewportUiElementRemovesElementFromViewportUi)
{
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto widget = viewportUi.GetViewportUiElement(m_buttonGroup->GetViewportUiElementId());
EXPECT_TRUE(widget.get() != nullptr);
@@ -89,7 +89,7 @@ namespace UnitTest
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.InitializeUiOverlay();
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.Update();
viewportUi.ShowViewportUiElement(m_buttonGroup->GetViewportUiElementId());
@@ -102,7 +102,7 @@ namespace UnitTest
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.InitializeUiOverlay();
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.HideViewportUiElement(m_buttonGroup->GetViewportUiElementId());
EXPECT_FALSE(viewportUi.IsViewportUiElementVisible(m_buttonGroup->GetViewportUiElementId()));
@@ -112,7 +112,7 @@ namespace UnitTest
{
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
viewportUi.InitializeUiOverlay();
viewportUi.AddCluster(m_buttonGroup);
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.Update();
auto widget = viewportUi.GetViewportUiElement(m_buttonGroup->GetViewportUiElementId());
@@ -129,7 +129,7 @@ namespace UnitTest
auto buttonGroup = AZStd::make_shared<ButtonGroup>();
buttonGroup->AddButton("");
viewportUi.AddCluster(buttonGroup);
viewportUi.AddCluster(buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
viewportUi.Update();
EXPECT_TRUE(viewportUi.GetUiMainWindow()->isVisible());
@@ -101,7 +101,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, CreateClusterAddsNewClusterAndReturnsId)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
EXPECT_TRUE(clusterEntry != m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().end());
@@ -110,7 +110,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, CreateClusterButtonAddsNewButtonAndReturnsId)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
@@ -120,7 +120,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, SetClusterActiveButtonSetsButtonStateToActive)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
@@ -133,7 +133,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, RegisterClusterEventHandlerConnectsHandlerToClusterEvent)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
// create a handler which will be triggered by the cluster
@@ -159,7 +159,7 @@ namespace UnitTest
TEST_F(ViewportUiManagerTestFixture, RemoveClusterRemovesClusterFromViewportUi)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
m_viewportManagerWrapper.GetViewportManager()->RemoveCluster(clusterId);
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
@@ -171,7 +171,7 @@ namespace UnitTest
{
m_viewportManagerWrapper.GetMockRenderOverlay()->setVisible(true);
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
m_viewportManagerWrapper.GetViewportManager()->Update();
+142
View File
@@ -0,0 +1,142 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AzFramework
{
std::ostream& operator<<(std::ostream& os, const ClickDetector::ClickOutcome clickOutcome)
{
switch (clickOutcome)
{
case ClickDetector::ClickOutcome::Click:
os << "ClickOutcome::Click";
break;
case ClickDetector::ClickOutcome::Move:
os << "ClickOutcome::Move";
break;
case ClickDetector::ClickOutcome::Release:
os << "ClickOutcome::Release";
break;
case ClickDetector::ClickOutcome::Nil:
os << "ClickOutcome::Nil";
break;
}
return os;
}
} // namespace AzFramework
namespace UnitTest
{
using AzFramework::ClickDetector;
using AzFramework::ScreenVector;
class ClickDetectorFixture : public ::testing::Test
{
public:
ClickDetector m_clickDetector;
};
TEST_F(ClickDetectorFixture, ClickIsDetectedWithNoMouseMovementOnMouseUp)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
}
TEST_F(ClickDetectorFixture, MoveIsDetectedWithMouseMovementAfterMouseDown)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialMoveOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
}
TEST_F(ClickDetectorFixture, ReleaseIsDetectedAfterMouseMovementOnMouseUp)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
// move
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Release));
}
TEST_F(ClickDetectorFixture, MoveIsReturnedOnlyAfterFirstMouseMove)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialMoveOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
const ClickDetector::ClickOutcome secondaryMoveOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
EXPECT_THAT(secondaryMoveOutcome, Eq(ClickDetector::ClickOutcome::Nil));
}
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterDoubleClick)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryUpOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // double click
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
}
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoredDoubleClick)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome initialDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondaryUpOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
}
} // namespace UnitTest
+54
View File
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Viewport/CursorState.h>
namespace UnitTest
{
using AzFramework::CursorState;
using AzFramework::ScreenVector;
using AzFramework::ScreenPoint;
class CursorStateFixture : public ::testing::Test
{
public:
CursorState m_cursorState;
};
TEST_F(CursorStateFixture, CursorStateHasZeroDeltaInitially)
{
using ::testing::Eq;
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
}
TEST_F(CursorStateFixture, CursorStateReturnsZeroDeltaAfterSingleMoveAndUpdate)
{
using ::testing::Eq;
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
m_cursorState.Update();
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
}
TEST_F(CursorStateFixture, CursorStateReturnsDeltaAfterSecondMoveAndUpdate)
{
using ::testing::Eq;
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
m_cursorState.Update();
m_cursorState.SetCurrentPosition(ScreenPoint(15, 22));
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(5, 12)));
}
} // namespace UnitTest

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