Merge branch 'main' into Prefab/CreatePrefab

This commit is contained in:
srikappa
2021-04-19 17:26:53 -07:00
117 changed files with 1845 additions and 1265 deletions
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:171f38d536d7b805cc644513d22dae5552a4eef2bffb88e97e089898cf769530
size 2126
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6388466c97009fd3993e5d3b59a2b0961f623c6becbd0a12a0a5eb7bd8da5d4e
size 12302
@@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
@@ -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.
"""
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../PythonTests')
from PythonAssetBuilder import bootstrap_tests
@@ -134,6 +134,25 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
)
endif()
## Python Asset Builder ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PythonAssetBuilder
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/PythonAssetBuilder
TIMEOUT 3600
RUNTIME_DEPENDENCIES
Legacy::Editor
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
Gem::EditorPythonBindings.Editor
Gem::PythonAssetBuilder.Editor
COMPONENT TestTools
)
endif()
## Blast ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
@@ -0,0 +1,57 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
#
# This launches the AssetProcessor and Editor then attempts to find the expected
# assets created by a Python Asset Builder and the output of a scene pipeline script
#
import sys
import os
import pytest
import logging
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import ly_test_tools.log.log_monitor
import ly_test_tools.environment.waiter as waiter
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize('launcher_platform', ['windows_editor'])
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['auto_test'])
class TestPythonAssetProcessing(object):
def test_DetectPythonCreatedAsset(self, request, editor, level, launcher_platform):
unexpected_lines = []
expected_lines = [
'Mock asset exists',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_negative.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_positive.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found'
]
timeout = 180
halt_on_unexpected = False
test_directory = os.path.join(os.path.dirname(__file__))
testFile = os.path.join(test_directory, 'AssetBuilder_test_case.py')
editor.args.extend(['-NullRenderer', "--skipWelcomeScreenDialog", "--autotest_mode", "--runpythontest", testFile])
with editor.start():
editorlog_file = os.path.join(editor.workspace.paths.project_log(), 'Editor.log')
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(editor, editorlog_file)
waiter.wait_for(
lambda: editor.is_alive(),
timeout,
exc=("Log file '{}' was never opened by another process.".format(editorlog_file)),
interval=1)
log_monitor.monitor_log_for_lines(expected_lines, unexpected_lines, halt_on_unexpected, timeout)
@@ -0,0 +1,52 @@
"""
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 azlmbr.bus
import azlmbr.asset
import azlmbr.editor
import azlmbr.math
import azlmbr.legacy.general
def raise_and_stop(msg):
print (msg)
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
# These tests are meant to check that the test_asset.mock source asset turned into
# a test_asset.mock_asset product asset via the Python asset builder system
mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
mockAssetPath = 'gem/pythontests/pythonassetbuilder/test_asset.mock_asset'
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', mockAssetPath, mockAssetType, False)
if (assetId.is_valid() is False):
raise_and_stop(f'Mock AssetId is not valid! Got {assetId.to_string()} instead')
if (assetId.to_string().endswith(':54c06b89') is False):
raise_and_stop(f'Mock AssetId has unexpected sub-id for {mockAssetPath}!')
print ('Mock asset exists')
# These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
def test_azmodel_product(generatedModelAssetPath, expectedSubId):
azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
assetIdString = assetId.to_string()
if (assetIdString.endswith(':' + expectedSubId) is False):
raise_and_stop(f'Asset has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath})!')
else:
print(f'Expected subId for asset ({generatedModelAssetPath}) found')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel', '10412075')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_positive.azmodel', '10d16e68')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_negative.azmodel', '10a71973')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Y_positive.azmodel', '10130556')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Y_negative.azmodel', '1065724d')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Z_positive.azmodel', '1024be55')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Z_negative.azmodel', '1052c94e')
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
@@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
@@ -0,0 +1,17 @@
"""
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 os
import sys
try:
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import mock_asset_builder
except:
print ('skipping asset builder testing via mock_asset_builder')
@@ -0,0 +1,88 @@
"""
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 uuid, os
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
def get_mesh_node_names(sceneGraph):
meshDataList = []
node = sceneGraph.get_root()
children = []
while node.IsValid():
# store children to process after siblings
if sceneGraph.has_node_child(node):
children.append(sceneGraph.get_node_child(node))
# store any node that has mesh data content
nodeContent = sceneGraph.get_node_content(node)
if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'):
if sceneGraph.is_node_end_point(node) is False:
meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node)))
# advance to next node
if sceneGraph.has_node_sibling(node):
node = sceneGraph.get_node_sibling(node)
elif children:
node = children.pop()
else:
node = azlmbr.scene.graph.NodeIndex()
return meshDataList
def update_manifest(scene):
graph = sceneData.SceneGraph(scene.graph)
meshNameList = get_mesh_node_names(graph)
sceneManifest = sceneData.SceneManifest()
sourceFilenameOnly = os.path.basename(scene.sourceFilename)
sourceFilenameOnly = sourceFilenameOnly.replace('.','_')
for activeMeshIndex in range(len(meshNameList)):
chunkName = meshNameList[activeMeshIndex]
chunkPath = chunkName.get_path()
meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name())
meshGroup = sceneManifest.add_mesh_group(meshGroupName)
meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}'
sceneManifest.mesh_group_add_comment(meshGroup, 'auto generated by scene manifest')
sceneManifest.mesh_group_add_advanced_coordinate_system(meshGroup, None, None, None, 1.0)
# create selection node list
pathSet = set()
for meshIndex in range(len(meshNameList)):
targetPath = meshNameList[meshIndex].get_path()
if (activeMeshIndex == meshIndex):
sceneManifest.mesh_group_select_node(meshGroup, targetPath)
else:
if targetPath not in pathSet:
pathSet.update(targetPath)
sceneManifest.mesh_group_unselect_node(meshGroup, targetPath)
return sceneManifest.export()
mySceneJobHandler = None
def on_update_manifest(args):
scene = args[0]
result = update_manifest(scene)
global mySceneJobHandler
mySceneJobHandler.disconnect()
mySceneJobHandler = None
return result
def main():
global mySceneJobHandler
mySceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
mySceneJobHandler.connect()
mySceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
if __name__ == "__main__":
main()
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66d38948309ef273adf74b63eaa38f8fc2e2bdfbab3933d2ee082ce6a8cb108e
size 30496
@@ -0,0 +1,9 @@
{
"values":
[
{
"$type": "ScriptProcessorRule",
"scriptFilename": "Gem/PythonTests/PythonAssetBuilder/export_chunks_builder.py"
}
]
}
@@ -0,0 +1,121 @@
"""
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 azlmbr.asset
import azlmbr.asset.builder
import azlmbr.bus
import azlmbr.math
import os, traceback, binascii, sys
jobKeyName = 'Mock Asset'
def log_exception_traceback():
exc_type, exc_value, exc_tb = sys.exc_info()
data = traceback.format_exception(exc_type, exc_value, exc_tb)
print(str(data))
# creates a single job to compile for each platform
def create_jobs(request):
# create job descriptor for each platform
jobDescriptorList = []
for platformInfo in request.enabledPlatforms:
jobDesc = azlmbr.asset.builder.JobDescriptor()
jobDesc.jobKey = jobKeyName
jobDesc.set_platform_identifier(platformInfo.identifier)
jobDescriptorList.append(jobDesc)
response = azlmbr.asset.builder.CreateJobsResponse()
response.result = azlmbr.asset.builder.CreateJobsResponse_ResultSuccess
response.createJobOutputs = jobDescriptorList
return response
def on_create_jobs(args):
try:
request = args[0]
return create_jobs(request)
except:
log_exception_traceback()
# returing back a default CreateJobsResponse() records an asset error
return azlmbr.asset.builder.CreateJobsResponse()
def process_file(request):
# prepare output folder
basePath, _ = os.path.split(request.sourceFile)
outputPath = os.path.join(request.tempDirPath, basePath)
os.makedirs(outputPath, exist_ok=True)
# write out a mock file
basePath, sourceFile = os.path.split(request.sourceFile)
mockFilename = os.path.splitext(sourceFile)[0] + '.mock_asset'
mockFilename = os.path.join(basePath, mockFilename)
mockFilename = mockFilename.replace('\\', '/')
tempFilename = os.path.join(request.tempDirPath, mockFilename)
# write out a tempFilename like a JSON
fileOutput = open(tempFilename, "w")
fileOutput.write('{}')
fileOutput.close()
# generate a product asset file entry
subId = binascii.crc32(mockFilename.encode())
mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
product = azlmbr.asset.builder.JobProduct(mockFilename, mockAssetType, subId)
product.dependenciesHandled = True
productOutputs = []
productOutputs.append(product)
# fill out response object
response = azlmbr.asset.builder.ProcessJobResponse()
response.outputProducts = productOutputs
response.resultCode = azlmbr.asset.builder.ProcessJobResponse_Success
response.dependenciesHandled = True
return response
# using the incoming 'request' find the type of job via 'jobKey' to determine what to do
def on_process_job(args):
try:
request = args[0]
if (request.jobDescription.jobKey.startswith(jobKeyName)):
return process_file(request)
except:
log_exception_traceback()
# returning back an empty ProcessJobResponse() will record an error
return azlmbr.asset.builder.ProcessJobResponse()
# register asset builder
def register_asset_builder(busId):
assetPattern = azlmbr.asset.builder.AssetBuilderPattern()
assetPattern.pattern = '*.mock'
assetPattern.type = azlmbr.asset.builder.AssetBuilderPattern_Wildcard
builderDescriptor = azlmbr.asset.builder.AssetBuilderDesc()
builderDescriptor.name = "Mock Builder"
builderDescriptor.patterns = [assetPattern]
builderDescriptor.busId = busId
builderDescriptor.version = 1
outcome = azlmbr.asset.builder.PythonAssetBuilderRequestBus(azlmbr.bus.Broadcast, 'RegisterAssetBuilder', builderDescriptor)
if outcome.IsSuccess():
# created the asset builder to hook into the notification bus
handler = azlmbr.asset.builder.PythonBuilderNotificationBusHandler()
handler.connect(busId)
handler.add_callback('OnCreateJobsRequest', on_create_jobs)
handler.add_callback('OnProcessJobRequest', on_process_job)
return handler
# create the asset builder handler
busIdString = '{CF5C74C1-9ED4-5851-95B1-0B15090DBEC7}'
busId = azlmbr.math.Uuid_CreateString(busIdString, 0)
handler = None
try:
handler = register_asset_builder(busId)
except:
handler = None
log_exception_traceback()
@@ -0,0 +1 @@
mock data
@@ -77,13 +77,13 @@ class TestImageGradient(EditorTestHelper):
# 3) Assign the processed gradient signal image as the Image Gradient's image asset and verify success
# First, check for the base image in the workspace
base_image = "lumberyard_gsi.png"
base_image = "image_grad_test_gsi.png"
base_image_path = os.path.join("AutomatedTesting", "Assets", "ImageGradients", base_image)
if os.path.isfile(base_image_path):
print(f"{base_image} was found in the workspace")
# Next, assign the processed image to the Image Gradient's Image Asset property
processed_image_path = os.path.join("Assets", "ImageGradients", "lumberyard_gsi.gradimage")
processed_image_path = os.path.join("Assets", "ImageGradients", "image_grad_test_gsi.gradimage")
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", processed_image_path, math.Uuid(),
False)
hydra.get_set_test(image_gradient_entity, 0, "Configuration|Image Asset", asset_id)
@@ -57,7 +57,7 @@ class TestImageGradientRequiresShape(object):
"Entity has a Image Gradient component",
"Entity has a Gradient Transform Modifier component",
"Entity has a Box Shape component",
"lumberyard_gsi.png was found in the workspace",
"image_grad_test_gsi.png was found in the workspace",
"Entity Configuration|Image Asset: SUCCESS",
"ImageGradient_ProcessedImageAssignedSucessfully: result=SUCCESS",
]
@@ -1,7 +1,6 @@
{
"configurations": [
{
"autoSelect": false,
"displayName": "Greenwich Park 02",
"skyboxImageAsset": {
"assetId": {
@@ -64,7 +63,6 @@
"shadowCatcherOpacity": 0.20000000298023225
},
{
"autoSelect": false,
"displayName": "Greenwich Park 02 (Alt)",
"skyboxImageAsset": {
"assetId": {
+12 -2
View File
@@ -19,6 +19,7 @@
#include <AzCore/Component/EntityId.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Range.h>
#include <AnimKey.h>
@@ -914,9 +915,18 @@ struct IAnimStringTable
*/
struct IAnimSequence
{
AZ_RTTI(IAnimSequence, "{A60F95F5-5A4A-47DB-B3BB-525BBC0BC8DB}")
AZ_RTTI(IAnimSequence, "{A60F95F5-5A4A-47DB-B3BB-525BBC0BC8DB}");
AZ_CLASS_ALLOCATOR(IAnimSequence, AZ::SystemAllocator, 0);
static const int kSequenceVersion = 4;
static const int kSequenceVersion = 5;
static void Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<IAnimSequence>();
}
}
//! Flags used for SetFlags(),GetFlags(),SetParentFlags(),GetParentFlags() methods.
enum EAnimSequenceFlags
@@ -178,14 +178,16 @@ namespace AZ
//! on an update to '/Amazon/AzCore/Bootstrap/project_path' key.
struct UpdateProjectSettingsEventHandler
{
UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry)
UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry, AZ::CommandLine& commandLine)
: m_registry{ registry }
, m_commandLine{ commandLine }
{
}
void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
// #1 Update the project settings when the project path is set
const auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
AZ::IO::FixedMaxPath newProjectPath;
if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectPathKey, path)
@@ -194,6 +196,7 @@ namespace AZ
UpdateProjectSettingsFromProjectPath(AZ::IO::PathView(newProjectPath));
}
// #2 Update the project specialization when the project name is set
const auto projectNameKey = FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + "/project_name";
FixedValueString newProjectName;
if (SettingsRegistryMergeUtils::IsPathAncestorDescendantOrEqual(projectNameKey, path)
@@ -201,6 +204,12 @@ namespace AZ
{
UpdateProjectSpecializationFromProjectName(newProjectName);
}
// #3 Update the ComponentApplication CommandLine instance when the command line settings are merged into the Settings Registry
if (path == AZ::SettingsRegistryMergeUtils::CommandLineValueChangedKey)
{
UpdateCommandLine();
}
}
//! Add the project name as a specialization underneath the /Amazon/AzCore/Settings/Specializations path
@@ -233,10 +242,16 @@ namespace AZ
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
}
void UpdateCommandLine()
{
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(m_registry, m_commandLine);
}
private:
AZ::IO::FixedMaxPath m_oldProjectPath;
AZ::SettingsRegistryInterface::FixedValueString m_oldProjectName;
AZ::SettingsRegistryInterface& m_registry;
AZ::CommandLine& m_commandLine;
};
void ComponentApplication::Descriptor::AllocatorRemapping::Reflect(ReflectContext* context, ComponentApplication* app)
@@ -415,6 +430,12 @@ namespace AZ
// Add the Command Line arguments into the SettingsRegistry
SettingsRegistryMergeUtils::StoreCommandLineToRegistry(*m_settingsRegistry, m_commandLine);
// Add a notifier to update the project_settings when
// 1. The 'project_path' key changes
// 2. The project specialization when the 'project-name' key changes
// 3. The ComponentApplication command line when the command line is stored to the registry
m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry, m_commandLine });
// Merge Command Line arguments
constexpr bool executeRegDumpCommands = false;
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
@@ -429,10 +450,6 @@ namespace AZ
// for the application root.
CalculateAppRoot();
// Add a notifier to update the /Amazon/AzCore/Settings/Specializations
// when the 'project_path' property changes within the SettingsRegistry
m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry });
// Merge the bootstrap.cfg file into the Settings Registry as soon as the OSAllocator has been created.
SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
@@ -909,6 +926,8 @@ namespace AZ
SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
#endif
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
}
void ComponentApplication::SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations)
@@ -0,0 +1,340 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/PlatformId/PlatformDefaults.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace AZ
{
inline namespace PlatformDefaults
{
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
const char* PlatformIdToPalFolder(AZ::PlatformId platform)
{
#ifdef IOS
#define AZ_REDEFINE_IOS_AT_END IOS
#undef IOS
#endif
switch (platform)
{
case AZ::PC:
return "PC";
case AZ::ES3:
return "Android";
case AZ::IOS:
return "iOS";
case AZ::OSX:
return "Mac";
case AZ::PROVO:
return "Provo";
case AZ::SALEM:
return "Salem";
case AZ::JASPER:
return "Jasper";
case AZ::SERVER:
return "Server";
case AZ::ALL:
case AZ::ALL_CLIENT:
case AZ::NumPlatformIds:
case AZ::Invalid:
default:
return "";
}
#ifdef AZ_REDEFINE_IOS_AT_END
#define IOS AZ_REDEFINE_IOS_AT_END
#endif
}
const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform)
{
if (osPlatform == PlatformCodeNameWindows || osPlatform == PlatformCodeNameLinux)
{
return PlatformPC;
}
else if (osPlatform == PlatformCodeNameMac)
{
return PlatformOSX;
}
else if (osPlatform == PlatformCodeNameAndroid)
{
return PlatformES3;
}
else if (osPlatform == PlatformCodeNameiOS)
{
return PlatformIOS;
}
else if (osPlatform == PlatformCodeNameProvo)
{
return PlatformProvo;
}
else if (osPlatform == PlatformCodeNameSalem)
{
return PlatformSalem;
}
else if (osPlatform == PlatformCodeNameJasper)
{
return PlatformJasper;
}
AZ_Error("PlatformDefault", false, R"(Supplied OS platform "%.*s" does not have a corresponding default asset platform)",
aznumeric_cast<int>(osPlatform.size()), osPlatform.data());
return "";
}
PlatformFlags PlatformHelper::GetPlatformFlagFromPlatformIndex(PlatformId platformIndex)
{
if (platformIndex < 0 || platformIndex > PlatformId::NumPlatformIds)
{
return PlatformFlags::Platform_NONE;
}
if (platformIndex == PlatformId::ALL)
{
return PlatformFlags::Platform_ALL;
}
if (platformIndex == PlatformId::ALL_CLIENT)
{
return PlatformFlags::Platform_ALL_CLIENT;
}
return static_cast<PlatformFlags>(1 << platformIndex);
}
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> PlatformHelper::GetPlatforms(PlatformFlags platformFlags)
{
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> platforms;
for (int platformNum = 0; platformNum < PlatformId::NumPlatformIds; ++platformNum)
{
const bool isAllPlatforms = PlatformId::ALL == static_cast<PlatformId>(platformNum)
&& ((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE);
const bool isAllClientPlatforms = PlatformId::ALL_CLIENT == static_cast<PlatformId>(platformNum)
&& ((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE);
if (isAllPlatforms || isAllClientPlatforms
|| (platformFlags & static_cast<PlatformFlags>(1 << platformNum)) != PlatformFlags::Platform_NONE)
{
platforms.push_back(PlatformNames[platformNum]);
}
}
return platforms;
}
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> PlatformHelper::GetPlatformsInterpreted(PlatformFlags platformFlags)
{
return GetPlatforms(GetPlatformFlagsInterpreted(platformFlags));
}
AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> PlatformHelper::GetPlatformIndices(PlatformFlags platformFlags)
{
AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> platformIndices;
for (int i = 0; i < PlatformId::NumPlatformIds; i++)
{
PlatformId index = static_cast<PlatformId>(i);
if ((GetPlatformFlagFromPlatformIndex(index) & platformFlags) != PlatformFlags::Platform_NONE)
{
platformIndices.emplace_back(index);
}
}
return platformIndices;
}
AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> PlatformHelper::GetPlatformIndicesInterpreted(PlatformFlags platformFlags)
{
return GetPlatformIndices(GetPlatformFlagsInterpreted(platformFlags));
}
PlatformFlags PlatformHelper::GetPlatformFlag(AZStd::string_view platform)
{
int platformIndex = GetPlatformIndexFromName(platform);
if (platformIndex == PlatformId::Invalid)
{
AZ_Error("PlatformDefault", false, "Invalid Platform ( %.*s ).\n", static_cast<int>(platform.length()), platform.data());
return PlatformFlags::Platform_NONE;
}
if (platformIndex == PlatformId::ALL)
{
return PlatformFlags::Platform_ALL;
}
if (platformIndex == PlatformId::ALL_CLIENT)
{
return PlatformFlags::Platform_ALL_CLIENT;
}
return static_cast<PlatformFlags>(1 << platformIndex);
}
const char* PlatformHelper::GetPlatformName(PlatformId platform)
{
if (platform < 0 || platform > PlatformId::NumPlatformIds)
{
return "invalid";
}
return PlatformNames[platform];
}
void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, AZStd::string_view platformId)
{
PlatformId platform = GetPlatformIdFromName(platformId);
AZ_Assert(platform != PlatformId::Invalid, "Unsupported Platform ID: %.*s", static_cast<int>(platformId.length()), platformId.data());
AppendPlatformCodeNames(platformCodes, platform);
}
void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, PlatformId platformId)
{
// The IOS SDK has a macro that defines IOS as 1 which causes the enum below to be incorrectly converted to "PlatformId::1".
#pragma push_macro("IOS")
#undef IOS
// To reduce work the Asset Processor groups assets that can be shared between hardware platforms together. For this
// reason "PC" can for instance cover both the Windows and Linux platforms and "IOS" can cover AppleTV and iOS.
switch (platformId)
{
case PlatformId::PC:
platformCodes.emplace_back(PlatformCodeNameWindows);
platformCodes.emplace_back(PlatformCodeNameLinux);
break;
case PlatformId::ES3:
platformCodes.emplace_back(PlatformCodeNameAndroid);
break;
case PlatformId::IOS:
platformCodes.emplace_back(PlatformCodeNameiOS);
break;
case PlatformId::OSX:
platformCodes.emplace_back(PlatformCodeNameMac);
break;
case PlatformId::PROVO:
platformCodes.emplace_back(PlatformCodeNameProvo);
break;
case PlatformId::SALEM:
platformCodes.emplace_back(PlatformCodeNameSalem);
break;
case PlatformId::JASPER:
platformCodes.emplace_back(PlatformCodeNameJasper);
break;
case PlatformId::SERVER:
// Server is not a hardware platform
break;
default:
AZ_Assert(false, "Unsupported Platform ID: %i", platformId);
break;
}
#pragma pop_macro("IOS")
}
int PlatformHelper::GetPlatformIndexFromName(AZStd::string_view platformName)
{
for (int idx = 0; idx < PlatformId::NumPlatformIds; idx++)
{
if (platformName == PlatformNames[idx])
{
return idx;
}
}
return PlatformId::Invalid;
}
PlatformId PlatformHelper::GetPlatformIdFromName(AZStd::string_view platformName)
{
return aznumeric_caster(GetPlatformIndexFromName(platformName));
}
AssetPlatformCombinedString PlatformHelper::GetCommaSeparatedPlatformList(PlatformFlags platformFlags)
{
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> platformNames = GetPlatforms(platformFlags);
AssetPlatformCombinedString platformsString;
AZ::StringFunc::Join(platformsString, platformNames.begin(), platformNames.end(), ", ");
return platformsString;
}
PlatformFlags PlatformHelper::GetPlatformFlagsInterpreted(PlatformFlags platformFlags)
{
PlatformFlags returnFlags = PlatformFlags::Platform_NONE;
if ((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE)
{
for (int i = 0; i < NumPlatforms; ++i)
{
auto platformId = static_cast<PlatformId>(i);
if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT)
{
returnFlags |= GetPlatformFlagFromPlatformIndex(platformId);
}
}
}
else if ((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE)
{
for (int i = 0; i < NumPlatforms; ++i)
{
auto platformId = static_cast<PlatformId>(i);
if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT && platformId != PlatformId::SERVER)
{
returnFlags |= GetPlatformFlagFromPlatformIndex(platformId);
}
}
}
else
{
returnFlags = platformFlags;
}
return returnFlags;
}
bool PlatformHelper::IsSpecialPlatform(PlatformFlags platformFlags)
{
return (platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE
|| (platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE;
}
bool HasFlagHelper(PlatformFlags flags, PlatformFlags checkPlatform)
{
return (flags & checkPlatform) == checkPlatform;
}
bool PlatformHelper::HasPlatformFlag(PlatformFlags flags, PlatformId checkPlatform)
{
// If checkPlatform contains any kind of invalid id, just exit out here
if (checkPlatform == PlatformId::Invalid || checkPlatform == NumPlatforms)
{
return false;
}
// ALL_CLIENT + SERVER = ALL
if (HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT | PlatformFlags::Platform_SERVER))
{
flags = PlatformFlags::Platform_ALL;
}
if (HasFlagHelper(flags, PlatformFlags::Platform_ALL))
{
// It doesn't matter what checkPlatform is set to in this case, just return true
return true;
}
if (HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT))
{
return checkPlatform != PlatformId::SERVER;
}
return HasFlagHelper(flags, GetPlatformFlagFromPlatformIndex(checkPlatform));
}
}
}
@@ -0,0 +1,157 @@
/*
* 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/Preprocessor/Enum.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string_view.h>
// On IOS builds IOS will be defined and interfere with the below enums
#pragma push_macro("IOS")
#undef IOS
namespace AZ
{
inline namespace PlatformDefaults
{
constexpr char PlatformPC[] = "pc";
constexpr char PlatformES3[] = "es3";
constexpr char PlatformIOS[] = "ios";
constexpr char PlatformOSX[] = "osx_gl";
constexpr char PlatformProvo[] = "provo";
constexpr char PlatformSalem[] = "salem";
constexpr char PlatformJasper[] = "jasper";
constexpr char PlatformServer[] = "server";
constexpr char PlatformCodeNameWindows[] = "Windows";
constexpr char PlatformCodeNameLinux[] = "Linux";
constexpr char PlatformCodeNameAndroid[] = "Android";
constexpr char PlatformCodeNameiOS[] = "iOS";
constexpr char PlatformCodeNameMac[] = "Mac";
constexpr char PlatformCodeNameProvo[] = "Provo";
constexpr char PlatformCodeNameSalem[] = "Salem";
constexpr char PlatformCodeNameJasper[] = "Jasper";
constexpr char PlatformAll[] = "all";
constexpr char PlatformAllClient[] = "all_client";
// Used for the capacity of a fixed vector to store the code names of platforms
// The value needs to be higher than the number of unique OS platforms that are supported(at this time 8)
constexpr size_t MaxPlatformCodeNames = 16;
//! This platform enum have platform values in sequence and can also be used to get the platform count.
AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int,
(Invalid, -1),
PC,
ES3,
IOS,
OSX,
PROVO,
SALEM,
JASPER,
SERVER, // Corresponds to the customer's flavor of "server" which could be windows, ubuntu, etc
ALL,
ALL_CLIENT,
// Add new platforms above this
NumPlatformIds
);
constexpr int NumClientPlatforms = 7;
constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently
enum class PlatformFlags : AZ::u32
{
Platform_NONE = 0x00,
Platform_PC = 1 << PlatformId::PC,
Platform_ES3 = 1 << PlatformId::ES3,
Platform_IOS = 1 << PlatformId::IOS,
Platform_OSX = 1 << PlatformId::OSX,
Platform_PROVO = 1 << PlatformId::PROVO,
Platform_SALEM = 1 << PlatformId::SALEM,
Platform_JASPER = 1 << PlatformId::JASPER,
Platform_SERVER = 1 << PlatformId::SERVER,
// A special platform that will always correspond to all platforms, even if new ones are added
Platform_ALL = 1ULL << 30,
// A special platform that will always correspond to all non-server platforms, even if new ones are added
Platform_ALL_CLIENT = 1ULL << 31,
AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags);
// 32 characters should be more than enough to store a platform name
using AssetPlatformFixedString = AZStd::fixed_string<32>;
// Fixed string which can store a comma separated list of platforms names
// Additional byte is added to take into account the comma
using AssetPlatformCombinedString = AZStd::fixed_string < (AssetPlatformFixedString{}.max_size() + 1)* PlatformId::NumPlatformIds > ;
const char* PlatformIdToPalFolder(PlatformId platform);
const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform);
//! Platform Helper is an utility class that can be used to retrieve platform related information
class PlatformHelper
{
public:
//! Given a platformIndex returns the platform name
static const char* GetPlatformName(PlatformId platform);
//! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME.
static void AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, AZStd::string_view platformName);
//! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME.
static void AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, PlatformId platformId);
//! Given a platform name returns a platform index.
//! If the platform is not found, the method returns -1.
static int GetPlatformIndexFromName(AZStd::string_view platformName);
//! Given a platform name returns a platform id.
//! If the platform is not found, the method returns -1.
static PlatformId GetPlatformIdFromName(AZStd::string_view platformName);
//! Given a platformIndex returns the platformFlags
static PlatformFlags GetPlatformFlagFromPlatformIndex(PlatformId platform);
//! Given a platformFlags returns all the platform identifiers that are set.
static AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> GetPlatforms(PlatformFlags platformFlags);
//! Given a platformFlags returns all the platform identifiers that are set, with special flags interpreted. Do not use the result for saving
static AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> GetPlatformsInterpreted(PlatformFlags platformFlags);
//! Given a platformFlags return a list of PlatformId indices
static AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> GetPlatformIndices(PlatformFlags platformFlags);
//! Given a platformFlags return a list of PlatformId indices, with special flags interpreted. Do not use the result for saving
static AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> GetPlatformIndicesInterpreted(PlatformFlags platformFlags);
//! Given a platform identifier returns its corresponding platform flag.
static PlatformFlags GetPlatformFlag(AZStd::string_view platform);
//! Given any platformFlags returns a string listing the input platforms
static AssetPlatformCombinedString GetCommaSeparatedPlatformList(PlatformFlags platformFlags);
//! If platformFlags contains any special flags, they are removed and replaced with the normal flags they represent
static PlatformFlags GetPlatformFlagsInterpreted(PlatformFlags platformFlags);
//! Returns true if platformFlags contains any special flags
static bool IsSpecialPlatform(PlatformFlags platformFlags);
//! Returns true if platformFlags has checkPlatform flag set.
static bool HasPlatformFlag(PlatformFlags platformFlags, PlatformId checkPlatform);
};
}
}
#pragma pop_macro("IOS")
@@ -17,6 +17,7 @@
#include <AzCore/JSON/pointer.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/writer.h>
#include <AzCore/PlatformId/PlatformDefaults.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Settings/CommandLine.h>
#include <AzCore/std/string/conversions.h>
@@ -132,23 +133,14 @@ namespace AZ::Internal
AZ::IO::FixedMaxPath ScanUpRootLocator(AZStd::string_view rootFileToLocate)
{
AZStd::fixed_string<AZ::IO::MaxPathLength> executableDir;
if (AZ::Utils::GetExecutableDirectory(executableDir.data(), executableDir.capacity()) == Utils::ExecutablePathResult::Success)
{
// Update the size value of the executable directory fixed string to correctly be the length of the null-terminated string
// stored within it
executableDir.resize_no_construct(AZStd::char_traits<char>::length(executableDir.data()));
}
AZ::IO::FixedMaxPath engineRootCandidate{ executableDir };
AZ::IO::FixedMaxPath rootCandidate{ AZ::Utils::GetExecutableDirectory() };
bool rootPathVisited = false;
do
{
if (AZ::IO::SystemFile::Exists((engineRootCandidate / rootFileToLocate).c_str()))
if (AZ::IO::SystemFile::Exists((rootCandidate / rootFileToLocate).c_str()))
{
return engineRootCandidate;
return rootCandidate;
}
// Note for posix filesystems the parent directory of '/' is '/' and for windows
@@ -156,38 +148,69 @@ namespace AZ::Internal
// Validate that the parent directory isn't itself, that would imply
// that it is the filesystem root path
AZ::IO::PathView parentPath = engineRootCandidate.ParentPath();
rootPathVisited = (engineRootCandidate == parentPath);
AZ::IO::PathView parentPath = rootCandidate.ParentPath();
rootPathVisited = (rootCandidate == parentPath);
// Recurse upwards one directory
engineRootCandidate = AZStd::move(parentPath);
rootCandidate = AZStd::move(parentPath);
} while (!rootPathVisited);
return {};
}
void InjectSettingToCommandLineFront(AZ::SettingsRegistryInterface& settingsRegistry,
AZStd::string_view path, AZStd::string_view value)
{
AZ::CommandLine commandLine;
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine);
AZ::CommandLine::ParamContainer paramContainer;
commandLine.Dump(paramContainer);
auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")",
aznumeric_cast<int>(path.size()), path.data(), aznumeric_cast<int>(value.size()), value.data());
paramContainer.emplace(paramContainer.begin(), AZStd::move(projectPathOverride));
commandLine.Parse(paramContainer);
AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine);
}
} // namespace AZ::Internal
namespace AZ::SettingsRegistryMergeUtils
{
constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Settings/Internal/engine_root_scan_up_path" };
constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Settings/Internal/project_root_scan_up_path" };
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry)
{
AZ::IO::FixedMaxPath engineRoot;
// This is the 'external' engine root key, as in passed from command-line or .setreg files.
auto engineRootKey = SettingsRegistryInterface::FixedValueString::format("%s/engine_path", BootstrapSettingsRootKey);
// Step 1 Run the scan upwards logic once to find the location of the engine.json if it exist
// Once this step is run the {InternalScanUpEngineRootKey} is set in the Settings Registry
// to have this scan logic only run once InternalScanUpEngineRootKey the supplied registry
if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == SettingsRegistryInterface::Type::NoType)
{
// We can scan up from exe directory to find engine.json, use that for engine root if it exists.
engineRoot = Internal::ScanUpRootLocator("engine.json");
// Set the {InternalScanUpEngineRootKey} to make sure this code path isn't called again for this settings registry
settingsRegistry.Set(InternalScanUpEngineRootKey, engineRoot.Native());
if (!engineRoot.empty())
{
settingsRegistry.Set(engineRootKey, engineRoot.Native());
// Inject the engine root into the front of the command line settings
Internal::InjectSettingToCommandLineFront(settingsRegistry, engineRootKey, engineRoot.Native());
return engineRoot;
}
}
// Step 2 check if the engine_path key has been supplied
if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty())
{
return engineRoot;
}
// We can scan up from exe directory to find engine.json, use that for engine root if it exists.
if (engineRoot = Internal::ScanUpRootLocator("engine.json"); !engineRoot.empty())
{
settingsRegistry.Set(engineRootKey, engineRoot.c_str());
return engineRoot;
}
// Step 3 locate the project root and attempt to find the engine root using the registered engine
// for the project in the project.json file
AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry);
if (projectRoot.empty())
{
@@ -207,16 +230,30 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
{
AZ::IO::FixedMaxPath projectRoot;
// This is the 'external' project root key, as in passed from command-line or .setreg files.
auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
if (settingsRegistry.Get(projectRoot.Native(), projectRootKey))
const auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
// Step 1 Run the scan upwards logic once to find the location of the project.json if it exist
// Once this step is run the {InternalScanUpProjectRootKey} is set in the Settings Registry
// to have this scan logic only run once for the supplied registry
// SettingsRegistryInterface::GetType is used to check if a key is set
if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == SettingsRegistryInterface::Type::NoType)
{
return projectRoot;
projectRoot = Internal::ScanUpRootLocator("project.json");
// Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry
settingsRegistry.Set(InternalScanUpProjectRootKey, projectRoot.Native());
if (!projectRoot.empty())
{
settingsRegistry.Set(projectRootKey, projectRoot.c_str());
// Inject the project root into the front of the command line settings
Internal::InjectSettingToCommandLineFront(settingsRegistry, projectRootKey, projectRoot.Native());
return projectRoot;
}
}
if (projectRoot = Internal::ScanUpRootLocator("project.json"); !projectRoot.empty())
// Step 2 Check the project-path key
// This is the project path root key, as in passed from command-line or .setreg files.
if (settingsRegistry.Get(projectRoot.Native(), projectRootKey))
{
settingsRegistry.Set(projectRootKey, projectRoot.c_str());
return projectRoot;
}
@@ -463,24 +500,13 @@ namespace AZ::SettingsRegistryMergeUtils
void MergeSettingsToRegistry_Bootstrap(SettingsRegistryInterface& registry)
{
ConfigParserSettings parserSettings;
parserSettings.m_commentPrefixFunc = [](AZStd::string_view line) -> AZStd::string_view
{
constexpr AZStd::string_view commentPrefixes[]{ "--", ";","#" };
for (AZStd::string_view commentPrefix : commentPrefixes)
{
if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
{
return line.substr(0, commentOffset);
}
}
return line;
};
parserSettings.m_registryRootPointerPath = BootstrapSettingsRootKey;
MergeSettingsToRegistry_ConfigFile(registry, "bootstrap.cfg", parserSettings);
}
void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
// Binary folder
AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
@@ -489,27 +515,25 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
constexpr size_t bufferSize = 64;
auto buffer = AZStd::fixed_string<bufferSize>::format("%s/project_path", BootstrapSettingsRootKey);
AZ::SettingsRegistryInterface::FixedValueString projectPathKey(buffer);
auto projectPathKey = FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
SettingsRegistryInterface::FixedValueString projectPathValue;
if (registry.Get(projectPathValue, projectPathKey))
{
// Cache folder
// Get the name of the asset platform assigned by the bootstrap. First check for platform version such as "windows_assets"
// and if that's missing just get "assets".
constexpr char platformName[] = AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER;
SettingsRegistryInterface::FixedValueString assetPlatform;
buffer = AZStd::fixed_string<bufferSize>::format("%s/%s_assets", BootstrapSettingsRootKey, platformName);
AZStd::string_view assetPlatformKey(buffer);
if (!registry.Get(assetPlatform, assetPlatformKey))
FixedValueString assetPlatform;
if (auto assetPlatformKey = FixedValueString::format("%s/%s_assets", BootstrapSettingsRootKey, AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER);
!registry.Get(assetPlatform, assetPlatformKey))
{
buffer = AZStd::fixed_string<bufferSize>::format("%s/assets", BootstrapSettingsRootKey);
assetPlatformKey = AZStd::string_view(buffer);
assetPlatformKey = FixedValueString::format("%s/assets", BootstrapSettingsRootKey);
registry.Get(assetPlatform, assetPlatformKey);
}
if (assetPlatform.empty())
{
// Use the platform codename to retrieve the default asset platform value
assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
}
// Project path - corresponds to the @devassets@ alias
// NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
@@ -549,8 +573,7 @@ namespace AZ::SettingsRegistryMergeUtils
{
// Cache: project root - no corresponding fileIO alias, but this is where the asset database lives.
// A registry override is accepted using the "project_cache_path" key.
buffer = AZStd::fixed_string<bufferSize>::format("%s/project_cache_path", BootstrapSettingsRootKey);
AZStd::string_view projectCacheRootOverrideKey(buffer);
auto projectCacheRootOverrideKey = FixedValueString::format("%s/project_cache_path", BootstrapSettingsRootKey);
// Clear path to make sure that the `project_cache_path` value isn't concatenated to the project path
path.clear();
if (registry.Get(path.Native(), projectCacheRootOverrideKey))
@@ -807,6 +830,11 @@ namespace AZ::SettingsRegistryMergeUtils
++argumentIndex;
commandLinePath.resize(commandLineRootSize);
}
// This key is used allow Notification Handlers to know when the command line has been updated within the
// registry. The value itself is meaningless. The JSON path of {CommandLineValueChangedKey}
// being passed to the Notification Event Handler indicates that the command line has be updated
registry.Set(CommandLineValueChangedKey, true);
}
bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine)
@@ -823,10 +851,16 @@ namespace AZ::SettingsRegistryMergeUtils
}
else if (valueName == "Value" && !value.empty())
{
m_arguments.push_back(value);
// Make sure value types are in quotes in case they start with a command option prefix
m_arguments.push_back(QuoteArgument(value));
}
}
AZStd::string QuoteArgument(AZStd::string_view arg)
{
return !arg.empty() ? AZStd::string::format(R"("%.*s")", aznumeric_cast<int>(arg.size()), arg.data()) : AZStd::string{ arg };
}
// The first parameter is skipped by the ComamndLine::Parse function so initialize
// the container with one empty element
AZ::CommandLine::ParamContainer m_arguments{ 1 };
@@ -57,6 +57,9 @@ namespace AZ::SettingsRegistryMergeUtils
//! Root key for where command line are stored at within the settings registry
inline static constexpr char CommandLineRootKey[] = "/Amazon/AzCore/Runtime/CommandLine";
//! Key set to trigger a notification that the CommandLine has been stored within the settings registry
//! The value of the key has no meaning. Notification Handlers only need to check if the key was supplied
inline static constexpr char CommandLineValueChangedKey[] = "/Amazon/AzCore/Runtime/CommandLineChanged";
//! Root key where raw project settings (project.json) file is merged to settings registry
inline static constexpr char ProjectSettingsRootKey[] = "/Amazon/Project/Settings";
@@ -74,6 +77,20 @@ namespace AZ::SettingsRegistryMergeUtils
//! If it's still not found, attempt to find the project (by similar means) then reconcile the
//! engine root by inspecting project.json and the engine manifest file.
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry);
//! The algorithm that is used to find the project root is as follows
//! 1. The first time this function is it performs a upward scan for a project.json file from
//! the executable directory and if found stores that path to an internal key.
//! In the same step it injects the path into the front of list of command line parameters
//! using the --regset="{BootstrapSettingsRootKey}/project_path=<path>" value
//! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set
//!
//! The order in which the project path settings are overridden proceeds in the following order
//! 1. project_path set in the <engine-root>/bootstrap.cfg file
//! 2. project_path set in a *.setreg/*.setregpatch file
//! 3. project_path found by scanning upwards from the executable directory to the project.json path
//! 4. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=<path>"
//! or --project_path=<path>
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry);
//! Query the specializations that will be used when loading the Settings Registry.
@@ -607,6 +607,8 @@ set(FILES
Utils/Utils.h
Script/lua/lua.h
Memory/HeapSchema.cpp
PlatformId/PlatformDefaults.h
PlatformId/PlatformDefaults.cpp
PlatformId/PlatformId.h
PlatformId/PlatformId.cpp
Socket/AzSocket_fwd.h
@@ -1,338 +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.
*
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace AzFramework
{
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
const char* PlatformIdToPalFolder(AzFramework::PlatformId platform)
{
#ifdef IOS
#define AZ_REDEFINE_IOS_AT_END IOS
#undef IOS
#endif
switch (platform)
{
case AzFramework::PC:
return "PC";
case AzFramework::ES3:
return "Android";
case AzFramework::IOS:
return "iOS";
case AzFramework::OSX:
return "Mac";
case AzFramework::PROVO:
return "Provo";
case AzFramework::SALEM:
return "Salem";
case AzFramework::JASPER:
return "Jasper";
case AzFramework::SERVER:
return "Server";
case AzFramework::ALL:
case AzFramework::ALL_CLIENT:
case AzFramework::NumPlatformIds:
case AzFramework::Invalid:
default:
return "";
}
#ifdef AZ_REDEFINE_IOS_AT_END
#define IOS AZ_REDEFINE_IOS_AT_END
#endif
}
const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform)
{
if (osPlatform == PlatformCodeNameWindows || osPlatform == PlatformCodeNameLinux)
{
return PlatformPC;
}
else if (osPlatform == PlatformCodeNameMac)
{
return PlatformOSX;
}
else if (osPlatform == PlatformCodeNameAndroid)
{
return PlatformES3;
}
else if (osPlatform == PlatformCodeNameiOS)
{
return PlatformIOS;
}
else if (osPlatform == PlatformCodeNameProvo)
{
return PlatformProvo;
}
else if (osPlatform == PlatformCodeNameSalem)
{
return PlatformSalem;
}
else if (osPlatform == PlatformCodeNameJasper)
{
return PlatformJasper;
}
AZ_Error("PlatformDefault", false, R"(Supplied OS platform "%.*s" does not have a corresponding default asset platform)",
aznumeric_cast<int>(osPlatform.size()), osPlatform.data());
return "";
}
PlatformFlags PlatformHelper::GetPlatformFlagFromPlatformIndex(PlatformId platformIndex)
{
if (platformIndex < 0 || platformIndex > PlatformId::NumPlatformIds)
{
return PlatformFlags::Platform_NONE;
}
if (platformIndex == PlatformId::ALL)
{
return PlatformFlags::Platform_ALL;
}
if (platformIndex == PlatformId::ALL_CLIENT)
{
return PlatformFlags::Platform_ALL_CLIENT;
}
return static_cast<PlatformFlags>(1 << platformIndex);
}
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> PlatformHelper::GetPlatforms(PlatformFlags platformFlags)
{
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> platforms;
for (int platformNum = 0; platformNum < PlatformId::NumPlatformIds; ++platformNum)
{
const bool isAllPlatforms = PlatformId::ALL == static_cast<PlatformId>(platformNum)
&& ((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE);
const bool isAllClientPlatforms = PlatformId::ALL_CLIENT == static_cast<PlatformId>(platformNum)
&& ((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE);
if (isAllPlatforms || isAllClientPlatforms
|| (platformFlags & static_cast<PlatformFlags>(1 << platformNum)) != PlatformFlags::Platform_NONE)
{
platforms.push_back(PlatformNames[platformNum]);
}
}
return platforms;
}
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> PlatformHelper::GetPlatformsInterpreted(PlatformFlags platformFlags)
{
return GetPlatforms(GetPlatformFlagsInterpreted(platformFlags));
}
AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> PlatformHelper::GetPlatformIndices(PlatformFlags platformFlags)
{
AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> platformIndices;
for (int i = 0; i < PlatformId::NumPlatformIds; i++)
{
PlatformId index = static_cast<PlatformId>(i);
if ((GetPlatformFlagFromPlatformIndex(index) & platformFlags) != PlatformFlags::Platform_NONE)
{
platformIndices.emplace_back(index);
}
}
return platformIndices;
}
AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> PlatformHelper::GetPlatformIndicesInterpreted(PlatformFlags platformFlags)
{
return GetPlatformIndices(GetPlatformFlagsInterpreted(platformFlags));
}
PlatformFlags PlatformHelper::GetPlatformFlag(AZStd::string_view platform)
{
int platformIndex = GetPlatformIndexFromName(platform);
if (platformIndex == PlatformId::Invalid)
{
AZ_Error("PlatformDefault", false, "Invalid Platform ( %.*s ).\n", static_cast<int>(platform.length()), platform.data());
return PlatformFlags::Platform_NONE;
}
if(platformIndex == PlatformId::ALL)
{
return PlatformFlags::Platform_ALL;
}
if (platformIndex == PlatformId::ALL_CLIENT)
{
return PlatformFlags::Platform_ALL_CLIENT;
}
return static_cast<PlatformFlags>(1 << platformIndex);
}
const char* PlatformHelper::GetPlatformName(PlatformId platform)
{
if (platform < 0 || platform > PlatformId::NumPlatformIds)
{
return "invalid";
}
return PlatformNames[platform];
}
void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, AZStd::string_view platformId)
{
PlatformId platform = GetPlatformIdFromName(platformId);
AZ_Assert(platform != PlatformId::Invalid, "Unsupported Platform ID: %.*s", static_cast<int>(platformId.length()), platformId.data());
AppendPlatformCodeNames(platformCodes, platform);
}
void PlatformHelper::AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, PlatformId platformId)
{
// The IOS SDK has a macro that defines IOS as 1 which causes the enum below to be incorrectly converted to "PlatformId::1".
#pragma push_macro("IOS")
#undef IOS
// To reduce work the Asset Processor groups assets that can be shared between hardware platforms together. For this
// reason "PC" can for instance cover both the Windows and Linux platforms and "IOS" can cover AppleTV and iOS.
switch (platformId)
{
case PlatformId::PC:
platformCodes.emplace_back(PlatformCodeNameWindows);
platformCodes.emplace_back(PlatformCodeNameLinux);
break;
case PlatformId::ES3:
platformCodes.emplace_back(PlatformCodeNameAndroid);
break;
case PlatformId::IOS:
platformCodes.emplace_back(PlatformCodeNameiOS);
break;
case PlatformId::OSX:
platformCodes.emplace_back(PlatformCodeNameMac);
break;
case PlatformId::PROVO:
platformCodes.emplace_back(PlatformCodeNameProvo);
break;
case PlatformId::SALEM:
platformCodes.emplace_back(PlatformCodeNameSalem);
break;
case PlatformId::JASPER:
platformCodes.emplace_back(PlatformCodeNameJasper);
break;
case PlatformId::SERVER:
// Server is not a hardware platform
break;
default:
AZ_Assert(false, "Unsupported Platform ID: %i", platformId);
break;
}
#pragma pop_macro("IOS")
}
int PlatformHelper::GetPlatformIndexFromName(AZStd::string_view platformName)
{
for (int idx = 0; idx < PlatformId::NumPlatformIds; idx++)
{
if (platformName == PlatformNames[idx])
{
return idx;
}
}
return PlatformId::Invalid;
}
PlatformId PlatformHelper::GetPlatformIdFromName(AZStd::string_view platformName)
{
return aznumeric_caster(GetPlatformIndexFromName(platformName));
}
AssetPlatformCombinedString PlatformHelper::GetCommaSeparatedPlatformList(PlatformFlags platformFlags)
{
AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> platformNames = GetPlatforms(platformFlags);
AssetPlatformCombinedString platformsString;
AZ::StringFunc::Join(platformsString, platformNames.begin(), platformNames.end(), ", ");
return platformsString;
}
PlatformFlags PlatformHelper::GetPlatformFlagsInterpreted(PlatformFlags platformFlags)
{
PlatformFlags returnFlags = PlatformFlags::Platform_NONE;
if((platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE)
{
for (int i = 0; i < NumPlatforms; ++i)
{
auto platformId = static_cast<PlatformId>(i);
if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT)
{
returnFlags |= GetPlatformFlagFromPlatformIndex(platformId);
}
}
}
else if((platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE)
{
for (int i = 0; i < NumPlatforms; ++i)
{
auto platformId = static_cast<PlatformId>(i);
if (platformId != PlatformId::ALL && platformId != PlatformId::ALL_CLIENT && platformId != PlatformId::SERVER)
{
returnFlags |= GetPlatformFlagFromPlatformIndex(platformId);
}
}
}
else
{
returnFlags = platformFlags;
}
return returnFlags;
}
bool PlatformHelper::IsSpecialPlatform(PlatformFlags platformFlags)
{
return (platformFlags & PlatformFlags::Platform_ALL) != PlatformFlags::Platform_NONE
|| (platformFlags & PlatformFlags::Platform_ALL_CLIENT) != PlatformFlags::Platform_NONE;
}
bool HasFlagHelper(PlatformFlags flags, PlatformFlags checkPlatform)
{
return (flags & checkPlatform) == checkPlatform;
}
bool PlatformHelper::HasPlatformFlag(PlatformFlags flags, PlatformId checkPlatform)
{
// If checkPlatform contains any kind of invalid id, just exit out here
if(checkPlatform == PlatformId::Invalid || checkPlatform == NumPlatforms)
{
return false;
}
// ALL_CLIENT + SERVER = ALL
if(HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT | PlatformFlags::Platform_SERVER))
{
flags = PlatformFlags::Platform_ALL;
}
if(HasFlagHelper(flags, PlatformFlags::Platform_ALL))
{
// It doesn't matter what checkPlatform is set to in this case, just return true
return true;
}
if(HasFlagHelper(flags, PlatformFlags::Platform_ALL_CLIENT))
{
return checkPlatform != PlatformId::SERVER;
}
return HasFlagHelper(flags, GetPlatformFlagFromPlatformIndex(checkPlatform));
}
}
@@ -12,144 +12,12 @@
#pragma once
#include <AzCore/Preprocessor/Enum.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string_view.h>
// On IOS builds IOS will be defined and interfere with the below enums
#pragma push_macro("IOS")
#undef IOS
#include <AzCore/PlatformId/PlatformDefaults.h>
// As the Platform defaults is needed within AzCore,
// those structures have been moved to AzCore and brought into
// The AzFramework namespace for backwards compatibility
namespace AzFramework
{
constexpr char PlatformPC[] = "pc";
constexpr char PlatformES3[] = "es3";
constexpr char PlatformIOS[] = "ios";
constexpr char PlatformOSX[] = "osx_gl";
constexpr char PlatformProvo[] = "provo";
constexpr char PlatformSalem[] = "salem";
constexpr char PlatformJasper[] = "jasper";
constexpr char PlatformServer[] = "server";
constexpr char PlatformCodeNameWindows[] = "Windows";
constexpr char PlatformCodeNameLinux[] = "Linux";
constexpr char PlatformCodeNameAndroid[] = "Android";
constexpr char PlatformCodeNameiOS[] = "iOS";
constexpr char PlatformCodeNameMac[] = "Mac";
constexpr char PlatformCodeNameProvo[] = "Provo";
constexpr char PlatformCodeNameSalem[] = "Salem";
constexpr char PlatformCodeNameJasper[] = "Jasper";
constexpr char PlatformAll[] = "all";
constexpr char PlatformAllClient[] = "all_client";
// Used for the capacity of a fixed vector to store the code names of platforms
// The value needs to be higher than the number of unique OS platforms that are supported(at this time 8)
constexpr size_t MaxPlatformCodeNames = 16;
//! This platform enum have platform values in sequence and can also be used to get the platform count.
AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int,
(Invalid, -1),
PC,
ES3,
IOS,
OSX,
PROVO,
SALEM,
JASPER,
SERVER, // Corresponds to the customer's flavor of "server" which could be windows, ubuntu, etc
ALL,
ALL_CLIENT,
// Add new platforms above this
NumPlatformIds
);
constexpr int NumClientPlatforms = 7;
constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently
enum class PlatformFlags : AZ::u32
{
Platform_NONE = 0x00,
Platform_PC = 1 << PlatformId::PC,
Platform_ES3 = 1 << PlatformId::ES3,
Platform_IOS = 1 << PlatformId::IOS,
Platform_OSX = 1 << PlatformId::OSX,
Platform_PROVO = 1 << PlatformId::PROVO,
Platform_SALEM = 1 << PlatformId::SALEM,
Platform_JASPER = 1 << PlatformId::JASPER,
Platform_SERVER = 1 << PlatformId::SERVER,
// A special platform that will always correspond to all platforms, even if new ones are added
Platform_ALL = 1ULL << 30,
// A special platform that will always correspond to all non-server platforms, even if new ones are added
Platform_ALL_CLIENT = 1ULL << 31,
AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags);
// 32 characters should be more than enough to store a platform name
using AssetPlatformFixedString = AZStd::fixed_string<32>;
// Fixed string which can store a comma separated list of platforms names
// Additional byte is added to take into account the comma
using AssetPlatformCombinedString = AZStd::fixed_string<(AssetPlatformFixedString{}.max_size() + 1) * PlatformId::NumPlatformIds>;
const char* PlatformIdToPalFolder(AzFramework::PlatformId platform);
const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform);
//! Platform Helper is an utility class that can be used to retrieve platform related information
class PlatformHelper
{
public:
//! Given a platformIndex returns the platform name
static const char* GetPlatformName(PlatformId platform);
//! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME.
static void AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, AZStd::string_view platformName);
//! Converts the platform name to the platform code names as defined in AZ_TRAIT_OS_PLATFORM_CODENAME.
static void AppendPlatformCodeNames(AZStd::fixed_vector<AZStd::string_view, MaxPlatformCodeNames>& platformCodes, PlatformId platformId);
//! Given a platform name returns a platform index.
//! If the platform is not found, the method returns -1.
static int GetPlatformIndexFromName(AZStd::string_view platformName);
//! Given a platform name returns a platform id.
//! If the platform is not found, the method returns -1.
static PlatformId GetPlatformIdFromName(AZStd::string_view platformName);
//! Given a platformIndex returns the platformFlags
static PlatformFlags GetPlatformFlagFromPlatformIndex(PlatformId platform);
//! Given a platformFlags returns all the platform identifiers that are set.
static AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> GetPlatforms(PlatformFlags platformFlags);
//! Given a platformFlags returns all the platform identifiers that are set, with special flags interpreted. Do not use the result for saving
static AZStd::fixed_vector<AZStd::string_view, PlatformId::NumPlatformIds> GetPlatformsInterpreted(PlatformFlags platformFlags);
//! Given a platformFlags return a list of PlatformId indices
static AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> GetPlatformIndices(PlatformFlags platformFlags);
//! Given a platformFlags return a list of PlatformId indices, with special flags interpreted. Do not use the result for saving
static AZStd::fixed_vector<PlatformId, PlatformId::NumPlatformIds> GetPlatformIndicesInterpreted(PlatformFlags platformFlags);
//! Given a platform identifier returns its corresponding platform flag.
static PlatformFlags GetPlatformFlag(AZStd::string_view platform);
//! Given any platformFlags returns a string listing the input platforms
static AssetPlatformCombinedString GetCommaSeparatedPlatformList(PlatformFlags platformFlags);
//! If platformFlags contains any special flags, they are removed and replaced with the normal flags they represent
static PlatformFlags GetPlatformFlagsInterpreted(PlatformFlags platformFlags);
//! Returns true if platformFlags contains any special flags
static bool IsSpecialPlatform(PlatformFlags platformFlags);
//! Returns true if platformFlags has checkPlatform flag set.
static bool HasPlatformFlag(PlatformFlags platformFlags, PlatformId checkPlatform);
};
using namespace AZ::PlatformDefaults;
}
#pragma pop_macro("IOS")
@@ -42,8 +42,14 @@ namespace AzFramework::ProjectManager
AZ::CommandLine commandLine;
commandLine.Parse(argc, argv);
AZ::SettingsRegistryImpl settingsRegistry;
// Store the Command line to the Setting Registry
AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(settingsRegistry);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
// Retrieve Command Line from Settings Registry, it may have been updated by the call to FindEngineRoot()
// in MergeSettingstoRegistry_ConfigFile
AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(settingsRegistry, commandLine, false);
engineRootPath = AZ::SettingsRegistryMergeUtils::FindEngineRoot(settingsRegistry);
projectRootPath = AZ::SettingsRegistryMergeUtils::FindProjectRoot(settingsRegistry);
@@ -317,7 +317,6 @@ set(FILES
Terrain/TerrainDataRequestBus.h
Terrain/TerrainDataRequestBus.cpp
Platform/PlatformDefaults.h
Platform/PlatformDefaults.cpp
Windowing/WindowBus.h
Windowing/NativeWindow.cpp
Windowing/NativeWindow.h
@@ -80,6 +80,8 @@ namespace AzGameFramework
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
#endif
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
}
AZ::ComponentTypeList GameApplication::GetRequiredSystemComponents() const
@@ -578,16 +578,6 @@ namespace AzToolsFramework
*/
virtual bool IsEditorInIsolationMode() = 0;
/*!
* Get the engine root path that the current tool is running under.
*/
virtual const char* GetEngineRootPath() const = 0;
/**
* Get the version of the engine the current tools application is running under
*/
virtual const char* GetEngineVersion() const = 0;
/**
* Creates and adds a new entity to the tools application from components which match at least one of the requiredTags
* The tag matching occurs on AZ::Edit::SystemComponentTags attribute from the reflected class data in the serialization context
@@ -224,112 +224,6 @@ namespace AzToolsFramework
} // Internal
#define AZ_MAX_ENGINE_VERSION_LEN 64
// Private Implementation class to manage the engine root and version
// Note: We are not using any AzCore classes because the ToolsApplication
// initialization happens early on, before the Allocators get instantiated,
// so we are using Qt privately instead
class ToolsApplication::EngineConfigImpl
{
private:
friend class ToolsApplication;
typedef QMap<QString, QString> EngineJsonMap;
EngineConfigImpl(const char* logWindow, const char* fileName)
: m_logWindow(logWindow)
, m_fileName(fileName)
{
m_engineRoot[0] = '\0';
m_engineVersion[0] = '\0';
}
char m_engineRoot[AZ_MAX_PATH_LEN];
char m_engineVersion[AZ_MAX_ENGINE_VERSION_LEN];
EngineJsonMap m_engineConfigMap;
const char* m_logWindow;
const char* m_fileName;
// Read an engine configuration into a map of key/value pairs
bool ReadEngineConfigIntoMap(QString engineJsonPath, EngineJsonMap& engineJsonMap)
{
QFile engineJsonFile(engineJsonPath);
if (!engineJsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
AZ_Warning(m_logWindow, false, "Unable to open file '%s' in the current root directory", engineJsonPath.toUtf8().data());
return false;
}
QByteArray engineJsonData = engineJsonFile.readAll();
engineJsonFile.close();
QJsonDocument engineJsonDoc(QJsonDocument::fromJson(engineJsonData));
if (engineJsonDoc.isNull())
{
AZ_Warning(m_logWindow, false, "Unable to read file '%s' in the current root directory", engineJsonPath.toUtf8().data());
return false;
}
QJsonObject engineJsonRoot = engineJsonDoc.object();
for (const QString& configKey : engineJsonRoot.keys())
{
QJsonValue configValue = engineJsonRoot[configKey];
if (configValue.isString() || configValue.isDouble())
{
// Only map strings and numbers, ignore every other type
engineJsonMap[configKey] = configValue.toString();
}
else
{
AZ_Warning(m_logWindow, false, "Ignoring key '%s' from '%s', unsupported type.", configKey.toUtf8().data(), engineJsonPath.toUtf8().data());
}
}
return true;
}
// Initialize the engine config object based on the current
bool Initialize(const char* currentEngineRoot)
{
// Start with the app root as the engine root (legacy), but check to see if the engine root
// is external to the app root
azstrncpy(m_engineRoot, AZ_ARRAY_SIZE(m_engineRoot), currentEngineRoot, strlen(currentEngineRoot) + 1);
// From the appRoot, check and see if we can read any external engine reference in engine.json
QString engineJsonFileName = QString(m_fileName);
QString engineJsonFilePath = QDir(currentEngineRoot).absoluteFilePath(engineJsonFileName);
// From the appRoot, check and see if we can read any external engine reference in engine.json
if (!QFile::exists(engineJsonFilePath))
{
AZ_Warning(m_logWindow, false, "Unable to find '%s' in the current app root directory.", m_fileName);
return false;
}
if (!ReadEngineConfigIntoMap(engineJsonFilePath, m_engineConfigMap))
{
AZ_Warning(m_logWindow, false, "Defaulting root engine path to '%s'", currentEngineRoot);
return false;
}
// Read in the local engine version value
auto localEngineVersionValue = m_engineConfigMap.find(QString(AzToolsFramework::Internal::s_engineConfigEngineVersionKey));
QString localEngineVersion(localEngineVersionValue.value());
azstrncpy(m_engineVersion, AZ_ARRAY_SIZE(m_engineVersion), localEngineVersion.toUtf8().data(), localEngineVersion.length() + 1);
return true;
}
const char* GetEngineRoot() const
{
return m_engineRoot;
}
const char* GetEngineVersion() const
{
return m_engineVersion;
}
};
ToolsApplication::ToolsApplication(int* argc, char*** argv)
: AzFramework::Application(argc, argv)
, m_selectionBounds(AZ::Aabb())
@@ -339,7 +233,6 @@ namespace AzToolsFramework
, m_isInIsolationMode(false)
{
ToolsApplicationRequests::Bus::Handler::BusConnect();
m_engineConfigImpl.reset(new ToolsApplication::EngineConfigImpl(AzToolsFramework::Internal::s_startupLogWindow, AzToolsFramework::Internal::s_engineConfigFileName));
m_undoCache.RegisterToUndoCacheInterface();
}
@@ -391,7 +284,6 @@ namespace AzToolsFramework
void ToolsApplication::Start(const Descriptor& descriptor, const StartupParameters& startupParameters/* = StartupParameters()*/)
{
Application::Start(descriptor, startupParameters);
InitializeEngineConfig();
m_editorEntityManager.Start();
@@ -399,14 +291,6 @@ namespace AzToolsFramework
AZ_Assert(m_editorEntityAPI, "ToolsApplication - Could not retrieve instance of EditorEntityAPI");
}
void ToolsApplication::InitializeEngineConfig()
{
if (!m_engineConfigImpl->Initialize(GetEngineRoot()))
{
AZ_Warning(AzToolsFramework::Internal::s_startupLogWindow, false, "Defaulting engine root path to '%s'", GetEngineRoot());
}
}
void ToolsApplication::StartCommon(AZ::Entity* systemEntity)
{
Application::StartCommon(systemEntity);
@@ -1832,16 +1716,6 @@ namespace AzToolsFramework
return m_isInIsolationMode;
}
const char* ToolsApplication::GetEngineRootPath() const
{
return m_engineConfigImpl->GetEngineRoot();
}
const char* ToolsApplication::GetEngineVersion() const
{
return m_engineConfigImpl->GetEngineVersion();
}
void ToolsApplication::CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName)
{
if (!entityName || !entityName[0])
@@ -150,8 +150,6 @@ namespace AzToolsFramework
void EnterEditorIsolationMode() override;
void ExitEditorIsolationMode() override;
bool IsEditorInIsolationMode() override;
const char* GetEngineRootPath() const override;
const char* GetEngineVersion() const override;
void CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName) override;
@@ -174,7 +172,6 @@ namespace AzToolsFramework
void CreateUndosForDirtyEntities();
void ConsistencyCheckUndoCache();
void InitializeEngineConfig();
AZ::Aabb m_selectionBounds;
EntityIdList m_selectedEntities;
EntityIdList m_highlightedEntities;
@@ -186,9 +183,6 @@ namespace AzToolsFramework
bool m_isInIsolationMode;
EntityIdSet m_isolatedEntityIdSet;
class EngineConfigImpl;
AZStd::unique_ptr<EngineConfigImpl> m_engineConfigImpl;
EditorEntityAPI* m_editorEntityAPI = nullptr;
EditorEntityManager m_editorEntityManager;
@@ -136,6 +136,48 @@ namespace AzToolsFramework
return entity->GetName();
}
EntityList EntityIdListToEntityList(const EntityIdList& inputEntityIds)
{
EntityList entities;
entities.reserve(inputEntityIds.size());
for (AZ::EntityId entityId : inputEntityIds)
{
if (!entityId.IsValid())
{
continue;
}
if (auto entity = GetEntityById(entityId))
{
entities.emplace_back(entity);
}
}
return entities;
}
EntityList EntityIdSetToEntityList(const EntityIdSet& inputEntityIds)
{
EntityList entities;
entities.reserve(inputEntityIds.size());
for (AZ::EntityId entityId : inputEntityIds)
{
if (!entityId.IsValid())
{
continue;
}
if (auto entity = GetEntityById(entityId))
{
entities.emplace_back(entity);
}
}
return entities;
}
void GetAllComponentsForEntity(const AZ::Entity* entity, AZ::Entity::ComponentArrayType& componentsOnEntity)
{
if (entity)
@@ -1068,6 +1110,45 @@ namespace AzToolsFramework
return !allEntityClonesContainer.m_entities.empty();
}
EntityIdSet GetCulledEntityHierarchy(const EntityIdList& entities)
{
EntityIdSet culledEntities;
for (const AZ::EntityId& entityId : entities)
{
bool selectionIncludesTransformHeritage = false;
AZ::EntityId parentEntityId = entityId;
do
{
AZ::EntityId nextParentId;
AZ::TransformBus::EventResult(
/*result*/ nextParentId,
/*address*/ parentEntityId,
&AZ::TransformBus::Events::GetParentId);
parentEntityId = nextParentId;
if (!parentEntityId.IsValid())
{
break;
}
for (const AZ::EntityId& parentCheck : entities)
{
if (parentCheck == parentEntityId)
{
selectionIncludesTransformHeritage = true;
break;
}
}
} while (parentEntityId.IsValid() && !selectionIncludesTransformHeritage);
if (!selectionIncludesTransformHeritage)
{
culledEntities.insert(entityId);
}
}
return culledEntities;
}
namespace Internal
{
void CloneSliceEntitiesAndChildren(
@@ -47,6 +47,9 @@ namespace AzToolsFramework
AZStd::string GetEntityName(const AZ::EntityId& entityId, const AZStd::string_view& nameOverride = {});
EntityList EntityIdListToEntityList(const EntityIdList& inputEntityIds);
EntityList EntityIdSetToEntityList(const EntityIdSet& inputEntityIds);
template <typename... ComponentTypes>
struct AddComponents
{
@@ -202,4 +205,8 @@ namespace AzToolsFramework
/// Wrap EBus SetSelectedEntities call.
void SelectEntities(const AzToolsFramework::EntityIdList& entities);
/// Return a set of entities, culling any that have an ancestor in the list.
/// e.g. This is useful for getting a concise set of entities that need to be duplicated.
EntityIdSet GetCulledEntityHierarchy(const EntityIdList& entities);
}; // namespace AzToolsFramework
@@ -153,7 +153,7 @@ namespace AzToolsFramework
AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance)
{
// Retrieve entityList from entityIds
EntityIdListToEntityList(entityIds, inputEntityList);
inputEntityList = EntityIdListToEntityList(entityIds);
// Find common root and top level entities
bool entitiesHaveCommonRoot = false;
@@ -485,8 +485,7 @@ namespace AzToolsFramework
InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityIds[0]);
// Retrieve entityList from entityIds
EntityList inputEntityList;
EntityIdListToEntityList(entityIds, inputEntityList);
EntityList inputEntityList = EntityIdListToEntityList(entityIds);
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -833,18 +832,5 @@ namespace AzToolsFramework
return true;
}
void PrefabPublicHandler::EntityIdListToEntityList(const EntityIdList& inputEntityIds, EntityList& outEntities)
{
outEntities.reserve(inputEntityIds.size());
for (AZ::EntityId entityId : inputEntityIds)
{
if (entityId.IsValid())
{
outEntities.emplace_back(GetEntityById(entityId));
}
}
}
}
}
@@ -99,7 +99,6 @@ namespace AzToolsFramework
static Instance* GetParentInstance(Instance* instance);
static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant);
static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation);
static void EntityIdListToEntityList(const EntityIdList& inputEntityIds, EntityList& outEntities);
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
@@ -38,6 +38,7 @@ AZ_POP_DISABLE_WARNING
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Asset/SimpleAsset.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
@@ -1212,9 +1213,8 @@ namespace AzToolsFramework
if (!QFile::exists(path))
{
const char* engineRoot = nullptr;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath);
QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current();
AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current();
path = engineDir.absoluteFilePath(iconPath.c_str());
}
@@ -68,7 +68,7 @@ namespace UnitTest
{
assets[idx] = AssetId(AZ::Uuid::CreateRandom(), 0);
AZ::Data::AssetInfo info;
info.m_relativePath = AZStd::string::format("Asset%d.txt", idx);
info.m_relativePath = AZStd::string::format("asset%d.txt", idx);
m_assetsPath[idx] = info.m_relativePath;
info.m_assetId = assets[idx];
m_assetRegistry->RegisterAsset(assets[idx], info);
@@ -623,7 +623,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList1, assets[fileIndex]));
if (m_fileStreams[0][fileIndex].Open(m_assetsPathFull[0][fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath))
{
AZStd::string fileContent = AZStd::string::format("Asset%d.txt", fileIndex);
AZStd::string fileContent = AZStd::string::format("asset%d.txt", fileIndex);
m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str());
m_fileStreams[0][fileIndex].Close();
}
@@ -654,7 +654,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList1, assets[fileIndex]));
if (m_fileStreams[0][fileIndex].Open(m_assetsPathFull[0][fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath))
{
AZStd::string fileContent = AZStd::string::format("Asset%d.txt", fileIndex + 1);// changing file content
AZStd::string fileContent = AZStd::string::format("asset%d.txt", fileIndex + 1);// changing file content
m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str());
m_fileStreams[0][fileIndex].Close();
}
@@ -987,7 +987,7 @@ namespace UnitTest
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset("Asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(secondSeedList.size(), 0);
}
@@ -1003,7 +1003,7 @@ namespace UnitTest
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset("Asset0.txt", AzFramework::PlatformFlags::Platform_PC);
m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC);
const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(secondSeedList.size(), 1);
}
@@ -1017,7 +1017,7 @@ namespace UnitTest
EXPECT_EQ(seedList.size(), 1);
m_assetSeedManager->RemoveSeedAsset("Asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(secondSeedList.size(), 1);
}
@@ -0,0 +1,57 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
namespace UnitTest
{
class EditorEntityHelpersTest
: public ToolsApplicationFixture
{
void SetUpEditorFixtureImpl() override
{
m_parent1 = CreateDefaultEditorEntity("Parent1");
m_child1 = CreateDefaultEditorEntity("Child1");
m_child2 = CreateDefaultEditorEntity("Child2");
m_grandChild1 = CreateDefaultEditorEntity("GrandChild1");
m_parent2 = CreateDefaultEditorEntity("Parent2");
AZ::TransformBus::Event(m_child1, &AZ::TransformBus::Events::SetParent, m_parent1);
AZ::TransformBus::Event(m_child2, &AZ::TransformBus::Events::SetParent, m_parent1);
AZ::TransformBus::Event(m_grandChild1, &AZ::TransformBus::Events::SetParent, m_child1);
}
public:
AZ::EntityId m_parent1;
AZ::EntityId m_child1;
AZ::EntityId m_child2;
AZ::EntityId m_grandChild1;
AZ::EntityId m_parent2;
};
TEST_F(EditorEntityHelpersTest, EditorEntityHelpersTests_GetCulledEntityHierarchy)
{
AzToolsFramework::EntityIdList testEntityIds{ m_parent1, m_child1, m_child2, m_grandChild1, m_parent2 };
AzToolsFramework::EntityIdSet culledSet = AzToolsFramework::GetCulledEntityHierarchy(testEntityIds);
// There should only be two EntityIds returned (m_parent1, and m_parent2),
// since all the others should be culled out since they have a common ancestor
// in the list already
using ::testing::UnorderedElementsAre;
EXPECT_THAT(culledSet, UnorderedElementsAre(m_parent1, m_parent2));
}
}
@@ -85,7 +85,9 @@ set(FILES
Prefab/SpawnableSortEntitiesTestFixture.cpp
Prefab/SpawnableSortEntitiesTestFixture.h
Entity/EditorEntityContextComponentTests.cpp
Entity/EditorEntityHelpersTests.cpp
Entity/EditorEntitySearchComponentTests.cpp
Entity/EditorEntitySelectionTests.cpp
SliceStabilityTests/SliceStabilityTestFramework.h
SliceStabilityTests/SliceStabilityTestFramework.cpp
SliceStabilityTests/SliceStabilityCreateTests.cpp
@@ -20,6 +20,7 @@
// AzCore
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Utils/Utils.h>
// AzToolsFramework
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
@@ -104,13 +105,9 @@ void AssetEditorWindow::SaveAssetAs(const AZStd::string_view assetPath)
return;
}
const char* engineRoot;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath);
auto absoluteAssetPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / assetPath;
AZStd::string absoluteAssetPath;
AzFramework::StringFunc::Path::Join(engineRoot, assetPath.data(), absoluteAssetPath);
if (!m_ui->m_assetEditorWidget->SaveAssetToPath(absoluteAssetPath))
if (!m_ui->m_assetEditorWidget->SaveAssetToPath(absoluteAssetPath.Native()))
{
AZ_Warning("Asset Editor", false, "File was not saved correctly via SaveAssetAs.");
}
@@ -1617,11 +1617,6 @@ void EditorViewportWidget::keyPressEvent(QKeyEvent* event)
// because we want the movement to be butter smooth.
if (!event->isAutoRepeat())
{
if (m_keyDown.isEmpty())
{
grabKeyboard();
}
m_keyDown.insert(event->key());
}
+4 -3
View File
@@ -19,6 +19,8 @@
#include <QMessageBox>
#include <QFileDialog>
#include <AzCore/Utils/Utils.h>
// AzToolsFramework
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
@@ -293,9 +295,8 @@ namespace
// If not found try editor folder
if (!CFileUtil::FileExists(path))
{
const char* engineRoot = nullptr;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath);
QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current();
AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current();
QString scriptFolder = engineDir.absoluteFilePath("Editor/Scripts/");
Path::ConvertBackSlashToSlash(scriptFolder);
+4 -3
View File
@@ -18,6 +18,8 @@
#include "ToolBox.h"
#include <AzCore/Utils/Utils.h>
// AzToolsFramework
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
@@ -419,9 +421,8 @@ void CToolBoxManager::Load(QString xmlpath, AmazonToolbar* pToolbar, bool bToolb
}
}
const char* engineRoot = nullptr;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath);
QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current();
AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current();
string enginePath = PathUtil::AddSlash(engineDir.absolutePath().toUtf8().data());
@@ -670,9 +670,13 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
action = menu->addAction(QObject::tr("Create layer"));
QObject::connect(action, &QAction::triggered, [this] { ContextMenu_NewLayer(); });
AzToolsFramework::EntityIdList entities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
entities,
&AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
SetupLayerContextMenu(menu);
AzToolsFramework::EntityIdSet flattenedSelection;
GetSelectedEntitiesSetWithFlattenedHierarchy(flattenedSelection);
AzToolsFramework::EntityIdSet flattenedSelection = AzToolsFramework::GetCulledEntityHierarchy(entities);
AzToolsFramework::SetupAddToLayerMenu(menu, flattenedSelection, [this] { return ContextMenu_NewLayer(); });
SetupSliceContextMenu(menu);
@@ -1220,10 +1224,14 @@ void SandboxIntegrationManager::CloneSelection(bool& handled)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
AzToolsFramework::EntityIdSet duplicationSet;
GetSelectedEntitiesSetWithFlattenedHierarchy(duplicationSet);
AzToolsFramework::EntityIdList entities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
entities,
&AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
if (duplicationSet.size() > 0)
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entities);
if (!duplicationSet.empty())
{
AZStd::unordered_set<AZ::EntityId> clonedEntities;
handled = AzToolsFramework::CloneInstantiatedEntities(duplicationSet, clonedEntities);
@@ -42,7 +42,108 @@ namespace AZ
// Downstream only supports 30 frames per second sample rate. Adjusting to 60 doubles the
// length of the animations, they still play back at 30 frames per second.
const double AssImpAnimationImporter::s_defaultTimeStepSampleRate = 1.0 / 30.0;
const double AssImpAnimationImporter::s_defaultTimeStepBetweenFrames = 1.0 / 30.0;
AZ::u32 GetNumKeyFrames(AZ::u32 keysSize, double duration, double ticksPerSecond)
{
if (AZ::IsClose(ticksPerSecond, 0))
{
AZ_Warning("AnimationImporter", false, "Animation ticks per second should not be zero, defaulting to %d keyframes for animation.", keysSize);
return keysSize;
}
const double totalTicks = duration / ticksPerSecond;
AZ::u32 numKeys = keysSize;
// +1 because the animation is from [0, duration] - we have a keyframe at the end of the duration which needs to be included
double totalFramesAtDefaultTimeStep = totalTicks / AssImpAnimationImporter::s_defaultTimeStepBetweenFrames + 1;
if (!AZ::IsClose(totalFramesAtDefaultTimeStep, numKeys, 1))
{
numKeys = AZStd::ceilf(totalFramesAtDefaultTimeStep);
}
return numKeys;
}
double GetTimeForFrame(AZ::u32 frame, double ticksPerSecond)
{
return frame * AssImpAnimationImporter::s_defaultTimeStepBetweenFrames * ticksPerSecond;
}
// Helper class to store key data, when translating from AssImp layout to the engine's scene format.
struct KeyData
{
KeyData(float value, float time) :
mValue(value),
mTime(time)
{
}
bool operator<(const KeyData& other) const
{
return mTime < other.mTime;
}
float mValue = 0;
float mTime = 0;
};
template<class T>
void LerpTemplate(T& start, const T& end, float t)
{
start = start * (1.0f - t) + end * t;
}
template<>
void LerpTemplate(aiQuaternion& start, const aiQuaternion& end, float t)
{
aiQuaternion::Interpolate(start, start, end, t);
}
template<>
void LerpTemplate(float& start, const float& end, float t)
{
start = AZ::Lerp(start, end, t);
}
template<class KeyContainerType, class FrameValueType>
bool SampleKeyFrame(FrameValueType& result, const KeyContainerType& keys, AZ::u32 numKeys, double time, AZ::u32& lastIndex)
{
if (numKeys == 0)
{
AZ_Error("AnimationImporter", numKeys > 0, "Animation key set must have at least 1 key");
return false;
}
if (numKeys == 1)
{
result = keys[0].mValue;
return true;
}
while (lastIndex < numKeys - 1 && time >= keys[lastIndex + 1].mTime)
{
++lastIndex;
}
result = keys[lastIndex].mValue;
if (lastIndex < numKeys - 1)
{
auto nextValue = keys[lastIndex + 1].mValue;
float normalizedTimeBetweenFrames = 0;
if (keys[lastIndex + 1].mTime != keys[lastIndex].mTime)
{
normalizedTimeBetweenFrames =
(time - keys[lastIndex].mTime) / (keys[lastIndex + 1].mTime - keys[lastIndex].mTime);
}
else
{
AZ_Warning("AnimationImporter", false,
"Animation has keys with duplicate time %5.5f, at indices %d and %d. The second will be ignored.",
keys[lastIndex].mTime,
lastIndex,
lastIndex + 1);
}
LerpTemplate(result, nextValue, normalizedTimeBetweenFrames);
}
return true;
}
AssImpAnimationImporter::AssImpAnimationImporter()
{
@@ -199,6 +300,14 @@ namespace AZ
for (AZ::u32 animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex)
{
const aiAnimation* animation = scene->mAnimations[animIndex];
if (animation->mTicksPerSecond == 0)
{
AZ_Error(
"AnimationImporter", false,
"Animation name %s has a sample rate of 0 ticks per second and cannot be processed.",
animation->mName.C_Str());
return Events::ProcessingResult::Failure;
}
mapAnimationsFunc(animation->mNumChannels, animation->mChannels, animation, boneAnimations);
@@ -410,70 +519,38 @@ namespace AZ
anim->mNumPositionKeys, anim->mNumRotationKeys, anim->mNumScalingKeys);
return Events::ProcessingResult::Failure;
}
auto sampleKeyFrame = [](const auto& keys, AZ::u32 numKeys, double time, AZ::u32& lastIndex)
{
AZ_Error("AnimationImporter", numKeys > 0, "Animation key set must have at least 1 key");
if (numKeys == 1)
{
return keys[0].mValue;
}
auto returnValue = keys[0].mValue;
for (AZ::u32 keyIndex = lastIndex; keyIndex < numKeys; ++keyIndex)
{
const auto& key = keys[keyIndex];
lastIndex = keyIndex;
// We want to return the key that exactly matches the time if possible, otherwise we'll keep track of the previous time
// If we don't find an exact match and end up going past the desired time (or run out of keyframes) then we return the previous key
if (key.mTime < time)
{
returnValue = key.mValue;
}
else if (AZ::IsClose(key.mTime, time))
{
return key.mValue;
}
else
{
return returnValue;
}
}
return returnValue;
};
// Resample the animations at a fixed time step. This matches the behaviour of
// the previous SDK used. Longer term, this could be data driven, or based on the
// smallest time step between key frames.
// AssImp has an animation->mTicksPerSecond and animation->mDuration, but those
// are less predictable than just using a fixed time step.
const double duration = animation->mDuration / animation->mTicksPerSecond;
// AssImp documentation claims animation->mDuration is the duration of the animation in ticks, but
// not all animations we've tested follow that pattern. Sometimes duration is in seconds.
const AZ::u32 numKeyFrames = GetNumKeyFrames(
AZStd::max(AZStd::max(anim->mNumScalingKeys, anim->mNumPositionKeys), anim->mNumRotationKeys),
animation->mDuration,
animation->mTicksPerSecond);
AZ::u32 numKeyFrames = AZStd::max(AZStd::max(anim->mNumScalingKeys, anim->mNumPositionKeys), anim->mNumRotationKeys);
if (!AZ::IsClose(duration / s_defaultTimeStepSampleRate, numKeyFrames, 1))
{
double dT = duration / s_defaultTimeStepSampleRate;
numKeyFrames = AZStd::ceilf(dT) + 1; // +1 because the animation is from [0, duration] - we have a keyframe at the end of the duration which needs to be included
}
AZStd::shared_ptr<SceneData::GraphData::AnimationData> createdAnimationData =
AZStd::make_shared<SceneData::GraphData::AnimationData>();
createdAnimationData->ReserveKeyFrames(numKeyFrames);
createdAnimationData->SetTimeStepBetweenFrames(s_defaultTimeStepSampleRate);
createdAnimationData->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
AZ::u32 lastScaleIndex = 0;
AZ::u32 lastPositionIndex = 0;
AZ::u32 lastRotationIndex = 0;
for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
{
double time = frame * s_defaultTimeStepSampleRate * animation->mTicksPerSecond;
aiVector3D scale = sampleKeyFrame(anim->mScalingKeys, anim->mNumScalingKeys, time, lastScaleIndex);
aiVector3D position = sampleKeyFrame(anim->mPositionKeys, anim->mNumPositionKeys, time, lastPositionIndex);
aiQuaternion rotation = sampleKeyFrame(anim->mRotationKeys, anim->mNumRotationKeys, time, lastRotationIndex);
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
aiVector3D scale = aiVector3D(1.f, 1.f, 1.f), position = aiVector3D(0.f, 0.f, 0.f);
aiQuaternion rotation(1.f, 0.f, 0.f, 0.f);
if (!SampleKeyFrame(scale, anim->mScalingKeys, anim->mNumScalingKeys, time, lastScaleIndex) ||
!SampleKeyFrame(position, anim->mPositionKeys, anim->mNumPositionKeys, time, lastPositionIndex) ||
!SampleKeyFrame(rotation, anim->mRotationKeys, anim->mNumRotationKeys, time, lastRotationIndex))
{
return Events::ProcessingResult::Failure;
}
aiMatrix4x4 transform(scale, rotation, position);
@@ -520,28 +597,6 @@ namespace AZ
// SetTimeStepBetweenFrames set on the animation data
// Keyframes. Weights (Values in FBX SDK) per key time.
// Keyframes generated for every single frame of the animation.
// Helper class to store key data, when translating from AssImp layout to the engine's scene format.
struct KeyData
{
KeyData(float weight, float time) :
m_weight(weight),
m_time(time)
{
}
bool operator<(const KeyData& other) const
{
return m_time < other.m_time;
}
// Naming in the previous SDK (FBX SDK) and in the engine's scene format
// doesn't match AssImp's naming convention.
// weight here is the AssImp's name for the data, it was named value in FBX SDK.
float m_weight = 0;
float m_time = 0;
};
typedef AZStd::map<int, AZStd::vector<KeyData>> ValueToKeyDataMap;
ValueToKeyDataMap valueToKeyDataMap;
@@ -562,44 +617,27 @@ namespace AZ
{
AZStd::shared_ptr<SceneData::GraphData::BlendShapeAnimationData> morphAnimNode =
AZStd::make_shared<SceneData::GraphData::BlendShapeAnimationData>();
morphAnimNode->ReserveKeyFrames(animation->mDuration + 1);
morphAnimNode->SetTimeStepBetweenFrames(1.0 / animation->mTicksPerSecond);
const AZ::u32 numKeyFrames = GetNumKeyFrames(keys.size(), animation->mDuration, animation->mTicksPerSecond);
morphAnimNode->ReserveKeyFrames(numKeyFrames);
morphAnimNode->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
aiAnimMesh* aiAnimMesh = mesh->mAnimMeshes[meshIdx];
AZStd::string_view nodeName(aiAnimMesh->mName.C_Str());
const AZ::u32 maxKeys = keys.size();
AZ::u32 keyIdx = 0;
for (AZ::u32 time = 0; time <= animation->mDuration; ++time)
for (AZ::u32 frame = 0; frame <= numKeyFrames; ++frame)
{
if (keyIdx < maxKeys - 1 && time >= keys[keyIdx+1].m_time)
{
++keyIdx;
}
float weight_value = keys[keyIdx].m_weight;
if (keyIdx < maxKeys - 1)
{
float nextWeight = keys[keyIdx+1].m_weight;
float normalizedTimeBetweenFrames = 0;
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
if (keys[keyIdx + 1].m_time != keys[keyIdx].m_time)
{
normalizedTimeBetweenFrames =
(time - keys[keyIdx].m_time) / (keys[keyIdx + 1].m_time - keys[keyIdx].m_time);
}
else
{
AZ_Warning("AnimationImporter", false,
"Morph target mesh %s has keys with duplicate time, at indices %d and %d. The second will be ignored.",
nodeName.data(),
keyIdx,
keyIdx+1);
}
// AssImp and FBX both only support linear interpolation for blend shapes.
weight_value = AZ::Lerp(weight_value, nextWeight, normalizedTimeBetweenFrames);
float weight = 0;
if (!SampleKeyFrame(weight, keys, keys.size(), time, keyIdx))
{
return Events::ProcessingResult::Failure;
}
morphAnimNode->AddKeyFrame(weight_value);
morphAnimNode->AddKeyFrame(weight);
}
@@ -45,7 +45,7 @@ namespace AZ
const aiMeshMorphAnim* meshMorphAnim,
const aiMesh* mesh);
static const double s_defaultTimeStepSampleRate;
static const double s_defaultTimeStepBetweenFrames;
protected:
static const char* s_animationNodeName;
@@ -43,7 +43,7 @@ namespace AZ
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<SceneGraph::NodeIndex>()
behaviorContext->Class<SceneGraph::NodeIndex>("NodeIndex")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene.graph")
->Constructor<>()
@@ -57,7 +57,7 @@ namespace AZ
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
behaviorContext->Class<SceneGraph::Name>()
behaviorContext->Class<SceneGraph::Name>("SceneGraphName")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene.graph")
->Constructor()
@@ -48,7 +48,7 @@ namespace AWSClientAuth
m_settingsRegistry = AZStd::make_shared<AZ::SettingsRegistryImpl>();
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPath{};
AZ::IO::FileIOBase::GetInstance()->ResolvePath(settingsRegistryPath.data(), resolvedPath.data(), resolvedPath.size());
fileIO->ResolvePath(settingsRegistryPath.data(), resolvedPath.data(), resolvedPath.size());
if (!m_settingsRegistry->MergeSettingsFile(resolvedPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch))
@@ -49,11 +49,6 @@
"IsArray": 1
}
},
{
"Name": "DepthStencilInputOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "DepthStencil"
},
{
"Name": "TileLightData",
"SlotType": "Input",
@@ -66,6 +61,13 @@
"ShaderInputName": "m_lightListRemapped",
"ScopeAttachmentUsage": "Shader"
},
// Input/Outputs...
{
"Name": "DepthStencilInputOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "DepthStencil"
},
// Outputs...
{
"Name": "DiffuseOutput",
"SlotType": "Output",
@@ -222,11 +224,12 @@
"Attachment": "Output"
}
},
"MultisampleSource": {
"Pass": "This",
"Attachment": "DepthStencilInputOutput"
},
"ImageDescriptor": {
"Format": "R8G8B8A8_UNORM",
"MultisampleState": {
"samples": 4
},
"SharedQueueMask": "Graphics"
}
},
@@ -9,7 +9,8 @@
"Slots": [
{
"Name": "Output",
"SlotType": 2
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
}
],
"PassRequests": [
@@ -101,13 +102,8 @@
]
},
{
"Name": "DepthMSAAPass",
"TemplateName": "EnvironmentCubeMapDepthMSAAPassTemplate",
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "depth",
"PipelineViewTag": "MainCamera"
},
"Name": "DepthPrePass",
"TemplateName": "DepthMSAAParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
@@ -115,70 +111,11 @@
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
}
]
},
// The light culling system can do highly accurate culling of transparent objects but it needs
// more depth information than the opaque geometry pass can provide
// Specifically the minimum and maximum depth of transparent objects
{
"Name": "DepthTransparentMinPass",
"TemplateName": "DepthPassTemplate",
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "depthTransparentMin",
"PipelineViewTag": "MainCamera"
},
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
}
]
},
{
"Name": "DepthTransparentMaxPass",
"TemplateName": "DepthMaxPassTemplate",
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "depthTransparentMax",
"PipelineViewTag": "MainCamera"
},
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
}
]
},
{
"Name": "LightCullingTilePreparePass",
"TemplateName": "LightCullingTilePrepareMSAATemplate",
"Connections": [
{
"LocalSlot": "Depth",
"AttachmentRef": {
"Pass": "DepthMSAAPass",
"Attachment": "Output"
}
},
{
"LocalSlot": "DepthTransparentMin",
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "DepthTransparentMinPass",
"Attachment": "Output"
}
},
{
"LocalSlot": "DepthTransparentMax",
"AttachmentRef": {
"Pass": "DepthTransparentMaxPass",
"Pass": "Parent",
"Attachment": "Output"
}
}
@@ -186,40 +123,27 @@
},
{
"Name": "LightCullingPass",
"TemplateName": "LightCullingTemplate",
"TemplateName": "LightCullingParentTemplate",
"Connections": [
{
"LocalSlot": "TileLightData",
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "LightCullingTilePreparePass",
"Attachment": "TileLightData"
}
}
]
},
{
"Name": "LightCullingRemapPass",
"TemplateName": "LightCullingRemapTemplate",
"Connections": [
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingTilePreparePass",
"Attachment": "TileLightData"
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "LightCount",
"LocalSlot": "DepthMSAA",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "LightCount"
"Pass": "DepthPrePass",
"Attachment": "DepthMSAA"
}
},
{
"LocalSlot": "LightList",
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "LightList"
"Pass": "Parent",
"Attachment": "Output"
}
}
]
@@ -259,21 +183,21 @@
{
"LocalSlot": "DepthStencilInputOutput",
"AttachmentRef": {
"Pass": "DepthMSAAPass",
"Attachment": "Output"
"Pass": "DepthPrePass",
"Attachment": "DepthMSAA"
}
},
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingRemapPass",
"Pass": "LightCullingPass",
"Attachment": "TileLightData"
}
},
{
"LocalSlot": "LightListRemapped",
"AttachmentRef": {
"Pass": "LightCullingRemapPass",
"Pass": "LightCullingPass",
"Attachment": "LightListRemapped"
}
}
@@ -32,11 +32,12 @@
"Attachment": "SpecularInputOutput"
}
},
"MultisampleSource": {
"Pass": "This",
"Attachment": "SpecularInputOutput"
},
"ImageDescriptor": {
"Format": "R16G16B16A16_FLOAT",
"MultisampleState": {
"samples": 4
},
"SharedQueueMask": "Graphics"
}
}
@@ -78,7 +78,6 @@ namespace AZ
AZ_CLASS_ALLOCATOR(LightingPreset, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
bool m_autoSelect = false;
AZStd::string m_displayName;
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_iblDiffuseImageAsset;
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_iblSpecularImageAsset;
@@ -32,7 +32,6 @@ namespace AZ
AZ_CLASS_ALLOCATOR(ModelPreset, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
bool m_autoSelect = false;
AZStd::string m_displayName;
AZ::Data::Asset<AZ::RPI::ModelAsset> m_modelAsset;
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_previewImageAsset;
@@ -131,8 +131,13 @@ namespace AZ
AZ_Assert(m_blendedLut.m_lutImage != nullptr, "BlendColorGradingLutsPass unable to acquire LUT image");
AZ::RHI::AttachmentId imageAttachmentId = AZ::RHI::AttachmentId("BlendColorGradingLutImageAttachmentId");
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(imageAttachmentId, m_blendedLut.m_lutImage);
AZ_Error("BlendColorGradingLutsPass", result == RHI::ResultCode::Success, "Failed to import compute buffer with error %d", result);
// import this attachment if it wasn't imported
if (!frameGraph.GetAttachmentDatabase().IsAttachmentValid(imageAttachmentId))
{
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(imageAttachmentId, m_blendedLut.m_lutImage);
AZ_Error("BlendColorGradingLutsPass", result == RHI::ResultCode::Success, "Failed to import BlendColorGradingLutImageAttachmentId with error %d", result);
}
RHI::ImageScopeAttachmentDescriptor desc;
desc.m_attachmentId = imageAttachmentId;
@@ -248,6 +248,9 @@ namespace AZ
AZ::RPI::RenderPipelineDescriptor environmentCubeMapPipelineDesc;
environmentCubeMapPipelineDesc.m_mainViewTagName = "MainCamera";
environmentCubeMapPipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
environmentCubeMapPipelineDesc.m_renderSettings.m_size.m_width = RPI::EnvironmentCubeMapPass::CubeMapFaceSize;
environmentCubeMapPipelineDesc.m_renderSettings.m_size.m_height = RPI::EnvironmentCubeMapPass::CubeMapFaceSize;
// create a unique name for the pipeline
AZ::Uuid uuid = AZ::Uuid::CreateRandom();
@@ -112,7 +112,6 @@ namespace AZ
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &LightingPreset::m_displayName, "Display Name", "Identifier used for display and selection")
->DataElement(AZ::Edit::UIHandlers::Default, &LightingPreset::m_autoSelect, "Auto Select", "When true, the configuration is automatically selected when loaded")
->DataElement(AZ::Edit::UIHandlers::Default, &LightingPreset::m_iblDiffuseImageAsset, "IBL Diffuse Image Asset", "IBL diffuse image asset reference")
->DataElement(AZ::Edit::UIHandlers::Default, &LightingPreset::m_iblSpecularImageAsset, "IBL Specular Image Asset", "IBL specular image asset reference")
->DataElement(AZ::Edit::UIHandlers::Default, &LightingPreset::m_skyboxImageAsset, "Skybox Image Asset", "Skybox image asset reference")
@@ -32,7 +32,6 @@ namespace AZ
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ModelPreset::m_displayName, "Display Name", "Identifier used for display and selection")
->DataElement(AZ::Edit::UIHandlers::Default, &ModelPreset::m_autoSelect, "Auto Select", "When true, the configuration is automatically selected when loaded")
->DataElement(AZ::Edit::UIHandlers::Default, &ModelPreset::m_modelAsset, "Model Asset", "Model asset reference")
->DataElement(AZ::Edit::UIHandlers::Default, &ModelPreset::m_previewImageAsset, "Preview Image Asset", "Preview image asset reference")
;
@@ -105,8 +105,7 @@ namespace AZ
serializeContext->RegisterGenericType<AZStd::vector<LightConfig>>();
serializeContext->Class<LightingPreset>()
->Version(4)
->Field("autoSelect", &LightingPreset::m_autoSelect)
->Version(5)
->Field("displayName", &LightingPreset::m_displayName)
->Field("iblDiffuseImageAsset", &LightingPreset::m_iblDiffuseImageAsset)
->Field("iblSpecularImageAsset", &LightingPreset::m_iblSpecularImageAsset)
@@ -128,7 +127,6 @@ namespace AZ
->Attribute(AZ::Script::Attributes::Module, "render")
->Constructor()
->Constructor<const LightingPreset&>()
->Property("autoSelect", BehaviorValueProperty(&LightingPreset::m_autoSelect))
->Property("displayName", BehaviorValueProperty(&LightingPreset::m_displayName))
->Property("alternateSkyboxImageAsset", BehaviorValueProperty(&LightingPreset::m_alternateSkyboxImageAsset))
->Property("skyboxImageAsset", BehaviorValueProperty(&LightingPreset::m_skyboxImageAsset))
@@ -25,8 +25,7 @@ namespace AZ
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ModelPreset>()
->Version(2)
->Field("autoSelect", &ModelPreset::m_autoSelect)
->Version(3)
->Field("displayName", &ModelPreset::m_displayName)
->Field("modelAsset", &ModelPreset::m_modelAsset)
->Field("previewImageAsset", &ModelPreset::m_previewImageAsset)
@@ -41,7 +40,6 @@ namespace AZ
->Attribute(AZ::Script::Attributes::Module, "render")
->Constructor()
->Constructor<const ModelPreset&>()
->Property("autoSelect", BehaviorValueProperty(&ModelPreset::m_autoSelect))
->Property("displayName", BehaviorValueProperty(&ModelPreset::m_displayName))
->Property("modelAsset", BehaviorValueProperty(&ModelPreset::m_modelAsset))
->Property("previewImageAsset", BehaviorValueProperty(&ModelPreset::m_previewImageAsset))
@@ -16,6 +16,6 @@ namespace AZ
{
namespace RHI
{
bool Validation::s_isEnabled = BuildOptions::IsDebugBuild;
bool Validation::s_isEnabled = BuildOptions::IsDebugBuild || BuildOptions::IsProfileBuild;
}
}
@@ -404,6 +404,12 @@ namespace AZ
Buffer& buffer = static_cast<Buffer&>(*bufferFrameAttachment.GetBuffer());
RHI::BufferScopeAttachment* scopeAttachment = bufferFrameAttachment.GetFirstScopeAttachment();
if (scopeAttachment == nullptr)
{
AZ_WarningOnce("RHI", false, "Imported BufferFrameAttachment isn't used in any Scope");
return;
}
D3D12_RESOURCE_TRANSITION_BARRIER transition;
transition.pResource = buffer.GetMemoryView().GetMemory();
transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
@@ -471,6 +477,12 @@ namespace AZ
Image& image = static_cast<Image&>(*imageFrameAttachment.GetImage());
RHI::ImageScopeAttachment* scopeAttachment = imageFrameAttachment.GetFirstScopeAttachment();
if (scopeAttachment == nullptr)
{
AZ_WarningOnce("RHI", false, "Imported ImageFrameAttachment isn't used in any Scope");
return;
}
D3D12_RESOURCE_TRANSITION_BARRIER transition;
transition.pResource = image.GetMemoryView().GetMemory();
@@ -57,7 +57,7 @@ namespace AZ
PassConnection childInputConnection;
childInputConnection.m_localSlot = "Output";
childInputConnection.m_attachmentRef.m_pass = "Parent";
childInputConnection.m_attachmentRef.m_attachment = "CubeMapOutput";
childInputConnection.m_attachmentRef.m_attachment = "Output";
childRequest.m_connections.emplace_back(childInputConnection);
PassSystemInterface* passSystem = PassSystemInterface::Get();
@@ -120,15 +120,16 @@ namespace AZ
// create output PassAttachment
m_passAttachment = aznew PassAttachment();
m_passAttachment->m_name = "CubeMapOutput";
m_passAttachment->m_path = "CubeMapOutput";
m_passAttachment->m_name = "Output";
AZ::Name attachmentPath(AZStd::string::format("%s.%s", GetPathName().GetCStr(), m_passAttachment->m_name.GetCStr()));
m_passAttachment->m_path = attachmentPath;
m_passAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient;
m_passAttachment->m_descriptor = m_outputImageDesc;
// create pass attachment binding
PassAttachmentBinding outputAttachment;
outputAttachment.m_name = "CubeMapOutput";
outputAttachment.m_slotType = PassSlotType::Output;
outputAttachment.m_name = "Output";
outputAttachment.m_slotType = PassSlotType::InputOutput;
outputAttachment.m_attachment = m_passAttachment;
outputAttachment.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::RenderTarget;
@@ -16,6 +16,6 @@ namespace AZ
{
namespace RPI
{
bool Validation::s_isEnabled = RHI::BuildOptions::IsDebugBuild;
bool Validation::s_isEnabled = RHI::BuildOptions::IsDebugBuild || RHI::BuildOptions::IsProfileBuild;
}
}
@@ -3,7 +3,6 @@
"Version": 1,
"ClassName": "AZ::Render::LightingPreset",
"ClassData": {
"autoSelect": false,
"displayName": "Urban Street 02",
"skyboxImageAsset": {
"assetId": {
@@ -2,7 +2,6 @@
"configurations": [
{
"displayName": "Substance: <template>",
"autoSelect": false,
"iblSpecularImageFilePath": "materialeditor/lightingpresets/_Substance_/<latlong_image>_iblskyboxcm_iblspecular.exr.streamingimage",
"iblDiffuseImageFilePath": "materialeditor/lightingpresets/_Substance_/<latlong_image>_iblskyboxcm_ibldiffuse.exr.streamingimage",
"skyboxFilePath": "materialeditor/lightingpresets/_Substance_/<latlong_image>_iblskyboxcm.exr.streamingimage",
@@ -45,7 +44,6 @@
},
{
"displayName": "Substance: <template> (Alt)",
"autoSelect": false,
"iblSpecularImageFilePath": "materialeditor/lightingpresets/_Substance_/<latlong_image>_iblskyboxcm_iblspecular.exr.streamingimage",
"iblDiffuseImageFilePath": "materialeditor/lightingpresets/_Substance_/<latlong_image>_iblskyboxcm_ibldiffuse.exr.streamingimage",
"skyboxFilePath": "materialeditor/lightingpresets/_Substance_/<latlong_image>_iblskyboxcm_ibldiffuse.exr.streamingimage",
@@ -3,7 +3,6 @@
"Version": 1,
"ClassName": "AZ::Render::LightingPreset",
"ClassData": {
"autoSelect": true,
"displayName": "Neutral Urban",
"iblDiffuseImageAsset": {
"assetId": {
@@ -3,7 +3,6 @@
"Version": 1,
"ClassName": "AZ::Render::ModelPreset",
"ClassData": {
"autoSelect": true,
"displayName": "Shader Ball",
"modelAsset": {
"assetId": {
@@ -278,7 +278,7 @@ namespace MaterialEditor
MaterialViewportNotificationBus::Broadcast(&MaterialViewportNotificationBus::Events::OnLightingPresetAdded, presetPtr);
if (preset.m_autoSelect || m_lightingPresetVector.size() == 1)
if (m_lightingPresetVector.size() == 1)
{
SelectLightingPreset(presetPtr);
}
@@ -373,7 +373,7 @@ namespace MaterialEditor
MaterialViewportNotificationBus::Broadcast(&MaterialViewportNotificationBus::Events::OnModelPresetAdded, presetPtr);
if (preset.m_autoSelect || m_modelPresetVector.size() == 1)
if (m_modelPresetVector.size() == 1)
{
SelectModelPreset(presetPtr);
}
@@ -510,6 +510,13 @@ namespace MaterialEditor
void MaterialViewportComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
AZ::TickBus::QueueFunction([this]() { ReloadContent(); });
AZ::TickBus::QueueFunction([this]() {
ReloadContent();
// Automatically select preferred default presets if they exist
// We will later data drive this with editor settings
SelectLightingPresetByName("Neutral Urban");
SelectModelPresetByName("Shader Ball");
});
}
}
@@ -60,13 +60,17 @@ namespace MaterialEditor
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, enumerateCB, nullptr);
//Update the material type file info whenever the combo box selection changes
QObject::connect(m_ui->m_materialTypeComboBox, static_cast<void(QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), m_ui->m_materialTypeComboBox, [this](int index) {
QVariant data = m_ui->m_materialTypeComboBox->itemData(index);
m_materialTypeFileInfo = QFileInfo(data.toString());
});
QObject::connect(m_ui->m_materialTypeComboBox, static_cast<void (QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, [this]() { UpdateMaterialTypeSelection(); });
QObject::connect(m_ui->m_materialTypeComboBox, &QComboBox::currentTextChanged, this, [this]() { UpdateMaterialTypeSelection(); });
//Select StandardPBR by default but we will later data drive this with editor settings
m_ui->m_materialTypeComboBox->setCurrentText("StandardPBR");
// Select StandardPBR by default but we will later data drive this with editor settings
const int index = m_ui->m_materialTypeComboBox->findText("StandardPBR");
if (index >= 0)
{
m_ui->m_materialTypeComboBox->setCurrentIndex(index);
}
UpdateMaterialTypeSelection();
}
void CreateMaterialDialog::InitMaterialFileSelection()
@@ -88,15 +92,24 @@ namespace MaterialEditor
m_materialFileInfo.absoluteFilePath(),
QString("Material (*.material)"));
//Reject empty or invalid filenames which indicate user cancellation
// Reject empty or invalid filenames which indicate user cancellation
if (!fileInfo.absoluteFilePath().isEmpty())
{
m_materialFileInfo = fileInfo;
m_ui->m_materialFilePicker->setText(m_materialFileInfo.fileName());
}
});
});
}
void CreateMaterialDialog::UpdateMaterialTypeSelection()
{
const int index = m_ui->m_materialTypeComboBox->currentIndex();
if (index >= 0)
{
const QVariant itemData = m_ui->m_materialTypeComboBox->itemData(index);
m_materialTypeFileInfo = QFileInfo(itemData.toString());
}
}
} // namespace MaterialEditor
#include <Window/CreateMaterialDialog/moc_CreateMaterialDialog.cpp>
@@ -36,5 +36,6 @@ namespace MaterialEditor
QScopedPointer<Ui::CreateMaterialDialog> m_ui;
void InitMaterialTypeSelection();
void InitMaterialFileSelection();
void UpdateMaterialTypeSelection();
};
} // namespace MaterialEditor
@@ -10,36 +10,32 @@
*
*/
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Atom/Document/MaterialDocumentRequestBus.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h>
#include <Source/Window/MaterialBrowserWidget.h>
#include <Source/Window/ui_MaterialBrowserWidget.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QDesktopServices>
#include <QUrl>
#include <QMessageBox>
#include <QMenu>
#include <QAction>
#include <QCursor>
#include <QPushButton>
#include <QList>
#include <QByteArray>
#include <QCursor>
#include <QDesktopServices>
#include <QList>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QUrl>
AZ_POP_DISABLE_WARNING
namespace MaterialEditor
@@ -73,7 +69,7 @@ namespace MaterialEditor
m_filterModel->SetFilter(CreateFilter());
m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel);
m_ui->m_assetBrowserTreeViewWidget->SetShowSourceControlIcons(true);
m_ui->m_assetBrowserTreeViewWidget->SetShowSourceControlIcons(false);
m_ui->m_assetBrowserTreeViewWidget->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
// Maintains the tree expansion state between runs
@@ -99,7 +95,6 @@ namespace MaterialEditor
}
});
AssetBrowserModelNotificationBus::Handler::BusConnect();
MaterialDocumentNotificationBus::Handler::BusConnect();
}
@@ -108,7 +103,7 @@ namespace MaterialEditor
// Maintains the tree expansion state between runs
m_ui->m_assetBrowserTreeViewWidget->SaveState();
MaterialDocumentNotificationBus::Handler::BusDisconnect();
AssetBrowserModelNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
}
AzToolsFramework::AssetBrowser::FilterConstType MaterialBrowserWidget::CreateFilter() const
@@ -151,72 +146,65 @@ namespace MaterialEditor
for (const AssetBrowserEntry* entry : entries)
{
const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
if (!sourceEntry)
if (entry)
{
const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry);
if (productEntry)
if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), MaterialExtension))
{
sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(productEntry->GetParent());
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, entry->GetFullPath());
}
}
if (sourceEntry)
{
if (AzFramework::StringFunc::Path::IsExtension(sourceEntry->GetFullPath().c_str(), MaterialExtension))
{
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, sourceEntry->GetFullPath());
}
else if (AzFramework::StringFunc::Path::IsExtension(sourceEntry->GetFullPath().c_str(), MaterialTypeExtension))
else if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), MaterialTypeExtension))
{
//ignore MaterialTypeExtension
}
else
{
QDesktopServices::openUrl(QUrl::fromLocalFile(sourceEntry->GetFullPath().c_str()));
QDesktopServices::openUrl(QUrl::fromLocalFile(entry->GetFullPath().c_str()));
}
}
}
}
void MaterialBrowserWidget::EntryAdded(const AssetBrowserEntry* entry)
{
if (m_pathToSelect.empty())
{
return;
}
const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
if (!sourceEntry)
{
const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry);
if (productEntry)
{
sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(productEntry->GetParent());
}
}
if (sourceEntry)
{
AZStd::string sourcePath = sourceEntry->GetFullPath();
AzFramework::StringFunc::Path::Normalize(sourcePath);
if (m_pathToSelect == sourcePath)
{
m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect);
m_pathToSelect.clear();
}
}
}
void MaterialBrowserWidget::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZStd::string absolutePath;
MaterialDocumentRequestBus::EventResult(absolutePath, documentId, &MaterialDocumentRequestBus::Events::GetAbsolutePath);
if (!absolutePath.empty())
{
// Selecting a new asset in the browser is not guaranteed to happen immediately.
// The asset browser model notifications are sent before the model is updated.
// Instead of relying on the notifications, queue the selection and process it on tick until this change occurs.
m_pathToSelect = absolutePath;
AzFramework::StringFunc::Path::Normalize(m_pathToSelect);
m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPath(m_pathToSelect);
AZ::TickBus::Handler::BusConnect();
}
}
void MaterialBrowserWidget::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AZ_UNUSED(time);
AZ_UNUSED(deltaTime);
if (!m_pathToSelect.empty())
{
// Attempt to select the new path
AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::SelectFileAtPath, m_pathToSelect);
// Iterate over the selected entries to verify if the selection was made
for (const AssetBrowserEntry* entry : m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets())
{
if (entry)
{
AZStd::string sourcePath = entry->GetFullPath();
AzFramework::StringFunc::Path::Normalize(sourcePath);
if (m_pathToSelect == sourcePath)
{
// Once the selection is confirmed, cancel the operation and disconnect
AZ::TickBus::Handler::BusDisconnect();
m_pathToSelect.clear();
}
}
}
}
}
@@ -13,11 +13,11 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <Atom/Document/MaterialDocumentNotificationBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <Atom/Document/MaterialDocumentNotificationBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWidget>
@@ -26,8 +26,6 @@ AZ_POP_DISABLE_WARNING
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
@@ -50,8 +48,8 @@ namespace MaterialEditor
//! Provides a tree view of all available materials and other assets exposed by the MaterialEditor.
class MaterialBrowserWidget
: public QWidget
, public AzToolsFramework::AssetBrowser::AssetBrowserModelNotificationBus::Handler
, public MaterialDocumentNotificationBus::Handler
, protected AZ::TickBus::Handler
, protected MaterialDocumentNotificationBus::Handler
{
Q_OBJECT
public:
@@ -62,20 +60,20 @@ namespace MaterialEditor
AzToolsFramework::AssetBrowser::FilterConstType CreateFilter() const;
void OpenSelectedEntries();
// MaterialDocumentNotificationBus::Handler implementation
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// AZ::TickBus::Handler
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
void OpenOptionsMenu();
QScopedPointer<Ui::MaterialBrowserWidget> m_ui;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_filterModel = nullptr;
//! if new asset is being created with this path it will automatically be selected
AZStd::string m_pathToSelect;
// AssetBrowserModelNotificationBus::Handler implementation
void EntryAdded(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) override;
// MaterialDocumentNotificationBus::Handler implementation
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OpenOptionsMenu();
QByteArray m_materialBrowserState;
};
} // namespace MaterialEditor
@@ -249,7 +249,7 @@ namespace AZ
bool m_showTimeline = false;
// Controls how often the timestamp data is refreshed
RefreshType m_refreshType = RefreshType::OncePerSecond;
RefreshType m_refreshType = RefreshType::Realtime;
AZStd::sys_time_t m_lastUpdateTimeMicroSecond;
};
@@ -16,6 +16,8 @@
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <inttypes.h>
namespace AZ
{
namespace Render
@@ -725,7 +727,7 @@ namespace AZ
ImGui::BeginTooltip();
ImGui::Text("Name: %s", passEntry->m_name.GetCStr());
ImGui::Text("Path: %s", passEntry->m_path.GetCStr());
ImGui::Text("Duration in ticks: %lu", passEntry->m_timestampResult.GetDurationInTicks());
ImGui::Text("Duration in ticks: %" PRIu64, passEntry->m_timestampResult.GetDurationInTicks());
ImGui::Text("Duration in microsecond: %.3f us", passEntry->m_timestampResult.GetDurationInNanoseconds()/1000.f);
ImGui::EndTooltip();
}
@@ -1,7 +1,6 @@
{
"configurations": [
{
"autoSelect": false,
"displayName": "Photo Studio 01",
"skyboxImageAsset": {
"assetId": {
@@ -64,7 +63,6 @@
"shadowCatcherOpacity": 0.25
},
{
"autoSelect": false,
"displayName": "Photo Studio 01 (Alt)",
"skyboxImageAsset": {
"assetId": {
@@ -36,7 +36,7 @@ namespace AZ
struct ThumbnailRendererData final
{
static constexpr const char* LightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
static constexpr const char* DefaultModelPath = "materialeditor/viewportmodels/quadsphere.azmodel";
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial";
RPI::ScenePtr m_scene;
+1 -1
View File
@@ -69,4 +69,4 @@ namespace Blast
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Blast_414bd211c99d4f74aef3a266b9ca208c, Blast::BlastModule)
AZ_DECLARE_MODULE_CLASS(Gem_Blast, Blast::BlastModule)
@@ -14,4 +14,4 @@
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Blast_414bd211c99d4f74aef3a266b9ca208c, AZ::Module)
AZ_DECLARE_MODULE_CLASS(Gem_Blast, AZ::Module)
+2 -2
View File
@@ -9,5 +9,5 @@ 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 asset_builder_blast
# LYN-652 to re-enable the next line
# import asset_builder_blast
-6
View File
@@ -68,12 +68,6 @@ ly_add_target(
)
if (PAL_TRAIT_BUILD_HOST_TOOLS)
find_package(OpenGL QUIET REQUIRED)
# Imported targets (like OpenGL::GL) are scoped to a directory. Add a
# a global scope
add_library(3rdParty::OpenGLInterface INTERFACE IMPORTED GLOBAL)
target_link_libraries(3rdParty::OpenGLInterface INTERFACE OpenGL::GL)
ly_add_target(
NAME EMotionFX.Editor.Static STATIC
@@ -229,7 +229,7 @@ namespace EMotionFX
void MakeLoopable(float fadeTime = 0.3f);
/**
* Optimize the keytrack by removing redundent frames.
* Optimize the keytrack by removing redundant frames.
* The way this is done is by comparing differences between the resulting curves when removing specific keyframes.
* If the error (difference) between those curve before and after keyframe removal is within a given maximum error value, the keyframe can be
* safely removed since there will not be much "visual" difference.
@@ -13,10 +13,4 @@
# based on the active platform
# NOTE: functions in cmake are global, therefore adding functions to this file
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
target_compile_definitions(3rdParty::OpenGLInterface
INTERFACE
# MacOS 10.14 deprecates OpenGL. This silences the warnings for now.
GL_SILENCE_DEPRECATION
)
# specific cmake files
@@ -346,13 +346,20 @@ namespace EMotionFX
void AnimGraphComponent::CreateSnapshot(bool isAuthoritative)
{
AZ_Error("EMotionFX", m_animGraphInstance, "Call create snapshot function only when anim graph is ready in this component.");
m_animGraphInstance->CreateSnapshot(isAuthoritative);
m_animGraphInstance->OnNetworkConnected();
if (m_animGraphInstance)
{
m_animGraphInstance->CreateSnapshot(isAuthoritative);
m_animGraphInstance->OnNetworkConnected();
// This will stop the MCore Job schedule update the actor instance and anim graph for authoritative entity.
// After doing so, we will have to update this actor manuelly in the networking update.
m_animGraphInstance->GetActorInstance()->SetIsEnabled(!isAuthoritative);
// This will stop the MCore Job schedule update the actor instance and anim graph for authoritative entity.
// After doing so, we will have to update this actor manually in the networking update.
m_animGraphInstance->GetActorInstance()->SetIsEnabled(!isAuthoritative);
}
else
{
AZ_Error("EMotionFX", false, "Cannot create snapshot as anim graph instance has not been created yet. "
"Please make sure you selected an anim graph in the anim graph component.");
}
}
void AnimGraphComponent::SetActiveStates(const AZStd::vector<AZ::u32>& activeStates)
@@ -437,5 +437,7 @@ namespace GraphCanvas
default:
return QGraphicsWidget::sizeHint(which, constraint);
}
return QGraphicsWidget::sizeHint(which, constraint);
}
}
@@ -823,20 +823,37 @@ void CAnimSequence::SetId(uint32 newId)
}
//////////////////////////////////////////////////////////////////////////
void CAnimSequence::Reflect(AZ::SerializeContext* serializeContext)
static bool AnimSequenceVersionConverter(
AZ::SerializeContext& serializeContext,
AZ::SerializeContext::DataElementNode& rootElement)
{
serializeContext->Class<CAnimSequence>()
->Version(4)
->Field("Name", &CAnimSequence::m_name)
->Field("SequenceEntityId", &CAnimSequence::m_sequenceEntityId)
->Field("Flags", &CAnimSequence::m_flags)
->Field("TimeRange", &CAnimSequence::m_timeRange)
->Field("ID", &CAnimSequence::m_id)
->Field("Nodes", &CAnimSequence::m_nodes)
->Field("SequenceType", &CAnimSequence::m_sequenceType)
->Field("Events", &CAnimSequence::m_events)
->Field("Expanded", &CAnimSequence::m_expanded)
->Field("ActiveDirectorNodeId", &CAnimSequence::m_activeDirectorNodeId);
if (rootElement.GetVersion() < 5)
{
rootElement.AddElement(serializeContext, "BaseClass1", azrtti_typeid<IAnimSequence>());
}
return true;
}
void CAnimSequence::Reflect(AZ::ReflectContext* context)
{
IAnimSequence::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<CAnimSequence, IAnimSequence>()
->Version(IAnimSequence::kSequenceVersion, &AnimSequenceVersionConverter)
->Field("Name", &CAnimSequence::m_name)
->Field("SequenceEntityId", &CAnimSequence::m_sequenceEntityId)
->Field("Flags", &CAnimSequence::m_flags)
->Field("TimeRange", &CAnimSequence::m_timeRange)
->Field("ID", &CAnimSequence::m_id)
->Field("Nodes", &CAnimSequence::m_nodes)
->Field("SequenceType", &CAnimSequence::m_sequenceType)
->Field("Events", &CAnimSequence::m_events)
->Field("Expanded", &CAnimSequence::m_expanded)
->Field("ActiveDirectorNodeId", &CAnimSequence::m_activeDirectorNodeId);
}
}
//////////////////////////////////////////////////////////////////////////
@@ -154,7 +154,7 @@ public:
return m_nextTrackId++;
}
static void Reflect(AZ::SerializeContext* serializeContext);
static void Reflect(AZ::ReflectContext* context);
private:
void ComputeTimeRange();
@@ -114,12 +114,17 @@ class SceneManifest():
def mesh_group_unselect_node(self, meshGroup, nodeName):
meshGroup['nodeSelectionList']['unselectedNodes'].append(nodeName)
def mesh_group_set_origin(self, meshGroup, originNodeName, x, y, z, scale):
def mesh_group_add_advanced_coordinate_system(self, meshGroup, originNodeName, translation, rotation, scale):
originRule = {}
originRule['$type'] = 'OriginRule'
originRule['originNodeName'] = 'World' if originNodeName is None else originNodeName
originRule['translation'] = [x, y, z]
originRule['scale'] = scale
originRule['$type'] = 'CoordinateSystemRule'
originRule['useAdvancedData'] = True
originRule['originNodeName'] = '' if originNodeName is None else originNodeName
if translation is not None:
originRule['translation'] = translation
if rotation is not None:
originRule['rotation'] = rotation
if scale != 1.0:
originRule['scale'] = scale
meshGroup['rules']['rules'].append(originRule)
def mesh_group_add_comment(self, meshGroup, comment):
@@ -2347,7 +2347,7 @@ namespace ScriptCanvasEditor
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> addOutcome;
// #functions2 slot<->variable re-use the activeDatum, send the pointer (actually, all of the source slot information, and make a special conversion)
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(addOutcome, GetScriptCanvasId(), &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, variableDatum);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(addOutcome, GetScriptCanvasId(), &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, variableDatum, true);
if (addOutcome.IsSuccess())
{
@@ -172,7 +172,7 @@ namespace ScriptCanvasEditor
{
ScriptCanvas::Datum datum = ScriptCanvas::Datum(entityId);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string > addVariableOutcome = variableManagerRequests->AddVariable(variableName, datum);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string > addVariableOutcome = variableManagerRequests->AddVariable(variableName, datum, false);
if (addVariableOutcome.IsSuccess())
{
@@ -578,6 +578,14 @@ namespace
categoryPath.append(displayName.c_str());
}
for (auto property : behaviorClass->m_properties)
{
if (property.second->m_setter)
{
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_setter, behaviorClass->IsMethodOverloaded(property.first));
}
}
for (auto methodIter : behaviorClass->m_methods)
{
if (!IsExplicitOverload(*methodIter.second))
@@ -1011,7 +1011,7 @@ namespace ScriptCanvasEditor
ScriptCanvas::Datum datum(variableType, ScriptCanvas::Datum::eOriginality::Original);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> outcome = AZ::Failure(AZStd::string());
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_activeGraphIds.scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, varName, datum);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_activeGraphIds.scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, varName, datum, false);
if (outcome.IsSuccess())
{
@@ -41,6 +41,7 @@
#include <ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
namespace ScriptCanvasEditor
{
@@ -538,7 +539,24 @@ namespace ScriptCanvasEditor
}
else if (index.column() == ColumnIndex::Scope)
{
// Scope is not changed by users
ScriptCanvas::GraphVariable* graphVariable = nullptr;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(graphVariable, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::FindVariableById, varId.m_identifier);
if (graphVariable)
{
QString comboBoxValue = value.toString();
if (!comboBoxValue.isEmpty())
{
AZStd::string scopeLabel = ScriptCanvas::VariableFlags::GetScopeDisplayLabel(graphVariable->GetScope());
if (scopeLabel.compare(comboBoxValue.toUtf8().data()) != 0)
{
modifiedData = true;
graphVariable->SetScope(ScriptCanvas::VariableFlags::GetScopeFromLabel(comboBoxValue.toUtf8().data()));
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::Refresh_EntireTree);
}
}
}
}
else if (index.column() == ColumnIndex::InitialValueSource)
{
@@ -607,8 +625,17 @@ namespace ScriptCanvasEditor
}
else if (index.column() == ColumnIndex::Scope)
{
ScriptCanvas::GraphScopedVariableId varId = FindScopedVariableIdForIndex(index);
ScriptCanvas::GraphVariable* graphVariable = nullptr;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(graphVariable, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::FindVariableById, varId.m_identifier);
if (graphVariable->GetScope() != ScriptCanvas::VariableFlags::Scope::FunctionReadOnly)
{
itemFlags |= Qt::ItemIsEditable;
}
}
else if (index.column() == ColumnIndex::InitialValueSource)
{
itemFlags |= Qt::ItemIsEditable;
@@ -73,6 +73,8 @@ namespace ScriptCanvasEditor
{
ui->setupUi(this);
ui->variablePalette->SetActiveScene(scriptCanvasId);
ui->searchFilter->setClearButtonEnabled(true);
QObject::connect(ui->searchFilter, &QLineEdit::textChanged, this, &SlotTypeSelectorWidget::OnQuickFilterChanged);
QObject::connect(ui->slotName, &QLineEdit::returnPressed, this, &SlotTypeSelectorWidget::OnReturnPressed);
@@ -812,7 +812,7 @@ namespace ScriptCanvasEditor
ScriptCanvas::Datum datum(varType, ScriptCanvas::Datum::eOriginality::Original);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> outcome = AZ::Failure(AZStd::string());
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, datum);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, datum, false);
AZ_Warning("VariablePanel", outcome.IsSuccess(), "Could not create new variable: %s", outcome.GetError().c_str());
GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasId);
@@ -81,6 +81,8 @@ namespace ScriptCanvas
{
return ConstructCustomNodeIdentifier(scriptCanvasNode->RTTI_GetType());
}
return NodeTypeIdentifier(0);
}
NodeTypeIdentifier NodeUtils::ConstructEBusIdentifier(ScriptCanvas::EBusBusId ebusIdentifier)
@@ -43,20 +43,12 @@ namespace ScriptCanvas
{
const char* GetScopeDisplayLabel(Scope scopeType)
{
switch (scopeType)
{
case Scope::Graph:
return "Graph";
case Scope::Function:
return "Function";
default:
return "?";
}
return GraphVariable::s_ScopeNames[static_cast<int>(scopeType)];
}
Scope GetScopeFromLabel(const char* label)
{
if (strcmp("Function", label) == 0)
if (strcmp(GraphVariable::s_ScopeNames[static_cast<int>(VariableFlags::Scope::Function)], label) == 0)
{
return Scope::Function;
}
@@ -71,6 +63,7 @@ namespace ScriptCanvas
case Scope::Graph:
return "Variable is accessible in the entire graph.";
case Scope::Function:
case Scope::FunctionReadOnly:
return "Variable is accessible only in the execution path of the function that defined it";
default:
return "?";
@@ -162,6 +155,14 @@ namespace ScriptCanvas
"From Component"
};
const char* GraphVariable::s_ScopeNames[static_cast<int>(VariableFlags::Scope::COUNT)] =
{
"Graph",
"Function",
"Function",
};
void GraphVariable::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -197,6 +198,13 @@ namespace ScriptCanvas
return choices;
};
auto scopeChoices = [] {
AZStd::vector< AZStd::pair<VariableFlags::Scope, AZStd::string>> choices;
choices.emplace_back(AZStd::make_pair(VariableFlags::Scope::Graph, s_ScopeNames[0]));
choices.emplace_back(AZStd::make_pair(VariableFlags::Scope::Function, s_ScopeNames[1]));
return choices;
};
editContext->Class<GraphVariable>("Variable", "Represents a Variable field within a Script Canvas Graph")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, &GraphVariable::GetVisibility)
@@ -215,8 +223,8 @@ namespace ScriptCanvas
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GraphVariable::OnValueChanged)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &GraphVariable::m_scope, "Scope", "Controls the scope of this variable. i.e. If this is exposed as input to this script, or output from this script, or if the variable is just locally scoped.")
->Attribute(AZ::Edit::Attributes::Visibility, &GraphVariable::GetInputControlVisibility)
->Attribute(AZ::Edit::Attributes::GenericValueList, &GraphVariable::GetScopes)
->Attribute(AZ::Edit::Attributes::Visibility, &GraphVariable::GetScopeControlVisibility)
->Attribute(AZ::Edit::Attributes::GenericValueList, scopeChoices)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GraphVariable::OnScopeTypedChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &GraphVariable::m_networkProperties, "Network Properties", "Enables whether or not this value should be network synchronized")
@@ -382,6 +390,16 @@ namespace ScriptCanvas
m_inputControlVisibility = inputControlVisibility;
}
AZ::Crc32 GraphVariable::GetScopeControlVisibility() const
{
if (m_scope == VariableFlags::Scope::FunctionReadOnly)
{
return AZ::Edit::PropertyVisibility::Hide;
}
return GetInputControlVisibility();
}
AZ::Crc32 GraphVariable::GetInputControlVisibility() const
{
return m_inputControlVisibility;
@@ -462,6 +480,8 @@ namespace ScriptCanvas
return m_scope == VariableFlags::Scope::Graph;
// All graph variables are in function local scope
case VariableFlags::Scope::Function:
case VariableFlags::Scope::FunctionReadOnly:
return true;
}
@@ -52,8 +52,10 @@ namespace ScriptCanvas
enum class Scope : AZ::u8
{
Graph = 0,
Function = 1,
Graph,
Function,
FunctionReadOnly,
COUNT
};
enum InitialValueSource : AZ::u8
@@ -142,6 +144,7 @@ namespace ScriptCanvas
void SetScriptInputControlVisibility(const AZ::Crc32& inputControlVisibility);
AZ::Crc32 GetInputControlVisibility() const;
AZ::Crc32 GetScopeControlVisibility() const;
AZ::Crc32 GetScriptInputControlVisibility() const;
AZ::Crc32 GetNetworkSettingsVisibility() const;
AZ::Crc32 GetFunctionInputControlVisibility() const;
@@ -181,6 +184,7 @@ namespace ScriptCanvas
int GetSortPriority() const;
static const char* s_InitialValueSourceNames[VariableFlags::InitialValueSource::COUNT];
static const char* s_ScopeNames[static_cast<int>(VariableFlags::Scope::COUNT)];
private:
@@ -225,7 +225,7 @@ namespace ScriptCanvas
}
// #functions2 slot<->variable add this to the graph, using the old datum
AZ::Outcome<VariableId, AZStd::string> GraphVariableManagerComponent::AddVariable(AZStd::string_view name, const Datum& value)
AZ::Outcome<VariableId, AZStd::string> GraphVariableManagerComponent::AddVariable(AZStd::string_view name, const Datum& value, bool functionScope)
{
if (FindVariable(name))
{
@@ -245,6 +245,10 @@ namespace ScriptCanvas
GraphVariable* variable = m_variableData.FindVariable(newId);
variable->SetOwningScriptCanvasId(GetScriptCanvasId());
if (functionScope)
{
variable->SetScope(VariableFlags::Scope::FunctionReadOnly);
}
VariableRequestBus::MultiHandler::BusConnect(GraphScopedVariableId(m_scriptCanvasId, newId));
GraphVariableManagerNotificationBus::Event(GetScriptCanvasId(), &GraphVariableManagerNotifications::OnVariableAddedToGraph, newId, name);
@@ -254,7 +258,7 @@ namespace ScriptCanvas
AZ::Outcome<VariableId, AZStd::string> GraphVariableManagerComponent::AddVariablePair(const AZStd::pair<AZStd::string_view, Datum>& keyValuePair)
{
return AddVariable(keyValuePair.first, keyValuePair.second);
return AddVariable(keyValuePair.first, keyValuePair.second, false);
}
VariableValidationOutcome GraphVariableManagerComponent::IsNameValid(AZStd::string_view varName)

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