Merge branch 'stabilization/2106' into mbalfour/gitflow_210622

# Conflicts:
#	Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp
#	Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h
#	Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp
monroegm-disable-blank-issue-2
mbalfour 5 years ago
commit df648db62e

@ -1,34 +0,0 @@
CryEngine tips of the day
You can toggle snap to grid by pressing G.
Ctrl+Shift+Clicking somewhere with an object selected quickly moves the object to that position when in move mode.
Pressing M will open the material editor.
Show and Hide helpers is bound to Shift + Space by default.
Enable AI/Physics is bound to Ctrl + P by default.
You can save a viewport location by pressing Ctrl + F1 through f12 and go to that position using Shift + F1 through F12.
You can link objects together by using the link command on the top menu of the editor.
Pressing 1 through 5 on the keyboard will cycle through brush operations such as move or scale.
You can simply bind keyboard shortcuts to editor functions by going to Tools --> Customize Keyboard.
Pressing H will hide the selected objects, Ctrl-H will unhide all hidden objects.
Pressing F will freeze the selected objects, Ctrl-F will unfreeze all frozen objects.
Pressing F3 will toggle wireframe view.
Camera/terrain collision can be toggled using Q.
You can restart the Editor by pressing the restart button on your PC.
Pressing Ctrl-C with an object selected will clone that object.
Toggle the console by pressing the tilde (~) key.
You can dock windows by dragging them onto the blue helpers that appear when you grab a window by the titlebar.
You can select materials by clicking on the dropper icon in the material editor and then clicking on the material you wish to select.
You can right click on the previewer in the material editor and change the model to different shapes and background colors.
Materials can be saved in the local level folder for re-distribution.
Always keep your level free of errors and immidiately fix errors reported by the error report screen when you load your level.
You must always export to engine before you can run it in pure game mode. (File --> Export to engine)
You must re-triangulate AI before playing your level in game mode. (AI --> Generate all navigation)
You must always re-generate surface textures after you finish painting the terrain. (File --> Regenerate surface textures)
Press Ctrl-G or F12 to go into the Game mode, ESC to return to Editing mode.
Quickly rebuild a level (without regenerating the ground texture) by pressing Ctrl-E.
Hold down the third mouse button and drag to move the camera up and down.
Missing objects are represented by a bright yellow sphere.
Hold Alt + Middle Mouse button to rotate around an object.
Select multiple objects by holding Ctrl.
You can place multiple instances of vegetation by holding Shift and clicking on the terrain.
A number of useful commands can be found in Tools --> User commands. This can also be dragged and docked to the main window.

