Merge branch 'development' of https://github.com/o3de/o3de into carlitosan/development

This commit is contained in:
chcurran
2021-07-14 13:00:53 -07:00
48 changed files with 1395 additions and 1180 deletions
@@ -6,13 +6,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
import logging
import os
import tempfile
import psutil
import ly_test_tools.log.log_monitor
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.environment.waiter as waiter
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
from ly_remote_console.remote_console_commands import send_command_and_expect_response as send_command_and_expect_response
logger = logging.getLogger(__name__)
@@ -95,7 +95,7 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc
return port_listening
if null_renderer:
launcher.args.extend(["-NullRenderer"])
launcher.args.extend(["-rhi=Null"])
# Start the Launcher
with launcher.start():
@@ -110,8 +110,8 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc
# Load the specified level in the launcher
send_command_and_expect_response(remote_console_instance,
f"map {level}",
"LEVEL_LOAD_COMPLETE", timeout=30)
f"LoadLevel {level}",
"LEVEL_LOAD_END", timeout=30)
# Monitor the console for expected lines
for line in expected_lines:
@@ -36,8 +36,8 @@ class TestAltitudeFilterFilterStageToggle(EditorTestHelper):
:return: None
"""
PREPROCESS_INSTANCE_COUNT = 24
POSTPROCESS_INSTANCE_COUNT = 18
PREPROCESS_INSTANCE_COUNT = 44
POSTPROCESS_INSTANCE_COUNT = 34
# Create empty level
self.test_success = self.create_level(
@@ -62,25 +62,7 @@ class TestAltitudeFilterFilterStageToggle(EditorTestHelper):
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
# Add entity with Mesh to replicate creation of hills
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 40.0, 40.0, 40.0)
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
# Increase Box Shape size to encompass the hills
vegetation.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(100.0, 100.0, 100.0))
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 10.0)
# Set a Min Altitude of 38 and Max of 40 in Vegetation Altitude Filter
vegetation.get_set_test(3, "Configuration|Altitude Min", 38.0)
@@ -9,9 +9,10 @@ import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.entity as EntityId
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
@@ -83,18 +84,12 @@ class TestDynamicSliceInstanceSpawnerEmbeddedEditor(EditorTestHelper):
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 5) Create a new entity with a Camera component for testing in the launcher
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
camera_component = ["Camera"]
new_entity_id2 = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", cam_position, EntityId.EntityId()
)
if new_entity_id2.IsValid():
self.log("Camera entity created")
camera_entity = hydra.Entity("Camera Entity", new_entity_id2)
camera_entity.components = []
for component in camera_component:
camera_entity.components.append(hydra.add_component(component, new_entity_id2))
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 6) Save and export to engine
general.save_level()
@@ -11,7 +11,8 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.entity as EntityId
import azlmbr.components as components
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
@@ -68,7 +69,7 @@ class TestDynamicSliceInstanceSpawnerExternalEditor(EditorTestHelper):
veg_area_required_components = ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List",
"Script Canvas"]
new_entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId()
)
if new_entity_id.IsValid():
self.log("Spawner entity created")
@@ -106,18 +107,12 @@ class TestDynamicSliceInstanceSpawnerExternalEditor(EditorTestHelper):
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 5) Create a new entity with a Camera component for testing in the launcher
entity_position = math.Vector3(512.0, 500.0, 35.0)
camera_component = ["Camera"]
new_entity_id2 = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if new_entity_id2.IsValid():
self.log("Camera entity created")
camera_entity = hydra.Entity("Camera Entity", new_entity_id2)
camera_entity.components = []
for component in camera_component:
camera_entity.components.append(hydra.add_component(component, new_entity_id2))
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 6) Save and export to engine
general.save_level()
@@ -18,9 +18,9 @@ import azlmbr.areasystem as areasystem
import azlmbr.legacy.general as general
import azlmbr
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.components as components
import azlmbr.math as math
import azlmbr.entity as EntityId
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
@@ -134,20 +134,14 @@ class TestVegLayerBlenderCreated(EditorTestHelper):
purple_count += 1
self.test_success = pink_count == purple_count and (pink_count + purple_count == num_expected) and self.test_success
# 5) Create a new entity with a Camera component for testing in the launcher
entity_position = math.Vector3(500.0, 500.0, 47.0)
rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0))
camera_component = ["Camera"]
camera_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if camera_id.IsValid():
self.log("Camera entity created")
camera_entity = hydra.Entity("Camera Entity", camera_id)
camera_entity.components = []
for component in camera_component:
camera_entity.components.append(hydra.add_component(component, camera_id))
azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", camera_id, rot_degrees_vector)
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(500.0, 500.0, 47.0)
cam_rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0))
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector)
# 6) Save and export level
general.save_level()
@@ -34,8 +34,8 @@ class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
:return: None
"""
PREPROCESS_INSTANCE_COUNT = 425
POSTPROCESS_INSTANCE_COUNT = 430
PREPROCESS_INSTANCE_COUNT = 21
POSTPROCESS_INSTANCE_COUNT = 19
# Create empty level
self.test_success = self.create_level(
@@ -56,7 +56,6 @@ class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
vegetation_entity.add_component("Vegetation Altitude Filter")
vegetation_entity.add_component("Vegetation Position Modifier")
# Create a child entity under vegetation area
child_entity = hydra.Entity("child_entity")
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
@@ -66,29 +65,13 @@ class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
vegetation_entity.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", child_entity.id)
vegetation_entity.get_set_test(4, "Configuration|Position Y|Gradient|Gradient Entity Id", child_entity.id)
# Set the min and max values for Altitude Filter
vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 32.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 35.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 34.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 38.0)
# Add entity with Mesh to replicate creation of hills and a flat surface to plant on
dynveg.create_surface_entity("Flat Surface", position, 32.0, 32.0, 1.0)
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0, 4.0, 4.0)
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0)
# Set the filter stage to preprocess and postprocess respectively and verify instance count
vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 1)
@@ -13,7 +13,7 @@ import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from automatedtesting_shared.editor_test_helper import EditorTestHelper
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
@@ -82,22 +82,7 @@ class test_MeshBlocker_InstancesBlockedByMesh(EditorTestHelper):
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", cubeId)
components.TransformBus(bus.Event, "SetLocalScale", blocker_entity.id, math.Vector3(2.0, 2.0, 2.0))
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 2.0)
# Verify spawned instance counts are accurate after addition of Blocker Entity
num_expected = 160 # Number of "PurpleFlower"s that plant on a 10 x 10 surface minus 2m blocker cube
@@ -88,24 +88,9 @@ class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorTestHelper):
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_id)
components.TransformBus(bus.Event, "SetLocalScale", blocker_entity.id, math.Vector3(5.0, 5.0, 5.0))
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 5.0)
components.TransformBus(bus.Event, "SetLocalRotation", blocker_entity.id, math.Vector3(0.0, y_rotation, 0.0))
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
# 5) Adjust the height Max percentage values of blocker
blocker_entity.get_set_test(0, "Configuration|Mesh Height Percent Max", 0.8)
@@ -90,7 +90,6 @@ class TestDynamicSliceInstanceSpawner(object):
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
@pytest.mark.skip # ATOM-14703
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
@@ -126,7 +125,6 @@ class TestDynamicSliceInstanceSpawner(object):
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
@pytest.mark.skip # ATOM-14703
def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
@@ -68,7 +68,6 @@ class TestLayerBlender(object):
"Entity has a Box Shape component",
"Blender Configuration|Vegetation Areas: SUCCESS",
"Blender Box Shape|Box Configuration|Dimensions: SUCCESS",
"Camera entity created",
"LayerBlender_E2E_Editor: result=SUCCESS"
]
@@ -85,12 +84,11 @@ class TestLayerBlender(object):
@pytest.mark.BAT
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.xfail
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_LayerBlender_E2E_Launcher(self, workspace, project, launcher, level, remote_console_instance,
launcher_platform):
launcher.args.extend(["-NullRenderer"])
launcher.args.extend(["-rhi=Null"])
launcher.start()
assert launcher.is_alive(), "Launcher failed to start"
@@ -121,7 +121,7 @@ class TestLayerSpawner(object):
@pytest.mark.test_case_id("C30000751")
@pytest.mark.SUITE_sandbox
@pytest.mark.dynveg_misc
@pytest.mark.skip # ATOM-14828
@pytest.mark.skip # https://github.com/o3de/o3de/issues/2038
def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, editor, level, launcher_platform):
expected_lines = [
@@ -136,5 +136,6 @@ class TestLayerSpawner(object):
editor,
"LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py",
expected_lines,
cfg_args=[level]
cfg_args=[level],
null_renderer=False
)
@@ -34,7 +34,7 @@ def create_surface_entity(name, center_point, box_size_x, box_size_y, box_size_z
return surface_entity
def create_mesh_surface_entity_with_slopes(name, center_point, scale_x, scale_y, scale_z):
def create_mesh_surface_entity_with_slopes(name, center_point, uniform_scale):
# Creates an entity with the assigned mesh_asset as the specified scale and sets up as a planting surface
mesh_asset_path = os.path.join("models", "sphere.azmodel")
mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(),
@@ -47,7 +47,7 @@ def create_mesh_surface_entity_with_slopes(name, center_point, scale_x, scale_y,
if surface_entity.id.IsValid():
print(f"'{surface_entity.name}' created")
hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset)
components.TransformBus(bus.Event, "SetLocalScale", surface_entity.id, math.Vector3(scale_x, scale_y, scale_z))
components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, uniform_scale)
return surface_entity
@@ -742,14 +742,14 @@
</Class>
<Class name="int" field="methodType" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZStd::string" field="methodName" value="GetOnPostsimulateEvent" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="namespaces" type="{99DAD0BC-740E-5E82-826B-8FC7968CC02C}"/>
<Class name="AZStd::vector" field="resultSlotIDs" type="{D0B13803-101B-54D8-914C-0DA49FDFA268}">
<Class name="SlotId" field="element" version="2" type="{14C629F6-467B-46FE-8B63-48FDFCA42175}">
<Class name="AZ::Uuid" field="m_id" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="AZStd::string" field="prettyClassName" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="prettyClassName" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
@@ -1045,14 +1045,14 @@
</Class>
<Class name="int" field="methodType" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZStd::string" field="methodName" value="GetOnPostsimulateEvent" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="namespaces" type="{99DAD0BC-740E-5E82-826B-8FC7968CC02C}"/>
<Class name="AZStd::vector" field="resultSlotIDs" type="{D0B13803-101B-54D8-914C-0DA49FDFA268}">
<Class name="SlotId" field="element" version="2" type="{14C629F6-467B-46FE-8B63-48FDFCA42175}">
<Class name="AZ::Uuid" field="m_id" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="AZStd::string" field="prettyClassName" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="prettyClassName" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
@@ -1085,14 +1085,14 @@
</Class>
<Class name="int" field="methodType" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZStd::string" field="methodName" value="GetOnPresimulateEvent" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="namespaces" type="{99DAD0BC-740E-5E82-826B-8FC7968CC02C}"/>
<Class name="AZStd::vector" field="resultSlotIDs" type="{D0B13803-101B-54D8-914C-0DA49FDFA268}">
<Class name="SlotId" field="element" version="2" type="{14C629F6-467B-46FE-8B63-48FDFCA42175}">
<Class name="AZ::Uuid" field="m_id" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="AZStd::string" field="prettyClassName" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="prettyClassName" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
+4 -2
View File
@@ -1896,7 +1896,7 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
if (m_pressedKeyState != KeyPressedState::PressedInPreviousFrame)
{
CUndo undo("Move Camera");
AzToolsFramework::ScopedUndoBatch undo("Move Camera");
if (bMoveOnly)
{
// specify eObjectUpdateFlags_UserInput so that an undo command gets logged
@@ -1932,7 +1932,7 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
if (m_pressedKeyState != KeyPressedState::PressedInPreviousFrame)
{
CUndo undo("Move Camera");
AzToolsFramework::ScopedUndoBatch undo("Move Camera");
if (bMoveOnly)
{
AZ::TransformBus::Event(
@@ -1945,6 +1945,8 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
m_viewEntityId, &AZ::TransformInterface::SetWorldTM,
LYTransformToAZTransform(camMatrix));
}
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::AddDirtyEntity, m_viewEntityId);
}
else
{
+19 -20
View File
@@ -83,9 +83,9 @@ AZ::RPI::ViewportContextPtr LegacyViewportCameraControllerInstance::GetViewportC
}
bool LegacyViewportCameraControllerInstance::HandleMouseMove(
const AzFramework::ScreenPoint& currentMousePos, const AzFramework::ScreenPoint& previousMousePos)
int dx, int dy)
{
if (previousMousePos == currentMousePos)
if (dx == 0 && dy == 0)
{
return false;
}
@@ -105,7 +105,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
if (m_inMoveMode || m_inOrbitMode || m_inRotateMode || m_inZoomMode)
{
m_totalMouseMoveDelta += (QPoint(currentMousePos.m_x, currentMousePos.m_y)-QPoint(previousMousePos.m_x, previousMousePos.m_y)).manhattanLength();
m_totalMouseMoveDelta += AZStd::abs(dx) + AZStd::abs(dy);
}
if ((m_inRotateMode && m_inMoveMode) || m_inZoomMode)
@@ -115,7 +115,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
Vec3 ydir = m.GetColumn1().GetNormalized();
Vec3 pos = m.GetTranslation();
const float posDelta = 0.2f * (previousMousePos.m_y - currentMousePos.m_y) * speedScale;
const float posDelta = 0.2f * dy * speedScale;
pos = pos - ydir * posDelta;
m_orbitDistance = m_orbitDistance + posDelta;
m_orbitDistance = fabs(m_orbitDistance);
@@ -126,7 +126,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
}
else if (m_inRotateMode)
{
Ang3 angles(-currentMousePos.m_y + previousMousePos.m_y, 0, -currentMousePos.m_x + previousMousePos.m_x);
Ang3 angles(dy, 0, dx);
angles = angles * 0.002f * gSettings.cameraRotateSpeed;
if (gSettings.invertYRotation)
{
@@ -158,7 +158,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
}
Vec3 pos = m.GetTranslation();
pos += 0.1f * xdir * (currentMousePos.m_x - previousMousePos.m_x) * speedScale + 0.1f * zdir * (previousMousePos.m_y - currentMousePos.m_y) * speedScale;
pos += 0.1f * xdir * dx * speedScale + 0.1f * zdir * dy * speedScale;
m.SetTranslation(pos);
AZ::Transform transform = viewportContext->GetCameraTransform();
@@ -168,7 +168,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
}
else if (m_inOrbitMode)
{
Ang3 angles(-currentMousePos.m_y + previousMousePos.m_y, 0, -currentMousePos.m_x + previousMousePos.m_x);
Ang3 angles(dy, 0, dx);
angles = angles * 0.002f * gSettings.cameraRotateSpeed;
if (gSettings.invertPan)
@@ -302,20 +302,19 @@ bool LegacyViewportCameraControllerInstance::HandleInputChannelEvent(const AzFra
bool shouldCaptureCursor = m_capturingCursor;
bool shouldConsumeEvent = false;
if (id == AzFramework::InputDeviceMouse::SystemCursorPosition)
if (id == AzFramework::InputDeviceMouse::Movement::X || id == AzFramework::InputDeviceMouse::Movement::Y)
{
bool result = false;
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Event(
GetViewportId(),
[this, &result](AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequests* mouseRequests)
{
if (auto previousMousePosition = mouseRequests->PreviousViewportCursorScreenPosition();
previousMousePosition.has_value())
{
result = HandleMouseMove(mouseRequests->ViewportCursorScreenPosition(), previousMousePosition.value());
}
});
return result;
int dx = 0;
int dy = 0;
if (id == AzFramework::InputDeviceMouse::Movement::X)
{
dx = -aznumeric_cast<int>(event.m_inputChannel.GetValue());
}
else
{
dy = -aznumeric_cast<int>(event.m_inputChannel.GetValue());
}
return HandleMouseMove(dx, dy);
}
else if (id == MouseButton::Left)
{
+1 -1
View File
@@ -69,7 +69,7 @@ namespace SandboxEditor
AZ::RPI::ViewportContextPtr GetViewportContext();
bool HandleMouseMove(const AzFramework::ScreenPoint& currentMousePos, const AzFramework::ScreenPoint& previousMousePos);
bool HandleMouseMove(int dx, int dy);
bool HandleMouseWheel(float zDelta);
bool IsKeyDown(Qt::Key key) const;
void UpdateCursorCapture(bool shouldCaptureCursor);
@@ -20,12 +20,12 @@
#include "ValidationHandler.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/Path/Path.h>
#include "AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h"
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <Util/FileUtil.h>
#include <QMessageBox>
#include <QCloseEvent>
@@ -52,7 +52,7 @@ namespace ProjectSettingsTool
PlatformEnabled(PlatformId::Ios) ?
ProjectSettingsContainer::PlistInitVector({
ProjectSettingsContainer::PlatformAndPath
{ PlatformId::Ios, m_projectRoot + PlatformResourcesFolder(PlatformId::Ios) }
{ PlatformId::Ios, GetPlatformResource(PlatformId::Ios) }
})
:
ProjectSettingsContainer::PlistInitVector())
@@ -647,33 +647,38 @@ namespace ProjectSettingsTool
// iOS can be disabled if the plist file is missing
if (platformId == PlatformId::Ios)
{
const AZStd::string filename = m_projectRoot + PlatformResourcesFolder(platformId);
return CFileUtil::FileExists(filename.c_str());
AZStd::string plistPath = GetPlatformResource(platformId);
return !plistPath.empty();
}
return true;
}
const char* ProjectSettingsToolWindow::PlatformResourcesFolder(PlatformId platformId)
AZStd::string ProjectSettingsToolWindow::GetPlatformResource(PlatformId platformId)
{
if (platformId == PlatformId::Ios)
{
const AZStd::string firstfilename = m_projectRoot + "/Gem/Resources/Platform/iOS/Info.plist";
if (CFileUtil::FileExists(firstfilename.c_str()))
const char* searchPaths[] = {
"Resources/Platform/iOS/Info.plist",
// legacy paths
"Gem/Resources/Platform/iOS/Info.plist",
"Gem/Resources/IOSLauncher/Info.plist",
};
for (auto relPath : searchPaths)
{
return "/Gem/Resources/Platform/iOS/Info.plist";
}
else
{
const AZStd::string filename = m_projectRoot + "/Gem/Resources/IOSLauncher/Info.plist";
if (CFileUtil::FileExists(filename.c_str()))
AZ::IO::FixedMaxPath projectPlist{ m_projectRoot };
projectPlist /= relPath;
if (AZ::IO::SystemFile::Exists(projectPlist.c_str()))
{
return "/Gem/Resources/IOSLauncher/Info.plist";
return projectPlist.LexicallyNormal().String();
}
}
}
return nullptr;
return AZStd::string();
}
#include <moc_ProjectSettingsToolWindow.cpp>
@@ -137,8 +137,8 @@ namespace ProjectSettingsTool
// returns true if the platform is enabled
bool PlatformEnabled(PlatformId platformId);
// returns the resource folder
const char* PlatformResourcesFolder(PlatformId platformId);
// returns the main platform specific resource file e.g. for iOS it would be the Info.plist
AZStd::string GetPlatformResource(PlatformId platformId);
// The ui for the window
QScopedPointer<Ui::ProjectSettingsToolWidget> m_ui;
@@ -116,7 +116,7 @@ namespace AZ
{
const char* uuidString = nullptr;
unsigned int uuidStringLength = 0;
if (dc.ReadArg(0, uuidString) && dc.ReadValue(1, uuidStringLength))
if (dc.ReadArg(0, uuidString) && dc.ReadArg(1, uuidStringLength))
{
dc.PushResult(Uuid(uuidString, uuidStringLength));
}
@@ -97,6 +97,9 @@ namespace AzFramework
//! This is called when the window is deactivated from code or if the user closes the window.
virtual void OnWindowClosed() {};
//! This is called when vsync interval is changed.
virtual void OnVsyncIntervalChanged(uint32_t interval) { AZ_UNUSED(interval); };
};
using WindowNotificationBus = AZ::EBus<WindowNotifications>;
@@ -63,6 +63,9 @@ namespace AzToolsFramework
//! Signal the Python handler to stop
virtual bool StopPython(bool silenceWarnings = false) = 0;
//! Query to determine if the Python VM has been initialized indicating an active state
virtual bool IsPythonActive() = 0;
//! Determines if the caller needs to wait for the Python VM to initialize (non-main thread only)
virtual void WaitForInitialization() {}
@@ -11,6 +11,7 @@
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
#include <QApplication>
#include <QCursor>
@@ -187,12 +188,6 @@ namespace AzToolsFramework
bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const
{
const AzFramework::InputChannelId& channelId = channel.GetInputChannelId();
if (channelId == AzFramework::InputDeviceMouse::Movement::X || channelId == AzFramework::InputDeviceMouse::Movement::Y)
{
return false;
}
// We map keyboard and mouse events from Qt, so flag all events coming from those devices
// as handled by our synthetic event system.
const AzFramework::InputDeviceId& deviceId = channel.GetInputDevice().GetInputDeviceId();
@@ -210,6 +205,22 @@ namespace AzToolsFramework
}
}
void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled)
{
if (m_capturingCursor != enabled)
{
m_capturingCursor = enabled;
if (m_capturingCursor)
{
qApp->setOverrideCursor(Qt::BlankCursor);
}
else
{
qApp->restoreOverrideCursor();
}
}
}
bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event)
{
// Abort if processing isn't enabled.
@@ -284,13 +295,25 @@ namespace AzToolsFramework
{
auto systemCursorChannel =
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::SystemCursorPosition);
auto movementXChannel =
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::X);
auto movementYChannel =
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::Y);
auto mouseWheelChannel =
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::Z);
systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength());
// Generate movement events based on the pixel delta divided by the DPI scaling factor, to calculate a rough approximation
// of cursor movement velocity.
movementXChannel->ProcessRawInputEvent(
m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast<float>(m_sourceWidget->width()) / m_sourceWidget->devicePixelRatioF());
movementYChannel->ProcessRawInputEvent(
m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast<float>(m_sourceWidget->height()) / m_sourceWidget->devicePixelRatioF());
mouseWheelChannel->ProcessRawInputEvent(0.f);
NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr);
NotifyUpdateChannelIfNotIdle(movementXChannel, nullptr);
NotifyUpdateChannelIfNotIdle(movementYChannel, nullptr);
NotifyUpdateChannelIfNotIdle(mouseWheelChannel, nullptr);
}
@@ -318,16 +341,42 @@ namespace AzToolsFramework
}
}
AZ::Vector2 QtEventToAzInputMapper::WidgetPositionToNormalizedPosition(QPoint position)
{
const float normalizedX = aznumeric_cast<float>(position.x()) / aznumeric_cast<float>(m_sourceWidget->width());
const float normalizedY = aznumeric_cast<float>(position.y()) / aznumeric_cast<float>(m_sourceWidget->height());
return AZ::Vector2{normalizedX, normalizedY};
}
QPoint QtEventToAzInputMapper::NormalizedPositionToWidgetPosition(AZ::Vector2 normalizedPosition)
{
const int denormalizedX = aznumeric_cast<int>(normalizedPosition.GetX() * m_sourceWidget->width());
const int denormalizedY = aznumeric_cast<int>(normalizedPosition.GetY() * m_sourceWidget->height());
return QPoint{denormalizedX, denormalizedY};
}
void QtEventToAzInputMapper::HandleMouseMoveEvent(QMouseEvent* mouseEvent)
{
AZ::Vector2 lastCursorPosition = m_cursorPosition->m_normalizedPosition;
const QPoint mousePos = mouseEvent->pos();
const float normalizedX = aznumeric_cast<float>(mousePos.x()) / aznumeric_cast<float>(m_sourceWidget->width());
const float normalizedY = aznumeric_cast<float>(mousePos.y()) / aznumeric_cast<float>(m_sourceWidget->height());
const AZ::Vector2 normalizedPosition(normalizedX, normalizedY);
const AZ::Vector2 normalizedPosition = WidgetPositionToNormalizedPosition(mousePos);
m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition;
m_cursorPosition->m_normalizedPosition = normalizedPosition;
ProcessPendingMouseEvents();
m_mouseChannelsNeedUpdate = true;
if (m_capturingCursor)
{
// Reset our cursor position to the previous point.
QPoint targetScreenPosition = m_sourceWidget->mapToGlobal(NormalizedPositionToWidgetPosition(lastCursorPosition));
AzQtComponents::SetCursorPos(targetScreenPosition);
// Even though we just set the cursor position, there are edge cases such as remote desktop that will leave
// the cursor position unchanged. For safety, we re-cache our last cursor position for delta generation.
QPoint actualWidgetPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
m_cursorPosition->m_normalizedPosition = WidgetPositionToNormalizedPosition(actualWidgetPosition);
}
}
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
@@ -47,6 +47,12 @@ namespace AzToolsFramework
//! Sets whether or not this input mapper should be updating its input channels from Qt events.
void SetEnabled(bool enabled);
//! Sets whether or not the cursor should be constrained to the source widget and invisible.
//! Internally, this will reset the cursor position after each move event to ensure movement
//! events don't allow the cursor to escape. This can be used for typical camera controls
//! like a dolly or rotation, where mouse movement is important but cursor location is not.
void SetCursorCaptureEnabled(bool enabled);
// QObject overrides...
bool eventFilter(QObject* object, QEvent* event) override;
@@ -106,6 +112,11 @@ namespace AzToolsFramework
// Processes any pending mouse movement events, this allows mouse movement channels to close themselves.
void ProcessPendingMouseEvents();
// Converts a point in logical source widget space [0..m_sourceWidget->size()] to normalized [0..1] space.
AZ::Vector2 WidgetPositionToNormalizedPosition(QPoint position);
// Converts a point in normalized [0..1] space to logical source widget space [0..m_sourceWidget->size()].
QPoint NormalizedPositionToWidgetPosition(AZ::Vector2 normalizedPosition);
// Handle mouse click events.
void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
// Handle mouse move events.
@@ -144,6 +155,8 @@ namespace AzToolsFramework
bool m_mouseChannelsNeedUpdate = false;
// Flags whether or not Qt events should currently be processed.
bool m_enabled = true;
// Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement).
bool m_capturingCursor = false;
// Our viewport-specific AZ devices. We control their internal input channel states.
AZStd::unique_ptr<EditorQtMouseDevice> m_mouseDevice;
@@ -276,11 +276,6 @@ namespace AzToolsFramework
virtual void EndCursorCapture() = 0;
//! Gets the most recent recorded cursor position in the viewport in screen space coordinates.
virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0;
//! Gets the cursor position recorded prior to the most recent cursor position.
//! Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result
//! from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
//! position delta.
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
//! Is mouse over viewport.
virtual bool IsMouseOver() const = 0;
@@ -7,6 +7,8 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace AZ
{
@@ -31,6 +33,12 @@ namespace AZ
{
serializeContext->Class<ExportingComponent, AZ::Component>()->Version(2);
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
Events::ExportProductList::Reflect(behaviorContext);
}
}
} // namespace SceneCore
} // namespace SceneAPI
@@ -211,6 +211,7 @@ namespace AZ
AZ::SceneAPI::Containers::SceneGraph::Reflect(context);
AZ::SceneAPI::Containers::SceneManifest::Reflect(context);
AZ::SceneAPI::Containers::RuleContainer::Reflect(context);
AZ::SceneAPI::SceneCore::ExportingComponent::Reflect(context);
}
void Activate()
@@ -6,6 +6,8 @@
*/
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/limits.h>
namespace AZ
{
@@ -49,6 +51,45 @@ namespace AZ
return *this;
}
void ExportProductList::Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ExportProduct>()->Version(1);
serializeContext->Class<ExportProductList>()->Version(1);
}
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ExportProduct>("ExportProduct")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Property("filename", BehaviorValueProperty(&ExportProduct::m_filename))
->Property("sourceId", BehaviorValueProperty(&ExportProduct::m_id))
->Property("assetType", BehaviorValueProperty(&ExportProduct::m_assetType))
->Property("productDependencies", BehaviorValueProperty(&ExportProduct::m_productDependencies))
->Property("subId",
[](ExportProduct* self) { return self->m_subId.has_value() ? self->m_subId.value() : 0; },
[](ExportProduct* self, u32 subId) { self->m_subId = AZStd::optional<u32>(subId); });
behaviorContext->Class<ExportProductList>("ExportProductList")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("AddProduct", [](ExportProductList& self, ExportProduct& product)
{
self.AddProduct(
product.m_filename,
product.m_id,
product.m_assetType,
product.m_lod,
product.m_subId,
product.m_dependencyFlags);
})
->Method("GetProducts", &ExportProductList::GetProducts)
->Method("AddDependencyToProduct", &ExportProductList::AddDependencyToProduct);
}
}
ExportProduct& ExportProductList::AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags)
{
@@ -14,6 +14,8 @@
namespace AZ
{
class ReflectContext;
namespace SceneAPI
{
namespace Events
@@ -24,6 +26,7 @@ namespace AZ
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
SCENE_CORE_API ExportProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
ExportProduct() = default;
ExportProduct(const ExportProduct& rhs) = default;
SCENE_CORE_API ExportProduct(ExportProduct&& rhs);
@@ -54,6 +57,8 @@ namespace AZ
class ExportProductList
{
public:
static void Reflect(ReflectContext* context);
SCENE_CORE_API ExportProduct& AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
SCENE_CORE_API ExportProduct& AddProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
@@ -69,3 +74,9 @@ namespace AZ
} // namespace Events
} // namespace SceneAPI
} // namespace AZ
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(SceneAPI::Events::ExportProduct, "{6054EDCB-4C04-4D96-BF26-704999FFB725}");
AZ_TYPE_INFO_SPECIALIZE(SceneAPI::Events::ExportProductList, "{1C76A51F-431B-4987-B653-CFCC940D0D0F}");
}
@@ -10,6 +10,7 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h>
@@ -75,15 +76,16 @@ namespace AZ
filename += s_extension;
filename += s_generated;
AZStd::string altManifestPath = path;
AZStd::string altManifestFolder = path;
AzFramework::ApplicationRequests::Bus::Broadcast(
&AzFramework::ApplicationRequests::Bus::Events::MakePathRootRelative,
altManifestPath);
&AzFramework::ApplicationRequests::Bus::Events::MakePathRelative,
altManifestFolder,
AZ::Utils::GetProjectPath().c_str());
AZ::StringFunc::Path::GetFolderPath(altManifestPath.c_str(), altManifestPath);
AZ::StringFunc::Path::GetFolderPath(altManifestFolder.c_str(), altManifestFolder);
AZStd::string generatedAssetInfoPath;
AZ::StringFunc::Path::Join(assetCacheRoot.c_str(), altManifestPath.c_str(), generatedAssetInfoPath);
AZ::StringFunc::Path::Join(assetCacheRoot.c_str(), altManifestFolder.c_str(), generatedAssetInfoPath);
AZ::StringFunc::Path::ConstructFull(generatedAssetInfoPath.c_str(), filename.c_str(), generatedAssetInfoPath);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(generatedAssetInfoPath.c_str()))
File diff suppressed because it is too large Load Diff
@@ -25,213 +25,319 @@
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
namespace AZ
namespace AZ::SceneAPI::Behaviors
{
namespace SceneAPI
class EditorPythonConsoleNotificationHandler final
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
{
namespace Behaviors
public:
EditorPythonConsoleNotificationHandler()
{
class EditorPythonConsoleNotificationHandler final
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
BusConnect();
}
~EditorPythonConsoleNotificationHandler()
{
BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorPythonConsoleNotifications
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
}
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
}
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
}
};
using ExportProductList = AZ::SceneAPI::Events::ExportProductList;
// a event bus to signal during scene building
struct ScriptBuildingNotifications
: public AZ::EBusTraits
{
virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0;
virtual ExportProductList OnPrepareForExport(
const Containers::Scene& scene,
AZStd::string_view outputDirectory,
AZStd::string_view platformIdentifier,
const ExportProductList& productList) = 0;
};
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>;
// a back end to handle scene builder events for a script
struct ScriptBuildingNotificationBusHandler final
: public ScriptBuildingNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(
ScriptBuildingNotificationBusHandler,
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}",
AZ::SystemAllocator,
OnUpdateManifest,
OnPrepareForExport);
virtual ~ScriptBuildingNotificationBusHandler() = default;
AZStd::string OnUpdateManifest(Containers::Scene& scene) override
{
AZStd::string result;
CallResult(result, FN_OnUpdateManifest, scene);
return result;
}
ExportProductList OnPrepareForExport(
const Containers::Scene& scene,
AZStd::string_view outputDirectory,
AZStd::string_view platformIdentifier,
const ExportProductList& productList) override
{
ExportProductList result;
CallResult(result, FN_OnPrepareForExport, scene, outputDirectory, platformIdentifier, productList);
return result;
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
public:
EditorPythonConsoleNotificationHandler()
{
BusConnect();
}
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Handler<ScriptBuildingNotificationBusHandler>()
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest)
->Event("OnPrepareForExport", &ScriptBuildingNotificationBus::Events::OnPrepareForExport);
}
}
};
~EditorPythonConsoleNotificationHandler()
{
BusDisconnect();
}
struct ScriptProcessorRuleBehavior::ExportEventHandler final
: public AZ::SceneAPI::SceneCore::ExportingComponent
{
using PreExportEventContextFunction = AZStd::function<bool(Events::PreExportEventContext&)>;
PreExportEventContextFunction m_preExportEventContextFunction;
////////////////////////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorPythonConsoleNotifications
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
}
ExportEventHandler(PreExportEventContextFunction preExportEventContextFunction)
: m_preExportEventContextFunction(preExportEventContextFunction)
{
BindToCall(&ExportEventHandler::PrepareForExport);
AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
}
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
}
~ExportEventHandler()
{
AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
}
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
}
};
// this allows a Python script to add product assets on "scene export"
Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
{
return m_preExportEventContextFunction(context) ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
}
};
// a event bus to signal during scene building
struct ScriptBuildingNotifications
: public AZ::EBusTraits
void ScriptProcessorRuleBehavior::Activate()
{
Events::AssetImportRequestBus::Handler::BusConnect();
m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](Events::PreExportEventContext& context)
{
return this->DoPrepareForExport(context);
});
}
void ScriptProcessorRuleBehavior::Deactivate()
{
m_exportEventHandler.reset();
Events::AssetImportRequestBus::Handler::BusDisconnect();
UnloadPython();
}
bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene)
{
if (m_editorPythonEventsInterface && !m_scriptFilename.empty())
{
return true;
}
// get project folder
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::IO::FixedMaxPath projectPath;
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
return false;
}
const AZ::SceneAPI::Containers::SceneManifest& manifest = scene.GetManifest();
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(manifest.GetValueStorage());
for (const auto& scriptItem : view)
{
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename());
if (scriptFilename.empty())
{
virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0;
};
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>;
AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
continue;
}
// a back end to handle scene builder events for a script
struct ScriptBuildingNotificationBusHandler final
: public ScriptBuildingNotificationBus::Handler
, public AZ::BehaviorEBusHandler
// check for file exist via absolute path
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
{
AZ_EBUS_BEHAVIOR_BINDER(
ScriptBuildingNotificationBusHandler,
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}",
AZ::SystemAllocator,
OnUpdateManifest);
virtual ~ScriptBuildingNotificationBusHandler() = default;
AZStd::string OnUpdateManifest(Containers::Scene& scene) override
// check for script in the project folder
AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
{
AZStd::string result;
CallResult(result, FN_OnUpdateManifest, scene);
return result;
AZ_Warning("scene", false, "Skipping a missing script (%s) in manifest file (%s)",
scriptFilename.c_str(),
scene.GetManifestFilename().c_str());
continue;
}
scriptFilename = AZStd::move(projectScriptPath);
}
static void Reflect(AZ::ReflectContext* context)
// lazy load the Python interface
auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
if (editorPythonEventsInterface->IsPythonActive() == false)
{
const bool silenceWarnings = false;
if (editorPythonEventsInterface->StartPython(silenceWarnings) == false)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Handler<ScriptBuildingNotificationBusHandler>()
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest);
}
editorPythonEventsInterface = nullptr;
}
}
// both Python and the script need to be ready
if (editorPythonEventsInterface == nullptr || scriptFilename.empty())
{
AZ_Warning("scene", false,"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
"please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
scene.GetManifestFilename().c_str(), scriptFilename.c_str());
return false;
}
m_editorPythonEventsInterface = editorPythonEventsInterface;
m_scriptFilename = scriptFilename.c_str();
return true;
}
return false;
}
void ScriptProcessorRuleBehavior::UnloadPython()
{
if (m_editorPythonEventsInterface)
{
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StopPython(silenceWarnings);
m_editorPythonEventsInterface = nullptr;
}
}
bool ScriptProcessorRuleBehavior::DoPrepareForExport(Events::PreExportEventContext& context)
{
using namespace AzToolsFramework;
auto executeCallback = [this, &context]()
{
// set up script's hook callback
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
m_scriptFilename.c_str());
// call script's callback to allow extra products
ExportProductList extraProducts;
ScriptBuildingNotificationBus::BroadcastResult(extraProducts, &ScriptBuildingNotificationBus::Events::OnPrepareForExport,
context.GetScene(),
context.GetOutputDirectory(),
context.GetPlatformIdentifier(),
context.GetProductList()
);
// add new products
for (const auto& product : extraProducts.GetProducts())
{
context.GetProductList().AddProduct(
product.m_filename,
product.m_id,
product.m_assetType,
product.m_lod,
product.m_subId,
product.m_dependencyFlags);
}
};
if (LoadPython(context.GetScene()))
{
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
}
return true;
}
void ScriptProcessorRuleBehavior::Reflect(ReflectContext* context)
{
ScriptBuildingNotificationBusHandler::Reflect(context);
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ScriptProcessorRuleBehavior, BehaviorComponent>()->Version(1);
}
}
Events::ProcessingResult ScriptProcessorRuleBehavior::UpdateManifest(
Containers::Scene& scene,
Events::AssetImportRequest::ManifestAction action,
[[maybe_unused]] Events::AssetImportRequest::RequestingApplication requester)
{
using namespace AzToolsFramework;
if (action != ManifestAction::Update)
{
return Events::ProcessingResult::Ignored;
}
if (LoadPython(scene))
{
AZStd::string manifestUpdate;
auto executeCallback = [this, &scene, &manifestUpdate]()
{
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
m_scriptFilename.c_str());
ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest,
scene);
};
void ScriptProcessorRuleBehavior::Activate()
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
// attempt to load the manifest string back to a JSON-scene-manifest
auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
if (loadOutcome.IsSuccess())
{
Events::AssetImportRequestBus::Handler::BusConnect();
scene.GetManifest().Clear();
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
{
scene.GetManifest().AddEntry(sceneManifestLoader->GetValue(entryIndex));
}
return Events::ProcessingResult::Success;
}
}
return Events::ProcessingResult::Ignored;
}
void ScriptProcessorRuleBehavior::Deactivate()
{
Events::AssetImportRequestBus::Handler::BusDisconnect();
if (m_editorPythonEventsInterface)
{
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StopPython(silenceWarnings);
m_editorPythonEventsInterface = nullptr;
}
}
void ScriptProcessorRuleBehavior::Reflect(ReflectContext* context)
{
ScriptBuildingNotificationBusHandler::Reflect(context);
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ScriptProcessorRuleBehavior, BehaviorComponent>()->Version(1);
}
}
Events::ProcessingResult ScriptProcessorRuleBehavior::UpdateManifest(
Containers::Scene& scene,
Events::AssetImportRequest::ManifestAction action,
[[maybe_unused]] Events::AssetImportRequest::RequestingApplication requester)
{
using namespace AzToolsFramework;
if (action != ManifestAction::Update)
{
return Events::ProcessingResult::Ignored;
}
// get project folder
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::IO::FixedMaxPath projectPath;
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
return Events::ProcessingResult::Ignored;
}
auto& sceneManifest = scene.GetManifest();
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(sceneManifest.GetValueStorage());
for (const auto& scriptItem : view)
{
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename());
if (scriptFilename.empty())
{
AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
continue;
}
// check for file exist via absolute path
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
{
// check for script in the project folder
AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
{
AZ_Warning("scene", false, "Skipping a missing script (%s) in manifest file (%s)",
scriptFilename.c_str(),
scene.GetManifestFilename().c_str());
continue;
}
scriptFilename = AZStd::move(projectScriptPath);
}
// lazy load the Python interface
if (!m_editorPythonEventsInterface)
{
m_editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StartPython(silenceWarnings);
}
if (!m_editorPythonEventsInterface && !scriptFilename.empty())
{
AZ_Warning("scene", false,
"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
"please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
scene.GetManifestFilename().c_str(), scriptFilename.c_str());
return Events::ProcessingResult::Ignored;
}
AZStd::string manifestUpdate;
auto executeCallback = [&scene, &scriptFilename, &manifestUpdate]()
{
EditorPythonRunnerRequestBus::Broadcast(
&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
scriptFilename.c_str());
ScriptBuildingNotificationBus::BroadcastResult(
manifestUpdate,
&ScriptBuildingNotificationBus::Events::OnUpdateManifest,
scene);
};
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
// attempt to load the manifest string back to a JSON-scene-manifest
auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
if (loadOutcome.IsSuccess())
{
sceneManifest.Clear();
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
{
sceneManifest.AddEntry(sceneManifestLoader->GetValue(entryIndex));
}
return Events::ProcessingResult::Success;
}
}
return Events::ProcessingResult::Ignored;
}
} // namespace Behaviors
} // namespace SceneAPI
} // namespace AZ
@@ -11,40 +11,56 @@
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
namespace AzToolsFramework
{
class EditorPythonEventsInterface;
}
namespace AZ
namespace AZ::SceneAPI::Events
{
namespace SceneAPI
class PreExportEventContext;
}
namespace AZ::SceneAPI::Containers
{
class Scene;
}
namespace AZ::SceneAPI::Behaviors
{
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
: public SceneCore::BehaviorComponent
, public Events::AssetImportRequestBus::Handler
{
namespace Behaviors
{
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
: public SceneCore::BehaviorComponent
, public Events::AssetImportRequestBus::Handler
{
public:
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
public:
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
~ScriptProcessorRuleBehavior() override = default;
~ScriptProcessorRuleBehavior() override = default;
SCENE_DATA_API void Activate() override;
SCENE_DATA_API void Deactivate() override;
static void Reflect(ReflectContext* context);
SCENE_DATA_API void Activate() override;
SCENE_DATA_API void Deactivate() override;
static void Reflect(ReflectContext* context);
// AssetImportRequestBus::Handler
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
Containers::Scene& scene,
ManifestAction action,
RequestingApplication requester) override;
// AssetImportRequestBus::Handler
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
Containers::Scene& scene,
ManifestAction action,
RequestingApplication requester) override;
private:
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
};
} // namespace SceneData
} // namespace SceneAPI
} // namespace AZ
protected:
bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene);
void UnloadPython();
bool DoPrepareForExport(Events::PreExportEventContext& context);
private:
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
AZStd::string m_scriptFilename;
struct ExportEventHandler;
AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;
};
} // namespace AZ::SceneAPI::Behaviors
@@ -123,6 +123,11 @@ namespace AZ
/// Returns the index of the current image after the swap.
virtual uint32_t PresentInternal() = 0;
virtual void SetVerticalSyncIntervalInternal(uint32_t previousVerticalSyncInterval)
{
AZ_UNUSED(previousVerticalSyncInterval);
}
//////////////////////////////////////////////////////////////////////////
SwapChainDescriptor m_descriptor;
@@ -168,7 +168,11 @@ namespace AZ
void SwapChain::SetVerticalSyncInterval(uint32_t verticalSyncInterval)
{
uint32_t previousVsyncInterval = m_descriptor.m_verticalSyncInterval;
m_descriptor.m_verticalSyncInterval = verticalSyncInterval;
SetVerticalSyncIntervalInternal(previousVsyncInterval);
}
const AttachmentId& SwapChain::GetAttachmentId() const
@@ -218,7 +218,8 @@ namespace AZ
createInfo.extent = extent;
createInfo.mipLevels = AZStd::min<uint32_t>(descriptor.m_mipLevels, formatProps.maxMipLevels);
createInfo.arrayLayers = AZStd::min<uint32_t>(descriptor.m_arraySize, formatProps.maxArrayLayers);
createInfo.samples = static_cast<VkSampleCountFlagBits>(RHI::FilterBits(static_cast<VkSampleCountFlags>(ConvertSampleCount(descriptor.m_multisampleState.m_samples)), formatProps.sampleCounts));
VkSampleCountFlagBits sampleCountFlagBits = static_cast<VkSampleCountFlagBits>(RHI::FilterBits(static_cast<VkSampleCountFlags>(ConvertSampleCount(descriptor.m_multisampleState.m_samples)), formatProps.sampleCounts));
createInfo.samples = (static_cast<uint32_t>(sampleCountFlagBits) > 0) ? sampleCountFlagBits : VK_SAMPLE_COUNT_1_BIT;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.usage = GetImageUsageFlags();
createInfo.sharingMode = exclusiveOwnership ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT;
@@ -56,6 +56,18 @@ namespace AZ
m_swapChainBarrier.m_isValid = true;
}
void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval)
{
uint32_t verticalSyncInterval = GetDescriptor().m_verticalSyncInterval;
if (verticalSyncInterval == 0 || previousVsyncInterval == 0)
{
// The presentation mode may change when transitioning to or from a vsynced presentation mode
// In this case, the swapchain must be recreated.
InvalidateNativeSwapChain();
BuildNativeSwapChain(GetDescriptor().m_dimensions, verticalSyncInterval);
}
}
void SwapChain::SetNameInternal(const AZStd::string_view& name)
{
if (IsInitialized() && !name.empty())
@@ -84,7 +96,7 @@ namespace AZ
auto& presentationQueue = device.GetCommandQueueContext().GetOrCreatePresentationCommandQueue(*this);
m_presentationQueue = &presentationQueue;
result = BuildNativeSwapChain(swapchainDimensions);
result = BuildNativeSwapChain(swapchainDimensions, descriptor.m_verticalSyncInterval);
RETURN_RESULT_IF_UNSUCCESSFUL(result);
uint32_t imageCount = 0;
VkResult vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &imageCount, nullptr);
@@ -166,7 +178,7 @@ namespace AZ
auto& presentationQueue = device.GetCommandQueueContext().GetOrCreatePresentationCommandQueue(*this);
m_presentationQueue = &presentationQueue;
BuildNativeSwapChain(resizeDimensions);
BuildNativeSwapChain(resizeDimensions, GetDescriptor().m_verticalSyncInterval);
resizeDimensions.m_imageCount = 0;
VkResult vkResult = vkGetSwapchainImagesKHR(device.GetNativeDevice(), m_nativeSwapChain, &resizeDimensions.m_imageCount, nullptr);
@@ -256,7 +268,8 @@ namespace AZ
info.pImageIndices = &imageIndex;
info.pResults = nullptr;
const VkResult result = vkQueuePresentKHR(vulkanQueue->GetNativeQueue(), &info);
VkResult result = vkQueuePresentKHR(vulkanQueue->GetNativeQueue(), &info);
// Resizing window cause recreation of SwapChain after calling this method,
// so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen at this point.
AZ_Assert(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR, "Failed to present swapchain %s", GetName().GetCStr());
@@ -321,9 +334,17 @@ namespace AZ
return surfaceFormats[0];
}
VkPresentModeKHR SwapChain::GetSupportedPresentMode() const
VkPresentModeKHR SwapChain::GetSupportedPresentMode(uint32_t verticalSyncInterval) const
{
AZ_Assert(m_surface, "Surface has not been initialized.");
if (verticalSyncInterval > 0)
{
// When a non-zero vsync interval is requested, the FIFO presentation mode (always available)
// is usable without needing to query available presentation modes.
return VK_PRESENT_MODE_FIFO_KHR;
}
auto& device = static_cast<Device&>(GetDevice());
const auto& physicalDevice = static_cast<const PhysicalDevice&>(device.GetPhysicalDevice());
@@ -335,12 +356,12 @@ namespace AZ
AZStd::vector<VkPresentModeKHR> supportedModes(modeCount);
AssertSuccess(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice.GetNativePhysicalDevice(), m_surface->GetNativeSurface(), &modeCount, supportedModes.data()));
VkPresentModeKHR preferedModes[] = {VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR};
for (VkPresentModeKHR preferedMode : preferedModes)
VkPresentModeKHR preferredModes[] = {VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR};
for (VkPresentModeKHR preferredMode : preferredModes)
{
for (VkPresentModeKHR supportedMode : supportedModes)
{
if (supportedMode == preferedMode)
if (supportedMode == preferredMode)
{
return supportedMode;
}
@@ -370,7 +391,7 @@ namespace AZ
return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
}
RHI::ResultCode SwapChain::BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions)
RHI::ResultCode SwapChain::BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions, uint32_t verticalSyncInterval)
{
AZ_Assert(m_nativeSwapChain == VK_NULL_HANDLE, "Vulkan's native SwapChain has been initialized already.");
auto& device = static_cast<Device&>(GetDevice());
@@ -421,7 +442,7 @@ namespace AZ
createInfo.pQueueFamilyIndices = familyIndices.empty() ? nullptr : familyIndices.data();
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = GetSupportedCompositeAlpha();
createInfo.presentMode = GetSupportedPresentMode();
createInfo.presentMode = GetSupportedPresentMode(verticalSyncInterval);
createInfo.clipped = VK_FALSE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
@@ -442,6 +463,7 @@ namespace AZ
imageAvailableSemaphore->GetNativeSemaphore(),
VK_NULL_HANDLE,
acquiredImageIndex);
// Resizing window cause recreation of SwapChain before calling this method,
// so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen.
AssertSuccess(vkResult);
@@ -49,7 +49,7 @@ namespace AZ
const CommandQueue& GetPresentationQueue() const;
void QueueBarrier(const VkPipelineStageFlags src, const VkPipelineStageFlags dst, const VkImageMemoryBarrier& imageBarrier);
private:
SwapChain() = default;
@@ -65,14 +65,15 @@ namespace AZ
RHI::ResultCode InitImageInternal(const RHI::SwapChain::InitImageRequest& request) override;
RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override;
uint32_t PresentInternal() override;
void SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval) override;
//////////////////////////////////////////////////////////////////////
RHI::ResultCode BuildSurface(const RHI::SwapChainDescriptor& descriptor);
bool ValidateSurfaceDimensions(const RHI::SwapChainDimensions& dimensions);
VkSurfaceFormatKHR GetSupportedSurfaceFormat(const RHI::Format format) const;
VkPresentModeKHR GetSupportedPresentMode() const;
VkPresentModeKHR GetSupportedPresentMode(uint32_t verticalSyncInterval) const;
VkCompositeAlphaFlagBitsKHR GetSupportedCompositeAlpha() const;
RHI::ResultCode BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions);
RHI::ResultCode BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions, uint32_t verticalSyncInterval);
RHI::ResultCode AcquireNewImage(uint32_t* acquiredImageIndex);
void InvalidateSurface();
@@ -71,6 +71,7 @@ namespace AZ
// WindowNotificationBus::Handler overrides ...
void OnWindowResized(uint32_t width, uint32_t height) override;
void OnWindowClosed() override;
void OnVsyncIntervalChanged(uint32_t interval) override;
// ExclusiveFullScreenRequestBus::Handler overrides ...
bool IsExclusiveFullScreenPreferred() const override;
@@ -116,7 +116,7 @@ namespace AZ
{
AZ_Assert(IsQueryTypeValid(queryType), "Provided QueryType is invalid");
return static_cast<uint32_t>(m_queryTypeSupport) & static_cast<uint32_t>(queryType);
return static_cast<uint32_t>(m_queryTypeSupport) & AZ_BIT(static_cast<uint32_t>(queryType));
}
RPI::QueryPool* GpuQuerySystem::GetQueryPoolByType(RHI::QueryType queryType)
@@ -13,6 +13,22 @@
#include <Atom/RHI/Factory.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Math/MathUtils.h>
void OnVsyncIntervalChanged(uint32_t const& interval)
{
AzFramework::WindowNotificationBus::Broadcast(
&AzFramework::WindowNotificationBus::Events::OnVsyncIntervalChanged,
AZ::GetClamp(interval, 0u, 4u));
}
// NOTE: On change, broadcasts the new requested vsync interval to all windows.
// The value of the vsync interval is constrained between 0 and 4
// Vsync intervals greater than 1 are not currently supported on the Vulkan RHI (see #2061 for discussion)
AZ_CVAR(uint32_t, rpi_vsync_interval, 0, OnVsyncIntervalChanged, AZ::ConsoleFunctorFlags::Null, "Set swapchain vsync interval");
namespace AZ
{
namespace RPI
@@ -103,6 +119,14 @@ namespace AZ
AzFramework::WindowNotificationBus::Handler::BusDisconnect(m_windowHandle);
}
void WindowContext::OnVsyncIntervalChanged(uint32_t interval)
{
if (m_swapChain->GetDescriptor().m_verticalSyncInterval != interval)
{
m_swapChain->SetVerticalSyncInterval(interval);
}
}
bool WindowContext::IsExclusiveFullScreenPreferred() const
{
return m_swapChain->IsExclusiveFullScreenPreferred();
@@ -135,7 +159,7 @@ namespace AZ
RHI::SwapChainDescriptor descriptor;
descriptor.m_window = windowHandle;
descriptor.m_verticalSyncInterval = 0;
descriptor.m_verticalSyncInterval = rpi_vsync_interval;
descriptor.m_dimensions.m_imageWidth = width;
descriptor.m_dimensions.m_imageHeight = height;
descriptor.m_dimensions.m_imageCount = 3;
@@ -109,7 +109,6 @@ namespace AtomToolsFramework
void BeginCursorCapture() override;
void EndCursorCapture() override;
AzFramework::ScreenPoint ViewportCursorScreenPosition() override;
AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() override;
bool IsMouseOver() const override;
// AzFramework::WindowRequestBus::Handler ...
@@ -160,8 +159,6 @@ namespace AtomToolsFramework
AZ::ScriptTimePoint m_time;
// Whether the Viewport is currently hiding and capturing the cursor position.
bool m_capturingCursor = false;
// The last known position of the mouse cursor, if one is available.
AZStd::optional<QPoint> m_lastCursorPosition;
// The viewport settings (e.g. grid snapping, grid size) for this viewport.
const AzToolsFramework::ViewportInteraction::ViewportSettings* m_viewportSettings = nullptr;
// Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList.
@@ -16,7 +16,6 @@
#include <AzCore/Math/MathUtils.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/Bootstrap/BootstrapRequestBus.h>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
#include <QApplication>
#include <QCursor>
@@ -234,18 +233,6 @@ namespace AtomToolsFramework
void RenderViewportWidget::mouseMoveEvent(QMouseEvent* event)
{
m_mousePosition = event->localPos();
if (m_capturingCursor && m_lastCursorPosition.has_value())
{
AzQtComponents::SetCursorPos(m_lastCursorPosition.value());
// Even though we just set the cursor position, there are edge cases such as remote desktop that will leave
// the cursor position unchanged. For safety, we re-cache our last cursor position for delta generation.
m_lastCursorPosition = QCursor::pos();
}
else
{
m_lastCursorPosition = event->globalPos();
}
}
void RenderViewportWidget::SendWindowResizeEvent()
@@ -420,13 +407,6 @@ namespace AtomToolsFramework
return AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(m_mousePosition.toPoint());
}
AZStd::optional<AzFramework::ScreenPoint> RenderViewportWidget::PreviousViewportCursorScreenPosition()
{
using AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint;
return m_lastCursorPosition.has_value() ? ScreenPointFromQPoint(mapFromGlobal(m_lastCursorPosition.value()))
: AZStd::optional<AzFramework::ScreenPoint>{};
}
bool RenderViewportWidget::IsMouseOver() const
{
return m_mouseOver;
@@ -434,24 +414,12 @@ namespace AtomToolsFramework
void RenderViewportWidget::BeginCursorCapture()
{
if (m_capturingCursor)
{
return;
}
qApp->setOverrideCursor(Qt::BlankCursor);
m_capturingCursor = true;
m_inputChannelMapper->SetCursorCaptureEnabled(true);
}
void RenderViewportWidget::EndCursorCapture()
{
if (!m_capturingCursor)
{
return;
}
qApp->restoreOverrideCursor();
m_capturingCursor = false;
m_inputChannelMapper->SetCursorCaptureEnabled(false);
}
void RenderViewportWidget::SetWindowTitle(const AZStd::string& title)
@@ -412,25 +412,24 @@ namespace EMotionFX
void NonUniformMotionData::UpdateDuration()
{
m_duration = 0.0f;
for (const JointData& jointData : m_jointData)
{
if (!jointData.m_positionTrack.m_times.empty())
{
m_duration = jointData.m_positionTrack.m_times.back();
return;
m_duration = AZ::GetMax(m_duration, jointData.m_positionTrack.m_times.back());
}
if (!jointData.m_rotationTrack.m_times.empty())
{
m_duration = jointData.m_rotationTrack.m_times.back();
return;
m_duration = AZ::GetMax(m_duration, jointData.m_rotationTrack.m_times.back());
}
#ifndef EMFX_SCALE_DISABLED
if (!jointData.m_scaleTrack.m_times.empty())
{
m_duration = jointData.m_scaleTrack.m_times.back();
return;
m_duration = AZ::GetMax(m_duration, jointData.m_scaleTrack.m_times.back());
}
#endif
}
@@ -439,8 +438,7 @@ namespace EMotionFX
{
if (!morphData.m_track.m_times.empty())
{
m_duration = morphData.m_track.m_times.back();
return;
m_duration = AZ::GetMax(m_duration, morphData.m_track.m_times.back());
}
}
@@ -448,12 +446,9 @@ namespace EMotionFX
{
if (!floatData.m_track.m_times.empty())
{
m_duration = floatData.m_track.m_times.back();
return;
m_duration = AZ::GetMax(m_duration, floatData.m_track.m_times.back());
}
}
m_duration = 0.0f;
}
void NonUniformMotionData::AllocateJointPositionSamples(size_t jointDataIndex, size_t numSamples)
@@ -237,8 +237,8 @@ namespace EditorPythonBindings
{
ec->Class<PythonSystemComponent>("PythonSystemComponent", "The Python interpreter")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
@@ -284,10 +284,10 @@ namespace EditorPythonBindings
ReleaseFunction m_releaseFunction;
};
ReleaseInitalizeWaiterScope scope([this]()
{
m_initalizeWaiter.release(m_initalizeWaiterCount);
m_initalizeWaiterCount = 0;
});
{
m_initalizeWaiter.release(m_initalizeWaiterCount);
m_initalizeWaiterCount = 0;
});
if (Py_IsInitialized())
{
@@ -327,6 +327,11 @@ namespace EditorPythonBindings
return result;
}
bool PythonSystemComponent::IsPythonActive()
{
return Py_IsInitialized() != 0;
}
void PythonSystemComponent::WaitForInitialization()
{
m_initalizeWaiterCount++;
@@ -44,6 +44,7 @@ namespace EditorPythonBindings
// AzToolsFramework::EditorPythonEventsInterface
bool StartPython(bool silenceWarnings = false) override;
bool StopPython(bool silenceWarnings = false) override;
bool IsPythonActive() override;
void WaitForInitialization() override;
void ExecuteWithLock(AZStd::function<void()> executionCallback) override;
////////////////////////////////////////////////////////////////////////