Merge branch 'stabilization/2110' of https://github.com/aws-lumberyard/o3de into Prism/CLIDisplayLastError

Signed-off-by: AMZN-Phil <pconroy@amazon.com>
This commit is contained in:
AMZN-Phil
2021-11-17 10:01:00 -08:00
191 changed files with 3005 additions and 1892 deletions
@@ -12,6 +12,7 @@ import pytest
import ly_test_tools.log.log_monitor
from AWS.common import constants
from AWS.common.resource_mappings import AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY
# fixture imports
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
@@ -70,6 +71,41 @@ class TestAWSClientAuthWindows(object):
halt_on_unexpected=True,
)
assert result, 'Anonymous credentials fetched successfully.'
@pytest.mark.parametrize('level', ['AWS/ClientAuth'])
def test_anonymous_credentials_no_global_accountid(self,
level: str,
launcher: pytest.fixture,
resource_mappings: pytest.fixture,
workspace: pytest.fixture,
asset_processor: pytest.fixture
):
"""
Test to verify AWS Cognito Identity pool anonymous authorization.
Setup: Updates resource mapping file using existing CloudFormation stacks.
Tests: Getting credentials when no credentials are configured
Verification: Log monitor looks for success credentials log.
"""
# Remove top-level account ID from resource mappings
resource_mappings.clear_select_keys([AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY])
asset_processor.start()
asset_processor.wait_for_idle()
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
launcher.args = ['+LoadLevel', level]
launcher.args.extend(['-rhi=null'])
with launcher.start(launch_ap=False):
result = log_monitor.monitor_log_for_lines(
expected_lines=['(Script) - Success anonymous credentials'],
unexpected_lines=['(Script) - Fail anonymous credentials'],
halt_on_unexpected=True,
)
assert result, 'Anonymous credentials fetched successfully.'
def test_password_signin_credentials(self,
launcher: pytest.fixture,
@@ -150,8 +150,7 @@ def create_basic_atom_level(level_name):
entity_position=default_position,
components=["HDRi Skybox", "Global Skylight (IBL)"],
parent_id=default_level.id)
global_skylight_asset_path = os.path.join(
"LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage")
global_skylight_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
global_skylight_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", global_skylight_asset_path, math.Uuid(), False)
global_skylight.get_set_test(0, "Controller|Configuration|Cubemap Texture", global_skylight_asset_value)
@@ -55,11 +55,13 @@ class AtomComponentProperties:
def camera(property: str = 'name') -> str:
"""
Camera component properties.
- 'Field of view': Sets the value for the camera's FOV (Field of View) in degrees, i.e. 60.0
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Camera',
'Field of view': 'Controller|Configuration|Field of view'
}
return properties[property]
@@ -198,11 +200,13 @@ class AtomComponentProperties:
def grid(property: str = 'name') -> str:
"""
Grid component properties.
- 'Secondary Grid Spacing': The spacing value for the secondary grid, i.e. 1.0
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Grid',
'Secondary Grid Spacing': 'Controller|Configuration|Secondary Grid Spacing',
}
return properties[property]
@@ -225,11 +229,13 @@ class AtomComponentProperties:
def hdri_skybox(property: str = 'name') -> str:
"""
HDRi Skybox component properties.
- 'Cubemap Texture': Asset.id for the cubemap texture to set.
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'HDRi Skybox',
'Cubemap Texture': 'Controller|Configuration|Cubemap Texture',
}
return properties[property]
@@ -268,12 +274,14 @@ class AtomComponentProperties:
Material component properties. Requires one of Actor OR Mesh component.
- 'requires' a list of component names as strings required by this component.
Only one of these is required at a time for this component.\n
- 'Material Asset': the material Asset.id of the material.
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Material',
'requires': [AtomComponentProperties.actor(), AtomComponentProperties.mesh()],
'Material Asset': 'Default Material|Material Asset',
}
return properties[property]
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:954d7d0df47c840a24e313893800eb3126d0c0d47c3380926776b51833778db7
oid sha256:aee1fd4d5264e5ef1676b507409ce70af6358cf1ff368d9aeb17f7b2597dfbca
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e81c19128f42ba362a2d5f3ccf159dfbc942d67ceeb1ac8c21f295a6fd9d2ce5
oid sha256:d4787cdafbcc2fe71c1cb3f1da53a249db839a9df539a9e88be43ccd6d8e4d6a
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e20801213e065b6ea8c95ede81c23faa9b6dc70a2002dc5bced293e1bed989f
oid sha256:5fac5bf41c9b16b6fbd762868e5cf514376af92d6ef7ebb9e819f024f1a3e1a7
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e250f812e594e5152bf2d6f23caa8b53b78276bfdf344d7a8d355dd96cb995c0
oid sha256:7a23969670499524725535e8be7428b55b6f3e887cc24e2e903f7ea821a6d1a5
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:95be359041f8291c74b335297a4dfe9902a180510f24a181b15e1a5ba4d3b024
oid sha256:2f1f4d8865c56ed7f96f339c39e5feb4e0dbc6c6a8b4a7843b4166381b06b00d
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:07e09d3eb5bf0cee3d9b3752aaad40f3ead1dcc5ddd837a6226fadde55d57274
oid sha256:5d4ee5641e19eef08dd6b93d2f4054a1aae2165325416ed2cbf0b8243f2c0b06
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:118e43e4b915e262726183467cc4b82f244565213fea5b6bfe02be07f0851ab1
oid sha256:55c8f0d1790bb12660b7557630efca297b2a1b59e6c93167a2563da79e0a8255
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc2ce3256a6552975962c9e113c52c1a22bf3817d417151f6f60640dd568e0fa
oid sha256:082ff368b621e12b083d96562a0889b11a1d683767a74296cbe6d8732830e9e8
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:287d98890b35427688999760f9d066bcbff1a3bc9001534241dc212b32edabd8
oid sha256:78cc62d89782899747875b41abee57c2efdfacf4c8af6511c88f82d76eaae4ca
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66e91c92c868167c850078cd91714db47e10a96e23cc30191994486bd79c353f
oid sha256:3d6719326f4dacae278d1723090ce1182193b793f250963af8be4b2c298e8841
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d950d173f5101820c5e18205401ca08ce5feeff2302ac2920b292750d86a8fa4
oid sha256:9e492bb394fb18fb117f8a5b61cd2789922f9d6e88fc83189b5b6d59ffb1c3ef
size 6220817
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72eddb7126eae0c839b933886e0fb69d78229f72d49ef13199de28df2b7879db
oid sha256:caca85f7728f660daae36afc81d681ba2de2377c516eb3c637599de5c94012aa
size 6220817
@@ -123,8 +123,7 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
Report.result(Tests.is_visible, global_skylight_entity.is_visible() is True)
# 8. Set the Diffuse Image asset on the Global Skylight (IBL) entity.
global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image"
diffuse_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
diffuse_image_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
diffuse_image_asset = Asset.find_asset_by_path(diffuse_image_path, False)
global_skylight_component.set_component_property_value(
global_skylight_diffuse_image_property, diffuse_image_asset.id)
@@ -133,8 +132,7 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
Report.result(Tests.diffuse_image_set, diffuse_image_set == diffuse_image_asset.id)
# 9. Set the Specular Image asset on the Global Light (IBL) entity.
global_skylight_specular_image_property = "Controller|Configuration|Specular Image"
specular_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
specular_image_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
specular_image_asset = Asset.find_asset_by_path(specular_image_path, False)
global_skylight_component.set_component_property_value(
global_skylight_specular_image_property, specular_image_asset.id)
@@ -6,30 +6,70 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# fmt: off
class Tests :
camera_component_added = ("Camera component was added", "Camera component wasn't added")
camera_fov_set = ("Camera component FOV property set", "Camera component FOV property wasn't set")
directional_light_component_added = ("Directional Light component added", "Directional Light component wasn't added")
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
global_skylight_component_added = ("Global Skylight (IBL) component added", "Global Skylight (IBL) component wasn't added")
global_skylight_diffuse_image_set = ("Global Skylight Diffuse Image property set", "Global Skylight Diffuse Image property wasn't set")
global_skylight_specular_image_set = ("Global Skylight Specular Image property set", "Global Skylight Specular Image property wasn't set")
ground_plane_material_asset_set = ("Ground Plane Material Asset was set", "Ground Plane Material Asset wasn't set")
ground_plane_material_component_added = ("Ground Plane Material component added", "Ground Plane Material component wasn't added")
ground_plane_mesh_asset_set = ("Ground Plane Mesh Asset property was set", "Ground Plane Mesh Asset property wasn't set")
hdri_skybox_component_added = ("HDRi Skybox component added", "HDRi Skybox component wasn't added")
hdri_skybox_cubemap_texture_set = ("HDRi Skybox Cubemap Texture property set", "HDRi Skybox Cubemap Texture property wasn't set")
mesh_component_added = ("Mesh component added", "Mesh component wasn't added")
no_assert_occurred = ("No asserts detected", "Asserts were detected")
no_error_occurred = ("No errors detected", "Errors were detected")
secondary_grid_spacing = ("Secondary Grid Spacing set", "Secondary Grid Spacing not set")
sphere_material_component_added = ("Sphere Material component added", "Sphere Material component wasn't added")
sphere_material_set = ("Sphere Material Asset was set", "Sphere Material Asset wasn't set")
sphere_mesh_asset_set = ("Sphere Mesh Asset was set", "Sphere Mesh Asset wasn't set")
viewport_set = ("Viewport set to correct size", "Viewport not set to correct size")
# fmt: on
class Tests:
camera_component_added = (
"Camera component was added",
"Camera component wasn't added")
camera_fov_set = (
"Camera component FOV property set",
"Camera component FOV property wasn't set")
directional_light_component_added = (
"Directional Light component added",
"Directional Light component wasn't added")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
global_skylight_component_added = (
"Global Skylight (IBL) component added",
"Global Skylight (IBL) component wasn't added")
global_skylight_diffuse_image_set = (
"Global Skylight Diffuse Image property set",
"Global Skylight Diffuse Image property wasn't set")
global_skylight_specular_image_set = (
"Global Skylight Specular Image property set",
"Global Skylight Specular Image property wasn't set")
ground_plane_material_asset_set = (
"Ground Plane Material Asset was set",
"Ground Plane Material Asset wasn't set")
ground_plane_material_component_added = (
"Ground Plane Material component added",
"Ground Plane Material component wasn't added")
ground_plane_mesh_asset_set = (
"Ground Plane Mesh Asset property was set",
"Ground Plane Mesh Asset property wasn't set")
hdri_skybox_component_added = (
"HDRi Skybox component added",
"HDRi Skybox component wasn't added")
hdri_skybox_cubemap_texture_set = (
"HDRi Skybox Cubemap Texture property set",
"HDRi Skybox Cubemap Texture property wasn't set")
mesh_component_added = (
"Mesh component added",
"Mesh component wasn't added")
no_assert_occurred = (
"No asserts detected",
"Asserts were detected")
no_error_occurred = (
"No errors detected",
"Errors were detected")
secondary_grid_spacing = (
"Secondary Grid Spacing set",
"Secondary Grid Spacing not set")
sphere_material_component_added = (
"Sphere Material component added",
"Sphere Material component wasn't added")
sphere_material_set = (
"Sphere Material Asset was set",
"Sphere Material Asset wasn't set")
sphere_mesh_asset_set = (
"Sphere Mesh Asset was set",
"Sphere Mesh Asset wasn't set")
viewport_set = (
"Viewport set to correct size",
"Viewport not set to correct size")
def AtomGPU_BasicLevelSetup_SetsUpLevel():
@@ -77,19 +117,17 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
import os
from math import isclose
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
from editor_python_test_tools.utils import Report, Tracer, TestHelper
from Atom.atom_utils.atom_constants import AtomComponentProperties
from Atom.atom_utils.screenshot_utils import ScreenshotHelper
MATERIAL_COMPONENT_NAME = "Material"
MESH_COMPONENT_NAME = "Mesh"
SCREENSHOT_NAME = "AtomBasicLevelSetup"
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
@@ -98,24 +136,24 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
def initial_viewport_setup(screen_width, screen_height):
general.set_viewport_size(screen_width, screen_height)
general.update_viewport()
result = isclose(
a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and isclose(
a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1)
return result
TestHelper.wait_for_condition(
function=lambda: isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1)
and isclose(a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1),
timeout_in_seconds=4.0
)
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
helper.init_idle()
helper.open_level("", "Base")
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Close error windows and display helpers then update the viewport size.
helper.close_error_windows()
helper.close_display_helpers()
TestHelper.close_error_windows()
TestHelper.close_display_helpers()
initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)
general.update_viewport()
Report.critical_result(Tests.viewport_set, initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT))
# 2. Create Default Level Entity.
default_level_entity_name = "Default Level"
@@ -123,168 +161,167 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
math.Vector3(0.0, 0.0, 0.0), default_level_entity_name)
# 3. Create Grid Entity as a child entity of the Default Level Entity.
grid_name = "Grid"
grid_entity = EditorEntity.create_editor_entity(grid_name, default_level_entity.id)
grid_entity = EditorEntity.create_editor_entity(AtomComponentProperties.grid(), default_level_entity.id)
# 4. Add Grid component to Grid Entity and set Secondary Grid Spacing.
grid_component = grid_entity.add_component(grid_name)
secondary_grid_spacing_property = "Controller|Configuration|Secondary Grid Spacing"
grid_component = grid_entity.add_component(AtomComponentProperties.grid())
secondary_grid_spacing_value = 1.0
grid_component.set_component_property_value(secondary_grid_spacing_property, secondary_grid_spacing_value)
grid_component.set_component_property_value(
AtomComponentProperties.grid('Secondary Grid Spacing'), secondary_grid_spacing_value)
secondary_grid_spacing_set = grid_component.get_component_property_value(
secondary_grid_spacing_property) == secondary_grid_spacing_value
AtomComponentProperties.grid('Secondary Grid Spacing')) == secondary_grid_spacing_value
Report.result(Tests.secondary_grid_spacing, secondary_grid_spacing_set)
# 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity.
global_skylight_name = "Global Skylight (IBL)"
global_skylight_entity = EditorEntity.create_editor_entity(global_skylight_name, default_level_entity.id)
global_skylight_entity = EditorEntity.create_editor_entity(
AtomComponentProperties.global_skylight(), default_level_entity.id)
# 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity.
hdri_skybox_name = "HDRi Skybox"
hdri_skybox_component = global_skylight_entity.add_component(hdri_skybox_name)
Report.result(Tests.hdri_skybox_component_added, global_skylight_entity.has_component(hdri_skybox_name))
hdri_skybox_component = global_skylight_entity.add_component(AtomComponentProperties.hdri_skybox())
Report.result(Tests.hdri_skybox_component_added, global_skylight_entity.has_component(
AtomComponentProperties.hdri_skybox()))
# 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity.
global_skylight_component = global_skylight_entity.add_component(global_skylight_name)
Report.result(Tests.global_skylight_component_added, global_skylight_entity.has_component(global_skylight_name))
global_skylight_component = global_skylight_entity.add_component(AtomComponentProperties.global_skylight())
Report.result(Tests.global_skylight_component_added, global_skylight_entity.has_component(
AtomComponentProperties.global_skylight()))
# 8. Set the Cubemap Texture property of the HDRi Skybox component.
global_skylight_image_asset_path = os.path.join(
"LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage")
global_skylight_image_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False)
hdri_skybox_cubemap_texture_property = "Controller|Configuration|Cubemap Texture"
global_skylight_image_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
global_skylight_image_asset = Asset.find_asset_by_path(global_skylight_image_asset_path, False)
hdri_skybox_component.set_component_property_value(
hdri_skybox_cubemap_texture_property, global_skylight_image_asset)
AtomComponentProperties.hdri_skybox('Cubemap Texture'), global_skylight_image_asset.id)
Report.result(
Tests.hdri_skybox_cubemap_texture_set,
hdri_skybox_component.get_component_property_value(
hdri_skybox_cubemap_texture_property) == global_skylight_image_asset)
AtomComponentProperties.hdri_skybox('Cubemap Texture')) == global_skylight_image_asset.id)
# 9. Set the Diffuse Image property of the Global Skylight (IBL) component.
# Re-use the same image that was used in the previous test step.
global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image"
global_skylight_diffuse_image_asset_path = os.path.join(
"LightingPresets", "default_iblskyboxcm_ibldiffuse.exr.streamingimage")
global_skylight_diffuse_image_asset = Asset.find_asset_by_path(global_skylight_diffuse_image_asset_path, False)
global_skylight_component.set_component_property_value(
global_skylight_diffuse_image_property, global_skylight_image_asset)
AtomComponentProperties.global_skylight('Diffuse Image'), global_skylight_diffuse_image_asset.id)
Report.result(
Tests.global_skylight_diffuse_image_set,
global_skylight_component.get_component_property_value(
global_skylight_diffuse_image_property) == global_skylight_image_asset)
AtomComponentProperties.global_skylight('Diffuse Image')) == global_skylight_diffuse_image_asset.id)
# 10. Set the Specular Image property of the Global Skylight (IBL) component.
# Re-use the same image that was used in the previous test step.
global_skylight_specular_image_property = "Controller|Configuration|Specular Image"
global_skylight_specular_image_asset_path = os.path.join(
"LightingPresets", "default_iblskyboxcm_iblspecular.exr.streamingimage")
global_skylight_specular_image_asset = Asset.find_asset_by_path(
global_skylight_specular_image_asset_path, False)
global_skylight_component.set_component_property_value(
global_skylight_specular_image_property, global_skylight_image_asset)
AtomComponentProperties.global_skylight('Specular Image'), global_skylight_specular_image_asset.id)
global_skylight_specular_image_set = global_skylight_component.get_component_property_value(
global_skylight_specular_image_property)
AtomComponentProperties.global_skylight('Specular Image'))
Report.result(
Tests.global_skylight_specular_image_set, global_skylight_specular_image_set == global_skylight_image_asset)
Tests.global_skylight_specular_image_set,
global_skylight_specular_image_set == global_skylight_specular_image_asset.id)
# 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity.
ground_plane_name = "Ground Plane"
ground_plane_entity = EditorEntity.create_editor_entity(ground_plane_name, default_level_entity.id)
ground_plane_material_component = ground_plane_entity.add_component(MATERIAL_COMPONENT_NAME)
ground_plane_material_component = ground_plane_entity.add_component(AtomComponentProperties.material())
Report.result(
Tests.ground_plane_material_component_added, ground_plane_entity.has_component(MATERIAL_COMPONENT_NAME))
Tests.ground_plane_material_component_added,
ground_plane_entity.has_component(AtomComponentProperties.material()))
# 12. Set the Material Asset property of the Material component for the Ground Plane Entity.
ground_plane_entity.set_local_uniform_scale(32.0)
ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
ground_plane_material_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
ground_plane_material_asset_property = "Default Material|Material Asset"
ground_plane_material_asset = Asset.find_asset_by_path(ground_plane_material_asset_path, False)
ground_plane_material_component.set_component_property_value(
ground_plane_material_asset_property, ground_plane_material_asset)
AtomComponentProperties.material('Material Asset'), ground_plane_material_asset.id)
Report.result(
Tests.ground_plane_material_asset_set,
ground_plane_material_component.get_component_property_value(
ground_plane_material_asset_property) == ground_plane_material_asset)
AtomComponentProperties.material('Material Asset')) == ground_plane_material_asset.id)
# 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property.
ground_plane_mesh_component = ground_plane_entity.add_component(MESH_COMPONENT_NAME)
Report.result(Tests.mesh_component_added, ground_plane_entity.has_component(MESH_COMPONENT_NAME))
ground_plane_mesh_asset_path = os.path.join("Objects", "plane.azmodel")
ground_plane_mesh_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
ground_plane_mesh_asset_property = "Controller|Configuration|Mesh Asset"
ground_plane_mesh_component = ground_plane_entity.add_component(AtomComponentProperties.mesh())
Report.result(Tests.mesh_component_added, ground_plane_entity.has_component(AtomComponentProperties.mesh()))
ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
ground_plane_mesh_asset = Asset.find_asset_by_path(ground_plane_mesh_asset_path, False)
ground_plane_mesh_component.set_component_property_value(
ground_plane_mesh_asset_property, ground_plane_mesh_asset)
AtomComponentProperties.mesh('Mesh Asset'), ground_plane_mesh_asset.id)
Report.result(
Tests.ground_plane_mesh_asset_set,
ground_plane_mesh_component.get_component_property_value(
ground_plane_mesh_asset_property) == ground_plane_mesh_asset)
AtomComponentProperties.mesh('Mesh Asset')) == ground_plane_mesh_asset.id)
# 14. Create a Directional Light Entity as a child entity of the Default Level Entity.
directional_light_name = "Directional Light"
directional_light_entity = EditorEntity.create_editor_entity_at(
math.Vector3(0.0, 0.0, 10.0), directional_light_name, default_level_entity.id)
math.Vector3(0.0, 0.0, 10.0), AtomComponentProperties.directional_light(), default_level_entity.id)
# 15. Add Directional Light component to Directional Light Entity and set entity rotation.
directional_light_entity.add_component(directional_light_name)
directional_light_entity.add_component(AtomComponentProperties.directional_light())
directional_light_entity_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0)
directional_light_entity.set_local_rotation(directional_light_entity_rotation)
Report.result(
Tests.directional_light_component_added, directional_light_entity.has_component(directional_light_name))
Tests.directional_light_component_added, directional_light_entity.has_component(
AtomComponentProperties.directional_light()))
# 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component.
sphere_entity = EditorEntity.create_editor_entity_at(
math.Vector3(0.0, 0.0, 1.0), "Sphere", default_level_entity.id)
sphere_material_component = sphere_entity.add_component(MATERIAL_COMPONENT_NAME)
Report.result(Tests.sphere_material_component_added, sphere_entity.has_component(MATERIAL_COMPONENT_NAME))
sphere_material_component = sphere_entity.add_component(AtomComponentProperties.material())
Report.result(Tests.sphere_material_component_added, sphere_entity.has_component(
AtomComponentProperties.material()))
# 17. Set the Material Asset property of the Material component for the Sphere Entity.
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
sphere_material_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
sphere_material_asset_property = "Default Material|Material Asset"
sphere_material_component.set_component_property_value(sphere_material_asset_property, sphere_material_asset)
sphere_material_asset = Asset.find_asset_by_path(sphere_material_asset_path, False)
sphere_material_component.set_component_property_value(
AtomComponentProperties.material('Material Asset'), sphere_material_asset.id)
Report.result(Tests.sphere_material_set, sphere_material_component.get_component_property_value(
sphere_material_asset_property) == sphere_material_asset)
AtomComponentProperties.material('Material Asset')) == sphere_material_asset.id)
# 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component.
sphere_mesh_component = sphere_entity.add_component(MESH_COMPONENT_NAME)
sphere_mesh_component = sphere_entity.add_component(AtomComponentProperties.mesh())
sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel")
sphere_mesh_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
sphere_mesh_asset_property = "Controller|Configuration|Mesh Asset"
sphere_mesh_component.set_component_property_value(sphere_mesh_asset_property, sphere_mesh_asset)
sphere_mesh_asset = Asset.find_asset_by_path(sphere_mesh_asset_path, False)
sphere_mesh_component.set_component_property_value(
AtomComponentProperties.mesh('Mesh Asset'), sphere_mesh_asset.id)
Report.result(Tests.sphere_mesh_asset_set, sphere_mesh_component.get_component_property_value(
sphere_mesh_asset_property) == sphere_mesh_asset)
AtomComponentProperties.mesh('Mesh Asset')) == sphere_mesh_asset.id)
# 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component.
camera_name = "Camera"
camera_entity = EditorEntity.create_editor_entity_at(
math.Vector3(5.5, -12.0, 9.0), camera_name, default_level_entity.id)
camera_component = camera_entity.add_component(camera_name)
Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name))
math.Vector3(5.5, -12.0, 9.0), AtomComponentProperties.camera(), default_level_entity.id)
camera_component = camera_entity.add_component(AtomComponentProperties.camera())
Report.result(Tests.camera_component_added, camera_entity.has_component(AtomComponentProperties.camera()))
# 20. Set the Camera Entity rotation value and set the Camera component Field of View value.
camera_entity_rotation = math.Vector3(
DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0)
camera_entity.set_local_rotation(camera_entity_rotation)
camera_fov_property = "Controller|Configuration|Field of view"
camera_fov_value = 60.0
camera_component.set_component_property_value(camera_fov_property, camera_fov_value)
camera_component.set_component_property_value(AtomComponentProperties.camera('Field of view'), camera_fov_value)
azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
Report.result(Tests.camera_fov_set, camera_component.get_component_property_value(
camera_fov_property) == camera_fov_value)
AtomComponentProperties.camera('Field of view')) == camera_fov_value)
# 21. Enter game mode.
helper.enter_game_mode(Tests.enter_game_mode)
helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0)
TestHelper.enter_game_mode(Tests.enter_game_mode)
TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0)
# 22. Take screenshot.
ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{SCREENSHOT_NAME}.ppm")
# 23. Exit game mode.
helper.exit_game_mode(Tests.exit_game_mode)
helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0)
TestHelper.exit_game_mode(Tests.exit_game_mode)
TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0)
# 24. Look for errors.
helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
Report.result(Tests.no_assert_occurred, not error_tracer.has_asserts)
Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
@@ -131,8 +131,7 @@ def run():
components=["HDRi Skybox", "Global Skylight (IBL)"],
parent_id=default_level.id
)
global_skylight_image_asset_path = os.path.join(
"LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage")
global_skylight_image_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
global_skylight_image_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False)
global_skylight.get_set_test(0, "Controller|Configuration|Cubemap Texture", global_skylight_image_asset)
@@ -57,6 +57,10 @@ def add_level_component(component_name):
level_component_list, entity.EntityType().Level)
level_component_outcome = editor.EditorLevelComponentAPIBus(bus.Broadcast, 'AddComponentsOfType',
[level_component_type_ids_list[0]])
if not level_component_outcome.IsSuccess():
print('Failed to add {} level component'.format(component_name))
return None
level_component = level_component_outcome.GetValue()[0]
return level_component
-7
View File
@@ -547,13 +547,6 @@ QPoint Q2DViewport::WorldToView(const Vec3& wp) const
QPoint p = QPoint(static_cast<int>(sp.x), static_cast<int>(sp.y));
return p;
}
//////////////////////////////////////////////////////////////////////////
QPoint Q2DViewport::WorldToViewParticleEditor(const Vec3& wp, [[maybe_unused]] int width, [[maybe_unused]] int height) const //Eric@conffx implement for the children class of IDisplayViewport
{
Vec3 sp = m_screenTM.TransformPoint(wp);
QPoint p = QPoint(static_cast<int>(sp.x), static_cast<int>(sp.y));
return p;
}
//////////////////////////////////////////////////////////////////////////
Vec3 Q2DViewport::ViewToWorld(const QPoint& vp, [[maybe_unused]] bool* collideWithTerrain, [[maybe_unused]] bool onlyTerrain, [[maybe_unused]] bool bSkipVegetation, [[maybe_unused]] bool bTestRenderMesh, [[maybe_unused]] bool* collideWithObject) const
-2
View File
@@ -50,8 +50,6 @@ public:
//! Map world space position to viewport position.
QPoint WorldToView(const Vec3& wp) const override;
QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override; //Eric@conffx
//! Map viewport position to world space position.
Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override;
//! Map viewport position to world space ray from camera.
@@ -145,6 +145,15 @@ namespace SandboxEditor
}
};
const auto trackingTransform = [viewportId = m_viewportId]
{
bool tracking = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
tracking, viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
return tracking;
};
m_firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraFreeLookChannelId());
m_firstPersonRotateCamera->m_rotateSpeedFn = []
@@ -152,6 +161,11 @@ namespace SandboxEditor
return SandboxEditor::CameraRotateSpeed();
};
m_firstPersonRotateCamera->m_constrainPitch = [trackingTransform]
{
return !trackingTransform();
};
// default behavior is to hide the cursor but this can be disabled (useful for remote desktop)
// note: See CaptureCursorLook in the Settings Registry
m_firstPersonRotateCamera->SetActivationBeganFn(hideCursor);
@@ -255,6 +269,11 @@ namespace SandboxEditor
return SandboxEditor::CameraOrbitYawRotationInverted();
};
m_orbitRotateCamera->m_constrainPitch = [trackingTransform]
{
return !trackingTransform();
};
m_orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffsetOrbit);
@@ -337,12 +356,12 @@ namespace SandboxEditor
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, worldFromLocal);
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, worldFromLocal);
}
else
{
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform);
}
}
+24 -80
View File
@@ -299,13 +299,9 @@ AzToolsFramework::ViewportInteraction::MousePick EditorViewportWidget::BuildMous
{
AzToolsFramework::ViewportInteraction::MousePick mousePick;
mousePick.m_screenCoordinates = AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(point);
if (const auto& ray = m_renderViewport->ViewportScreenToWorldRay(mousePick.m_screenCoordinates);
ray.has_value())
{
mousePick.m_rayOrigin = ray.value().origin;
mousePick.m_rayDirection = ray.value().direction;
}
const auto[origin, direction] = m_renderViewport->ViewportScreenToWorldRay(mousePick.m_screenCoordinates);
mousePick.m_rayOrigin = origin;
mousePick.m_rayDirection = direction;
return mousePick;
}
@@ -912,23 +908,6 @@ AZ::Vector3 EditorViewportWidget::PickTerrain(const AzFramework::ScreenPoint& po
return LYVec3ToAZVec3(ViewToWorld(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), nullptr, true));
}
AZ::EntityId EditorViewportWidget::PickEntity(const AzFramework::ScreenPoint& point)
{
AZ::EntityId entityId;
HitContext hitInfo;
hitInfo.view = this;
if (HitTest(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), hitInfo))
{
if (hitInfo.object && (hitInfo.object->GetType() == OBJTYPE_AZENTITY))
{
auto entityObject = static_cast<CComponentEntityObject*>(hitInfo.object);
entityId = entityObject->GetAssociatedEntityId();
}
}
return entityId;
}
float EditorViewportWidget::TerrainHeight(const AZ::Vector2& position)
{
return GetIEditor()->GetTerrainElevation(position.GetX(), position.GetY());
@@ -1653,16 +1632,15 @@ void EditorViewportWidget::RenderSelectedRegion()
Vec3 EditorViewportWidget::WorldToView3D(const Vec3& wp, [[maybe_unused]] int nFlags) const
{
Vec3 out(0, 0, 0);
float x, y, z;
float x, y;
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y, &z);
if (_finite(x) && _finite(y) && _finite(z))
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y);
if (_finite(x) && _finite(y))
{
out.x = (x / 100) * m_rcClient.width();
out.y = (y / 100) * m_rcClient.height();
out.x /= static_cast<float>(QHighDpiScaling::factor(windowHandle()->screen()));
out.y /= static_cast<float>(QHighDpiScaling::factor(windowHandle()->screen()));
out.z = z;
}
return out;
}
@@ -1672,24 +1650,6 @@ QPoint EditorViewportWidget::WorldToView(const Vec3& wp) const
{
return AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(m_renderViewport->ViewportWorldToScreen(LYVec3ToAZVec3(wp)));
}
//////////////////////////////////////////////////////////////////////////
QPoint EditorViewportWidget::WorldToViewParticleEditor(const Vec3& wp, int width, int height) const
{
QPoint p;
float x, y, z;
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y, &z);
if (_finite(x) || _finite(y))
{
p.rx() = static_cast<int>((x / 100) * width);
p.ry() = static_cast<int>((y / 100) * height);
}
else
{
QPoint(0, 0);
}
return p;
}
//////////////////////////////////////////////////////////////////////////
Vec3 EditorViewportWidget::ViewToWorld(
@@ -1705,20 +1665,16 @@ Vec3 EditorViewportWidget::ViewToWorld(
AZ_UNUSED(collideWithObject);
auto ray = m_renderViewport->ViewportScreenToWorldRay(AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(vp));
if (!ray.has_value())
{
return Vec3(0, 0, 0);
}
const float maxDistance = 10000.f;
Vec3 v = AZVec3ToLYVec3(ray.value().direction) * maxDistance;
Vec3 v = AZVec3ToLYVec3(ray.direction) * maxDistance;
if (!_finite(v.x) || !_finite(v.y) || !_finite(v.z))
{
return Vec3(0, 0, 0);
}
Vec3 colp = AZVec3ToLYVec3(ray.value().origin) + 0.002f * v;
Vec3 colp = AZVec3ToLYVec3(ray.origin) + 0.002f * v;
return colp;
}
@@ -1757,21 +1713,19 @@ bool EditorViewportWidget::RayRenderMeshIntersection(IRenderMesh* pRenderMesh, c
return bRes;*/
}
void EditorViewportWidget::UnProjectFromScreen(float sx, float sy, float sz, float* px, float* py, float* pz) const
void EditorViewportWidget::UnProjectFromScreen(float sx, float sy, float* px, float* py, float* pz) const
{
AZ::Vector3 wp;
wp = m_renderViewport->ViewportScreenToWorld(AzFramework::ScreenPoint{(int)sx, m_rcClient.bottom() - ((int)sy)}, sz).value_or(wp);
const AZ::Vector3 wp = m_renderViewport->ViewportScreenToWorld(AzFramework::ScreenPoint{(int)sx, m_rcClient.bottom() - ((int)sy)});
*px = wp.GetX();
*py = wp.GetY();
*pz = wp.GetZ();
}
void EditorViewportWidget::ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy, float* sz) const
void EditorViewportWidget::ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy) const
{
AzFramework::ScreenPoint screenPosition = m_renderViewport->ViewportWorldToScreen(AZ::Vector3{ptx, pty, ptz});
*sx = static_cast<float>(screenPosition.m_x);
*sy = static_cast<float>(screenPosition.m_y);
*sz = 0.f;
}
//////////////////////////////////////////////////////////////////////////
@@ -1781,32 +1735,22 @@ void EditorViewportWidget::ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3&
Vec3 pos0, pos1;
float wx, wy, wz;
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), 0.0f, &wx, &wy, &wz);
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
{
return;
}
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
{
return;
}
pos0(wx, wy, wz);
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), 1.0f, &wx, &wy, &wz);
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
{
return;
}
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
{
return;
}
pos1(wx, wy, wz);
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), &wx, &wy, &wz);
Vec3 v = (pos1 - pos0);
v = v.GetNormalized();
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
{
return;
}
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
{
return;
}
pos0(wx, wy, wz);
raySrc = pos0;
rayDir = v;
rayDir = (pos0 - AZVec3ToLYVec3(m_renderViewport->GetCameraState().m_position)).GetNormalized();
}
//////////////////////////////////////////////////////////////////////////
+2 -4
View File
@@ -166,7 +166,6 @@ private:
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, const QPoint& point) override;
void SetViewportId(int id) override;
QPoint WorldToView(const Vec3& wp) const override;
QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override;
Vec3 WorldToView3D(const Vec3& wp, int nFlags = 0) const override;
Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override;
void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const override;
@@ -208,7 +207,6 @@ private:
void* GetSystemCursorConstraintWindow() const override;
// AzToolsFramework::MainEditorViewportInteractionRequestBus overrides ...
AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) override;
AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) override;
float TerrainHeight(const AZ::Vector2& position) override;
bool ShowingWorldSpace() override;
@@ -306,8 +304,8 @@ private:
const DisplayContext& GetDisplayContext() const { return m_displayContext; }
CBaseObject* GetCameraObject() const;
void UnProjectFromScreen(float sx, float sy, float sz, float* px, float* py, float* pz) const;
void ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy, float* sz) const;
void UnProjectFromScreen(float sx, float sy, float* px, float* py, float* pz) const;
void ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy) const;
AZ::RPI::ViewPtr GetCurrentAtomView() const;
-1
View File
@@ -47,7 +47,6 @@ struct IDisplayViewport
virtual const Matrix34& GetViewTM() const = 0;
virtual const Matrix34& GetScreenTM() const = 0;
virtual QPoint WorldToView(const Vec3& worldPoint) const = 0;
virtual QPoint WorldToViewParticleEditor(const Vec3& worldPoint, int width, int height) const = 0;
virtual Vec3 WorldToView3D(const Vec3& worldPoint, int flags = 0) const = 0;
virtual Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const = 0;
virtual void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const = 0;
@@ -38,7 +38,9 @@ namespace UnitTest
AzFramework::ViewportControllerListPtr m_controllerList;
AZStd::unique_ptr<AZ::Entity> m_entity;
static const AzFramework::ViewportId TestViewportId;
static inline constexpr AzFramework::ViewportId TestViewportId = 2345;
static inline constexpr float HalfInterpolateToTransformDuration =
AtomToolsFramework::ModularViewportCameraControllerRequests::InterpolateToTransformDuration * 0.5f;
void SetUp() override
{
@@ -77,8 +79,6 @@ namespace UnitTest
}
};
const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337);
TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged)
{
// Given
@@ -92,8 +92,8 @@ namespace UnitTest
&Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId());
// ensure the viewport updates after the viewport view entity change
const float deltaTime = 1.0f / 60.0f;
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// note: do a large step to ensure smoothing finishes (e.g. not 1.0f/60.0f)
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(2.0f), AZ::ScriptTimePoint() });
// retrieve updated camera transform
const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform();
@@ -103,61 +103,40 @@ namespace UnitTest
EXPECT_THAT(cameraTransform, IsClose(entityTransform));
}
TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet)
TEST_F(EditorCameraFixture, TrackingTransformIsTrueAfterTransformIsTracked)
{
// Given
m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)));
// Given/When
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
// When
AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
bool trackingTransform = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
// Then
// reference frame is still the identity
EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity()));
EXPECT_THAT(trackingTransform, ::testing::IsTrue());
}
TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame)
TEST_F(EditorCameraFixture, TrackingTransformIsFalseAfterTransformIsStoppedBeingTracked)
{
// Given
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f));
m_cameraViewportContextView->SetCameraTransform(nextTransform);
// When
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
currentReferenceFrame, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
// Then
EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform));
}
TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear)
{
// Given
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
// When
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
currentReferenceFrame, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform);
// Then
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
bool trackingTransform = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
EXPECT_THAT(trackingTransform, ::testing::IsFalse());
}
TEST_F(EditorCameraFixture, InterpolateToTransform)
@@ -170,8 +149,10 @@ namespace UnitTest
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
@@ -185,7 +166,7 @@ namespace UnitTest
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
@@ -196,19 +177,86 @@ namespace UnitTest
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
currentReferenceFrame, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
// Then
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
}
TEST_F(EditorCameraFixture, BeginningCameraInterpolationReturnsTrue)
{
// Given/When
bool interpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
interpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
// Then
EXPECT_THAT(interpolationBegan, ::testing::IsTrue());
}
TEST_F(EditorCameraFixture, CameraInterpolationDoesNotBeginDuringAnExistingInterpolation)
{
// Given/When
bool initialInterpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
initialInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
bool nextInterpolationBegan = true;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
nextInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
bool interpolating = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
interpolating, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsInterpolating);
// Then
EXPECT_THAT(initialInterpolationBegan, ::testing::IsTrue());
EXPECT_THAT(nextInterpolationBegan, ::testing::IsFalse());
EXPECT_THAT(interpolating, ::testing::IsTrue());
}
TEST_F(EditorCameraFixture, CameraInterpolationCanBeginAfterAnInterpolationCompletes)
{
// Given/When
bool initialInterpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
initialInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
m_controllerList->UpdateViewport(
{ TestViewportId,
AzFramework::FloatSeconds(AtomToolsFramework::ModularViewportCameraControllerRequests::InterpolateToTransformDuration + 0.5f),
AZ::ScriptTimePoint() });
bool interpolating = true;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
interpolating, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsInterpolating);
bool nextInterpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
nextInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
// Then
EXPECT_THAT(initialInterpolationBegan, ::testing::IsTrue());
EXPECT_THAT(interpolating, ::testing::IsFalse());
EXPECT_THAT(nextInterpolationBegan, ::testing::IsTrue());
}
} // namespace UnitTest
@@ -74,7 +74,7 @@ namespace UnitTest
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
{
public:
static const AzFramework::ViewportId TestViewportId;
static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
void SetUp() override
{
@@ -146,6 +146,17 @@ namespace UnitTest
controller->SetCameraPropsBuilderCallback(
[](AzFramework::CameraProps& cameraProps)
{
// note: rotateSmoothness is also used for roll (not related to camera input directly)
cameraProps.m_rotateSmoothnessFn = []
{
return 5.0f;
};
cameraProps.m_translateSmoothnessFn = []
{
return 5.0f;
};
cameraProps.m_rotateSmoothingEnabledFn = []
{
return false;
@@ -209,8 +220,6 @@ namespace UnitTest
AZStd::unique_ptr<SandboxEditor::EditorModularViewportCameraComposer> m_editorModularViewportCameraComposer;
};
const AzFramework::ViewportId ModularViewportCameraControllerFixture::TestViewportId = AzFramework::ViewportId(0);
TEST_F(ModularViewportCameraControllerFixture, MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithVaryingDeltaTime)
{
SandboxEditor::SetCameraCaptureCursorForLook(false);
@@ -1675,6 +1675,12 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
if (auto viewportContext = viewportContextManager->GetViewportContextById(viewIndex))
{
const AZ::Transform cameraTransform = viewportContext->GetCameraTransform();
// do not attempt to interpolate to where we currently are
if (cameraTransform.GetTranslation().IsClose(center))
{
continue;
}
const AZ::Vector3 forward = (center - cameraTransform.GetTranslation()).GetNormalized();
// move camera 25% further back than required
+21 -16
View File
@@ -8,13 +8,15 @@
#include "ViewportManipulatorController.h"
#include <AzCore/Script/ScriptTimePoint.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/Script/ScriptTimePoint.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <QApplication>
@@ -87,8 +89,14 @@ namespace SandboxEditor
}
using InteractionBus = AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
using namespace AzToolsFramework::ViewportInteraction;
using AzFramework::InputChannel;
using AzToolsFramework::ViewportInteraction::KeyboardModifier;
using AzToolsFramework::ViewportInteraction::MouseButton;
using AzToolsFramework::ViewportInteraction::MouseEvent;
using AzToolsFramework::ViewportInteraction::MouseInteraction;
using AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
using AzToolsFramework::ViewportInteraction::ProjectedViewportRay;
using AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus;
bool interactionHandled = false;
float wheelDelta = 0.0f;
@@ -117,16 +125,13 @@ namespace SandboxEditor
aznumeric_cast<int>(position->m_normalizedPosition.GetX() * windowSize.m_width),
aznumeric_cast<int>(position->m_normalizedPosition.GetY() * windowSize.m_height));
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
AZStd::optional<ProjectedViewportRay> ray;
ProjectedViewportRay ray{};
ViewportInteractionRequestBus::EventResult(
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
if (ray.has_value())
{
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.value().origin;
m_mouseInteraction.m_mousePick.m_rayDirection = ray.value().direction;
}
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.origin;
m_mouseInteraction.m_mousePick.m_rayDirection = ray.direction;
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
}
eventType = MouseEvent::Move;
@@ -160,8 +165,8 @@ namespace SandboxEditor
else if (state == InputChannel::State::Ended)
{
// If we've actually logged a mouse down event, forward a mouse up event.
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this viewport,
// due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this
// viewport, due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
if (m_mouseInteraction.m_mouseButtons.m_mouseButtons & mouseButtonValue)
{
// Erase the button from our state if we're done processing events.
@@ -259,4 +264,4 @@ namespace SandboxEditor
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
return (m_curTime.GetMilliseconds() - clickIt->second.GetMilliseconds()) < doubleClickThresholdMilliseconds;
}
} //namespace SandboxEditor
} // namespace SandboxEditor
@@ -94,27 +94,27 @@ namespace AzFramework
float y;
float z;
// 2.4 Factor as RzRyRx
if (orientation.GetElement(2, 0) < 1.0f)
// 2.5 Factor as RzRxRy
if (orientation.GetElement(2, 1) < 1.0f)
{
if (orientation.GetElement(2, 0) > -1.0f)
if (orientation.GetElement(2, 1) > -1.0f)
{
x = AZStd::atan2(orientation.GetElement(2, 1), orientation.GetElement(2, 2));
y = AZStd::asin(-orientation.GetElement(2, 0));
z = AZStd::atan2(orientation.GetElement(1, 0), orientation.GetElement(0, 0));
x = AZStd::asin(orientation.GetElement(2, 1));
y = AZStd::atan2(-orientation.GetElement(2, 0), orientation.GetElement(2, 2));
z = AZStd::atan2(-orientation.GetElement(0, 1), orientation.GetElement(1, 1));
}
else
{
x = 0.0f;
y = AZ::Constants::Pi * 0.5f;
z = -AZStd::atan2(-orientation.GetElement(2, 1), orientation.GetElement(1, 1));
x = -AZ::Constants::Pi * 0.5f;
y = 0.0f;
z = -AZStd::atan2(orientation.GetElement(0, 2), orientation.GetElement(0, 0));
}
}
else
{
x = 0.0f;
y = -AZ::Constants::Pi * 0.5f;
z = AZStd::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1));
x = AZ::Constants::Pi * 0.5f;
y = 0.0f;
z = AZStd::atan2(orientation.GetElement(0, 2), orientation.GetElement(0, 0));
}
return { x, y, z };
@@ -122,14 +122,36 @@ namespace AzFramework
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform)
{
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform));
UpdateCameraFromTranslationAndRotation(
camera, transform.GetTranslation(), AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform)));
}
void UpdateCameraFromTranslationAndRotation(Camera& camera, const AZ::Vector3& translation, const AZ::Vector3& eulerAngles)
{
camera.m_pitch = eulerAngles.GetX();
camera.m_yaw = eulerAngles.GetZ();
camera.m_pivot = transform.GetTranslation();
camera.m_pivot = translation;
camera.m_offset = AZ::Vector3::CreateZero();
}
float SmoothValueTime(const float smoothness, float deltaTime)
{
// note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent
// article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php
const float rate = AZStd::exp2(smoothness);
return AZStd::exp2(-rate * deltaTime);
}
float SmoothValue(const float target, const float current, const float time)
{
return AZ::Lerp(target, current, time);
}
float SmoothValue(const float target, const float current, const float smoothness, const float deltaTime)
{
return SmoothValue(target, current, SmoothValueTime(smoothness, deltaTime));
}
bool CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor = AZStd::get_if<CursorEvent>(&event))
@@ -291,6 +313,11 @@ namespace AzFramework
{
return false;
};
m_constrainPitch = []() constexpr
{
return true;
};
}
bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
@@ -312,7 +339,10 @@ namespace AzFramework
nextCamera.m_yaw -= float(cursorDelta.m_x) * rotateSpeed * Invert(m_invertYawFn());
nextCamera.m_yaw = WrapYawRotation(nextCamera.m_yaw);
nextCamera.m_pitch = ClampPitchRotation(nextCamera.m_pitch);
if (m_constrainPitch())
{
nextCamera.m_pitch = ClampPitchRotation(nextCamera.m_pitch);
}
return nextCamera;
}
@@ -726,14 +756,14 @@ namespace AzFramework
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const CameraProps& cameraProps, const float deltaTime)
{
const auto clamp_rotation = [](const float angle)
const auto clampRotation = [](const float angle)
{
return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
};
// keep yaw in 0 - 360 range
float targetYaw = clamp_rotation(targetCamera.m_yaw);
const float currentYaw = clamp_rotation(currentCamera.m_yaw);
float targetYaw = clampRotation(targetCamera.m_yaw);
const float currentYaw = clampRotation(currentCamera.m_yaw);
// return the sign of the float input (-1, 0, 1)
const auto sign = [](const float value)
@@ -742,21 +772,17 @@ namespace AzFramework
};
// ensure smooth transition when moving across 0 - 360 boundary
const float yawDelta = targetYaw - currentYaw;
if (AZStd::abs(yawDelta) >= AZ::Constants::Pi)
if (const float yawDelta = targetYaw - currentYaw; AZStd::abs(yawDelta) >= AZ::Constants::Pi)
{
targetYaw -= AZ::Constants::TwoPi * sign(yawDelta);
}
Camera camera;
// note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent
// article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php
if (cameraProps.m_rotateSmoothingEnabledFn())
{
const float lookRate = AZStd::exp2(cameraProps.m_rotateSmoothnessFn());
const float lookTime = AZStd::exp2(-lookRate * deltaTime);
camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookTime);
camera.m_yaw = AZ::Lerp(targetYaw, currentYaw, lookTime);
const float lookTime = SmoothValueTime(cameraProps.m_rotateSmoothnessFn(), deltaTime);
camera.m_pitch = SmoothValue(targetCamera.m_pitch, currentCamera.m_pitch, lookTime);
camera.m_yaw = SmoothValue(targetYaw, currentYaw, lookTime);
}
else
{
@@ -766,8 +792,7 @@ namespace AzFramework
if (cameraProps.m_translateSmoothingEnabledFn())
{
const float moveRate = AZStd::exp2(cameraProps.m_translateSmoothnessFn());
const float moveTime = AZStd::exp2(-moveRate * deltaTime);
const float moveTime = SmoothValueTime(cameraProps.m_rotateSmoothnessFn(), deltaTime);
camera.m_pivot = targetCamera.m_pivot.Lerp(currentCamera.m_pivot, moveTime);
camera.m_offset = targetCamera.m_offset.Lerp(currentCamera.m_offset, moveTime);
}
@@ -85,6 +85,19 @@ namespace AzFramework
//! Extracts Euler angles (orientation) and translation from the transform and writes the values to the camera.
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform);
//! Writes the translation value and Euler angles to the camera.
void UpdateCameraFromTranslationAndRotation(Camera& camera, const AZ::Vector3& translation, const AZ::Vector3& eulerAngles);
//! Returns the time ('t') input value to use with SmoothValue.
//! Useful if it is to be reused for multiple calls to SmoothValue.
float SmoothValueTime(float smoothness, float deltaTime);
// Smoothly interpolate a value from current to target according to a smoothing parameter.
float SmoothValue(float target, float current, float smoothness, float deltaTime);
// Overload of SmoothValue that takes time ('t') value directly.
float SmoothValue(float target, float current, float time);
//! Generic motion type.
template<typename MotionTag>
struct MotionEvent
@@ -334,6 +347,7 @@ namespace AzFramework
AZStd::function<float()> m_rotateSpeedFn;
AZStd::function<bool()> m_invertPitchFn;
AZStd::function<bool()> m_invertYawFn;
AZStd::function<bool()> m_constrainPitch;
private:
InputChannelId m_rotateChannelId; //!< Input channel to begin the rotate camera input.
@@ -8,18 +8,18 @@
#include "CameraState.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzFramework
{
void SetCameraClippingVolume(
AzFramework::CameraState& cameraState, const float nearPlane, const float farPlane, const float fovRad)
AzFramework::CameraState& cameraState, const float nearPlane, const float farPlane, const float verticalFovRad)
{
cameraState.m_nearClip = nearPlane;
cameraState.m_farClip = farPlane;
cameraState.m_fovOrZoom = fovRad;
cameraState.m_fovOrZoom = verticalFovRad;
}
void SetCameraTransform(CameraState& cameraState, const AZ::Transform& transform)
@@ -35,20 +35,34 @@ namespace AzFramework
SetCameraClippingVolume(cameraState, 0.1f, 1000.0f, AZ::DegToRad(60.0f));
}
AzFramework::CameraState CreateDefaultCamera(
const AZ::Transform& transform, const AZ::Vector2& viewportSize)
CameraState CreateCamera(
const AZ::Transform& transform,
const float nearPlane,
const float farPlane,
const float verticalFovRad,
const AZ::Vector2& viewportSize)
{
AzFramework::CameraState cameraState;
SetDefaultCameraClippingVolume(cameraState);
SetCameraTransform(cameraState, transform);
SetCameraClippingVolume(cameraState, nearPlane, farPlane, verticalFovRad);
cameraState.m_viewportSize = viewportSize;
return cameraState;
}
AzFramework::CameraState CreateIdentityDefaultCamera(
const AZ::Vector3& position, const AZ::Vector2& viewportSize)
AzFramework::CameraState CreateDefaultCamera(const AZ::Transform& transform, const AZ::Vector2& viewportSize)
{
AzFramework::CameraState cameraState;
SetCameraTransform(cameraState, transform);
SetDefaultCameraClippingVolume(cameraState);
cameraState.m_viewportSize = viewportSize;
return cameraState;
}
AzFramework::CameraState CreateIdentityDefaultCamera(const AZ::Vector3& position, const AZ::Vector2& viewportSize)
{
return CreateDefaultCamera(AZ::Transform::CreateTranslation(position), viewportSize);
}
@@ -89,15 +103,15 @@ namespace AzFramework
void CameraState::Reflect(AZ::SerializeContext& serializeContext)
{
serializeContext.Class<CameraState>()->
Field("Position", &CameraState::m_position)->
Field("Forward", &CameraState::m_forward)->
Field("Side", &CameraState::m_side)->
Field("Up", &CameraState::m_up)->
Field("ViewportSize", &CameraState::m_viewportSize)->
Field("NearClip", &CameraState::m_nearClip)->
Field("FarClip", &CameraState::m_farClip)->
Field("FovZoom", &CameraState::m_fovOrZoom)->
Field("Ortho", &CameraState::m_orthographic);
serializeContext.Class<CameraState>()
->Field("Position", &CameraState::m_position)
->Field("Forward", &CameraState::m_forward)
->Field("Side", &CameraState::m_side)
->Field("Up", &CameraState::m_up)
->Field("ViewportSize", &CameraState::m_viewportSize)
->Field("NearClip", &CameraState::m_nearClip)
->Field("FarClip", &CameraState::m_farClip)
->Field("FovZoom", &CameraState::m_fovOrZoom)
->Field("Ortho", &CameraState::m_orthographic);
}
} // namespace AzFramework
@@ -40,10 +40,14 @@ namespace AzFramework
AZ::Vector2 m_viewportSize = AZ::Vector2::CreateZero(); //!< Dimensions of the viewport.
float m_nearClip = 0.01f; //!< Near clip plane of the camera.
float m_farClip = 100.0f; //!< Far clip plane of the camera.
float m_fovOrZoom = 0.0f; //!< Fov or zoom of camera depending on if it is using orthographic projection or not.
float m_fovOrZoom = 0.0f; //!< Vertical fov or zoom of camera depending on if it is using orthographic projection or not.
bool m_orthographic = false; //!< Is the camera using orthographic projection or not.
};
//! Create a camera at the given transform, specifying the near and far clip planes as well as the fov with a specific viewport size.
CameraState CreateCamera(
const AZ::Transform& transform, float nearPlane, float farPlane, float verticalFovRad, const AZ::Vector2& viewportSize);
//! Create a camera at the given transform with a specific viewport size.
//! @note The near/far clip planes and fov are sensible default values - please
//! use SetCameraClippingVolume to override them.
@@ -60,7 +64,7 @@ namespace AzFramework
CameraState CreateCameraFromWorldFromViewMatrix(const AZ::Matrix4x4& worldFromView, const AZ::Vector2& viewportSize);
//! Override the default near/far clipping planes and fov of the camera.
void SetCameraClippingVolume(CameraState& cameraState, float nearPlane, float farPlane, float fovRad);
void SetCameraClippingVolume(CameraState& cameraState, float nearPlane, float farPlane, float verticalFovRad);
//! Override the default near/far clipping planes and fov of the camera by inferring them the specified right handed transform into clip space.
void SetCameraClippingVolumeFromPerspectiveFovMatrixRH(CameraState& cameraState, const AZ::Matrix4x4& clipFromView);
@@ -24,6 +24,10 @@ namespace AzFramework
serializeContext->Class<ScreenVector>()->
Field("X", &ScreenVector::m_x)->
Field("Y", &ScreenVector::m_y);
serializeContext->Class<ScreenSize>()->
Field("Width", &ScreenSize::m_width)->
Field("Height", &ScreenSize::m_height);
}
}
} // namespace AzFramework
@@ -26,7 +26,7 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}");
ScreenPoint() = default;
ScreenPoint(int x, int y)
constexpr ScreenPoint(int x, int y)
: m_x(x)
, m_y(y)
{
@@ -45,7 +45,7 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}");
ScreenVector() = default;
ScreenVector(int x, int y)
constexpr ScreenVector(int x, int y)
: m_x(x)
, m_y(y)
{
@@ -55,6 +55,22 @@ namespace AzFramework
int m_y; //!< Y screen delta.
};
//! A wrapper around a screen width and height.
struct ScreenSize
{
AZ_TYPE_INFO(ScreenSize, "{26D28916-6E8E-44B8-83F9-C44BCDA370E2}");
ScreenSize() = default;
constexpr ScreenSize(int width, int height)
: m_width(width)
, m_height(height)
{
}
int m_width; //!< Screen size width.
int m_height; //!< Screen size height.
};
void ScreenGeometryReflect(AZ::ReflectContext* context);
inline const ScreenVector operator-(const ScreenPoint& lhs, const ScreenPoint& rhs)
@@ -138,6 +154,16 @@ namespace AzFramework
return !operator==(lhs, rhs);
}
inline const bool operator==(const ScreenSize& lhs, const ScreenSize& rhs)
{
return lhs.m_width == rhs.m_width && lhs.m_height == rhs.m_height;
}
inline const bool operator!=(const ScreenSize& lhs, const ScreenSize& rhs)
{
return !operator==(lhs, rhs);
}
inline ScreenVector& operator*=(ScreenVector& lhs, const float rhs)
{
lhs.m_x = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_x) * rhs));
@@ -152,6 +178,20 @@ namespace AzFramework
return result;
}
inline ScreenSize& operator*=(ScreenSize& lhs, const float rhs)
{
lhs.m_width = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_width) * rhs));
lhs.m_height = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_height) * rhs));
return lhs;
}
inline const ScreenSize operator*(const ScreenSize& lhs, const float rhs)
{
ScreenSize result{ lhs };
result *= rhs;
return result;
}
inline float ScreenVectorLength(const ScreenVector& screenVector)
{
return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y));
@@ -168,4 +208,28 @@ namespace AzFramework
{
return AZ::Vector2(aznumeric_cast<float>(screenVector.m_x), aznumeric_cast<float>(screenVector.m_y));
}
//! Return an AZ::Vector2 from a ScreenSize.
inline AZ::Vector2 Vector2FromScreenSize(const ScreenSize& screenSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenSize.m_width), aznumeric_cast<float>(screenSize.m_height));
}
//! Return a ScreenPoint from an AZ::Vector2.
inline ScreenPoint ScreenPointFromVector2(const AZ::Vector2& vector2)
{
return ScreenPoint(aznumeric_cast<int>(AZStd::lround(vector2.GetX())), aznumeric_cast<int>(AZStd::lround(vector2.GetY())));
}
//! Return a ScreenVector from an AZ::Vector2.
inline ScreenVector ScreenVectorFromVector2(const AZ::Vector2& vector2)
{
return ScreenVector(aznumeric_cast<int>(AZStd::lround(vector2.GetX())), aznumeric_cast<int>(AZStd::lround(vector2.GetY())));
}
//! Return a ScreenSize from an AZ::Vector2.
inline ScreenSize ScreenSizeFromVector2(const AZ::Vector2& vector2)
{
return ScreenSize(aznumeric_cast<int>(AZStd::lround(vector2.GetX())), aznumeric_cast<int>(AZStd::lround(vector2.GetY())));
}
} // namespace AzFramework
@@ -10,6 +10,7 @@
#include <AzCore/Math/Frustum.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/VectorConversions.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
@@ -112,9 +113,8 @@ namespace AzFramework
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize)
{
const auto ndcNormalizedPosition = WorldToScreenNdc(worldPosition, cameraView, cameraProjection);
// scale ndc position by screen dimensions to return screen position
return ScreenPointFromNdc(AZ::Vector3ToVector2(ndcNormalizedPosition), viewportSize);
return ScreenPointFromNdc(AZ::Vector3ToVector2(WorldToScreenNdc(worldPosition, cameraView, cameraProjection)), viewportSize);
}
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState)
@@ -144,9 +144,7 @@ namespace AzFramework
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize)
{
const auto normalizedScreenPosition = NdcFromScreenPoint(screenPosition, viewportSize);
return ScreenNdcToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
return ScreenNdcToWorld(NdcFromScreenPoint(screenPosition, viewportSize), inverseCameraView, inverseCameraProjection);
}
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState)
@@ -104,9 +104,10 @@ namespace UnitTest
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero();
//! This is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based
//! on vertical or horizontal motion) as the rotate speed function is set to be 1/1000.
inline static const int PixelMotionDelta = 1570;
// this is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based
// on vertical or horizontal motion) as the rotate speed function is set to be 1/1000.
inline static const int PixelMotionDelta90Degrees = 1570;
inline static const int PixelMotionDelta135Degrees = 2356;
};
TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents)
@@ -292,7 +293,7 @@ namespace UnitTest
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ PixelMotionDelta });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ PixelMotionDelta90Degrees });
const float expectedYaw = AzFramework::WrapYawRotation(-AZ::Constants::HalfPi);
@@ -310,7 +311,7 @@ namespace UnitTest
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta90Degrees });
const float expectedPitch = AzFramework::ClampPitchRotation(-AZ::Constants::HalfPi);
@@ -331,7 +332,7 @@ namespace UnitTest
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta90Degrees });
const auto expectedCameraEndingPosition = AZ::Vector3(0.0f, -10.0f, 10.0f);
const float expectedPitch = AzFramework::ClampPitchRotation(-AZ::Constants::HalfPi);
@@ -354,7 +355,7 @@ namespace UnitTest
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta90Degrees });
const auto expectedCameraEndingPosition = AZ::Vector3(20.0f, -5.0f, 0.0f);
const float expectedYaw = AzFramework::WrapYawRotation(AZ::Constants::HalfPi);
@@ -366,4 +367,42 @@ namespace UnitTest
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3(5.0f, -10.0f, 0.0f)));
EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f));
}
TEST_F(CameraInputFixture, CameraPitchCanNotBeMovedPastNinetyDegreesWhenConstrained)
{
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
// pitch by 135.0 degrees
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ -PixelMotionDelta135Degrees });
// clamped to 90.0 degrees
const float expectedPitch = AZ::DegToRad(90.0f);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
}
TEST_F(CameraInputFixture, CameraPitchCanBeMovedPastNinetyDegreesWhenUnconstrained)
{
m_firstPersonRotateCamera->m_constrainPitch = []
{
return false;
};
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
// pitch by 135.0 degrees
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ -PixelMotionDelta135Degrees });
const float expectedPitch = AZ::DegToRad(135.0f);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
}
} // namespace UnitTest
@@ -11,6 +11,7 @@
#include <AzFramework/Viewport/CameraState.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AzCore/Math/SimdMath.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Matrix4x4.h>
namespace UnitTest
@@ -51,22 +52,6 @@ namespace UnitTest
{
};
// Taken from Atom::MatrixUtils for testing purposes, this can be removed if MakePerspectiveFovMatrixRH makes it into AZ
static AZ::Matrix4x4 MakePerspectiveMatrixRH(float fovY, float aspectRatio, float nearClip, float farClip)
{
float sinFov, cosFov;
AZ::SinCos(0.5f * fovY, sinFov, cosFov);
float yScale = cosFov / sinFov; //cot(fovY/2)
float xScale = yScale / aspectRatio;
AZ::Matrix4x4 out;
out.SetRow(0, xScale, 0.f, 0.f, 0.f );
out.SetRow(1, 0.f, yScale, 0.f, 0.f );
out.SetRow(2, 0.f, 0.f, farClip / (nearClip - farClip), nearClip*farClip / (nearClip - farClip) );
out.SetRow(3, 0.f, 0.f, -1.f, 0.f );
return out;
}
TEST_P(Translation, Permutation)
{
// Given a position
@@ -176,7 +161,8 @@ namespace UnitTest
{
auto [fovY, aspectRatio, nearClip, farClip] = GetParam();
AZ::Matrix4x4 clipFromView = MakePerspectiveMatrixRH(fovY, aspectRatio, nearClip, farClip);
AZ::Matrix4x4 clipFromView;
MakePerspectiveFovMatrixRH(clipFromView, fovY, aspectRatio, nearClip, farClip);
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(m_cameraState, clipFromView);
@@ -8,12 +8,13 @@
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Viewport/CursorState.h>
#include <Tests/Utils/Printers.h>
namespace UnitTest
{
using AzFramework::CursorState;
using AzFramework::ScreenVector;
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
class CursorStateFixture : public ::testing::Test
{
@@ -0,0 +1,32 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Printers.h"
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <ostream>
#include <string>
namespace AzFramework
{
void PrintTo(const ScreenPoint& screenPoint, std::ostream* os)
{
*os << "(x: " << screenPoint.m_x << ", y: " << screenPoint.m_y << ")";
}
void PrintTo(const ScreenVector& screenVector, std::ostream* os)
{
*os << "(x: " << screenVector.m_x << ", y: " << screenVector.m_y << ")";
}
void PrintTo(const ScreenSize& screenSize, std::ostream* os)
{
*os << "(width: " << screenSize.m_width << ", height: " << screenSize.m_height << ")";
}
} // namespace AzFramework
@@ -0,0 +1,20 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <iosfwd>
namespace AzFramework
{
struct ScreenPoint;
struct ScreenVector;
struct ScreenSize;
void PrintTo(const ScreenPoint& screenPoint, std::ostream* os);
void PrintTo(const ScreenVector& screenVector, std::ostream* os);
void PrintTo(const ScreenSize& screenSize, std::ostream* os);
} // namespace AzFramework
@@ -11,5 +11,7 @@ set(FILES
Mocks/MockWindowRequests.h
Utils/Utils.h
Utils/Utils.cpp
Utils/Printers.h
Utils/Printers.cpp
FrameworkApplicationFixture.h
)
@@ -41,8 +41,8 @@ namespace AzManipulatorTestFramework
// ViewportInteractionRequestBus overrides ...
AzFramework::CameraState GetCameraState() override;
AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) override;
AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override;
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportScreenToWorldRay(
AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) override;
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportScreenToWorldRay(
const AzFramework::ScreenPoint& screenPosition) override;
float DeviceScalingFactor() override;
@@ -95,7 +95,7 @@ namespace AzManipulatorTestFramework
AzToolsFramework::ViewportInteraction::MousePick mousePick;
mousePick.m_screenCoordinates = screenPoint;
mousePick.m_rayOrigin = cameraState.m_position;
mousePick.m_rayOrigin = nearPlaneWorldPosition;
mousePick.m_rayDirection = (nearPlaneWorldPosition - cameraState.m_position).GetNormalized();
return mousePick;
@@ -69,8 +69,6 @@ namespace AzManipulatorTestFramework
void ImmediateModeActionDispatcher::CameraStateImpl(const AzFramework::CameraState& cameraState)
{
m_viewportManipulatorInteraction.GetViewportInteraction().SetCameraState(cameraState);
GetMouseInteractionEvent()->m_mouseInteraction.m_mousePick.m_rayOrigin = cameraState.m_position;
GetMouseInteractionEvent()->m_mouseInteraction.m_mousePick.m_rayDirection = cameraState.m_forward;
}
void ImmediateModeActionDispatcher::MouseLButtonDownImpl()
@@ -20,7 +20,8 @@ namespace AzManipulatorTestFramework
{
public:
IndirectCallManipulatorManager(ViewportInteractionInterface& viewportInteraction);
// ManipulatorManagerInterface ...
// ManipulatorManagerInterface overrides ...
void ConsumeMouseInteractionEvent(const MouseInteractionEvent& event) override;
AzToolsFramework::ManipulatorManagerId GetId() const override;
bool ManipulatorBeingInteracted() const override;
@@ -140,13 +140,12 @@ namespace AzManipulatorTestFramework
return m_viewportId;
}
AZStd::optional<AZ::Vector3> ViewportInteraction::ViewportScreenToWorld(
[[maybe_unused]] const AzFramework::ScreenPoint& screenPosition, [[maybe_unused]] float depth)
AZ::Vector3 ViewportInteraction::ViewportScreenToWorld([[maybe_unused]] const AzFramework::ScreenPoint& screenPosition)
{
return {};
return AZ::Vector3::CreateZero();
}
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> ViewportInteraction::ViewportScreenToWorldRay(
AzToolsFramework::ViewportInteraction::ProjectedViewportRay ViewportInteraction::ViewportScreenToWorldRay(
[[maybe_unused]] const AzFramework::ScreenPoint& screenPosition)
{
return {};
@@ -140,8 +140,8 @@ namespace UnitTest
// given a left mouse down ray in world space
// consume the mouse move event
state.m_actionDispatcher->CameraState(m_cameraState)
->MouseLButtonDown()
->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState))
->MouseLButtonDown()
->ExpectTrue(state.m_linearManipulator->PerformingAction())
->ExpectManipulatorBeingInteracted()
->MouseLButtonUp()
@@ -0,0 +1,25 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace AzToolsFramework::EmbeddedPython
{
// When using embedded Python, some platforms need to explicitly load the python library.
// For any modules that depend on 3rdParty::Python package, the AZ::Module should inherit this class.
class PythonLoader
{
public:
PythonLoader();
~PythonLoader();
private:
void* m_embeddedLibPythonHandle{ nullptr };
};
} // namespace AzToolsFramework::EmbeddedPython
@@ -1196,14 +1196,25 @@ namespace AzToolsFramework
AZ::EntityId ToolsApplication::GetCurrentLevelEntityId()
{
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
AZ::SliceComponent* rootSliceComponent = nullptr;
AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceComponent, editorEntityContextId,
&AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
if (rootSliceComponent && rootSliceComponent->GetMetadataEntity())
if (IsPrefabSystemEnabled())
{
return rootSliceComponent->GetMetadataEntity()->GetId();
if (auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get())
{
return prefabPublicInterface->GetLevelInstanceContainerEntityId();
}
}
else
{
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
AZ::SliceComponent* rootSliceComponent = nullptr;
AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(
rootSliceComponent, editorEntityContextId, &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
if (rootSliceComponent && rootSliceComponent->GetMetadataEntity())
{
return rootSliceComponent->GetMetadataEntity()->GetId();
}
}
return AZ::EntityId();
@@ -158,7 +158,10 @@ namespace UnitTest
{
// Create & Start a new ToolsApplication if there's no existing one
m_app = CreateTestApplication();
m_app->Start(AzFramework::Application::Descriptor());
AZ::ComponentApplication::StartupParameters startupParameters;
startupParameters.m_loadAssetCatalog = false;
m_app->Start(AzFramework::Application::Descriptor(), startupParameters);
}
// without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -162,12 +162,11 @@ namespace AzToolsFramework
//! Multiply by DeviceScalingFactor to get the position in viewport pixel space.
virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0;
//! Transforms a point from Qt widget screen space to world space based on the given clip space depth.
//! Depth specifies a relative camera depth to project in the range of [0.f, 1.f].
//! Returns the world space position if successful.
virtual AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0;
virtual AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) = 0;
//! Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane.
//! Returns a ray containing the ray's origin and a direction normal, if successful.
virtual AZStd::optional<ProjectedViewportRay> ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
virtual ProjectedViewportRay ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
//! Gets the DPI scaling factor that translates Qt widget space into viewport pixel space.
virtual float DeviceScalingFactor() = 0;
@@ -229,9 +228,6 @@ namespace AzToolsFramework
class MainEditorViewportInteractionRequests
{
public:
//! Given a point in screen space, return the picked entity (if any).
//! Picked EntityId will be returned, InvalidEntityId will be returned on failure.
virtual AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) = 0;
//! Given a point in screen space, return the terrain position in world space.
virtual AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) = 0;
//! Return the terrain height given a world position in 2d (xy plane).
@@ -266,7 +262,6 @@ namespace AzToolsFramework
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//! Returns the current state of the keyboard modifier keys.
virtual KeyboardModifiers QueryKeyboardModifiers() = 0;
@@ -290,7 +285,6 @@ namespace AzToolsFramework
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//! Returns the current time in seconds.
//! This interface can be overridden for the purposes of testing to simplify viewport input requests.
@@ -148,7 +148,6 @@ namespace AzToolsFramework
return false;
}
EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
: m_entityDataCache(entityDataCache)
{
@@ -190,7 +189,10 @@ namespace AzToolsFramework
if (helpersVisible)
{
// some components choose to hide their icons (e.g. meshes)
if (!m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex))
// we also do not want to test against icons that may not be showing as they're inside a 'closed' entity container
// (these icons only become visible when it is opened for editing)
if (!m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) &&
m_entityDataCache->IsVisibleEntityIndividuallySelectableInViewport(entityCacheIndex))
{
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
@@ -235,7 +237,7 @@ namespace AzToolsFramework
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor,
ViewportInteraction::CursorStyleOverride::Forbidden);
}
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down ||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick)
@@ -409,7 +409,7 @@ namespace AzToolsFramework
const AzFramework::CameraState cameraState = GetCameraState(viewportId);
for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex)
{
if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex))
if (!entityDataCache.IsVisibleEntityIndividuallySelectableInViewport(entityCacheIndex))
{
continue;
}
@@ -983,7 +983,7 @@ namespace AzToolsFramework
{
if (auto entityIndex = entityDataCache.GetVisibleEntityIndexFromId(entityId))
{
if (entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex))
if (entityDataCache.IsVisibleEntityIndividuallySelectableInViewport(*entityIndex))
{
return *entityIndex;
}
@@ -293,12 +293,10 @@ namespace AzToolsFramework
return m_impl->m_visibleEntityDatas[index].m_iconHidden;
}
bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const
bool EditorVisibleEntityDataCache::IsVisibleEntityIndividuallySelectableInViewport(const size_t index) const
{
return m_impl->m_visibleEntityDatas[index].m_visible
&& !m_impl->m_visibleEntityDatas[index].m_locked
&& m_impl->m_visibleEntityDatas[index].m_inFocus
&& !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked &&
m_impl->m_visibleEntityDatas[index].m_inFocus && !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
}
AZStd::optional<size_t> EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const
@@ -55,7 +55,10 @@ namespace AzToolsFramework
bool IsVisibleEntityVisible(size_t index) const;
bool IsVisibleEntitySelected(size_t index) const;
bool IsVisibleEntityIconHidden(size_t index) const;
bool IsVisibleEntitySelectableInViewport(size_t index) const;
//! Returns true if the entity is individually selectable (none of its ancestors are a closed container entity).
//! @note It may still be desirable to be able to 'click' an entity that is a descendant of a closed container
//! to select the container itself, not the individual entity.
bool IsVisibleEntityIndividuallySelectableInViewport(size_t index) const;
AZStd::optional<size_t> GetVisibleEntityIndexFromId(AZ::EntityId entityId) const;
@@ -47,6 +47,7 @@ set(FILES
API/EntityCompositionRequestBus.h
API/EntityCompositionNotificationBus.h
API/EditorViewportIconDisplayInterface.h
API/PythonLoader.h
API/ViewPaneOptions.h
API/ViewportEditorModeTrackerInterface.h
Application/Ticker.h
@@ -0,0 +1,20 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/API/PythonLoader.h>
namespace AzToolsFramework::EmbeddedPython
{
PythonLoader::PythonLoader()
{
}
PythonLoader::~PythonLoader()
{
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/API/PythonLoader.h>
#include <AzCore/Debug/Trace.h>
#include <dlfcn.h>
namespace AzToolsFramework::EmbeddedPython
{
PythonLoader::PythonLoader()
{
constexpr char libPythonName[] = "libpython3.7m.so.1.0";
if (m_embeddedLibPythonHandle = dlopen(libPythonName, RTLD_NOW | RTLD_GLOBAL);
m_embeddedLibPythonHandle == nullptr)
{
char* err = dlerror();
AZ_Error("PythonLoader", false, "Failed to load %s with error: %s\n", libPythonName, err ? err : "Unknown Error");
}
}
PythonLoader::~PythonLoader()
{
if (m_embeddedLibPythonHandle)
{
dlclose(m_embeddedLibPythonHandle);
}
}
} // namespace AzToolsFramework::EmbeddedPython
@@ -7,4 +7,5 @@
#
set(FILES
AzToolsFramework/API/PythonLoader_Linux.cpp
)
@@ -7,4 +7,5 @@
#
set(FILES
../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
)
@@ -7,4 +7,5 @@
#
set(FILES
../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
)
@@ -40,6 +40,9 @@ namespace UnitTest
{
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
// default local bounds to unit cube
m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
}
void BoundsTestComponent::Deactivate()
@@ -57,7 +60,6 @@ namespace UnitTest
AZ::Aabb BoundsTestComponent::GetLocalBounds()
{
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
return m_localBounds;
}
} // namespace UnitTest
@@ -41,5 +41,7 @@ namespace UnitTest
// BoundsRequestBus overrides ...
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
AZ::Aabb m_localBounds; //!< Local bounds that can be modified for certain tests (defaults to unit cube).
};
} // namespace UnitTest
@@ -38,7 +38,7 @@
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
#include<Tests/BoundsTestComponent.h>
#include <Tests/BoundsTestComponent.h>
namespace AZ
{
@@ -493,12 +493,8 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Then
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
const AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
@@ -527,12 +523,8 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Then
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId1, entity2, entity3, entity4 };
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
const AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId1, entity2, entity3, entity4 };
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
@@ -946,6 +938,42 @@ namespace UnitTest
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
}
TEST_F(
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, BoundsBetweenCameraAndNearClipPlaneDoesNotIntersectMouseRay)
{
// move camera to 10 units along the y-axis
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
// send a very narrow bounds for entity1
AZ::Entity* entity1 = AzToolsFramework::GetEntityById(m_entityId1);
auto* boundTestComponent = entity1->FindComponent<BoundsTestComponent>();
boundTestComponent->m_localBounds =
AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f, -0.0025f, -0.5f), AZ::Vector3(0.5f, 0.0025f, 0.5f));
// move entity1 in front of the camera between it and the near clip plane
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.05f)));
// move entity2 behind entity1
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(15.0f)));
const auto entity2ScreenPosition = AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(m_entityId2), m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
->CameraState(m_cameraState)
->MousePosition(entity2ScreenPosition)
->CameraState(m_cameraState)
->MouseLButtonDown()
->MouseLButtonUp();
// ensure entity1 is not selected as it is before the near clip plane
using ::testing::UnorderedElementsAreArray;
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
const AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId2 };
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
}
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
, public ::testing::WithParamInterface<bool>
@@ -23,6 +23,7 @@
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <Tests/Utils/Printers.h>
using namespace AzToolsFramework;
@@ -17,6 +17,7 @@
#include <AzTest/AzTest.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <Tests/Utils/Printers.h>
namespace UnitTest
{
@@ -35,6 +36,7 @@ namespace UnitTest
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
return AzFramework::WorldToScreen(worldResult, cameraState);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// ScreenPoint tests
TEST(ViewportScreen, WorldToScreenAndScreenToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
@@ -102,8 +104,8 @@ namespace UnitTest
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// NDC tests
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
// Ndc tests
TEST(ViewportScreen, WorldToScreenNdcAndScreenNdcToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
{
using NdcPoint = AZ::Vector2;
@@ -136,7 +138,7 @@ namespace UnitTest
}
}
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueOrientatedCamera)
TEST(ViewportScreen, WorldToScreenNdcAndScreenNdcToWorldReturnsTheSameValueOrientatedCamera)
{
using NdcPoint = AZ::Vector2;
@@ -153,7 +155,7 @@ namespace UnitTest
// note: nearClip is 0.1 - the world space value returned will be aligned to the near clip
// plane of the camera so use that to confirm the mapping to/from is correct
TEST(ViewportScreen, ScreenNDCToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
TEST(ViewportScreen, ScreenNdcToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
{
using NdcPoint = AZ::Vector2;
@@ -23,6 +23,8 @@
#include <AzCore/RTTI/BehaviorContext.h>
//////////////////////////////////////////////////////////////////////////
#include <xxhash/xxhash.h>
namespace AssetBuilderSDK
{
const char* const ErrorWindow = "Error"; //Use this window name to log error messages.
@@ -1599,4 +1601,70 @@ namespace AssetBuilderSDK
{
return m_errorsOccurred;
}
AZ::u64 GetHashFromIOStream(AZ::IO::GenericStream& readStream, AZ::IO::SizeType* bytesReadOut, int hashMsDelay)
{
constexpr AZ::u64 HashBufferSize = 1024 * 64;
char buffer[HashBufferSize];
if(readStream.IsOpen() && readStream.CanRead())
{
AZ::IO::SizeType bytesRead;
auto* state = XXH64_createState();
if(state == nullptr)
{
AZ_Assert(false, "Failed to create hash state");
return 0;
}
if (XXH64_reset(state, 0) == XXH_ERROR)
{
AZ_Assert(false, "Failed to reset hash state");
return 0;
}
do
{
// In edge cases where another process is writing to this file while this hashing is occuring and that file wasn't locked,
// the following read check can fail because it performs an end of file check, and asserts and shuts down if the read size
// was smaller than the buffer and the read is not at the end of the file. The logic used to check end of file internal to read
// will be out of date in the edge cases where another process is actively writing to this file while this hash is running.
// The stream's length ends up more accurate in this case, preventing this assert and shut down.
// One area this occurs is the navigation mesh file (mnmnavmission0.bai) that's temporarily created when exporting a level,
// the navigation system can still be writing to this file when hashing begins, causing the EoF marker to change.
AZ::IO::SizeType remainingToRead = AZStd::min(readStream.GetLength() - readStream.GetCurPos(), aznumeric_cast<AZ::IO::SizeType>(AZ_ARRAY_SIZE(buffer)));
bytesRead = readStream.Read(remainingToRead, buffer);
if(bytesReadOut)
{
*bytesReadOut += bytesRead;
}
XXH64_update(state, buffer, bytesRead);
// Used by unit tests to force the race condition mentioned above, to verify the crash fix.
if(hashMsDelay > 0)
{
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(hashMsDelay));
}
} while (bytesRead > 0);
auto hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
return 0;
}
AZ::u64 GetFileHash(const char* filePath, AZ::IO::SizeType* bytesReadOut, int hashMsDelay)
{
constexpr bool ErrorOnReadFailure = true;
AZ::IO::FileIOStream readStream(filePath, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, ErrorOnReadFailure);
return GetHashFromIOStream(readStream, bytesReadOut, hashMsDelay);
}
}
@@ -910,6 +910,19 @@ namespace AssetBuilderSDK
//! There can be multiple builders running at once, so we need to filter out ones coming from other builders
AZStd::thread_id m_jobThreadId;
};
//! Get hash for a whole file
//! @filePath the path for the file
//! @bytesReadOut output the read file size in bytes
//! @hashMsDelay [Do not use except for unit test] add a delay in ms for between each block reading.
AZ::u64 GetFileHash(const char* filePath, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
//! Get hash for a generic IO stream
//! @readStream the input readable stream
//! @bytesReadOut output the read size in bytes
//! @hashMsDelay [Do not use except for unit test] add a delay in ms for between each block reading.
AZ::u64 GetHashFromIOStream(AZ::IO::GenericStream& readStream, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
} // namespace AssetBuilderSDK
namespace AZ
@@ -32,6 +32,7 @@ ly_add_target(
PUBLIC
AZ::AzFramework
AZ::AzToolsFramework
3rdParty::xxhash
)
ly_add_source_properties(
SOURCES AssetBuilderSDK/AssetBuilderSDK.cpp
@@ -32,7 +32,8 @@ struct FolderRootWatch::PlatformImplementation
{
if (m_iNotifyHandle < 0)
{
m_iNotifyHandle = inotify_init();
// The CLOEXEC flag prevents the inotify watchers from copying on fork/exec
m_iNotifyHandle = inotify_init1(IN_CLOEXEC);
}
return (m_iNotifyHandle >= 0);
}
@@ -1161,7 +1161,7 @@ namespace AssetUtilities
{
#ifndef AZ_TESTS_ENABLED
// Only used for unit tests, speed is critical for GetFileHash.
AZ_UNUSED(hashMsDelay);
hashMsDelay = 0;
#endif
bool useFileHashing = ShouldUseFileHashing();
@@ -1170,10 +1170,10 @@ namespace AssetUtilities
return 0;
}
AZ::u64 hash = 0;
if(!force)
{
auto* fileStateInterface = AZ::Interface<AssetProcessor::IFileStateRequests>::Get();
AZ::u64 hash = 0;
if (fileStateInterface && fileStateInterface->GetHash(filePath, &hash))
{
@@ -1181,64 +1181,8 @@ namespace AssetUtilities
}
}
char buffer[FileHashBufferSize];
constexpr bool ErrorOnReadFailure = true;
AZ::IO::FileIOStream readStream(filePath, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, ErrorOnReadFailure);
if(readStream.IsOpen() && readStream.CanRead())
{
AZ::IO::SizeType bytesRead;
auto* state = XXH64_createState();
if(state == nullptr)
{
AZ_Assert(false, "Failed to create hash state");
return 0;
}
if (XXH64_reset(state, 0) == XXH_ERROR)
{
AZ_Assert(false, "Failed to reset hash state");
return 0;
}
do
{
// In edge cases where another process is writing to this file while this hashing is occuring and that file wasn't locked,
// the following read check can fail because it performs an end of file check, and asserts and shuts down if the read size
// was smaller than the buffer and the read is not at the end of the file. The logic used to check end of file internal to read
// will be out of date in the edge cases where another process is actively writing to this file while this hash is running.
// The stream's length ends up more accurate in this case, preventing this assert and shut down.
// One area this occurs is the navigation mesh file (mnmnavmission0.bai) that's temporarily created when exporting a level,
// the navigation system can still be writing to this file when hashing begins, causing the EoF marker to change.
AZ::IO::SizeType remainingToRead = AZStd::min(readStream.GetLength() - readStream.GetCurPos(), aznumeric_cast<AZ::IO::SizeType>(AZ_ARRAY_SIZE(buffer)));
bytesRead = readStream.Read(remainingToRead, buffer);
if(bytesReadOut)
{
*bytesReadOut += bytesRead;
}
XXH64_update(state, buffer, bytesRead);
#ifdef AZ_TESTS_ENABLED
// Used by unit tests to force the race condition mentioned above, to verify the crash fix.
if(hashMsDelay > 0)
{
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(hashMsDelay));
}
#endif
} while (bytesRead > 0);
auto hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
return 0;
hash = AssetBuilderSDK::GetFileHash(filePath, bytesReadOut, hashMsDelay);
return hash;
}
AZ::u64 AdjustTimestamp(QDateTime timestamp)
@@ -188,7 +188,7 @@ namespace O3DE::ProjectManager
}
// add all the gem repos into the hash
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemReposGemInfos();
if (allRepoGemInfosResult.IsSuccess())
{
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
@@ -363,7 +363,10 @@ namespace O3DE::ProjectManager
{
const QString selectedGemPath = m_gemModel->GetPath(modelIndex);
// Remove gem from gems to be added
const bool wasAdded = GemModel::WasPreviouslyAdded(modelIndex);
const bool wasAddedDependency = GemModel::WasPreviouslyAddedDependency(modelIndex);
// Remove gem from gems to be added to update any dependencies
GemModel::SetIsAdded(*m_gemModel, modelIndex, false);
// Unregister the gem
@@ -391,6 +394,8 @@ namespace O3DE::ProjectManager
// Select remote gem
QModelIndex remoteGemIndex = m_gemModel->FindIndexByNameString(selectedGemName);
GemModel::SetWasPreviouslyAdded(*m_gemModel, remoteGemIndex, wasAdded);
GemModel::SetWasPreviouslyAddedDependency(*m_gemModel, remoteGemIndex, wasAddedDependency);
QModelIndex proxyIndex = m_proxyModel->mapFromSource(remoteGemIndex);
m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect);
}
@@ -435,7 +440,7 @@ namespace O3DE::ProjectManager
m_gemModel->AddGem(gemInfo);
}
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemReposGemInfos();
if (allRepoGemInfosResult.IsSuccess())
{
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
@@ -523,7 +528,9 @@ namespace O3DE::ProjectManager
const QString& gemPath = GemModel::GetPath(modelIndex);
// make sure any remote gems we added were downloaded successfully
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded)
const GemInfo::DownloadStatus status = GemModel::GetDownloadStatus(modelIndex);
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote &&
!(status == GemInfo::Downloaded || status == GemInfo::DownloadSuccessful))
{
QMessageBox::critical(
nullptr, "Cannot add gem that isn't downloaded",
@@ -37,7 +37,7 @@ namespace O3DE::ProjectManager
QString m_additionalInfo = "";
QString m_directoryLink = "";
QString m_repoUri = "";
QStringList m_includedGemPaths = {};
QStringList m_includedGemUris = {};
QDateTime m_lastUpdated;
};
} // namespace O3DE::ProjectManager
@@ -8,6 +8,7 @@
#include <GemRepo/GemRepoInspector.h>
#include <GemRepo/GemRepoItemDelegate.h>
#include <PythonBindingsInterface.h>
#include <QFrame>
#include <QLabel>
@@ -60,8 +61,10 @@ namespace O3DE::ProjectManager
// Repo name and url link
m_nameLabel->setText(m_model->GetName(modelIndex));
m_repoLinkLabel->setText(m_model->GetRepoUri(modelIndex));
m_repoLinkLabel->SetUrl(m_model->GetRepoUri(modelIndex));
const QString repoUri = m_model->GetRepoUri(modelIndex);
m_repoLinkLabel->setText(repoUri);
m_repoLinkLabel->SetUrl(repoUri);
// Repo summary
m_summaryLabel->setText(m_model->GetSummary(modelIndex));
@@ -41,7 +41,7 @@ namespace O3DE::ProjectManager
item->setData(gemRepoInfo.m_lastUpdated, RoleLastUpdated);
item->setData(gemRepoInfo.m_path, RolePath);
item->setData(gemRepoInfo.m_additionalInfo, RoleAdditionalInfo);
item->setData(gemRepoInfo.m_includedGemPaths, RoleIncludedGems);
item->setData(gemRepoInfo.m_includedGemUris, RoleIncludedGems);
appendRow(item);
@@ -98,7 +98,7 @@ namespace O3DE::ProjectManager
return modelIndex.data(RolePath).toString();
}
QStringList GemRepoModel::GetIncludedGemPaths(const QModelIndex& modelIndex)
QStringList GemRepoModel::GetIncludedGemUris(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleIncludedGems).toStringList();
}
@@ -118,23 +118,19 @@ namespace O3DE::ProjectManager
QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)
{
QVector<GemInfo> allGemInfos;
QStringList repoGemPaths = GetIncludedGemPaths(modelIndex);
QString repoUri = GetRepoUri(modelIndex);
for (const QString& gemPath : repoGemPaths)
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& gemInfosResult = PythonBindingsInterface::Get()->GetGemRepoGemInfos(repoUri);
if (gemInfosResult.IsSuccess())
{
AZ::Outcome<GemInfo> gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(gemPath);
if (gemInfoResult.IsSuccess())
{
allGemInfos.append(gemInfoResult.GetValue());
}
else
{
QMessageBox::critical(nullptr, tr("Gem Not Found"), tr("Cannot find info for gem %1.").arg(gemPath));
}
return gemInfosResult.GetValue();
}
else
{
QMessageBox::critical(nullptr, tr("Gems not found"), tr("Cannot find info for gems from repo %1").arg(GetName(modelIndex)));
}
return allGemInfos;
return QVector<GemInfo>();
}
bool GemRepoModel::IsEnabled(const QModelIndex& modelIndex)
@@ -39,7 +39,7 @@ namespace O3DE::ProjectManager
static QDateTime GetLastUpdated(const QModelIndex& modelIndex);
static QString GetPath(const QModelIndex& modelIndex);
static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex);
static QStringList GetIncludedGemUris(const QModelIndex& modelIndex);
static QVector<Tag> GetIncludedGemTags(const QModelIndex& modelIndex);
static QVector<GemInfo> GetIncludedGemInfos(const QModelIndex& modelIndex);
@@ -9,12 +9,14 @@
#include <ProjectBuilderController.h>
#include <ProjectBuilderWorker.h>
#include <ProjectButtonWidget.h>
#include <ProjectManagerSettings.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <QMessageBox>
#include <QDesktopServices>
#include <QUrl>
namespace O3DE::ProjectManager
{
ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent)
@@ -27,6 +29,15 @@ namespace O3DE::ProjectManager
m_worker = new ProjectBuilderWorker(m_projectInfo);
m_worker->moveToThread(&m_workerThread);
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
// Remove key here in case Project Manager crashing while building that causes HandleResults to not be called
QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
settingsRegistry->Remove(settingsKey.toStdString().c_str());
SaveProjectManagerSettings();
}
connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater);
connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject);
connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults);
@@ -80,6 +91,8 @@ namespace O3DE::ProjectManager
void ProjectBuilderController::HandleResults(const QString& result)
{
QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
if (!result.isEmpty())
{
if (result.contains(tr("log")))
@@ -109,12 +122,26 @@ namespace O3DE::ProjectManager
emit NotifyBuildProject(m_projectInfo);
}
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
settingsRegistry->Remove(settingsKey.toStdString().c_str());
SaveProjectManagerSettings();
}
emit Done(false);
return;
}
else
{
m_projectInfo.m_buildFailed = false;
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
settingsRegistry->Set(settingsKey.toStdString().c_str(), true);
SaveProjectManagerSettings();
}
}
emit Done(true);
@@ -0,0 +1,54 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectManagerSettings.h"
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/Utils/Utils.h>
namespace O3DE::ProjectManager
{
void SaveProjectManagerSettings()
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
dumperSettings.m_jsonPointerPrefix = ProjectManagerKeyPrefix;
AZStd::string stringBuffer;
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(
*settingsRegistry, ProjectManagerKeyPrefix, stringStream, dumperSettings))
{
AZ_Warning("ProjectManager", false, "Could not save Project Manager settings to stream");
return;
}
AZ::IO::FixedMaxPath o3deUserPath = AZ::Utils::GetO3deManifestDirectory();
o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder;
o3deUserPath /= "ProjectManager.setreg";
bool saved = false;
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
AZ::IO::SystemFile outputFile;
if (outputFile.Open(o3deUserPath.c_str(), configurationMode))
{
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
}
AZ_Warning("ProjectManager", saved, "Unable to save Project Manager registry file to path: %s", o3deUserPath.c_str());
}
QString GetProjectBuiltSuccessfullyKey(const QString& projectName)
{
return QString("%1/Projects/%2/BuiltSuccessfully").arg(ProjectManagerKeyPrefix).arg(projectName);
}
}
@@ -0,0 +1,21 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QString>
#endif
namespace O3DE::ProjectManager
{
static constexpr char ProjectManagerKeyPrefix[] = "/O3DE/ProjectManager";
void SaveProjectManagerSettings();
QString GetProjectBuiltSuccessfullyKey(const QString& projectName);
}
@@ -14,6 +14,7 @@
#include <ProjectUtils.h>
#include <ProjectBuilderController.h>
#include <ScreensCtrl.h>
#include <ProjectManagerSettings.h>
#include <AzQtComponents/Components/FlowLayout.h>
#include <AzCore/Platform.h>
@@ -22,6 +23,7 @@
#include <AzFramework/Process/ProcessCommon.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
@@ -269,17 +271,36 @@ namespace O3DE::ProjectManager
// Add any missing project buttons and restore buttons to default state
for (const ProjectInfo& project : projectsVector)
{
ProjectButton* currentButton = nullptr;
if (!m_projectButtons.contains(QDir::toNativeSeparators(project.m_path)))
{
m_projectButtons.insert(QDir::toNativeSeparators(project.m_path), CreateProjectButton(project));
currentButton = CreateProjectButton(project);
m_projectButtons.insert(QDir::toNativeSeparators(project.m_path), currentButton);
}
else
{
auto projectButtonIter = m_projectButtons.find(QDir::toNativeSeparators(project.m_path));
if (projectButtonIter != m_projectButtons.end())
{
projectButtonIter.value()->RestoreDefaultState();
m_projectsFlowLayout->addWidget(projectButtonIter.value());
currentButton = projectButtonIter.value();
currentButton->RestoreDefaultState();
m_projectsFlowLayout->addWidget(currentButton);
}
}
// Check whether project manager has successfully built the project
if (currentButton)
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
bool projectBuiltSuccessfully = false;
if (settingsRegistry)
{
QString settingsKey = GetProjectBuiltSuccessfullyKey(project.m_projectName);
settingsRegistry->Get(projectBuiltSuccessfully, settingsKey.toStdString().c_str());
}
if (!projectBuiltSuccessfully)
{
currentButton->ShowBuildRequired();
}
}
}
@@ -1154,11 +1154,11 @@ namespace O3DE::ProjectManager
gemRepoInfo.m_isEnabled = false;
}
if (data.contains("gem_paths"))
if (data.contains("gems"))
{
for (auto gemPath : data["gem_paths"])
for (auto gemPath : data["gems"])
{
gemRepoInfo.m_includedGemPaths.push_back(Py_To_String(gemPath));
gemRepoInfo.m_includedGemUris.push_back(Py_To_String(gemPath));
}
}
}
@@ -1207,7 +1207,35 @@ namespace O3DE::ProjectManager
return AZ::Success(AZStd::move(gemRepos));
}
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetAllGemRepoGemsInfos()
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetGemRepoGemInfos(const QString& repoUri)
{
QVector<GemInfo> gemInfos;
AZ::Outcome<void, AZStd::string> result = ExecuteWithLockErrorHandling(
[&]
{
auto pyUri = QString_To_Py_String(repoUri);
auto gemPaths = m_repo.attr("get_gem_json_paths_from_cached_repo")(pyUri);
if (pybind11::isinstance<pybind11::set>(gemPaths))
{
for (auto path : gemPaths)
{
GemInfo gemInfo = GemInfoFromPath(path, pybind11::none());
gemInfo.m_downloadStatus = GemInfo::DownloadStatus::NotDownloaded;
gemInfos.push_back(gemInfo);
}
}
});
if (!result.IsSuccess())
{
return AZ::Failure(result.GetError());
}
return AZ::Success(AZStd::move(gemInfos));
}
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetAllGemReposGemInfos()
{
QVector<GemInfo> gemInfos;
AZ::Outcome<void, AZStd::string> result = ExecuteWithLockErrorHandling(
@@ -65,7 +65,8 @@ namespace O3DE::ProjectManager
AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> AddGemRepo(const QString& repoUri) override;
bool RemoveGemRepo(const QString& repoUri) override;
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() override;
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemRepoGemsInfos() override;
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetGemRepoGemInfos(const QString& repoUri) override;
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemReposGemInfos() override;
AZ::Outcome<void, AZStd::pair<AZStd::string, AZStd::string>> DownloadGem(
const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force = false) override;
void CancelDownload() override;
@@ -217,11 +217,18 @@ namespace O3DE::ProjectManager
*/
virtual AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() = 0;
/**
* Gathers all gem infos for repos
* @param repoUri the absolute filesystem path or url to the gem repo.
* @return A list of gem infos.
*/
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetGemRepoGemInfos(const QString& repoUri) = 0;
/**
* Gathers all gem infos for all gems registered from repos.
* @return A list of gem infos.
*/
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemRepoGemsInfos() = 0;
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemReposGemInfos() = 0;
/**
* Downloads and registers a Gem.
@@ -15,6 +15,9 @@
#include <UpdateProjectCtrl.h>
#include <UpdateProjectSettingsScreen.h>
#include <ProjectUtils.h>
#include <ProjectManagerSettings.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <QDialogButtonBox>
#include <QMessageBox>
@@ -280,6 +283,21 @@ namespace O3DE::ProjectManager
}
}
if (newProjectSettings.m_projectName != m_projectInfo.m_projectName)
{
// update reg key
QString oldSettingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
QString newSettingsKey = GetProjectBuiltSuccessfullyKey(newProjectSettings.m_projectName);
auto settingsRegistry = AZ::SettingsRegistry::Get();
bool projectBuiltSuccessfully = false;
if (settingsRegistry && settingsRegistry->Get(projectBuiltSuccessfully, oldSettingsKey.toStdString().c_str()))
{
settingsRegistry->Set(newSettingsKey.toStdString().c_str(), projectBuiltSuccessfully);
SaveProjectManagerSettings();
}
}
if (!newProjectSettings.m_newPreviewImagePath.isEmpty())
{
if (!ProjectUtils::ReplaceProjectFile(
@@ -58,6 +58,8 @@ set(FILES
Source/CreateProjectCtrl.cpp
Source/UpdateProjectCtrl.h
Source/UpdateProjectCtrl.cpp
Source/ProjectManagerSettings.h
Source/ProjectManagerSettings.cpp
Source/ProjectsScreen.h
Source/ProjectsScreen.cpp
Source/ProjectSettingsScreen.h
@@ -0,0 +1,44 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <aws/cognito-identity/CognitoIdentityClient.h>
#include <aws/identity-management/auth/CognitoCachingCredentialsProvider.h>
#include <aws/identity-management/auth/PersistentCognitoIdentityProvider.h>
namespace AWSClientAuth
{
//! Cognito Caching Credentials Provider implementation that is derived from AWS Native SDK.
//! For use with authenticated credentials.
class AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider
: public Aws::Auth::CognitoCachingCredentialsProvider
{
public:
AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider(
const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient = nullptr);
protected:
Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome GetCredentialsFromCognito() const override;
};
//! Cognito Caching Credentials Provider implementation that is eventually derived from AWS Native SDK.
//! For use with anonymous credentials.
class AWSClientAuthCachingAnonymousCredsProvider : public AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider
{
public:
AWSClientAuthCachingAnonymousCredsProvider(
const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient = nullptr);
protected:
Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome GetCredentialsFromCognito() const override;
};
} // namespace AWSClientAuth
@@ -9,6 +9,7 @@
#pragma once
#include <Authorization/AWSCognitoAuthorizationBus.h>
#include <Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h>
#include <Authorization/AWSClientAuthPersistentCognitoIdentityProvider.h>
#include <Authentication/AuthenticationProviderBus.h>
#include <Credential/AWSCredentialBus.h>
@@ -51,8 +52,8 @@ namespace AWSClientAuth
std::shared_ptr<AWSClientAuthPersistentCognitoIdentityProvider> m_persistentCognitoIdentityProvider;
std::shared_ptr<AWSClientAuthPersistentCognitoIdentityProvider> m_persistentAnonymousCognitoIdentityProvider;
std::shared_ptr<Aws::Auth::CognitoCachingAuthenticatedCredentialsProvider> m_cognitoCachingCredentialsProvider;
std::shared_ptr<Aws::Auth::CognitoCachingAnonymousCredentialsProvider> m_cognitoCachingAnonymousCredentialsProvider;
std::shared_ptr<AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider> m_cognitoCachingCredentialsProvider;
std::shared_ptr<AWSClientAuthCachingAnonymousCredsProvider> m_cognitoCachingAnonymousCredentialsProvider;
AZStd::string m_cognitoIdentityPoolId;
AZStd::string m_formattedCognitoUserPoolId;
@@ -0,0 +1,122 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h>
#include <AzCore/Debug/Trace.h>
#include <aws/cognito-identity/CognitoIdentityClient.h>
#include <aws/cognito-identity/model/GetCredentialsForIdentityRequest.h>
#include <aws/cognito-identity/model/GetIdRequest.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/logging/LogMacros.h>
#include <aws/identity-management/auth/CognitoCachingCredentialsProvider.h>
#include <aws/identity-management/auth/PersistentCognitoIdentityProvider.h>
namespace AWSClientAuth
{
static const char* AUTH_LOG_TAG = "AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider";
static const char* ANON_LOG_TAG = "AWSClientAuthCachingAnonymousCredsProvider";
// Modification of https://github.com/aws/aws-sdk-cpp/blob/main/aws-cpp-sdk-identity-management/source/auth/CognitoCachingCredentialsProvider.cpp#L92
// to work around account ID requirement. Account id is not required for call to succeed and is not set unless provided.
// see: https://github.com/aws/aws-sdk-cpp/issues/1448
Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome FetchCredsFromCognito(
const Aws::CognitoIdentity::CognitoIdentityClient& cognitoIdentityClient,
Aws::Auth::PersistentCognitoIdentityProvider& identityRepository,
const char* logTag,
bool includeLogins)
{
auto logins = identityRepository.GetLogins();
Aws::Map<Aws::String, Aws::String> cognitoLogins;
for (auto& login : logins)
{
cognitoLogins[login.first] = login.second.accessToken;
}
if (!identityRepository.HasIdentityId())
{
auto accountId = identityRepository.GetAccountId();
auto identityPoolId = identityRepository.GetIdentityPoolId();
Aws::CognitoIdentity::Model::GetIdRequest getIdRequest;
getIdRequest.SetIdentityPoolId(identityPoolId);
if (!accountId.empty()) // new check
{
getIdRequest.SetAccountId(accountId);
AWS_LOGSTREAM_INFO(logTag, "Identity not found, requesting an id for accountId "
<< accountId << " identity pool id "
<< identityPoolId << " with logins.");
}
else
{
AWS_LOGSTREAM_INFO(
logTag, "Identity not found, requesting an id for identity pool id %s" << identityPoolId << " with logins.");
}
if (includeLogins)
{
getIdRequest.SetLogins(cognitoLogins);
}
auto getIdOutcome = cognitoIdentityClient.GetId(getIdRequest);
if (getIdOutcome.IsSuccess())
{
auto identityId = getIdOutcome.GetResult().GetIdentityId();
AWS_LOGSTREAM_INFO(logTag, "Successfully retrieved identity: " << identityId);
identityRepository.PersistIdentityId(identityId);
}
else
{
AWS_LOGSTREAM_ERROR(
logTag,
"Failed to retrieve identity. Error: " << getIdOutcome.GetError().GetExceptionName() << " "
<< getIdOutcome.GetError().GetMessage());
return Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome(getIdOutcome.GetError());
}
}
Aws::CognitoIdentity::Model::GetCredentialsForIdentityRequest getCredentialsForIdentityRequest;
getCredentialsForIdentityRequest.SetIdentityId(identityRepository.GetIdentityId());
if (includeLogins)
{
getCredentialsForIdentityRequest.SetLogins(cognitoLogins);
}
return cognitoIdentityClient.GetCredentialsForIdentity(getCredentialsForIdentityRequest);
}
AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider::AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider(
const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient)
: CognitoCachingCredentialsProvider(identityRepository, cognitoIdentityClient)
{
}
Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome
AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider::GetCredentialsFromCognito() const
{
return FetchCredsFromCognito(*m_cognitoIdentityClient, *m_identityRepository, AUTH_LOG_TAG, true);
}
AWSClientAuthCachingAnonymousCredsProvider::AWSClientAuthCachingAnonymousCredsProvider(
const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient)
: AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider(identityRepository, cognitoIdentityClient)
{
}
Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome AWSClientAuthCachingAnonymousCredsProvider::
GetCredentialsFromCognito() const
{
return FetchCredsFromCognito(*m_cognitoIdentityClient, *m_identityRepository, ANON_LOG_TAG, false);
}
} // namespace AWSClientAuth
@@ -8,6 +8,7 @@
#include <AWSClientAuthBus.h>
#include <AWSCoreBus.h>
#include <Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h>
#include <Authorization/AWSCognitoAuthorizationController.h>
#include <ResourceMapping/AWSResourceMappingBus.h>
#include <AWSClientAuthResourceMappingConstants.h>
@@ -38,10 +39,12 @@ namespace AWSClientAuth
auto identityClient = AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIdentityClient();
m_cognitoCachingCredentialsProvider =
std::make_shared<Aws::Auth::CognitoCachingAuthenticatedCredentialsProvider>(m_persistentCognitoIdentityProvider, identityClient);
std::make_shared<AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider>(
m_persistentCognitoIdentityProvider, identityClient);
m_cognitoCachingAnonymousCredentialsProvider =
std::make_shared<Aws::Auth::CognitoCachingAnonymousCredentialsProvider>(m_persistentAnonymousCognitoIdentityProvider, identityClient);
std::make_shared<AWSClientAuthCachingAnonymousCredsProvider>(
m_persistentAnonymousCognitoIdentityProvider, identityClient);
}
AWSCognitoAuthorizationController::~AWSCognitoAuthorizationController()
@@ -65,9 +68,13 @@ namespace AWSClientAuth
AWSCore::AWSResourceMappingRequestBus::BroadcastResult(
m_cognitoIdentityPoolId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoIdentityPoolIdResourceMappingKey);
if (m_awsAccountId.empty() || m_cognitoIdentityPoolId.empty())
if (m_awsAccountId.empty())
{
AZ_TracePrintf("AWSCognitoAuthorizationController", "AWS account id not not configured. Proceeding without it.");
}
if (m_cognitoIdentityPoolId.empty())
{
AZ_Warning("AWSCognitoAuthorizationController", !m_awsAccountId.empty(), "Missing AWS account id not configured.");
AZ_Warning("AWSCognitoAuthorizationController", !m_cognitoIdentityPoolId.empty(), "Missing Cognito Identity pool id in resource mappings.");
return false;
}
@@ -62,6 +62,14 @@ TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Success)
ASSERT_TRUE(m_mockController->m_cognitoIdentityPoolId == AWSClientAuthUnitTest::TEST_RESOURCE_NAME_ID);
}
TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Success_GetAWSAccountEmpty)
{
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetResourceNameId(testing::_)).Times(2);
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultAccountId()).Times(1).WillOnce(testing::Return(""));
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultRegion()).Times(1);
ASSERT_TRUE(m_mockController->Initialize());
}
TEST_F(AWSCognitoAuthorizationControllerTest, RequestAWSCredentials_WithLogins_Success)
{
AWSClientAuth::AuthenticationTokens tokens(
@@ -121,7 +129,7 @@ TEST_F(AWSCognitoAuthorizationControllerTest, MultipleCalls_UsesCacheCredentials
m_mockController->RequestAWSCredentialsAsync();
}
TEST_F(AWSCognitoAuthorizationControllerTest, RequestAWSCredentials_Fail_GetIdError)
TEST_F(AWSCognitoAuthorizationControllerTest, RequestAWSCredentials_Fail_GetIdError) // fail
{
AWSClientAuth::AuthenticationTokens cognitoTokens(
AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN,
@@ -321,7 +329,7 @@ TEST_F(AWSCognitoAuthorizationControllerTest, GetCredentialsProvider_NoPersisted
EXPECT_TRUE(actualCredentialsProvider == m_mockController->m_cognitoCachingAnonymousCredentialsProvider);
}
TEST_F(AWSCognitoAuthorizationControllerTest, GetCredentialsProvider_NoPersistedLogins_NoAnonymousCredentials_ResultNullPtr)
TEST_F(AWSCognitoAuthorizationControllerTest, GetCredentialsProvider_NoPersistedLogins_NoAnonymousCredentials_ResultNullPtr) // fails
{
Aws::Client::AWSError<Aws::CognitoIdentity::CognitoIdentityErrors> error;
error.SetExceptionName(AWSClientAuthUnitTest::TEST_EXCEPTION);
@@ -431,11 +439,3 @@ TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Fail_GetResourceNameEmp
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultAccountId()).Times(1);
ASSERT_FALSE(m_mockController->Initialize());
}
TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Fail_GetAWSAccountEmpty)
{
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetResourceNameId(testing::_)).Times(1);
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultAccountId()).Times(1).WillOnce(testing::Return(""));
EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultRegion()).Times(0);
ASSERT_FALSE(m_mockController->Initialize());
}
@@ -24,6 +24,7 @@ set(FILES
Include/Private/Authorization/AWSCognitoAuthorizationController.h
Include/Private/Authorization/AWSClientAuthPersistentCognitoIdentityProvider.h
Include/Private/Authorization/AWSCognitoAuthorizationNotificationBusBehaviorHandler.h
Include/Private/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h
Include/Private/UserManagement/AWSCognitoUserManagementController.h
Include/Private/UserManagement/UserManagementNotificationBusBehaviorHandler.h
@@ -45,6 +46,7 @@ set(FILES
Source/Authorization/ClientAuthAWSCredentials.cpp
Source/Authorization/AWSCognitoAuthorizationController.cpp
Source/Authorization/AWSClientAuthPersistentCognitoIdentityProvider.cpp
Source/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.cpp
Source/UserManagement/AWSCognitoUserManagementController.cpp
)
@@ -7,16 +7,6 @@
"UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
"Name": "Albedo",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_basecolor",
"_diff",
"_color",
"_col",
"_albedo",
"_alb",
"_bc",
"_diffuse"
],
"PixelFormat": "BC1",
"DiscardAlpha": true,
"IsPowerOf2": true,
@@ -29,16 +19,6 @@
"UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
"Name": "Albedo",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_col",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "ASTC_6x6",
"MaxTextureSize": 2048,
"DiscardAlpha": true,
@@ -51,16 +31,6 @@
"UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
"Name": "Albedo",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_col",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "ASTC_6x6",
"MaxTextureSize": 2048,
"DiscardAlpha": true,
@@ -73,16 +43,6 @@
"UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
"Name": "Albedo",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_col",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC1",
"DiscardAlpha": true,
"IsPowerOf2": true,
@@ -94,16 +54,6 @@
"UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
"Name": "Albedo",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_col",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC1",
"DiscardAlpha": true,
"IsPowerOf2": true,
@@ -7,15 +7,6 @@
"UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
"Name": "AlbedoWithCoverage",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC1a",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -27,15 +18,6 @@
"UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
"Name": "AlbedoWithCoverage",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "ASTC_6x6",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -46,15 +28,6 @@
"UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
"Name": "AlbedoWithCoverage",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "ASTC_6x6",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -65,15 +38,6 @@
"UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
"Name": "AlbedoWithCoverage",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC1a",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -84,15 +48,6 @@
"UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
"Name": "AlbedoWithCoverage",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC1a",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -7,17 +7,7 @@
"UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
"Name": "AlbedoWithGenericAlpha",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC3",
"IsPowerOf2": true,
"PixelFormat": "ASTC_4x4",
"MipMapSetting": {
"MipGenType": "Box"
}
@@ -27,18 +17,8 @@
"UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
"Name": "AlbedoWithGenericAlpha",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "ASTC_6x6",
"MaxTextureSize": 2048,
"IsPowerOf2": true,
"MipMapSetting": {
"MipGenType": "Box"
}
@@ -47,18 +27,8 @@
"UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
"Name": "AlbedoWithGenericAlpha",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "ASTC_6x6",
"MaxTextureSize": 2048,
"IsPowerOf2": true,
"MipMapSetting": {
"MipGenType": "Box"
}
@@ -67,17 +37,7 @@
"UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
"Name": "AlbedoWithGenericAlpha",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC3",
"IsPowerOf2": true,
"MipMapSetting": {
"MipGenType": "Box"
}
@@ -86,17 +46,7 @@
"UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
"Name": "AlbedoWithGenericAlpha",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_diff",
"_color",
"_albedo",
"_alb",
"_basecolor",
"_bc",
"_diffuse"
],
"PixelFormat": "BC3",
"IsPowerOf2": true,
"MipMapSetting": {
"MipGenType": "Box"
}
@@ -8,12 +8,6 @@
"Name": "AmbientOcclusion",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ao",
"_ambocc",
"_amb",
"_ambientocclusion"
],
"PixelFormat": "BC4"
},
"PlatformsPresets": {
@@ -22,12 +16,6 @@
"Name": "AmbientOcclusion",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ao",
"_ambocc",
"_amb",
"_ambientocclusion"
],
"MaxTextureSize": 2048,
"PixelFormat": "ASTC_4x4"
},
@@ -36,12 +24,6 @@
"Name": "AmbientOcclusion",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ao",
"_ambocc",
"_amb",
"_ambientocclusion"
],
"MaxTextureSize": 2048,
"PixelFormat": "ASTC_4x4"
},
@@ -50,12 +32,6 @@
"Name": "AmbientOcclusion",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ao",
"_ambocc",
"_amb",
"_ambientocclusion"
],
"PixelFormat": "BC4"
},
"provo": {
@@ -63,12 +39,6 @@
"Name": "AmbientOcclusion",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ao",
"_ambocc",
"_amb",
"_ambientocclusion"
],
"PixelFormat": "BC4"
}
}
@@ -8,10 +8,6 @@
"Name": "ConvolvedCubemap",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ccm",
"_convolvedcubemap"
],
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
@@ -35,10 +31,6 @@
"Name": "ConvolvedCubemap",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ccm",
"_convolvedcubemap"
],
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
@@ -61,10 +53,6 @@
"Name": "ConvolvedCubemap",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ccm",
"_convolvedcubemap"
],
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
@@ -87,10 +75,6 @@
"Name": "ConvolvedCubemap",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ccm",
"_convolvedcubemap"
],
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
@@ -113,10 +97,6 @@
"Name": "ConvolvedCubemap",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_ccm",
"_convolvedcubemap"
],
"SuppressEngineReduce": true,
"PixelFormat": "R9G9B9E5",
"DiscardAlpha": true,
@@ -6,9 +6,6 @@
"DefaultPreset": {
"UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
"Name": "Decal_AlbedoWithOpacity",
"FileMasks": [
"_decal"
],
"PixelFormat": "BC7t",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -21,9 +18,6 @@
"android": {
"UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
"Name": "Decal_AlbedoWithOpacity",
"FileMasks": [
"_decal"
],
"PixelFormat": "ASTC_4x4",
"MaxTextureSize": 2048,
"IsPowerOf2": true,
@@ -36,9 +30,6 @@
"ios": {
"UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
"Name": "Decal_AlbedoWithOpacity",
"FileMasks": [
"_decal"
],
"PixelFormat": "ASTC_4x4",
"MaxTextureSize": 2048,
"IsPowerOf2": true,
@@ -51,9 +42,6 @@
"mac": {
"UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
"Name": "Decal_AlbedoWithOpacity",
"FileMasks": [
"_decal"
],
"PixelFormat": "BC3",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -65,9 +53,6 @@
"provo": {
"UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
"Name": "Decal_AlbedoWithOpacity",
"FileMasks": [
"_decal"
],
"PixelFormat": "BC7t",
"IsPowerOf2": true,
"MipMapSetting": {
@@ -8,18 +8,6 @@
"Name": "Displacement",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_displ",
"_disp",
"_dsp",
"_d",
"_dm",
"_displacement",
"_height",
"_hm",
"_ht",
"_h"
],
"PixelFormat": "BC4",
"DiscardAlpha": true,
"IsPowerOf2": true,
@@ -33,18 +21,6 @@
"Name": "Displacement",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_displ",
"_disp",
"_dsp",
"_d",
"_dm",
"_displacement",
"_height",
"_hm",
"_ht",
"_h"
],
"PixelFormat": "ASTC_4x4",
"MaxTextureSize": 2048,
"DiscardAlpha": true,
@@ -59,18 +35,6 @@
"Name": "Displacement",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_displ",
"_disp",
"_dsp",
"_d",
"_dm",
"_displacement",
"_height",
"_hm",
"_ht",
"_h"
],
"PixelFormat": "ASTC_4x4",
"MaxTextureSize": 2048,
"DiscardAlpha": true,
@@ -84,18 +48,6 @@
"Name": "Displacement",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_displ",
"_disp",
"_dsp",
"_d",
"_dm",
"_displacement",
"_height",
"_hm",
"_ht",
"_h"
],
"PixelFormat": "BC4",
"DiscardAlpha": true,
"IsPowerOf2": true,
@@ -108,18 +60,6 @@
"Name": "Displacement",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_displ",
"_disp",
"_dsp",
"_d",
"_dm",
"_displacement",
"_height",
"_hm",
"_ht",
"_h"
],
"PixelFormat": "BC4",
"DiscardAlpha": true,
"IsPowerOf2": true,
@@ -7,13 +7,6 @@
"UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
"Name": "Emissive",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_emissive",
"_e",
"_glow",
"_em",
"_emit"
],
"PixelFormat": "BC7",
"DiscardAlpha": true
},
@@ -22,13 +15,6 @@
"UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
"Name": "Emissive",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_emissive",
"_e",
"_glow",
"_em",
"_emit"
],
"PixelFormat": "ASTC_6x6",
"MaxTextureSize": 2048,
"DiscardAlpha": true
@@ -37,13 +23,6 @@
"UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
"Name": "Emissive",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_emissive",
"_e",
"_glow",
"_em",
"_emit"
],
"PixelFormat": "ASTC_6x6",
"MaxTextureSize": 2048,
"DiscardAlpha": true
@@ -52,13 +31,6 @@
"UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
"Name": "Emissive",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_emissive",
"_e",
"_glow",
"_em",
"_emit"
],
"PixelFormat": "BC7",
"DiscardAlpha": true
},
@@ -66,13 +38,6 @@
"UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
"Name": "Emissive",
"RGB_Weight": "CIEXYZ",
"FileMasks": [
"_emissive",
"_e",
"_glow",
"_em",
"_emit"
],
"PixelFormat": "BC7",
"DiscardAlpha": true
}

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