@ -14679,6 +14679,25 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<translation></translation>
</message>
</context>
<context>
<name>Method: NetBindComponent</name>
<message id="NETBINDCOMPONENT_ISNETENTITYROLEAUTHORITY_TOOLTIP">
<source>NETBINDCOMPONENT_ISNETENTITYROLEAUTHORITY_TOOLTIP</source>
<translation>Returns true if this network entity is an authoritative proxy on a server (full authority); otherwise false.</translation>
</message>
<message id="NETBINDCOMPONENT_ISNETENTITYROLEAUTONOMOUS_TOOLTIP">
<source>NETBINDCOMPONENT_ISNETENTITYROLEAUTONOMOUS_TOOLTIP</source>
<translation>Returns true if this network entity is an autonomous proxy on a client (can execute local prediction) or if this network entity is an authoritative proxy on a server but has autonomous privileges (ie: a host who is also a player); otherwise false.</translation>
</message>
<message id="NETBINDCOMPONENT_ISNETENTITYROLECLIENT_TOOLTIP">
<source>NETBINDCOMPONENT_ISNETENTITYROLECLIENT_TOOLTIP</source>
<translation>Returns true if this network entity is a simulated proxy on a client; otherwise false.</translation>
</message>
<message id="NETBINDCOMPONENT_ISNETENTITYROLESERVER_TOOLTIP">
<source>NETBINDCOMPONENT_ISNETENTITYROLESERVER_TOOLTIP</source>
<translation>Returns true if this network entity is a simulated proxy on a server (ie: a different server may own this entity, but the entity has been replicated to this server; otherwise false.</translation>
</message>
</context>
<context>
<name>Method: Math</name>
<message id="MATH_NAME">

@ -21,6 +21,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/${PAL_PLATFORM_NAME}/
TIMEOUT 3000
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor

@ -18,11 +18,12 @@ import typing
from datetime import datetime
import ly_test_tools.log.log_monitor
# fixture imports
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.common.aws_utils import AwsUtils
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
from AWS.common.aws_utils import aws_utils
from AWS.common.aws_credentials import aws_credentials
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk import cdk
from .aws_metrics_utils import aws_metrics_utils
AWS_METRICS_FEATURE_NAME = 'AWSMetrics'
@ -32,7 +33,7 @@ logger = logging.getLogger(__name__)
def setup(launcher: ly_test_tools.launchers.Launcher,
cdk: cdk,
cdk: Cdk,
asset_processor: asset_processor,
resource_mappings: resource_mappings,
context_variable: str = '') -> typing.Tuple[ly_test_tools.log.log_monitor.LogMonitor, str, str]:
@ -116,18 +117,18 @@ def remove_file(file_path: str) -> None:
@pytest.mark.parametrize('region_name', ['us-west-2'])
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
class TestAWSMetrics_Windows(object):
def test_AWSMetrics_RealTimeAnalytics_MetricsSentToCloudWatch(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
asset_processor: pytest.fixture,
workspace: pytest.fixture,
aws_utils: aws_utils,
aws_credentials: aws_credentials,
resource_mappings: resource_mappings,
cdk: cdk,
aws_metrics_utils: aws_metrics_utils,
):
class TestAWSMetricsWindows(object):
def test_realtime_analytics_metrics_sent_to_cloudwatch(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
asset_processor: pytest.fixture,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
aws_credentials: aws_credentials,
resource_mappings: pytest.fixture,
cdk: pytest.fixture,
aws_metrics_utils: aws_metrics_utils,
):
"""
Tests that the submitted metrics are sent to CloudWatch for real-time analytics.
"""
@ -148,7 +149,7 @@ class TestAWSMetrics_Windows(object):
'AWS/Lambda',
'Invocations',
[{'Name': 'FunctionName',
'Value': f'{stack_name}-AnalyticsProcessingLambda'}],
'Value': f'{stack_name}-AnalyticsProcessingLambdaName'}],
start_time)
logger.info('Operational health metrics sent to CloudWatch.')
@ -162,14 +163,14 @@ class TestAWSMetrics_Windows(object):
# Stop the Kinesis Data Analytics application.
aws_metrics_utils.stop_kinesis_data_analytics_application(analytics_application_name)
def test_AWSMetrics_UnauthorizedUser_RequestRejected(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
cdk: cdk,
aws_credentials: aws_credentials,
asset_processor: pytest.fixture,
resource_mappings: resource_mappings,
workspace: pytest.fixture):
def test_unauthorized_user_request_rejected(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
cdk: pytest.fixture,
aws_credentials: aws_credentials,
asset_processor: pytest.fixture,
resource_mappings: pytest.fixture,
workspace: pytest.fixture):
"""
Tests that unauthorized users cannot send metrics events to the AWS backed backend.
"""
@ -187,14 +188,14 @@ class TestAWSMetrics_Windows(object):
assert result, 'Metrics events are sent successfully by unauthorized user'
logger.info('Unauthorized user is rejected to send metrics.')
def test_AWSMetrics_BatchAnalytics_MetricsDeliveredToS3(self,
def test_batch_analytics_metrics_delivered_to_s3(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
cdk: cdk,
cdk: pytest.fixture,
aws_credentials: aws_credentials,
asset_processor: pytest.fixture,
resource_mappings: resource_mappings,
aws_utils: aws_utils,
resource_mappings: pytest.fixture,
aws_utils: pytest.fixture,
aws_metrics_utils: aws_metrics_utils,
workspace: pytest.fixture):
"""
@ -234,4 +235,3 @@ class TestAWSMetrics_Windows(object):
time.sleep(60)
# Empty the S3 bucket. S3 buckets can only be deleted successfully when it doesn't contain any object.
aws_metrics_utils.empty_s3_bucket(analytics_bucket_name)

@ -12,6 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import os
import pytest
import boto3
import uuid
import logging
import subprocess
import botocore
import ly_test_tools.environment.process_utils as process_utils
from typing import List
@ -19,22 +23,71 @@ from typing import List
BOOTSTRAP_STACK_NAME = 'CDKToolkit'
BOOTSTRAP_STAGING_BUCKET_LOGIC_ID = 'StagingBucket'
logger = logging.getLogger(__name__)
class Cdk:
"""
Cdk class that provides methods to run cdk application commands.
Expects system to have NodeJS, AWS CLI and CDK installed globally and have their paths setup as env variables.
"""
def __init__(self, cdk_path: str, project: str, account_id: str,
workspace: pytest.fixture, session: boto3.session.Session):
def __init__(self):
self._cdk_env = ''
self._stacks = []
self._cdk_path = os.path.dirname(os.path.realpath(__file__))
self._session = ''
cdk_npm_latest_version_cmd = ['npm', 'view', 'aws-cdk', 'version']
output = process_utils.check_output(
cdk_npm_latest_version_cmd,
cwd=self._cdk_path,
shell=True)
cdk_npm_latest_version = output.split()[0]
cdk_version_cmd = ['cdk', 'version']
output = process_utils.check_output(
cdk_version_cmd,
cwd=self._cdk_path,
shell=True)
cdk_version = output.split()[0]
logger.info(f'Current CDK version {cdk_version}')
if cdk_version != cdk_npm_latest_version:
try:
logger.info(f'Updating CDK to latest')
# uninstall and reinstall cdk in case npm has been updated.
output = process_utils.check_output(
'npm uninstall -g aws-cdk',
cwd=self._cdk_path,
shell=True)
logger.info(f'Uninstall CDK output: {output}')
output = process_utils.check_output(
'npm install -g aws-cdk@latest',
cwd=self._cdk_path,
shell=True)
logger.info(f'Install CDK output: {output}')
except subprocess.CalledProcessError as error:
logger.warning(f'Failed reinstalling latest CDK on npm'
f'\nError:{error.stderr}')
def setup(self, cdk_path: str, project: str, account_id: str,
workspace: pytest.fixture, session: boto3.session.Session, bootstrap_required: bool):
"""
:param cdk_path: Path where cdk app.py is stored.
:param project: Project name used for cdk project name env variable.
:param account_id: AWS account id to use with cdk application.
:param workspace: ly_test_tools workspace fixture.
:param workspace: bootstrap_required deploys bootstrap stack.
"""
self._cdk_env = os.environ.copy()
self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project
unique_id = uuid.uuid4().hex[-4:]
self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project[:4] + unique_id if len(project) > 4 else project + unique_id
self._cdk_env['O3DE_AWS_DEPLOY_REGION'] = session.region_name
self._cdk_env['O3DE_AWS_DEPLOY_ACCOUNT'] = account_id
self._cdk_env['PATH'] = f'{workspace.paths.engine_root()}\\python;' + self._cdk_env['PATH']
@ -43,27 +96,37 @@ class Cdk:
self._cdk_env['AWS_ACCESS_KEY_ID'] = credentials.access_key
self._cdk_env['AWS_SECRET_ACCESS_KEY'] = credentials.secret_key
self._cdk_env['AWS_SESSION_TOKEN'] = credentials.token
self._stacks = []
self._cdk_path = cdk_path
self._session = session
output = process_utils.check_output(
'python -m pip install -r requirements.txt',
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
logger.info(f'Installing cdk python dependencies: {output}')
if bootstrap_required:
self.bootstrap()
def bootstrap(self) -> None:
"""
Deploy the bootstrap stack.
"""
bootstrap_cmd = ['cdk', 'bootstrap',
f'aws://{self._cdk_env["O3DE_AWS_DEPLOY_ACCOUNT"]}/{self._cdk_env["O3DE_AWS_DEPLOY_REGION"]}']
process_utils.check_call(
bootstrap_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
try:
bootstrap_cmd = ['cdk', 'bootstrap',
f'aws://{self._cdk_env["O3DE_AWS_DEPLOY_ACCOUNT"]}/{self._cdk_env["O3DE_AWS_DEPLOY_REGION"]}']
process_utils.check_call(
bootstrap_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
except botocore.exceptions.ClientError as clientError:
logger.warning(f'Failed creating Bootstrap stack {BOOTSTRAP_STACK_NAME} not found. '
f'\nError:{clientError["Error"]["Message"]}')
def list(self) -> List[str]:
"""
@ -131,83 +194,51 @@ class Cdk:
"""
Destroys the cdk application.
"""
destroy_cdk_application_cmd = ['cdk', 'destroy', '-f']
process_utils.check_output(
destroy_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
logger.info(f'CDK Path {self._cdk_path}')
destroy_cdk_application_cmd = ['cdk', 'destroy', '--all', '-f']
try:
process_utils.check_output(
destroy_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
except subprocess.CalledProcessError as e:
logger.error(e.output)
raise e
self._stacks = []
self._cdk_path = ''
@staticmethod
def remove_bootstrap_stack(aws_utils: pytest.fixture) -> None:
def remove_bootstrap_stack(self) -> None:
"""
Remove the CDK bootstrap stack.
:param aws_utils: aws_utils fixture.
"""
# Check if the bootstrap stack exists.
response = aws_utils.client('cloudformation').describe_stacks(
response = self._session.client('cloudformation').describe_stacks(
StackName=BOOTSTRAP_STACK_NAME
)
stacks = response.get('Stacks', [])
if not stacks:
if not stacks or len(stacks) is 0:
return
# Clear the bootstrap staging bucket before deleting the bootstrap stack.
response = aws_utils.client('cloudformation').describe_stack_resource(
response = self._session.client('cloudformation').describe_stack_resource(
StackName=BOOTSTRAP_STACK_NAME,
LogicalResourceId=BOOTSTRAP_STAGING_BUCKET_LOGIC_ID
)
staging_bucket_name = response.get('StackResourceDetail', {}).get('PhysicalResourceId', '')
if staging_bucket_name:
s3 = aws_utils.resource('s3')
s3 = self._session.resource('s3')
bucket = s3.Bucket(staging_bucket_name)
for key in bucket.objects.all():
key.delete()
# Delete the bootstrap stack.
aws_utils.client('cloudformation').delete_stack(
StackName=BOOTSTRAP_STACK_NAME
)
@pytest.fixture(scope='function')
def cdk(
request: pytest.fixture,
project: str,
feature_name: str,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
bootstrap_required: bool = True,
destroy_stacks_on_teardown: bool = True) -> Cdk:
"""
Fixture for setting up a Cdk
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param project: Project name used for cdk project name env variable.
:param feature_name: Feature gem name to expect cdk folder in.
:param workspace: ly_test_tools workspace fixture.
:param aws_utils: aws_utils fixture.
:param bootstrap_required: Whether the bootstrap stack needs to be created to
provision resources the AWS CDK needs to perform the deployment.
:param destroy_stacks_on_teardown: option to control calling destroy ot the end of test.
:return Cdk class object.
"""
cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk'
cdk_obj = Cdk(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session())
if bootstrap_required:
cdk_obj.bootstrap()
def teardown():
if destroy_stacks_on_teardown:
cdk_obj.destroy()
cdk_obj.remove_bootstrap_stack(aws_utils)
request.addfinalizer(teardown)
return cdk_obj
# Should not need to delete the stack if S3 bucket can be cleaned.
# self._session.client('cloudformation').delete_stack(
# StackName=BOOTSTRAP_STACK_NAME
# )

@ -12,9 +12,10 @@ import os
import logging
import ly_test_tools.log.log_monitor
# fixture imports
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk import cdk
from AWS.common.aws_utils import aws_utils
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.common.aws_utils import AwsUtils
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
AWS_PROJECT_NAME = 'AWS-AutomationTest'
@ -75,5 +76,5 @@ class TestAWSClientAuthAnonymousCredentials(object):
expected_lines=['(Script) - Success anonymous credentials'],
unexpected_lines=['(Script) - Fail anonymous credentials'],
halt_on_unexpected=True,
)
)
assert result, 'Anonymous credentials fetched successfully.'

@ -12,9 +12,10 @@ import os
import logging
import ly_test_tools.log.log_monitor
# fixture imports
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk import cdk
from AWS.common.aws_utils import aws_utils
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.common.aws_utils import AwsUtils
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
AWS_PROJECT_NAME = 'AWS-AutomationTest'

@ -8,11 +8,12 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import boto3
import pytest
import logging
logger = logging.getLogger(__name__)
logging.getLogger('boto').setLevel(logging.CRITICAL)
logging.getLogger('boto3').setLevel(logging.WARNING)
logging.getLogger('botocore').setLevel(logging.WARNING)
logging.getLogger('nose').setLevel(logging.WARNING)
class AwsUtils:
@ -63,28 +64,3 @@ class AwsUtils:
clears stored session
"""
self._assume_session = None
@pytest.fixture(scope='function')
def aws_utils(
request: pytest.fixture,
assume_role_arn: str,
session_name: str,
region_name: str):
"""
Fixture for AWS util functions
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials.
:param session_name: Session name to set.
:param region_name: AWS account region to set for session.
:return AWSUtils class object.
"""
aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name)
def teardown():
aws_utils_obj.destroy()
request.addfinalizer(teardown)
return aws_utils_obj

@ -0,0 +1,87 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import pytest
import logging
from AWS.common.aws_utils import AwsUtils
from AWS.Windows.cdk.cdk_utils import Cdk
logger = logging.getLogger(__name__)
@pytest.fixture(scope='function')
def aws_utils(
request: pytest.fixture,
assume_role_arn: str,
session_name: str,
region_name: str):
"""
Fixture for AWS util functions
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials.
:param session_name: Session name to set.
:param region_name: AWS account region to set for session.
:return AWSUtils class object.
"""
aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name)
def teardown():
aws_utils_obj.destroy()
request.addfinalizer(teardown)
return aws_utils_obj
# Set global pytest variable for cdk to avoid recreating instance
pytest.cdk_obj = None
@pytest.fixture(scope='function')
def cdk(
request: pytest.fixture,
project: str,
feature_name: str,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
bootstrap_required: bool = True,
destroy_stacks_on_teardown: bool = True) -> Cdk:
"""
Fixture for setting up a Cdk
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param project: Project name used for cdk project name env variable.
:param feature_name: Feature gem name to expect cdk folder in.
:param workspace: ly_test_tools workspace fixture.
:param aws_utils: aws_utils fixture.
:param bootstrap_required: Whether the bootstrap stack needs to be created to
provision resources the AWS CDK needs to perform the deployment.
:param destroy_stacks_on_teardown: option to control calling destroy ot the end of test.
:return Cdk class object.
"""
cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk'
logger.info(f'CDK Path {cdk_path}')
if pytest.cdk_obj is None:
pytest.cdk_obj = Cdk()
pytest.cdk_obj.setup(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session(),
bootstrap_required)
def teardown():
if destroy_stacks_on_teardown:
pytest.cdk_obj.destroy()
# Enable after https://github.com/aws/aws-cdk/issues/986 is fixed.
# Until then clean the bootstrap bucket manually.
# cdk_obj.remove_bootstrap_stack()
request.addfinalizer(teardown)
return pytest.cdk_obj

@ -60,5 +60,4 @@ add_subdirectory(streaming)
add_subdirectory(smoke)
## AWS ##
# Enable when AWS Gems work on Linux and Android.
# add_subdirectory(AWS)
add_subdirectory(AWS)

@ -79,8 +79,6 @@ class TestBasicEditorWorkflows(EditorTestHelper):
grp_box = new_level_dlg.findChild(QtWidgets.QGroupBox, "STATIC_GROUP1")
level_name = grp_box.findChild(QtWidgets.QLineEdit, "LEVEL")
level_name.setText(self.args["level"])
level_folders = grp_box.findChild(QtWidgets.QComboBox, "LEVEL_FOLDERS")
level_folders.setCurrentText("Levels/")
button_box = new_level_dlg.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()

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

@ -0,0 +1,6 @@
<download name="ClientAuth" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="ED0" md5="dbf5115226e4b0ea38ebdc3967ba3aa9"/>
</files>
</download>

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

@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0

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

@ -0,0 +1,6 @@
<download name="ClientAuthPasswordSignIn" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="DDF" md5="ebe91ae5f1ea1ec735b6650b14f3f95b"/>
</files>
</download>

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

@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0

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

@ -0,0 +1,6 @@
<download name="ClientAuthPasswordSignUp" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="DDA" md5="aa6df891d505d6d9175beee4b55626db"/>
</files>
</download>

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

@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0

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

@ -0,0 +1,79 @@
----------------------------------------------------------------------------------------------------
--
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
-- its licensors.
--
-- For complete copyright and license terms please see the LICENSE at the root of this
-- distribution (the "License"). All use of this software is governed by the License,
-- or, if provided, by the license below or the license accompanying this file. Do not
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--
--
----------------------------------------------------------------------------------------------------
local metrics = {
}
function metrics:OnActivate()
self.tickTime = 0
self.numSubmittedMetricsEvents = 0
self.tickBusHandler = TickBus.Connect(self,self.entityId)
self.metricsNotificationHandler = AWSMetricsNotificationBus.Connect(self, self.entityId)
LyShineLua.ShowMouseCursor(true)
end
function metrics:OnSendMetricsSuccess(requestId)
Debug.Log("Metrics is sent successfully.")
end
function metrics:OnSendMetricsFailure(requestId, errorMessage)
Debug.Log("Failed to send metrics.")
end
function metrics:OnDeactivate()
AWSMetricsRequestBus.Broadcast.FlushMetrics()
Debug.Log("Stop generating new test events and flushed the buffered metrics.")
self.tickBusHandler:Disconnect()
self.metricsNotificationHandler:Disconnect()
end
function metrics:OnTick(deltaTime, timePoint)
self.tickTime = self.tickTime + deltaTime
if self.tickTime > 2.0 then
defaultAttribute = AWSMetrics_MetricsAttribute()
defaultAttribute:SetName("event_name")
defaultAttribute:SetStrValue("login")
customAttribute = AWSMetrics_MetricsAttribute()
customAttribute:SetName("custom_attribute")
customAttribute:SetStrValue("value")
attributeList = AWSMetrics_AttributesSubmissionList()
attributeList.attributes:push_back(defaultAttribute)
attributeList.attributes:push_back(customAttribute)
if self.numSubmittedMetricsEvents % 2 == 0 then
if AWSMetricsRequestBus.Broadcast.SubmitMetrics(attributeList.attributes, 0, "lua", false) then
Debug.Log("Submitted metrics without buffer.")
else
Debug.Log("Failed to Submit metrics without buffer.")
end
else
if AWSMetricsRequestBus.Broadcast.SubmitMetrics(attributeList.attributes, 0, "lua", true) then
Debug.Log("Submitted metrics with buffer.")
else
Debug.Log("Failed to Submit metrics with buffer.")
end
end
self.numSubmittedMetricsEvents = self.numSubmittedMetricsEvents + 1
self.tickTime = 0
end
end
return metrics

@ -0,0 +1,6 @@
<download name="Metrics" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="E09" md5="f16fff2970a4037af5909b269ceece4b"/>
</files>
</download>

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

@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0

@ -57,7 +57,13 @@ namespace AZ
//! Determines if a component can be created from the asset type
//! This will be called before attempting to create a component from an asset (drag&drop, etc)
//! You can use this to filter by subIds or do your own validation here if needed
virtual bool CanCreateComponent(const AZ::Data::AssetId& /*assetId*/) const { return true; }
virtual bool CanCreateComponent([[maybe_unused]] const AZ::Data::AssetId& assetId) const { return true; }
//! Determines if other products conflict with the given one when multiple are generated from a source asset.
//! This will be called before attempting to create a component from an asset (drag&drop, etc)
//! You can use this to filter by conflicting product types or in case you want to skip for UX reasons.
//! @param[in] productAssetTypes Asset types of all generated products, including the one for our given type in this bus.
virtual bool HasConflictingProducts([[maybe_unused]] const AZStd::vector<AZ::Data::AssetType>& productAssetTypes) const { return false; }
};
using AssetTypeInfoBus = AZ::EBus<AssetTypeInfo>;

@ -1206,7 +1206,7 @@ namespace JsonSerializationTests
if (this->m_features.m_enableInitializationTest)
{
auto instance = this->m_description.CreateDefaultInstance();
typename TypeParam::Type compare;
AZStd::remove_cvref_t<typename TypeParam::Type> compare;
if (!this->m_description.AreEqual(*instance, compare))
{
auto serializer = this->m_description.CreateSerializer();

@ -75,6 +75,8 @@ namespace AzFramework
// Add a fullscreen button in the upper right of the title bar.
[m_nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
m_nativeWindow.tabbingMode = NSWindowTabbingModeDisallowed;
// Make the window active
[m_nativeWindow makeKeyAndOrderFront:nil];
m_nativeWindow.title = m_windowTitle;

@ -44,6 +44,11 @@ namespace AzNetworking
//! @class IConnection
//! @brief interface class for network connections.
//!
//! IConnection provides a pure-virtual interface for all network connection types. IConnections provide access to
//! a ConnectionMetrics object which provides a variety of metrics on the connection itself such as data rate, RTT and
//! packet statistics.
class IConnection
{
public:

@ -22,6 +22,12 @@ namespace AzNetworking
{
//! @class IConnectionListener
//! @brief interface class for application layer dealing with connection level events.
//!
//! IConnectionListener defines an abstract interface that the user of AzNetworking is expected to implement to react and
//! handle all IConnection related events, including the handling of any received IPacket derived packets. The AzNetworking
//! user should derive a handler class from IConnectionListener, and provide an instance of that handler to any
//! INetworkInterface the user instantiates. The lifetime of the IConnectionListener must outlive the lifetime of the
//! INetworkInterface.
class IConnectionListener
{
public:

@ -18,6 +18,11 @@ namespace AzNetworking
{
//! @class IConnectionSet
//! @brief interface class for managing a set of connections.
//!
//! IConnectionSet defines a simple interface for working with an abstract set of IConnections bound to an
//! INetworkInterface. Generally users of AzNetworking will not have reason to interact directly with the IConnectionSet,
//! as its interface is completely wrapped by INetworkInterface.
class IConnectionSet
{
public:

@ -23,10 +23,10 @@ namespace AzNetworking
//! Collection of compression related error codes
enum class CompressorError
{
Ok, ///< No error, operation finished successfully
InsufficientBuffer, ///< Buffer size is insufficient for the operation to complete, increase the size and try again
CorruptData, ///< Malformed or hacked packet, potentially security issue
Uninitialized ///< Compressor or supplied buffers are uninitialized
Ok, //!< No error, operation finished successfully
InsufficientBuffer, //!< Buffer size is insufficient for the operation to complete, increase the size and try again
CorruptData, //!< Malformed or hacked packet, potentially security issue
Uninitialized //!< Compressor or supplied buffers are uninitialized
};
//! Unique identifier of a given compressor
@ -34,6 +34,12 @@ namespace AzNetworking
//! @class ICompressor
//! @brief Packet data compressor interface.
//!
//! ICompressor is an abstract compression interface meant for user provided GEMs to implement (such as the [Multiplayer
//! Compression Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression)).
//! Compression is supported for both TCP and UDP connections. Instantiation of a compressor is controlled by the
//! `net_UdpCompressor` or `net_TcpCompressor` cvar for their respective protocols.
class ICompressor
{
public:
@ -87,8 +93,16 @@ namespace AzNetworking
) = 0;
};
//! Abstract factory to instantiate compressors.
//! Used by the network interface to create a compressor
//! @class ICompressorFactory
//! @brief Abstract factory to instantiate compressors.
//!
//! ICompressorFactory is an abstract compression interface meant for user provided GEMs to implement. ICompressorFactory
//! implementations can be registered to classes implementing INetworking. Registered factories can then be used to create
//! ICompressor implementations on demand. The [Multiplayer Compression
//! Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression) is an example of an ICompressorFactory
//! for an LZ4 Compressor. In it, MultiplayerCompressionSystemComponent registers its ICompressorFactory with
//! NetworkingSystemComponent, which is an implementation of INetworking. Registered factories are keyed by their AZ Name
//! which is accessed through the factory's GetFactoryName method.
class ICompressorFactory
{
public:

@ -22,7 +22,16 @@
namespace AzNetworking
{
//! @class INetworkInterface
//! @brief pure virtual network interface class to abstract client/server and tcp/udp concerns from application code.
//! @brief Network interface class to abstract client/server and protocol concerns from application code.
//!
//! INetworkInterface provides an abstract API capable of receiving and opening IConnection objects, sending IPacket objects with optional
//! reliability, and determining the delivery status of packets that have been sent unreliably (delivery of reliable packets
//! is guaranteed as long as the associated connection remains open). INetworkInterface must be provided an
//! IConnectionListener instance that outlives the INetworkInterface itself. The INetworkInterface also creates and manages
//! the IConnectionSet, which tracks all open connections bound to the interface. INetworkInterface also provides GetMetrics
//! functions which can be used to fetch a struct detailing a variety of metrics relating to send and receive rates for both
//! packets and bytes in addition to the effect of features on those rates (such as packet size reduction due to compression.)
class INetworkInterface
{
public:

@ -23,6 +23,17 @@ namespace AzNetworking
//! @class INetworking
//! @brief The interface for creating and working with network interfaces.
//!
//! INetworking is an Az::Interface<T> that provides applications access to higher level networking abstractions.
//! AzNetworking::INetworking can be used to instantiate new INetworkInterfaces that can be configured to operate over
//! either TCP or UDP, enable or disable encryption, and be assigned a trust level.
//!
//! INetworking is also responsible for registering ICompressorFactory implementations. This allows a developer to have
//! access to multiple ICompressorFactory implementations by name. The [MultiplayerCompressor
//! Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression) is an example of this using the
//! [LZ4](https://wikipedia.org/wiki/LZ4_%28compression_algorithm%29) algorithm.
//!
class INetworking
{
public:

@ -24,6 +24,15 @@ namespace AzNetworking
//! @class IPacket
//! @brief Base class for all packets.
//!
//! IPacket defines an abstract interface that all packets transmitted using AzNetworking must conform to. While there are
//! a number of core packets used internally by AzNetworking, it is fully possible for end-users to define their own custom
//! packets using this interface. PacketType should be distinct, and should be greater than
//! AzNetworking::CorePackets::MAX. The Serialize method allows the IPacket to be used by an
//! ISerializer to move data between hosts safely and efficiently.
//!
//! For more information on the packet format and best practices for extending the packet system, read
//! [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) on the O3DE documentation site.
class IPacket
{
public:

@ -28,6 +28,19 @@ namespace AzNetworking
//! @class IPacketHeader
//! @brief A packet header that lets us deduce packet type for any incoming packet.
//!
//! IPacketHeader defines an abstract interface for a descriptor of all AzNetworking::IPacket sent through AzNetworking. The
//! PacketHeader is used to identify and describe the contents of a Packet so that transport logic can identify what
//! additional processing steps need to be taken (if any) and what type of Packet is being inspected.
//!
//! The PacketFlags portion of the header represents the first byte of the header. While it can be encrypted it is
//! otherwise not exposed to additional processing (such as an AzNetworking::ICompressor). PacketFlags are a bitfield use to provide up
//! front information about the state of the packet. Currently there is only one flag to indicate if the Packet is
//! compressed or not.
//!
//! The remainder of the header contains the PacketType and the PacketId. While the PacketFlags byte is exempt from most
//! additional forms of processing, the remainder of the header is not.
class IPacketHeader
{
public:

@ -27,6 +27,18 @@ namespace AzNetworking
//! @class ISerializer
//! @brief Interface class for all serializers to derive from.
//!
//! ISerializer defines an abstract interface for visiting an object hierarchy and performing operations upon that hierarchy,
//! typically reading from or writing data to the object hierarchy for reasons of persistence or network transmission.
//!
//! While the most common types of serializers are provided by the AzNetworking framework, users can implement custom
//! serializers and perform complex operations on any serializable structures. A few types native to AzNetworking, many of which
//! relate to packets, demonstrate this.
//!
//! Provided serializers include NetworkInputSerializer for writing an object model into a bytestream, NetworkOutputSerializer
//! for writing to an object model, TrackChangesSerializer which is used to efficiently serialize objects without incurring significant
//! copy or comparison overhead, and HashSerializer which can be used to generate a hash of all visited data which is important for
//! automated desync detection.
class ISerializer
{
public:

@ -25,6 +25,44 @@ namespace AzNetworking
//! @class TcpNetworkInterface
//! @brief This class implements a TCP network interface.
//!
//! TcpNetworkInterface is an implementation of AzNetworking::INetworkInterface.
//! Unlike UDP, TCP implements a variety of transport features such as congestion
//! avoidance, flow control, and reliability. These features are valuable, but TCP
//! offers minimal configuration of them. This is why UdpNetworkInterface offers
//! similar features, but with greater flexibility in configuration. If your project doesn't
//! require the low latency of UDP, consider using TCP.
//!
//! ## Packet structure
//!
//! * Flags - A bitfield a receiving endpoint can quickly inspect to learn about configuration of a packet
//! * Header - Details the type of packet and other information related to reliability
//! * Payload - The actual serialized content of the packet
//!
//! For more information, read [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) in the O3DE documentation.
//!
//! ## Reliability
//!
//! TCP packets can only be sent reliably. This is a feature of TCP itself.
//!
//! ## Fragmentation
//!
//! TCP implements fragmentation under the hood. Consumers of TCP packets will never
//! need to worry about reconstructing the contents over multiple transmissions.
//!
//! ## Compression
//!
//! Compression here refers to content insensitive compression using libraries like
//! LZ4. If enabled, the target payload is run through the compressor and replaces
//! the original payload if it's in fact smaller. To tell if compression is enabled
//! on a given packet, we operate on a bit in the packet's Flags. The Sender writes
//! this bit while the Receiver checks it to see if a packet needs to be
//! decompressed.
//!
//! ## Encryption
//!
//! AzNetworking uses the [OpenSSL](https://www.openssl.org/) library to implement TLS encryption. If enabled,
//! the O3DE network layer handles the OpenSSL handshake under the hood using provided certificates.
class TcpNetworkInterface final
: public INetworkInterface
{

@ -27,12 +27,58 @@ namespace AzNetworking
class IConnectionListener;
class ICompressor;
// 20 byte IPv4 header + 8 byte UDP header
static const uint32_t UdpPacketHeaderSize = 20 + 8;
static const uint32_t DtlsPacketHeaderSize = 13; // DTLS1_RT_HEADER_LENGTH
static const uint32_t UdpPacketHeaderSize = 20 + 8; //!< 20 byte IPv4 header + 8 byte UDP header
static const uint32_t DtlsPacketHeaderSize = 13; //!< DTLS1_RT_HEADER_LENGTH
//! @class UdpNetworkInterface
//! @brief This class implements a UDP network interface.
//!
//! UdpNetworkInterface is an implementation of AzNetworking::INetworkInterface. Since UDP is a very bare bones protocol,
//! the Open 3D Engine implementation has to provide significantly more than its TCP counterpart (since TCP implements a
//! significant number of reliability features.)
//!
//! When sent through UDP, a packet can have additional actions performed on it depending on which features are enabled and
//! configured. Each feature listed in this description is in the order a packet will see them on Send.
//!
//! ### Packet structure
//!
//! The general structure of a UDP packet is:
//!
//! * Flags - A bitfield a receiving endpoint can quickly inspect to learn about configuration of a packet
//! * Header - Details the type of packet and other information related to reliability
//! * Payload - The actual serialized content of the packet
//!
//! For more information, read [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) in the O3DE documentation.
//!
//! ### Reliability
//!
//! UDP packets can be sent reliably or unreliably. Reliably sent packets are registered for tracking first. This causes the
//! reliable packet to be resent if a timeout on the packet is reached. Once the packet is acknowledged, the packet is
//! unregistered.
//!
//! ### Fragmentation
//!
//! If the raw packet size exceeds the configured maximum transmission unit (MTU) then the packet is broken into
//! multiple reliable fragments to avoid fragmentation at the routing level. Fragments are always reliable so the original
//! packet can be reconstructed. Operations that alter the payload generally follow this step so that they can be
//! separately applied to the Fragments in addition to not being applied to both the original and Fragments.
//!
//! ### Compression
//!
//! Compression here refers to content insensitive compression using libraries like LZ4. If enabled, the target payload is
//! run through the compressor and replaces the original payload if it's in fact smaller. To tell if compression is enabled
//! on a given packet, we operate on a bit in the packet's Flags. The Sender writes this bit while the Receiver checks it to
//! see if a packet needs to be decompressed.
//!
//! O3DE could potentially move from over MTU to under with compression, and the UDP interface doesn't check for this. Detecting a change
//! that would reduce the number of fragmented packets would require pre-emptively compressing payloads to tell if that change happened,
//! which could potentially lead to a lot of unnecessary calls to the compressor.
//!
//! ### Encryption
//!
//! AzNetworking uses the [OpenSSL](https://www.openssl.org/) library to implement Datagram Layer Transport Security (DTLS) encryption
//! on UDP traffic. Encryption operates as described in [O3DE Networking Encryption](http://docs.o3de.org/docs/user-guide/networking/encryption)
//! on the documentation website. Once both endpoints have completed their handshake, all traffic is expected to be fully encrypted.
class UdpNetworkInterface final
: public INetworkInterface
{

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Helpers Icon</title>
<defs>
<circle id="path-1" cx="8" cy="8" r="8"></circle>
<path d="M8.59736328,10.1821289 L8.59736328,9.79394531 C8.59736328,9.07617188 8.86835937,8.71728516 9.84248047,8.15332031 C10.8751953,7.54541016 11.4098633,6.77636719 11.4098633,5.67041016 C11.4098633,4.00048828 10.0255859,2.8359375 7.93085937,2.8359375 C5.68232422,2.8359375 4.40791016,4.09570312 4.37128906,5.89746094 L6.35615234,5.89746094 C6.40009766,5.06982422 6.95673828,4.53515625 7.79902344,4.53515625 C8.63398437,4.53515625 9.190625,5.04052734 9.190625,5.73632812 C9.190625,6.43212891 8.90498047,6.79101562 7.96015625,7.35498047 C6.94941406,7.94824219 6.54658203,8.60742188 6.64179687,9.75732422 L6.65644531,10.1821289 L8.59736328,10.1821289 Z M7.70380859,13.7124023 C8.52412109,13.7124023 9.02949219,13.2436523 9.02949219,12.4892578 C9.02949219,11.7275391 8.52412109,11.2587891 7.70380859,11.2587891 C6.88349609,11.2587891 6.37080078,11.7275391 6.37080078,12.4892578 C6.37080078,13.2436523 6.88349609,13.7124023 7.70380859,13.7124023 Z" id="path-3"></path>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="2nd-ToolBar-v2-(Perspective)-3" transform="translate(-1161.000000, -7.000000)">
<g id="2nd-ToolBar-buttons-on-right" transform="translate(1076.000000, 5.000000)">
<g id="Helpers-Icon" transform="translate(85.000000, 2.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Oval" fill="#FFFFFF" xlink:href="#path-1"></use>
<g id="?" fill-rule="nonzero" mask="url(#mask-2)">
<use fill="#909090" xlink:href="#path-3"></use>
<use fill="#3F3F3F" xlink:href="#path-3"></use>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Buttons / Dropdown button with Icon / no arrow</title>
<g id="Buttons-/-Dropdown-button-with-Icon-/-no-arrow" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Icons-/-System-/-Menu" fill="#FFFFFF">
<rect id="Rectangle-11" x="1.33333333" y="2.66666667" width="13.3333333" height="1.33333333"></rect>
<rect id="Rectangle-11" x="1.33333333" y="7.33333333" width="13.3333333" height="1.33333333"></rect>
<rect id="Rectangle-11" x="1.33333333" y="12" width="13.3333333" height="1.33333333"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 786 B

@ -28,5 +28,7 @@
<file alias="script_canvas_editor.svg">Menu/script_canvas_editor.svg</file>
<file alias="trackview_editor.svg">Menu/trackview_editor.svg</file>
<file alias="ui_editor.svg">Menu/ui_editor.svg</file>
<file alias="menu.svg">Menu/menu.svg</file>
<file alias="helpers.svg">Menu/helpers.svg</file>
</qresource>
</RCC>

@ -524,8 +524,6 @@ namespace AzToolsFramework
rootSpawnableIndex = m_playInEditorData.m_assets.size();
}
LoadReferencedAssets(product.GetReferencedAssets());
AZ::Data::AssetInfo info;
info.m_assetId = product.GetAsset().GetId();
info.m_assetType = product.GetAssetType();
@ -534,6 +532,19 @@ namespace AzToolsFramework
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
// Ensure the product asset is registered with the AssetManager
// Hold on to the returned asset to keep ref count alive until we assign it the latest data
AZ::Data::Asset<AZ::Data::AssetData> asset =
AZ::Data::AssetManager::Instance().FindOrCreateAsset(info.m_assetId, info.m_assetType, AZ::Data::AssetLoadBehavior::Default);
// Update the asset registered in the AssetManager with the data of our product from the Prefab Processor
AZ::Data::AssetManager::Instance().AssignAssetData(m_playInEditorData.m_assets.back());
}
for (auto& product : context.GetProcessedObjects())
{
LoadReferencedAssets(product.GetReferencedAssets());
}
// make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of

@ -326,6 +326,16 @@ namespace AzToolsFramework
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));
}
void Instance::DetachNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>)>& callback)
{
for (auto&& [instanceAlias, instance] : m_nestedInstances)
{
instance->m_parent = nullptr;
callback(AZStd::move(instance));
}
m_nestedInstances.clear();
}
AZStd::unique_ptr<Instance> Instance::DetachNestedInstance(const InstanceAlias& instanceAlias)
{
AZStd::unique_ptr<Instance> removedNestedInstance;

@ -103,6 +103,7 @@ namespace AzToolsFramework
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
void DetachNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>)>& callback);
/**
* Gets the aliases for the entities in the Instance DOM.

@ -151,6 +151,10 @@ namespace AzToolsFramework
return InvalidTemplateId;
}
// Add or replace the Source parameter in the dom
PrefabDomPath sourcePath = PrefabDomPath((AZStd::string("/") + PrefabDomUtils::SourceName).c_str());
sourcePath.Set(readPrefabFileResult.GetValue(), relativePath.Native().c_str());
// Create new Template with the Prefab DOM.
TemplateId newTemplateId = m_prefabSystemComponentInterface->AddTemplate(relativePath, readPrefabFileResult.TakeValue());
if (newTemplateId == InvalidTemplateId)

@ -922,8 +922,8 @@ namespace AzToolsFramework
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
}
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
// cannot duplicate an instance from itself.
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance
// This is because containers, despite representing the nested instance in the parent, are owned by the child.
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
{
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
@ -967,17 +967,18 @@ namespace AzToolsFramework
// Duplicate any nested entities and instances as requested
AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
AZStd::unordered_map<EntityAlias, EntityAlias> duplicateEntityAliasMap;
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
entities, instanceDomAfter, duplicatedEntityAndInstanceIds);
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
instances, instanceDomAfter, duplicatedEntityAndInstanceIds,
newInstanceAliasToOldInstanceMap);
entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap);
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
command->SetParent(undoBatch.GetUndoBatch());
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
command->Redo();
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
// Create links for our duplicated instances (if any were duplicated)
for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
{
@ -995,8 +996,35 @@ namespace AzToolsFramework
PrefabDom linkPatchesCopy;
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
m_prefabSystemComponentInterface->CreateLink(
commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy);
// If the instance was duplicated as part of an ancestor's nested hierarchy, the container's parent patch
// will need to be refreshed to point to the new duplicated parent entity
auto oldInstanceContainerEntityId = oldInstance->GetContainerEntityId();
AZ_Assert(oldInstanceContainerEntityId.IsValid(), "Instance returned invalid Container Entity Id");
AZ::EntityId previousParentEntityId;
AZ::TransformBus::EventResult(previousParentEntityId, oldInstanceContainerEntityId, &AZ::TransformBus::Events::GetParentId);
if (previousParentEntityId.IsValid() && AZStd::find(duplicatedEntityAndInstanceIds.begin(), duplicatedEntityAndInstanceIds.end(), previousParentEntityId))
{
auto oldParentAlias = commonOwningInstance->get().GetEntityAlias(previousParentEntityId);
if (oldParentAlias.has_value() && duplicateEntityAliasMap.contains(oldParentAlias->get()))
{
// Get the dom into a QString for search/replace purposes
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
linkPatchesCopy.Accept(writer);
QString linkPatchesString(buffer.GetString());
ReplaceOldAliases(linkPatchesString, oldParentAlias->get(), duplicateEntityAliasMap[oldParentAlias->get()]);
linkPatchesCopy.Parse(linkPatchesString.toUtf8().constData());
}
}
PrefabUndoHelpers::CreateLink(
oldInstance->GetTemplateId(), commonOwningInstance->get().GetTemplateId(),
AZStd::move(linkPatchesCopy), newInstanceAlias, undoBatch.GetUndoBatch());
}
// Select the duplicated entities/instances
@ -1211,25 +1239,23 @@ namespace AzToolsFramework
const auto instanceTemplateId = instancePtr->GetTemplateId();
auto parentContainerEntityId = parentInstance.GetContainerEntityId();
instancePtr->GetNestedInstances(
[&](AZStd::unique_ptr<Instance>& nestedInstancePtr)
{
//get previous link patch
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstancePtr->GetLinkId());
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
AZ_Assert(
linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.",
nestedInstancePtr->GetLinkId());
PrefabDom linkPatchesCopy;
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
instancePtr->DetachNestedInstances(
[&](AZStd::unique_ptr<Instance> detachedNestedInstance)
{
PrefabDom& nestedInstanceTemplateDom =
m_prefabSystemComponentInterface->FindTemplateDom(detachedNestedInstance->GetTemplateId());
RemoveLink(nestedInstancePtr, instanceTemplateId, undoBatch.GetUndoBatch());
Instance& nestedInstanceUnderNewParent = parentInstance.AddInstance(AZStd::move(detachedNestedInstance));
UpdateLinkPatchesWithNewEntityAliases(linkPatchesCopy, oldEntityAliases, parentInstance);
PrefabDom nestedInstanceDomUnderNewParent;
m_instanceToTemplateInterface->GenerateDomForInstance(
nestedInstanceDomUnderNewParent, nestedInstanceUnderNewParent);
PrefabDom reparentPatch;
m_instanceToTemplateInterface->GeneratePatch(
reparentPatch, nestedInstanceTemplateDom, nestedInstanceDomUnderNewParent);
CreateLink(*nestedInstancePtr, parentTemplateId, undoBatch.GetUndoBatch(),
AZStd::move(linkPatchesCopy), true);
CreateLink(nestedInstanceUnderNewParent, parentTemplateId, undoBatch.GetUndoBatch(), AZStd::move(reparentPatch), true);
});
}
@ -1509,14 +1535,13 @@ namespace AzToolsFramework
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
EntityIdList& duplicatedEntityIds)
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap)
{
if (entities.empty())
{
return;
}
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
for (AZ::Entity* entity : entities)

@ -87,7 +87,7 @@ namespace AzToolsFramework
*/
void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
EntityIdList& duplicatedEntityIds);
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap);
/**
* Duplicate a list of instances owned by a common owning instance by directly
* copying/modifying their entries in the instance DOM

@ -207,6 +207,10 @@ namespace AzToolsFramework
instanceValue->CopyFrom(linkDom, m_prefabDom.GetAllocator());
}
// Remove Source parameter from the dom. It will be added on file load, and should not be stored to disk.
PrefabDomPath sourcePath = PrefabDomPath((AZStd::string("/") + PrefabDomUtils::SourceName).c_str());
sourcePath.Erase(output);
return true;
}

@ -13,6 +13,7 @@
#include <AzCore/Math/ToString.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
@ -57,6 +58,13 @@ namespace AzToolsFramework
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->ConstantProperty("EditorNonUniformScaleComponentTypeId", BehaviorConstant(EditorNonUniformScaleComponent::RTTI_Type()))
->Attribute(AZ::Script::Attributes::Module, "editor")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
}
}
void EditorNonUniformScaleComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)

@ -214,7 +214,11 @@ namespace AzToolsFramework
class ViewportSettingNotifications
{
public:
virtual void OnGridSnappingChanged(bool enabled) = 0;
virtual void OnGridSnappingChanged([[maybe_unused]] bool enabled) {}
virtual void OnDrawHelpersChanged([[maybe_unused]] bool enabled) {}
protected:
ViewportSettingNotifications() = default;
};
using ViewportSettingsNotificationBus = AZ::EBus<ViewportSettingNotifications, ViewportEBusTraits>;

@ -56,7 +56,8 @@ namespace AzAssetBrowserRequestHandlerPrivate
using namespace AzToolsFramework;
using namespace AzToolsFramework::AssetBrowser;
// return true ONLY if we can handle the drop request in the viewport.
bool CanSpawnEntityForProduct(const ProductAssetBrowserEntry* product)
bool CanSpawnEntityForProduct(const ProductAssetBrowserEntry* product,
AZStd::optional<const AZStd::vector<AZ::Data::AssetType>> optionalProductAssetTypes = AZStd::nullopt)
{
if (!product)
{
@ -70,7 +71,6 @@ namespace AzAssetBrowserRequestHandlerPrivate
bool canCreateComponent = false;
AZ::AssetTypeInfoBus::EventResult(canCreateComponent, product->GetAssetType(), &AZ::AssetTypeInfo::CanCreateComponent, product->GetAssetId());
if (!canCreateComponent)
{
return false;
@ -78,16 +78,25 @@ namespace AzAssetBrowserRequestHandlerPrivate
AZ::Uuid componentTypeId = AZ::Uuid::CreateNull();
AZ::AssetTypeInfoBus::EventResult(componentTypeId, product->GetAssetType(), &AZ::AssetTypeInfo::GetComponentTypeId);
if (!componentTypeId.IsNull())
if (componentTypeId.IsNull())
{
// we have a component type that handles this asset.
return true;
return false;
}
if (optionalProductAssetTypes.has_value())
{
bool hasConflictingProducts = false;
AZ::AssetTypeInfoBus::EventResult(hasConflictingProducts, product->GetAssetType(), &AZ::AssetTypeInfo::HasConflictingProducts, optionalProductAssetTypes.value());
if (hasConflictingProducts)
{
return false;
}
}
// additional operations can be added here.
return false;
return true;
}
void SpawnEntityAtPoint(const ProductAssetBrowserEntry* product, AzQtComponents::ViewportDragContext* viewportDragContext, EntityIdList& spawnList, AzFramework::SliceInstantiationTicket& spawnTicket)
@ -511,9 +520,16 @@ void AzAssetBrowserRequestHandler::Drop(QDropEvent* event, AzQtComponents::DragA
}
// Handle products
AZStd::vector<AZ::Data::AssetType> productAssetTypes;
productAssetTypes.reserve(products.size());
for (const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* entry : products)
{
productAssetTypes.emplace_back(entry->GetAssetType());
}
for (const ProductAssetBrowserEntry* product : products)
{
if (CanSpawnEntityForProduct(product))
if (CanSpawnEntityForProduct(product, productAssetTypes))
{
SpawnEntityAtPoint(product, viewportDragContext, spawnedEntities, spawnTicket);
}

@ -128,6 +128,7 @@ ly_add_target(
Legacy::EditorCore
RUNTIME_DEPENDENCIES
Gem::AtomViewportDisplayInfo
Legacy::EditorCommon
)
ly_add_source_properties(
SOURCES CryEdit.cpp

@ -33,13 +33,13 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Amazon.com, Inc."
VALUE "FileDescription", "Lumberyard Editor"
VALUE "CompanyName", "Open 3D Foundation"
VALUE "FileDescription", "O3DE Editor"
VALUE "FileVersion", "0.1.0.1"
VALUE "InternalName", "Editor"
VALUE "LegalCopyright", "Portions of this file Copyright (c) Amazon.com, Inc. or its affiliates. All Rights Reserved. Original file Copyright (c) Crytek GMBH. Used under license by Amazon.com, Inc. and its affiliates."
VALUE "OriginalFilename", "Editor.exe"
VALUE "ProductName", "Lumberyard Editor"
VALUE "ProductName", "O3DE Editor"
VALUE "ProductVersion", "0.1.0.1"
END
END

@ -1,460 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "ImageHDR.h"
// Editor
#include "Util/Image.h"
// We need globals because of the callbacks (they don't allow us to pass state)
static CryMutex globalFileMutex;
static size_t globalFileBufferOffset = 0;
static size_t globalFileBufferSize = 0;
static char* fgets(char* _Buf, [[maybe_unused]] int _MaxCount, CCryFile* _File)
{
while (globalFileBufferOffset < globalFileBufferSize)
{
char chr;
_File->ReadRaw(&chr, 1);
globalFileBufferOffset++;
*_Buf++ = chr;
if (chr == '\n')
{
break;
}
}
*_Buf = '\0';
return _Buf;
}
static size_t fread(void* _DstBuf, size_t _ElementSize, size_t _Count, CCryFile* _File)
{
size_t cpy = min(_ElementSize * _Count, globalFileBufferSize - globalFileBufferOffset);
_File->ReadRaw(_DstBuf, cpy);
globalFileBufferOffset += cpy;
return cpy;
}
/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
* WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
* IT IS STRICTLY USE AT YOUR OWN RISK. */
/* utility for reading and writing Ward's rgbe image format.
See rgbe.txt file for more details.
*/
#include <stdio.h>
typedef struct
{
int valid; /* indicate which fields are valid */
char programtype[16]; /* listed at beginning of file to identify it
* after "#?". defaults to "RGBE" */
float gamma; /* image has already been gamma corrected with
* given gamma. defaults to 1.0 (no correction) */
float exposure; /* a value of 1.0 in an image corresponds to
* <exposure> watts/steradian/m^2.
* defaults to 1.0 */
char instructions[512];
} rgbe_header_info;
/* flags indicating which fields in an rgbe_header_info are valid */
#define RGBE_VALID_PROGRAMTYPE 0x01
#define RGBE_VALID_GAMMA 0x02
#define RGBE_VALID_EXPOSURE 0x04
#define RGBE_VALID_INSTRUCTIONS 0x08
/* return codes for rgbe routines */
#define RGBE_RETURN_SUCCESS 0
#define RGBE_RETURN_FAILURE -1
/* read or write headers */
/* you may set rgbe_header_info to null if you want to */
int RGBE_ReadHeader(CCryFile* fp, uint32* width, uint32* height, rgbe_header_info* info);
/* read or write pixels */
/* can read or write pixels in chunks of any size including single pixels*/
int RGBE_ReadPixels(CCryFile* fp, float* data, int numpixels);
/* read or write run length encoded files */
/* must be called to read or write whole scanlines */
int RGBE_ReadPixels_RLE(CCryFile* fp, float* data, uint32 scanline_width,
uint32 num_scanlines);
/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
* WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
* IT IS STRICTLY USE AT YOUR OWN RISK. */
#include <math.h>
#include <string.h>
#include <ctype.h>
/* This file contains code to read and write four byte rgbe file format
developed by Greg Ward. It handles the conversions between rgbe and
pixels consisting of floats. The data is assumed to be an array of floats.
By default there are three floats per pixel in the order red, green, blue.
(RGBE_DATA_??? values control this.) Only the mimimal header reading and
writing is implemented. Each routine does error checking and will return
a status value as defined below. This code is intended as a skeleton so
feel free to modify it to suit your needs.
(Place notice here if you modified the code.)
posted to http://www.graphics.cornell.edu/~bjw/
written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95
based on code written by Greg Ward
*/
#ifndef INLINE
#ifdef _CPLUSPLUS
/* define if your compiler understands inline commands */
#define INLINE inline
#else
#define INLINE
#endif
#endif
/* offsets to red, green, and blue components in a data (float) pixel */
#define RGBE_DATA_RED 0
#define RGBE_DATA_GREEN 1
#define RGBE_DATA_BLUE 2
#define RGBE_DATA_ALPHA 3
/* number of floats per pixel */
#define RGBE_DATA_SIZE 4
enum rgbe_error_codes
{
rgbe_read_error,
rgbe_write_error,
rgbe_format_error,
rgbe_memory_error,
};
/* default error routine. change this to change error handling */
static int rgbe_error(int rgbe_error_code, const char* msg)
{
switch (rgbe_error_code)
{
case rgbe_read_error:
CLogFile::FormatLine("RGBE read error");
break;
case rgbe_write_error:
CLogFile::FormatLine("RGBE write error");
break;
case rgbe_format_error:
CLogFile::FormatLine("RGBE bad file format: %s\n", msg);
break;
default:
case rgbe_memory_error:
CLogFile::FormatLine("RGBE error: %s\n", msg);
}
return RGBE_RETURN_FAILURE;
}
/* standard conversion from rgbe to float pixels */
/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
/* in the range [0,1] to map back into the range [0,1]. */
static INLINE void
rgbe2type(char* red, char* green, char* blue, unsigned char rgbe[4])
{
float f;
if (rgbe[3]) /*nonzero pixel*/
{
f = ldexp(1.0f, rgbe[3] - (int)(128 + 8)) * 255.0f;
*red = (unsigned char) max(0.0f, min(rgbe[0] * f, 255.0f));
*green = (unsigned char) max(0.0f, min(rgbe[1] * f, 255.0f));
*blue = (unsigned char) max(0.0f, min(rgbe[2] * f, 255.0f));
}
else
{
*red = *green = *blue = 0;
}
}
/* minimal header reading. modify if you want to parse more information */
int RGBE_ReadHeader(CCryFile* fp, uint32* width, uint32* height, rgbe_header_info* info)
{
char buf[512];
int found_format;
float tempf;
int i;
found_format = 0;
if (info)
{
info->valid = 0;
info->programtype[0] = 0;
info->gamma = info->exposure = 1.0;
}
if (fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == NULL)
{
return rgbe_error(rgbe_read_error, NULL);
}
if ((buf[0] != '#') || (buf[1] != '?'))
{
/* if you want to require the magic token then uncomment the next line */
/*return rgbe_error(rgbe_format_error,"bad initial token"); */
}
else if (info)
{
info->valid |= RGBE_VALID_PROGRAMTYPE;
for (i = 0; i < sizeof(info->programtype) - 1; i++)
{
if ((buf[i + 2] == 0) || isspace(buf[i + 2]))
{
break;
}
info->programtype[i] = buf[i + 2];
}
info->programtype[i] = 0;
if (fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0)
{
return rgbe_error(rgbe_read_error, NULL);
}
}
for (;; )
{
if ((buf[0] == 0) || (buf[0] == '\n'))
{
return rgbe_error(rgbe_format_error, "no FORMAT specifier found");
}
else if (strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0)
{
break; /* format found so break out of loop */
}
else if (info && (azsscanf(buf, "GAMMA=%g", &tempf) == 1))
{
info->gamma = tempf;
info->valid |= RGBE_VALID_GAMMA;
}
else if (info && (azsscanf(buf, "EXPOSURE=%g", &tempf) == 1))
{
info->exposure = tempf;
info->valid |= RGBE_VALID_EXPOSURE;
}
else if (info && (!strncmp(buf, "INSTRUCTIONS=", 13)))
{
info->valid |= RGBE_VALID_INSTRUCTIONS;
for (i = 0; i < sizeof(info->instructions) - 1; i++)
{
if ((buf[i + 13] == 0) || isspace(buf[i + 13]))
{
break;
}
info->instructions[i] = buf[i + 13];
}
}
if (fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0)
{
return rgbe_error(rgbe_read_error, NULL);
}
}
if (fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0)
{
return rgbe_error(rgbe_read_error, NULL);
}
if (strcmp(buf, "\n") != 0)
{
return rgbe_error(rgbe_format_error,
"missing blank line after FORMAT specifier");
}
if (fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0)
{
return rgbe_error(rgbe_read_error, NULL);
}
if (azsscanf(buf, "-Y %d +X %d", height, width) < 2)
{
return rgbe_error(rgbe_format_error, "missing image size specifier");
}
return RGBE_RETURN_SUCCESS;
}
/* simple read routine. will not correctly handle run length encoding */
int RGBE_ReadPixels(CCryFile* fp, char* data, int numpixels)
{
unsigned char rgbe[4];
while (numpixels-- > 0)
{
if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
{
return rgbe_error(rgbe_read_error, NULL);
}
rgbe2type(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN],
&data[RGBE_DATA_BLUE], rgbe);
data[RGBE_DATA_ALPHA] = 0.0f;
data += RGBE_DATA_SIZE;
}
return RGBE_RETURN_SUCCESS;
}
int RGBE_ReadPixels_RLE(CCryFile* fp, char* data, uint32 scanline_width,
uint32 num_scanlines)
{
unsigned char rgbe[4], * scanline_buffer, * ptr, * ptr_end;
int i, count;
unsigned char buf[2];
if ((scanline_width < 8) || (scanline_width > 0x7fff))
{
/* run length encoding is not allowed so read flat*/
return RGBE_ReadPixels(fp, data, scanline_width * num_scanlines);
}
scanline_buffer = NULL;
/* read in each successive scanline */
while (num_scanlines > 0)
{
if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
{
free(scanline_buffer);
return rgbe_error(rgbe_read_error, NULL);
}
if ((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80))
{
/* this file is not run length encoded */
rgbe2type(&data[0], &data[1], &data[2], rgbe);
data += RGBE_DATA_SIZE;
free(scanline_buffer);
return RGBE_ReadPixels(fp, data, scanline_width * num_scanlines - 1);
}
if ((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width)
{
free(scanline_buffer);
return rgbe_error(rgbe_format_error, "wrong scanline width");
}
if (scanline_buffer == NULL)
{
scanline_buffer = (unsigned char*)
malloc(sizeof(unsigned char) * 4 * scanline_width);
}
if (scanline_buffer == NULL)
{
return rgbe_error(rgbe_memory_error, "unable to allocate buffer space");
}
ptr = &scanline_buffer[0];
/* read each of the four channels for the scanline into the buffer */
for (i = 0; i < 4; i++)
{
ptr_end = &scanline_buffer[(i + 1) * scanline_width];
while (ptr < ptr_end)
{
if (fread(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
{
free(scanline_buffer);
return rgbe_error(rgbe_read_error, NULL);
}
if (buf[0] > 128)
{
/* a run of the same value */
count = buf[0] - 128;
if ((count == 0) || (count > ptr_end - ptr))
{
free(scanline_buffer);
return rgbe_error(rgbe_format_error, "bad scanline data");
}
while (count-- > 0)
{
*ptr++ = buf[1];
}
}
else
{
/* a non-run */
count = buf[0];
if ((count == 0) || (count > ptr_end - ptr))
{
free(scanline_buffer);
return rgbe_error(rgbe_format_error, "bad scanline data");
}
*ptr++ = buf[1];
if (--count > 0)
{
if (fread(ptr, sizeof(*ptr) * count, 1, fp) < 1)
{
free(scanline_buffer);
return rgbe_error(rgbe_read_error, NULL);
}
ptr += count;
}
}
}
}
/* now convert data from buffer into floats */
for (i = 0; i < scanline_width; i++)
{
rgbe[0] = scanline_buffer[i];
rgbe[1] = scanline_buffer[i + scanline_width];
rgbe[2] = scanline_buffer[i + 2 * scanline_width];
rgbe[3] = scanline_buffer[i + 3 * scanline_width];
rgbe2type(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN],
&data[RGBE_DATA_BLUE], rgbe);
data[RGBE_DATA_ALPHA] = 0.0f;
data += RGBE_DATA_SIZE;
}
num_scanlines--;
}
free(scanline_buffer);
return RGBE_RETURN_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////////
bool CImageHDR::Load(const QString& fileName, CImageEx& outImage)
{
CCryFile file;
if (!file.Open(fileName.toUtf8().data(), "rb"))
{
CLogFile::FormatLine("File not found %s", fileName.toUtf8().data());
return false;
}
// We use some global variables in callbacks, so we must
// prevent multithread access to the data
CryAutoLock<CryMutex> tifAutoLock(globalFileMutex);
globalFileBufferSize = file.GetLength();
globalFileBufferOffset = 0;
bool bRet = false;
uint32 dwWidth, dwHeight;
rgbe_header_info info;
if (RGBE_RETURN_SUCCESS == RGBE_ReadHeader(&file, &dwWidth, &dwHeight, &info))
{
if (outImage.Allocate(dwWidth, dwHeight))
{
char* pDst = (char*)outImage.GetData();
if (RGBE_RETURN_SUCCESS == RGBE_ReadPixels_RLE(&file, (char*)pDst, dwWidth, dwHeight))
{
bRet = true;
}
}
}
if (!bRet)
{
outImage.Detach();
}
return bRet;
}

@ -1,22 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
class CImageEx;
class CImageHDR
{
public:
bool Load(const QString& fileName, CImageEx& outImage);
};

@ -21,7 +21,6 @@
// Editor
#include "Util/ImageGif.h"
#include "Util/ImageTIF.h"
#include "Util/ImageHDR.h"
//////////////////////////////////////////////////////////////////////////
bool CImageUtil::Save(const QString& strFileName, CImageEx& inImage)
@ -275,10 +274,6 @@ bool CImageUtil::LoadImage(const QString& fileName, CImageEx& image, bool* pQual
{
return CImageUtil::Load(fileName, image);
}
else if (azstricmp(ext, ".hdr") == 0)
{
return CImageHDR().Load(fileName, image);
}
else
{
return CImageUtil::Load(fileName, image);

@ -45,6 +45,7 @@
#include <AzCore/std/algorithm.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include "ui_ViewportTitleDlg.h"
@ -57,13 +58,16 @@ inline namespace Helpers
{
void ToggleHelpers()
{
GetIEditor()->GetDisplaySettings()->DisplayHelpers(!GetIEditor()->GetDisplaySettings()->IsDisplayHelpers());
const bool newValue = !GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
GetIEditor()->GetDisplaySettings()->DisplayHelpers(newValue);
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
if (GetIEditor()->GetDisplaySettings()->IsDisplayHelpers() == false)
if (newValue == false)
{
GetIEditor()->GetObjectManager()->SendEvent(EVENT_HIDE_HELPER);
}
AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Broadcast(
&AzToolsFramework::ViewportInteraction::ViewportSettingNotifications::OnDrawHelpersChanged, newValue);
}
bool IsHelpersShown()
@ -126,6 +130,7 @@ CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
SetupCameraDropdownMenu();
SetupResolutionDropdownMenu();
SetupViewportInformationMenu();
SetupHelpersButton();
SetupOverflowMenu();
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, gSettings.bMuteAudio ? m_oMuteAudioRequest : m_oUnmuteAudioRequest);
@ -208,15 +213,16 @@ void CViewportTitleDlg::SetupViewportInformationMenu()
}
void CViewportTitleDlg::SetupHelpersButton()
{
connect(m_ui->m_helpers, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleHelpers);
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
}
void CViewportTitleDlg::SetupOverflowMenu()
{
// Setup the overflow menu
QMenu* overFlowMenu = new QMenu(this);
m_debugHelpersAction = new QAction("Debug Helpers", overFlowMenu);
m_debugHelpersAction->setCheckable(true);
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
connect(m_debugHelpersAction, &QAction::triggered, this, &CViewportTitleDlg::OnToggleHelpers);
overFlowMenu->addAction(m_debugHelpersAction);
m_audioMuteAction = new QAction("Mute Audio", overFlowMenu);
connect(m_audioMuteAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedMuteAudio);
@ -330,7 +336,7 @@ void CViewportTitleDlg::OnMaximize()
void CViewportTitleDlg::OnToggleHelpers()
{
Helpers::ToggleHelpers();
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
}
void CViewportTitleDlg::SetNoViewportInfo()
@ -756,7 +762,7 @@ void CViewportTitleDlg::OnEditorNotifyEvent(EEditorNotifyEvent event)
switch (event)
{
case eNotify_OnDisplayRenderUpdate:
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
break;
case eNotify_OnBeginGameMode:
case eNotify_OnEndGameMode:

@ -102,6 +102,7 @@ protected:
void SetupResolutionDropdownMenu();
void SetupViewportInformationMenu();
void SetupOverflowMenu();
void SetupHelpersButton();
QString m_title;
@ -172,7 +173,6 @@ protected:
QAction* m_normalInformationAction = nullptr;
QAction* m_fullInformationAction = nullptr;
QAction* m_compactInformationAction = nullptr;
QAction* m_debugHelpersAction = nullptr;
QAction* m_audioMuteAction = nullptr;
QAction* m_enableVRAction = nullptr;
QAction* m_enableGridSnappingAction = nullptr;

@ -81,6 +81,18 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_helpers">
<property name="icon">
<iconset>
<normaloff>:/Menu/helpers.svg</normaloff>:/Menu/helpers.svg
</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_resolutionMenu">
<property name="icon">
@ -94,7 +106,7 @@
<widget class="QToolButton" name="m_overflowBtn">
<property name="icon">
<iconset>
<normaloff>:/stylesheet/img/UI20/menu-centered.svg</normaloff>:/stylesheet/img/UI20/menu-centered.svg
<normaloff>:/Menu/menu.svg</normaloff>:/Menu/menu.svg
</iconset>
</property>
</widget>

@ -737,8 +737,6 @@ set(FILES
Util/GeometryUtil.cpp
Util/GuidUtil.cpp
Util/GuidUtil.h
Util/ImageHDR.cpp
Util/ImageHDR.h
Util/IObservable.h
Util/IndexedFiles.cpp
Util/IndexedFiles.h

@ -255,7 +255,6 @@ namespace AZ
{
return AZStd::make_pair(animation, anim);
}
Events::ProcessingResult AssImpAnimationImporter::ImportAnimation(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", "Animation");
@ -447,7 +446,22 @@ namespace AZ
return combinedAnimationResult.GetResult();
}
decltype(boneAnimations) parentFillerAnimations;
AZStd::unordered_set<AZStd::string> boneList;
for (int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
{
aiMesh* mesh = scene->mMeshes[meshIndex];
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
aiBone* bone = mesh->mBones[boneIndex];
boneList.insert(bone->mName.C_Str());
}
}
decltype(boneAnimations) fillerAnimations;
// Go through all the animations and make sure we create animations for bones who's parents don't have an animation
for (auto&& anim : boneAnimations)
@ -459,8 +473,8 @@ namespace AZ
{
if (!IsPivotNode(parent->mName))
{
if (boneAnimations.find(parent->mName.C_Str()) == boneAnimations.end() &&
parentFillerAnimations.find(parent->mName.C_Str()) == parentFillerAnimations.end())
if (!boneAnimations.contains(parent->mName.C_Str()) &&
!fillerAnimations.contains(parent->mName.C_Str()))
{
// Create 1 key for each type that just copies the current transform
ConsolidatedNodeAnim emptyAnimation;
@ -482,8 +496,8 @@ namespace AZ
emptyAnimation.m_ownedScalingKeys.emplace_back(0, scale);
emptyAnimation.mScalingKeys = emptyAnimation.m_ownedScalingKeys.data();
parentFillerAnimations.insert(
AZStd::make_pair(parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
fillerAnimations.insert(AZStd::make_pair(
parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
}
}
@ -491,7 +505,7 @@ namespace AZ
}
}
boneAnimations.insert(AZStd::make_move_iterator(parentFillerAnimations.begin()), AZStd::make_move_iterator(parentFillerAnimations.end()));
boneAnimations.insert(AZStd::make_move_iterator(fillerAnimations.begin()), AZStd::make_move_iterator(fillerAnimations.end()));
auto animItr = boneAnimations.equal_range(currentNode->mName.C_Str());

@ -98,6 +98,20 @@ namespace AZ
}
}
aiMatrix4x4 CalculateWorldTransform(const aiNode* currentNode)
{
aiMatrix4x4 transform = {};
const aiNode* iteratingNode = currentNode;
while (iteratingNode)
{
transform = iteratingNode->mTransformation * transform;
iteratingNode = iteratingNode->mParent;
}
return transform;
}
Events::ProcessingResult AssImpBoneImporter::ImportBone(AssImpNodeEncounteredContext& context)
{
AZ_TraceContext("Importer", "Bone");
@ -112,11 +126,6 @@ namespace AZ
bool isBone = false;
if (NodeParentIsOfType(context.m_scene.GetGraph(), context.m_currentGraphPosition, DataTypes::IBoneData::TYPEINFO_Uuid()))
{
isBone = true;
}
else
{
AZStd::unordered_map<AZStd::string, const aiNode*> mainBoneList;
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
@ -171,14 +180,7 @@ namespace AZ
createdBoneData = AZStd::make_shared<SceneData::GraphData::RootBoneData>();
}
aiMatrix4x4 transform = currentNode->mTransformation;
const aiNode* parent = currentNode->mParent;
while (parent)
{
transform = parent->mTransformation * transform;
parent = parent->mParent;
}
aiMatrix4x4 transform = CalculateWorldTransform(currentNode);
SceneAPI::DataTypes::MatrixType globalTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);

@ -47,7 +47,8 @@ namespace AZ
}
}
void GetAllBones(const aiScene* scene, AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
void GetAllBones(
const aiScene* scene, AZStd::unordered_multimap<AZStd::string, const aiBone*>& boneLookup)
{
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
{
@ -57,7 +58,7 @@ namespace AZ
{
const aiBone* bone = mesh->mBones[boneIndex];
boneLookup[bone->mName.C_Str()] = bone;
boneLookup.emplace(bone->mName.C_Str(), bone);
}
}
}
@ -73,41 +74,53 @@ namespace AZ
return Events::ProcessingResult::Ignored;
}
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
AZStd::unordered_multimap<AZStd::string, const aiBone*> boneLookup;
GetAllBones(scene, boneLookup);
auto boneIterator = boneLookup.find(currentNode->mName.C_Str());
const bool isBone = boneIterator != boneLookup.end();
aiMatrix4x4 combinedTransform;
DataTypes::MatrixType localTransform;
if (isBone)
{
auto parentNode = currentNode->mParent;
AZStd::vector<DataTypes::MatrixType> offsets, inverseOffsets;
auto iteratingNode = currentNode;
aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix;
aiMatrix4x4 parentOffset {};
while (iteratingNode && boneLookup.count(iteratingNode->mName.C_Str()))
{
AZStd::string name = iteratingNode->mName.C_Str();
auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str());
auto range = boneLookup.equal_range(name);
if (parentNode && parentBoneIterator != boneLookup.end())
{
const auto& parentBone = parentBoneIterator->second;
if (range.first != range.second)
{
// There can be multiple offsetMatrices for a given bone, we're only interested in grabbing the first one
auto boneFirstOffsetMatrix = range.first->second->mOffsetMatrix;
auto azMat = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(boneFirstOffsetMatrix);
offsets.push_back(azMat);
inverseOffsets.push_back(azMat.GetInverseFull());
}
parentOffset = parentBone->mOffsetMatrix;
iteratingNode = iteratingNode->mParent;
}
auto inverseOffset = offsetMatrix;
inverseOffset.Inverse();
combinedTransform = parentOffset * inverseOffset;
localTransform =
offsets.at(AZ::GetMin(offsets.size()-1, static_cast<decltype(offsets.size())>(1))) // parent bone offset, or if there is no parent, then current node offset
* inverseOffsets.at(inverseOffsets.size() - 1) // Inverse of root bone offset
* offsets.at(offsets.size() - 1) // Root bone offset
* inverseOffsets.at(0); // Inverse of current node offset
}
else
{
combinedTransform = GetConcatenatedLocalTransform(currentNode);
localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(GetConcatenatedLocalTransform(currentNode));
}
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
// Don't bother adding a node with the identity matrix
if (localTransform == DataTypes::MatrixType::Identity())
{
return Events::ProcessingResult::Ignored;
}
context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
context.m_sourceSceneSystem.ConvertUnit(localTransform);

@ -1765,16 +1765,8 @@ namespace LUAEditor
return false;
}
//name has the full path in it, we need to convert it to an asset name
AZStd::string projectRoot, databaseRoot, databasePath, databaseFile, fileExtension;
if (!AzFramework::StringFunc::AssetDatabasePath::Split(name.toUtf8().data(), &projectRoot, &databaseRoot, &databasePath, &databaseFile, &fileExtension))
{
AZ_Warning("LUAEditorMainWindow", false, AZStd::string::format("<span severity=\"err\">Path is invalid: '%s'</span>", name.toUtf8().data()).c_str());
return false;
}
AzFramework::StringFunc::Path::Split(name.toUtf8().data(), nullptr, &m_lastOpenFilePath);
AzFramework::StringFunc::AssetDatabasePath::Join(databasePath.c_str(), databaseFile.c_str(), newAssetName);
newAssetName = name.toUtf8().data();
return true;
}

@ -144,6 +144,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
FILES_CMAKE
awscore_editor_tests_files.cmake
${pal_editor_include_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
Tests/Editor/Platform/${PAL_PLATFORM_NAME}/awscore_editor_tests_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Include/Private

@ -0,0 +1,13 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)

@ -0,0 +1,13 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
)

@ -0,0 +1,22 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
../../AWSCoreEditorSystemComponentTest.cpp
../../Attribution/AWSCoreAttributionManagerTest.cpp
../../Attribution/AWSCoreAttributionMetricTest.cpp
../../Attribution/AWSCoreAttributionSystemComponentTest.cpp
../../Attribution/AWSAttributionServiceApiTest.cpp
../../UI/AWSCoreEditorMenuTest.cpp
../../UI/AWSCoreEditorUIFixture.h
../../UI/AWSCoreResourceMappingToolActionTest.cpp
../../AWSCoreEditorManagerTest.cpp
)

@ -10,14 +10,5 @@
#
set(FILES
Tests/AWSCoreEditorSystemComponentTest.cpp
Tests/Editor/Attribution/AWSCoreAttributionManagerTest.cpp
Tests/Editor/Attribution/AWSCoreAttributionMetricTest.cpp
Tests/Editor/Attribution/AWSCoreAttributionSystemComponentTest.cpp
Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp
Tests/Editor/UI/AWSCoreEditorMenuTest.cpp
Tests/Editor/UI/AWSCoreEditorUIFixture.h
Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp
Tests/Editor/AWSCoreEditorManagerTest.cpp
Tests/Editor/AWSCoreEditorTest.cpp
)

@ -11,10 +11,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import os
from aws_cdk import (
aws_lambda as _lambda,
aws_s3 as _s3,
aws_lambda as lambda_,
aws_s3 as s3,
aws_s3_deployment as s3_deployment,
aws_dynamodb as _dynamo,
aws_dynamodb as dynamo,
core
)
@ -39,7 +39,7 @@ class ExampleResources(core.Stack):
self._feature_name = feature_name
self._policy = AuthPolicy(context=self).generate_admin_policy(stack=self)
self._s3 = self.__create_s3_bucket()
self._s3_bucket = self.__create_s3_bucket()
self._lambda = self.__create_example_lambda()
self._table = self.__create_dynamodb_table()
@ -49,8 +49,8 @@ class ExampleResources(core.Stack):
self.__grant_access(props=props_)
def __grant_access(self, props: CoreStackProperties):
self._s3.grant_read(props.user_group)
self._s3.grant_read(props.admin_group)
self._s3_bucket.grant_read(props.user_group)
self._s3_bucket.grant_read(props.admin_group)
self._lambda.grant_invoke(props.user_group)
self._lambda.grant_invoke(props.admin_group)
@ -58,42 +58,50 @@ class ExampleResources(core.Stack):
self._table.grant_read_data(props.user_group)
self._table.grant_read_data(props.admin_group)
def __create_s3_bucket(self) -> _s3.Bucket:
# create s3 bucket
# create s3 bucket
s3 = _s3.Bucket(self, f'{self._project_name}-{self._feature_name}-Example-S3bucket')
def __create_s3_bucket(self) -> s3.Bucket:
# Create a sample S3 bucket following S3 best practices
# # See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html
# 1. Block all public access to the bucket
# 2. Use SSE-S3 encryption. Explore encryption at rest options via
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html
example_bucket = s3.Bucket(
self,
f'{self._project_name}-{self._feature_name}-Example-S3bucket',
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
encryption=s3.BucketEncryption.S3_MANAGED
)
s3_deployment.BucketDeployment(
self,
f'{self._project_name}-{self._feature_name}-S3bucket-Deployment',
destination_bucket=s3,
destination_bucket=example_bucket,
sources=[
s3_deployment.Source.asset('example/s3_content')
],
retain_on_delete=False
)
return example_bucket
return s3
def __create_example_lambda(self) -> _lambda.Function:
def __create_example_lambda(self) -> lambda_.Function:
# create lambda function
function = _lambda.Function(self,
f'{self._project_name}-{self._feature_name}-Lambda-Function',
runtime=_lambda.Runtime.PYTHON_3_8,
handler="lambda-handler.main",
code=_lambda.Code.asset(os.path.join(os.path.dirname(__file__), 'lambda')))
function = lambda_.Function(
self,
f'{self._project_name}-{self._feature_name}-Lambda-Function',
runtime=lambda_.Runtime.PYTHON_3_8,
handler="lambda-handler.main",
code=lambda_.Code.asset(os.path.join(os.path.dirname(__file__), 'lambda'))
)
return function
def __create_dynamodb_table(self) -> _dynamo.Table:
def __create_dynamodb_table(self) -> dynamo.Table:
# create dynamo table
# NB: CDK does not support seeding data, see simple table_seeder.py
demo_table = _dynamo.Table(
demo_table = dynamo.Table(
self,
f'{self._project_name}-{self._feature_name}-Table',
partition_key=_dynamo.Attribute(
partition_key=dynamo.Attribute(
name="id",
type=_dynamo.AttributeType.STRING
type=dynamo.AttributeType.STRING
)
)
return demo_table
@ -106,7 +114,7 @@ class ExampleResources(core.Stack):
id=f'ExampleBucketOutput',
description='An example S3 bucket to use with AWSCore ScriptBehaviors',
export_name=f"ExampleS3Bucket",
value=self._s3.bucket_arn)
value=self._s3_bucket.bucket_arn)
# Define exports
# Export resource group

@ -182,7 +182,7 @@ class RealTimeDataProcessing:
"""
Generate the analytics processing lambda to send processed data to CloudWatch for visualization.
"""
analytics_processing_function_name = f'{self._stack.stack_name}-AnalyticsProcessingLambda'
analytics_processing_function_name = f'{self._stack.stack_name}-AnalyticsProcessingLambdaName'
self._analytics_processing_lambda_role = self._create_analytics_processing_lambda_role(
analytics_processing_function_name
)

@ -1015,7 +1015,7 @@
{
"id": "pdo",
"displayName": "Pixel Depth Offset",
"description": "Whether to enable the pixel depth offset feature.",
"description": "Enable PDO to offset the original pixel depths. This will affect any shaders using depth, for example, when receiving shadows.",
"type": "Bool",
"defaultValue": false,
"connection": {

@ -417,7 +417,7 @@
{
"id": "pdo",
"displayName": "Pixel Depth Offset",
"description": "Whether to enable the pixel depth offset feature.",
"description": "Enable PDO to offset the original pixel depths. This will affect any shaders using depth, for example, when receiving shadows.",
"type": "Bool",
"defaultValue": false,
"connection": {

@ -956,7 +956,7 @@
{
"id": "pdo",
"displayName": "Pixel Depth Offset",
"description": "Whether to enable the pixel depth offset feature.",
"description": "Enable PDO to offset the original pixel depths. This will affect any shaders using depth, for example, when receiving shadows.",
"type": "Bool",
"defaultValue": false,
"connection": {

@ -21,7 +21,7 @@
"SlotType": "Output",
"ScopeAttachmentUsage": "RenderTarget",
"LoadStoreAction": {
"LoadAction": "Load"
"LoadAction": "DontCare"
}
}
],

@ -40,6 +40,11 @@ partial ShaderResourceGroup SceneSrg
ConstantBuffer<PhysicalSkyData> m_physicalSkyData;
bool m_physicalSky;
float m_fogTopHeight;
float m_fogBottomHeight;
float4 m_fogColor;
bool m_fogEnable;
TextureCube m_skyboxCubemap;
float4x4 m_cubemapRotationMatrix;
float m_cubemapExposure;

@ -153,6 +153,18 @@ PSOutput MainPS(VSOutput input)
color = TransformColor(srgbColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
}
}
if (SceneSrg::m_fogEnable)
{
if (input.m_cubemapCoord.z >= 0.0 && input.m_cubemapCoord.z <= SceneSrg::m_fogTopHeight)
{
color = lerp(SceneSrg::m_fogColor.rgb, color, input.m_cubemapCoord.z > 0.0 ? input.m_cubemapCoord.z/SceneSrg::m_fogTopHeight : 0.0);
}
else if (input.m_cubemapCoord.z < 0.0 && input.m_cubemapCoord.z >= -SceneSrg::m_fogBottomHeight)
{
color = SceneSrg::m_fogColor.rgb;
}
}
}
else
{

@ -16,6 +16,7 @@
#include <Atom/RPI.Reflect/Image/Image.h>
#include <Atom/Feature/CoreLights/PhotometricValue.h>
#include <AzCore/Math/Matrix4x4.h>
#include <SkyBox/SkyBoxFogSettings.h>
namespace AZ
{
@ -49,8 +50,9 @@ namespace AZ
AZ_RTTI(AZ::Render::SkyBoxFeatureProcessorInterface, "{71061869-1190-4451-A337-E9CFF16441B4}");
virtual void Enable(bool enable) = 0;
virtual bool IsEnable() = 0;
virtual bool IsEnabled() = 0;
virtual void SetSkyboxMode(SkyBoxMode mode) = 0;
virtual void SetFogSettings(const SkyBoxFogSettings& fogSettings) = 0;
// HDRiSkyBox
virtual void SetCubemap(Data::Instance<RPI::Image> cubemap) = 0;
@ -64,6 +66,13 @@ namespace AZ
virtual void SetSkyIntensity(float intensity, PhotometricUnit type) = 0;
virtual void SetSunIntensity(float intensity, PhotometricUnit type) = 0;
virtual void SetSunRadiusFactor(float factor) = 0;
// Fog Settings
virtual void SetFogEnabled(bool enable) = 0;
virtual bool IsFogEnabled() = 0;
virtual void SetFogColor(const AZ::Color &color) = 0;
virtual void SetFogTopHeight(float topHeight) = 0;
virtual void SetFogBottomHeight(float bottomHeight) = 0;
};
}
}

@ -0,0 +1,46 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Color.h>
namespace AZ
{
namespace Render
{
// EBus to get and set fog settings rendered with the sky
class SkyBoxFogRequests
: public ComponentBus
{
public:
AZ_RTTI(AZ::Render::SkyBoxFogRequests, "{4D477566-54B1-49EC-B8FE-4264EA228482}");
static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Single;
virtual ~SkyBoxFogRequests() {}
virtual void SetEnabled(bool enable) = 0;
virtual bool IsEnabled() const = 0;
virtual void SetColor(const AZ::Color& color) = 0;
virtual const AZ::Color& GetColor() const = 0;
// Set and Get the height upwards from the horizon
virtual void SetTopHeight(float topHeight) = 0;
virtual float GetTopHeight() const = 0;
// Set and Get the height downwards from the horizon
virtual void SetBottomHeight(float bottomHeight) = 0;
virtual float GetBottomHeight() const = 0;
};
typedef AZ::EBus<SkyBoxFogRequests> SkyBoxFogRequestBus;
}
}

@ -70,7 +70,7 @@
#include <PostProcessing/BloomCompositePass.h>
#include <ScreenSpace/DeferredFogPass.h>
#include <Shadows/ProjectedShadowFeatureProcessor.h>
#include <SkyBox/SkyBoxFogSettings.h>
#include <SkyBox/SkyBoxFeatureProcessor.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
@ -117,6 +117,7 @@ namespace AZ
TransformServiceFeatureProcessor::Reflect(context);
ProjectedShadowFeatureProcessor::Reflect(context);
SkyBoxFeatureProcessor::Reflect(context);
SkyBoxFogSettings::Reflect(context);
UseTextureFunctor::Reflect(context);
DrawListFunctor::Reflect(context);
SubsurfaceTransmissionParameterFunctor::Reflect(context);

@ -173,7 +173,7 @@ namespace AZ
{
if (inputPropertyValue.IsObject() && inputPropertyValue.HasMember("Value") && inputPropertyValue.HasMember("$type"))
{
// Requiring explicit type info to differentiate be=tween colors versus vectors and numeric types
// Requiring explicit type info to differentiate between colors versus vectors and numeric types
const AZ::Uuid baseTypeId = azrtti_typeid<T>();
AZ::Uuid typeId = AZ::Uuid::CreateNull();
result.Combine(LoadTypeId(typeId, inputPropertyValue, context, &baseTypeId));
@ -198,7 +198,7 @@ namespace AZ
{
outputPropertyValue.SetObject();
// Storing explicit type info to differentiate be=tween colors versus vectors and numeric types
// Storing explicit type info to differentiate between colors versus vectors and numeric types
rapidjson::Value typeValue;
result.Combine(StoreTypeId(typeValue, azrtti_typeid<T>(), context));
outputPropertyValue.AddMember("$type", typeValue, context.GetJsonAllocator());

@ -47,6 +47,9 @@ namespace AZ
m_updateSrg = true;
}
params.m_viewportState = RHI::Viewport(0, static_cast<float>(m_imageSize.m_width), 0, static_cast<float>(m_imageSize.m_height));
params.m_scissorState = RHI::Scissor(0, 0, m_imageSize.m_width, m_imageSize.m_height);
FullscreenTrianglePass::FrameBeginInternal(params);
}

@ -89,6 +89,10 @@ namespace AZ
m_cubemapIndex.Reset();
m_cubemapRotationMatrixIndex.Reset();
m_cubemapExposureIndex.Reset();
m_fogEnableIndex.Reset();
m_fogColorIndex.Reset();
m_fogTopHeightIndex.Reset();
m_fogBottomHeightIndex.Reset();
if (m_buffer)
{
@ -160,6 +164,14 @@ namespace AZ
m_mapBuffer = false;
}
m_sceneSrg->SetConstant(m_fogEnableIndex, m_fogSettings.m_enable);
if (m_fogSettings.m_enable)
{
m_sceneSrg->SetConstant(m_fogTopHeightIndex, m_fogSettings.m_topHeight);
m_sceneSrg->SetConstant(m_fogBottomHeightIndex, m_fogSettings.m_bottomHeight);
m_sceneSrg->SetConstant(m_fogColorIndex, m_fogSettings.m_color);
}
m_sceneSrg->SetConstant(m_physicalSkyIndex, true);
break;
}
@ -213,7 +225,7 @@ namespace AZ
m_enable = enable;
}
bool SkyBoxFeatureProcessor::IsEnable()
bool SkyBoxFeatureProcessor::IsEnabled()
{
return m_enable;
}
@ -238,6 +250,36 @@ namespace AZ
m_skyboxMode = mode;
}
void SkyBoxFeatureProcessor::SetFogSettings(const SkyBoxFogSettings& fogSettings)
{
m_fogSettings = fogSettings;
}
void SkyBoxFeatureProcessor::SetFogEnabled(bool enable)
{
m_fogSettings.m_enable = enable;
}
bool SkyBoxFeatureProcessor::IsFogEnabled()
{
return m_fogSettings.m_enable;
}
void SkyBoxFeatureProcessor::SetFogColor(const AZ::Color& color)
{
m_fogSettings.m_color = color;
}
void SkyBoxFeatureProcessor::SetFogTopHeight(float topHeight)
{
m_fogSettings.m_topHeight = topHeight;
}
void SkyBoxFeatureProcessor::SetFogBottomHeight(float bottomHeight)
{
m_fogSettings.m_bottomHeight = bottomHeight;
}
void SkyBoxFeatureProcessor::SetSunPosition(SunPosition sunPosition)
{
m_skyNeedUpdate = true;

@ -67,8 +67,14 @@ namespace AZ
// SkyBoxFeatureProcessorInterface overrides ...
void Enable(bool enable) override;
bool IsEnable() override;
bool IsEnabled() override;
void SetSkyboxMode(SkyBoxMode mode) override;
void SetFogSettings(const SkyBoxFogSettings& fogSettings) override;
void SetFogEnabled(bool enable) override;
bool IsFogEnabled() override;
void SetFogColor(const AZ::Color& color) override;
void SetFogTopHeight(float topHeight) override;
void SetFogBottomHeight(float bottomHeight) override;
void SetCubemapRotationMatrix(AZ::Matrix4x4 matrix) override;
void SetCubemap(Data::Instance<RPI::Image> cubemap) override;
@ -145,6 +151,10 @@ namespace AZ
RHI::ShaderInputNameIndex m_cubemapIndex = "m_skyboxCubemap";
RHI::ShaderInputNameIndex m_cubemapRotationMatrixIndex = "m_cubemapRotationMatrix";
RHI::ShaderInputNameIndex m_cubemapExposureIndex = "m_cubemapExposure";
RHI::ShaderInputNameIndex m_fogEnableIndex = "m_fogEnable";
RHI::ShaderInputNameIndex m_fogColorIndex = "m_fogColor";
RHI::ShaderInputNameIndex m_fogTopHeightIndex = "m_fogTopHeight";
RHI::ShaderInputNameIndex m_fogBottomHeightIndex = "m_fogBottomHeight";
bool m_skyNeedUpdate = true;
bool m_sunNeedUpdate = true;
@ -152,6 +162,7 @@ namespace AZ
bool m_enable = false;
SkyBoxMode m_skyboxMode = SkyBoxMode::None;
SkyBoxFogSettings m_fogSettings;
Data::Instance<RPI::ShaderResourceGroup> m_sceneSrg = nullptr;
Data::Instance<RPI::Image> m_cubemapTexture = nullptr;

@ -0,0 +1,83 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SkyBox/SkyBoxFogSettings.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <Atom/Feature/SkyBox/SkyBoxFogBus.h>
namespace AZ
{
namespace Render
{
void SkyBoxFogSettings::Reflect(ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SkyBoxFogSettings>()
->Version(1)
->Field("Enable", &SkyBoxFogSettings::m_enable)
->Field("Color", &SkyBoxFogSettings::m_color)
->Field("TopHeight", &SkyBoxFogSettings::m_topHeight)
->Field("BottomHeight", &SkyBoxFogSettings::m_bottomHeight)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<SkyBoxFogSettings>("SkyBoxFogSettings", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(AZ::Edit::UIHandlers::Default, &SkyBoxFogSettings::m_enable, "Enable Fog", "Toggle fog on or off")
->DataElement(AZ::Edit::UIHandlers::Default, &SkyBoxFogSettings::m_color, "Fog Color", "Color of the fog")
->Attribute(AZ::Edit::Attributes::ReadOnly, &SkyBoxFogSettings::IsFogDisabled)
->DataElement(AZ::Edit::UIHandlers::Slider, &SkyBoxFogSettings::m_topHeight, "Fog Top Height", "Height of the fog upwards from the horizon")
->Attribute(AZ::Edit::Attributes::ReadOnly, &SkyBoxFogSettings::IsFogDisabled)
->Attribute(AZ::Edit::Attributes::Min, 0.0)
->Attribute(AZ::Edit::Attributes::Max, 0.5)
->Attribute(AZ::Edit::Attributes::Step, 0.01)
->DataElement(AZ::Edit::UIHandlers::Slider, &SkyBoxFogSettings::m_bottomHeight, "Fog Bottom Height", "Height of the fog downwards from the horizon")
->Attribute(AZ::Edit::Attributes::ReadOnly, &SkyBoxFogSettings::IsFogDisabled)
->Attribute(AZ::Edit::Attributes::Min, 0.0)
->Attribute(AZ::Edit::Attributes::Max, 0.3)
->Attribute(AZ::Edit::Attributes::Step, 0.01)
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<SkyBoxFogRequestBus>("SkyBoxFogRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "render")
->Attribute(AZ::Script::Attributes::Module, "render")
->Event("SetEnabled", &SkyBoxFogRequestBus::Events::SetEnabled)
->Event("IsEnabled", &SkyBoxFogRequestBus::Events::IsEnabled)
->Event("SetColor", &SkyBoxFogRequestBus::Events::SetColor)
->Event("GetColor", &SkyBoxFogRequestBus::Events::GetColor)
->Event("SetTopHeight", &SkyBoxFogRequestBus::Events::SetTopHeight)
->Event("GetTopHeight", &SkyBoxFogRequestBus::Events::GetTopHeight)
->Event("SetBottomHeight", &SkyBoxFogRequestBus::Events::SetBottomHeight)
->Event("GetBottomHeight", &SkyBoxFogRequestBus::Events::GetBottomHeight)
->VirtualProperty("Enable", "IsEnabled", "SetEnabled")
->VirtualProperty("Color", "GetColor", "SetColor")
->VirtualProperty("TopHeight", "GetTopHeight", "SetTopHeight")
->VirtualProperty("BottomHeight", "GetTopHeight", "SetBottomHeight")
;
}
}
bool SkyBoxFogSettings::IsFogDisabled() const
{
return !m_enable;
}
}
}

@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Color.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
namespace Render
{
struct SkyBoxFogSettings final
{
AZ_RTTI(AZ::Render::SkyBoxFogSettings, "{DB13027C-BA92-4E46-B428-BB77C2A80C51}");
static void Reflect(ReflectContext* context);
SkyBoxFogSettings() = default;
bool IsFogDisabled() const;
AZ::Color m_color = AZ::Color::CreateOne();
bool m_enable = false;
float m_topHeight = 0.01;
float m_bottomHeight = 0.0;
};
}
}

@ -32,6 +32,7 @@ set(FILES
Include/Atom/Feature/PostProcessing/SMAAFeatureProcessorInterface.h
Include/Atom/Feature/PostProcess/PostFxLayerCategoriesConstants.h
Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h
Include/Atom/Feature/SkyBox/SkyBoxFogBus.h
Include/Atom/Feature/SkyBox/SkyboxConstants.h
Include/Atom/Feature/SkyBox/SkyBoxLUT.h
Include/Atom/Feature/SphericalHarmonics/SphericalHarmonicsUtility.h
@ -297,6 +298,8 @@ set(FILES
Source/SkinnedMesh/SkinnedMeshVertexStreamProperties.h
Source/SkyBox/SkyBoxFeatureProcessor.cpp
Source/SkyBox/SkyBoxFeatureProcessor.h
Source/SkyBox/SkyBoxFogSettings.h
Source/SkyBox/SkyBoxFogSettings.cpp
Source/TransformService/TransformServiceFeatureProcessor.cpp
Source/Utils/GpuBufferHandler.cpp
Source/LuxCore/LuxCoreTexturePass.cpp

@ -35,7 +35,6 @@ namespace AZ
{
m_device = device;
m_srgLayout = srgLayout;
m_srgPool = srgPool;
m_constantBufferSize = srgLayout->GetConstantDataSize();
if (m_constantBufferSize)
@ -93,9 +92,6 @@ namespace AZ
//Attach the constant buffer
AttachConstantBuffer();
m_samplerCache = [[NSCache alloc]init];
[m_samplerCache setName:@"SamplerCache"];
}
}
}
@ -211,8 +207,8 @@ namespace AZ
}
else
{
RHI::Ptr<Memory> nullMtlBufferMemPtr = m_device->GetNullDescriptorManager().GetNullImage(shaderInputImage.m_type).GetMemory();
mtlTextures[imageArrayLen] = nullMtlBufferMemPtr->GetGpuAddress<id<MTLTexture>>();
RHI::Ptr<Memory> nullMtlImagePtr = m_device->GetNullDescriptorManager().GetNullImage(shaderInputImage.m_type).GetMemory();
mtlTextures[imageArrayLen] = nullMtlImagePtr->GetGpuAddress<id<MTLTexture>>();
}
imageArrayLen++;
}
@ -345,15 +341,20 @@ namespace AZ
m_device->GetArgumentBufferAllocator().DeAllocate(m_argumentBuffer);
}
#endif
m_argumentBuffer = {};
m_constantBuffer = {};
[m_samplerCache removeAllObjects];
[m_samplerCache release];
m_samplerCache = nil;
if(m_argumentBuffer.IsValid())
{
m_device->QueueForRelease(m_argumentBuffer);
}
if(m_constantBuffer.IsValid())
{
m_device->QueueForRelease(m_constantBuffer);
}
[m_argumentEncoder release];
m_argumentEncoder = nil;
Base::Shutdown();
}
@ -374,23 +375,22 @@ namespace AZ
id<MTLSamplerState> ArgumentBuffer::GetMtlSampler(MTLSamplerDescriptor* samplerDesc)
{
id<MTLSamplerState> mtlSamplerState = [m_samplerCache objectForKey:samplerDesc];
const NSCache* samplerCache = m_device->GetSamplerCache();
id<MTLSamplerState> mtlSamplerState = [samplerCache objectForKey:samplerDesc];
if(mtlSamplerState == nil)
{
mtlSamplerState = [m_device->GetMtlDevice() newSamplerStateWithDescriptor:samplerDesc];
[m_samplerCache setObject:mtlSamplerState forKey:samplerDesc];
[samplerCache setObject:mtlSamplerState forKey:samplerDesc];
}
return mtlSamplerState;
}
void ArgumentBuffer::AddUntrackedResourcesToEncoder(id<MTLCommandEncoder> commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const
void ArgumentBuffer::CollectUntrackedResources(id<MTLCommandEncoder> commandEncoder,
const ShaderResourceGroupVisibility& srgResourcesVisInfo,
ComputeResourcesToMakeResidentMap& resourcesToMakeResidentCompute,
GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentGraphics) const
{
//Map to cache all the resources based on the usage as we can batch all the resources for a given usage
ComputeResourcesToMakeResidentMap resourcesToMakeResidentCompute;
//Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage
GraphicsResourcesToMakeResidentMap resourcesToMakeResidentGraphics;
//Cache the constant buffer associated with a srg
if (m_constantBufferSize)
{
@ -434,25 +434,6 @@ namespace AZ
}
}
}
//Call UseResource on all resources for Compute stage
for (const auto& key : resourcesToMakeResidentCompute)
{
AZStd::vector<id <MTLResource>> resourcesToProcessVec(key.second.begin(), key.second.end());
[static_cast<id<MTLComputeCommandEncoder>>(commandEncoder) useResources: &resourcesToProcessVec[0]
count: resourcesToProcessVec.size()
usage: key.first];
}
//Call UseResource on all resources for Vertex and Fragment stages
for (const auto& key : resourcesToMakeResidentGraphics)
{
AZStd::vector<id <MTLResource>> resourcesToProcessVec(key.second.begin(), key.second.end());
[static_cast<id<MTLRenderCommandEncoder>>(commandEncoder) useResources: &resourcesToProcessVec[0]
count: resourcesToProcessVec.size()
usage: key.first.first
stages: key.first.second];
}
}
void ArgumentBuffer::CollectResourcesForCompute(id<MTLCommandEncoder> encoder,

@ -97,7 +97,15 @@ namespace AZ
id<MTLBuffer> GetArgEncoderBuffer() const;
size_t GetOffset() const;
void AddUntrackedResourcesToEncoder(id<MTLCommandEncoder> commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const;
//Map to cache all the resources based on the usage as we can batch all the resources for a given usage.
using ComputeResourcesToMakeResidentMap = AZStd::unordered_map<MTLResourceUsage, AZStd::unordered_set<id <MTLResource>>>;
//Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage.
using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map<AZStd::pair<MTLResourceUsage,MTLRenderStages>, AZStd::unordered_set<id <MTLResource>>>;
void CollectUntrackedResources(id<MTLCommandEncoder> commandEncoder,
const ShaderResourceGroupVisibility& srgResourcesVisInfo,
ComputeResourcesToMakeResidentMap& resourcesToMakeResidentCompute,
GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentGraphics) const;
void ClearResourceTracking();
@ -120,10 +128,6 @@ namespace AZ
ResourceBindingsMap m_resourceBindings;
static const int MaxEntriesInArgTable = 31;
//Map to cache all the resources based on the usage as we can batch all the resources for a given usage.
using ComputeResourcesToMakeResidentMap = AZStd::unordered_map<MTLResourceUsage, AZStd::unordered_set<id <MTLResource>>>;
//Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage.
using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map<AZStd::pair<MTLResourceUsage,MTLRenderStages>, AZStd::unordered_set<id <MTLResource>>>;
void CollectResourcesForCompute(id<MTLCommandEncoder> encoder,
const ResourceBindingsSet& resourceBindingData,
@ -153,9 +157,6 @@ namespace AZ
MemoryView m_argumentBuffer;
MemoryView m_constantBuffer;
#endif
ShaderResourceGroupPool* m_srgPool = nullptr;
NSCache* m_samplerCache;
};
}
}

@ -40,7 +40,7 @@ namespace AZ
buffer->m_pendingResolves++;
uploadRequest.m_attachmentBuffer = buffer;
uploadRequest.m_byteOffset = buffer->GetMemoryView().GetOffset() + request.m_byteOffset;
uploadRequest.m_byteOffset = request.m_byteOffset;
uploadRequest.m_stagingBuffer = stagingBuffer;
return stagingBuffer->GetMemoryView().GetCpuAddress();
@ -51,6 +51,12 @@ namespace AZ
void BufferPoolResolver::Compile()
{
for (BufferUploadPacket& packet : m_uploadPackets)
{
Buffer* stagingBuffer = packet.m_stagingBuffer.get();
//Inform the GPU that the CPU has modified the staging buffer.
Platform::SynchronizeBufferOnCPU(stagingBuffer->GetMemoryView().GetGpuAddress<id<MTLBuffer>>(), stagingBuffer->GetMemoryView().GetOffset(), stagingBuffer->GetMemoryView().GetSize());
}
}
void BufferPoolResolver::Resolve(CommandList& commandList) const
@ -63,14 +69,11 @@ namespace AZ
AZ_Assert(stagingBuffer, "Staging Buffer is null.");
AZ_Assert(destBuffer, "Attachment Buffer is null.");
//Inform the GPU that the CPU has modified the staging buffer.
Platform::SynchronizeBufferOnCPU(stagingBuffer->GetMemoryView().GetGpuAddress<id<MTLBuffer>>(), stagingBuffer->GetMemoryView().GetOffset(), stagingBuffer->GetMemoryView().GetSize());
RHI::CopyBufferDescriptor copyDescriptor;
copyDescriptor.m_sourceBuffer = stagingBuffer;
copyDescriptor.m_sourceOffset = stagingBuffer->GetMemoryView().GetOffset();
copyDescriptor.m_destinationBuffer = destBuffer;
copyDescriptor.m_destinationOffset = static_cast<uint32_t>(packet.m_byteOffset);
copyDescriptor.m_destinationOffset = destBuffer->GetMemoryView().GetOffset() + static_cast<uint32_t>(packet.m_byteOffset);
copyDescriptor.m_size = stagingBuffer->GetMemoryView().GetSize();
commandList.Submit(RHI::CopyItem(copyDescriptor));

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

Loading…
Cancel
Save