Merge branch 'development' into cmake/SPEC-2513_w4267

This commit is contained in:
Esteban Papp
2021-08-05 14:57:24 -07:00
171 changed files with 4614 additions and 2832 deletions
+3 -3
View File
@@ -212,10 +212,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"subId": 277333723
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 277889906
},
"assetHint": "objects/groudplane/groundplane_521x521m.azmodel"
"assetHint": "objects/groudplane/groundplane_512x512m.azmodel"
}
}
}
@@ -2771,6 +2771,80 @@
<source>VECTOR2_CREATEONE_OUTPUT0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_NAME">
<source>VECTOR2_DIRECTIONTO_NAME</source>
<comment>Class/Bus: Vector2 Event/Method: DirectionTo</comment>
<translation type="unfinished">Get Direction Vector</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_TOOLTIP</source>
<translation>Returns a direction vector between two points and the distance between them, by default the direction will be normalized, it may be optionally scaled using the Scale parameter if different from 1.0</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_CATEGORY">
<source>VECTOR2_DIRECTIONTO_CATEGORY</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUT_NAME">
<source>VECTOR2_DIRECTIONTO_OUT_NAME</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUT_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_OUT_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTOL_IN_NAME">
<source>VECTOR2_DIRECTIONTOL_IN_NAME</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_IN_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_IN_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT0_NAME">
<source>VECTOR2_DIRECTIONTO_OUTPUT0_NAME</source>
<comment>C++ Type: const Vector2</comment>
<translation type="unfinished">Direction</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT0_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_OUTPUT0_TOOLTIP</source>
<translation>The direction between To and From normalized and optionally scaled</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT1_NAME">
<source>VECTOR2_DIRECTIONTO_OUTPUT1_NAME</source>
<comment>C++ Type: float</comment>
<translation type="unfinished">Distance</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT1_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_OUTPUT1_TOOLTIP</source>
<translation>The distance between To and From</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM0_NAME">
<source>VECTOR2_DIRECTIONTO_PARAM0_NAME</source>
<comment>Simple Type: Vector2 C++ Type: Vector2*</comment>
<translation type="unfinished">From</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM0_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM1_NAME">
<source>VECTOR2_DIRECTIONTO_PARAM1_NAME</source>
<comment>Simple Type: Vector2 C++ Type: Vector2*</comment>
<translation type="unfinished">To</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM1_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_PARAM1_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM2_NAME">
<source>VECTOR2_DIRECTIONTO_PARAM2_NAME</source>
<comment>Simple Type: Vector2 C++ Type: Vector2*</comment>
<translation type="unfinished">Scale</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM2_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_PARAM2_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_GETPROJECTED_NAME">
<source>VECTOR2_GETPROJECTED_NAME</source>
<comment>Class/Bus: Vector2 Event/Method: GetProjected</comment>
@@ -32262,6 +32336,80 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<source>VECTOR4_GETRECIPROCAL_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_NAME">
<source>VECTOR4_DIRECTIONTO_NAME</source>
<comment>Class/Bus: Vector4 Event/Method: DirectionTo</comment>
<translation type="unfinished">Get Direction Vector</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_TOOLTIP</source>
<translation>Returns a direction vector between two points and the distance between them, by default the direction will be normalized, it may be optionally scaled using the Scale parameter if different from 1.0</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_CATEGORY">
<source>VECTOR4_DIRECTIONTO_CATEGORY</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUT_NAME">
<source>VECTOR4_DIRECTIONTO_OUT_NAME</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUT_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_OUT_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTOL_IN_NAME">
<source>VECTOR4_DIRECTIONTOL_IN_NAME</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_IN_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_IN_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT0_NAME">
<source>VECTOR4_DIRECTIONTO_OUTPUT0_NAME</source>
<comment>C++ Type: const Vector4</comment>
<translation type="unfinished">Direction</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT0_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_OUTPUT0_TOOLTIP</source>
<translation>The direction between To and From normalized and optionally scaled</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT1_NAME">
<source>VECTOR4_DIRECTIONTO_OUTPUT1_NAME</source>
<comment>C++ Type: float</comment>
<translation type="unfinished">Distance</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT1_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_OUTPUT1_TOOLTIP</source>
<translation>The distance between To and From</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM0_NAME">
<source>VECTOR4_DIRECTIONTO_PARAM0_NAME</source>
<comment>Simple Type: Vector4 C++ Type: Vector4*</comment>
<translation type="unfinished">From</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM0_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM1_NAME">
<source>VECTOR4_DIRECTIONTO_PARAM1_NAME</source>
<comment>Simple Type: Vector4 C++ Type: Vector4*</comment>
<translation type="unfinished">To</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM1_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_PARAM1_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM2_NAME">
<source>VECTOR4_DIRECTIONTO_PARAM2_NAME</source>
<comment>Simple Type: Vector4 C++ Type: Vector4*</comment>
<translation type="unfinished">Scale</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM2_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_PARAM2_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_AXISX_NAME">
<source>VECTOR4_AXISX_NAME</source>
<comment>Class/Bus: Vector4 Event/Method: CreateAxisX</comment>
@@ -37469,6 +37617,80 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<source>VECTOR3_GETRECIPROCAL_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_NAME">
<source>VECTOR3_DIRECTIONTO_NAME</source>
<comment>Class/Bus: Vector3 Event/Method: DirectionTo</comment>
<translation type="unfinished">Get Direction Vector</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_TOOLTIP</source>
<translation>Returns a direction vector between two points and the distance between them, by default the direction will be normalized, it may be optionally scaled using the Scale parameter if different from 1.0</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_CATEGORY">
<source>VECTOR3_DIRECTIONTO_CATEGORY</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUT_NAME">
<source>VECTOR3_DIRECTIONTO_OUT_NAME</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUT_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_OUT_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTOL_IN_NAME">
<source>VECTOR3_DIRECTIONTOL_IN_NAME</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_IN_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_IN_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT0_NAME">
<source>VECTOR3_DIRECTIONTO_OUTPUT0_NAME</source>
<comment>C++ Type: const Vector3</comment>
<translation type="unfinished">Direction</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT0_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_OUTPUT0_TOOLTIP</source>
<translation>The direction between To and From normalized and optionally scaled</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT1_NAME">
<source>VECTOR3_DIRECTIONTO_OUTPUT1_NAME</source>
<comment>C++ Type: float</comment>
<translation type="unfinished">Distance</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT1_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_OUTPUT1_TOOLTIP</source>
<translation>The distance between To and From</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM0_NAME">
<source>VECTOR3_DIRECTIONTO_PARAM0_NAME</source>
<comment>Simple Type: Vector3 C++ Type: Vector3*</comment>
<translation type="unfinished">From</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM0_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM1_NAME">
<source>VECTOR3_DIRECTIONTO_PARAM1_NAME</source>
<comment>Simple Type: Vector3 C++ Type: Vector3*</comment>
<translation type="unfinished">To</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM1_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_PARAM1_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM2_NAME">
<source>VECTOR3_DIRECTIONTO_PARAM2_NAME</source>
<comment>Simple Type: Vector3 C++ Type: Vector3*</comment>
<translation type="unfinished">Scale</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM2_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_PARAM2_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_PROJECT_NAME">
<source>VECTOR3_PROJECT_NAME</source>
<comment>Class/Bus: Vector3 Event/Method: Project</comment>
@@ -97,7 +97,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_bundler_batch_tests.py
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
TEST_SERIAL
TIMEOUT 1500
TIMEOUT 2400
TEST_SUITE periodic
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
@@ -113,7 +113,7 @@ class TestsAssetBuilder_WindowsAndMac(object):
if listening_port:
corrupted_slice_command.append(f'-port={listening_port}')
if workspace.project:
corrupted_slice_command.append(f'-gamename={workspace.project}')
corrupted_slice_command.append(f'--project-path={workspace.project}')
corrupted_slice_output = utils.safe_subprocess(corrupted_slice_command)
# Verify corrupted slice produced error
@@ -902,7 +902,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
second_input_arg = asset_lists_to_string(second_asset_list) # --secondAssetList
output_arg = asset_lists_to_string(output_file) # --output
def generate_compare_command(platform_arg: str) -> object:
def generate_compare_command(platform_arg: str, project_name : str) -> object:
"""Creates a string containing a full Compare command. This string can be executed as-is."""
cmd = [helper["bundler_batch"], "compare", f"--firstassetFile={first_input_arg}", f"--output={output_arg}"]
if platform_arg is not None:
@@ -918,6 +918,8 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
if comp_type == "4":
# Extra arguments for pattern comparison
cmd.extend([f"--filePatternType={pattern_type}", f"--filePattern={pattern}"])
if workspace.project:
cmd.append(f'--project-path={project_name}')
return cmd
# End generate_compare_command()
@@ -936,6 +938,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
# End verify_asset_list_contents()
def run_compare_command_and_verify(platform_arg: str, expect_pc_output: bool, expect_mac_output: bool) -> None:
# Expected asset list to equal result of comparison
expected_pc_asset_list = None
expected_mac_asset_list = None
@@ -957,7 +960,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
output_mac_asset_list = helper.platform_file_name(last_output_arg, platform)
# Build execution command
cmd = generate_compare_command(platform_arg)
cmd = generate_compare_command(platform_arg, workspace.project)
# Execute command
subprocess.check_call(cmd)
@@ -992,10 +995,12 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
f"--comparisonRulesFile={rule_file}",
f"--comparisonType={args[1]}",
r"--addComparison",
f"--project-path={workspace.project}",
]
if args[1] == "4":
# If pattern comparison, append a few extra arguments
cmd.extend(["--filePatternType=0", "--filePattern=*.dat"])
subprocess.check_call(cmd)
assert os.path.exists(rule_file), f"Rule file {args[0]} was not created at location: {rule_file}"
@@ -17,7 +17,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_MainSuite.py
TEST_SERIAL
TIMEOUT 400
TIMEOUT 600
RUNTIME_DEPENDENCIES
AssetProcessor
AutomatedTesting.Assets
@@ -0,0 +1,217 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
Hydra script that creates an entity, attaches the Light component to it for test verifications.
The test verifies that each light type option is available and can be selected without errors.
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
import azlmbr.legacy.general as general
sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from atom_renderer.atom_utils.atom_component_helper import LIGHT_TYPES
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES = [
("Controller|Configuration|Shadows|Enable shadow", True),
("Controller|Configuration|Shadows|Shadowmap size", 0), # 256
("Controller|Configuration|Shadows|Shadowmap size", 1), # 512
("Controller|Configuration|Shadows|Shadowmap size", 2), # 1024
("Controller|Configuration|Shadows|Shadowmap size", 3), # 2048
("Controller|Configuration|Shadows|Shadow filter method", 1), # PCF
("Controller|Configuration|Shadows|Filtering sample count", 4.0),
("Controller|Configuration|Shadows|Filtering sample count", 64.0),
("Controller|Configuration|Shadows|PCF method", 0), # Bicubic
("Controller|Configuration|Shadows|PCF method", 1), # Boundary search
("Controller|Configuration|Shadows|Shadow filter method", 2), # ECM
("Controller|Configuration|Shadows|ESM exponent", 50),
("Controller|Configuration|Shadows|ESM exponent", 5000),
("Controller|Configuration|Shadows|Shadow filter method", 3), # ESM+PCF
]
QUAD_LIGHT_PROPERTIES = [
("Controller|Configuration|Both directions", True),
("Controller|Configuration|Fast approximation", True),
]
SIMPLE_POINT_LIGHT_PROPERTIES = [
("Controller|Configuration|Attenuation radius|Mode", 0),
("Controller|Configuration|Attenuation radius|Radius", 100.0),
]
SIMPLE_SPOT_LIGHT_PROPERTIES = [
("Controller|Configuration|Shutters|Inner angle", 45.0),
("Controller|Configuration|Shutters|Outer angle", 90.0),
]
def verify_required_component_property_value(entity_name, component, property_path, expected_property_value):
"""
Compares the property value of component against the expected_property_value.
:param entity_name: name of the entity to use (for test verification purposes).
:param component: component to check on a given entity for its current property value.
:param property_path: the path to the property inside the component.
:param expected_property_value: The value expected from the value inside property_path.
:return: None, but prints to general.log() which the test uses to verify against.
"""
property_value = editor.EditorComponentAPIBus(
bus.Broadcast, "GetComponentProperty", component, property_path).GetValue()
general.log(f"{entity_name}_test: Property value is {property_value} "
f"which matches {expected_property_value}")
def run():
"""
Test Case - Light Component
1. Creates a "light_entity" Entity and attaches a "Light" component to it.
2. Updates the Light component to each light type option from the LIGHT_TYPES constant.
3. The test will check the Editor log to ensure each light type was selected.
4. Prints the string "Light component test (non-GPU) completed" after completion.
Tests will fail immediately if any of these log lines are found:
1. Trace::Assert
2. Trace::Error
3. Traceback (most recent call last):
:return: None
"""
# Create a "light_entity" entity with "Light" component.
light_entity_name = "light_entity"
light_component = "Light"
light_entity = hydra.Entity(light_entity_name)
light_entity.create_entity(math.Vector3(-1.0, -2.0, 3.0), [light_component])
general.log(
f"{light_entity_name}_test: Component added to the entity: "
f"{hydra.has_components(light_entity.id, [light_component])}")
# Populate the light_component_id_pair value so that it can be used to select all Light component options.
light_component_id_pair = None
component_type_id_list = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', [light_component], 0)
if len(component_type_id_list) < 1:
general.log(f"ERROR: A component class with name {light_component} doesn't exist")
light_component_id_pair = None
elif len(component_type_id_list) > 1:
general.log(f"ERROR: Found more than one component classes with same name: {light_component}")
light_component_id_pair = None
entity_component_id_pair = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'GetComponentOfType', light_entity.id, component_type_id_list[0])
if entity_component_id_pair.IsSuccess():
light_component_id_pair = entity_component_id_pair.GetValue()
# Test each Light component option can be selected and it's properties updated.
# Point (sphere) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['sphere'],
light_properties=SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Spot (disk) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['spot_disk'],
light_properties=SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Capsule light type checks.
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
LIGHT_TYPES['capsule']
)
verify_required_component_property_value(
entity_name=light_entity_name,
component=light_entity.components[0],
property_path=LIGHT_TYPE_PROPERTY,
expected_property_value=LIGHT_TYPES['capsule']
)
# Quad light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['quad'],
light_properties=QUAD_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Polygon light type checks.
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
LIGHT_TYPES['polygon']
)
verify_required_component_property_value(
entity_name=light_entity_name,
component=light_entity.components[0],
property_path=LIGHT_TYPE_PROPERTY,
expected_property_value=LIGHT_TYPES['polygon']
)
# Point (simple punctual) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['simple_point'],
light_properties=SIMPLE_POINT_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Spot (simple punctual) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['simple_spot'],
light_properties=SIMPLE_SPOT_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
general.log("Light component test (non-GPU) completed.")
def light_type_property_test(light_type, light_properties, light_component_id_pair, light_entity_name, light_entity):
"""
Updates the current light type and modifies its properties, then verifies they are accurate to what was set.
:param light_type: The type of light to update, must match a value in LIGHT_TYPES
:param light_properties: List of tuples detailing properties to modify with update values.
:param light_component_id_pair: Entity + component ID pair for updating the light component on a given entity.
:param light_entity_name: the name of the Entity holding the light component.
:param light_entity: the Entity object containing the light component.
:return: None
"""
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
light_type
)
verify_required_component_property_value(
entity_name=light_entity_name,
component=light_entity.components[0],
property_path=LIGHT_TYPE_PROPERTY,
expected_property_value=light_type
)
for light_property in light_properties:
light_entity.get_set_test(0, light_property[0], light_property[1])
if __name__ == "__main__":
run()
@@ -0,0 +1,19 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
File to assist with common hydra component functions or constants used across various Atom tests.
"""
# Light type options for the Light component.
LIGHT_TYPES = {
'unknown': 0,
'sphere': 1,
'spot_disk': 2,
'capsule': 3,
'quad': 4,
'polygon': 5,
'simple_point': 6,
'simple_spot': 7,
}
@@ -12,9 +12,10 @@ import os
import pytest
import editor_python_test_tools.hydra_test_utils as hydra
from atom_renderer.atom_utils.atom_component_helper import LIGHT_TYPES
logger = logging.getLogger(__name__)
EDITOR_TIMEOUT = 300
EDITOR_TIMEOUT = 120
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts")
@@ -180,3 +181,64 @@ class TestAtomEditorComponentsMain(object):
null_renderer=True,
cfg_args=cfg_args,
)
def test_AtomEditorComponents_LightComponent(
self, request, editor, workspace, project, launcher_platform, level):
"""
Please review the hydra script run by this test for more specific test info.
Tests that the Light component has the expected property options available to it.
"""
cfg_args = [level]
expected_lines = [
"light_entity Entity successfully created",
"Entity has a Light component",
"light_entity_test: Component added to the entity: True",
f"light_entity_test: Property value is {LIGHT_TYPES['sphere']} which matches {LIGHT_TYPES['sphere']}",
"Controller|Configuration|Shadows|Enable shadow set to True",
"light_entity Controller|Configuration|Shadows|Shadowmap size: SUCCESS",
"Controller|Configuration|Shadows|Shadow filter method set to 1", # PCF
"Controller|Configuration|Shadows|Filtering sample count set to 4",
"Controller|Configuration|Shadows|Filtering sample count set to 64",
"Controller|Configuration|Shadows|PCF method set to 0",
"Controller|Configuration|Shadows|PCF method set to 1",
"Controller|Configuration|Shadows|Shadow filter method set to 2", # ESM
"Controller|Configuration|Shadows|ESM exponent set to 50.0",
"Controller|Configuration|Shadows|ESM exponent set to 5000.0",
"Controller|Configuration|Shadows|Shadow filter method set to 3", # ESM+PCF
f"light_entity_test: Property value is {LIGHT_TYPES['spot_disk']} which matches {LIGHT_TYPES['spot_disk']}",
f"light_entity_test: Property value is {LIGHT_TYPES['capsule']} which matches {LIGHT_TYPES['capsule']}",
f"light_entity_test: Property value is {LIGHT_TYPES['quad']} which matches {LIGHT_TYPES['quad']}",
"light_entity Controller|Configuration|Fast approximation: SUCCESS",
"light_entity Controller|Configuration|Both directions: SUCCESS",
f"light_entity_test: Property value is {LIGHT_TYPES['polygon']} which matches {LIGHT_TYPES['polygon']}",
f"light_entity_test: Property value is {LIGHT_TYPES['simple_point']} "
f"which matches {LIGHT_TYPES['simple_point']}",
"Controller|Configuration|Attenuation radius|Mode set to 0",
"Controller|Configuration|Attenuation radius|Radius set to 100.0",
f"light_entity_test: Property value is {LIGHT_TYPES['simple_spot']} "
f"which matches {LIGHT_TYPES['simple_spot']}",
"Controller|Configuration|Shutters|Outer angle set to 45.0",
"Controller|Configuration|Shutters|Outer angle set to 90.0",
"light_entity_test: Component added to the entity: True",
"Light component test (non-GPU) completed.",
]
unexpected_lines = [
"Trace::Assert",
"Trace::Error",
"Traceback (most recent call last):",
]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
editor,
"hydra_AtomEditorComponents_LightComponent.py",
timeout=EDITOR_TIMEOUT,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
null_renderer=True,
cfg_args=cfg_args,
)
@@ -64,7 +64,8 @@ def C15096740_Material_LibraryUpdatedCorrectly():
# Constants
library_property_path = "Configuration|Physics Material|Library"
default_material_path = "surfacetypemateriallibrary.physmaterial"
default_material_path = os.path.join("assets", "physics", "surfacetypemateriallibrary.physmaterial")
new_material_path = os.path.join("physicssurfaces", "default_phys_materials.physmaterial")
helper.init_idle()
@@ -82,7 +83,7 @@ def C15096740_Material_LibraryUpdatedCorrectly():
default_asset = Asset.find_asset_by_path(default_material_path)
test_component.set_component_property_value(library_property_path, default_asset.id)
default_asset.id = test_component.get_component_property_value(library_property_path)
Report.result(Tests.override_default_library, default_asset.get_path() == default_material_path)
Report.result(Tests.override_default_library, default_asset.get_path() == default_material_path.replace(os.sep, '/'))
# 4) Switch it back again to the default material library.
test_component.set_component_property_value(library_property_path, azasset.AssetId())
@@ -133,7 +133,7 @@ class Physmaterial_Editor:
def _set_path(self):
# type: (str) -> str
if self.document_filename == None:
self.document_filename = os.path.join(self.project_folder, "surfacetypemateriallibrary.physmaterial")
self.document_filename = os.path.join(self.project_folder, "assets", "physics", "surfacetypemateriallibrary.physmaterial")
else:
for (root, directories, root_files) in os.walk(self.project_folder):
for root_file in root_files:
+483
View File
@@ -0,0 +1,483 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "MainPipeline",
"PassClass": "ParentPass",
"Slots": [
{
"Name": "SwapChainOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
}
],
"PassRequests": [
{
"Name": "MorphTargetPass",
"TemplateName": "MorphTargetPassTemplate"
},
{
"Name": "SkinningPass",
"TemplateName": "SkinningPassTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshOutputStream",
"AttachmentRef": {
"Pass": "MorphTargetPass",
"Attachment": "MorphTargetDeltaOutput"
}
}
]
},
{
"Name": "RayTracingAccelerationStructurePass",
"TemplateName": "RayTracingAccelerationStructurePassTemplate"
},
{
"Name": "DiffuseProbeGridUpdatePass",
"TemplateName": "DiffuseProbeGridUpdatePassTemplate",
"ExecuteAfter": [
"RayTracingAccelerationStructurePass"
]
},
{
"Name": "DepthPrePass",
"TemplateName": "DepthMSAAParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "MotionVectorPass",
"TemplateName": "MotionVectorParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "Depth",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "LightCullingPass",
"TemplateName": "LightCullingParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "DepthMSAA",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthMSAA"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "ShadowPass",
"TemplateName": "ShadowParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "OpaquePass",
"TemplateName": "OpaqueParentTemplate",
"Connections": [
{
"LocalSlot": "DirectionalShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalShadowmap"
}
},
{
"LocalSlot": "DirectionalESM",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalESM"
}
},
{
"LocalSlot": "ProjectedShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedShadowmap"
}
},
{
"LocalSlot": "ProjectedESM",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedESM"
}
},
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "TileLightData"
}
},
{
"LocalSlot": "LightListRemapped",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "LightListRemapped"
}
},
{
"LocalSlot": "DepthLinear",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthLinear"
}
},
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthMSAA"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "TransparentPass",
"TemplateName": "TransparentParentTemplate",
"Connections": [
{
"LocalSlot": "DirectionalShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalShadowmap"
}
},
{
"LocalSlot": "DirectionalESM",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalESM"
}
},
{
"LocalSlot": "ProjectedShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedShadowmap"
}
},
{
"LocalSlot": "ProjectedESM",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedESM"
}
},
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "TileLightData"
}
},
{
"LocalSlot": "LightListRemapped",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "LightListRemapped"
}
},
{
"LocalSlot": "InputLinearDepth",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthLinear"
}
},
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "InputOutput",
"AttachmentRef": {
"Pass": "OpaquePass",
"Attachment": "Output"
}
}
]
},
{
"Name": "DeferredFogPass",
"TemplateName": "DeferredFogPassTemplate",
"Enabled": false,
"Connections": [
{
"LocalSlot": "InputLinearDepth",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthLinear"
}
},
{
"LocalSlot": "InputDepthStencil",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "RenderTargetInputOutput",
"AttachmentRef": {
"Pass": "TransparentPass",
"Attachment": "InputOutput"
}
}
],
"PassData": {
"$type": "FullscreenTrianglePassData",
"ShaderAsset": {
"FilePath": "Shaders/ScreenSpace/DeferredFog.shader"
},
"PipelineViewTag": "MainCamera"
}
},
{
"Name": "ReflectionCopyFrameBufferPass",
"TemplateName": "ReflectionCopyFrameBufferPassTemplate",
"Enabled": false,
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "DeferredFogPass",
"Attachment": "RenderTargetInputOutput"
}
}
]
},
{
"Name": "PostProcessPass",
"TemplateName": "PostProcessParentTemplate",
"Connections": [
{
"LocalSlot": "LightingInput",
"AttachmentRef": {
"Pass": "DeferredFogPass",
"Attachment": "RenderTargetInputOutput"
}
},
{
"LocalSlot": "Depth",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "MotionVectors",
"AttachmentRef": {
"Pass": "MotionVectorPass",
"Attachment": "MotionVectorOutput"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "AuxGeomPass",
"TemplateName": "AuxGeomPassTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "PostProcessPass",
"Attachment": "Output"
}
},
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
}
],
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "auxgeom",
"PipelineViewTag": "MainCamera"
}
},
{
"Name": "DebugOverlayPass",
"TemplateName": "DebugOverlayParentTemplate",
"Connections": [
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "TileLightData"
}
},
{
"LocalSlot": "RawLightingInput",
"AttachmentRef": {
"Pass": "PostProcessPass",
"Attachment": "RawLightingOutput"
}
},
{
"LocalSlot": "LuminanceMipChainInput",
"AttachmentRef": {
"Pass": "PostProcessPass",
"Attachment": "LuminanceMipChainOutput"
}
},
{
"LocalSlot": "InputOutput",
"AttachmentRef": {
"Pass": "AuxGeomPass",
"Attachment": "ColorInputOutput"
}
}
]
},
{
"Name": "LyShinePass",
"TemplateName": "LyShineParentTemplate",
"Connections": [
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "DebugOverlayPass",
"Attachment": "InputOutput"
}
},
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
}
]
},
{
"Name": "UIPass",
"TemplateName": "UIParentTemplate",
"Connections": [
{
"LocalSlot": "InputOutput",
"AttachmentRef": {
"Pass": "LyShinePass",
"Attachment": "ColorInputOutput"
}
},
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
}
]
},
{
"Name": "CopyToSwapChain",
"TemplateName": "FullscreenCopyTemplate",
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "UIPass",
"Attachment": "InputOutput"
}
},
{
"LocalSlot": "Output",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
}
]
}
}
}
@@ -121,9 +121,10 @@
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"
"guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
},
"assetHint": "surfacetypemateriallibrary.physmaterial"
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
}
}
}
@@ -109,9 +109,10 @@
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"
"guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
},
"assetHint": "surfacetypemateriallibrary.physmaterial"
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
}
}
}
@@ -121,9 +121,10 @@
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"
"guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
},
"assetHint": "surfacetypemateriallibrary.physmaterial"
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
}
}
}
@@ -121,9 +121,10 @@
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"
"guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
},
"assetHint": "surfacetypemateriallibrary.physmaterial"
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
}
}
}
@@ -121,9 +121,10 @@
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"
"guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
},
"assetHint": "surfacetypemateriallibrary.physmaterial"
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
}
}
}
@@ -103,9 +103,10 @@
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"
"guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
},
"assetHint": "surfacetypemateriallibrary.physmaterial"
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
}
}
}
+19
View File
@@ -338,6 +338,8 @@ CConsoleSCB::CConsoleSCB(QWidget* parent)
connect(findPreviousAction, &QAction::triggered, this, &CConsoleSCB::findPrevious);
ui->findPrevButton->addAction(findPreviousAction);
GetIEditor()->RegisterNotifyListener(this);
connect(ui->button, &QPushButton::clicked, this, &CConsoleSCB::showVariableEditor);
connect(ui->findButton, &QPushButton::clicked, this, &CConsoleSCB::toggleConsoleSearch);
connect(ui->textEdit, &ConsoleTextEdit::searchBarRequested, this, [this]
@@ -376,6 +378,8 @@ CConsoleSCB::~CConsoleSCB()
{
AzToolsFramework::EditorPreferencesNotificationBus::Handler::BusDisconnect();
GetIEditor()->UnregisterNotifyListener(this);
s_consoleSCB = nullptr;
CLogFile::AttachEditBox(nullptr);
}
@@ -1352,4 +1356,19 @@ CConsoleSCB* CConsoleSCB::GetCreatedInstance()
return s_consoleSCB;
}
void CConsoleSCB::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnBeginGameMode:
if (gSettings.clearConsoleOnGameModeStart)
{
ui->textEdit->clear();
}
break;
default:
break;
}
}
#include <Controls/moc_ConsoleSCB.cpp>
+3
View File
@@ -159,6 +159,7 @@ private:
class CConsoleSCB
: public QWidget
, private AzToolsFramework::EditorPreferencesNotificationBus::Handler
, public IEditorNotifyListener
{
Q_OBJECT
public:
@@ -187,6 +188,8 @@ private Q_SLOTS:
void findNext();
private:
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
QScopedPointer<Ui::Console> ui;
int m_richEditTextLength;
+4 -4
View File
@@ -544,12 +544,12 @@ void LevelEditorMenuHandler::PopulateEditMenu(ActionManager::MenuWrapper& editMe
auto snapMenu = modifyMenu.AddMenu(tr("Snap"));
snapMenu.AddAction(ID_SNAPANGLE);
snapMenu.AddAction(AzToolsFramework::SnapAngle);
auto transformModeMenu = modifyMenu.AddMenu(tr("Transform Mode"));
transformModeMenu.AddAction(ID_EDITMODE_MOVE);
transformModeMenu.AddAction(ID_EDITMODE_ROTATE);
transformModeMenu.AddAction(ID_EDITMODE_SCALE);
transformModeMenu.AddAction(AzToolsFramework::EditModeMove);
transformModeMenu.AddAction(AzToolsFramework::EditModeRotate);
transformModeMenu.AddAction(AzToolsFramework::EditModeScale);
editMenu.AddSeparator();
+8 -73
View File
@@ -375,9 +375,6 @@ void CCryEditApp::RegisterActionHandlers()
});
ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject)
ON_COMMAND(ID_RENAME_OBJ, OnRenameObj)
ON_COMMAND(ID_EDITMODE_MOVE, OnEditmodeMove)
ON_COMMAND(ID_EDITMODE_ROTATE, OnEditmodeRotate)
ON_COMMAND(ID_EDITMODE_SCALE, OnEditmodeScale)
ON_COMMAND(ID_UNDO, OnUndo)
ON_COMMAND(ID_TOOLBAR_WIDGET_REDO, OnUndo) // Can't use the same ID, because for the menu we can't have a QWidgetAction, while for the toolbar we want one
ON_COMMAND(ID_IMPORT_ASSET, OnOpenAssetImporter)
@@ -2579,75 +2576,6 @@ void CCryEditApp::OnRenameObj()
{
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnEditmodeMove()
{
using namespace AzToolsFramework;
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(),
&EditorTransformComponentSelectionRequests::SetTransformMode,
EditorTransformComponentSelectionRequests::Mode::Translation);
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnEditmodeRotate()
{
using namespace AzToolsFramework;
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(),
&EditorTransformComponentSelectionRequests::SetTransformMode,
EditorTransformComponentSelectionRequests::Mode::Rotation);
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnEditmodeScale()
{
using namespace AzToolsFramework;
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(),
&EditorTransformComponentSelectionRequests::SetTransformMode,
EditorTransformComponentSelectionRequests::Mode::Scale);
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnUpdateEditmodeMove(QAction* action)
{
Q_ASSERT(action->isCheckable());
AzToolsFramework::EditorTransformComponentSelectionRequests::Mode mode;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
mode, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequests::GetTransformMode);
action->setChecked(mode == AzToolsFramework::EditorTransformComponentSelectionRequests::Mode::Translation);
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnUpdateEditmodeRotate(QAction* action)
{
Q_ASSERT(action->isCheckable());
AzToolsFramework::EditorTransformComponentSelectionRequests::Mode mode;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
mode, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequests::GetTransformMode);
action->setChecked(mode == AzToolsFramework::EditorTransformComponentSelectionRequests::Mode::Rotation);
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnUpdateEditmodeScale(QAction* action)
{
Q_ASSERT(action->isCheckable());
AzToolsFramework::EditorTransformComponentSelectionRequests::Mode mode;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
mode, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequests::GetTransformMode);
action->setChecked(mode == AzToolsFramework::EditorTransformComponentSelectionRequests::Mode::Scale);
}
void CCryEditApp::OnViewSwitchToGame()
{
if (IsInPreviewMode())
@@ -2901,7 +2829,14 @@ void CCryEditApp::OpenProjectManager(const AZStd::string& screen)
{
// provide the current project path for in case we want to update the project
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project-path %s", screen.c_str(), projectPath.c_str());
#if !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
const char* argumentQuoteString = R"(")";
#else
const char* argumentQuoteString = R"(\")";
#endif
const AZStd::string commandLineOptions = AZStd::string::format(R"( --screen %s --project-path %s%s%s)",
screen.c_str(),
argumentQuoteString, projectPath.c_str(), argumentQuoteString);
bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions);
if (!launchSuccess)
{
-6
View File
@@ -208,12 +208,6 @@ public:
void DeleteSelectedEntities(bool includeDescendants);
void OnMoveObject();
void OnRenameObj();
void OnEditmodeMove();
void OnEditmodeRotate();
void OnEditmodeScale();
void OnUpdateEditmodeMove(QAction* action);
void OnUpdateEditmodeRotate(QAction* action);
void OnUpdateEditmodeScale(QAction* action);
void OnUndo();
void OnOpenAssetImporter();
void OnUpdateSelected(QAction* action);
+63 -84
View File
@@ -108,21 +108,12 @@ namespace Internal
// CCryEditDoc construction/destruction
CCryEditDoc::CCryEditDoc()
: doc_validate_surface_types(0)
: doc_validate_surface_types(nullptr)
, m_modifiedModuleFlags(eModifiedNothing)
// It assumes loaded levels have already been exported. Can be a big fat lie, though.
// The right way would require us to save to the level folder the export status of the
// level.
, m_boLevelExported(true)
, m_modified(false)
, m_envProbeHeight(200.0f)
, m_envProbeSliceRelativePath("EngineAssets/Slices/DefaultLevelSetup.slice")
{
////////////////////////////////////////////////////////////////////////
// Set member variables to initial values
////////////////////////////////////////////////////////////////////////
m_bLoadFailed = false;
m_waterColor = QColor(0, 0, 255);
m_fogTemplate = GetIEditor()->FindTemplate("Fog");
m_environmentTemplate = GetIEditor()->FindTemplate("Environment");
@@ -136,7 +127,6 @@ CCryEditDoc::CCryEditDoc()
m_environmentTemplate = XmlHelpers::CreateXmlNode("Environment");
}
m_bDocumentReady = false;
GetIEditor()->SetDocument(this);
CLogFile::WriteLine("Document created");
RegisterConsoleVariables();
@@ -195,7 +185,7 @@ CCryEditDoc::DocumentEditingMode CCryEditDoc::GetEditMode() const
QString CCryEditDoc::GetActivePathName() const
{
return DocumentEditingMode() == CCryEditDoc::DocumentEditingMode::SliceEdit ? GetSlicePathName() : GetLevelPathName();
return GetEditMode() == CCryEditDoc::DocumentEditingMode::SliceEdit ? GetSlicePathName() : GetLevelPathName();
}
QString CCryEditDoc::GetTitle() const
@@ -260,9 +250,9 @@ void CCryEditDoc::DeleteContents()
GetIEditor()->FlushUndo();
// Notify listeners.
for (std::list<IDocListener*>::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
for (IDocListener* listener : m_listeners)
{
(*it)->OnCloseDocument();
listener->OnCloseDocument();
}
GetIEditor()->ResetViews();
@@ -458,7 +448,7 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
//////////////////////////////////////////////////////////////////////////
// Load water color.
//////////////////////////////////////////////////////////////////////////
(*arrXmlAr[DMAS_GENERAL]).root->getAttr("WaterColor", m_waterColor);
(*arrXmlAr[DMAS_GENERAL]).root->getAttr("WaterColor", m_waterColor);
//////////////////////////////////////////////////////////////////////////
// Load View Settings
@@ -507,9 +497,9 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
CAutoLogTime logtime("Post Load");
// Notify listeners.
for (std::list<IDocListener*>::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
for (IDocListener* listener : m_listeners)
{
(*it)->OnLoadDocument();
listener->OnLoadDocument();
}
}
@@ -708,7 +698,8 @@ bool CCryEditDoc::SaveModified()
return true;
}
auto button = QMessageBox::question(AzToolsFramework::GetActiveWindow(), QString(), tr("Save changes to %1?").arg(GetTitle()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
auto button = QMessageBox::question(AzToolsFramework::GetActiveWindow(), QString(), tr("Save changes to %1?").arg(GetTitle()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
switch (button)
{
case QMessageBox::Cancel:
@@ -933,8 +924,7 @@ bool CCryEditDoc::OnSaveDocument(const QString& lpszPathName)
}
TSaveDocContext context;
if (shouldSaveLevel &&
BeforeSaveDocument(lpszPathName, context))
if (shouldSaveLevel && BeforeSaveDocument(lpszPathName, context))
{
DoSaveDocument(lpszPathName, context);
saveSuccess = AfterSaveDocument(lpszPathName, context);
@@ -972,7 +962,7 @@ bool CCryEditDoc::BeforeSaveDocument(const QString& lpszPathName, TSaveDocContex
return TRUE;
}
bool CCryEditDoc::HasLayerNameConflicts()
bool CCryEditDoc::HasLayerNameConflicts() const
{
AZStd::vector<AZ::Entity*> editorEntities;
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
@@ -1004,43 +994,42 @@ bool CCryEditDoc::HasLayerNameConflicts()
bool CCryEditDoc::DoSaveDocument(const QString& filename, TSaveDocContext& context)
{
bool& bSaved = context.bSaved;
if (bSaved)
if (!bSaved)
{
// Paranoia - we shouldn't get this far into the save routine without a level loaded (empty levelPath)
// If nothing is loaded, we don't need to save anything
if (filename.isEmpty())
{
bSaved = false;
}
else
{
// Save Tag Point locations to file if auto save of tag points disabled
if (!gSettings.bAutoSaveTagPoints)
{
CCryEditApp::instance()->SaveTagLocations();
}
QString normalizedPath = Path::ToUnixPath(filename);
if (IsSliceFile(normalizedPath))
{
bSaved = SaveSlice(normalizedPath);
}
else
{
bSaved = SaveLevel(normalizedPath);
}
// Changes filename for this document.
SetPathName(normalizedPath);
}
return false;
}
// Paranoia - we shouldn't get this far into the save routine without a level loaded (empty levelPath)
// If nothing is loaded, we don't need to save anything
if (filename.isEmpty())
{
bSaved = false;
return false;
}
// Save Tag Point locations to file if auto save of tag points disabled
if (!gSettings.bAutoSaveTagPoints)
{
CCryEditApp::instance()->SaveTagLocations();
}
QString normalizedPath = Path::ToUnixPath(filename);
if (IsSliceFile(normalizedPath))
{
bSaved = SaveSlice(normalizedPath);
}
else
{
bSaved = SaveLevel(normalizedPath);
}
// Changes filename for this document.
SetPathName(normalizedPath);
return bSaved;
}
bool CCryEditDoc::AfterSaveDocument([[maybe_unused]] const QString& lpszPathName, TSaveDocContext& context, bool bShowPrompt)
{
bool& bSaved = context.bSaved;
bool bSaved = context.bSaved;
GetIEditor()->Notify(eNotify_OnEndSceneSave);
@@ -1067,8 +1056,7 @@ bool CCryEditDoc::AfterSaveDocument([[maybe_unused]] const QString& lpszPathName
static void GetUserSettingsFile(const QString& levelFolder, QString& userSettings)
{
const char* pUserName = GetISystem()->GetUserName();
QString fileName;
fileName = QStringLiteral("%1_usersettings.editor_xml").arg(pUserName);
QString fileName = QStringLiteral("%1_usersettings.editor_xml").arg(pUserName);
userSettings = Path::Make(levelFolder, fileName);
}
@@ -1182,9 +1170,9 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
}
QString oldFilePath = QDir(oldLevelFolder).absoluteFilePath(sourceName);
QString newFilePath = QDir(newLevelFolder).absoluteFilePath(sourceName);
QString newFilePath = QDir(newLevelFolder).absoluteFilePath(destName);
CFileUtil::CopyFile(oldFilePath, newFilePath);
} while (findHandle = pIPak->FindNext(findHandle));
} while ((findHandle = pIPak->FindNext(findHandle)));
pIPak->FindClose(findHandle);
}
@@ -1506,7 +1494,7 @@ bool CCryEditDoc::LoadEntitiesFromLevel(const QString& levelPakFile)
{
AZStd::vector<char> fileBuffer;
fileBuffer.resize(entitiesFile.GetLength());
if (fileBuffer.size() > 0)
if (!fileBuffer.empty())
{
if (fileBuffer.size() == entitiesFile.ReadRaw(fileBuffer.begin(), fileBuffer.size()))
{
@@ -1910,7 +1898,7 @@ void CCryEditDoc::UnregisterListener(IDocListener* listener)
m_listeners.remove(listener);
}
void CCryEditDoc::LogLoadTime(int time)
void CCryEditDoc::LogLoadTime(int time) const
{
QString appFilePath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath());
QString exePath = Path::GetPath(appFilePath);
@@ -1922,21 +1910,18 @@ void CCryEditDoc::LogLoadTime(int time)
SetFileAttributes(filename.toUtf8().data(), FILE_ATTRIBUTE_ARCHIVE);
#endif
FILE* file = nullptr;
azfopen(&file, filename.toUtf8().data(), "at");
if (file)
QFile file(filename);
if (!file.open(QFile::Append | QFile::Text))
{
char version[50];
GetIEditor()->GetFileVersion().ToShortString(version, AZ_ARRAY_SIZE(version));
QString text;
time = time / 1000;
text = QStringLiteral("\n[%1] Level %2 loaded in %3 seconds").arg(version, level).arg(time);
fwrite(text.toUtf8().data(), text.toUtf8().length(), 1, file);
fclose(file);
return;
}
char version[50];
GetIEditor()->GetFileVersion().ToShortString(version, AZ_ARRAY_SIZE(version));
time = time / 1000;
QString text = QStringLiteral("\n[%1] Level %2 loaded in %3 seconds").arg(version, level).arg(time);
file.write(text.toUtf8());
}
void CCryEditDoc::SetDocumentReady(bool bReady)
@@ -1944,7 +1929,7 @@ void CCryEditDoc::SetDocumentReady(bool bReady)
m_bDocumentReady = bReady;
}
void CCryEditDoc::GetMemoryUsage(ICrySizer* pSizer)
void CCryEditDoc::GetMemoryUsage(ICrySizer* pSizer) const
{
{
SIZER_COMPONENT_NAME(pSizer, "UndoManager(estimate)");
@@ -2068,12 +2053,9 @@ void CCryEditDoc::InitEmptyLevel(int /*resolution*/, int /*unitSize*/, bool /*bU
{
// Notify listeners.
std::list<IDocListener*> listeners = m_listeners;
std::list<IDocListener*>::iterator it, next;
for (it = listeners.begin(); it != listeners.end(); it = next)
for (IDocListener* listener : listeners)
{
next = it;
next++;
(*it)->OnNewDocument();
listener->OnNewDocument();
}
}
@@ -2134,25 +2116,23 @@ void CCryEditDoc::OnEnvironmentPropertyChanged(IVariable* pVar)
{
return;
}
QString childValue;
if (pVar->GetDataType() == IVariable::DT_COLOR)
{
Vec3 value;
pVar->Get(value);
QString buff;
QColor gammaColor = ColorLinearToGamma(ColorF(value.x, value.y, value.z));
buff = QStringLiteral("%1,%2,%3").arg(gammaColor.red()).arg(gammaColor.green()).arg(gammaColor.blue());
childNode->setAttr("value", buff.toUtf8().data());
childValue = QStringLiteral("%1,%2,%3").arg(gammaColor.red()).arg(gammaColor.green()).arg(gammaColor.blue());
}
else
{
QString value;
pVar->Get(value);
childNode->setAttr("value", value.toUtf8().data());
pVar->Get(childValue);
}
childNode->setAttr("value", childValue.toUtf8().data());
}
QString CCryEditDoc::GetCryIndexPath(const LPCTSTR levelFilePath)
QString CCryEditDoc::GetCryIndexPath(const LPCTSTR levelFilePath) const
{
QString levelPath = Path::GetPath(levelFilePath);
QString levelName = Path::GetFileName(levelFilePath);
@@ -2183,8 +2163,7 @@ BOOL CCryEditDoc::LoadXmlArchiveArray(TDocMultiArchive& arrXmlAr, const QString&
}
CPakFile pakFile;
bool loadFromPakSuccess;
loadFromPakSuccess = xmlAr.LoadFromPak(levelPath, pakFile);
bool loadFromPakSuccess = xmlAr.LoadFromPak(levelPath, pakFile);
pIPak->ClosePack(absoluteLevelPath.toUtf8().data());
if (!loadFromPakSuccess)
{
+16 -14
View File
@@ -91,7 +91,7 @@ public: // Create from serialization only
// ClassWizard generated virtual function overrides
virtual bool OnOpenDocument(const QString& lpszPathName);
const bool IsLevelLoadFailed() const { return m_bLoadFailed; }
bool IsLevelLoadFailed() const { return m_bLoadFailed; }
//! Marks this document as having errors.
void SetHasErrors() { m_hasErrors = true; }
@@ -121,7 +121,7 @@ public: // Create from serialization only
CClouds* GetClouds() { return m_pClouds; }
void SetWaterColor(const QColor& col) { m_waterColor = col; }
QColor GetWaterColor() { return m_waterColor; }
QColor GetWaterColor() const { return m_waterColor; }
XmlNodeRef& GetFogTemplate() { return m_fogTemplate; }
XmlNodeRef& GetEnvironmentTemplate() { return m_environmentTemplate; }
void OnEnvironmentPropertyChanged(IVariable* pVar);
@@ -129,7 +129,7 @@ public: // Create from serialization only
void RegisterListener(IDocListener* listener);
void UnregisterListener(IDocListener* listener);
void GetMemoryUsage(ICrySizer* pSizer);
void GetMemoryUsage(ICrySizer* pSizer) const;
static bool IsBackupOrTempLevelSubdirectory(const QString& folderName);
protected:
@@ -161,14 +161,14 @@ protected:
void SerializeFogSettings(CXmlArchive& xmlAr);
virtual void SerializeViewSettings(CXmlArchive& xmlAr);
void SerializeNameSelection(CXmlArchive& xmlAr);
void LogLoadTime(int time);
void LogLoadTime(int time) const;
struct TSaveDocContext
{
bool bSaved;
};
bool BeforeSaveDocument(const QString& lpszPathName, TSaveDocContext& context);
bool HasLayerNameConflicts();
bool HasLayerNameConflicts() const;
bool DoSaveDocument(const QString& lpszPathName, TSaveDocContext& context);
bool AfterSaveDocument(const QString& lpszPathName, TSaveDocContext& context, bool bShowPrompt = true);
@@ -180,7 +180,7 @@ protected:
void OnStartLevelResourceList();
static void OnValidateSurfaceTypesChanged(ICVar*);
QString GetCryIndexPath(const LPCTSTR levelFilePath);
QString GetCryIndexPath(const LPCTSTR levelFilePath) const;
//////////////////////////////////////////////////////////////////////////
// SliceEditorEntityOwnershipServiceNotificationBus::Handler
@@ -188,24 +188,26 @@ protected:
void OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId, const AzFramework::SliceInstantiationTicket& /*ticket*/) override;
//////////////////////////////////////////////////////////////////////////
bool m_bLoadFailed;
QColor m_waterColor;
bool m_bLoadFailed = false;
QColor m_waterColor = QColor(0, 0, 255);
XmlNodeRef m_fogTemplate;
XmlNodeRef m_environmentTemplate;
CClouds* m_pClouds;
std::list<IDocListener*> m_listeners;
bool m_bDocumentReady;
ICVar* doc_validate_surface_types;
bool m_bDocumentReady = false;
ICVar* doc_validate_surface_types = nullptr;
int m_modifiedModuleFlags;
bool m_boLevelExported;
bool m_modified;
// On construction, it assumes loaded levels have already been exported. Can be a big fat lie, though.
// The right way would require us to save to the level folder the export status of the level.
bool m_boLevelExported = true;
bool m_modified = false;
QString m_pathName;
QString m_slicePathName;
QString m_title;
AZ::Data::AssetId m_envProbeSliceAssetId;
float m_terrainSize;
const char* m_envProbeSliceRelativePath;
const float m_envProbeHeight;
const char* m_envProbeSliceRelativePath = "EngineAssets/Slices/DefaultLevelSetup.slice";
const float m_envProbeHeight = 200.0f;
bool m_hasErrors = false; ///< This is used to warn the user that they may lose work when they go to save.
};
@@ -32,6 +32,7 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize)
->Field("PreviewPanel", &GeneralSettings::m_previewPanel)
->Field("ApplyConfigSpec", &GeneralSettings::m_applyConfigSpec)
->Field("EnableSourceControl", &GeneralSettings::m_enableSourceControl)
->Field("ClearConsole", &GeneralSettings::m_clearConsoleOnGameModeStart)
->Field("ConsoleBackgroundColorTheme", &GeneralSettings::m_consoleBackgroundColorTheme)
->Field("AutoloadLastLevel", &GeneralSettings::m_autoLoadLastLevel)
->Field("ShowTimeInConsole", &GeneralSettings::m_bShowTimeInConsole)
@@ -77,6 +78,8 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize)
->DataElement(AZ::Edit::UIHandlers::CheckBox, &GeneralSettings::m_previewPanel, "Show Geometry Preview Panel", "Show Geometry Preview Panel")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &GeneralSettings::m_applyConfigSpec, "Hide objects by config spec", "Hide objects by config spec")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &GeneralSettings::m_enableSourceControl, "Enable Source Control", "Enable Source Control")
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &GeneralSettings::m_clearConsoleOnGameModeStart, "Clear Console at game startup", "Clear Console when game mode starts")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &GeneralSettings::m_consoleBackgroundColorTheme, "Console Background", "Console Background")
->EnumAttribute(AzToolsFramework::ConsoleColorTheme::Light, "Light")
->EnumAttribute(AzToolsFramework::ConsoleColorTheme::Dark, "Dark")
@@ -142,6 +145,7 @@ void CEditorPreferencesPage_General::OnApply()
gSettings.bPreviewGeometryWindow = m_generalSettings.m_previewPanel;
gSettings.bApplyConfigSpecInEditor = m_generalSettings.m_applyConfigSpec;
gSettings.enableSourceControl = m_generalSettings.m_enableSourceControl;
gSettings.clearConsoleOnGameModeStart = m_generalSettings.m_clearConsoleOnGameModeStart;
gSettings.consoleBackgroundColorTheme = m_generalSettings.m_consoleBackgroundColorTheme;
gSettings.bShowTimeInConsole = m_generalSettings.m_bShowTimeInConsole;
gSettings.bShowDashboardAtStartup = m_messaging.m_showDashboard;
@@ -176,6 +180,7 @@ void CEditorPreferencesPage_General::InitializeSettings()
m_generalSettings.m_previewPanel = gSettings.bPreviewGeometryWindow;
m_generalSettings.m_applyConfigSpec = gSettings.bApplyConfigSpecInEditor;
m_generalSettings.m_enableSourceControl = gSettings.enableSourceControl;
m_generalSettings.m_clearConsoleOnGameModeStart = gSettings.clearConsoleOnGameModeStart;
m_generalSettings.m_consoleBackgroundColorTheme = gSettings.consoleBackgroundColorTheme;
m_generalSettings.m_bShowTimeInConsole = gSettings.bShowTimeInConsole;
m_generalSettings.m_autoLoadLastLevel = gSettings.bAutoloadLastLevelAtStartup;
@@ -46,6 +46,7 @@ private:
bool m_previewPanel;
bool m_applyConfigSpec;
bool m_enableSourceControl;
bool m_clearConsoleOnGameModeStart;
AzToolsFramework::ConsoleColorTheme m_consoleBackgroundColorTheme;
bool m_autoLoadLastLevel;
bool m_bShowTimeInConsole;
+13 -2
View File
@@ -472,6 +472,12 @@ void EditorViewportWidget::Update()
m_Camera.SetZRange(cameraState.m_nearClip, cameraState.m_farClip);
}
// Ensure the FOV matches our internally stored setting if we're using the Editor camera
if (!m_viewEntityId.IsValid() && !GetIEditor()->IsInGameMode())
{
SetFOV(GetFOV());
}
// Reset the camera update flag now that we're finished updating our viewport context
m_updateCameraPositionNextTick = false;
@@ -1234,6 +1240,13 @@ AZStd::shared_ptr<AtomToolsFramework::ModularViewportCameraController> CreateMod
AzFramework::ViewportId viewportId)
{
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
controller->SetCameraPriorityBuilderCallback(
[](AtomToolsFramework::CameraControllerPriorityFn& cameraControllerPriorityFn)
{
cameraControllerPriorityFn = AtomToolsFramework::DefaultCameraControllerPriority;
});
controller->SetCameraPropsBuilderCallback(
[](AzFramework::CameraProps& cameraProps)
{
@@ -2624,8 +2637,6 @@ void EditorViewportWidget::DestroyRenderContext()
//////////////////////////////////////////////////////////////////////////
void EditorViewportWidget::SetDefaultCamera()
{
// Ensure the FOV matches our internally stored setting
SetFOV(GetFOV());
if (IsDefaultCamera())
{
return;
+1 -1
View File
@@ -566,7 +566,7 @@ void CGameEngine::SwitchToInGame()
streamer->QueueRequest(flush);
wait.acquire();
}
GetIEditor()->Notify(eNotify_OnBeginGameMode);
m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(true);
+61 -8
View File
@@ -46,6 +46,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
#include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
// AzQtComponents
#include <AzQtComponents/Buses/ShortcutDispatch.h>
@@ -731,32 +732,84 @@ void MainWindow::InitActions()
.SetStatusTip(tr("Restore saved state (Fetch)"));
// Modify actions
am->AddAction(ID_EDITMODE_MOVE, tr("Move"))
am->AddAction(AzToolsFramework::EditModeMove, tr("Move"))
.SetIcon(Style::icon("Move"))
.SetApplyHoverEffect()
.SetShortcut(tr("1"))
.SetToolTip(tr("Move (1)"))
.SetCheckable(true)
.SetStatusTip(tr("Select and move selected object(s)"))
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateEditmodeMove);
am->AddAction(ID_EDITMODE_ROTATE, tr("Rotate"))
.RegisterUpdateCallback([](QAction* action)
{
Q_ASSERT(action->isCheckable());
AzToolsFramework::EditorTransformComponentSelectionRequests::Mode mode;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
mode, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequests::GetTransformMode);
action->setChecked(mode == AzToolsFramework::EditorTransformComponentSelectionRequests::Mode::Translation);
})
.Connect(
&QAction::triggered,
[]()
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode,
EditorTransformComponentSelectionRequests::Mode::Translation);
});
am->AddAction(AzToolsFramework::EditModeRotate, tr("Rotate"))
.SetIcon(Style::icon("Translate"))
.SetApplyHoverEffect()
.SetShortcut(tr("2"))
.SetToolTip(tr("Rotate (2)"))
.SetCheckable(true)
.SetStatusTip(tr("Select and rotate selected object(s)"))
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateEditmodeRotate);
am->AddAction(ID_EDITMODE_SCALE, tr("Scale"))
.RegisterUpdateCallback([](QAction* action)
{
Q_ASSERT(action->isCheckable());
AzToolsFramework::EditorTransformComponentSelectionRequests::Mode mode;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
mode, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequests::GetTransformMode);
action->setChecked(mode == AzToolsFramework::EditorTransformComponentSelectionRequests::Mode::Rotation);
})
.Connect(
&QAction::triggered,
[]()
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode,
EditorTransformComponentSelectionRequests::Mode::Rotation);
});
am->AddAction(AzToolsFramework::EditModeScale, tr("Scale"))
.SetIcon(Style::icon("Scale"))
.SetApplyHoverEffect()
.SetShortcut(tr("3"))
.SetToolTip(tr("Scale (3)"))
.SetCheckable(true)
.SetStatusTip(tr("Select and scale selected object(s)"))
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateEditmodeScale);
.RegisterUpdateCallback([](QAction* action)
{
Q_ASSERT(action->isCheckable());
am->AddAction(ID_SNAP_TO_GRID, tr("Snap to grid"))
AzToolsFramework::EditorTransformComponentSelectionRequests::Mode mode;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
mode, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequests::GetTransformMode);
action->setChecked(mode == AzToolsFramework::EditorTransformComponentSelectionRequests::Mode::Scale);
})
.Connect( &QAction::triggered,[]()
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode,
EditorTransformComponentSelectionRequests::Mode::Scale);
});
am->AddAction(AzToolsFramework::SnapToGrid, tr("Snap to grid"))
.SetIcon(Style::icon("Grid"))
.SetApplyHoverEffect()
.SetShortcut(tr("G"))
@@ -769,7 +822,7 @@ void MainWindow::InitActions()
})
.Connect(&QAction::triggered, []() { SandboxEditor::SetGridSnapping(!SandboxEditor::GridSnappingEnabled()); });
am->AddAction(ID_SNAPANGLE, tr("Snap angle"))
am->AddAction(AzToolsFramework::SnapAngle, tr("Snap angle"))
.SetIcon(Style::icon("Angle"))
.SetApplyHoverEffect()
.SetStatusTip(tr("Snap angle"))
+10 -4
View File
@@ -59,11 +59,17 @@ namespace AzQtComponents
namespace AzToolsFramework
{
class Ticker;
}
namespace AzToolsFramework
{
class QtSourceControlNotificationHandler;
//! @name Reverse URLs.
//! Used to identify common actions and override them when necessary.
//@{
constexpr inline AZ::Crc32 EditModeMove = AZ_CRC_CE("com.o3de.action.editor.editmode.move");
constexpr inline AZ::Crc32 EditModeRotate = AZ_CRC_CE("com.o3de.action.editor.editmode.rotate");
constexpr inline AZ::Crc32 EditModeScale = AZ_CRC_CE("com.o3de.action.editor.editmode.scale");
constexpr inline AZ::Crc32 SnapToGrid = AZ_CRC_CE("com.o3de.action.editor.snaptogrid");
constexpr inline AZ::Crc32 SnapAngle = AZ_CRC_CE("com.o3de.action.editor.snapangle");
//@}
}
#define MAINFRM_LAYOUT_NORMAL "NormalLayout"
-5
View File
@@ -82,7 +82,6 @@
#define ID_TOOLS_CUSTOMIZEKEYBOARD 32914
#define ID_EXPORT_INDOORS 32915
#define ID_VIEW_CYCLE2DVIEWPORT 32916
#define ID_SNAPANGLE 32917
#define ID_PHYSICS_GETPHYSICSSTATE 32937
#define ID_PHYSICS_RESETPHYSICSSTATE 32938
#define ID_GAME_SYNCPLAYER 32941
@@ -108,9 +107,6 @@
#define ID_MOVE_OBJECT 33481
#define ID_RENAME_OBJ 33483
#define ID_FETCH 33496
#define ID_EDITMODE_ROTATE 33506
#define ID_EDITMODE_SCALE 33507
#define ID_EDITMODE_MOVE 33508
#define ID_SELECTION_DELETE 33512
#define ID_EDIT_ESCAPE 33513
#define ID_UNDO 33524
@@ -137,7 +133,6 @@
#define ID_ADDNODE 33570
#define ID_ADDSCENETRACK 33573
#define ID_FIND 33574
#define ID_SNAP_TO_GRID 33575
#define ID_TAG_LOC1 33576
#define ID_TAG_LOC2 33577
#define ID_TAG_LOC3 33578
+7 -2
View File
@@ -189,6 +189,7 @@ SEditorSettings::SEditorSettings()
consoleBackgroundColorTheme = AzToolsFramework::ConsoleColorTheme::Dark;
bShowTimeInConsole = false;
clearConsoleOnGameModeStart = false;
enableSceneInspector = false;
@@ -527,6 +528,8 @@ void SEditorSettings::Save()
SaveValue("Settings", "ConsoleBackgroundColorThemeV2", (int)consoleBackgroundColorTheme);
SaveValue("Settings", "ClearConsoleOnGameModeStart", clearConsoleOnGameModeStart);
SaveValue("Settings", "ShowTimeInConsole", bShowTimeInConsole);
SaveValue("Settings", "EnableSceneInspector", enableSceneInspector);
@@ -745,6 +748,8 @@ void SEditorSettings::Load()
consoleBackgroundColorTheme = AzToolsFramework::ConsoleColorTheme::Dark;
}
LoadValue("Settings", "ClearConsoleOnGameModeStart", clearConsoleOnGameModeStart);
LoadValue("Settings", "ShowTimeInConsole", bShowTimeInConsole);
LoadValue("Settings", "EnableSceneInspector", enableSceneInspector);
@@ -1083,7 +1088,7 @@ void SEditorSettings::ConvertPath(const AZStd::string_view sourcePath, AZStd::st
AzToolsFramework::EditorSettingsAPIRequests::SettingOutcome SEditorSettings::GetValue(const AZStd::string_view path)
{
if (path.find("|") < 0)
if (path.find("|") == AZStd::string_view::npos)
{
return { AZStd::string("Invalid Path - could not find separator \"|\"") };
}
@@ -1101,7 +1106,7 @@ AzToolsFramework::EditorSettingsAPIRequests::SettingOutcome SEditorSettings::Get
AzToolsFramework::EditorSettingsAPIRequests::SettingOutcome SEditorSettings::SetValue(const AZStd::string_view path, const AZStd::any& value)
{
if (path.find("|") < 0)
if (path.find("|") == AZStd::string_view::npos)
{
return { AZStd::string("Invalid Path - could not find separator \"|\"") };
}
+1
View File
@@ -380,6 +380,7 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
//! Source Control Enabling.
bool enableSourceControl;
bool clearConsoleOnGameModeStart;
//! Text editor.
QString textEditorForScript;
+4 -4
View File
@@ -953,13 +953,13 @@ void CViewportTitleDlg::CheckForCameraSpeedUpdate()
void CViewportTitleDlg::OnGridSnappingToggled()
{
m_gridSizeActionWidget->setEnabled(m_enableGridSnappingAction->isChecked());
MainWindow::instance()->GetActionManager()->GetAction(ID_SNAP_TO_GRID)->trigger();
MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::SnapToGrid)->trigger();
}
void CViewportTitleDlg::OnAngleSnappingToggled()
{
m_angleSizeActionWidget->setEnabled(m_enableAngleSnappingAction->isChecked());
MainWindow::instance()->GetActionManager()->GetAction(ID_SNAPANGLE)->trigger();
MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::SnapAngle)->trigger();
}
void CViewportTitleDlg::OnGridSpinBoxChanged(double value)
@@ -974,14 +974,14 @@ void CViewportTitleDlg::OnAngleSpinBoxChanged(double value)
void CViewportTitleDlg::UpdateOverFlowMenuState()
{
bool gridSnappingActive = MainWindow::instance()->GetActionManager()->GetAction(ID_SNAP_TO_GRID)->isChecked();
bool gridSnappingActive = MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::SnapToGrid)->isChecked();
{
QSignalBlocker signalBlocker(m_enableGridSnappingAction);
m_enableGridSnappingAction->setChecked(gridSnappingActive);
}
m_gridSizeActionWidget->setEnabled(gridSnappingActive);
bool angleSnappingActive = MainWindow::instance()->GetActionManager()->GetAction(ID_SNAPANGLE)->isChecked();
bool angleSnappingActive = MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::SnapAngle)->isChecked();
{
QSignalBlocker signalBlocker(m_enableAngleSnappingAction);
m_enableAngleSnappingAction->setChecked(angleSnappingActive);
@@ -211,7 +211,7 @@ namespace AZStd
// 20.9.3.2, observer:
constexpr rep count() const { return m_rep; }
// 20.9.3.3, arithmetic:
constexpr duration operator+() const { *this; }
constexpr duration operator+() const { return *this; }
constexpr duration operator-() const { return duration(-m_rep); }
constexpr duration& operator++() { ++m_rep; return *this; }
constexpr duration operator++(int) { return duration(m_rep++); }
@@ -1056,7 +1056,7 @@ namespace AZStd
inline void insert(const iterator& pos, ForwardIterator first, ForwardIterator last, const AZStd::forward_iterator_tag&)
{
size_type size = AZStd::distance(first, last);
AZSTD_CONTAINER_ASSERT(size >= 0, "AZStd::ring_buffer::insert - there are no elements to insert!");
AZSTD_CONTAINER_ASSERT(first > last, "AZStd::ring_buffer::insert - there are no elements to insert!");
if (size == 0)
{
return;
@@ -21,6 +21,7 @@ namespace AZStd
1610612741ul, 3221225473ul, 4294967291ul
};
// Bucket size suitable to hold n elements.
AZStd::size_t hash_next_bucket_size(AZStd::size_t n)
{
const AZStd::size_t* first = prime_list;
+42 -26
View File
@@ -134,6 +134,7 @@ namespace AZStd
void rehash(HashTable* table, size_type numBucketsMin)
{
size_type num_buckets = 0;
numBucketsMin = (AZStd::max)(numBucketsMin, (size_type)ceilf((float)m_list.size() / m_max_load_factor));
if (numBucketsMin != 0)
@@ -143,7 +144,7 @@ namespace AZStd
if (num_buckets == m_numBuckets)
{
return; // no point
return; // no need yet to rehash
}
m_numBuckets = num_buckets;
@@ -165,32 +166,43 @@ namespace AZStd
while (!m_list.empty())
{
cur = m_list.begin();
typename list_type::iterator insertIter, curEnd(cur);
const typename HashTable::key_type& valueKey = Traits::key_from_value(*cur);
typename list_type::iterator newIter, iter(cur);
size_type numValues = 1;
for (++iter; iter != last && table->m_keyEqual(Traits::key_from_value(*cur), Traits::key_from_value(*iter)); ++iter, ++numValues)
// Get the number of same consecutive elements in the table with same key,
// this allows range insertion of elements at once
for (++curEnd; curEnd != last && table->m_keyEqual(valueKey, Traits::key_from_value(*curEnd)); ++curEnd, ++numValues)
{
}
;
const typename HashTable::key_type& valueKey = Traits::key_from_value(*cur);
size_type newBucketIndex = table->bucket_from_hash(table->m_hasher(valueKey));
// newBucket.first holds the total number of elements in the bucket
// newBucket.second contains the pointer to the first element in the bucket
vector_value_type& newBucket = newBuckets[newBucketIndex];
size_type numElements = newBucket.first;
newIter = newBucket.second;
insertIter = newBucket.second;
// If we don't have elements in the bucket yet, transfer the elements directly
if (numElements == 0)
{
newList.splice(newList.begin(), m_list, cur, iter);
newList.splice(newList.begin(), m_list, cur, curEnd);
newBucket.second = newList.begin();
}
else
{
if (!table->find_insert_position(valueKey, table->m_keyEqual, newIter, numElements, integral_constant<bool, Traits::has_multi_elements>()))
// Since there are elements already in the bucket, update `insertIter` to where the elements will need to be inserted.
if (!table->find_insert_position(valueKey, table->m_keyEqual, insertIter, numElements, integral_constant<bool, Traits::has_multi_elements>()))
{
continue;
// An element was found but we don't allow for duplicate elements in this table.
// This happens when there was an insertion of two elements that are equal but have different hashes,
// which is undefined behavior for a hash table: ISO C++ N4713, section 23.14.15 - 5.3
AZ_Assert(false, "Found a duplicate element when rehashing. "
"Review the hashing function for this type and make sure two equal elements always have the same hash");
}
newList.splice(newIter, m_list, cur, iter);
newList.splice(insertIter, m_list, cur, curEnd);
}
newBucket.first += numValues;
@@ -251,15 +263,15 @@ namespace AZStd
m_vector.set_allocator(typename vector_type::allocator_type(&m_allocator));
}
allocator_type m_allocator; ///< The single instance of the allocator shared between list and vector containers.
list_type m_list; ///< List with elements.
vector_type m_vector; ///< Buckets with list iterators.
allocator_type m_allocator; //!< The single instance of the allocator shared between list and vector containers.
list_type m_list; //!< List with elements.
vector_type m_vector; //!< Buckets with list iterators.
private:
vector_value_type* m_buckets; ///< Current buckets array. (can point to the m_vector or m_startBucket).
size_type m_numBuckets; ///< Current number of buckets.
float m_max_load_factor;
vector_value_type m_startBucket; ///< Start bucket used for before we start dynamically allocate memory from m_vector.
vector_value_type* m_buckets; //!< Current buckets array. (can point to the m_vector or m_startBucket).
size_type m_numBuckets; //!< Current number of buckets.
float m_max_load_factor; //!< Maximum load (elements/buckets) before rehashing.
vector_value_type m_startBucket; //!< Start bucket used for before we start dynamically allocate memory from m_vector.
};
/**
@@ -321,8 +333,8 @@ namespace AZStd
template<class HashTable>
AZ_FORCE_INLINE void rehash(HashTable*, size_type) {}
vector_type m_vector; ///< Buckets with list iterators.
list_type m_list; ///< List with elements.
vector_type m_vector; //!< Buckets with list iterators.
list_type m_list; //!< List with elements.
};
}
@@ -972,28 +984,32 @@ namespace AZStd
rhs.clear();
}
// find_insert_position sets insertIter to where the element should be inserted
// and returns true if the element should be inserted, otherwise false
template<class ComparableToKey, class KeyEq>
bool find_insert_position(const ComparableToKey& keyCmp, const KeyEq& keyEq, iterator& iter, size_type numElements, const true_type& /* is multi elements */)
bool find_insert_position(const ComparableToKey& keyCmp, const KeyEq& keyEq, iterator& insertIter, size_type numElements, const true_type& /* is multi elements */)
{
for (size_type i = 0; i < numElements; ++i, ++iter)
for (size_type i = 0; i < numElements; ++i, ++insertIter)
{
if (keyEq(keyCmp, Traits::key_from_value(*iter)))
if (keyEq(keyCmp, Traits::key_from_value(*insertIter)))
{
++iter;
++insertIter;
break;
}
}
// always return true since multi elements (like multiset) allow repeated elements
return true;
}
template<class ComparableToKey, class KeyEq>
bool find_insert_position(const ComparableToKey& keyCmp, const KeyEq& keyEq, iterator& iter, size_type numElements, const false_type& /* !is multi elements */)
bool find_insert_position(const ComparableToKey& keyCmp, const KeyEq& keyEq, iterator& insertIter, size_type numElements, const false_type& /* !is multi elements */)
{
for (size_type i = 0; i < numElements; ++i, ++iter)
for (size_type i = 0; i < numElements; ++i, ++insertIter)
{
if (keyEq(keyCmp, Traits::key_from_value(*iter)))
if (keyEq(keyCmp, Traits::key_from_value(*insertIter)))
{
// Element already exists, it shouldn't be inserted as we don't allow more than one repeated element for this specialization
return false;
}
}
+1 -1
View File
@@ -294,7 +294,7 @@ namespace AZStd
T& m_v;
constexpr addr_impl_ref(T& v)
: m_v(v) {}
constexpr addr_impl_ref& operator=(const addr_impl_ref& v) { m_v = v; }
constexpr addr_impl_ref& operator=(const addr_impl_ref& v) { m_v = v; return *this; }
constexpr operator T& () const { return m_v; }
};
@@ -287,6 +287,55 @@ namespace UnitTest
}
}
TEST_F(HashedContainers, HashTable_InsertionDuplicateOnRehash)
{
struct TwoPtrs
{
void* m_ptr1;
void* m_ptr2;
bool operator==(const TwoPtrs& other) const
{
if (m_ptr1 == other.m_ptr1)
{
return m_ptr2 == other.m_ptr2;
}
else if (m_ptr1 == other.m_ptr2)
{
return m_ptr2 == other.m_ptr1;
}
return false;
}
};
// This hashing function produces different hashes for two equal values,
// which violates the requirement for hashing functions.
// The test makes sure that this does not reproduce an issue that caused the insert() function to loop infinitely.
struct TwoPtrsHasher
{
size_t operator()(const TwoPtrs& p) const
{
size_t hash{ 0 };
AZStd::hash_combine(hash, p.m_ptr1, p.m_ptr2);
return hash;
}
};
using PairSet = AZStd::unordered_set<TwoPtrs, TwoPtrsHasher>;
PairSet set;
set.insert({ (void*)1, (void*)2 });
set.insert({ (void*)3, (void*)4 });
set.insert({ (void*)5, (void*)6 });
set.insert({ (void*)7, (void*)8 });
// Elements with different hashes, but equal
set.insert({ (void*)0x000001ceddd9ca20, (void*)0x000001ceddd9cba0 }); // hash(148335135725641)
set.insert({ (void*)0x000001ceddd9cba0, (void*)0x000001ceddd9ca20 }); // hash(148335135764189)
AZ_TEST_START_TRACE_SUPPRESSION;
// This will trigger the assertion of duplicated elements found
// A bucket size of 23 since is where the collision between different hashes happens
set.rehash(23);
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // 1 assertion
}
TEST_F(HashedContainers, HashTable_Fixed)
{
array<int, 5> elements = {
@@ -1150,7 +1150,7 @@ namespace UnitTest
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_ContainerFilterTest_ContainersWithAndWithoutFiltering_Success)
#else
TEST_F(AssetJobsFloodTest, ContainerFilterTest_ContainersWithAndWithoutFiltering_Success)
TEST_F(AssetJobsFloodTest, DISABLED_ContainerFilterTest_ContainersWithAndWithoutFiltering_Success)
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
@@ -163,7 +163,11 @@ namespace AzFramework
AZ_TracePrintfOnce("AssetSystemComponent", "Failed to find asset platform, setting 'pc'\n");
outputConnectionSettings.m_assetPlatform = "pc";
}
outputConnectionSettings.m_assetPlatform = assetsPlatform;
else
{
outputConnectionSettings.m_assetPlatform = assetsPlatform;
}
if (outputConnectionSettings.m_assetPlatform.empty())
{
assetsPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
@@ -227,8 +227,10 @@ namespace AzFramework
EntitySpawnTicket::EntitySpawnTicket(EntitySpawnTicket&& rhs)
: m_payload(rhs.m_payload)
, m_id(rhs.m_id)
{
rhs.m_payload = nullptr;
rhs.m_id = 0;
}
EntitySpawnTicket::EntitySpawnTicket(AZ::Data::Asset<Spawnable> spawnable)
@@ -269,7 +269,7 @@ namespace AzFramework
int numEnvironmentVars = 0;
if (processLaunchInfo.m_environmentVariables)
{
const int numEnvironmentVars = processLaunchInfo.m_environmentVariables->size();
numEnvironmentVars = processLaunchInfo.m_environmentVariables->size();
// Adding one more as exec expects the array to have a nullptr as the last element
environmentVariables = new char*[numEnvironmentVars + 1];
for (int i = 0; i < numEnvironmentVars; i++)
@@ -366,6 +366,24 @@ namespace UnitTest
}
}
TEST_F(SpawnableEntitiesManagerTest, EntitySpawnTicket_Move_Works)
{
AzFramework::EntitySpawnTicket ticket1(*m_spawnableAsset);
AzFramework::EntitySpawnTicket ticket2(*m_spawnableAsset);
const AzFramework::EntitySpawnTicket::Id ticket1Id = ticket1.GetId();
const AzFramework::EntitySpawnTicket::Id ticket2Id = ticket2.GetId();
AzFramework::EntitySpawnTicket ticketMoveConstructor(AZStd::move(ticket1));
EXPECT_TRUE(ticketMoveConstructor.IsValid());
EXPECT_EQ(ticketMoveConstructor.GetId(), ticket1Id);
AzFramework::EntitySpawnTicket ticketMoveOperator;
ticketMoveOperator = AZStd::move(ticket2);
EXPECT_TRUE(ticketMoveOperator.IsValid());
EXPECT_EQ(ticketMoveOperator.GetId(), ticket2Id);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash)
{
{
@@ -103,6 +103,14 @@ namespace AzNetworking
//! @return boolean true on success
virtual bool Disconnect(ConnectionId connectionId, DisconnectReason reason) = 0;
//! Sets whether this connection interface can disconnect by virtue of a timeout
//! @param timeoutEnabled If this connection interface will automatically disconnect due to a timeout
virtual void SetTimeoutEnabled(bool timeoutEnabled) = 0;
//! Whether this connection interface will disconnect by virtue of a time out (does not account for cvars affecting all connections)
//! @return boolean true if this connection will not disconnect on timeout (does not account for cvars affecting all connections)
virtual bool IsTimeoutEnabled() const = 0;
//! Const access to the metrics tracked by this network interface.
//! @return const reference to the metrics tracked by this network interface
const NetworkInterfaceMetrics& GetMetrics() const;
@@ -174,6 +174,16 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
void TcpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
{
m_timeoutEnabled = timeoutEnabled;
}
bool TcpNetworkInterface::IsTimeoutEnabled() const
{
return m_timeoutEnabled;
}
void TcpNetworkInterface::QueueNewConnection(const PendingConnection& pendingConnection)
{
m_pendingConnections.PushBackItem(pendingConnection);
@@ -306,7 +316,7 @@ namespace AzNetworking
{
tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket());
}
else if (net_TcpTimeoutConnections)
else if (net_TcpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
{
tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
@@ -99,6 +99,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
void SetTimeoutEnabled(bool timeoutEnabled) override;
bool IsTimeoutEnabled() const override;
//! @}
//! Queues a new incoming connection for this network interface.
@@ -154,6 +156,7 @@ namespace AzNetworking
AZ::Name m_name;
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_timeoutEnabled = true;
IConnectionListener& m_connectionListener;
TcpConnectionSet m_connectionSet;
TcpSocketManager m_tcpSocketManager;
@@ -397,6 +397,16 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
void UdpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
{
m_timeoutEnabled = timeoutEnabled;
}
bool UdpNetworkInterface::IsTimeoutEnabled() const
{
return m_timeoutEnabled;
}
bool UdpNetworkInterface::IsEncrypted() const
{
return m_socket->IsEncrypted();
@@ -729,7 +739,7 @@ namespace AzNetworking
{
udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket());
}
else if (net_UdpTimeoutConnections)
else if (net_UdpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
{
udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
@@ -104,6 +104,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
void SetTimeoutEnabled(bool timeoutEnabled) override;
bool IsTimeoutEnabled() const override;
//! @}
//! Returns true if this is an encrypted socket, false if not.
@@ -179,6 +181,7 @@ namespace AzNetworking
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_allowIncomingConnections = false;
bool m_timeoutEnabled = true;
IConnectionListener& m_connectionListener;
UdpConnectionSet m_connectionSet;
TimeoutQueue m_connectionTimeoutQueue;
@@ -72,15 +72,8 @@ namespace AzToolsFramework
if (m_rootInstance != nullptr)
{
// Need to save off the template id to remove the template after the instance is deleted.
Prefab::TemplateId templateId = m_rootInstance->GetTemplateId();
m_rootInstance.reset();
if (templateId != Prefab::InvalidTemplateId)
{
// Remove the template here so that if we're in a Deactivate/Activate cycle, it can recreate the template/rootInstance
// correctly
m_prefabSystemComponent->RemoveTemplate(templateId);
}
m_prefabSystemComponent->RemoveAllTemplates();
}
}
@@ -95,7 +88,7 @@ namespace AzToolsFramework
if (templateId != Prefab::InvalidTemplateId)
{
m_rootInstance->SetTemplateId(Prefab::InvalidTemplateId);
m_prefabSystemComponent->RemoveTemplate(templateId);
m_prefabSystemComponent->RemoveAllTemplates();
}
m_rootInstance->SetContainerEntityName("Level");
}
@@ -245,7 +245,9 @@ namespace UnitTest
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, *firstInstance);
//remove instance from instance
firstInstance->DetachNestedInstance(addedAlias);
AZStd::unique_ptr<Instance> detachedInstance = firstInstance->DetachNestedInstance(addedAlias);
ASSERT_TRUE(detachedInstance != nullptr);
m_prefabSystemComponent->RemoveLink(detachedInstance->GetLinkId());
//create document with after change snapshot
PrefabDom instanceDomAfterUpdate;
@@ -309,6 +309,7 @@ namespace UnitTest
// and use the updated enclosing Instance to update the PrefabDom of Template.
AZStd::unique_ptr<Instance> detachedInstance = newEnclosingInstance->DetachNestedInstance(nestedInstanceAliases.front());
ASSERT_TRUE(detachedInstance);
m_prefabSystemComponent->RemoveLink(detachedInstance->GetLinkId());
PrefabDom updatedTemplateDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newEnclosingInstance, updatedTemplateDom));
@@ -274,6 +274,7 @@ namespace UnitTest
InstanceAlias aliasOfWheelInstanceToRetain = wheelInstanceAliasesUnderAxle.front();
AZStd::unique_ptr<Instance> detachedInstance = axleInstance->DetachNestedInstance(wheelInstanceAliasesUnderAxle.back());
ASSERT_TRUE(detachedInstance);
m_prefabSystemComponent->RemoveLink(detachedInstance->GetLinkId());
PrefabDom updatedAxleInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*axleInstance, updatedAxleInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(axleTemplateId, updatedAxleInstanceDom);
@@ -118,7 +118,9 @@ namespace O3DE::ProjectManager
}
}
if (m_configProjectProcess->exitCode() != 0 || !containsGeneratingDone)
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit
|| m_configProjectProcess->exitCode() != 0
|| !containsGeneratingDone)
{
QString error = tr("Configuring project failed. See log for details.");
QStringToAZTracePrint(error);
@@ -180,7 +182,8 @@ namespace O3DE::ProjectManager
}
}
if (m_configProjectProcess->exitCode() != 0)
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit
|| m_configProjectProcess->exitCode() != 0)
{
QString error = tr("Building project failed. See log for details.");
QStringToAZTracePrint(error);
@@ -265,6 +265,6 @@ namespace O3DE::ProjectManager
void CreateProjectCtrl::ReinitGemCatalogForSelectedTemplate()
{
const QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true);
m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template");
}
} // namespace O3DE::ProjectManager
@@ -62,10 +62,10 @@ namespace O3DE::ProjectManager
hLayout->addWidget(m_gemInspector);
}
void GemCatalogScreen::ReinitForProject(const QString& projectPath, bool isNewProject)
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
{
m_gemModel->clear();
FillModel(projectPath, isNewProject);
FillModel(projectPath);
if (m_filterWidget)
{
@@ -88,18 +88,9 @@ namespace O3DE::ProjectManager
});
}
void GemCatalogScreen::FillModel(const QString& projectPath, bool isNewProject)
void GemCatalogScreen::FillModel(const QString& projectPath)
{
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult;
if (isNewProject)
{
allGemInfosResult = PythonBindingsInterface::Get()->GetEngineGemInfos();
}
else
{
allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
}
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
if (allGemInfosResult.IsSuccess())
{
// Add all available gems to the model.
@@ -28,13 +28,13 @@ namespace O3DE::ProjectManager
~GemCatalogScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
void ReinitForProject(const QString& projectPath, bool isNewProject);
void ReinitForProject(const QString& projectPath);
bool EnableDisableGemsForProject(const QString& projectPath);
GemModel* GetGemModel() const { return m_gemModel; }
private:
void FillModel(const QString& projectPath, bool isNewProject);
void FillModel(const QString& projectPath);
GemListView* m_gemListView = nullptr;
GemInspector* m_gemInspector = nullptr;
@@ -104,7 +104,7 @@ namespace O3DE::ProjectManager
QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result);
m_projectInfo.m_buildFailed = true;
m_projectInfo.m_logUrl = QUrl();
m_projectInfo.m_logUrl = QUrl("file:///" + m_worker->GetLogFilePath());
emit NotifyBuildProject(m_projectInfo);
}
@@ -94,7 +94,7 @@ namespace O3DE::ProjectManager
Update();
// Gather the available gems that will be shown in the gem catalog.
m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/false);
m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path);
}
void UpdateProjectCtrl::HandleGemsButton()
@@ -235,7 +235,7 @@ namespace AZ
UpdateViewToClipMatrix();
}
void CameraComponent::SetOrthographic(bool orthographic)
void CameraComponent::SetOrthographic([[maybe_unused]] bool orthographic)
{
AZ_Assert(!orthographic, "DebugCamera does not support orthographic projection");
}
@@ -13,13 +13,7 @@
"ScopeAttachmentUsage": "DepthStencil",
"LoadStoreAction": {
"ClearValue": {
"Type": "DepthStencil",
"Value": [
0.0,
0.0,
0.0,
0.0
]
"Type": "DepthStencil"
},
"LoadActionStencil": "Clear"
}
@@ -109,7 +109,7 @@ namespace AZ
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
{
serialize->Class<ModelAssetBuilderComponent, SceneAPI::SceneCore::ExportingComponent>()
->Version(29); // (updated to separate material slot ID from default material asset)
->Version(30); // (updated to separate material slot ID from default material asset)
}
}
@@ -172,7 +172,7 @@ namespace AZ
}
m_pipelineState = m_shader->AcquirePipelineState(descriptor);
}
}
m_dirty = false;
}
return m_pipelineState;
@@ -16,37 +16,62 @@
namespace AtomToolsFramework
{
class ModernViewportCameraControllerInstance;
class ModularViewportCameraControllerInstance;
//! A function object to represent returning a camera controller priority.
using CameraControllerPriorityFn =
AZStd::function<AzFramework::ViewportControllerPriority(const AzFramework::CameraSystem& cameraSystem)>;
//! The default behavior for what priority the camera controller should respond to events at.
//! @note This can change based on the state of the camera controller/system.
AzFramework::ViewportControllerPriority DefaultCameraControllerPriority(const AzFramework::CameraSystem& cameraSystem);
//! Builder class to create and configure a ModularViewportCameraControllerInstance.
class ModularViewportCameraController
: public AzFramework::MultiViewportController<
ModernViewportCameraControllerInstance, AzFramework::ViewportControllerPriority::DispatchToAllPriorities>
ModularViewportCameraControllerInstance,
AzFramework::ViewportControllerPriority::DispatchToAllPriorities>
{
public:
friend ModularViewportCameraControllerInstance;
using CameraListBuilder = AZStd::function<void(AzFramework::Cameras&)>;
using CameraPropsBuilder = AZStd::function<void(AzFramework::CameraProps&)>;
using CameraPriorityBuilder = AZStd::function<void(CameraControllerPriorityFn&)>;
//! Sets the camera list builder callback used to populate new ModernViewportCameraControllerInstances
//! Sets the camera list builder callback used to populate new ModularViewportCameraControllerInstances.
void SetCameraListBuilderCallback(const CameraListBuilder& builder);
//! Sets the camera props builder callback used to populate new ModernViewportCameraControllerInstances
//! Sets the camera props builder callback used to populate new ModularViewportCameraControllerInstances.
void SetCameraPropsBuilderCallback(const CameraPropsBuilder& builder);
//! Sets up a camera list based on this controller's CameraListBuilderCallback
void SetupCameras(AzFramework::Cameras& cameras);
//! Sets up properties shared across all cameras
void SetupCameraProperies(AzFramework::CameraProps& cameraProps);
//! Sets the camera controller priority builder callback used to populate new ModularViewportCameraControllerInstances.
void SetCameraPriorityBuilderCallback(const CameraPriorityBuilder& builder);
private:
//! Sets up a camera list based on this controller's CameraListBuilderCallback.
void SetupCameras(AzFramework::Cameras& cameras);
//! Sets up properties shared across all cameras.
void SetupCameraProperties(AzFramework::CameraProps& cameraProps);
//! Sets up how the camera controller should decide at what priority level to respond to.
void SetupCameraControllerPriority(CameraControllerPriorityFn& cameraPriorityFn);
//! Builder to generate a list of CameraInputs to run in the ModularViewportCameraControllerInstance.
CameraListBuilder m_cameraListBuilder;
//! Builder to define custom camera properties to use for things such as rotate and translate interpolation.
CameraPropsBuilder m_cameraPropsBuilder;
//! Builder to define what priority level the camera controller should respond to events at.
CameraPriorityBuilder m_cameraControllerPriorityBuilder;
};
class ModernViewportCameraControllerInstance final
: public AzFramework::MultiViewportControllerInstanceInterface<ModularViewportCameraController>,
public ModularViewportCameraControllerRequestBus::Handler,
private AzFramework::ViewportDebugDisplayEventBus::Handler
//! A customizable camera controller that can be configured to run a varying set of CameraInput instances.
//! The controller can also be animated from its current transform to a new translation and orientation.
class ModularViewportCameraControllerInstance final
: public AzFramework::MultiViewportControllerInstanceInterface<ModularViewportCameraController>
, public ModularViewportCameraControllerRequestBus::Handler
, private AzFramework::ViewportDebugDisplayEventBus::Handler
{
public:
explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModularViewportCameraController* controller);
~ModernViewportCameraControllerInstance() override;
explicit ModularViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModularViewportCameraController* controller);
~ModularViewportCameraControllerInstance() override;
// MultiViewportControllerInstanceInterface overrides ...
bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override;
@@ -60,25 +85,35 @@ namespace AtomToolsFramework
// AzFramework::ViewportDebugDisplayEventBus overrides ...
void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
//! The current mode the camera controller is in.
enum class CameraMode
{
Control,
Animation
Control, //!< The camera is being driven by user input.
Animation //!< The camera is being animated (interpolated) from one transform to another.
};
AzFramework::Camera m_camera;
AzFramework::Camera m_targetCamera;
AzFramework::CameraSystem m_cameraSystem;
AzFramework::CameraProps m_cameraProps;
//! Encapsulates an animation (interpolation) between two transforms.
struct CameraAnimation
{
//! The transform of the camera at the start of the animation.
AZ::Transform m_transformStart = AZ::Transform::CreateIdentity();
AZ::Transform m_transformEnd = AZ::Transform::CreateIdentity(); //!< The transform of the camera at the end of the animation.
float m_time = 0.0f; //!< The interpolation amount between the start and end transforms (in the range 0.0 - 1.0).
};
AZ::Transform m_transformStart = AZ::Transform::CreateIdentity();
AZ::Transform m_transformEnd = AZ::Transform::CreateIdentity();
float m_animationT = 0.0f;
CameraMode m_cameraMode = CameraMode::Control;
AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance).
AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to.
AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs.
AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness.
CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events.
CameraAnimation m_cameraAnimation; //!< Camera animation state (used during CameraMode::Animation).
CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in.
AZStd::optional<AZ::Vector3> m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished.
//!< Will be cleared when the view changes (camera looks away).
bool m_updatingTransform = false;
//! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally).
bool m_updatingTransformInternally = false;
//! Listen for camera view changes outside of the camera controller.
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler;
};
} // namespace AtomToolsFramework
@@ -87,14 +87,12 @@ namespace AtomToolsFramework
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
auto targetName = GetBuildTargetName();
// this will put these methods into the 'azlmbr.AtomTools.general' module
auto addGeneral = [targetName](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
auto addGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
{
methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, targetName);
->Attribute(AZ::Script::Attributes::Module, "atomtools.general");
};
// The reflection here is based on patterns in CryEditPythonHandler::Reflect
addGeneral(behaviorContext->Method(
@@ -68,6 +68,11 @@ namespace AtomToolsFramework
m_cameraPropsBuilder = builder;
}
void ModularViewportCameraController::SetCameraPriorityBuilderCallback(const CameraPriorityBuilder& builder)
{
m_cameraControllerPriorityBuilder = builder;
}
void ModularViewportCameraController::SetupCameras(AzFramework::Cameras& cameras)
{
if (m_cameraListBuilder)
@@ -76,7 +81,7 @@ namespace AtomToolsFramework
}
}
void ModularViewportCameraController::SetupCameraProperies(AzFramework::CameraProps& cameraProps)
void ModularViewportCameraController::SetupCameraProperties(AzFramework::CameraProps& cameraProps)
{
if (m_cameraPropsBuilder)
{
@@ -84,18 +89,43 @@ namespace AtomToolsFramework
}
}
ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance(
void ModularViewportCameraController::SetupCameraControllerPriority(CameraControllerPriorityFn& cameraPriorityFn)
{
if (m_cameraControllerPriorityBuilder)
{
m_cameraControllerPriorityBuilder(cameraPriorityFn);
}
}
// what priority should the camera system respond to
AzFramework::ViewportControllerPriority DefaultCameraControllerPriority(const AzFramework::CameraSystem& cameraSystem)
{
// ModernViewportCameraControllerInstance receives events at all priorities, when it is in 'exclusive' mode
// or it is actively handling events (essentially when the camera system is 'active' and responding to inputs)
// it should only respond to the highest priority
if (cameraSystem.m_cameras.Exclusive() || cameraSystem.HandlingEvents())
{
return AzFramework::ViewportControllerPriority::Highest;
}
// otherwise it should only respond to normal priority events
return AzFramework::ViewportControllerPriority::Normal;
}
ModularViewportCameraControllerInstance::ModularViewportCameraControllerInstance(
const AzFramework::ViewportId viewportId, ModularViewportCameraController* controller)
: MultiViewportControllerInstanceInterface<ModularViewportCameraController>(viewportId, controller)
{
controller->SetupCameras(m_cameraSystem.m_cameras);
controller->SetupCameraProperies(m_cameraProps);
controller->SetupCameraProperties(m_cameraProps);
controller->SetupCameraControllerPriority(m_priorityFn);
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
{
auto handleCameraChange = [this, viewportContext](const AZ::Matrix4x4&)
{
if (!m_updatingTransform)
// ignore these updates if the camera is being updated internally
if (!m_updatingTransformInternally)
{
UpdateCameraFromTransform(m_targetCamera, viewportContext->GetCameraTransform());
m_camera = m_targetCamera;
@@ -111,30 +141,15 @@ namespace AtomToolsFramework
ModularViewportCameraControllerRequestBus::Handler::BusConnect(viewportId);
}
ModernViewportCameraControllerInstance::~ModernViewportCameraControllerInstance()
ModularViewportCameraControllerInstance::~ModularViewportCameraControllerInstance()
{
ModularViewportCameraControllerRequestBus::Handler::BusDisconnect();
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
}
// what priority should the camera system respond to
static AzFramework::ViewportControllerPriority GetPriority(const AzFramework::CameraSystem& cameraSystem)
bool ModularViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
{
// ModernViewportCameraControllerInstance receives events at all priorities, when it is in 'exclusive' mode
// or it is actively handling events (essentially when the camera system is 'active' and responding to inputs)
// it should only respond to the highest priority
if (cameraSystem.m_cameras.Exclusive() || cameraSystem.HandlingEvents())
{
return AzFramework::ViewportControllerPriority::Highest;
}
// otherwise it should only respond to normal priority events
return AzFramework::ViewportControllerPriority::Normal;
}
bool ModernViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
{
if (event.m_priority == GetPriority(m_cameraSystem))
if (event.m_priority == m_priorityFn(m_cameraSystem))
{
return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel));
}
@@ -142,7 +157,7 @@ namespace AtomToolsFramework
return false;
}
void ModernViewportCameraControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
void ModularViewportCameraControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
{
// only update for a single priority (normal is the default)
if (event.m_priority != AzFramework::ViewportControllerPriority::Normal)
@@ -152,7 +167,7 @@ namespace AtomToolsFramework
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
{
m_updatingTransform = true;
m_updatingTransformInternally = true;
if (m_cameraMode == CameraMode::Control)
{
@@ -180,10 +195,12 @@ namespace AtomToolsFramework
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
};
const float transitionT = smootherStepFn(m_animationT);
const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation;
const float transitionTime = smootherStepFn(animationTime);
const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation(
m_transformStart.GetRotation().Slerp(m_transformEnd.GetRotation(), transitionT),
m_transformStart.GetTranslation().Lerp(m_transformEnd.GetTranslation(), transitionT));
transformStart.GetRotation().Slerp(transformEnd.GetRotation(), transitionTime),
transformStart.GetTranslation().Lerp(transformEnd.GetTranslation(), transitionTime));
const AZ::Vector3 eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(current));
m_camera.m_pitch = eulerAngles.GetX();
@@ -191,21 +208,21 @@ namespace AtomToolsFramework
m_camera.m_lookAt = current.GetTranslation();
m_targetCamera = m_camera;
if (m_animationT >= 1.0f)
if (animationTime >= 1.0f)
{
m_cameraMode = CameraMode::Control;
}
m_animationT = AZ::GetClamp(m_animationT + event.m_deltaTime.count(), 0.0f, 1.0f);
m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f);
viewportContext->SetCameraTransform(current);
}
m_updatingTransform = false;
m_updatingTransformInternally = false;
}
}
void ModernViewportCameraControllerInstance::DisplayViewport(
void ModularViewportCameraControllerInstance::DisplayViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
{
if (const float alpha = AZStd::min(-m_camera.m_lookDist / 5.0f, 1.0f); alpha > AZ::Constants::FloatEpsilon)
@@ -216,16 +233,14 @@ namespace AtomToolsFramework
}
}
void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance)
void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance)
{
m_animationT = 0.0f;
m_cameraMode = CameraMode::Animation;
m_transformStart = m_camera.Transform();
m_transformEnd = worldFromLocal;
m_lookAtAfterInterpolation = m_transformEnd.GetTranslation() + m_transformEnd.GetBasisY() * lookAtDistance;
m_cameraAnimation = CameraAnimation{ m_camera.Transform(), worldFromLocal, 0.0f };
m_lookAtAfterInterpolation = worldFromLocal.GetTranslation() + worldFromLocal.GetBasisY() * lookAtDistance;
}
AZStd::optional<AZ::Vector3> ModernViewportCameraControllerInstance::LookAtAfterInterpolation() const
AZStd::optional<AZ::Vector3> ModularViewportCameraControllerInstance::LookAtAfterInterpolation() const
{
return m_lookAtAfterInterpolation;
}
@@ -9,7 +9,6 @@
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Asset/AssetSystemComponent.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/Network/AssetProcessorConnection.h>
#include <AzFramework/StringFunc/StringFunc.h>
@@ -28,11 +27,11 @@
#include <AtomToolsFramework/Util/Util.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/Document/MaterialDocumentModule.h>
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
#include <Atom/Viewport/MaterialViewportModule.h>
#include <Atom/Window/MaterialEditorWindowFactoryRequestBus.h>
#include <Atom/Window/MaterialEditorWindowModule.h>
@@ -73,15 +72,14 @@ namespace MaterialEditor
{
QApplication::setApplicationName("O3DE Material Editor");
// The settings registry has been created at this point, so add the CMake target
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
*AZ::SettingsRegistry::Get(), GetBuildTargetName());
}
MaterialEditorApplication::~MaterialEditorApplication()
{
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
MaterialEditorWindowNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
}
void MaterialEditorApplication::CreateStaticModules(AZStd::vector<AZ::Module*>& outModules)
@@ -122,7 +120,7 @@ namespace MaterialEditor
&MaterialEditor::MaterialEditorWindowRequestBus::Handler::ActivateWindow);
}
// Process command line options for opening one or more material documents on startup
// Process command line options for opening one or more documents on startup
size_t openDocumentCount = commandLine.GetNumMiscValues();
for (size_t openDocumentIndex = 0; openDocumentIndex < openDocumentCount; ++openDocumentIndex)
{
@@ -114,11 +114,11 @@ def SetCameraPitch(pitch):
azlmbr.render.ArcBallControllerRequestBus(azlmbr.bus.Broadcast, 'SetPitch', pitch)
def IdleFrames(numFrames):
azlmbr.materialeditor.general.idle_wait_frames(numFrames)
azlmbr.atomtools.general.idle_wait_frames(numFrames)
def CaptureScreenshot(screenshotOutputPath):
print("Capturing screenshot to " + screenshotOutputPath + " ...")
return ScreenshotHelper(azlmbr.materialeditor.general.idle_wait_frames).capture_screenshot_blocking(screenshotOutputPath)
return ScreenshotHelper(azlmbr.atomtools.general.idle_wait_frames).capture_screenshot_blocking(screenshotOutputPath)
def ResizeViewport(width, height):
# This locks the size of the render target to the desired resolution
@@ -25,26 +25,27 @@
#include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <AtomToolsFramework/Util/Util.h>
#include <ShaderManagementConsoleApplication.h>
#include <ShaderManagementConsole_Traits_Platform.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/Document/ShaderManagementConsoleDocumentModule.h>
#include <Atom/Document/ShaderManagementConsoleDocumentSystemRequestBus.h>
#include <Atom/Window/ShaderManagementConsoleWindowModule.h>
#include <Atom/Window/ShaderManagementConsoleWindowRequestBus.h>
#include <ShaderManagementConsoleApplication.h>
#include <ShaderManagementConsole_Traits_Platform.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QFileInfo>
#include <QMessageBox>
#include <QObject>
AZ_POP_DISABLE_WARNING
namespace ShaderManagementConsole
{
//! This function returns the build system target name of "ShaderManagementConsole
AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const
{
#if !defined(LY_CMAKE_TARGET)
@@ -74,6 +75,11 @@ namespace ShaderManagementConsole
*AZ::SettingsRegistry::Get(), GetBuildTargetName());
}
ShaderManagementConsoleApplication::~ShaderManagementConsoleApplication()
{
ShaderManagementConsoleWindowNotificationBus::Handler::BusDisconnect();
}
void ShaderManagementConsoleApplication::CreateStaticModules(AZStd::vector<AZ::Module*>& outModules)
{
Base::CreateStaticModules(outModules);
@@ -84,8 +90,6 @@ namespace ShaderManagementConsole
void ShaderManagementConsoleApplication::OnShaderManagementConsoleWindowClosing()
{
ExitMainLoop();
ShaderManagementConsoleWindowNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
}
void ShaderManagementConsoleApplication::Destroy()
@@ -104,27 +108,19 @@ namespace ShaderManagementConsole
return AZStd::vector<AZStd::string>({ "passes/", "config/" });
}
void ShaderManagementConsoleApplication::ProcessCommandLine()
void ShaderManagementConsoleApplication::ProcessCommandLine(const AZ::CommandLine& commandLine)
{
// Process command line options for running one or more python scripts on startup
const AZStd::string runPythonScriptSwitchName = "runpython";
size_t runPythonScriptCount = m_commandLine.GetNumSwitchValues(runPythonScriptSwitchName);
for (size_t runPythonScriptIndex = 0; runPythonScriptIndex < runPythonScriptCount; ++runPythonScriptIndex)
{
const AZStd::string runPythonScriptPath = m_commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex);
AZStd::vector<AZStd::string_view> runPythonArgs;
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, runPythonScriptPath, runPythonArgs);
}
// Process command line options for opening one or more documents on startup
size_t openDocumentCount = m_commandLine.GetNumMiscValues();
size_t openDocumentCount = commandLine.GetNumMiscValues();
for (size_t openDocumentIndex = 0; openDocumentIndex < openDocumentCount; ++openDocumentIndex)
{
const AZStd::string openDocumentPath = m_commandLine.GetMiscValue(openDocumentIndex);
ShaderManagementConsoleDocumentSystemRequestBus::Broadcast(
&ShaderManagementConsoleDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath);
const AZStd::string openDocumentPath = commandLine.GetMiscValue(openDocumentIndex);
AZ_Printf(GetBuildTargetName().c_str(), "Opening document: %s", openDocumentPath.c_str());
ShaderManagementConsoleDocumentSystemRequestBus::Broadcast(&ShaderManagementConsoleDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath);
}
Base::ProcessCommandLine(commandLine);
}
void ShaderManagementConsoleApplication::StartInternal()
@@ -136,4 +132,12 @@ namespace ShaderManagementConsole
ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Broadcast(
&ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Handler::CreateShaderManagementConsoleWindow);
}
void ShaderManagementConsoleApplication::Stop()
{
ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Broadcast(
&ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Handler::DestroyShaderManagementConsoleWindow);
Base::Stop();
}
} // namespace ShaderManagementConsole
@@ -26,12 +26,13 @@ namespace ShaderManagementConsole
using Base = AtomToolsFramework::AtomToolsApplication;
ShaderManagementConsoleApplication(int* argc, char*** argv);
virtual ~ShaderManagementConsoleApplication() = default;
virtual ~ShaderManagementConsoleApplication();
//////////////////////////////////////////////////////////////////////////
// AzFramework::Application
void CreateStaticModules(AZStd::vector<AZ::Module*>& outModules) override;
const char* GetCurrentConfigurationName() const override;
void Stop() override;
private:
//////////////////////////////////////////////////////////////////////////
@@ -44,9 +45,9 @@ namespace ShaderManagementConsole
void Destroy() override;
//////////////////////////////////////////////////////////////////////////
void ProcessCommandLine();
void ProcessCommandLine(const AZ::CommandLine& commandLine) override;
void StartInternal() override;
AZStd::string GetBuildTargetName() const override;
AZStd::vector<AZStd::string> GetCriticalAssetFilters() const override;
};
};
} // namespace ShaderManagementConsole
@@ -520,13 +520,14 @@ namespace AZ
{
AZStd::size_t sizeBeforeRemove = savedRegions.size();
auto firstRegionToKeep = AZStd::lower_bound(
savedRegions.begin(), savedRegions.end(), deleteBeforeTick,
[](const TimeRegion& region, AZStd::sys_time_t target)
// Use erase_if over plain upper_bound + erase to avoid repeated shifts. erase requires a shift of all elements to the right
// for each element that is erased, while erase_if squashes all removes into a single shift which significantly improves perf.
AZStd::erase_if(
savedRegions,
[deleteBeforeTick](const TimeRegion& region)
{
return region.m_startTick < target;
return region.m_startTick < deleteBeforeTick;
});
savedRegions.erase(savedRegions.begin(), firstRegionToKeep);
m_savedRegionCount -= sizeBeforeRemove - savedRegions.size();
}
@@ -10,9 +10,6 @@
#include <Atom/Features/PostProcessing/PostProcessUtil.azsli>
// Indicates whether to use pre-multiplied alpha
option bool o_preMultiplyAlpha;
// If true pixels with an alpha value of less than 0.5 are clipped
option bool o_alphaTest;
@@ -86,9 +83,9 @@ struct PSOutput
float4 m_color : SV_Target0;
};
float4 SampleTriangleTexture(int texIndex, float2 uv)
float4 SampleTriangleTexture(uint texIndex, float2 uv)
{
if ((InstanceSrg::m_isClamp & (1 << texIndex)) != 0)
if ((InstanceSrg::m_isClamp & (1U << texIndex)) != 0)
{
return InstanceSrg::m_texture[texIndex].Sample(InstanceSrg::m_clampSampler, uv);
}
@@ -120,14 +117,6 @@ PSOutput MainPS(VSOutput IN)
resColor.xyz = LinearToSRGB(resColor.xyz);
}
// Check for flag to premultiply alpha
if (o_preMultiplyAlpha)
{
// premultiply the color by the alpha. This would not be required if we had full access to the separate alpha blend mode
float preMult = resColor.w;
resColor.xyz *= preMult;
}
// If the o_modulate option is not None it means that the verts have two texture indicies. The second texture is used to
// mask the first. This is used for gradient masks.
if (o_modulate == Modulate::Alpha)
@@ -4,7 +4,6 @@
{
"StableId": 1,
"Options": {
"o_preMultiplyAlpha": "false",
"o_alphaTest": "false",
"o_srgbWrite": "true",
"o_modulate": "Modulate::None"
@@ -13,11 +12,26 @@
{
"StableId": 2,
"Options": {
"o_preMultiplyAlpha": "false",
"o_alphaTest": "true",
"o_srgbWrite": "true",
"o_alphaTest": "false",
"o_srgbWrite": "false",
"o_modulate": "Modulate::None"
}
},
{
"StableId": 3,
"Options": {
"o_alphaTest": "true",
"o_srgbWrite": "false",
"o_modulate": "Modulate::None"
}
},
{
"StableId": 4,
"Options": {
"o_alphaTest": "false",
"o_srgbWrite": "false",
"o_modulate": "Modulate::Alpha"
}
}
]
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Asset/BlastChunksAsset.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace Blast
{
void BlastChunksAsset::SetModelAssetIds(const AZStd::vector<AZ::Data::AssetId>& modelAssetIds)
{
m_modelAssetIds = modelAssetIds;
}
const AZStd::vector<AZ::Data::AssetId>& BlastChunksAsset::GetModelAssetIds() const
{
return m_modelAssetIds;
}
void BlastChunksAsset::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<BlastChunksAsset, AZ::Data::AssetData>()
->Version(1)
->Field("modelAssetIds", &BlastChunksAsset::m_modelAssetIds);
}
}
} // namespace Blast
@@ -0,0 +1,32 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
namespace Blast
{
//! The product asset file from a .blast_chunks file product asset file
class BlastChunksAsset final
: public AZ::Data::AssetData
{
public:
AZ_RTTI(BlastChunksAsset, "{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}", AZ::Data::AssetData);
AZ_CLASS_ALLOCATOR(BlastChunksAsset, AZ::SystemAllocator, 0);
BlastChunksAsset() = default;
~BlastChunksAsset() override = default;
void SetModelAssetIds(const AZStd::vector<AZ::Data::AssetId>& modelAssetIds);
const AZStd::vector<AZ::Data::AssetId>& GetModelAssetIds() const;
static void Reflect(AZ::ReflectContext* context);
private:
AZStd::vector<AZ::Data::AssetId> m_modelAssetIds;
};
} // namespace Blast
@@ -1,62 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Asset/BlastSliceAsset.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace Blast
{
void BlastSliceAsset::SetMeshIdList(const AZStd::vector<AZ::Data::AssetId>& meshAssetIdList)
{
m_meshAssetIdList = meshAssetIdList;
}
const AZStd::vector<AZ::Data::AssetId>& BlastSliceAsset::GetMeshIdList() const
{
return m_meshAssetIdList;
}
void BlastSliceAsset::SetMaterialId(const AZ::Data::AssetId& materialAssetId)
{
m_materialAssetId = materialAssetId;
}
const AZ::Data::AssetId& BlastSliceAsset::GetMaterialId() const
{
return m_materialAssetId;
}
void BlastSliceAsset::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<BlastSliceAsset, AZ::Data::AssetData>()
->Version(1)
->Field("meshAssetIdList", &BlastSliceAsset::m_meshAssetIdList)
->Field("materialAssetId", &BlastSliceAsset::m_materialAssetId);
}
if (AZ::BehaviorContext* behavior = azrtti_cast<AZ::BehaviorContext*>(context))
{
behavior->Class<BlastSliceAsset>("BlastSliceAsset")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "blast")
->Method("SetMeshIdList", &BlastSliceAsset::SetMeshIdList)
->Method("GetMeshIdList", &BlastSliceAsset::GetMeshIdList)
->Method("SetMaterialId", &BlastSliceAsset::SetMaterialId)
->Method("GetMaterialId", &BlastSliceAsset::GetMaterialId)
->Method(
"GetAssetTypeId",
[](BlastSliceAsset*)
{
return azrtti_typeid<BlastSliceAsset>();
});
}
}
} // namespace Blast
@@ -1,36 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
namespace Blast
{
//! The product asset file from a .blast_slice file product asset file
class BlastSliceAsset final : public AZ::Data::AssetData
{
public:
AZ_RTTI(BlastSliceAsset, "{D04AAF07-EB12-4E50-8964-114A9B9C1FD1}", AZ::Data::AssetData);
AZ_CLASS_ALLOCATOR(BlastSliceAsset, AZ::SystemAllocator, 0);
BlastSliceAsset() = default;
~BlastSliceAsset() override = default;
void SetMeshIdList(const AZStd::vector<AZ::Data::AssetId>& meshAssetIdList);
const AZStd::vector<AZ::Data::AssetId>& GetMeshIdList() const;
void SetMaterialId(const AZ::Data::AssetId& materialAssetId);
const AZ::Data::AssetId& GetMaterialId() const;
static void Reflect(AZ::ReflectContext* context);
private:
AZStd::vector<AZ::Data::AssetId> m_meshAssetIdList;
AZ::Data::AssetId m_materialAssetId;
};
} // namespace Blast
+1 -3
View File
@@ -16,7 +16,6 @@
#ifdef BLAST_EDITOR
#include <Editor/EditorBlastFamilyComponent.h>
#include <Editor/EditorBlastMeshDataComponent.h>
#include <Editor/EditorBlastSliceAssetHandler.h>
#include <Editor/EditorSystemComponent.h>
#endif
@@ -40,8 +39,7 @@ namespace Blast
#ifdef BLAST_EDITOR
EditorSystemComponent::CreateDescriptor(),
EditorBlastFamilyComponent::CreateDescriptor(),
EditorBlastMeshDataComponent::CreateDescriptor(),
BlastSliceAssetStorageComponent::CreateDescriptor(),
EditorBlastMeshDataComponent::CreateDescriptor()
#endif
});
}
@@ -0,0 +1,144 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/EditorBlastMeshDataComponent.h>
#include <Editor/EditorBlastChunksAssetHandler.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
namespace Blast
{
//
// EditorBlastChunksAssetHandler
//
EditorBlastChunksAssetHandler::~EditorBlastChunksAssetHandler()
{
Unregister();
}
AZ::Data::AssetPtr EditorBlastChunksAssetHandler::CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type)
{
if (type != GetAssetType())
{
AZ_Error("Blast", type == GetAssetType(), "Invalid asset type! We only handle 'BlastChunksAsset'");
return {};
}
if (!CanHandleAsset(id))
{
return nullptr;
}
return aznew BlastChunksAsset;
}
AZ::Data::AssetHandler::LoadResult EditorBlastChunksAssetHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
[[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
BlastChunksAsset* blastChunksAsset = asset.GetAs<BlastChunksAsset>();
AZ_Error("blast", blastChunksAsset,
"This should be a BlastChunksAsset type, as this is the only type we process!");
if (!blastChunksAsset)
{
return LoadResult::Error;
}
// get all products from the source scene asset
bool found = false;
AZStd::vector<AZ::Data::AssetInfo> productsAssetInfo;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
found,
&AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID,
asset.Get()->GetId().m_guid,
productsAssetInfo);
if (!found)
{
AZ_Error("blast",
found,
"Could not find asset models produced by source asset ID %s, verify the output product model assets.",
asset.Get()->GetId().m_guid.ToString<AZStd::string>().c_str());
return LoadResult::Error;
}
// find all model assets
AZStd::vector<AZ::Data::AssetId> modelAssetIdList;
for (const AZ::Data::AssetInfo& assetInfo : productsAssetInfo)
{
if (azrtti_typeid<AZ::RPI::ModelAsset>() == assetInfo.m_assetType)
{
modelAssetIdList.push_back(assetInfo.m_assetId);
}
}
blastChunksAsset->SetModelAssetIds(modelAssetIdList);
return LoadResult::LoadComplete;
}
void EditorBlastChunksAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr)
{
delete ptr;
}
void EditorBlastChunksAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes)
{
assetTypes.push_back(azrtti_typeid<BlastChunksAsset>());
}
void EditorBlastChunksAssetHandler::Register()
{
AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!");
AZ::Data::AssetManager::Instance().RegisterHandler(this, azrtti_typeid<BlastChunksAsset>());
AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid<BlastChunksAsset>());
}
void EditorBlastChunksAssetHandler::Unregister()
{
AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid<BlastChunksAsset>());
if (AZ::Data::AssetManager::IsReady())
{
AZ::Data::AssetManager::Instance().UnregisterHandler(this);
}
}
AZ::Data::AssetType EditorBlastChunksAssetHandler::GetAssetType() const
{
return azrtti_typeid<BlastChunksAsset>();
}
const char* EditorBlastChunksAssetHandler::GetAssetTypeDisplayName() const
{
return "Blast Chunks Asset";
}
const char* EditorBlastChunksAssetHandler::GetGroup() const
{
return "Blast";
}
const char* EditorBlastChunksAssetHandler::GetBrowserIcon() const
{
return "Icons/Components/Box.png";
}
void EditorBlastChunksAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
{
extensions.push_back("blast_chunks");
}
} // namespace Blast
@@ -0,0 +1,46 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Asset/BlastChunksAsset.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
namespace Blast
{
class EditorBlastChunksAssetHandler final
: public AZ::Data::AssetHandler
, public AZ::AssetTypeInfoBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EditorBlastChunksAssetHandler, AZ::SystemAllocator, 0);
~EditorBlastChunksAssetHandler() override;
// AZ::Data::AssetHandler
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override;
LoadResult LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
void DestroyAsset(AZ::Data::AssetPtr ptr) override;
void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override;
// AZ::AssetTypeInfoBus::Handler
AZ::Data::AssetType GetAssetType() const override;
const char* GetAssetTypeDisplayName() const override;
const char* GetGroup() const override;
const char* GetBrowserIcon() const override;
void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
void Register();
void Unregister();
};
} // namespace Blast
@@ -45,10 +45,10 @@ namespace Blast
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<EditorBlastMeshDataComponent, AzToolsFramework::Components::EditorComponentBase>()
->Version(4)
->Version(5)
->Field("Show Mesh Assets", &EditorBlastMeshDataComponent::m_showMeshAssets)
->Field("Mesh Assets", &EditorBlastMeshDataComponent::m_meshAssets)
->Field("Blast Slice", &EditorBlastMeshDataComponent::m_blastSliceAsset);
->Field("Blast Chunks", &EditorBlastMeshDataComponent::m_blastChunksAsset);
if (AZ::EditContext* ec = serialize->GetEditContext())
{
@@ -77,9 +77,9 @@ namespace Blast
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorBlastMeshDataComponent::OnMeshAssetsChanged)
->DataElement(
AZ::Edit::UIHandlers::Default, &EditorBlastMeshDataComponent::m_blastSliceAsset, "Blast Slice",
"Slice override to fill out meshes and material")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorBlastMeshDataComponent::OnSliceAssetChanged);
AZ::Edit::UIHandlers::Default, &EditorBlastMeshDataComponent::m_blastChunksAsset, "Blast Chunks",
"Manifest override to fill out meshes and material")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorBlastMeshDataComponent::OnBlastChunksAssetChanged);
}
}
}
@@ -107,23 +107,27 @@ namespace Blast
UnregisterModel();
}
void EditorBlastMeshDataComponent::OnSliceAssetChanged()
void EditorBlastMeshDataComponent::OnBlastChunksAssetChanged()
{
if (!m_blastSliceAsset.GetId().IsValid())
if (!m_blastChunksAsset.GetId().IsValid())
{
return;
}
using namespace AZ::Data;
const AssetId blastAssetId = m_blastChunksAsset.GetId();
m_blastChunksAsset = AssetManager::Instance().GetAsset<BlastChunksAsset>(blastAssetId, AssetLoadBehavior::QueueLoad);
m_blastChunksAsset.BlockUntilLoadComplete();
const AssetId blastAssetId = m_blastSliceAsset.GetId();
m_blastSliceAsset =
AssetManager::Instance().GetAsset<BlastSliceAsset>(blastAssetId, AssetLoadBehavior::QueueLoad);
m_blastSliceAsset.BlockUntilLoadComplete();
if (!m_blastChunksAsset.Get() || m_blastChunksAsset.Get()->GetModelAssetIds().empty())
{
AZ_Warning("blast", false, "Blast Chunk Asset does not contain any models.")
return;
}
// load up the new mesh list
m_meshAssets.clear();
for (const auto& meshId : m_blastSliceAsset.Get()->GetMeshIdList())
for (const auto& meshId : m_blastChunksAsset.Get()->GetModelAssetIds())
{
auto meshAsset = AssetManager::Instance().GetAsset<AZ::RPI::ModelAsset>(meshId, AssetLoadBehavior::QueueLoad);
if (meshAsset)
@@ -135,8 +139,8 @@ namespace Blast
UnregisterModel();
RegisterModel();
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
using namespace AzToolsFramework;
ToolsApplicationEvents::Bus::Broadcast(&ToolsApplicationEvents::InvalidatePropertyDisplay, Refresh_EntireTree);
}
void EditorBlastMeshDataComponent::OnMeshAssetsChanged()
@@ -205,9 +209,9 @@ namespace Blast
gameEntity->CreateComponent<BlastMeshDataComponent>(m_meshAssets);
}
const AZ::Data::Asset<BlastSliceAsset>& EditorBlastMeshDataComponent::GetBlastSliceAsset() const
const AZ::Data::Asset<BlastChunksAsset>& EditorBlastMeshDataComponent::GetBlastChunksAsset() const
{
return m_blastSliceAsset;
return m_blastChunksAsset;
}
const AZStd::vector<AZ::Data::Asset<AZ::RPI::ModelAsset>>& EditorBlastMeshDataComponent::GetMeshAssets() const
@@ -7,7 +7,7 @@
*/
#pragma once
#include <Asset/BlastSliceAsset.h>
#include <Asset/BlastChunksAsset.h>
#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Model/Model.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
@@ -43,14 +43,14 @@ namespace Blast
// EditorComponentBase
void BuildGameEntity(AZ::Entity* gameEntity) override;
const AZ::Data::Asset<BlastSliceAsset>& GetBlastSliceAsset() const;
const AZ::Data::Asset<BlastChunksAsset>& GetBlastChunksAsset() const;
const AZStd::vector<AZ::Data::Asset<AZ::RPI::ModelAsset>>& GetMeshAssets() const;
void OnMaterialsUpdated(const AZ::Render::MaterialAssignmentMap& materials) override;
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
private:
void OnSliceAssetChanged();
void OnBlastChunksAssetChanged();
void OnMeshAssetsChanged();
AZ::Crc32 GetMeshAssetsVisibility() const;
void OnMeshAssetsVisibilityChanged();
@@ -62,7 +62,7 @@ namespace Blast
//////////////////////////////////////////////////////////////////////////
// Reflected data
bool m_showMeshAssets = false;
AZ::Data::Asset<BlastSliceAsset> m_blastSliceAsset;
AZ::Data::Asset<BlastChunksAsset> m_blastChunksAsset;
AZStd::vector<AZ::Data::Asset<AZ::RPI::ModelAsset>> m_meshAssets;
//////////////////////////////////////////////////////////////////////////
@@ -1,345 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/EditorBlastMeshDataComponent.h>
#include <Editor/EditorBlastSliceAssetHandler.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/RuleContainer.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneData/Groups/MeshGroup.h>
#include <SceneAPI/SceneData/ManifestBase/SceneNodeSelectionList.h>
#include <SceneAPI/SceneData/Rules/MaterialRule.h>
#include <GFxFramework/MaterialIO/Material.h>
namespace Blast
{
// BlastSliceAssetStorageComponent
void BlastSliceAssetStorageComponent::Reflect(AZ::ReflectContext* context)
{
using namespace AZ::Edit;
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<BlastSliceAssetStorageComponent, AzToolsFramework::Components::EditorComponentBase>()
->Version(2)
->Field("Mesh Data", &BlastSliceAssetStorageComponent::m_meshAssetIdList)
->Field("Mesh Path List", &BlastSliceAssetStorageComponent::m_meshAssetPathList);
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<BlastSliceAssetStorageComponent>(
"Blast Slice Storage Component", "Used process blast slice data")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Physics")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Box.png")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Box.png")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AddableByUser, false)
->DataElement(
AZ::Edit::UIHandlers::Default, &BlastSliceAssetStorageComponent::m_meshAssetIdList, "Mesh Data",
"Slice data to fill out the mesh list")
->DataElement(
AZ::Edit::UIHandlers::Default, &BlastSliceAssetStorageComponent::m_meshAssetPathList,
"Mesh Paths", "The mesh path list");
}
}
if (AZ::BehaviorContext* behavior = azrtti_cast<AZ::BehaviorContext*>(context))
{
behavior->Class<BlastSliceAssetStorageComponent>("BlastSliceAssetStorageComponent")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "blast")
->Method("GenerateAssetInfo", &BlastSliceAssetStorageComponent::GenerateAssetInfo)
->Method("WriteMaterialFile", &BlastSliceAssetStorageComponent::WriteMaterialFile);
}
}
bool BlastSliceAssetStorageComponent::GenerateAssetInfo(
const AZStd::vector<AZStd::string>& chunkNames, AZStd::string_view blastFilename,
AZStd::string_view assetinfoFilename)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(
serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (serializeContext == nullptr)
{
return false;
}
using namespace AZ::SceneAPI::Containers;
using namespace AZ::SceneAPI::SceneData;
AZStd::string filename;
AZ::StringFunc::Path::Split(blastFilename.data(), nullptr, nullptr, &filename, nullptr);
AZStd::any sceneManifestPointer(serializeContext->CreateAny(azrtti_typeid<SceneManifest>()));
SceneManifest* sceneManifest = AZStd::any_cast<SceneManifest>(&sceneManifestPointer);
AZStd::vector<AZStd::any> meshGroupData;
meshGroupData.reserve(chunkNames.size());
AZStd::vector<AZStd::any> materialRuleData;
materialRuleData.reserve(chunkNames.size());
for (const AZStd::string& chunkName : chunkNames)
{
meshGroupData.emplace_back(serializeContext->CreateAny(azrtti_typeid<MeshGroup>()));
AZStd::any& meshGroupPointer = meshGroupData.back();
MeshGroup* meshGroup = AZStd::any_cast<MeshGroup>(&meshGroupPointer);
// make selection list
meshGroup->GetSceneNodeSelectionList().RemoveSelectedNode("RootNode");
for (const AZStd::string& node : chunkNames)
{
meshGroup->GetSceneNodeSelectionList().RemoveSelectedNode(
AZStd::string::format("RootNode.%s", node.c_str()));
}
meshGroup->GetSceneNodeSelectionList().AddSelectedNode(
AZStd::string::format("RootNode.%s", chunkName.c_str()));
// create a default material for the mesh group
materialRuleData.emplace_back(serializeContext->CreateAny(azrtti_typeid<MaterialRule>()));
AZStd::any& materialRulePointer = materialRuleData.back();
MaterialRule* materialRule = AZStd::any_cast<MaterialRule>(&materialRulePointer);
// override the deleter since the AZStd::any will clean up later on
AZStd::shared_ptr<MaterialRule> materialRuleEntry = AZStd::shared_ptr<MaterialRule>(
materialRule,
[](auto)
{
});
meshGroup->GetRuleContainer().AddRule(materialRuleEntry);
// construct the asset name for the chunk's mesh group
AZStd::string meshGroupName(filename);
meshGroupName.append("-");
meshGroupName.append(chunkName);
// TODO: Uncomment lines below as part of SPEC-3542
// meshGroup->OverrideId(AZ::Uuid::CreateName(meshGroupName.c_str()));
// meshGroup->SetName(AZStd::move(meshGroupName));
// override the deleter since the AZStd::any will clean up later on
AZStd::shared_ptr<MeshGroup> meshGroupEntry = AZStd::shared_ptr<MeshGroup>(
meshGroup,
[](auto)
{
});
sceneManifest->AddEntry(AZStd::move(meshGroupEntry));
}
return sceneManifest->SaveToFile(assetinfoFilename.data());
}
bool BlastSliceAssetStorageComponent::WriteMaterialFile(
AZStd::string_view materialGroupName, const AZStd::vector<AZStd::string>& materialNames,
AZStd::string_view materialFilename)
{
AZ::GFxFramework::MaterialGroup group;
for (const auto& texture : materialNames)
{
auto mat = AZStd::make_shared<AZ::GFxFramework::Material>();
mat->SetName(texture);
mat->SetTexture(AZ::GFxFramework::TextureMapType::Diffuse, "EngineAssets/Textures/white.dds");
group.AddMaterial(mat);
}
group.SetMtlName(materialGroupName);
return group.WriteMtlFile(materialFilename.data());
}
//
// EditorBlastSliceAssetHandler
//
EditorBlastSliceAssetHandler::~EditorBlastSliceAssetHandler()
{
Unregister();
}
AZ::Data::AssetPtr EditorBlastSliceAssetHandler::CreateAsset(
const AZ::Data::AssetId& id, const AZ::Data::AssetType& type)
{
if (type != GetAssetType())
{
AZ_Error("Blast", type == GetAssetType(), "Invalid asset type! We only handle 'BlastAsset'");
return {};
}
if (!CanHandleAsset(id))
{
return nullptr;
}
return aznew BlastSliceAsset;
}
AZ::Data::AssetHandler::LoadResult EditorBlastSliceAssetHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset, AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
BlastSliceAsset* blastSliceAssetData = asset.GetAs<BlastSliceAsset>();
AZ_Error(
"blast", blastSliceAssetData,
"This should be a BlastSliceAsset type, as this is the only type we process!");
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(
serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (blastSliceAssetData && serializeContext)
{
AZ::ObjectStream::FilterDescriptor filter(assetLoadFilterCB);
AZStd::unique_ptr<AZ::Entity> baseEntity(
AZ::Utils::LoadObjectFromStream<AZ::Entity>(*stream, serializeContext, filter));
AZ_Error("Blast", baseEntity, "Could not load slice root entity {asset id}");
if (!baseEntity)
{
return LoadResult::Error;
}
auto&& sliceComponent = baseEntity->FindComponent<AZ::SliceComponent>();
AZ_Error("Blast", sliceComponent, "blast_slice entity missing SliceComponent!");
if (sliceComponent == nullptr)
{
return LoadResult::Error;
}
AZStd::vector<AZ::Entity*> enityList;
sliceComponent->GetEntities(enityList);
for (auto&& entity : enityList)
{
// the base element type to store Blast mesh data is the BlastSliceAssetStorageComponent
auto&& blastSliceAssetStorage = entity->FindComponent<BlastSliceAssetStorageComponent>();
if (blastSliceAssetStorage)
{
if (blastSliceAssetStorage->GetMeshData().empty() == false)
{
blastSliceAssetData->SetMeshIdList(blastSliceAssetStorage->GetMeshData());
return LoadResult::LoadComplete;
}
else if (blastSliceAssetStorage->GetMeshPathList().empty() == false)
{
AZStd::vector<AZ::Data::AssetId> meshAssetIdList;
meshAssetIdList.reserve(blastSliceAssetStorage->GetMeshPathList().size());
for (auto&& assetPath : blastSliceAssetStorage->GetMeshPathList())
{
AZ::Data::AssetId meshAssetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
meshAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
assetPath.c_str(), AZ::Data::s_invalidAssetType, false);
if (meshAssetId.IsValid())
{
meshAssetIdList.emplace_back(meshAssetId);
}
}
blastSliceAssetData->SetMeshIdList(meshAssetIdList);
return LoadResult::LoadComplete;
}
}
// back up logic to load blast data for the EditorBlastMeshDataComponent
auto&& meshDataComponent = entity->FindComponent<EditorBlastMeshDataComponent>();
if (meshDataComponent)
{
auto&& innerBlastSliceAsset = meshDataComponent->GetBlastSliceAsset();
if (innerBlastSliceAsset.IsReady())
{
blastSliceAssetData->SetMeshIdList(innerBlastSliceAsset.Get()->GetMeshIdList());
blastSliceAssetData->SetMaterialId(innerBlastSliceAsset.Get()->GetMaterialId());
return LoadResult::LoadComplete;
}
else
{
auto&& meshDataList = meshDataComponent->GetMeshAssets();
AZStd::vector<AZ::Data::AssetId> meshAssetIdList;
meshAssetIdList.reserve(meshDataList.size());
for (auto&& meshData : meshDataList)
{
AZ::RPI::ModelAsset* meshAsset = meshData.Get();
if (meshAsset)
{
meshAssetIdList.push_back(meshAsset->GetId());
}
}
blastSliceAssetData->SetMeshIdList(meshAssetIdList);
return LoadResult::LoadComplete;
}
}
}
AZ_Error(
"Blast", false, "blast_slice assetId:%s missing EditorBlastMeshDataComponent!",
asset->GetId().ToString<AZStd::string>().c_str());
}
return LoadResult::Error;
}
void EditorBlastSliceAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr)
{
delete ptr;
}
void EditorBlastSliceAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes)
{
assetTypes.push_back(azrtti_typeid<BlastSliceAsset>());
}
void EditorBlastSliceAssetHandler::Register()
{
AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!");
AZ::Data::AssetManager::Instance().RegisterHandler(this, azrtti_typeid<BlastSliceAsset>());
AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid<BlastSliceAsset>());
}
void EditorBlastSliceAssetHandler::Unregister()
{
AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid<BlastSliceAsset>());
if (AZ::Data::AssetManager::IsReady())
{
AZ::Data::AssetManager::Instance().UnregisterHandler(this);
}
}
AZ::Data::AssetType EditorBlastSliceAssetHandler::GetAssetType() const
{
return azrtti_typeid<BlastSliceAsset>();
}
const char* EditorBlastSliceAssetHandler::GetAssetTypeDisplayName() const
{
return "Blast Slice Asset";
}
const char* EditorBlastSliceAssetHandler::GetGroup() const
{
return "Blast";
}
const char* EditorBlastSliceAssetHandler::GetBrowserIcon() const
{
return "Icons/Components/Box.png";
}
void EditorBlastSliceAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
{
extensions.push_back("blast_slice");
}
} // namespace Blast
@@ -1,101 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Asset/BlastSliceAsset.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
namespace Blast
{
//! Used to create store asset references (i.e. ids) to fill out the EditorBlastMeshDataComponent
class BlastSliceAssetStorageComponent final : public AzToolsFramework::Components::EditorComponentBase
{
public:
AZ_COMPONENT(
BlastSliceAssetStorageComponent, "{696C7E62-1EA4-41E2-B4F6-7BD0D30888DC}",
AzToolsFramework::Components::EditorComponentBase);
~BlastSliceAssetStorageComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
const AZStd::vector<AZ::Data::AssetId>& GetMeshData() const
{
return m_meshAssetIdList;
}
void SetMeshData(const AZStd::vector<AZ::Data::AssetId>& meshAssetIdList)
{
m_meshAssetIdList = meshAssetIdList;
}
const AZStd::vector<AZStd::string>& GetMeshPathList() const
{
return m_meshAssetPathList;
}
void SetMeshPathList(const AZStd::vector<AZStd::string>& meshAssetPathList)
{
m_meshAssetPathList = meshAssetPathList;
}
private:
// AZ::Component interface implementation
void Activate() override {}
void Deactivate() override {}
// EditorComponentBase
void BuildGameEntity([[maybe_unused]] AZ::Entity* gameEntity) override {}
// Script API
bool GenerateAssetInfo(
const AZStd::vector<AZStd::string>& chunkNames,
AZStd::string_view blastFilename,
AZStd::string_view assetinfoFilename);
bool WriteMaterialFile(
AZStd::string_view materialGroupName,
const AZStd::vector<AZStd::string>& materialNames,
AZStd::string_view materialFilename);
AZStd::vector<AZ::Data::AssetId> m_meshAssetIdList;
AZStd::vector<AZStd::string> m_meshAssetPathList;
};
class EditorBlastSliceAssetHandler final
: public AZ::Data::AssetHandler
, public AZ::AssetTypeInfoBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EditorBlastSliceAssetHandler, AZ::SystemAllocator, 0);
~EditorBlastSliceAssetHandler() override;
// AZ::Data::AssetHandler
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override;
LoadResult LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset, AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
void DestroyAsset(AZ::Data::AssetPtr ptr) override;
void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override;
// AZ::AssetTypeInfoBus::Handler
AZ::Data::AssetType GetAssetType() const override;
const char* GetAssetTypeDisplayName() const override;
const char* GetGroup() const override;
const char* GetBrowserIcon() const override;
void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
void Register();
void Unregister();
};
} // namespace Blast
@@ -6,7 +6,7 @@
*
*/
#include <Asset/BlastSliceAsset.h>
#include <Asset/BlastChunksAsset.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Editor/EditorSystemComponent.h>
#include <Editor/EditorWindow.h>
@@ -16,7 +16,7 @@ namespace Blast
{
void EditorSystemComponent::Reflect(AZ::ReflectContext* context)
{
BlastSliceAsset::Reflect(context);
BlastChunksAsset::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
@@ -26,14 +26,14 @@ namespace Blast
void EditorSystemComponent::Activate()
{
m_editorBlastSliceAssetHandler = AZStd::make_unique<EditorBlastSliceAssetHandler>();
m_editorBlastSliceAssetHandler->Register();
m_editorBlastChunksAssetHandler = AZStd::make_unique<EditorBlastChunksAssetHandler>();
m_editorBlastChunksAssetHandler->Register();
auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
if (assetCatalog)
{
assetCatalog->EnableCatalogForAsset(azrtti_typeid<BlastSliceAsset>());
assetCatalog->AddExtension("blast_slice");
assetCatalog->EnableCatalogForAsset(azrtti_typeid<BlastChunksAsset>());
assetCatalog->AddExtension("blast_chunks");
}
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
@@ -46,7 +46,7 @@ namespace Blast
void EditorSystemComponent::Deactivate()
{
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
m_editorBlastSliceAssetHandler.reset();
m_editorBlastChunksAssetHandler.reset();
}
// This will be called when the IEditor instance is ready
@@ -11,7 +11,7 @@
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <Editor/EditorBlastSliceAssetHandler.h>
#include <Editor/EditorBlastChunksAssetHandler.h>
namespace Blast
{
@@ -39,7 +39,7 @@ namespace Blast
required.push_back(AZ_CRC("BlastService", 0x75beae2d));
}
AZStd::unique_ptr<EditorBlastSliceAssetHandler> m_editorBlastSliceAssetHandler;
AZStd::unique_ptr<EditorBlastChunksAssetHandler> m_editorBlastChunksAssetHandler;
// AZ::Component
void Activate() override;
@@ -0,0 +1,184 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/EditorBlastChunksAssetHandler.h>
#include <Editor/EditorBlastMeshDataComponent.h>
#include <Asset/BlastChunksAsset.h>
#include <AzTest/AzTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <gmock/gmock.h>
#include <AzCore/UnitTest/MockComponentApplication.h>
namespace UnitTest
{
MockComponentApplication::MockComponentApplication()
{
AZ::ComponentApplicationBus::Handler::BusConnect();
AZ::Interface<AZ::ComponentApplicationRequests>::Register(this);
}
MockComponentApplication::~MockComponentApplication()
{
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(this);
AZ::ComponentApplicationBus::Handler::BusDisconnect();
}
class MockAssetCatalogRequestBusHandler final
: public AZ::Data::AssetCatalogRequestBus::Handler
{
public:
MockAssetCatalogRequestBusHandler()
{
AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
}
virtual ~MockAssetCatalogRequestBusHandler()
{
AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
}
MOCK_METHOD3(GetAssetIdByPath, AZ::Data::AssetId(const char*, const AZ::Data::AssetType&, bool));
MOCK_METHOD1(GetAssetInfoById, AZ::Data::AssetInfo(const AZ::Data::AssetId&));
MOCK_METHOD1(AddAssetType, void(const AZ::Data::AssetType&));
MOCK_METHOD1(AddDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>));
MOCK_METHOD1(AddExtension, void(const char*));
MOCK_METHOD0(ClearCatalog, void());
MOCK_METHOD5(CreateBundleManifest, bool(const AZStd::string&, const AZStd::vector<AZStd::string>&, const AZStd::string&, int, const AZStd::vector<AZStd::string>&));
MOCK_METHOD2(CreateDeltaCatalog, bool(const AZStd::vector<AZStd::string>&, const AZStd::string&));
MOCK_METHOD0(DisableCatalog, void());
MOCK_METHOD1(EnableCatalogForAsset, void(const AZ::Data::AssetType&));
MOCK_METHOD3(EnumerateAssets, void(BeginAssetEnumerationCB, AssetEnumerationCB, EndAssetEnumerationCB));
MOCK_METHOD1(GenerateAssetIdTEMP, AZ::Data::AssetId(const char*));
MOCK_METHOD1(GetAllProductDependencies, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&));
MOCK_METHOD3(GetAllProductDependenciesFilter, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&, const AZStd::unordered_set<AZ::Data::AssetId>&, const AZStd::vector<AZStd::string>&));
MOCK_METHOD1(GetAssetPathById, AZStd::string(const AZ::Data::AssetId&));
MOCK_METHOD1(GetDirectProductDependencies, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&));
MOCK_METHOD1(GetHandledAssetTypes, void(AZStd::vector<AZ::Data::AssetType>&));
MOCK_METHOD0(GetRegisteredAssetPaths, AZStd::vector<AZStd::string>());
MOCK_METHOD2(InsertDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>, size_t));
MOCK_METHOD2(InsertDeltaCatalogBefore, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>, AZStd::shared_ptr<AzFramework::AssetRegistry>));
MOCK_METHOD1(LoadCatalog, bool(const char*));
MOCK_METHOD2(RegisterAsset, void(const AZ::Data::AssetId&, AZ::Data::AssetInfo&));
MOCK_METHOD1(RemoveDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>));
MOCK_METHOD1(SaveCatalog, bool(const char*));
MOCK_METHOD0(StartMonitoringAssets, void());
MOCK_METHOD0(StopMonitoringAssets, void());
MOCK_METHOD1(UnregisterAsset, void(const AZ::Data::AssetId&));
};
class MockAssetManager
: public AZ::Data::AssetManager
{
public:
MockAssetManager(const AZ::Data::AssetManager::Descriptor& desc) :
AssetManager(desc)
{
}
};
class EditorBlastChunkAssetHandlerTestFixture
: public AllocatorsTestFixture
{
public:
AZStd::unique_ptr<UnitTest::MockComponentApplication> m_mockComponentApplicationBusHandler;
AZStd::unique_ptr<MockAssetCatalogRequestBusHandler> m_mockAssetCatalogRequestBusHandler;
AZStd::unique_ptr<MockAssetManager> m_mockAssetManager;
void SetUp() override final
{
AllocatorsTestFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
m_mockComponentApplicationBusHandler = AZStd::make_unique<UnitTest::MockComponentApplication>();
m_mockAssetCatalogRequestBusHandler = AZStd::make_unique<MockAssetCatalogRequestBusHandler>();
m_mockAssetManager = AZStd::make_unique<MockAssetManager>(AZ::Data::AssetManager::Descriptor{});
AZ::Data::AssetManager::SetInstance(m_mockAssetManager.get());
}
void TearDown() override final
{
m_mockAssetManager.release();
AZ::Data::AssetManager::Destroy();
m_mockAssetCatalogRequestBusHandler.reset();
m_mockComponentApplicationBusHandler.reset();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
AllocatorsTestFixture::TearDown();
}
};
TEST_F(EditorBlastChunkAssetHandlerTestFixture, EditorBlastChunkAssetHandler_AssetManager_Registered)
{
Blast::EditorBlastChunksAssetHandler handler;
handler.Register();
EXPECT_NE(nullptr, AZ::Data::AssetManager::Instance().GetHandler(azrtti_typeid<Blast::BlastChunksAsset>()));
handler.Unregister();
}
TEST_F(EditorBlastChunkAssetHandlerTestFixture, EditorBlastChunkAssetHandler_AssetTypeInfoBus_Responds)
{
auto assetId = azrtti_typeid<Blast::BlastChunksAsset>();
Blast::EditorBlastChunksAssetHandler handler;
handler.Register();
AZ::Data::AssetType assetType = AZ::Uuid::CreateNull();
AZ::AssetTypeInfoBus::EventResult(assetType, assetId, &AZ::AssetTypeInfoBus::Events::GetAssetType);
EXPECT_NE(AZ::Uuid::CreateNull(), assetType);
const char* displayName = nullptr;
AZ::AssetTypeInfoBus::EventResult(displayName, assetId, &AZ::AssetTypeInfoBus::Events::GetAssetTypeDisplayName);
EXPECT_STREQ("Blast Chunks Asset", displayName);
const char* group = nullptr;
AZ::AssetTypeInfoBus::EventResult(group, assetId, &AZ::AssetTypeInfoBus::Events::GetGroup);
EXPECT_STREQ("Blast", group);
const char* icon = nullptr;
AZ::AssetTypeInfoBus::EventResult(icon, assetId, &AZ::AssetTypeInfoBus::Events::GetBrowserIcon);
EXPECT_STREQ("Icons/Components/Box.png", icon);
AZStd::vector<AZStd::string> extensions;
AZ::AssetTypeInfoBus::Event(assetId, &AZ::AssetTypeInfoBus::Events::GetAssetTypeExtensions, extensions);
ASSERT_EQ(1, extensions.size());
ASSERT_EQ("blast_chunks", extensions[0]);
handler.Unregister();
}
TEST_F(EditorBlastChunkAssetHandlerTestFixture, EditorBlastChunkAssetHandler_AssetHandler_Ready)
{
auto assetType = azrtti_typeid<Blast::BlastChunksAsset>();
auto&& assetManager = AZ::Data::AssetManager::Instance();
Blast::EditorBlastChunksAssetHandler handler;
handler.Register();
EXPECT_EQ(&handler, assetManager.GetHandler(assetType));
// create and release an instance of the BlastChunkAsset asset type
{
using ::testing::Return;
using ::testing::_;
EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetInfoById(_))
.Times(2)
.WillRepeatedly(Return(AZ::Data::AssetInfo{}));
auto assetPtr = assetManager.CreateAsset<Blast::BlastChunksAsset>(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0));
EXPECT_NE(nullptr, assetPtr.Get());
EXPECT_EQ(azrtti_typeid<Blast::BlastChunksAsset>(), assetPtr.GetType());
}
handler.Unregister();
}
}
@@ -1,377 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/EditorBlastSliceAssetHandler.h>
#include <Editor/EditorBlastMeshDataComponent.h>
#include <Asset/BlastSliceAsset.h>
#include <AzCore/UnitTest/MockComponentApplication.h>
#include <AzTest/AzTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <gmock/gmock.h>
namespace UnitTest
{
class MockComponentApplicationBusHandler final
//: public MockComponentApplication
: public AZ::ComponentApplicationBus::Handler
{
public:
MockComponentApplicationBusHandler()
{
AZ::ComponentApplicationBus::Handler::BusConnect();
}
virtual ~MockComponentApplicationBusHandler()
{
AZ::ComponentApplicationBus::Handler::BusDisconnect();
}
MOCK_METHOD0(Destroy, void());
MOCK_METHOD1(RegisterComponentDescriptor, void(const AZ::ComponentDescriptor*));
MOCK_METHOD1(UnregisterComponentDescriptor, void(const AZ::ComponentDescriptor*));
MOCK_METHOD1(RemoveEntity, bool(AZ::Entity*));
MOCK_METHOD1(DeleteEntity, bool(const AZ::EntityId&));
MOCK_METHOD1(GetEntityName, AZStd::string(const AZ::EntityId&));
MOCK_METHOD1(AddEntity, bool(AZ::Entity*));
MOCK_METHOD1(FindEntity, AZ::Entity*(const AZ::EntityId&));
MOCK_METHOD1(EnumerateEntities, void(const ComponentApplicationRequests::EntityCallback&));
MOCK_METHOD0(GetApplication, AZ::ComponentApplication* ());
MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ());
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
MOCK_METHOD0(GetAppRoot, const char* ());
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ());
MOCK_METHOD0(GetTickDeltaTime, float());
MOCK_METHOD1(Tick, void(float));
MOCK_METHOD0(TickSystem, void());
MOCK_CONST_METHOD0(GetRequiredSystemComponents, AZ::ComponentTypeList());
MOCK_METHOD1(ResolveModulePath, void(AZ::OSString&));
MOCK_METHOD0(CreateSerializeContext, void());
MOCK_METHOD0(DestroySerializeContext, void());
MOCK_METHOD0(CreateBehaviorContext, void());
MOCK_METHOD0(DestroyBehaviorContext, void());
MOCK_METHOD0(RegisterCoreComponents, void());
MOCK_METHOD1(AddSystemComponents, void(AZ::Entity*));
MOCK_METHOD0(ReflectSerialize, void());
MOCK_METHOD1(Reflect, void(AZ::ReflectContext*));
MOCK_CONST_METHOD0(GetBinFolder, const char* ());
};
class MockAssetCatalogRequestBusHandler final
: public AZ::Data::AssetCatalogRequestBus::Handler
{
public:
MockAssetCatalogRequestBusHandler()
{
AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
}
virtual ~MockAssetCatalogRequestBusHandler()
{
AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
}
MOCK_METHOD3(GetAssetIdByPath, AZ::Data::AssetId(const char*, const AZ::Data::AssetType&, bool));
MOCK_METHOD1(GetAssetInfoById, AZ::Data::AssetInfo(const AZ::Data::AssetId&));
MOCK_METHOD1(AddAssetType, void(const AZ::Data::AssetType&));
MOCK_METHOD1(AddDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>));
MOCK_METHOD1(AddExtension, void(const char*));
MOCK_METHOD0(ClearCatalog, void());
MOCK_METHOD5(CreateBundleManifest, bool(const AZStd::string&, const AZStd::vector<AZStd::string>&, const AZStd::string&, int, const AZStd::vector<AZStd::string>&));
MOCK_METHOD2(CreateDeltaCatalog, bool(const AZStd::vector<AZStd::string>&, const AZStd::string&));
MOCK_METHOD0(DisableCatalog, void());
MOCK_METHOD1(EnableCatalogForAsset, void(const AZ::Data::AssetType&));
MOCK_METHOD3(EnumerateAssets, void(BeginAssetEnumerationCB, AssetEnumerationCB, EndAssetEnumerationCB));
MOCK_METHOD1(GenerateAssetIdTEMP, AZ::Data::AssetId(const char*));
MOCK_METHOD1(GetAllProductDependencies, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&));
MOCK_METHOD3(GetAllProductDependenciesFilter, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&, const AZStd::unordered_set<AZ::Data::AssetId>&, const AZStd::vector<AZStd::string>&));
MOCK_METHOD1(GetAssetPathById, AZStd::string(const AZ::Data::AssetId&));
MOCK_METHOD1(GetDirectProductDependencies, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&));
MOCK_METHOD1(GetHandledAssetTypes, void(AZStd::vector<AZ::Data::AssetType>&));
MOCK_METHOD0(GetRegisteredAssetPaths, AZStd::vector<AZStd::string>());
MOCK_METHOD2(InsertDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>, size_t));
MOCK_METHOD2(InsertDeltaCatalogBefore, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>, AZStd::shared_ptr<AzFramework::AssetRegistry>));
MOCK_METHOD1(LoadCatalog, bool(const char*));
MOCK_METHOD2(RegisterAsset, void(const AZ::Data::AssetId&, AZ::Data::AssetInfo&));
MOCK_METHOD1(RemoveDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>));
MOCK_METHOD1(SaveCatalog, bool(const char*));
MOCK_METHOD0(StartMonitoringAssets, void());
MOCK_METHOD0(StopMonitoringAssets, void());
MOCK_METHOD1(UnregisterAsset, void(const AZ::Data::AssetId&));
};
class MockAssetManager
: public AZ::Data::AssetManager
{
public:
MockAssetManager(const AZ::Data::AssetManager::Descriptor& desc) :
AssetManager(desc)
{
}
};
class EditorBlastSliceAssetHandlerTestFixture
: public AllocatorsTestFixture
{
public:
AZStd::unique_ptr<MockComponentApplicationBusHandler> m_mockComponentApplicationBusHandler;
//AZStd::unique_ptr<UnitTest::MockComponentApplication> m_mockComponentApplicationBusHandler;
AZStd::unique_ptr<MockAssetCatalogRequestBusHandler> m_mockAssetCatalogRequestBusHandler;
AZStd::unique_ptr<MockAssetManager> m_mockAssetManager;
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
const AZ::ComponentDescriptor* m_sliceComponentDescriptor = nullptr;
void SetUpSliceComponents()
{
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
AZ::Entity::Reflect(m_serializeContext.get());
Blast::BlastSliceAssetStorageComponent::Reflect(m_serializeContext.get());
AzToolsFramework::Components::EditorComponentBase::Reflect(m_serializeContext.get());
m_sliceComponentDescriptor = AZ::SliceComponent::CreateDescriptor();
m_sliceComponentDescriptor->Reflect(m_serializeContext.get());
}
void TearDownSliceComponents()
{
delete m_sliceComponentDescriptor;
m_serializeContext.reset();
}
void SetUp() override final
{
AllocatorsTestFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
m_mockComponentApplicationBusHandler = AZStd::make_unique<MockComponentApplicationBusHandler>();
m_mockAssetCatalogRequestBusHandler = AZStd::make_unique<MockAssetCatalogRequestBusHandler>();
m_mockAssetManager = AZStd::make_unique<MockAssetManager>(AZ::Data::AssetManager::Descriptor{});
AZ::Data::AssetManager::SetInstance(m_mockAssetManager.get());
}
void TearDown() override final
{
AZ::Data::AssetManager::SetInstance(nullptr);
m_mockAssetManager.reset();
m_mockAssetCatalogRequestBusHandler.reset();
m_mockComponentApplicationBusHandler.reset();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
AllocatorsTestFixture::TearDown();
}
void SaveSliceAssetToStream(AZ::Entity* sliceAssetEntity, AZStd::vector<char>& buffer)
{
buffer.clear();
AZ::IO::ByteContainerStream<AZStd::vector<char>> stream(&buffer);
AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&stream, *m_serializeContext.get(), AZ::ObjectStream::ST_XML);
objStream->WriteClass(sliceAssetEntity);
EXPECT_TRUE(objStream->Finalize());
}
};
TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetManager_Registered)
{
Blast::EditorBlastSliceAssetHandler handler;
handler.Register();
EXPECT_NE(nullptr, AZ::Data::AssetManager::Instance().GetHandler(azrtti_typeid<Blast::BlastSliceAsset>()));
handler.Unregister();
}
TEST_F(EditorBlastSliceAssetHandlerTestFixture, BlastSliceAssetStorageComponent_Behavior_Registered)
{
AZ::BehaviorContext behaviorContext;
Blast::BlastSliceAssetStorageComponent::Reflect(&behaviorContext);
auto classEntry = behaviorContext.m_classes.find("BlastSliceAssetStorageComponent");
EXPECT_NE(behaviorContext.m_classes.end(), classEntry);
AZ::BehaviorClass* behaviorClass = classEntry->second;
auto methodEntry = behaviorClass->m_methods.find("GenerateAssetInfo");
EXPECT_NE(behaviorClass->m_methods.end(), methodEntry);
AZ::BehaviorMethod* behaviorMethod = methodEntry->second;
EXPECT_EQ(4, behaviorMethod->GetNumArguments());
EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAssetStorageComponent>());
EXPECT_EQ(behaviorMethod->GetArgument(1)->m_typeId, azrtti_typeid<AZStd::vector<AZStd::string>>());
EXPECT_EQ(behaviorMethod->GetArgument(2)->m_typeId, azrtti_typeid<AZStd::string_view>());
EXPECT_EQ(behaviorMethod->GetArgument(3)->m_typeId, azrtti_typeid<AZStd::string_view>());
}
TEST_F(EditorBlastSliceAssetHandlerTestFixture, BlastSliceAsset_Behavior_Registered)
{
AZ::BehaviorContext behaviorContext;
Blast::BlastSliceAsset::Reflect(&behaviorContext);
auto classEntry = behaviorContext.m_classes.find("BlastSliceAsset");
EXPECT_NE(behaviorContext.m_classes.end(), classEntry);
AZ::BehaviorClass* behaviorClass = classEntry->second;
auto setMeshIdListEntry = behaviorClass->m_methods.find("SetMeshIdList");
EXPECT_NE(behaviorClass->m_methods.end(), setMeshIdListEntry);
{
AZ::BehaviorMethod* behaviorMethod = setMeshIdListEntry->second;
EXPECT_EQ(2, behaviorMethod->GetNumArguments());
EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>());
EXPECT_EQ(behaviorMethod->GetArgument(1)->m_typeId, azrtti_typeid<AZStd::vector<AZ::Data::AssetId>>());
}
auto getMeshIdListEntry = behaviorClass->m_methods.find("GetMeshIdList");
EXPECT_NE(behaviorClass->m_methods.end(), getMeshIdListEntry);
{
AZ::BehaviorMethod* behaviorMethod = getMeshIdListEntry->second;
EXPECT_EQ(1, behaviorMethod->GetNumArguments());
EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>());
EXPECT_EQ(behaviorMethod->GetResult()->m_typeId, azrtti_typeid<AZStd::vector<AZ::Data::AssetId>>());
}
auto setMaterialIdEntry = behaviorClass->m_methods.find("SetMaterialId");
EXPECT_NE(behaviorClass->m_methods.end(), setMaterialIdEntry);
{
AZ::BehaviorMethod* behaviorMethod = setMaterialIdEntry->second;
EXPECT_EQ(2, behaviorMethod->GetNumArguments());
EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>());
EXPECT_EQ(behaviorMethod->GetArgument(1)->m_typeId, azrtti_typeid<AZ::Data::AssetId>());
}
auto getMaterialIdEntry = behaviorClass->m_methods.find("GetMaterialId");
EXPECT_NE(behaviorClass->m_methods.end(), getMaterialIdEntry);
{
AZ::BehaviorMethod* behaviorMethod = getMaterialIdEntry->second;
EXPECT_EQ(1, behaviorMethod->GetNumArguments());
EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>());
EXPECT_EQ(behaviorMethod->GetResult()->m_typeId, azrtti_typeid<AZ::Data::AssetId>());
}
auto getAssetTypeIdEntry = behaviorClass->m_methods.find("GetAssetTypeId");
EXPECT_NE(behaviorClass->m_methods.end(), getAssetTypeIdEntry);
{
AZ::BehaviorMethod* behaviorMethod = getAssetTypeIdEntry->second;
EXPECT_EQ(1, behaviorMethod->GetNumArguments());
EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>());
EXPECT_EQ(behaviorMethod->GetResult()->m_typeId, azrtti_typeid<AZ::TypeId>());
}
}
TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetTypeInfoBus_Responds)
{
auto assetId = azrtti_typeid<Blast::BlastSliceAsset>();
Blast::EditorBlastSliceAssetHandler handler;
handler.Register();
AZ::Data::AssetType assetType = AZ::Uuid::CreateNull();
AZ::AssetTypeInfoBus::EventResult(assetType, assetId, &AZ::AssetTypeInfoBus::Events::GetAssetType);
EXPECT_NE(AZ::Uuid::CreateNull(), assetType);
const char* displayName = nullptr;
AZ::AssetTypeInfoBus::EventResult(displayName, assetId, &AZ::AssetTypeInfoBus::Events::GetAssetTypeDisplayName);
EXPECT_STREQ("Blast Slice Asset", displayName);
const char* group = nullptr;
AZ::AssetTypeInfoBus::EventResult(group, assetId, &AZ::AssetTypeInfoBus::Events::GetGroup);
EXPECT_STREQ("Blast", group);
const char* icon = nullptr;
AZ::AssetTypeInfoBus::EventResult(icon, assetId, &AZ::AssetTypeInfoBus::Events::GetBrowserIcon);
EXPECT_STREQ("Editor/Icons/Components/Box.png", icon);
AZStd::vector<AZStd::string> extensions;
AZ::AssetTypeInfoBus::Event(assetId, &AZ::AssetTypeInfoBus::Events::GetAssetTypeExtensions, extensions);
ASSERT_EQ(1, extensions.size());
ASSERT_EQ("blast_slice", extensions[0]);
handler.Unregister();
}
TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetHandler_Ready)
{
auto assetType = azrtti_typeid<Blast::BlastSliceAsset>();
auto&& assetManager = AZ::Data::AssetManager::Instance();
Blast::EditorBlastSliceAssetHandler handler;
handler.Register();
EXPECT_EQ(&handler, assetManager.GetHandler(assetType));
// create and release an instance of the BlastSliceAsset asset type
{
using ::testing::Return;
using ::testing::_;
EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetInfoById(_))
.Times(1)
.WillRepeatedly(Return(AZ::Data::AssetInfo{}));
auto assetPtr = assetManager.CreateAsset<Blast::BlastSliceAsset>(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0));
EXPECT_NE(nullptr, assetPtr.Get());
EXPECT_EQ(azrtti_typeid<Blast::BlastSliceAsset>(), assetPtr.GetType());
}
handler.Unregister();
}
TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetHandler_LoadsAssetData)
{
SetUpSliceComponents();
AZStd::vector<AZStd::string> meshAssetPathList = { "/foo/path/thing.cgf", "/foo/path/that.cgf" };
AZ::Entity* storageEntity = aznew AZ::Entity();
auto* blastStorage = storageEntity->CreateComponent<Blast::BlastSliceAssetStorageComponent>();
blastStorage->SetMeshPathList(meshAssetPathList);
AZ::Entity sliceEntity;
AZ::SliceComponent* slice = sliceEntity.CreateComponent<AZ::SliceComponent>();
slice->AddEntity(storageEntity);
AZStd::vector<char> buffer;
SaveSliceAssetToStream(&sliceEntity, buffer);
// Load a slice with the BlastSliceAssetStorageComponent
Blast::EditorBlastSliceAssetHandler handler;
handler.Register();
{
using ::testing::Return;
using ::testing::_;
EXPECT_CALL(*m_mockComponentApplicationBusHandler, GetSerializeContext)
.Times(1)
.WillOnce(Return(m_serializeContext.get()));
EXPECT_CALL(*m_mockComponentApplicationBusHandler, FindEntity(_))
.Times(1)
.WillOnce(Return(&sliceEntity));
EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetIdByPath(_,_,_))
.Times(2)
.WillRepeatedly(Return(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0)));
EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetInfoById(_))
.Times(2)
.WillRepeatedly(Return(AZ::Data::AssetInfo{}));
auto&& assetManager = AZ::Data::AssetManager::Instance();
auto assetPtr = assetManager.CreateAsset<Blast::BlastSliceAsset>(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0));
AZ::IO::ByteContainerStream<AZStd::vector<char>> stream(&buffer);
stream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
const AZ::Data::AssetFilterCB assetLoadFilterCB{};
bool loaded = handler.LoadAssetData(assetPtr, &stream, assetLoadFilterCB);
EXPECT_TRUE(loaded);
}
handler.Unregister();
TearDownSliceComponents();
}
}
+2 -2
View File
@@ -11,8 +11,8 @@ set(FILES
Source/Editor/EditorBlastFamilyComponent.cpp
Source/Editor/EditorBlastMeshDataComponent.cpp
Source/Editor/EditorBlastMeshDataComponent.h
Source/Editor/EditorBlastSliceAssetHandler.h
Source/Editor/EditorBlastSliceAssetHandler.cpp
Source/Editor/EditorBlastChunksAssetHandler.h
Source/Editor/EditorBlastChunksAssetHandler.cpp
Source/Editor/EditorSystemComponent.h
Source/Editor/EditorSystemComponent.cpp
Editor/ConfigurationWidget.h
@@ -7,6 +7,6 @@
#
set(FILES
# Tests/Editor/EditorBlastSliceAssetHandlerTest.cpp
# Disabled until SPEC-7904 is fixed Tests/Editor/EditorBlastChunksAssetHandlerTest.cpp
Tests/Editor/EditorTestMain.cpp
)
+2 -2
View File
@@ -26,8 +26,8 @@ set(FILES
Source/Asset/BlastAsset.cpp
Source/Asset/BlastAssetHandler.h
Source/Asset/BlastAssetHandler.cpp
Source/Asset/BlastSliceAsset.h
Source/Asset/BlastSliceAsset.cpp
Source/Asset/BlastChunksAsset.h
Source/Asset/BlastChunksAsset.cpp
Source/Components/BlastFamilyComponent.h
Source/Components/BlastFamilyComponent.cpp
Source/Components/BlastFamilyComponentNotificationBusHandler.h
@@ -1,323 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def install_user_site():
import os
import sys
import azlmbr.paths
executableBinFolder = azlmbr.paths.executableFolder
# the PyAssImp module checks the Windows PATH for the assimp DLL file
if os.name == "nt":
os.environ['PATH'] = os.environ['PATH'] + os.pathsep + executableBinFolder
# PyAssImp module needs to find the shared library for assimp to load; "posix" handles Mac and Linux
if os.name == "posix":
if 'LD_LIBRARY_PATH' in os.environ:
os.environ['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH'] + os.pathsep + executableBinFolder
else:
os.environ['LD_LIBRARY_PATH'] = executableBinFolder
# add the user site packages folder to find the pyassimp egg link
import site
for item in sys.path:
if (item.find('site-packages') != -1):
site.addsitedir(item)
install_user_site()
import pyassimp
import azlmbr.asset
import azlmbr.asset.builder
import azlmbr.asset.entity
import azlmbr.blast
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity
import azlmbr.math
import os
import traceback
import binascii
import sys
# the UUID must be unique amongst all the asset builders in Python or otherwise
# a collision of builders will happen preventing one from running
busIdString = '{CF5C74D1-9ED4-4851-85B1-9B15090DBEC7}'
busId = azlmbr.math.Uuid_CreateString(busIdString, 0)
handler = None
jobKeyName = 'Blast Chunk Assets'
sceneManifestType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
dccMaterialType = azlmbr.math.Uuid_CreateString('{C88469CF-21E7-41EB-96FD-BF14FBB05EDC}', 0)
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))
def get_source_fbx_filename(request):
fullPath = os.path.join(request.watchFolder, request.sourceFile)
basePath, filePart = os.path.split(fullPath)
filename = os.path.splitext(filePart)[0] + '.fbx'
filename = os.path.join(basePath, filename)
return filename
def raise_error(message):
raise RuntimeError(f'[ERROR]: {message}')
def generate_asset_info(chunkNames, request):
import azlmbr.blast
# write out an object stream with the extension of .fbx.assetinfo.generated
basePath, sceneFile = os.path.split(request.sourceFile)
assetinfoFilename = os.path.splitext(sceneFile)[0] + '.fbx.assetinfo.generated'
assetinfoFilename = os.path.join(basePath, assetinfoFilename)
assetinfoFilename = assetinfoFilename.replace('\\', '/').lower()
outputFilename = os.path.join(request.tempDirPath, assetinfoFilename)
storage = azlmbr.blast.BlastSliceAssetStorageComponent()
if (storage.GenerateAssetInfo(chunkNames, request.sourceFile, outputFilename)):
product = azlmbr.asset.builder.JobProduct(assetinfoFilename, sceneManifestType, 1)
product.dependenciesHandled = True
return product
raise_error('Failed to generate assetinfo.generated')
def export_fbx_manifest(request):
output = []
fbxFilename = get_source_fbx_filename(request)
sceneAsset = pyassimp.load(fbxFilename)
with sceneAsset as scene:
rootNode = scene.mRootNode.contents
for index in range(0, rootNode.mNumChildren):
child = rootNode.mChildren[index]
childNode = child.contents
childNodeName = bytes.decode(childNode.mName.data)
output.append(str(childNodeName))
return output
def convert_to_asset_paths(fbxFilename, gameRoot, chunkNameList):
realtivePath = fbxFilename[len(gameRoot) + 1:]
realtivePath = os.path.splitext(realtivePath)[0]
output = []
for chunk in chunkNameList:
assetPath = realtivePath + '-' + chunk + '.cgf'
assetPath = assetPath.replace('\\', '/')
assetPath = assetPath.lower()
output.append(assetPath)
return output
# creates a single job to compile for each platform
def create_jobs(request):
fbxSidecarFilename = get_source_fbx_filename(request)
if (os.path.exists(fbxSidecarFilename) is False):
print('[WARN] Sidecar FBX file {} is missing for blast file {}'.format(fbxSidecarFilename, request.sourceFile))
return azlmbr.asset.builder.CreateJobsResponse()
# see if the FBX file already has a .assetinfo source asset, if so then do not create a job
if (os.path.exists(f'{fbxSidecarFilename}.assetinfo')):
response = azlmbr.asset.builder.CreateJobsResponse()
response.result = azlmbr.asset.builder.CreateJobsResponse_ResultSuccess
return response
# create job descriptor for each platform
jobDescriptorList = []
for platformInfo in request.enabledPlatforms:
jobDesc = azlmbr.asset.builder.JobDescriptor()
jobDesc.jobKey = jobKeyName
jobDesc.priority = 12 # higher than the 'Scene compilation' or 'fbx'
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
# handler to create jobs for a source asset
def on_create_jobs(args):
try:
request = args[0]
return create_jobs(request)
except:
log_exception_traceback()
return azlmbr.asset.builder.CreateJobsResponse()
def generate_blast_slice_asset(chunkNameList, request):
# get list of relative chunk paths
fbxFilename = get_source_fbx_filename(request)
assetPaths = convert_to_asset_paths(fbxFilename, request.watchFolder, chunkNameList)
outcome = azlmbr.asset.entity.PythonBuilderRequestBus(bus.Broadcast, 'CreateEditorEntity', 'BlastData')
if (outcome.IsSuccess() is False):
raise_error('could not create an editor entity')
blastDataEntityId = outcome.GetValue()
# create a component for the editor entity
gameType = azlmbr.entity.EntityType().Game
blastMeshDataTypeIdList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Blast Slice Storage Component"], gameType)
componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentOfType', blastDataEntityId, blastMeshDataTypeIdList[0])
if (componentOutcome.IsSuccess() is False):
raise_error('failed to add component (Blast Slice Storage Component) to the blast_slice')
# build the blast slice using the chunk asset paths
blastMeshComponentId = componentOutcome.GetValue()[0]
outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'BuildComponentPropertyTreeEditor', blastMeshComponentId)
if(outcome.IsSuccess() is False):
raise_error(f'failed to create Property Tree Editor for component ({blastMeshComponentId})')
pte = outcome.GetValue()
pte.set_visible_enforcement(True)
pte.set_value('Mesh Paths', assetPaths)
# write out an object stream with the extension of .blast_slice
basePath, sceneFile = os.path.split(request.sourceFile)
blastFilename = os.path.splitext(sceneFile)[0] + '.blast_slice'
blastFilename = os.path.join(basePath, blastFilename)
blastFilename = blastFilename.replace('\\', '/').lower()
tempFilename = os.path.join(request.tempDirPath, blastFilename)
entityList = [blastDataEntityId]
makeDynamic = False
outcome = azlmbr.asset.entity.PythonBuilderRequestBus(bus.Broadcast, 'WriteSliceFile', tempFilename, entityList, makeDynamic)
if (outcome.IsSuccess() is False):
raise_error(f'WriteSliceFile failed for blast_slice file ({blastFilename})')
# return a job product
blastSliceAsset = azlmbr.blast.BlastSliceAsset()
subId = binascii.crc32(blastFilename.encode('utf8'))
product = azlmbr.asset.builder.JobProduct(blastFilename, blastSliceAsset.GetAssetTypeId(), subId)
product.dependenciesHandled = True
return product
def read_in_string(data, dataLength):
stringData = ''
for idx in range(4, dataLength - 1):
char = bytes.decode(data[idx])
if (str.isascii(char)):
stringData += char
return stringData
def import_material_info(fbxFilename):
_, group_name = os.path.split(fbxFilename)
group_name = os.path.splitext(group_name)[0]
output = {}
output['group_name'] = group_name
output['material_name_list'] = []
sceneAsset = pyassimp.load(fbxFilename)
with sceneAsset as scene:
for materialIndex in range(0, scene.mNumMaterials):
material = scene.mMaterials[materialIndex].contents
for materialPropertyIdx in range(0, material.mNumProperties):
materialProperty = material.mProperties[materialPropertyIdx].contents
materialPropertyName = bytes.decode(materialProperty.mKey.data)
if (materialPropertyName.endswith('mat.name') and materialProperty.mType is 3):
stringData = read_in_string(materialProperty.mData, materialProperty.mDataLength)
output['material_name_list'].append(stringData)
return output
def write_material_file(sourceFile, destFolder):
# preserve source MTL files
rootPath, materialSourceFile = os.path.split(sourceFile)
materialSourceFile = os.path.splitext(materialSourceFile)[0] + '.mtl'
materialSourceFile = os.path.join(rootPath, materialSourceFile)
if (os.path.exists(materialSourceFile)):
print(f'{materialSourceFile} source already exists')
return None
# auto-generate a DCC material file
info = import_material_info(sourceFile)
materialGroupName = info['group_name']
materialNames = info['material_name_list']
materialFilename = materialGroupName + '.dccmtl.generated'
subId = binascii.crc32(materialFilename.encode('utf8'))
materialFilename = os.path.join(destFolder, materialFilename)
storage = azlmbr.blast.BlastSliceAssetStorageComponent()
storage.WriteMaterialFile(materialGroupName, materialNames, materialFilename)
product = azlmbr.asset.builder.JobProduct(materialFilename, dccMaterialType, subId)
product.dependenciesHandled = True
return product
def process_fbx_file(request):
# fill out response object
response = azlmbr.asset.builder.ProcessJobResponse()
productOutputs = []
# write out DCCMTL file as a product (if needed)
materialProduct = write_material_file(get_source_fbx_filename(request), request.tempDirPath)
if (materialProduct is not None):
productOutputs.append(materialProduct)
# prepare output folder
basePath, _ = os.path.split(request.sourceFile)
outputPath = os.path.join(request.tempDirPath, basePath)
os.makedirs(outputPath)
# parse FBX for chunk names
chunkNameList = export_fbx_manifest(request)
# create assetinfo generated (is product)
productOutputs.append(generate_asset_info(chunkNameList, request))
# write out the blast_slice object stream
productOutputs.append(generate_blast_slice_asset(chunkNameList, request))
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_fbx_file(request)
return azlmbr.asset.builder.ProcessJobResponse()
except:
log_exception_traceback()
return azlmbr.asset.builder.ProcessJobResponse()
# register asset builder
def register_asset_builder():
assetPattern = azlmbr.asset.builder.AssetBuilderPattern()
assetPattern.pattern = '*.blast'
assetPattern.type = azlmbr.asset.builder.AssetBuilderPattern_Wildcard
builderDescriptor = azlmbr.asset.builder.AssetBuilderDesc()
builderDescriptor.name = "Blast Gem"
builderDescriptor.patterns = [assetPattern]
builderDescriptor.busId = busId
builderDescriptor.version = 5
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
try:
handler = register_asset_builder()
except:
handler = None
log_exception_traceback()
@@ -0,0 +1,290 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
This a Python Asset Builder script examines each .blast file to see if an
associated .fbx file needs to be processed by exporting all of its chunks
into a scene manifest
This is also a SceneAPI script that executes from a foo.fbx.assetinfo scene
manifest that writes out asset chunk data for .blast files
"""
import os, traceback, binascii, sys, json, pathlib
import azlmbr.math
import azlmbr.asset
import azlmbr.asset.entity
import azlmbr.asset.builder
import azlmbr.bus
#
# Python Asset Builder
#
busId = azlmbr.math.Uuid_CreateString('{D4FA20E3-8EF4-44A3-A045-AAE6C1CCAAAB}', 0)
jobKeyName = 'Blast Chunk Assets'
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))
def raise_error(message):
print (f'ERROR - {message}');
raise RuntimeError(f'[ERROR]: {message}');
# creates a single job to compile for each platform
def get_source_fbx_filename(request):
fullPath = os.path.join(request.watchFolder, request.sourceFile)
basePath, filePart = os.path.split(fullPath)
filename = os.path.splitext(filePart)[0] + '.fbx'
filename = os.path.join(basePath, filename)
return filename
def create_jobs(request):
fbxSidecarFilename = get_source_fbx_filename(request)
if (os.path.exists(fbxSidecarFilename) is False):
print('[WARN] Sidecar FBX file {} is missing for blast file {}'.format(fbxSidecarFilename, request.sourceFile))
return azlmbr.asset.builder.CreateJobsResponse()
# see if the FBX file already has a .assetinfo source asset, if so then do not create a job
establishedAssetInfo = f'{fbxSidecarFilename}.assetinfo';
if (os.path.exists(establishedAssetInfo)):
response = azlmbr.asset.builder.CreateJobsResponse()
response.result = azlmbr.asset.builder.CreateJobsResponse_ResultSuccess
return response
# create job descriptor for each platform
jobDescriptorList = []
for platformInfo in request.enabledPlatforms:
sourceFileDependency = azlmbr.asset.builder.SourceFileDependency()
sourceFileDependency.sourceFileDependencyPath = fbxSidecarFilename
jobDependency = azlmbr.asset.builder.JobDependency()
jobDependency.sourceFile = sourceFileDependency
jobDependency.jobKey = jobKeyName
jobDependency.platformIdentifier = platformInfo.identifier
jobDesc = azlmbr.asset.builder.JobDescriptor()
jobDesc.jobKey = jobKeyName
jobDesc.set_platform_identifier(platformInfo.identifier)
jobDesc.jobDependencyList = [jobDependency]
jobDescriptorList.append(jobDesc)
response = azlmbr.asset.builder.CreateJobsResponse()
response.result = azlmbr.asset.builder.CreateJobsResponse_ResultSuccess
response.createJobOutputs = jobDescriptorList
return response
# to create jobs for a source asset
def on_create_jobs(args):
try:
request = args[0]
return create_jobs(request)
except:
log_exception_traceback()
return azlmbr.asset.builder.CreateJobsResponse()
def generate_assetinfo_product(request):
# write out a product asset file with the extension of .fbx.assetinfo.generated
basePath, sceneFile = os.path.split(request.sourceFile)
assetinfoFilename = os.path.splitext(sceneFile)[0] + '.fbx.assetinfo.generated'
assetinfoFilename = os.path.join(basePath, assetinfoFilename)
assetinfoFilename = assetinfoFilename.replace('\\', '/').lower()
outputFilename = os.path.join(request.tempDirPath, assetinfoFilename)
# the only rule in it is to run this file again as a scene processor
currentScript = pathlib.Path(__file__).resolve()
aDict = {"values": [{"$type": "ScriptProcessorRule", "scriptFilename": f"{currentScript}"}]}
jsonString = json.dumps(aDict)
jsonFile = open(outputFilename, "w")
jsonFile.write(jsonString)
jsonFile.close()
# return a job product for the generated assetinfo file
sceneManifestType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
subId = 1
product = azlmbr.asset.builder.JobProduct(outputFilename, sceneManifestType, subId)
product.dependenciesHandled = True
return product
def process_fbx_file(request):
# fill out response object
response = azlmbr.asset.builder.ProcessJobResponse()
productOutputs = []
# prepare output folder
basePath, _ = os.path.split(request.sourceFile)
outputPath = os.path.join(request.tempDirPath, basePath)
os.makedirs(outputPath)
# create assetinfo generated file
productOutputs.append(generate_assetinfo_product(request))
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_fbx_file(request)
return azlmbr.asset.builder.ProcessJobResponse()
except:
log_exception_traceback()
return azlmbr.asset.builder.ProcessJobResponse()
# register asset builder
def register_asset_builder():
assetPattern = azlmbr.asset.builder.AssetBuilderPattern()
assetPattern.pattern = '*.blast'
assetPattern.type = azlmbr.asset.builder.AssetBuilderPattern_Wildcard
builderDescriptor = azlmbr.asset.builder.AssetBuilderDesc()
builderDescriptor.name = "Blast Scene 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
pythonAssetBuilderHandler = None
try:
if (pythonAssetBuilderHandler == None):
pythonAssetBuilderHandler = register_asset_builder()
except:
pythonAssetBuilderHandler = None
#
# SceneAPI Processor
#
blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0)
def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList):
import azlmbr.scene
import azlmbr.object
import azlmbr.paths
import json, os
jsonFilename = os.path.basename(scene.sourceFilename)
jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks')
# prepare output folder
basePath, _ = os.path.split(jsonFilename)
outputPath = os.path.join(outputDirectory, basePath)
if not os.path.exists(outputPath):
os.makedirs(outputPath, False)
# write out a JSON file with the chunk file info
with open(jsonFilename, "w") as jsonFile:
jsonFile.write(scene.manifest.ExportToJson())
exportProduct = azlmbr.scene.ExportProduct()
exportProduct.filename = jsonFilename
exportProduct.sourceId = scene.sourceGuid
exportProduct.assetType = blastChunksAssetType
exportProduct.subId = 101
exportProductList = azlmbr.scene.ExportProductList()
exportProductList.AddProduct(exportProduct)
return exportProductList
def on_prepare_for_export(args):
try:
scene = args[0] # azlmbr.scene.Scene
outputDirectory = args[1] # string
platformIdentifier = args[2] # string
productList = args[3] # azlmbr.scene.ExportProductList
return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList)
except:
log_exception_traceback()
def get_mesh_node_names(sceneGraph):
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
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:
nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node))
nodePath = nodeName.get_path()
if (len(nodeName.get_path())):
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):
import uuid, os
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
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_select_node(meshGroup, chunkPath)
return sceneManifest.export()
sceneJobHandler = None
def on_update_manifest(args):
try:
scene = args[0]
return update_manifest(scene)
except:
global sceneJobHandler
sceneJobHandler = None
log_exception_traceback()
# try to create SceneAPI handler for processing
try:
import azlmbr.scene as sceneApi
if (sceneJobHandler == None):
sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
sceneJobHandler.connect()
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export)
except:
sceneJobHandler = None
+10 -3
View File
@@ -4,6 +4,13 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# LYN-652 to re-enable once the Blast gem tests are stable
# import asset_builder_blast
try:
import azlmbr.asset
import azlmbr.asset.entity
import azlmbr.asset.builder
import blast_asset_builder
except:
# this script only runs in an asset processing environment
# like the AssetProcessor or an AssetBuilder
# plus the Blast gem needs to be enabled for the project
pass
@@ -11,6 +11,7 @@
#include "CommandManager.h"
#include <EMotionFX/CommandSystem/Source/AnimGraphTriggerActionCommands.h>
#include <EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/AnimGraph.h>
@@ -858,8 +859,16 @@ namespace CommandSystem
// add it to the old node group if it was assigned to one before
if (!mNodeGroupName.empty())
{
commandString = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -nodeNames \"%s\" -nodeAction \"add\"", animGraph->GetID(), mNodeGroupName.c_str(), mName.c_str());
if (GetCommandManager()->ExecuteCommandInsideCommand(commandString.c_str(), outResult) == false)
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
/*animGraphId = */ animGraph->GetID(),
/*name = */ mNodeGroupName,
/*visible = */ AZStd::nullopt,
/*newName = */ AZStd::nullopt,
/*nodeNames = */ {{mName}},
/*nodeAction = */ CommandSystem::CommandAnimGraphAdjustNodeGroup::NodeAction::Add
);
if (GetCommandManager()->ExecuteCommandInsideCommand(command, outResult) == false)
{
if (outResult.size() > 0)
{
@@ -1363,11 +1372,16 @@ namespace CommandSystem
EMotionFX::AnimGraphNodeGroup* nodeGroup = node->GetAnimGraph()->FindNodeGroupForNode(node);
if (nodeGroup && !cutMode)
{
commandString = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %d -name \"%s\" -nodeNames \"%s\" -nodeAction \"add\"",
targetAnimGraph->GetID(),
nodeGroup->GetName(),
nodeName.c_str());
commandGroup->AddCommandString(commandString);
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
/*animGraphId = */ targetAnimGraph->GetID(),
/*name = */ nodeGroup->GetNameString(),
/*visible = */ AZStd::nullopt,
/*newName = */ AZStd::nullopt,
/*nodeNames = */ {{nodeName}},
/*nodeAction = */ CommandSystem::CommandAnimGraphAdjustNodeGroup::NodeAction::Add
);
commandGroup->AddCommand(command);
}
// Recurse through the child nodes.
@@ -6,6 +6,7 @@
*
*/
#include <AzCore/std/optional.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include "AnimGraphNodeGroupCommands.h"
#include "AnimGraphConnectionCommands.h"
@@ -22,45 +23,46 @@
namespace CommandSystem
{
AZ_CLASS_ALLOCATOR_IMPL(CommandAnimGraphAdjustNodeGroup, EMotionFX::CommandAllocator, 0)
//--------------------------------------------------------------------------------
// CommandAnimGraphAdjustNodeGroup
//--------------------------------------------------------------------------------
CommandAnimGraphAdjustNodeGroup::CommandAnimGraphAdjustNodeGroup(MCore::Command* orgCommand)
: MCore::Command("AnimGraphAdjustNodeGroup", orgCommand)
CommandAnimGraphAdjustNodeGroup::CommandAnimGraphAdjustNodeGroup(
MCore::Command* orgCommand,
AZ::u32 animGraphId,
AZStd::string name,
AZStd::optional<bool> visible,
AZStd::optional<AZStd::string> newName,
AZStd::optional<AZStd::vector<AZStd::string>> nodeNames,
AZStd::optional<NodeAction> nodeAction,
AZStd::optional<AZ::u32> color,
AZStd::optional<bool> updateUI
)
: MCore::Command(s_commandName, orgCommand)
, ParameterMixinAnimGraphId(animGraphId)
, m_name(AZStd::move(name))
, m_isVisible(visible)
, m_newName(AZStd::move(newName))
, m_nodeNames(AZStd::move(nodeNames))
, m_nodeAction(nodeAction)
, m_color(color)
, m_updateUI(updateUI)
{
}
CommandAnimGraphAdjustNodeGroup::~CommandAnimGraphAdjustNodeGroup()
AZStd::vector<AZStd::string> CommandAnimGraphAdjustNodeGroup::GenerateNodeNameVector(EMotionFX::AnimGraph* animGraph, const AZStd::vector<EMotionFX::AnimGraphNodeId>& nodeIDs)
{
}
AZStd::string CommandAnimGraphAdjustNodeGroup::GenerateNodeNameString(EMotionFX::AnimGraph* animGraph, const AZStd::vector<EMotionFX::AnimGraphNodeId>& nodeIDs)
{
if (nodeIDs.empty())
AZStd::vector<AZStd::string> result;
for (const auto& nodeID : nodeIDs)
{
return "";
}
AZStd::string result;
const size_t numNodes = nodeIDs.size();
for (size_t i = 0; i < numNodes; ++i)
{
EMotionFX::AnimGraphNode* animGraphNode = animGraph->RecursiveFindNodeById(nodeIDs[i]);
const EMotionFX::AnimGraphNode* animGraphNode = animGraph->RecursiveFindNodeById(nodeID);
if (!animGraphNode)
{
continue;
}
result += animGraphNode->GetName();
if (i < numNodes - 1)
{
result += ';';
}
result.emplace_back(animGraphNode->GetName());
}
return result;
}
@@ -80,78 +82,51 @@ namespace CommandSystem
}
bool CommandAnimGraphAdjustNodeGroup::Execute(const MCore::CommandLine& parameters, AZStd::string& outResult)
bool CommandAnimGraphAdjustNodeGroup::Execute(const MCore::CommandLine&, AZStd::string& outResult)
{
EMotionFX::AnimGraph* animGraph = CommandsGetAnimGraph(parameters, this, outResult);
EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(m_animGraphId);
if (!animGraph)
{
return false;
}
// get the node group name
AZStd::string groupName;
parameters.GetValue("name", this, groupName);
// find the node group index
const uint32 groupIndex = animGraph->FindNodeGroupIndexByName(groupName.c_str());
const uint32 groupIndex = animGraph->FindNodeGroupIndexByName(m_name.c_str());
if (groupIndex == MCORE_INVALIDINDEX32)
{
outResult = AZStd::string::format("Node group \"%s\" can not be found.", groupName.c_str());
outResult = AZStd::string::format("Node group \"%s\" can not be found.", m_name.c_str());
return false;
}
// get a pointer to the node group and keep the old name
EMotionFX::AnimGraphNodeGroup* nodeGroup = animGraph->GetNodeGroup(groupIndex);
mOldName = nodeGroup->GetName();
// is visible?
if (parameters.CheckIfHasParameter("isVisible"))
if (m_isVisible.has_value())
{
const bool isVisible = parameters.GetValueAsBool("isVisible", this);
mOldIsVisible = nodeGroup->GetIsVisible();
nodeGroup->SetIsVisible(isVisible);
m_oldIsVisible = nodeGroup->GetIsVisible();
nodeGroup->SetIsVisible(*m_isVisible);
}
// background color
if (parameters.CheckIfHasParameter("color"))
if (m_color.has_value())
{
const AZ::Vector4 colorVector4 = parameters.GetValueAsVector4("color", this);
const AZ::u32 color = AZ::Color(static_cast<float>(colorVector4.GetX()), static_cast<float>(colorVector4.GetY()), static_cast<float>(colorVector4.GetZ()), static_cast<float>(colorVector4.GetW())).ToU32();
mOldColor = nodeGroup->GetColor();
nodeGroup->SetColor(color);
m_oldColor = nodeGroup->GetColor();
nodeGroup->SetColor(*m_color);
}
// set the new name
// if the new name is empty, the name is not changed
AZStd::string newGroupName;
parameters.GetValue("newName", this, newGroupName);
if (!newGroupName.empty())
if (m_newName.has_value())
{
nodeGroup->SetName(newGroupName.c_str());
nodeGroup->SetName(m_newName->c_str());
}
// check if parametes nodeNames is set
if (parameters.CheckIfHasParameter("nodeNames"))
if (m_nodeNames.has_value())
{
// keep the old nodes IDs
mOldNodeIds = CollectNodeIdsFromGroup(nodeGroup);
// get the node action
AZStd::string nodeAction;
parameters.GetValue("nodeAction", this, nodeAction);
// get the node names and split the string
AZStd::string nodeNamesString;
parameters.GetValue("nodeNames", this, nodeNamesString);
AZStd::vector<AZStd::string> nodeNames;
AzFramework::StringFunc::Tokenize(nodeNamesString.c_str(), nodeNames, ";", false, true);
m_oldNodeIds = CollectNodeIdsFromGroup(nodeGroup);
// remove the selected nodes from the given node group
if (AzFramework::StringFunc::Equal(nodeAction.c_str(), "remove"))
if (*m_nodeAction == NodeAction::Remove)
{
for (const AZStd::string& nodeName : nodeNames)
for (const AZStd::string& nodeName : *m_nodeNames)
{
EMotionFX::AnimGraphNode* animGraphNode = animGraph->RecursiveFindNodeByName(nodeName.c_str());
if (!animGraphNode)
@@ -163,9 +138,9 @@ namespace CommandSystem
nodeGroup->RemoveNodeById(animGraphNode->GetId());
}
}
else if (AzFramework::StringFunc::Equal(nodeAction.c_str(), "add")) // add the selected nodes to the given node group
else if (*m_nodeAction == NodeAction::Add)
{
for (const AZStd::string& nodeName : nodeNames)
for (const AZStd::string& nodeName : *m_nodeNames)
{
EMotionFX::AnimGraphNode* animGraphNode = animGraph->RecursiveFindNodeByName(nodeName.c_str());
if (!animGraphNode)
@@ -184,12 +159,12 @@ namespace CommandSystem
nodeGroup->AddNode(animGraphNode->GetId());
}
}
else if (AzFramework::StringFunc::Equal(nodeAction.c_str(), "replace")) // clear the node group and then add the selected nodes to the given node group
else if (*m_nodeAction == NodeAction::Replace)
{
// clear the node group upfront
nodeGroup->RemoveAllNodes();
for (const AZStd::string& nodeName : nodeNames)
for (const AZStd::string& nodeName : *m_nodeNames)
{
EMotionFX::AnimGraphNode* animGraphNode = animGraph->RecursiveFindNodeByName(nodeName.c_str());
if (!animGraphNode)
@@ -211,68 +186,40 @@ namespace CommandSystem
}
// save the current dirty flag and tell the anim graph that something got changed
mOldDirtyFlag = animGraph->GetDirtyFlag();
m_oldDirtyFlag = animGraph->GetDirtyFlag();
animGraph->SetDirtyFlag(true);
return true;
}
// undo the command
bool CommandAnimGraphAdjustNodeGroup::Undo(const MCore::CommandLine& parameters, AZStd::string& outResult)
bool CommandAnimGraphAdjustNodeGroup::Undo(const MCore::CommandLine&, AZStd::string& outResult)
{
EMotionFX::AnimGraph* animGraph = CommandsGetAnimGraph(parameters, this, outResult);
EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(m_animGraphId);
if (!animGraph)
{
return false;
}
AZStd::string commandString = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i", animGraph->GetID());
// set the old name or simply set the name if the name is not changed
if (parameters.CheckIfHasParameter("newName"))
{
AZStd::string newName;
parameters.GetValue("newName", this, newName);
commandString += AZStd::string::format(" -name \"%s\"", newName.c_str());
commandString += AZStd::string::format(" -newName \"%s\"", mOldName.c_str());
}
else
{
commandString += AZStd::string::format(" -name \"%s\"", mOldName.c_str());
}
// set the old visible flag
if (parameters.CheckIfHasParameter("isVisible"))
{
commandString += AZStd::string::format(" -isVisible %i", mOldIsVisible);
}
// set the old color
if (parameters.CheckIfHasParameter("color"))
{
AZ::Color oldColor;
oldColor.FromU32(mOldColor);
const AZStd::string oldColorString = AZStd::string::format("%.8f,%.8f,%.8f,%.8f", static_cast<float>(oldColor.GetR()), static_cast<float>(oldColor.GetG()), static_cast<float>(oldColor.GetB()), static_cast<float>(oldColor.GetA()));
commandString += AZStd::string::format(" -color \"%s\"", oldColorString.c_str());
}
// set the old nodes
if (parameters.CheckIfHasParameter("nodeNames"))
{
const AZStd::string nodeNamesString = CommandAnimGraphAdjustNodeGroup::GenerateNodeNameString(animGraph, mOldNodeIds);
commandString += AZStd::string::format(" -nodeNames \"%s\" -nodeAction \"replace\"", nodeNamesString.c_str());
}
CommandAnimGraphAdjustNodeGroup* command = aznew CommandAnimGraphAdjustNodeGroup(
GetCommandManager()->FindCommand(CommandAnimGraphAdjustNodeGroup::s_commandName),
/*animGraphId = */ m_animGraphId,
/*name = */ m_newName.has_value() ? *m_newName : m_name,
/*visible = */ m_isVisible.has_value() ? AZStd::optional<bool>(m_oldIsVisible) : AZStd::nullopt,
/*newName = */ m_newName.has_value() ? AZStd::optional<AZStd::string>(m_name) : AZStd::nullopt,
/*nodeNames = */ m_nodeNames.has_value() ? AZStd::optional<AZStd::vector<AZStd::string>>(GenerateNodeNameVector(animGraph, m_oldNodeIds)) : AZStd::nullopt,
/*nodeAction = */ m_nodeNames.has_value() ? AZStd::optional<NodeAction>(NodeAction::Replace) : AZStd::nullopt,
/*color = */ m_color.has_value() ? AZStd::optional<AZ::u32>(m_oldColor) : AZStd::nullopt
);
// execute the command
if (!GetCommandManager()->ExecuteCommandInsideCommand(commandString, outResult))
if (!GetCommandManager()->ExecuteCommandInsideCommand(command, outResult))
{
AZ_Error("EMotionFX", false, outResult.c_str());
}
// set the dirty flag back to the old value
animGraph->SetDirtyFlag(mOldDirtyFlag);
animGraph->SetDirtyFlag(m_oldDirtyFlag);
return true;
}
@@ -282,7 +229,7 @@ namespace CommandSystem
{
GetSyntax().ReserveParameters(8);
GetSyntax().AddRequiredParameter("name", "The name of the node group to adjust.", MCore::CommandSyntax::PARAMTYPE_STRING);
GetSyntax().AddParameter("animGraphID", "The id of the blend set the node group belongs to.", MCore::CommandSyntax::PARAMTYPE_INT, "-1");
EMotionFX::ParameterMixinAnimGraphId::InitSyntax(GetSyntax(), /*isParameterRequired=*/ false);
GetSyntax().AddParameter("isVisible", "The visibility flag of the node group.", MCore::CommandSyntax::PARAMTYPE_BOOLEAN, "true");
GetSyntax().AddParameter("newName", "The new name of the node group.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
GetSyntax().AddParameter("nodeNames", "A list of node names that should be added/removed to/from the node group.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
@@ -291,6 +238,51 @@ namespace CommandSystem
GetSyntax().AddParameter("updateUI", "Setting this to true will trigger a refresh of the node groups UI.", MCore::CommandSyntax::PARAMTYPE_BOOLEAN, "true");
}
bool CommandAnimGraphAdjustNodeGroup::SetCommandParameters(const MCore::CommandLine& parameters)
{
EMotionFX::ParameterMixinAnimGraphId::SetCommandParameters(parameters);
m_name = parameters.GetValue("name", this);
if (parameters.CheckIfHasParameter("isVisible"))
{
m_isVisible = parameters.GetValueAsBool("isVisible", this);
}
if (parameters.CheckIfHasParameter("newName"))
{
m_newName = parameters.GetValue("newName", this);
}
if (parameters.CheckIfHasParameter("nodeNames"))
{
m_nodeNames.emplace();
AzFramework::StringFunc::Tokenize(parameters.GetValue("nodeNames", this), m_nodeNames.value(), ";", false, true);
}
if (parameters.CheckIfHasValue("nodeAction"))
{
const AZStd::string& nodeActionStr = parameters.GetValue("nodeAction", this);
if (nodeActionStr == "add")
{
m_nodeAction = NodeAction::Add;
}
else if (nodeActionStr == "remove")
{
m_nodeAction = NodeAction::Remove;
}
else if (nodeActionStr == "replace")
{
m_nodeAction = NodeAction::Replace;
}
}
if (parameters.CheckIfHasParameter("color"))
{
m_color = AZ::Color(parameters.GetValueAsVector4("color", this)).ToU32();
}
if (parameters.CheckIfHasParameter("updateUI"))
{
m_updateUI = parameters.GetValueAsBool("updateUI", this);
}
return true;
}
const char* CommandAnimGraphAdjustNodeGroup::GetDescription() const
{
@@ -447,21 +439,20 @@ namespace CommandSystem
MCore::CommandGroup commandGroup;
AZStd::string commandString = AZStd::string::format("AnimGraphAddNodeGroup -animGraphID %i -name \"%s\" -updateUI %s",animGraph->GetID(), mOldName.c_str(), updateWindow.c_str());
commandGroup.AddCommandString(commandString);
commandGroup.AddCommandString(AZStd::string::format("AnimGraphAddNodeGroup -animGraphID %i -name \"%s\" -updateUI %s",animGraph->GetID(), mOldName.c_str(), updateWindow.c_str()));
const AZStd::string nodeNamesString = CommandAnimGraphAdjustNodeGroup::GenerateNodeNameString(animGraph, mOldNodeIds);
auto* command = aznew CommandAnimGraphAdjustNodeGroup(
GetCommandManager()->FindCommand(CommandAnimGraphAdjustNodeGroup::s_commandName),
/*animGraphId = */ animGraph->GetID(),
/*name = */ mOldName,
/*visible = */ mOldIsVisible,
/*newName = */ AZStd::nullopt,
/*nodeNames = */ CommandAnimGraphAdjustNodeGroup::GenerateNodeNameVector(animGraph, mOldNodeIds),
/*nodeAction = */ CommandAnimGraphAdjustNodeGroup::NodeAction::Add,
/*color = */ mOldColor
);
AZ::Color oldColor;
oldColor.FromU32(mOldColor);
const AZStd::string oldColorString = AZStd::string::format("%.8f,%.8f,%.8f,%.8f",
static_cast<float>(oldColor.GetR()), static_cast<float>(oldColor.GetG()), static_cast<float>(oldColor.GetB()), static_cast<float>(oldColor.GetA()));
commandString = AZStd::string::format(
"AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -isVisible %s -color \"%s\" -nodeNames \"%s\" -nodeAction \"add\" -updateUI %s",
animGraph->GetID(), mOldName.c_str(), AZStd::to_string(mOldIsVisible).c_str(), oldColorString.c_str(), nodeNamesString.c_str(), updateWindow.c_str());
commandGroup.AddCommandString(commandString);
commandGroup.AddCommand(command);
AZStd::string result;
if (!GetCommandManager()->ExecuteCommandGroupInsideCommand(commandGroup, result))

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