Merge branch 'development' of https://github.com/o3de/o3de into Network/olexl/multiplayer_per_entity_analytics
This commit is contained in:
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+7
-2
@@ -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
|
||||
|
||||
+217
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -2624,8 +2630,6 @@ void EditorViewportWidget::DestroyRenderContext()
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void EditorViewportWidget::SetDefaultCamera()
|
||||
{
|
||||
// Ensure the FOV matches our internally stored setting
|
||||
SetFOV(GetFOV());
|
||||
if (IsDefaultCamera())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
m_pipelineState = m_shader->AcquirePipelineState(descriptor);
|
||||
}
|
||||
}
|
||||
m_dirty = false;
|
||||
}
|
||||
return m_pipelineState;
|
||||
|
||||
+39
-23
@@ -16,18 +16,21 @@
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
class ModernViewportCameraControllerInstance;
|
||||
class ModularViewportCameraControllerInstance;
|
||||
|
||||
//! Builder class to create and configure a ModularViewportCameraControllerInstance.
|
||||
class ModularViewportCameraController
|
||||
: public AzFramework::MultiViewportController<
|
||||
ModernViewportCameraControllerInstance, AzFramework::ViewportControllerPriority::DispatchToAllPriorities>
|
||||
ModularViewportCameraControllerInstance,
|
||||
AzFramework::ViewportControllerPriority::DispatchToAllPriorities>
|
||||
{
|
||||
public:
|
||||
using CameraListBuilder = AZStd::function<void(AzFramework::Cameras&)>;
|
||||
using CameraPropsBuilder = AZStd::function<void(AzFramework::CameraProps&)>;
|
||||
|
||||
//! 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);
|
||||
@@ -35,18 +38,22 @@ namespace AtomToolsFramework
|
||||
void SetupCameraProperies(AzFramework::CameraProps& cameraProps);
|
||||
|
||||
private:
|
||||
//! Builder to generate a list of CameraInputs to run in the ModularViewportCameraControllerInstance.
|
||||
CameraListBuilder m_cameraListBuilder;
|
||||
CameraPropsBuilder m_cameraPropsBuilder;
|
||||
CameraPropsBuilder m_cameraPropsBuilder; //!< Builder to define custom camera properties to use for things such as rotate and
|
||||
//!< translate interpolation.
|
||||
};
|
||||
|
||||
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 +67,34 @@ 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.
|
||||
|
||||
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
|
||||
|
||||
+20
-19
@@ -84,7 +84,7 @@ namespace AtomToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance(
|
||||
ModularViewportCameraControllerInstance::ModularViewportCameraControllerInstance(
|
||||
const AzFramework::ViewportId viewportId, ModularViewportCameraController* controller)
|
||||
: MultiViewportControllerInstanceInterface<ModularViewportCameraController>(viewportId, controller)
|
||||
{
|
||||
@@ -95,7 +95,8 @@ namespace AtomToolsFramework
|
||||
{
|
||||
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,7 +112,7 @@ namespace AtomToolsFramework
|
||||
ModularViewportCameraControllerRequestBus::Handler::BusConnect(viewportId);
|
||||
}
|
||||
|
||||
ModernViewportCameraControllerInstance::~ModernViewportCameraControllerInstance()
|
||||
ModularViewportCameraControllerInstance::~ModularViewportCameraControllerInstance()
|
||||
{
|
||||
ModularViewportCameraControllerRequestBus::Handler::BusDisconnect();
|
||||
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
@@ -132,7 +133,7 @@ namespace AtomToolsFramework
|
||||
return AzFramework::ViewportControllerPriority::Normal;
|
||||
}
|
||||
|
||||
bool ModernViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
|
||||
bool ModularViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
|
||||
{
|
||||
if (event.m_priority == GetPriority(m_cameraSystem))
|
||||
{
|
||||
@@ -142,7 +143,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 +153,7 @@ namespace AtomToolsFramework
|
||||
|
||||
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
|
||||
{
|
||||
m_updatingTransform = true;
|
||||
m_updatingTransformInternally = true;
|
||||
|
||||
if (m_cameraMode == CameraMode::Control)
|
||||
{
|
||||
@@ -180,10 +181,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 +194,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 +219,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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,207 @@
|
||||
/*
|
||||
* 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;
|
||||
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
|
||||
|
||||
void SetUpChunkComponents()
|
||||
{
|
||||
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
|
||||
|
||||
AZ::Entity::Reflect(m_serializeContext.get());
|
||||
AzToolsFramework::Components::EditorComponentBase::Reflect(m_serializeContext.get());
|
||||
}
|
||||
|
||||
void TearDownChunkComponents()
|
||||
{
|
||||
m_serializeContext.reset();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void SaveChunkAssetToStream(AZ::Entity* chunkAssetEntity, 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(chunkAssetEntity);
|
||||
EXPECT_TRUE(objStream->Finalize());
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Tests/Editor/EditorBlastChunksAssetHandlerTest.cpp
|
||||
Tests/Editor/EditorTestMain.cpp
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
+121
-130
@@ -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))
|
||||
|
||||
@@ -13,22 +13,73 @@
|
||||
#include <MCore/Source/CommandGroup.h>
|
||||
#include <EMotionFX/Source/AnimGraphNodeGroup.h>
|
||||
#include <EMotionFX/Source/AnimGraph.h>
|
||||
#include <EMotionFX/CommandSystem/Source/ParameterMixins.h>
|
||||
|
||||
namespace CommandSystem
|
||||
{
|
||||
// adjust a node group
|
||||
MCORE_DEFINECOMMAND_START(CommandAnimGraphAdjustNodeGroup, "Adjust anim graph node group", true)
|
||||
public:
|
||||
static AZStd::string GenerateNodeNameString(EMotionFX::AnimGraph* animGraph, const AZStd::vector<EMotionFX::AnimGraphNodeId>& nodeIDs);
|
||||
static AZStd::vector<EMotionFX::AnimGraphNodeId> CollectNodeIdsFromGroup(EMotionFX::AnimGraphNodeGroup* nodeGroup);
|
||||
class CommandAnimGraphAdjustNodeGroup
|
||||
: public MCore::Command
|
||||
, public EMotionFX::ParameterMixinAnimGraphId
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
AZStd::string mOldName;
|
||||
bool mOldIsVisible;
|
||||
AZ::u32 mOldColor;
|
||||
AZStd::vector<EMotionFX::AnimGraphNodeId> mOldNodeIds;
|
||||
bool mOldDirtyFlag;
|
||||
MCORE_DEFINECOMMAND_END
|
||||
static constexpr inline AZStd::string_view s_commandName = "AnimGraphAdjustNodeGroup";
|
||||
|
||||
enum class NodeAction
|
||||
{
|
||||
Add,
|
||||
Remove,
|
||||
Replace
|
||||
};
|
||||
|
||||
explicit CommandAnimGraphAdjustNodeGroup(
|
||||
MCore::Command* orgCommand = nullptr,
|
||||
AZ::u32 animGraphId = MCORE_INVALIDINDEX32,
|
||||
AZStd::string name = AZStd::string{},
|
||||
AZStd::optional<bool> visible = AZStd::nullopt,
|
||||
AZStd::optional<AZStd::string> newName = AZStd::nullopt,
|
||||
AZStd::optional<AZStd::vector<AZStd::string>> nodeNames = AZStd::nullopt,
|
||||
AZStd::optional<NodeAction> nodeAction = AZStd::nullopt,
|
||||
AZStd::optional<AZ::u32> color = AZStd::nullopt,
|
||||
AZStd::optional<bool> updateUI = AZStd::nullopt
|
||||
);
|
||||
bool Execute(const MCore::CommandLine& parameters, AZStd::string& outResult) override;
|
||||
bool Undo(const MCore::CommandLine& parameters, AZStd::string& outResult) override;
|
||||
void InitSyntax() override;
|
||||
bool SetCommandParameters(const MCore::CommandLine& parameters) override;
|
||||
bool GetIsUndoable() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const char* GetHistoryName() const override
|
||||
{
|
||||
return "Adjust anim graph node group";
|
||||
}
|
||||
const char* GetDescription() const override;
|
||||
MCore::Command* Create() override
|
||||
{
|
||||
return new CommandAnimGraphAdjustNodeGroup(this);
|
||||
}
|
||||
|
||||
static AZStd::vector<AZStd::string> GenerateNodeNameVector(EMotionFX::AnimGraph* animGraph, const AZStd::vector<EMotionFX::AnimGraphNodeId>& nodeIDs);
|
||||
static AZStd::vector<EMotionFX::AnimGraphNodeId> CollectNodeIdsFromGroup(EMotionFX::AnimGraphNodeGroup* nodeGroup);
|
||||
|
||||
private:
|
||||
AZStd::string m_name;
|
||||
AZStd::optional<bool> m_isVisible;
|
||||
AZStd::optional<AZStd::string> m_newName;
|
||||
AZStd::optional<AZStd::vector<AZStd::string>> m_nodeNames;
|
||||
AZStd::optional<NodeAction> m_nodeAction;
|
||||
AZStd::optional<AZ::u32> m_color;
|
||||
AZStd::optional<bool> m_updateUI;
|
||||
|
||||
bool m_oldIsVisible;
|
||||
AZ::u32 m_oldColor;
|
||||
AZStd::vector<EMotionFX::AnimGraphNodeId> m_oldNodeIds;
|
||||
bool m_oldDirtyFlag;
|
||||
};
|
||||
|
||||
// add node group
|
||||
MCORE_DEFINECOMMAND_START(CommandAnimGraphAddNodeGroup, "Add anim graph node group", true)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// include the required headers
|
||||
#include "NodeGroupCommands.h"
|
||||
#include "CommandManager.h"
|
||||
#include <EMotionFX/Source/NodeGroup.h>
|
||||
#include <EMotionFX/Source/Allocators.h>
|
||||
#include <EMotionFX/Source/ActorManager.h>
|
||||
#include <MCore/Source/LogManager.h>
|
||||
#include <MCore/Source/StringConversions.h>
|
||||
@@ -20,230 +20,147 @@ namespace CommandSystem
|
||||
//--------------------------------------------------------------------------------
|
||||
// CommandAdjustNodeGroup
|
||||
//--------------------------------------------------------------------------------
|
||||
AZ_CLASS_ALLOCATOR_IMPL(CommandAdjustNodeGroup, EMotionFX::CommandAllocator, 0)
|
||||
|
||||
// constructor
|
||||
CommandAdjustNodeGroup::CommandAdjustNodeGroup(MCore::Command* orgCommand)
|
||||
: MCore::Command("AdjustNodeGroup", orgCommand)
|
||||
, mOldNodeGroup(nullptr)
|
||||
CommandAdjustNodeGroup::CommandAdjustNodeGroup(
|
||||
MCore::Command* orgCommand,
|
||||
uint32 actorId,
|
||||
const AZStd::string& name,
|
||||
AZStd::optional<AZStd::string> newName,
|
||||
AZStd::optional<bool> enabledOnDefault,
|
||||
AZStd::optional<AZStd::vector<AZStd::string>> nodeNames,
|
||||
AZStd::optional<NodeAction> nodeAction
|
||||
)
|
||||
: MCore::Command(s_commandName.data(), orgCommand)
|
||||
, EMotionFX::ParameterMixinActorId(actorId)
|
||||
, m_name(name)
|
||||
, m_newName(AZStd::move(newName))
|
||||
, m_enabledOnDefault(enabledOnDefault)
|
||||
, m_nodeNames(AZStd::move(nodeNames))
|
||||
, m_nodeAction(nodeAction)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
CommandAdjustNodeGroup::~CommandAdjustNodeGroup()
|
||||
{
|
||||
if (mOldNodeGroup)
|
||||
{
|
||||
mOldNodeGroup->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// execute
|
||||
bool CommandAdjustNodeGroup::Execute(const MCore::CommandLine& parameters, AZStd::string& outResult)
|
||||
bool CommandAdjustNodeGroup::Execute(const MCore::CommandLine&, AZStd::string& outResult)
|
||||
{
|
||||
AZStd::string valueString;
|
||||
|
||||
// get the motion id and the corresponding motion pointer
|
||||
const int32 actorID = parameters.GetValueAsInt("actorID", this);
|
||||
parameters.GetValue("name", this, &valueString);
|
||||
|
||||
// get the actor
|
||||
EMotionFX::Actor* actor = EMotionFX::GetActorManager().FindActorByID(actorID);
|
||||
EMotionFX::Actor* actor = EMotionFX::GetActorManager().FindActorByID(m_actorId);
|
||||
if (actor == nullptr)
|
||||
{
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Actor with id='%i' does not exist.", actorID);
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Actor with id='%i' does not exist.", m_actorId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the node group
|
||||
EMotionFX::NodeGroup* nodeGroup = actor->FindNodeGroupByNameNoCase(valueString.c_str());
|
||||
EMotionFX::NodeGroup* nodeGroup = actor->FindNodeGroupByNameNoCase(m_name.c_str());
|
||||
if (nodeGroup == nullptr)
|
||||
{
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Node group with name='%s' does not exist.", valueString.c_str());
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Node group with name='%s' does not exist.", m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the old node group for undo
|
||||
if (mOldNodeGroup)
|
||||
{
|
||||
mOldNodeGroup->Destroy();
|
||||
}
|
||||
|
||||
mOldNodeGroup = aznew EMotionFX::NodeGroup(*nodeGroup);
|
||||
m_oldNodeGroup = AZStd::make_unique<EMotionFX::NodeGroup>(*nodeGroup);
|
||||
|
||||
// check if newName is set and apply new name
|
||||
if (parameters.CheckIfHasParameter("newName"))
|
||||
if (m_newName.has_value())
|
||||
{
|
||||
parameters.GetValue("newName", this, &valueString);
|
||||
nodeGroup->SetName(valueString.c_str());
|
||||
nodeGroup->SetName(*m_newName);
|
||||
}
|
||||
|
||||
// check if parameter disabledOnDefault is set and adjust it
|
||||
if (parameters.CheckIfHasParameter("enabledOnDefault"))
|
||||
if (m_enabledOnDefault.has_value())
|
||||
{
|
||||
const bool enabledOnDefault = parameters.GetValueAsBool("enabledOnDefault", this);
|
||||
nodeGroup->SetIsEnabledOnDefault(enabledOnDefault);
|
||||
nodeGroup->SetIsEnabledOnDefault(*m_enabledOnDefault);
|
||||
}
|
||||
|
||||
// check if parametes nodeNames is set
|
||||
if (parameters.CheckIfHasParameter("nodeNames"))
|
||||
if (m_nodeNames.has_value())
|
||||
{
|
||||
// get the node action
|
||||
AZStd::string nodeAction;
|
||||
parameters.GetValue("nodeAction", this, &valueString);
|
||||
|
||||
// get the node names and split the string
|
||||
AZStd::string nodeNameString;
|
||||
parameters.GetValue("nodeNames", this, &nodeNameString);
|
||||
|
||||
// get the individual node names
|
||||
AZStd::vector<AZStd::string> nodeNames;
|
||||
AzFramework::StringFunc::Tokenize(nodeNameString.c_str(), nodeNames, MCore::CharacterConstants::semiColon, true /* keep empty strings */, true /* keep space strings */);
|
||||
|
||||
// get the number of nodes
|
||||
const size_t numNodes = nodeNames.size();
|
||||
|
||||
// remove the selected nodes from the node group
|
||||
if (AzFramework::StringFunc::Equal(valueString.c_str(), "remove", false /* no case */))
|
||||
if (*m_nodeAction == NodeAction::Replace)
|
||||
{
|
||||
for (size_t i = 0; i < numNodes; ++i)
|
||||
{
|
||||
// get the node
|
||||
EMotionFX::Node* node = actor->GetSkeleton()->FindNodeByName(nodeNames[i].c_str());
|
||||
if (node == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove the node
|
||||
nodeGroup->RemoveNodeByNodeIndex((uint16)node->GetNodeIndex());
|
||||
}
|
||||
}
|
||||
else if (AzFramework::StringFunc::Equal(valueString.c_str(), "add", false /* no case */)) // add the selected nodes to the node group
|
||||
{
|
||||
for (size_t i = 0; i < numNodes; ++i)
|
||||
{
|
||||
// get the node
|
||||
EMotionFX::Node* node = actor->GetSkeleton()->FindNodeByName(nodeNames[i].c_str());
|
||||
if (node == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the node
|
||||
uint16 nodeIndex = (uint16)node->GetNodeIndex();
|
||||
nodeGroup->RemoveNodeByNodeIndex(nodeIndex);
|
||||
nodeGroup->AddNode(nodeIndex);
|
||||
}
|
||||
}
|
||||
else // selected nodes form the new node group
|
||||
{
|
||||
// clear previous nodes
|
||||
nodeGroup->GetNodeArray().Clear();
|
||||
|
||||
// add all nodes to the group
|
||||
for (size_t i = 0; i < numNodes; ++i)
|
||||
}
|
||||
for (const AZStd::string& nodeName : *m_nodeNames)
|
||||
{
|
||||
EMotionFX::Node* node = actor->GetSkeleton()->FindNodeByName(nodeName);
|
||||
if (!node)
|
||||
{
|
||||
// get the node
|
||||
EMotionFX::Node* node = actor->GetSkeleton()->FindNodeByName(nodeNames[i].c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if node exists
|
||||
if (node == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the node
|
||||
nodeGroup->AddNode((uint16)node->GetNodeIndex());
|
||||
uint16 nodeIndex = (uint16)node->GetNodeIndex();
|
||||
nodeGroup->RemoveNodeByNodeIndex(nodeIndex);
|
||||
if (*m_nodeAction == NodeAction::Add || *m_nodeAction == NodeAction::Replace)
|
||||
{
|
||||
nodeGroup->AddNode(nodeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save the current dirty flag and tell the actor that something got changed
|
||||
mOldDirtyFlag = actor->GetDirtyFlag();
|
||||
m_oldDirtyFlag = actor->GetDirtyFlag();
|
||||
actor->SetDirtyFlag(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// undo the command
|
||||
bool CommandAdjustNodeGroup::Undo(const MCore::CommandLine& parameters, AZStd::string& outResult)
|
||||
bool CommandAdjustNodeGroup::Undo(const MCore::CommandLine&, AZStd::string& outResult)
|
||||
{
|
||||
// return if no information about the previous node group was stored
|
||||
if (!mOldNodeGroup)
|
||||
if (!m_oldNodeGroup)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the motion id and the corresponding motion pointer
|
||||
int32 actorID = parameters.GetValueAsInt("actorID", this);
|
||||
|
||||
// get the name
|
||||
AZStd::string name;
|
||||
if (parameters.CheckIfHasParameter("newName"))
|
||||
{
|
||||
parameters.GetValue("newName", this, &name);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.GetValue("name", this, &name);
|
||||
}
|
||||
|
||||
// get the actor
|
||||
EMotionFX::Actor* actor = EMotionFX::GetActorManager().FindActorByID(actorID);
|
||||
EMotionFX::Actor* actor = EMotionFX::GetActorManager().FindActorByID(m_actorId);
|
||||
|
||||
// return error if actor was not found
|
||||
if (actor == nullptr)
|
||||
{
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Actor with id='%i' does not exist.", actorID);
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Actor with id='%i' does not exist.", m_actorId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the node group
|
||||
EMotionFX::NodeGroup* nodeGroup = actor->FindNodeGroupByNameNoCase(name.c_str());
|
||||
EMotionFX::NodeGroup* nodeGroup = actor->FindNodeGroupByNameNoCase(m_newName.has_value() ? m_newName->c_str() : m_name.c_str());
|
||||
|
||||
// return error if node group name is not set
|
||||
if (nodeGroup == nullptr)
|
||||
if (!nodeGroup)
|
||||
{
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Node group with name='%s' does not exist.", name.c_str());
|
||||
outResult = AZStd::string::format("Cannot adjust node group. Node group with name='%s' does not exist.", m_newName.has_value() ? m_newName->c_str() : m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset the old values
|
||||
if (parameters.CheckIfHasParameter("enabledOnDefault"))
|
||||
if (m_enabledOnDefault.has_value())
|
||||
{
|
||||
nodeGroup->SetIsEnabledOnDefault(mOldNodeGroup->GetIsEnabledOnDefault());
|
||||
nodeGroup->SetIsEnabledOnDefault(m_oldNodeGroup->GetIsEnabledOnDefault());
|
||||
}
|
||||
|
||||
if (parameters.CheckIfHasParameter("newName"))
|
||||
if (m_newName.has_value())
|
||||
{
|
||||
nodeGroup->SetName(mOldNodeGroup->GetName());
|
||||
nodeGroup->SetName(m_oldNodeGroup->GetName());
|
||||
}
|
||||
|
||||
if (parameters.CheckIfHasParameter("nodeNames"))
|
||||
if (m_nodeNames.has_value())
|
||||
{
|
||||
// clear previous nodes
|
||||
nodeGroup->GetNodeArray().Clear();
|
||||
const uint32 numNodes = mOldNodeGroup->GetNumNodes();
|
||||
nodeGroup->SetNumNodes(static_cast<uint16>(numNodes));
|
||||
const uint16 numNodes = m_oldNodeGroup->GetNumNodes();
|
||||
nodeGroup->SetNumNodes(numNodes);
|
||||
|
||||
// add all nodes to the group
|
||||
for (uint32 i = 0; i < numNodes; ++i)
|
||||
for (uint16 i = 0; i < numNodes; ++i)
|
||||
{
|
||||
nodeGroup->SetNode(static_cast<uint16>(i), mOldNodeGroup->GetNode(static_cast<uint16>(i)));
|
||||
nodeGroup->SetNode(i, m_oldNodeGroup->GetNode(i));
|
||||
}
|
||||
}
|
||||
|
||||
// delete the old node group
|
||||
if (mOldNodeGroup)
|
||||
{
|
||||
mOldNodeGroup->Destroy();
|
||||
}
|
||||
mOldNodeGroup = nullptr;
|
||||
m_oldNodeGroup = nullptr;
|
||||
|
||||
// set the dirty flag back to the old value
|
||||
actor->SetDirtyFlag(mOldDirtyFlag);
|
||||
actor->SetDirtyFlag(m_oldDirtyFlag);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -252,7 +169,7 @@ namespace CommandSystem
|
||||
void CommandAdjustNodeGroup::InitSyntax()
|
||||
{
|
||||
GetSyntax().ReserveParameters(6);
|
||||
GetSyntax().AddRequiredParameter("actorID", "The id of the actor the node group belongs to.", MCore::CommandSyntax::PARAMTYPE_INT);
|
||||
EMotionFX::ParameterMixinActorId::InitSyntax(GetSyntax(), /*isParameterRequired=*/ true);
|
||||
GetSyntax().AddRequiredParameter("name", "The name of the node group to adjust.", MCore::CommandSyntax::PARAMTYPE_STRING);
|
||||
GetSyntax().AddParameter("newName", "The new name of the node group.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
|
||||
GetSyntax().AddParameter("enabledOnDefault", "The enabled on default flag.", MCore::CommandSyntax::PARAMTYPE_BOOLEAN, "false");
|
||||
@@ -261,6 +178,45 @@ namespace CommandSystem
|
||||
}
|
||||
|
||||
|
||||
bool CommandAdjustNodeGroup::SetCommandParameters(const MCore::CommandLine& parameters)
|
||||
{
|
||||
EMotionFX::ParameterMixinActorId::SetCommandParameters(parameters);
|
||||
|
||||
m_name = parameters.GetValue("name", this);
|
||||
if (parameters.CheckIfHasParameter("newName"))
|
||||
{
|
||||
m_newName = parameters.GetValue("newName", this);
|
||||
}
|
||||
if (parameters.CheckIfHasParameter("enabledOnDefault"))
|
||||
{
|
||||
m_enabledOnDefault = parameters.GetValueAsBool("enabledOnDefault", this);
|
||||
}
|
||||
if (parameters.CheckIfHasParameter("nodeNames"))
|
||||
{
|
||||
m_nodeNames.emplace();
|
||||
AzFramework::StringFunc::Tokenize(parameters.GetValue("nodeNames", this), m_nodeNames.value(), ";", false, true);
|
||||
}
|
||||
if (parameters.CheckIfHasParameter("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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// get the description
|
||||
const char* CommandAdjustNodeGroup::GetDescription() const
|
||||
{
|
||||
@@ -304,7 +260,7 @@ namespace CommandSystem
|
||||
}
|
||||
|
||||
// add new node group to the actor
|
||||
EMotionFX::NodeGroup* nodeGroup = EMotionFX::NodeGroup::Create(name.c_str());
|
||||
EMotionFX::NodeGroup* nodeGroup = aznew EMotionFX::NodeGroup(name);
|
||||
actor->AddNodeGroup(nodeGroup);
|
||||
|
||||
// save the current dirty flag and tell the actor that something got changed
|
||||
@@ -374,10 +330,7 @@ namespace CommandSystem
|
||||
// destructor
|
||||
CommandRemoveNodeGroup::~CommandRemoveNodeGroup()
|
||||
{
|
||||
if (mOldNodeGroup)
|
||||
{
|
||||
mOldNodeGroup->Destroy();
|
||||
}
|
||||
delete mOldNodeGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -407,11 +360,7 @@ namespace CommandSystem
|
||||
}
|
||||
|
||||
// copy the old node group for undo
|
||||
if (mOldNodeGroup)
|
||||
{
|
||||
mOldNodeGroup->Destroy();
|
||||
}
|
||||
|
||||
delete mOldNodeGroup;
|
||||
mOldNodeGroup = aznew EMotionFX::NodeGroup(*nodeGroup);
|
||||
|
||||
// remove the node group
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
#pragma once
|
||||
|
||||
// include the required headers
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include "CommandSystemConfig.h"
|
||||
#include <MCore/Source/Command.h>
|
||||
#include <MCore/Source/CommandGroup.h>
|
||||
#include <EMotionFX/Source/EMotionFXConfig.h>
|
||||
#include <EMotionFX/Source/NodeGroup.h>
|
||||
#include <EMotionFX/CommandSystem/Source/ParameterMixins.h>
|
||||
|
||||
EMFX_FORWARD_DECLARE(Actor);
|
||||
EMFX_FORWARD_DECLARE(NodeGroup);
|
||||
@@ -20,20 +23,68 @@ EMFX_FORWARD_DECLARE(NodeGroup);
|
||||
namespace CommandSystem
|
||||
{
|
||||
// adjust a node group
|
||||
MCORE_DEFINECOMMAND_START(CommandAdjustNodeGroup, "Adjust node group", true)
|
||||
bool mOldDirtyFlag;
|
||||
EMotionFX::NodeGroup* mOldNodeGroup;
|
||||
MCORE_DEFINECOMMAND_END
|
||||
class CommandAdjustNodeGroup
|
||||
: public MCore::Command
|
||||
, public EMotionFX::ParameterMixinActorId
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
enum class NodeAction
|
||||
{
|
||||
Add,
|
||||
Remove,
|
||||
Replace
|
||||
};
|
||||
|
||||
static constexpr inline AZStd::string_view s_commandName = "AdjustNodeGroup";
|
||||
|
||||
CommandAdjustNodeGroup(
|
||||
MCore::Command* orgCommand = nullptr,
|
||||
uint32 actorId = MCORE_INVALIDINDEX32,
|
||||
const AZStd::string& name = {},
|
||||
AZStd::optional<AZStd::string> newName = AZStd::nullopt,
|
||||
AZStd::optional<bool> enabledOnDefault = AZStd::nullopt,
|
||||
AZStd::optional<AZStd::vector<AZStd::string>> nodeNames = AZStd::nullopt,
|
||||
AZStd::optional<NodeAction> nodeAction = AZStd::nullopt
|
||||
);
|
||||
bool Execute(const MCore::CommandLine& parameters, AZStd::string& outResult) override;
|
||||
bool Undo(const MCore::CommandLine& parameters, AZStd::string& outResult) override;
|
||||
void InitSyntax() override;
|
||||
bool SetCommandParameters(const MCore::CommandLine& parameters) override;
|
||||
bool GetIsUndoable() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const char* GetHistoryName() const override
|
||||
{
|
||||
return "Adjust node group";
|
||||
}
|
||||
const char* GetDescription() const override;
|
||||
MCore::Command* Create() override
|
||||
{
|
||||
return new CommandAdjustNodeGroup(this);
|
||||
}
|
||||
|
||||
private:
|
||||
AZStd::string m_name;
|
||||
AZStd::optional<AZStd::string> m_newName;
|
||||
AZStd::optional<bool> m_enabledOnDefault;
|
||||
AZStd::optional<AZStd::vector<AZStd::string>> m_nodeNames;
|
||||
AZStd::optional<NodeAction> m_nodeAction;
|
||||
|
||||
bool m_oldDirtyFlag = false;
|
||||
AZStd::unique_ptr<EMotionFX::NodeGroup> m_oldNodeGroup = nullptr;
|
||||
};
|
||||
|
||||
// add node group
|
||||
MCORE_DEFINECOMMAND_START(CommandAddNodeGroup, "Add node group", true)
|
||||
MCORE_DEFINECOMMAND_START(CommandAddNodeGroup, "Add node group", true)
|
||||
bool mOldDirtyFlag;
|
||||
MCORE_DEFINECOMMAND_END
|
||||
|
||||
|
||||
// remove a node group
|
||||
MCORE_DEFINECOMMAND_START(CommandRemoveNodeGroup, "Remove node group", true)
|
||||
MCORE_DEFINECOMMAND_START(CommandRemoveNodeGroup, "Remove node group", true)
|
||||
EMotionFX::NodeGroup * mOldNodeGroup;
|
||||
bool mOldDirtyFlag;
|
||||
MCORE_DEFINECOMMAND_END
|
||||
|
||||
@@ -80,6 +80,8 @@ namespace EMotionFX
|
||||
AZ_RTTI(ParameterMixinAnimGraphId, "{3F48199E-6566-471F-A7EA-ADF67CAC4DCD}")
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
ParameterMixinAnimGraphId() = default;
|
||||
ParameterMixinAnimGraphId(AZ::u32 id) : m_animGraphId(id) {}
|
||||
virtual ~ParameterMixinAnimGraphId() = default;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
@@ -1015,7 +1015,7 @@ namespace EMotionFX
|
||||
const uint32 numGroups = mNodeGroups.GetLength();
|
||||
for (uint32 i = 0; i < numGroups; ++i)
|
||||
{
|
||||
mNodeGroups[i]->Destroy();
|
||||
delete mNodeGroups[i];
|
||||
}
|
||||
mNodeGroups.Clear();
|
||||
}
|
||||
@@ -2085,7 +2085,7 @@ namespace EMotionFX
|
||||
{
|
||||
if (delFromMem)
|
||||
{
|
||||
mNodeGroups[index]->Destroy();
|
||||
delete mNodeGroups[index];
|
||||
}
|
||||
|
||||
mNodeGroups.Remove(index);
|
||||
@@ -2097,7 +2097,7 @@ namespace EMotionFX
|
||||
mNodeGroups.RemoveByValue(group);
|
||||
if (delFromMem)
|
||||
{
|
||||
group->Destroy();
|
||||
delete group;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1469,7 +1469,7 @@ namespace EMotionFX
|
||||
}
|
||||
|
||||
// create the new group inside the actor
|
||||
NodeGroup* newGroup = NodeGroup::Create(groupName, fileGroup.mNumNodes, fileGroup.mDisabledOnDefault ? false : true);
|
||||
NodeGroup* newGroup = aznew NodeGroup(groupName, fileGroup.mNumNodes, fileGroup.mDisabledOnDefault ? false : true);
|
||||
|
||||
// read the node numbers
|
||||
uint16 nodeIndex;
|
||||
|
||||
@@ -17,63 +17,16 @@ namespace EMotionFX
|
||||
AZ_CLASS_ALLOCATOR_IMPL(NodeGroup, NodeAllocator, 0)
|
||||
|
||||
|
||||
// default constructor
|
||||
NodeGroup::NodeGroup()
|
||||
: BaseObject()
|
||||
NodeGroup::NodeGroup(const AZStd::string& groupName, uint16 numNodes, bool enabledOnDefault)
|
||||
: mName(groupName)
|
||||
, mNodes(numNodes)
|
||||
, mEnabledOnDefault(enabledOnDefault)
|
||||
{
|
||||
SetIsEnabledOnDefault(true);
|
||||
}
|
||||
|
||||
|
||||
// extended constructor
|
||||
NodeGroup::NodeGroup(const char* groupName, bool enabledOnDefault)
|
||||
: BaseObject()
|
||||
{
|
||||
SetName(groupName);
|
||||
SetIsEnabledOnDefault(enabledOnDefault);
|
||||
}
|
||||
|
||||
|
||||
// another extended constructor
|
||||
NodeGroup::NodeGroup(const char* groupName, uint16 numNodes, bool enabledOnDefault)
|
||||
: BaseObject()
|
||||
{
|
||||
SetName(groupName);
|
||||
SetNumNodes(numNodes);
|
||||
SetIsEnabledOnDefault(enabledOnDefault);
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
NodeGroup::~NodeGroup()
|
||||
{
|
||||
mNodes.Clear();
|
||||
}
|
||||
|
||||
|
||||
// create
|
||||
NodeGroup* NodeGroup::Create()
|
||||
{
|
||||
return aznew NodeGroup();
|
||||
}
|
||||
|
||||
|
||||
// create
|
||||
NodeGroup* NodeGroup::Create(const char* groupName, bool enabledOnDefault)
|
||||
{
|
||||
return aznew NodeGroup(groupName, enabledOnDefault);
|
||||
}
|
||||
|
||||
|
||||
// create
|
||||
NodeGroup* NodeGroup::Create(const char* groupName, uint16 numNodes, bool enabledOnDefault)
|
||||
{
|
||||
return aznew NodeGroup(groupName, numNodes, enabledOnDefault);
|
||||
}
|
||||
|
||||
|
||||
// set the name of the group
|
||||
void NodeGroup::SetName(const char* groupName)
|
||||
void NodeGroup::SetName(const AZStd::string& groupName)
|
||||
{
|
||||
mName = groupName;
|
||||
}
|
||||
|
||||
@@ -30,38 +30,19 @@ namespace EMotionFX
|
||||
* might contain incorrect or even uninitialized data.
|
||||
*/
|
||||
class EMFX_API NodeGroup
|
||||
: public BaseObject
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
/**
|
||||
* The default creation method.
|
||||
* This does not assign a name and there will be nodes inside this group on default.
|
||||
* Also the default enabled state is set to true.
|
||||
*/
|
||||
static NodeGroup* Create();
|
||||
|
||||
/**
|
||||
* Extended creation.
|
||||
* @param groupName The name of the group. Please keep in mind that it is not allowed to have two groups with the same name inside an Actor.
|
||||
* @param enabledOnDefault Set to true (default) when the nodes inside this group should be enabled on default.
|
||||
*/
|
||||
static NodeGroup* Create(const char* groupName, bool enabledOnDefault = true);
|
||||
|
||||
/**
|
||||
* Another extended constructor.
|
||||
* @param groupName The name of the group. Please keep in mind that it is not allowed to have two groups with the same name inside an Actor.
|
||||
* @param numNodes The number of nodes to create inside the group. This will have all uninitialized values for the node indices in the group, so be sure that you
|
||||
* set them all to some valid node index using the NodeGroup::SetNode(...) method. This method automatically calls the SetNumNodes(...) method.
|
||||
* @param enabledOnDefault Set to true (default) when the nodes inside this group should be enabled on default.
|
||||
*/
|
||||
static NodeGroup* Create(const char* groupName, uint16 numNodes, bool enabledOnDefault = true);
|
||||
NodeGroup(const AZStd::string& groupName = {}, uint16 numNodes = 0, bool enabledOnDefault = true);
|
||||
NodeGroup(const NodeGroup& aOther);
|
||||
NodeGroup& operator=(const NodeGroup& aOther);
|
||||
|
||||
/**
|
||||
* Set the name of the group. Please keep in mind that group names must be unique inside the Actor objects. So you should not have two or more groups with the same name.
|
||||
* @param groupName The name of the group.
|
||||
*/
|
||||
void SetName(const char* groupName);
|
||||
void SetName(const AZStd::string& groupName);
|
||||
|
||||
/**
|
||||
* Get the name of the group as null terminated character buffer.
|
||||
@@ -172,37 +153,6 @@ namespace EMotionFX
|
||||
*/
|
||||
void SetIsEnabledOnDefault(bool enabledOnDefault);
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
* This does not assign a name and there will be nodes inside this group on default.
|
||||
* Also the default enabled state is set to true.
|
||||
*/
|
||||
NodeGroup();
|
||||
|
||||
/**
|
||||
* Extended constructor.
|
||||
* @param groupName The name of the group. Please keep in mind that it is not allowed to have two groups with the same name inside an Actor.
|
||||
* @param enabledOnDefault Set to true (default) when the nodes inside this group should be enabled on default.
|
||||
*/
|
||||
NodeGroup(const char* groupName, bool enabledOnDefault = true);
|
||||
|
||||
/**
|
||||
* Another extended constructor.
|
||||
* @param groupName The name of the group. Please keep in mind that it is not allowed to have two groups with the same name inside an Actor.
|
||||
* @param numNodes The number of nodes to create inside the group. This will have all uninitialized values for the node indices in the group, so be sure that you
|
||||
* set them all to some valid node index using the NodeGroup::SetNode(...) method. This method automatically calls the SetNumNodes(...) method.
|
||||
* @param enabledOnDefault Set to true (default) when the nodes inside this group should be enabled on default.
|
||||
*/
|
||||
NodeGroup(const char* groupName, uint16 numNodes, bool enabledOnDefault = true);
|
||||
|
||||
/**
|
||||
* The destructor.
|
||||
*/
|
||||
~NodeGroup();
|
||||
|
||||
NodeGroup(const NodeGroup& aOther);
|
||||
NodeGroup& operator=(const NodeGroup& aOther);
|
||||
|
||||
private:
|
||||
AZStd::string mName; /**< The name of the group. */
|
||||
MCore::SmallArray<uint16> mNodes; /**< The node index numbers that are inside this group. */
|
||||
|
||||
+23
-7
@@ -9,6 +9,7 @@
|
||||
#include <AzQtComponents/Utilities/Conversions.h>
|
||||
#include <EMotionFX/CommandSystem/Source/AnimGraphConnectionCommands.h>
|
||||
#include <EMotionFX/CommandSystem/Source/AnimGraphNodeCommands.h>
|
||||
#include <EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h>
|
||||
#include <EMotionFX/CommandSystem/Source/MotionSetCommands.h>
|
||||
#include <EMotionFX/Source/AnimGraphExitNode.h>
|
||||
#include <EMotionFX/Source/AnimGraphMotionNode.h>
|
||||
@@ -1208,7 +1209,7 @@ namespace EMStudio
|
||||
|
||||
MCore::CommandGroup commandGroup("Adjust anim graph node group");
|
||||
|
||||
AZStd::string nodeNames;
|
||||
AZStd::vector<AZStd::string> nodeNames;
|
||||
for (const QModelIndex& selectedIndex : selectionList)
|
||||
{
|
||||
// Skip transitions and blend tree connections.
|
||||
@@ -1221,12 +1222,19 @@ namespace EMStudio
|
||||
EMotionFX::AnimGraphNodeGroup* nodeGroup = animGraph->FindNodeGroupForNode(selectedNode);
|
||||
if (nodeGroup)
|
||||
{
|
||||
const AZStd::string command = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -nodeNames \"%s\" -nodeAction \"remove\"", animGraph->GetID(), nodeGroup->GetName(), selectedNode->GetName());
|
||||
commandGroup.AddCommandString(command);
|
||||
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
|
||||
/*animGraphId = */ animGraph->GetID(),
|
||||
/*name = */ nodeGroup->GetNameString(),
|
||||
/*visible = */ AZStd::nullopt,
|
||||
/*newName = */ AZStd::nullopt,
|
||||
/*nodeNames = */ {{selectedNode->GetNameString()}},
|
||||
/*nodeAction = */ CommandSystem::CommandAnimGraphAdjustNodeGroup::NodeAction::Remove
|
||||
);
|
||||
commandGroup.AddCommand(command);
|
||||
}
|
||||
|
||||
nodeNames += selectedNode->GetName();
|
||||
nodeNames += ";";
|
||||
nodeNames.emplace_back(selectedNode->GetName());
|
||||
}
|
||||
if (!nodeNames.empty())
|
||||
{
|
||||
@@ -1235,8 +1243,16 @@ namespace EMStudio
|
||||
|
||||
if (newNodeGroup)
|
||||
{
|
||||
const AZStd::string command = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -nodeNames \"%s\" -nodeAction \"add\"", animGraph->GetID(), newNodeGroup->GetName(), nodeNames.c_str());
|
||||
commandGroup.AddCommandString(command);
|
||||
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
|
||||
/*animGraphId = */ animGraph->GetID(),
|
||||
/*name = */ newNodeGroup->GetNameString(),
|
||||
/*visible = */ AZStd::nullopt,
|
||||
/*newName = */ AZStd::nullopt,
|
||||
/*nodeNames = */ nodeNames,
|
||||
/*nodeAction = */ CommandSystem::CommandAnimGraphAdjustNodeGroup::NodeAction::Add
|
||||
);
|
||||
commandGroup.AddCommand(command);
|
||||
}
|
||||
|
||||
AZStd::string outResult;
|
||||
|
||||
+28
-19
@@ -74,11 +74,6 @@ namespace EMStudio
|
||||
mLineEdit->setText(nodeGroup.c_str());
|
||||
mLineEdit->selectAll();
|
||||
|
||||
// create add the error message
|
||||
/*mErrorMsg = new QLabel("<font color='red'>Error: Duplicate name found</font>");
|
||||
mErrorMsg->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
|
||||
mErrorMsg->setVisible(false);*/
|
||||
|
||||
// create the button layout
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
||||
mOKButton = new QPushButton("OK");
|
||||
@@ -139,10 +134,16 @@ namespace EMStudio
|
||||
void NodeGroupRenameWindow::Accepted()
|
||||
{
|
||||
// Execute the command
|
||||
AZStd::string commandString, outResult;
|
||||
AZStd::string outResult;
|
||||
const AZStd::string convertedNewName = FromQtString(mLineEdit->text());
|
||||
commandString = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -newName \"%s\"", mAnimGraph->GetID(), mNodeGroup.c_str(), convertedNewName.c_str());
|
||||
if (GetCommandManager()->ExecuteCommand(commandString.c_str(), outResult) == false)
|
||||
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
|
||||
mAnimGraph->GetID(),
|
||||
/*name = */ mNodeGroup,
|
||||
/*visible = */ AZStd::nullopt,
|
||||
/*newName = */ convertedNewName
|
||||
);
|
||||
if (!GetCommandManager()->ExecuteCommand(command, outResult))
|
||||
{
|
||||
MCore::LogError(outResult.c_str());
|
||||
}
|
||||
@@ -167,7 +168,7 @@ namespace EMStudio
|
||||
mAdjustCallback = new CommandAnimGraphAdjustNodeGroupCallback(false);
|
||||
GetCommandManager()->RegisterCommandCallback("AnimGraphAddNodeGroup", mCreateCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("AnimGraphRemoveNodeGroup", mRemoveCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("AnimGraphAdjustNodeGroup", mAdjustCallback);
|
||||
GetCommandManager()->RegisterCommandCallback(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName.data(), mAdjustCallback);
|
||||
|
||||
// add the add button
|
||||
mAddAction = new QAction(MysticQt::GetMysticQt()->FindIcon("Images/Icons/Plus.svg"), tr("Add new node group"), this);
|
||||
@@ -486,13 +487,16 @@ namespace EMStudio
|
||||
|
||||
bool isVisible = state == Qt::Checked;
|
||||
|
||||
// construct the command
|
||||
AZStd::string commandString;
|
||||
commandString = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -isVisible %s", animGraph->GetID(), nodeGroup->GetName(), AZStd::to_string(isVisible).c_str());
|
||||
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
|
||||
/*animGraphId = */ animGraph->GetID(),
|
||||
/*name = */ nodeGroup->GetNameString(),
|
||||
/*visible = */ isVisible
|
||||
);
|
||||
|
||||
// execute the command
|
||||
AZStd::string resultString;
|
||||
if (GetCommandManager()->ExecuteCommand(commandString.c_str(), resultString) == false)
|
||||
if (GetCommandManager()->ExecuteCommand(command, resultString) == false)
|
||||
{
|
||||
if (resultString.size() > 0)
|
||||
{
|
||||
@@ -519,16 +523,21 @@ namespace EMStudio
|
||||
// get a pointer to the node group
|
||||
EMotionFX::AnimGraphNodeGroup* nodeGroup = animGraph->GetNodeGroup(groupIndex);
|
||||
|
||||
// get the color
|
||||
AZ::Vector4 finalColor = color.GetAsVector4();
|
||||
|
||||
// construct the command
|
||||
AZStd::string commandString;
|
||||
commandString = AZStd::string::format("AnimGraphAdjustNodeGroup -animGraphID %i -name \"%s\" -color \"%s\"", animGraph->GetID(), nodeGroup->GetName(), AZStd::to_string(finalColor).c_str());
|
||||
auto* command = aznew CommandSystem::CommandAnimGraphAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAnimGraphAdjustNodeGroup::s_commandName),
|
||||
/*animGraphId = */ animGraph->GetID(),
|
||||
/*name = */ nodeGroup->GetName(),
|
||||
/*visible = */ AZStd::nullopt,
|
||||
/*newName = */ AZStd::nullopt,
|
||||
/*nodeNames = */ AZStd::nullopt,
|
||||
/*nodeAction = */ AZStd::nullopt,
|
||||
/*color = */ color.ToU32()
|
||||
);
|
||||
|
||||
// execute the command
|
||||
AZStd::string resultString;
|
||||
if (GetCommandManager()->ExecuteCommand(commandString.c_str(), resultString) == false)
|
||||
if (GetCommandManager()->ExecuteCommand(command, resultString) == false)
|
||||
{
|
||||
if (resultString.size() > 0)
|
||||
{
|
||||
|
||||
+16
-99
@@ -112,8 +112,13 @@ namespace EMStudio
|
||||
|
||||
// execute the command
|
||||
AZStd::string outResult;
|
||||
AZStd::string command = AZStd::string::format("AdjustNodeGroup -actorID %i -name \"%s\" -newName \"%s\"", mActor->GetID(), mNodeGroupName.c_str(), convertedNewName.c_str());
|
||||
if (GetCommandManager()->ExecuteCommand(command.c_str(), outResult) == false)
|
||||
auto* command = aznew CommandSystem::CommandAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAdjustNodeGroup::s_commandName),
|
||||
/*actorId=*/ mActor->GetID(),
|
||||
/*name=*/ mNodeGroupName,
|
||||
/*newName=*/ convertedNewName
|
||||
);
|
||||
if (GetCommandManager()->ExecuteCommand(command, outResult) == false)
|
||||
{
|
||||
AZ_Error("EMotionFX", false, outResult.c_str());
|
||||
}
|
||||
@@ -362,98 +367,6 @@ namespace EMStudio
|
||||
mSelectedRow = MCORE_INVALIDINDEX32;
|
||||
}
|
||||
}
|
||||
/*void NodeGroupManagementWidget::UpdateNodeGroupWidget(QTableWidgetItem* current, QTableWidgetItem* previous)
|
||||
{
|
||||
MCORE_UNUSED(previous);
|
||||
|
||||
// return if no node group widget is set
|
||||
if (mNodeGroupWidget == nullptr)
|
||||
return;
|
||||
|
||||
// set the node group widget to the actual selection
|
||||
mNodeGroupWidget->SetActor( mActor );
|
||||
|
||||
if (current)
|
||||
{
|
||||
// set the current row
|
||||
mSelectedRow = current->row();
|
||||
|
||||
// set the node group
|
||||
NodeGroup* nodeGroup = mActor->FindNodeGroupByName( FromQtString(mNodeGroupsTable->item(current->row(), 1)->text()).c_str() );
|
||||
mNodeGroupWidget->SetNodeGroup( nodeGroup );
|
||||
}
|
||||
else
|
||||
{
|
||||
mNodeGroupWidget->SetNodeGroup( nullptr );
|
||||
mSelectedRow = MCORE_INVALIDINDEX32;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// called whenever a cell is changed
|
||||
/*void NodeGroupManagementWidget::NodeGroupNamesChanged(const QString& text)
|
||||
{
|
||||
// get the sender widget
|
||||
QWidget* senderWidget = (QWidget*)sender();
|
||||
|
||||
// check for duplicates
|
||||
const int duplicateFound = SearchTableForString( mNodeGroupsTable, text );
|
||||
|
||||
// mark edit field in red, if entry already exists
|
||||
if (duplicateFound >= 0)
|
||||
GetManager()->SetWidgetAsInvalidInput( senderWidget );
|
||||
else
|
||||
senderWidget->setStyleSheet("");
|
||||
}*/
|
||||
|
||||
|
||||
// starts editing
|
||||
/*void NodeGroupManagementWidget::NodeGroupeNameDoubleClicked(QTableWidgetItem* item)
|
||||
{
|
||||
// add new line edit for the selected widget
|
||||
QLineEdit* lineEdit = new QLineEdit( mNodeGroupsTable->item(item->row(), 0)->text() );
|
||||
mNodeGroupsTable->setCellWidget( item->row(), 0, lineEdit );
|
||||
|
||||
// jump into the edit field
|
||||
lineEdit->selectAll();
|
||||
lineEdit->setFocus();
|
||||
mNodeGroupsTable->setCurrentCell( item->row(), 0 );
|
||||
|
||||
// connect slots for edit finishing and text change
|
||||
connect( lineEdit, SIGNAL(editingFinished()), this, SLOT(NodeGroupNameEditingFinished()) );
|
||||
connect( lineEdit, SIGNAL(textChanged(QString)), this, SLOT(NodeGroupNamesChanged(QString)) );
|
||||
}*/
|
||||
|
||||
|
||||
// called when editing is finished
|
||||
/*void NodeGroupManagementWidget::NodeGroupNameEditingFinished()
|
||||
{
|
||||
// get the current item
|
||||
QTableWidgetItem* item = mNodeGroupsTable->currentItem();
|
||||
|
||||
// get the sender widget
|
||||
QLineEdit* senderWidget = (QLineEdit*)sender();
|
||||
|
||||
// return if one of the widgets does not exist
|
||||
if (item == nullptr || senderWidget == nullptr)
|
||||
return;
|
||||
|
||||
// call commands for name change if name does not exist yet
|
||||
if (senderWidget->styleSheet() == "")
|
||||
{
|
||||
// call command for adding a new node group
|
||||
String outResult;
|
||||
String command;
|
||||
command.Format( "AdjustNodeGroup -actorID %i -name \"%s\" -newName \"%s\"", mActor->GetID(), FromQtString(item->text()).c_str(), FromQtString(senderWidget->text()).c_str() );
|
||||
if (EMStudio::GetCommandManager()->ExecuteCommand( command.c_str(), outResult ) == false)
|
||||
LogError( outResult.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete the line edit
|
||||
mNodeGroupsTable->setCellWidget(item->row(), item->column(), nullptr);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// function to add a new node group with the specified name
|
||||
@@ -562,16 +475,20 @@ namespace EMStudio
|
||||
if (rowChechbox == senderCheckbox)
|
||||
{
|
||||
nodeGroupName = mNodeGroupsTable->item(i, 1)->text().toUtf8().data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// execute the command
|
||||
AZStd::string outResult;
|
||||
const AZStd::string command = AZStd::string::format("AdjustNodeGroup -actorID %i -name \"%s\" -enabledOnDefault \"%s\"",
|
||||
mActor->GetID(),
|
||||
nodeGroupName.c_str(),
|
||||
AZStd::to_string(checked).c_str());
|
||||
if (EMStudio::GetCommandManager()->ExecuteCommand(command.c_str(), outResult) == false)
|
||||
auto* command = aznew CommandSystem::CommandAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAdjustNodeGroup::s_commandName),
|
||||
/*actorId=*/ mActor->GetID(),
|
||||
/*name=*/ nodeGroupName,
|
||||
/*newName=*/ AZStd::nullopt,
|
||||
/*enabledOnDefault=*/ checked
|
||||
);
|
||||
if (EMStudio::GetCommandManager()->ExecuteCommand(command, outResult) == false)
|
||||
{
|
||||
AZ_Error("EMotionFX", false, outResult.c_str());
|
||||
}
|
||||
|
||||
+35
-41
@@ -35,7 +35,6 @@ namespace EMStudio
|
||||
mNodeTable = nullptr;
|
||||
mSelectNodesButton = nullptr;
|
||||
mNodeGroup = nullptr;
|
||||
mNodeAction = "";
|
||||
|
||||
// init the widget
|
||||
Init();
|
||||
@@ -254,11 +253,11 @@ namespace EMStudio
|
||||
QWidget* senderWidget = (QWidget*)sender();
|
||||
if (senderWidget == mAddNodesButton)
|
||||
{
|
||||
mNodeAction = "add";
|
||||
mNodeAction = CommandSystem::CommandAdjustNodeGroup::NodeAction::Add;
|
||||
}
|
||||
else
|
||||
{
|
||||
mNodeAction = "select";
|
||||
mNodeAction = CommandSystem::CommandAdjustNodeGroup::NodeAction::Replace;
|
||||
}
|
||||
|
||||
// get the selected actorinstance
|
||||
@@ -293,46 +292,37 @@ namespace EMStudio
|
||||
// remove nodes
|
||||
void NodeGroupWidget::RemoveNodesButtonPressed()
|
||||
{
|
||||
// generate node list string
|
||||
AZStd::string nodeList;
|
||||
uint32 lowestSelectedRow = MCORE_INVALIDINDEX32;
|
||||
const uint32 numTableRows = mNodeTable->rowCount();
|
||||
for (uint32 i = 0; i < numTableRows; ++i)
|
||||
{
|
||||
// get the current table item
|
||||
QTableWidgetItem* item = mNodeTable->item(i, 0);
|
||||
if (item == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the item to remove list, if it's selected
|
||||
if (item->isSelected())
|
||||
{
|
||||
nodeList += AZStd::string::format("%s;", item->text().toUtf8().data());
|
||||
if ((uint32)item->row() < lowestSelectedRow)
|
||||
{
|
||||
lowestSelectedRow = (uint32)item->row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop here if nothing selected
|
||||
if (nodeList.empty())
|
||||
if (mNodeTable->selectedItems().empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// call command for adjusting disable on default flag
|
||||
// generate node list string
|
||||
AZStd::vector<AZStd::string> nodeList;
|
||||
int lowestSelectedRow = AZStd::numeric_limits<int>::max();
|
||||
for (const QTableWidgetItem* item : mNodeTable->selectedItems())
|
||||
{
|
||||
nodeList.emplace_back(FromQtString(item->text()));
|
||||
lowestSelectedRow = AZStd::min(lowestSelectedRow, item->row());
|
||||
}
|
||||
|
||||
AZStd::string outResult;
|
||||
AZStd::string command = AZStd::string::format("AdjustNodeGroup -actorID %i -name \"%s\" -nodeAction \"remove\" -nodeNames \"%s\"", mActor->GetID(), mNodeGroup->GetName(), nodeList.c_str());
|
||||
auto* command = aznew CommandSystem::CommandAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAdjustNodeGroup::s_commandName),
|
||||
/*actorId=*/ mActor->GetID(),
|
||||
/*name=*/ mNodeGroup->GetName(),
|
||||
/*newName=*/ AZStd::nullopt,
|
||||
/*enabledOnDefault=*/ AZStd::nullopt,
|
||||
/*nodeNames=*/ AZStd::move(nodeList),
|
||||
/*nodeAction=*/ CommandSystem::CommandAdjustNodeGroup::NodeAction::Remove
|
||||
);
|
||||
if (EMStudio::GetCommandManager()->ExecuteCommand(command, outResult) == false)
|
||||
{
|
||||
AZ_Error("EMotionFX", false, outResult.c_str());
|
||||
}
|
||||
|
||||
// selected the next row
|
||||
if (lowestSelectedRow > ((uint32)mNodeTable->rowCount() - 1))
|
||||
if (lowestSelectedRow > (mNodeTable->rowCount() - 1))
|
||||
{
|
||||
mNodeTable->selectRow(lowestSelectedRow - 1);
|
||||
}
|
||||
@@ -353,19 +343,23 @@ namespace EMStudio
|
||||
}
|
||||
|
||||
// generate node list string
|
||||
AZStd::string nodeList;
|
||||
nodeList.reserve(16448);
|
||||
const uint32 numSelectedNodes = selectionList.GetLength();
|
||||
for (uint32 i = 0; i < numSelectedNodes; ++i)
|
||||
AZStd::vector<AZStd::string> nodeList;
|
||||
const uint32 selectionListSize = selectionList.GetLength();
|
||||
for (uint32 i = 0; i < selectionListSize; ++i)
|
||||
{
|
||||
nodeList += selectionList[i].GetNodeName();
|
||||
nodeList += ";";
|
||||
nodeList.emplace_back(selectionList[i].GetNodeName());
|
||||
}
|
||||
AzFramework::StringFunc::Strip(nodeList, MCore::CharacterConstants::semiColon, true /* case sensitive */, false /* beginning */, true /* ending */);
|
||||
|
||||
// call command for adjusting disable on default flag
|
||||
AZStd::string outResult;
|
||||
AZStd::string command = AZStd::string::format("AdjustNodeGroup -actorID %i -name \"%s\" -nodeAction \"%s\" -nodeNames \"%s\"", mActor->GetID(), mNodeGroup->GetName(), mNodeAction.c_str(), nodeList.c_str());
|
||||
auto* command = aznew CommandSystem::CommandAdjustNodeGroup(
|
||||
GetCommandManager()->FindCommand(CommandSystem::CommandAdjustNodeGroup::s_commandName),
|
||||
/*actorId=*/ mActor->GetID(),
|
||||
/*name=*/ mNodeGroup->GetName(),
|
||||
/*newName=*/ AZStd::nullopt,
|
||||
/*enabledOnDefault=*/ AZStd::nullopt,
|
||||
/*nodeNames=*/ AZStd::move(nodeList),
|
||||
/*nodeAction=*/ mNodeAction
|
||||
);
|
||||
if (EMStudio::GetCommandManager()->ExecuteCommand(command, outResult) == false)
|
||||
{
|
||||
AZ_Error("EMotionFX", false, outResult.c_str());
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@
|
||||
#include <MysticQt/Source/DialogStack.h>
|
||||
#include "../../../../EMStudioSDK/Source/DockWidgetPlugin.h"
|
||||
#include "../../../../EMStudioSDK/Source/NodeSelectionWindow.h"
|
||||
#include <EMotionFX/CommandSystem/Source/NodeGroupCommands.h>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLineEdit)
|
||||
@@ -58,7 +59,7 @@ namespace EMStudio
|
||||
CommandSystem::SelectionList mNodeSelectionList;
|
||||
EMotionFX::NodeGroup* mNodeGroup;
|
||||
uint16 mNodeGroupIndex;
|
||||
AZStd::string mNodeAction;
|
||||
CommandSystem::CommandAdjustNodeGroup::NodeAction mNodeAction;
|
||||
|
||||
// widgets
|
||||
QTableWidget* mNodeTable;
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@
|
||||
#include "../../../../EMStudioSDK/Source/EMStudioCore.h"
|
||||
#include <MCore/Source/LogManager.h>
|
||||
#include <EMotionFX/CommandSystem/Source/CommandManager.h>
|
||||
#include <EMotionFX/CommandSystem/Source/NodeGroupCommands.h>
|
||||
#include "../../../../EMStudioSDK/Source/EMStudioManager.h"
|
||||
|
||||
// include qt headers
|
||||
@@ -99,7 +100,7 @@ namespace EMStudio
|
||||
GetCommandManager()->RegisterCommandCallback("Select", mSelectCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("Unselect", mUnselectCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("ClearSelection", mClearSelectionCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("AdjustNodeGroup", mAdjustNodeGroupCallback);
|
||||
GetCommandManager()->RegisterCommandCallback(CommandSystem::CommandAdjustNodeGroup::s_commandName.data(), mAdjustNodeGroupCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("AddNodeGroup", mAddNodeGroupCallback);
|
||||
GetCommandManager()->RegisterCommandCallback("RemoveNodeGroup", mRemoveNodeGroupCallback);
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace MCore
|
||||
|
||||
|
||||
// constructor
|
||||
Command::Command(const char* commandName, Command* originalCommand)
|
||||
Command::Command(AZStd::string commandName, Command* originalCommand)
|
||||
: mOrgCommand(originalCommand)
|
||||
, mCommandName(AZStd::move(commandName))
|
||||
{
|
||||
mCommandName = commandName;
|
||||
mOrgCommand = originalCommand;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace MCore
|
||||
* @param commandName The unique identifier for the command.
|
||||
* @param originalCommand The original command, or nullptr when this is the original command.
|
||||
*/
|
||||
Command(const char* commandName, Command* originalCommand);
|
||||
Command(AZStd::string commandName, Command* originalCommand);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "LyShineParentTemplate",
|
||||
"PassClass": "LyShinePass",
|
||||
"Slots": [
|
||||
{
|
||||
"Name": "ColorInputOutput",
|
||||
"SlotType": "InputOutput"
|
||||
},
|
||||
{
|
||||
"Name": "DepthInputOutput",
|
||||
"SlotType": "InputOutput",
|
||||
"ScopeAttachmentUsage": "DepthStencil"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "AssetAliasesSourceData",
|
||||
"ClassData": {
|
||||
"AssetPaths": [
|
||||
{
|
||||
"Name": "LyShineParentTemplate",
|
||||
"Path": "Passes/LyShineParent.pass"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -193,6 +193,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
FILES_CMAKE
|
||||
lyshine_common_module_files.cmake
|
||||
lyshine_tests_files.cmake
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
LYSHINE_TESTS
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
|
||||
@@ -1547,6 +1547,20 @@ AssetTreeEntry* EditorWindow::GetSliceLibraryTree()
|
||||
return m_sliceLibraryTree;
|
||||
}
|
||||
|
||||
AZ::EntityId EditorWindow::GetCanvasForCurrentEditorMode()
|
||||
{
|
||||
AZ::EntityId canvasEntityId;
|
||||
if (GetEditorMode() == UiEditorMode::Edit)
|
||||
{
|
||||
canvasEntityId = GetCanvas();
|
||||
}
|
||||
else
|
||||
{
|
||||
canvasEntityId = GetPreviewModeCanvas();
|
||||
}
|
||||
return canvasEntityId;
|
||||
}
|
||||
|
||||
void EditorWindow::ToggleEditorMode()
|
||||
{
|
||||
m_editorMode = (m_editorMode == UiEditorMode::Edit) ? UiEditorMode::Preview : UiEditorMode::Edit;
|
||||
|
||||
@@ -143,6 +143,9 @@ public: // member functions
|
||||
//! Returns the current mode of the editor (Edit or Preview)
|
||||
UiEditorMode GetEditorMode() { return m_editorMode; }
|
||||
|
||||
//! Returns the UI canvas for the current mode (Edit or Preview)
|
||||
AZ::EntityId GetCanvasForCurrentEditorMode();
|
||||
|
||||
//! Toggle the editor mode between Edit and Preview
|
||||
void ToggleEditorMode();
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
#include "EditorCommon.h"
|
||||
|
||||
#include "UiCanvasComponent.h"
|
||||
|
||||
#include "EditorDefs.h"
|
||||
#include "Settings.h"
|
||||
#include <AzCore/std/containers/map.h>
|
||||
@@ -245,6 +247,7 @@ ViewportWidget::ViewportWidget(EditorWindow* parent)
|
||||
|
||||
FontNotificationBus::Handler::BusConnect();
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
AZ::RPI::ViewportContextNotificationBus::Handler::BusConnect(GetCurrentContextName());
|
||||
}
|
||||
|
||||
ViewportWidget::~ViewportWidget()
|
||||
@@ -252,6 +255,8 @@ ViewportWidget::~ViewportWidget()
|
||||
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
|
||||
FontNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
LyShinePassDataRequestBus::Handler::BusDisconnect();
|
||||
AZ::RPI::ViewportContextNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
m_uiRenderer.reset();
|
||||
|
||||
@@ -272,6 +277,8 @@ void ViewportWidget::InitUiRenderer()
|
||||
lyShine->SetUiRendererForEditor(m_uiRenderer);
|
||||
|
||||
m_draw2d = AZStd::make_shared<CDraw2d>(GetViewportContext());
|
||||
|
||||
LyShinePassDataRequestBus::Handler::BusConnect(GetViewportContext()->GetRenderScene()->GetId());
|
||||
}
|
||||
|
||||
ViewportInteraction* ViewportWidget::GetViewportInteraction()
|
||||
@@ -487,30 +494,44 @@ void ViewportWidget::EnableCanvasRender()
|
||||
}
|
||||
|
||||
void ViewportWidget::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
// Update
|
||||
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
||||
if (editorMode == UiEditorMode::Edit)
|
||||
{
|
||||
UpdateEditMode(deltaTime);
|
||||
}
|
||||
else // if (editorMode == UiEditorMode::Preview)
|
||||
{
|
||||
UpdatePreviewMode(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
int ViewportWidget::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_PRE_RENDER;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void ViewportWidget::OnRenderTick()
|
||||
{
|
||||
if (!m_uiRenderer->IsReady() || !m_canvasRenderIsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO
|
||||
gEnv->pRenderer->SetSrgbWrite(true);
|
||||
#endif
|
||||
|
||||
const float dpiScale = QtHelpers::GetHighDpiScaleFactor(*this);
|
||||
ViewportIcon::SetDpiScaleFactor(dpiScale);
|
||||
|
||||
// Set up to render a frame to this viewport's window
|
||||
GetViewportContext()->RenderTick();
|
||||
|
||||
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
||||
if (editorMode == UiEditorMode::Edit)
|
||||
{
|
||||
RenderEditMode(deltaTime);
|
||||
RenderEditMode();
|
||||
}
|
||||
else // if (editorMode == UiEditorMode::Preview)
|
||||
{
|
||||
RenderPreviewMode(deltaTime);
|
||||
RenderPreviewMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -884,17 +905,37 @@ void ViewportWidget::OnFontTextureUpdated([[maybe_unused]] IFFont* font)
|
||||
m_fontTextureHasChanged = true;
|
||||
}
|
||||
|
||||
LyShine::AttachmentImagesAndDependencies ViewportWidget::GetRenderTargets()
|
||||
{
|
||||
LyShine::AttachmentImagesAndDependencies canvasTargets;
|
||||
|
||||
AZ::EntityId canvasEntityId = m_editorWindow->GetCanvasForCurrentEditorMode();
|
||||
if (canvasEntityId.IsValid())
|
||||
{
|
||||
AZ::Entity* canvasEntity = nullptr;
|
||||
EBUS_EVENT_RESULT(canvasEntity, AZ::ComponentApplicationBus, FindEntity, canvasEntityId);
|
||||
AZ_Assert(canvasEntity, "Canvas entity not found by ID");
|
||||
if (canvasEntity)
|
||||
{
|
||||
UiCanvasComponent* canvasComponent = canvasEntity->FindComponent<UiCanvasComponent>();
|
||||
AZ_Assert(canvasComponent, "Canvas entity has no canvas component");
|
||||
if (canvasComponent)
|
||||
{
|
||||
canvasComponent->GetRenderTargets(canvasTargets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return canvasTargets;
|
||||
}
|
||||
|
||||
QPointF ViewportWidget::WidgetToViewport(const QPointF & point) const
|
||||
{
|
||||
return point * WidgetToViewportFactor();
|
||||
}
|
||||
|
||||
void ViewportWidget::RenderEditMode(float deltaTime)
|
||||
void ViewportWidget::UpdateEditMode(float deltaTime)
|
||||
{
|
||||
// sort keys for different layers
|
||||
static const int64_t backgroundKey = -0x1000;
|
||||
static const int64_t topLayerKey = 0x1000000;
|
||||
|
||||
if (m_fontTextureHasChanged)
|
||||
{
|
||||
// A font texture has changed since we last rendered. Force a render graph update for each loaded canvas
|
||||
@@ -908,6 +949,28 @@ void ViewportWidget::RenderEditMode(float deltaTime)
|
||||
return; // this can happen if a render happens during a restart
|
||||
}
|
||||
|
||||
AZ::Vector2 canvasSize;
|
||||
EBUS_EVENT_ID_RESULT(canvasSize, canvasEntityId, UiCanvasBus, GetCanvasSize);
|
||||
|
||||
// Set the target size of the canvas
|
||||
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, false, canvasSize);
|
||||
|
||||
// Update this canvas (must be done after SetTargetCanvasSize)
|
||||
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, false);
|
||||
}
|
||||
|
||||
void ViewportWidget::RenderEditMode()
|
||||
{
|
||||
// sort keys for different layers
|
||||
static const int64_t backgroundKey = -0x1000;
|
||||
static const int64_t topLayerKey = 0x1000000;
|
||||
|
||||
AZ::EntityId canvasEntityId = m_editorWindow->GetCanvas();
|
||||
if (!canvasEntityId.IsValid())
|
||||
{
|
||||
return; // this can happen if a render happens during a restart
|
||||
}
|
||||
|
||||
Draw2dHelper draw2d(m_draw2d.get()); // sets and resets 2D draw mode in constructor/destructor
|
||||
|
||||
QTreeWidgetItemRawPtrQList selection = m_editorWindow->GetHierarchy()->selectedItems();
|
||||
@@ -936,9 +999,6 @@ void ViewportWidget::RenderEditMode(float deltaTime)
|
||||
// Set the target size of the canvas
|
||||
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, false, canvasSize);
|
||||
|
||||
// Update this canvas (must be done after SetTargetCanvasSize)
|
||||
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, false);
|
||||
|
||||
// Render this canvas
|
||||
QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this);
|
||||
AZ::Vector2 viewportSize(scaledViewportSize.width(), scaledViewportSize.height());
|
||||
@@ -1037,11 +1097,8 @@ void ViewportWidget::RenderEditMode(float deltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportWidget::RenderPreviewMode(float deltaTime)
|
||||
void ViewportWidget::UpdatePreviewMode(float deltaTime)
|
||||
{
|
||||
// sort keys for different layers
|
||||
static const int64_t backgroundKey = -0x1000;
|
||||
|
||||
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
||||
|
||||
if (m_fontTextureHasChanged)
|
||||
@@ -1051,6 +1108,37 @@ void ViewportWidget::RenderPreviewMode(float deltaTime)
|
||||
m_fontTextureHasChanged = false;
|
||||
}
|
||||
|
||||
if (canvasEntityId.IsValid())
|
||||
{
|
||||
QSize scaledViewportSize = QtHelpers::GetDpiScaledViewportSize(*this);
|
||||
AZ::Vector2 viewportSize(scaledViewportSize.width(), scaledViewportSize.height());
|
||||
|
||||
// Get the canvas size
|
||||
AZ::Vector2 canvasSize = m_editorWindow->GetPreviewCanvasSize();
|
||||
if (canvasSize.GetX() == 0.0f && canvasSize.GetY() == 0.0f)
|
||||
{
|
||||
// special value of (0,0) means use the viewport size
|
||||
canvasSize = viewportSize;
|
||||
}
|
||||
|
||||
// Set the target size of the canvas
|
||||
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, true, canvasSize);
|
||||
|
||||
// Update this canvas (must be done after SetTargetCanvasSize)
|
||||
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, true);
|
||||
|
||||
// Execute events that have been queued during the canvas update
|
||||
gEnv->pLyShine->ExecuteQueuedEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportWidget::RenderPreviewMode()
|
||||
{
|
||||
// sort keys for different layers
|
||||
static const int64_t backgroundKey = -0x1000;
|
||||
|
||||
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
||||
|
||||
// Rather than scaling to exactly fit we try to draw at one of these preset scale factors
|
||||
// to make it it bit more obvious that the canvas size is changing
|
||||
float zoomScales[] = {
|
||||
@@ -1096,15 +1184,6 @@ void ViewportWidget::RenderPreviewMode(float deltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the target size of the canvas
|
||||
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, true, canvasSize);
|
||||
|
||||
// Update this canvas (must be done after SetTargetCanvasSize)
|
||||
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, true);
|
||||
|
||||
// Execute events that have been queued during the canvas update
|
||||
gEnv->pLyShine->ExecuteQueuedEvents();
|
||||
|
||||
// match scale to one of the predefined scales. If the scale is so small
|
||||
// that it is less than the smallest scale then leave it as it is
|
||||
for (int i = 0; i < AZ_ARRAY_SIZE(zoomScales); ++i)
|
||||
@@ -1131,14 +1210,6 @@ void ViewportWidget::RenderPreviewMode(float deltaTime)
|
||||
canvasToViewportMatrix.SetTranslation(translation);
|
||||
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetCanvasToViewportMatrix, canvasToViewportMatrix);
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // mask support with Atom
|
||||
// clear the stencil buffer before rendering each canvas - required for masking
|
||||
// NOTE: the FRT_CLEAR_IMMEDIATE is required since we will not be setting the render target
|
||||
// We also clear the color to a mid grey so that we can see the bounds of the canvas
|
||||
ColorF viewportBackgroundColor(0.5f, 0.5f, 0.5f, 0); // if clearing color we want to set alpha to zero also
|
||||
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR, viewportBackgroundColor);
|
||||
#endif
|
||||
|
||||
m_draw2d->SetSortKey(backgroundKey);
|
||||
|
||||
RenderViewportBackground();
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "EditorCommon.h"
|
||||
#include "LyShinePassDataBus.h"
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
|
||||
#include <Atom/RPI.Public/ViewportContextBus.h>
|
||||
|
||||
#include <IFont.h>
|
||||
|
||||
@@ -27,6 +29,8 @@ class ViewportWidget
|
||||
: public AtomToolsFramework::RenderViewportWidget
|
||||
, private AzToolsFramework::EditorPickModeNotificationBus::Handler
|
||||
, private FontNotificationBus::Handler
|
||||
, private LyShinePassDataRequestBus::Handler
|
||||
, public AZ::RPI::ViewportContextNotificationBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -138,15 +142,29 @@ private: // member functions
|
||||
void OnFontTextureUpdated(IFFont* font) override;
|
||||
// ~FontNotifications
|
||||
|
||||
// LyShinePassDataRequestBus
|
||||
LyShine::AttachmentImagesAndDependencies GetRenderTargets() override;
|
||||
// ~LyShinePassDataRequestBus
|
||||
|
||||
// AZ::TickBus::Handler
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
// ~AZ::TickBus::Handler
|
||||
|
||||
// AZ::RPI::ViewportContextNotificationBus::Handler overrides...
|
||||
void OnRenderTick() override;
|
||||
|
||||
//! Update UI canvases when in edit mode
|
||||
void UpdateEditMode(float deltaTime);
|
||||
|
||||
//! Render the viewport when in edit mode
|
||||
void RenderEditMode(float deltaTime);
|
||||
void RenderEditMode();
|
||||
|
||||
//! Update UI canvases when in preview mode
|
||||
void UpdatePreviewMode(float deltaTime);
|
||||
|
||||
//! Render the viewport when in preview mode
|
||||
void RenderPreviewMode(float deltaTime);
|
||||
void RenderPreviewMode();
|
||||
|
||||
//! Fill the entire viewport area with a background color
|
||||
void RenderViewportBackground();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <IRenderer.h> // for SVF_P3F_C4B_T2F which will be removed in a coming PR
|
||||
|
||||
#include <LyShine/Draw2d.h>
|
||||
#include "LyShinePassDataBus.h"
|
||||
|
||||
#include <AzCore/Math/Matrix3x3.h>
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
@@ -95,6 +96,12 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc
|
||||
AZ_Assert(scene != nullptr, "Attempting to create a DynamicDrawContext for a viewport context that has not been associated with a scene yet.");
|
||||
|
||||
// Create and initialize a DynamicDrawContext for 2d drawing
|
||||
|
||||
// Get the pass for the dynamic draw context to render to
|
||||
AZ::RPI::RasterPass* uiCanvasPass = nullptr;
|
||||
AZ::RPI::SceneId sceneId = scene->GetId();
|
||||
LyShinePassRequestBus::EventResult(uiCanvasPass, sceneId, &LyShinePassRequestBus::Events::GetUiCanvasPass);
|
||||
|
||||
m_dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
|
||||
AZ::RPI::ShaderOptionList shaderOptions;
|
||||
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true")));
|
||||
@@ -106,7 +113,15 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc
|
||||
{"TEXCOORD0", AZ::RHI::Format::R32G32_FLOAT} });
|
||||
m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::PrimitiveType
|
||||
| AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode);
|
||||
m_dynamicDraw->SetOutputScope(scene.get());
|
||||
if (uiCanvasPass)
|
||||
{
|
||||
m_dynamicDraw->SetOutputScope(uiCanvasPass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Render target support is disabled
|
||||
m_dynamicDraw->SetOutputScope(scene.get());
|
||||
}
|
||||
m_dynamicDraw->EndInit();
|
||||
|
||||
AZ::RHI::TargetBlendState targetBlendState;
|
||||
@@ -491,6 +506,7 @@ bool CDraw2d::GetDeferPrimitives()
|
||||
return m_deferCalls;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void CDraw2d::SetSortKey(int64_t key)
|
||||
{
|
||||
m_dynamicDraw->SetSortKey(key);
|
||||
|
||||
@@ -163,6 +163,8 @@ CLyShine::CLyShine(ISystem* system)
|
||||
AzFramework::InputTextEventListener::Connect();
|
||||
UiCursorBus::Handler::BusConnect();
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
AZ::RPI::ViewportContextNotificationBus::Handler::BusConnect(
|
||||
AZ::RPI::ViewportContextRequests::Get()->GetDefaultViewportContextName());
|
||||
AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
|
||||
|
||||
// These are internal Amazon components, so register them so that we can send back their names to our metrics collection
|
||||
@@ -240,9 +242,11 @@ CLyShine::~CLyShine()
|
||||
{
|
||||
UiCursorBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AZ::RPI::ViewportContextNotificationBus::Handler::BusDisconnect();
|
||||
AzFramework::InputTextEventListener::Disconnect();
|
||||
AzFramework::InputChannelEventListener::Disconnect();
|
||||
AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect();
|
||||
LyShinePassDataRequestBus::Handler::BusDisconnect();
|
||||
|
||||
UiCanvasComponent::Shutdown();
|
||||
|
||||
@@ -642,15 +646,19 @@ void CLyShine::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time
|
||||
{
|
||||
// Update the loaded UI canvases
|
||||
Update(deltaTime);
|
||||
|
||||
// Recreate dirty render graphs and send primitive data to the dynamic draw context
|
||||
Render();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
int CLyShine::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_UI;
|
||||
return AZ::TICK_PRE_RENDER;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void CLyShine::OnRenderTick()
|
||||
{
|
||||
// Recreate dirty render graphs and send primitive data to the dynamic draw context
|
||||
Render();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -658,6 +666,16 @@ void CLyShine::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapS
|
||||
{
|
||||
// Load cursor if its path was set before RPI was initialized
|
||||
LoadUiCursor();
|
||||
|
||||
LyShinePassDataRequestBus::Handler::BusConnect(AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->GetId());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
LyShine::AttachmentImagesAndDependencies CLyShine::GetRenderTargets()
|
||||
{
|
||||
LyShine::AttachmentImagesAndDependencies attachmentImagesAndDependencies;
|
||||
m_uiCanvasManager->GetRenderTargets(attachmentImagesAndDependencies);
|
||||
return attachmentImagesAndDependencies;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
#include <AzFramework/Input/Events/InputTextEventListener.h>
|
||||
|
||||
#include <Atom/Bootstrap/BootstrapNotificationBus.h>
|
||||
#include <Atom/RPI.Public/ViewportContextBus.h>
|
||||
#include <Atom/RPI.Reflect/Image/Image.h>
|
||||
|
||||
#include "LyShinePassDataBus.h"
|
||||
|
||||
#if !defined(_RELEASE)
|
||||
#define LYSHINE_INTERNAL_UNIT_TEST
|
||||
#endif
|
||||
@@ -40,7 +43,9 @@ class CLyShine
|
||||
, public AzFramework::InputChannelEventListener
|
||||
, public AzFramework::InputTextEventListener
|
||||
, public AZ::TickBus::Handler
|
||||
, public AZ::RPI::ViewportContextNotificationBus::Handler
|
||||
, protected AZ::Render::Bootstrap::NotificationBus::Handler
|
||||
, protected LyShinePassDataRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -111,10 +116,17 @@ public:
|
||||
int GetTickOrder() override;
|
||||
// ~TickEvents
|
||||
|
||||
// AZ::RPI::ViewportContextNotificationBus::Handler overrides...
|
||||
void OnRenderTick() override;
|
||||
|
||||
// AZ::Render::Bootstrap::NotificationBus
|
||||
void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override;
|
||||
// ~AZ::Render::Bootstrap::NotificationBus
|
||||
|
||||
// LyShinePassDataRequestBus
|
||||
LyShine::AttachmentImagesAndDependencies GetRenderTargets() override;
|
||||
// ~LyShinePassDataRequestBus
|
||||
|
||||
// Get the UIRenderer for the game (which is owned by CLyShine). This is not exposed outside the gem.
|
||||
UiRenderer* GetUiRenderer();
|
||||
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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 <AzCore/Debug/Trace.h>
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/PassAttachment.h>
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Reflect/Pass/RasterPassData.h>
|
||||
#include <AzCore/std/iterator.h>
|
||||
|
||||
#include "LyShinePass.h"
|
||||
|
||||
namespace LyShine
|
||||
{
|
||||
AZ::RPI::Ptr<LyShinePass> LyShinePass::Create(const AZ::RPI::PassDescriptor& descriptor)
|
||||
{
|
||||
return aznew LyShinePass(descriptor);
|
||||
}
|
||||
|
||||
LyShinePass::LyShinePass(const AZ::RPI::PassDescriptor& descriptor)
|
||||
: Base(descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
LyShinePass::~LyShinePass()
|
||||
{
|
||||
LyShinePassRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void LyShinePass::ResetInternal()
|
||||
{
|
||||
LyShinePassRequestBus::Handler::BusDisconnect();
|
||||
|
||||
Base::ResetInternal();
|
||||
}
|
||||
|
||||
void LyShinePass::BuildInternal()
|
||||
{
|
||||
AZ::RPI::Scene* scene = GetScene();
|
||||
if (scene)
|
||||
{
|
||||
// Listen for rebuild requests
|
||||
LyShinePassRequestBus::Handler::BusConnect(scene->GetId());
|
||||
|
||||
RemoveChildren();
|
||||
|
||||
// Get the current list of render targets being used across all loaded UI Canvases
|
||||
LyShine::AttachmentImagesAndDependencies attachmentImagesAndDependencies;
|
||||
LyShinePassDataRequestBus::EventResult(
|
||||
attachmentImagesAndDependencies,
|
||||
scene->GetId(),
|
||||
&LyShinePassDataRequestBus::Events::GetRenderTargets
|
||||
);
|
||||
|
||||
AddRttChildPasses(attachmentImagesAndDependencies);
|
||||
AddUiCanvasChildPass(attachmentImagesAndDependencies);
|
||||
}
|
||||
|
||||
Base::BuildInternal();
|
||||
}
|
||||
|
||||
void LyShinePass::RebuildRttChildren()
|
||||
{
|
||||
QueueForBuildAndInitialization();
|
||||
}
|
||||
|
||||
AZ::RPI::RasterPass* LyShinePass::GetRttPass(const AZStd::string& name)
|
||||
{
|
||||
for (auto child:m_children)
|
||||
{
|
||||
if (child->GetName() == AZ::Name(name))
|
||||
{
|
||||
return azrtti_cast<AZ::RPI::RasterPass*>(child.get());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZ::RPI::RasterPass* LyShinePass::GetUiCanvasPass()
|
||||
{
|
||||
return m_uiCanvasChildPass.get();
|
||||
}
|
||||
|
||||
void LyShinePass::AddRttChildPasses(LyShine::AttachmentImagesAndDependencies attachmentImagesAndDependencies)
|
||||
{
|
||||
for (const auto& attachmentImageAndDependencies : attachmentImagesAndDependencies)
|
||||
{
|
||||
AddRttChildPass(attachmentImageAndDependencies.first, attachmentImageAndDependencies.second);
|
||||
}
|
||||
}
|
||||
|
||||
void LyShinePass::AddRttChildPass(AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage, AttachmentImages attachmentImageDependencies)
|
||||
{
|
||||
// Add a pass that renders to the specified texture
|
||||
|
||||
// Create a pass template
|
||||
auto passTemplate = AZStd::make_shared<AZ::RPI::PassTemplate>();
|
||||
passTemplate->m_name = "RttChildPass";
|
||||
passTemplate->m_passClass = AZ::Name("RttChildPass");
|
||||
|
||||
// Slots
|
||||
passTemplate->m_slots.resize(2);
|
||||
|
||||
AZ::RPI::PassSlot& depthInOutSlot = passTemplate->m_slots[0];
|
||||
depthInOutSlot.m_name = "DepthInputOutput";
|
||||
depthInOutSlot.m_slotType = AZ::RPI::PassSlotType::InputOutput;
|
||||
depthInOutSlot.m_scopeAttachmentUsage = AZ::RHI::ScopeAttachmentUsage::DepthStencil;
|
||||
depthInOutSlot.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepthStencil(0.0f, 0);
|
||||
depthInOutSlot.m_loadStoreAction.m_loadActionStencil = AZ::RHI::AttachmentLoadAction::Clear;
|
||||
|
||||
AZ::RPI::PassSlot& outSlot = passTemplate->m_slots[1];
|
||||
outSlot.m_name = AZ::Name("RenderTargetOutput");
|
||||
outSlot.m_slotType = AZ::RPI::PassSlotType::Output;
|
||||
outSlot.m_scopeAttachmentUsage = AZ::RHI::ScopeAttachmentUsage::RenderTarget;
|
||||
outSlot.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
outSlot.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
|
||||
|
||||
// Connections
|
||||
passTemplate->m_connections.resize(1);
|
||||
|
||||
AZ::RPI::PassConnection& depthInOutConnection = passTemplate->m_connections[0];
|
||||
depthInOutConnection.m_localSlot = "DepthInputOutput";
|
||||
depthInOutConnection.m_attachmentRef.m_pass = "Parent";
|
||||
depthInOutConnection.m_attachmentRef.m_attachment = "DepthInputOutput";
|
||||
|
||||
// Pass data
|
||||
AZStd::shared_ptr<AZ::RPI::RasterPassData> passData = AZStd::make_shared<AZ::RPI::RasterPassData>();
|
||||
passData->m_drawListTag = AZ::Name("uicanvas");
|
||||
passData->m_pipelineViewTag = AZ::Name("MainCamera");
|
||||
auto size = attachmentImage->GetRHIImage()->GetDescriptor().m_size;
|
||||
passData->m_overrideScissor = AZ::RHI::Scissor(0, 0, size.m_width, size.m_height);
|
||||
passData->m_overrideViewport = AZ::RHI::Viewport(0, size.m_width, 0, size.m_height);
|
||||
passTemplate->m_passData = AZStd::move(passData);
|
||||
// Create a pass descriptor for the new child pass
|
||||
AZ::RPI::PassDescriptor childDesc;
|
||||
childDesc.m_passTemplate = passTemplate;
|
||||
childDesc.m_passName = attachmentImage->GetAttachmentId();
|
||||
|
||||
AZ::RPI::PassSystemInterface* passSystem = AZ::RPI::PassSystemInterface::Get();
|
||||
AZ::RPI::Ptr<RttChildPass> rttChildPass = passSystem->CreatePass<RttChildPass>(childDesc);
|
||||
AZ_Assert(rttChildPass, "[LyShinePass] Unable to create %s.", passTemplate->m_name.GetCStr());
|
||||
|
||||
// Store the info needed to attach to slots and set up frame graph dependencies
|
||||
rttChildPass->m_attachmentImage = attachmentImage;
|
||||
rttChildPass->m_attachmentImageDependencies = attachmentImageDependencies;
|
||||
|
||||
AddChild(rttChildPass);
|
||||
}
|
||||
|
||||
void LyShinePass::AddUiCanvasChildPass(LyShine::AttachmentImagesAndDependencies AttachmentImagesAndDependencies)
|
||||
{
|
||||
if (!m_uiCanvasChildPass)
|
||||
{
|
||||
// Create a pass template
|
||||
auto passTemplate = AZStd::make_shared<AZ::RPI::PassTemplate>();
|
||||
passTemplate->m_name = AZ::Name("LyShineChildPass");
|
||||
passTemplate->m_passClass = AZ::Name("LyShineChildPass");
|
||||
|
||||
// Slots
|
||||
passTemplate->m_slots.resize(2);
|
||||
|
||||
AZ::RPI::PassSlot& depthInOutSlot = passTemplate->m_slots[0];
|
||||
depthInOutSlot.m_name = "DepthInputOutput";
|
||||
depthInOutSlot.m_slotType = AZ::RPI::PassSlotType::InputOutput;
|
||||
depthInOutSlot.m_scopeAttachmentUsage = AZ::RHI::ScopeAttachmentUsage::DepthStencil;
|
||||
depthInOutSlot.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepthStencil(0.0f, 0);
|
||||
depthInOutSlot.m_loadStoreAction.m_loadActionStencil = AZ::RHI::AttachmentLoadAction::Clear;
|
||||
|
||||
AZ::RPI::PassSlot& inOutSlot = passTemplate->m_slots[1];
|
||||
inOutSlot.m_name = "ColorInputOutput";
|
||||
inOutSlot.m_slotType = AZ::RPI::PassSlotType::InputOutput;
|
||||
inOutSlot.m_scopeAttachmentUsage = AZ::RHI::ScopeAttachmentUsage::RenderTarget;
|
||||
|
||||
// Connections
|
||||
passTemplate->m_connections.resize(2);
|
||||
|
||||
AZ::RPI::PassConnection& depthInOutConnection = passTemplate->m_connections[0];
|
||||
depthInOutConnection.m_localSlot = "DepthInputOutput";
|
||||
depthInOutConnection.m_attachmentRef.m_pass = "Parent";
|
||||
depthInOutConnection.m_attachmentRef.m_attachment = "DepthInputOutput";
|
||||
|
||||
AZ::RPI::PassConnection& inOutConnection = passTemplate->m_connections[1];
|
||||
inOutConnection.m_localSlot = "ColorInputOutput";
|
||||
inOutConnection.m_attachmentRef.m_pass = "Parent";
|
||||
inOutConnection.m_attachmentRef.m_attachment = "ColorInputOutput";
|
||||
|
||||
// Pass data
|
||||
AZStd::shared_ptr<AZ::RPI::RasterPassData> passData = AZStd::make_shared<AZ::RPI::RasterPassData>();
|
||||
passData->m_drawListTag = AZ::Name("uicanvas");
|
||||
passData->m_pipelineViewTag = AZ::Name("MainCamera");
|
||||
passTemplate->m_passData = AZStd::move(passData);
|
||||
|
||||
// Create a pass descriptor for the new child pass
|
||||
AZ::RPI::PassDescriptor childDesc;
|
||||
childDesc.m_passTemplate = passTemplate;
|
||||
childDesc.m_passName = AZ::Name("LyShineChildPass");
|
||||
|
||||
AZ::RPI::PassSystemInterface* passSystem = AZ::RPI::PassSystemInterface::Get();
|
||||
m_uiCanvasChildPass = passSystem->CreatePass<LyShineChildPass>(childDesc);
|
||||
AZ_Assert(m_uiCanvasChildPass, "[LyShinePass] Unable to create %s.", passTemplate->m_name.GetCStr());
|
||||
}
|
||||
|
||||
// Store the info needed to set up frame graph dependencies
|
||||
m_uiCanvasChildPass->m_attachmentImageDependencies.clear();
|
||||
for (const auto& attachmentImageAndDescendents : AttachmentImagesAndDependencies)
|
||||
{
|
||||
m_uiCanvasChildPass->m_attachmentImageDependencies.emplace_back(attachmentImageAndDescendents.first);
|
||||
}
|
||||
|
||||
AddChild(m_uiCanvasChildPass);
|
||||
}
|
||||
|
||||
AZ::RPI::Ptr<LyShineChildPass> LyShineChildPass::Create(const AZ::RPI::PassDescriptor& descriptor)
|
||||
{
|
||||
return aznew LyShineChildPass(descriptor);
|
||||
}
|
||||
|
||||
LyShineChildPass::LyShineChildPass(const AZ::RPI::PassDescriptor& descriptor)
|
||||
: RasterPass(descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
LyShineChildPass::~LyShineChildPass()
|
||||
{
|
||||
}
|
||||
|
||||
void LyShineChildPass::SetupFrameGraphDependencies(AZ::RHI::FrameGraphInterface frameGraph)
|
||||
{
|
||||
AZ::RPI::RasterPass::SetupFrameGraphDependencies(frameGraph);
|
||||
|
||||
for (auto attachmentImage : m_attachmentImageDependencies)
|
||||
{
|
||||
// Ensure that the image is imported into the attachment database.
|
||||
// The image may not be imported if the owning pass has been disabled.
|
||||
auto attachmentImageId = attachmentImage->GetAttachmentId();
|
||||
if (!frameGraph.GetAttachmentDatabase().IsAttachmentValid(attachmentImageId))
|
||||
{
|
||||
frameGraph.GetAttachmentDatabase().ImportImage(attachmentImageId, attachmentImage->GetRHIImage());
|
||||
}
|
||||
|
||||
AZ::RHI::ImageScopeAttachmentDescriptor desc;
|
||||
desc.m_attachmentId = attachmentImageId;
|
||||
desc.m_imageViewDescriptor = attachmentImage->GetImageView()->GetDescriptor();
|
||||
desc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load;
|
||||
|
||||
frameGraph.UseShaderAttachment(desc, AZ::RHI::ScopeAttachmentAccess::Read);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::RPI::Ptr<RttChildPass> RttChildPass::Create(const AZ::RPI::PassDescriptor& descriptor)
|
||||
{
|
||||
return aznew RttChildPass(descriptor);
|
||||
}
|
||||
|
||||
RttChildPass::RttChildPass(const AZ::RPI::PassDescriptor& descriptor)
|
||||
: LyShineChildPass(descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
RttChildPass::~RttChildPass()
|
||||
{
|
||||
}
|
||||
|
||||
void RttChildPass::BuildInternal()
|
||||
{
|
||||
AttachImageToSlot(AZ::Name("RenderTargetOutput"), m_attachmentImage);
|
||||
}
|
||||
} // namespace LyShine
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 <Atom/RPI.Public/Pass/ParentPass.h>
|
||||
#include <Atom/RPI.Public/Pass/RasterPass.h>
|
||||
#include <AtomCore/std/containers/array_view.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include "LyShinePassDataBus.h"
|
||||
|
||||
namespace LyShine
|
||||
{
|
||||
class LyShineChildPass;
|
||||
|
||||
//! Manages child passes at runtime that render to render targets
|
||||
class LyShinePass final
|
||||
: public AZ::RPI::ParentPass
|
||||
, protected LyShinePassRequestBus::Handler
|
||||
{
|
||||
AZ_RPI_PASS(LyShinePass);
|
||||
using Base = AZ::RPI::ParentPass;
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(LyShinePass, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(LyShinePass, "C3B812ED-3771-42F4-A96F-EBD94B4D54CA", Base);
|
||||
|
||||
virtual ~LyShinePass();
|
||||
static AZ::RPI::Ptr<LyShinePass> Create(const AZ::RPI::PassDescriptor& descriptor);
|
||||
|
||||
protected:
|
||||
// Pass behavior overrides
|
||||
void ResetInternal() override;
|
||||
void BuildInternal() override;
|
||||
|
||||
// LyShinePassRequestBus overrides
|
||||
void RebuildRttChildren() override;
|
||||
AZ::RPI::RasterPass* GetRttPass(const AZStd::string& name) override;
|
||||
AZ::RPI::RasterPass* GetUiCanvasPass() override;
|
||||
|
||||
private:
|
||||
LyShinePass() = delete;
|
||||
explicit LyShinePass(const AZ::RPI::PassDescriptor& descriptor);
|
||||
|
||||
// Build the render to texture child passes
|
||||
void AddRttChildPasses(LyShine::AttachmentImagesAndDependencies AttachmentImagesAndDependencies);
|
||||
|
||||
// Add a render to texture child pass
|
||||
void AddRttChildPass(AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage, AttachmentImages dependentAttachmentImages);
|
||||
|
||||
// Append the final pass to render UI Canvas elements to the screen
|
||||
void AddUiCanvasChildPass(LyShine::AttachmentImagesAndDependencies AttachmentImagesAndDependencies);
|
||||
|
||||
// Pass that renders the UI Canvas elements to the screen
|
||||
AZ::RPI::Ptr<LyShineChildPass> m_uiCanvasChildPass;
|
||||
};
|
||||
|
||||
// Child pass with potential attachment dependencies
|
||||
class LyShineChildPass
|
||||
: public AZ::RPI::RasterPass
|
||||
{
|
||||
AZ_RPI_PASS(LyShineChildPass);
|
||||
|
||||
friend class LyShinePass;
|
||||
public:
|
||||
AZ_RTTI(LyShineChildPass, "{41D525F9-09EB-4004-97DC-082078FF8DD2}", RasterPass);
|
||||
AZ_CLASS_ALLOCATOR(LyShineChildPass, AZ::SystemAllocator, 0);
|
||||
virtual ~LyShineChildPass();
|
||||
|
||||
//! Creates a LyShineChildPass
|
||||
static AZ::RPI::Ptr<LyShineChildPass> Create(const AZ::RPI::PassDescriptor& descriptor);
|
||||
|
||||
protected:
|
||||
LyShineChildPass(const AZ::RPI::PassDescriptor& descriptor);
|
||||
|
||||
// Scope producer Overrides...
|
||||
void SetupFrameGraphDependencies(AZ::RHI::FrameGraphInterface frameGraph) override;
|
||||
|
||||
AttachmentImages m_attachmentImageDependencies;
|
||||
};
|
||||
|
||||
// Child pass that renders UI elements to a render target
|
||||
class RttChildPass
|
||||
: public LyShineChildPass
|
||||
{
|
||||
AZ_RPI_PASS(RttChildPass);
|
||||
|
||||
friend class LyShinePass;
|
||||
|
||||
public:
|
||||
AZ_RTTI(RttChildPass, "{54B0574D-2EB3-4054-9E1D-0E0D9C8CB09A}", LyShineChildPass);
|
||||
AZ_CLASS_ALLOCATOR(RttChildPass, AZ::SystemAllocator, 0);
|
||||
virtual ~RttChildPass();
|
||||
|
||||
//! Creates a RttChildPass
|
||||
static AZ::RPI::Ptr<RttChildPass> Create(const AZ::RPI::PassDescriptor& descriptor);
|
||||
|
||||
protected:
|
||||
RttChildPass(const AZ::RPI::PassDescriptor& descriptor);
|
||||
|
||||
// Pass behavior overrides
|
||||
void BuildInternal() override;
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> m_attachmentImage;
|
||||
};
|
||||
} // namespace LyShine
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AtomCore/Instance/Instance.h>
|
||||
#include <Atom/RPI.Public/Base.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
class AttachmentImage;
|
||||
class RasterPass;
|
||||
}
|
||||
}
|
||||
|
||||
namespace LyShine
|
||||
{
|
||||
using AttachmentImages = AZStd::vector<AZ::Data::Instance<AZ::RPI::AttachmentImage>>;
|
||||
using AttachmentImageAndDependentsPair = AZStd::pair<AZ::Data::Instance<AZ::RPI::AttachmentImage>, AttachmentImages>;
|
||||
using AttachmentImagesAndDependencies = AZStd::vector<AttachmentImageAndDependentsPair>;
|
||||
}
|
||||
|
||||
class LyShinePassRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AZ::RPI::SceneId;
|
||||
|
||||
//! Called when the number of render targets has changed and the LyShine pass needs to rebuild
|
||||
virtual void RebuildRttChildren() = 0;
|
||||
|
||||
//! Returns a render to texture pass based on render target name
|
||||
virtual AZ::RPI::RasterPass* GetRttPass(const AZStd::string& name) = 0;
|
||||
|
||||
//! Returns the final pass that renders the UI canvas contents
|
||||
virtual AZ::RPI::RasterPass* GetUiCanvasPass() = 0;
|
||||
};
|
||||
using LyShinePassRequestBus = AZ::EBus<LyShinePassRequests>;
|
||||
|
||||
class LyShinePassDataRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AZ::RPI::SceneId;
|
||||
|
||||
//! Get a list of render targets that require a render to texture pass, and any
|
||||
//! other render targets that are drawn on them
|
||||
virtual LyShine::AttachmentImagesAndDependencies GetRenderTargets() = 0;
|
||||
};
|
||||
using LyShinePassDataRequestBus = AZ::EBus<LyShinePassDataRequests>;
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "UiDynamicLayoutComponent.h"
|
||||
#include "UiDynamicScrollBoxComponent.h"
|
||||
#include "UiNavigationSettings.h"
|
||||
#include "LyShinePass.h"
|
||||
|
||||
namespace LyShine
|
||||
{
|
||||
@@ -113,9 +114,11 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void LyShineSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
void LyShineSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
(void)required;
|
||||
#if !defined(LYSHINE_BUILDER) && !defined(LYSHINE_TESTS)
|
||||
required.push_back(AZ_CRC("RPISystem", 0xf2add773));
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -186,6 +189,17 @@ namespace LyShine
|
||||
RegisterComponentTypeForMenuOrdering(UiDynamicScrollBoxComponent::RTTI_Type());
|
||||
RegisterComponentTypeForMenuOrdering(UiParticleEmitterComponent::RTTI_Type());
|
||||
RegisterComponentTypeForMenuOrdering(UiFlipbookAnimationComponent::RTTI_Type());
|
||||
|
||||
#if !defined(LYSHINE_BUILDER) && !defined(LYSHINE_TESTS)
|
||||
// Add LyShine pass
|
||||
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
|
||||
AZ_Assert(passSystem, "Cannot get the pass system.");
|
||||
passSystem->AddPassCreator(AZ::Name("LyShinePass"), &LyShine::LyShinePass::Create);
|
||||
|
||||
// Setup handler for load pass template mappings
|
||||
m_loadTemplatesHandler = AZ::RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler([this]() { this->LoadPassTemplateMappings(); });
|
||||
AZ::RPI::PassSystemInterface::Get()->ConnectEvent(m_loadTemplatesHandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -386,4 +400,13 @@ namespace LyShine
|
||||
{
|
||||
UiCursorBus::Broadcast(&UiCursorInterface::SetUiCursor, m_cursorImagePathname.GetAssetPath().c_str());
|
||||
}
|
||||
|
||||
#if !defined(LYSHINE_BUILDER) && !defined(LYSHINE_TESTS)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void LyShineSystemComponent::LoadPassTemplateMappings()
|
||||
{
|
||||
const char* passTemplatesFile = "Passes/LyShinePassTemplates.azasset";
|
||||
AZ::RPI::PassSystemInterface::Get()->LoadPassTemplateMappings(passTemplatesFile);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include <LyShine/UiComponentTypes.h>
|
||||
#include "LyShine.h"
|
||||
|
||||
#if !defined(LYSHINE_BUILDER) && !defined(LYSHINE_TESTS)
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
#endif
|
||||
|
||||
namespace LyShine
|
||||
{
|
||||
// LyShine depends on the LegacyAllocator and CryStringAllocator. This will be managed
|
||||
@@ -90,6 +94,11 @@ namespace LyShine
|
||||
|
||||
void BroadcastCursorImagePathname();
|
||||
|
||||
#if !defined(LYSHINE_BUILDER) && !defined(LYSHINE_TESTS)
|
||||
// Load pass template mappings for this gem
|
||||
void LoadPassTemplateMappings();
|
||||
#endif
|
||||
|
||||
protected: // data
|
||||
|
||||
CLyShine* m_pLyShine = nullptr;
|
||||
@@ -102,5 +111,9 @@ namespace LyShine
|
||||
|
||||
// We only store this in order to generate metrics on LyShine specific components
|
||||
static const AZStd::list<AZ::ComponentDescriptor*>* m_componentDescriptors;
|
||||
|
||||
#if !defined(LYSHINE_BUILDER) && !defined(LYSHINE_TESTS)
|
||||
AZ::RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler m_loadTemplatesHandler;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "UiRenderer.h"
|
||||
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
|
||||
#ifndef _RELEASE
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
@@ -78,57 +81,28 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void PrimitiveListRenderNode::Render(UiRenderer* uiRenderer)
|
||||
void PrimitiveListRenderNode::Render(UiRenderer* uiRenderer
|
||||
, const AZ::Matrix4x4& modelViewProjMat
|
||||
, AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw)
|
||||
{
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (masks/render targets)
|
||||
for (int i = 0; i < m_numTextures; ++i)
|
||||
{
|
||||
uiRenderer->SetTexture(m_textures[i].m_texture, i, m_textures[i].m_isClampTextureMode);
|
||||
}
|
||||
|
||||
int blendModeState = m_blendModeState;
|
||||
|
||||
IRenderer* renderer = gEnv->pRenderer;
|
||||
renderer->SetState(blendModeState | uiRenderer->GetBaseState());
|
||||
|
||||
if (m_isTextureSRGB)
|
||||
{
|
||||
renderer->SetSrgbWrite(false);
|
||||
}
|
||||
|
||||
// We are using SetColorOp as a way to set flags for the ui.cfx shader by reusing flags
|
||||
// that the FixedPipelineEmu.cfx shader uses. So the names colorOp and alphaOp are used
|
||||
// just because this are the inputs to SetColorOp.
|
||||
uint8 colorOp = m_preMultiplyAlpha ? ColorOp_PreMultiplyAlpha : ColorOp_Normal;
|
||||
uint8 alphaOp = AlphaOp_Normal;
|
||||
switch (m_alphaMaskType)
|
||||
{
|
||||
case AlphaMaskType::None:
|
||||
alphaOp = AlphaOp_Normal;
|
||||
break;
|
||||
case AlphaMaskType::ModulateAlpha:
|
||||
alphaOp = AlphaOp_ModulateAlpha;
|
||||
break;
|
||||
case AlphaMaskType::ModulateAlphaAndColor:
|
||||
alphaOp = AlphaOp_ModulateAlphaAndColor;
|
||||
break;
|
||||
}
|
||||
|
||||
renderer->SetColorOp(colorOp, alphaOp, DEF_TEXARG0, DEF_TEXARG0);
|
||||
|
||||
renderer->DrawDynUiPrimitiveList(m_primitives, m_totalNumVertices, m_totalNumIndices);
|
||||
|
||||
if (m_isTextureSRGB)
|
||||
{
|
||||
renderer->SetSrgbWrite(true);
|
||||
}
|
||||
#endif
|
||||
if (!uiRenderer->IsReady())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext();
|
||||
UiRenderer::BaseState curBaseState = uiRenderer->GetBaseState();
|
||||
UiRenderer::BaseState prevBaseState = curBaseState;
|
||||
if (m_isTextureSRGB)
|
||||
{
|
||||
curBaseState.m_srgbWrite = false;
|
||||
}
|
||||
|
||||
if (m_alphaMaskType == AlphaMaskType::ModulateAlpha)
|
||||
{
|
||||
curBaseState.m_modulateAlpha = true;
|
||||
}
|
||||
uiRenderer->SetBaseState(curBaseState);
|
||||
|
||||
const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData();
|
||||
|
||||
// Set render state
|
||||
@@ -167,7 +141,7 @@ namespace LyShine
|
||||
drawSrg->SetConstant(uiShaderData.m_isClampInputIndex, isClampTextureMode);
|
||||
|
||||
// Set projection matrix
|
||||
drawSrg->SetConstant(uiShaderData.m_viewProjInputIndex, uiRenderer->GetModelViewProjectionMatrix());
|
||||
drawSrg->SetConstant(uiShaderData.m_viewProjInputIndex, modelViewProjMat);
|
||||
|
||||
drawSrg->Compile();
|
||||
|
||||
@@ -180,6 +154,8 @@ namespace LyShine
|
||||
{
|
||||
dynamicDraw->DrawIndexed(primitive.m_vertices, primitive.m_numVertices, primitive.m_indices, primitive.m_numIndices, AZ::RHI::IndexFormat::Uint16, drawSrg);
|
||||
}
|
||||
|
||||
uiRenderer->SetBaseState(prevBaseState);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -303,33 +279,35 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void MaskRenderNode::Render(UiRenderer* uiRenderer)
|
||||
void MaskRenderNode::Render(UiRenderer* uiRenderer
|
||||
, const AZ::Matrix4x4& modelViewProjMat
|
||||
, AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw)
|
||||
{
|
||||
UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState();
|
||||
|
||||
if (m_isMaskingEnabled || m_drawBehind)
|
||||
{
|
||||
SetupBeforeRenderingMask(uiRenderer, true, priorBaseState);
|
||||
SetupBeforeRenderingMask(uiRenderer, dynamicDraw, true, priorBaseState);
|
||||
for (RenderNode* renderNode : m_maskRenderNodes)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
renderNode->Render(uiRenderer, modelViewProjMat, dynamicDraw);
|
||||
}
|
||||
SetupAfterRenderingMask(uiRenderer, true, priorBaseState);
|
||||
SetupAfterRenderingMask(uiRenderer, dynamicDraw, true, priorBaseState);
|
||||
}
|
||||
|
||||
for (RenderNode* renderNode : m_contentRenderNodes)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
renderNode->Render(uiRenderer, modelViewProjMat, dynamicDraw);
|
||||
}
|
||||
|
||||
if (m_isMaskingEnabled || m_drawInFront)
|
||||
{
|
||||
SetupBeforeRenderingMask(uiRenderer, false, priorBaseState);
|
||||
SetupBeforeRenderingMask(uiRenderer, dynamicDraw, false, priorBaseState);
|
||||
for (RenderNode* renderNode : m_maskRenderNodes)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
renderNode->Render(uiRenderer, modelViewProjMat, dynamicDraw);
|
||||
}
|
||||
SetupAfterRenderingMask(uiRenderer, false, priorBaseState);
|
||||
SetupAfterRenderingMask(uiRenderer, dynamicDraw, false, priorBaseState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +345,9 @@ namespace LyShine
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState)
|
||||
void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer,
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
|
||||
bool firstPass, UiRenderer::BaseState priorBaseState)
|
||||
{
|
||||
UiRenderer::BaseState curBaseState = priorBaseState;
|
||||
|
||||
@@ -406,7 +386,6 @@ namespace LyShine
|
||||
curBaseState.m_stencilState.m_backFace = stencilOpState;
|
||||
|
||||
// set up for stencil write
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext();
|
||||
dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef());
|
||||
curBaseState.m_stencilState.m_enable = true;
|
||||
curBaseState.m_stencilState.m_writeMask = 0xFF;
|
||||
@@ -421,7 +400,9 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState)
|
||||
void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer,
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
|
||||
bool firstPass, UiRenderer::BaseState priorBaseState)
|
||||
{
|
||||
if (m_isMaskingEnabled)
|
||||
{
|
||||
@@ -439,7 +420,6 @@ namespace LyShine
|
||||
uiRenderer->DecrementStencilRef();
|
||||
}
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext();
|
||||
dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef());
|
||||
|
||||
if (firstPass)
|
||||
@@ -474,16 +454,14 @@ namespace LyShine
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
RenderTargetRenderNode::RenderTargetRenderNode(
|
||||
RenderTargetRenderNode* parentRenderTarget,
|
||||
int renderTargetHandle,
|
||||
SDepthTexture* renderTargetDepthSurface,
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage,
|
||||
const AZ::Vector2& viewportTopLeft,
|
||||
const AZ::Vector2& viewportSize,
|
||||
const AZ::Color& clearColor,
|
||||
int nestLevel)
|
||||
: RenderNode(RenderNodeType::RenderTarget)
|
||||
, m_parentRenderTarget(parentRenderTarget)
|
||||
, m_renderTargetHandle(renderTargetHandle)
|
||||
, m_renderTargetDepthSurface(renderTargetDepthSurface)
|
||||
, m_attachmentImage(attachmentImage)
|
||||
, m_viewportX(viewportTopLeft.GetX())
|
||||
, m_viewportY(viewportTopLeft.GetY())
|
||||
, m_viewportWidth(viewportSize.GetX())
|
||||
@@ -491,6 +469,13 @@ namespace LyShine
|
||||
, m_clearColor(clearColor)
|
||||
, m_nestLevel(nestLevel)
|
||||
{
|
||||
AZ::MakeOrthographicMatrixRH(m_modelViewProjMat,
|
||||
m_viewportX,
|
||||
m_viewportX + m_viewportWidth,
|
||||
m_viewportY + m_viewportHeight,
|
||||
m_viewportY,
|
||||
0.0f,
|
||||
1.0f);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -505,9 +490,11 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderTargetRenderNode::Render(UiRenderer* uiRenderer)
|
||||
void RenderTargetRenderNode::Render(UiRenderer* uiRenderer
|
||||
, [[maybe_unused]] const AZ::Matrix4x4& modelViewProjMat
|
||||
, [[maybe_unused]] AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw)
|
||||
{
|
||||
if (m_renderTargetHandle <= 0)
|
||||
if (!m_attachmentImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -515,39 +502,52 @@ namespace LyShine
|
||||
ISystem* system = gEnv->pSystem;
|
||||
if (system && !gEnv->IsDedicated())
|
||||
{
|
||||
TransformationMatrices backupMatrices;
|
||||
gEnv->pRenderer->Set2DModeNonZeroTopLeft(m_viewportX, m_viewportY, m_viewportWidth, m_viewportHeight, backupMatrices);
|
||||
|
||||
// this will change the viewport
|
||||
gEnv->pRenderer->SetRenderTarget(m_renderTargetHandle, m_renderTargetDepthSurface);
|
||||
|
||||
// clear the render target before rendering to it
|
||||
// NOTE: the FRT_CLEAR_IMMEDIATE is required since we will have already set the render target
|
||||
// In theory we could call this before setting the render target without the immediate flag
|
||||
// but that doesn't work. Perhaps because FX_Commit is not called.
|
||||
ColorF viewportBackgroundColor(m_clearColor.GetR(), m_clearColor.GetG(), m_clearColor.GetB(), m_clearColor.GetA());
|
||||
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR, viewportBackgroundColor);
|
||||
|
||||
// we could use SetSrgbWrite to write to a linear texture here. But that gets complicated with
|
||||
// having to affect all decsendant element renders. So we just let it write srgb to the render target and
|
||||
// allow for that when we render using the render target as a source texture.
|
||||
|
||||
for (RenderNode* renderNode : m_childRenderNodes)
|
||||
// Use a dedicated dynamic draw context for rendering to the texture since it can only have one draw list tag
|
||||
if (!m_dynamicDraw)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
m_dynamicDraw = uiRenderer->CreateDynamicDrawContextForRTT(GetRenderTargetName());
|
||||
}
|
||||
|
||||
gEnv->pRenderer->SetRenderTarget(0); // restore render target
|
||||
if (m_dynamicDraw)
|
||||
{
|
||||
UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState();
|
||||
|
||||
gEnv->pRenderer->Unset2DMode(backupMatrices);
|
||||
UiRenderer::BaseState curBaseState = priorBaseState;
|
||||
curBaseState.m_blendState.m_blendAlphaSource = AZ::RHI::BlendFactor::One;
|
||||
curBaseState.m_blendState.m_blendAlphaDest = AZ::RHI::BlendFactor::AlphaSource1Inverse;
|
||||
uiRenderer->SetBaseState(curBaseState);
|
||||
|
||||
for (RenderNode* renderNode : m_childRenderNodes)
|
||||
{
|
||||
renderNode->Render(uiRenderer, m_modelViewProjMat, m_dynamicDraw);
|
||||
}
|
||||
|
||||
uiRenderer->SetBaseState(priorBaseState);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_WarningOnce("UI", false, "Failed to create a Dynamic Draw Context for UI Element's render target. "\
|
||||
"Please ensure that the custom LyShinePass has been added to the project's main render pipeline.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const char* RenderTargetRenderNode::GetRenderTargetName() const
|
||||
{
|
||||
ITexture* texture = gEnv->pRenderer->EF_GetTextureByID(m_renderTargetHandle);
|
||||
return texture->GetName();
|
||||
return m_attachmentImage->GetRHIImage()->GetName().GetCStr();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
int RenderTargetRenderNode::GetNestLevel() const
|
||||
{
|
||||
return m_nestLevel;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const AZ::Data::Instance<AZ::RPI::AttachmentImage> RenderTargetRenderNode::GetRenderTarget() const
|
||||
{
|
||||
return m_attachmentImage;
|
||||
}
|
||||
|
||||
#ifndef _RELEASE
|
||||
@@ -671,31 +671,29 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::BeginRenderToTexture(int renderTargetHandle, SDepthTexture* renderTargetDepthSurface,
|
||||
void RenderGraph::BeginRenderToTexture([[maybe_unused]] int renderTargetHandle, [[maybe_unused]] SDepthTexture* renderTargetDepthSurface,
|
||||
[[maybe_unused]] const AZ::Vector2& viewportTopLeft, [[maybe_unused]] const AZ::Vector2& viewportSize, [[maybe_unused]] const AZ::Color& clearColor)
|
||||
{
|
||||
// LYSHINE_ATOM_TODO - this function will be removed when all IRenderer references are gone from UI components
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::BeginRenderToTexture(AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage,
|
||||
const AZ::Vector2& viewportTopLeft, const AZ::Vector2& viewportSize, const AZ::Color& clearColor)
|
||||
{
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets)
|
||||
// this uses pool allocator
|
||||
RenderTargetRenderNode* renderTargetRenderNode = new RenderTargetRenderNode(
|
||||
m_currentRenderTarget, renderTargetHandle, renderTargetDepthSurface,
|
||||
m_currentRenderTarget, attachmentImage,
|
||||
viewportTopLeft, viewportSize, clearColor, m_renderTargetNestLevel);
|
||||
|
||||
m_currentRenderTarget = renderTargetRenderNode;
|
||||
m_renderNodeListStack.push(&m_currentRenderTarget->GetChildRenderNodeList());
|
||||
m_renderTargetNestLevel++;
|
||||
#else
|
||||
AZ_UNUSED(clearColor);
|
||||
AZ_UNUSED(viewportSize);
|
||||
AZ_UNUSED(viewportTopLeft);
|
||||
AZ_UNUSED(renderTargetDepthSurface);
|
||||
AZ_UNUSED(renderTargetHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::EndRenderToTexture()
|
||||
{
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets)
|
||||
AZ_Assert(m_currentRenderTarget, "Calling EndRenderToTexture while not defining a render target node");
|
||||
if (m_currentRenderTarget)
|
||||
{
|
||||
@@ -709,7 +707,6 @@ namespace LyShine
|
||||
m_renderNodeListStack.pop();
|
||||
m_renderTargetNestLevel--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenderGraph::AddPrimitive(
|
||||
@@ -803,11 +800,22 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::AddAlphaMaskPrimitive(IRenderer::DynUiPrimitive* primitive,
|
||||
ITexture* texture, ITexture* maskTexture,
|
||||
bool isClampTextureMode, bool isTextureSRGB, bool isTexturePremultipliedAlpha, BlendMode blendMode)
|
||||
void RenderGraph::AddAlphaMaskPrimitive([[maybe_unused]] IRenderer::DynUiPrimitive* primitive,
|
||||
[[maybe_unused]] ITexture* texture, [[maybe_unused]] ITexture* maskTexture,
|
||||
[[maybe_unused]] bool isClampTextureMode, [[maybe_unused]] bool isTextureSRGB, [[maybe_unused]] bool isTexturePremultipliedAlpha, [[maybe_unused]] BlendMode blendMode)
|
||||
{
|
||||
// LYSHINE_ATOM_TODO - this function will be removed when all IRenderer references are gone from UI components
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::AddAlphaMaskPrimitiveAtom(IRenderer::DynUiPrimitive* primitive,
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> contentAttachmentImage,
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> maskAttachmentImage,
|
||||
bool isClampTextureMode,
|
||||
bool isTextureSRGB,
|
||||
bool isTexturePremultipliedAlpha,
|
||||
BlendMode blendMode)
|
||||
{
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets)
|
||||
AZStd::vector<RenderNode*>* renderNodeList = m_renderNodeListStack.top();
|
||||
|
||||
int texUnit0 = -1;
|
||||
@@ -842,8 +850,8 @@ namespace LyShine
|
||||
{
|
||||
// render state is the same - we can add the primitive to this list if the texture is in
|
||||
// the list or there is space for another texture
|
||||
texUnit0 = primListRenderNode->GetOrAddTexture(texture, true);
|
||||
texUnit1 = primListRenderNode->GetOrAddTexture(maskTexture, true);
|
||||
texUnit0 = primListRenderNode->GetOrAddTexture(contentAttachmentImage, true);
|
||||
texUnit1 = primListRenderNode->GetOrAddTexture(maskAttachmentImage, true);
|
||||
|
||||
if (texUnit0 != -1 && texUnit1 != -1)
|
||||
{
|
||||
@@ -857,7 +865,7 @@ namespace LyShine
|
||||
{
|
||||
// We can't add this primitive to the existing render node, we need to create a new render node
|
||||
// this uses a pool allocator for fast allocation
|
||||
renderNodeToAddTo = new PrimitiveListRenderNode(texture, maskTexture,
|
||||
renderNodeToAddTo = new PrimitiveListRenderNode(contentAttachmentImage, maskAttachmentImage,
|
||||
isClampTextureMode, isTextureSRGB, isPreMultiplyAlpha, alphaMaskType, blendModeState);
|
||||
|
||||
renderNodeList->push_back(renderNodeToAddTo);
|
||||
@@ -881,15 +889,6 @@ namespace LyShine
|
||||
// add this primitive to the render node
|
||||
renderNodeToAddTo->AddPrimitive(primitive);
|
||||
}
|
||||
#else
|
||||
AZ_UNUSED(primitive);
|
||||
AZ_UNUSED(texture);
|
||||
AZ_UNUSED(maskTexture);
|
||||
AZ_UNUSED(isClampTextureMode);
|
||||
AZ_UNUSED(isTextureSRGB);
|
||||
AZ_UNUSED(isTexturePremultipliedAlpha);
|
||||
AZ_UNUSED(blendMode);
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -972,11 +971,8 @@ namespace LyShine
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::Render(UiRenderer* uiRenderer, const AZ::Vector2& viewportSize)
|
||||
void RenderGraph::Render(UiRenderer* uiRenderer, [[maybe_unused]] const AZ::Vector2& viewportSize)
|
||||
{
|
||||
// LYSHINE_ATOM_TODO - will probably need to support this when converting UI Editor to use Atom
|
||||
AZ_UNUSED(viewportSize);
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = uiRenderer->GetDynamicDrawContext();
|
||||
|
||||
// Disable stencil and enable blend/color write
|
||||
@@ -984,57 +980,35 @@ namespace LyShine
|
||||
dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState);
|
||||
|
||||
// First render the render targets, they are sorted so that more deeply nested ones are rendered first.
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (render targets)
|
||||
// They only need to be rendered the first time that a render graph is rendered after it has been built.
|
||||
// Though there is a special case, if this is the first time a shader variant has been used it can miss
|
||||
// the first render. So to be safe we only stop rendering to render targets after we have rendered to
|
||||
// them twice with no shader compiles initiated.
|
||||
if (m_renderToRenderTargetCount < 2)
|
||||
if (m_renderToRenderTargetCount == 0)
|
||||
{
|
||||
// Enable the Rtt passes to draw onto the render targets
|
||||
SetRttPassesEnabled(uiRenderer, true);
|
||||
}
|
||||
|
||||
// LYSHINE_ATOM_TODO - It is currently necessary to render to the targets twice. Needs investigation
|
||||
constexpr int timesToRenderToRenderTargets = 2;
|
||||
if (m_renderToRenderTargetCount < timesToRenderToRenderTargets)
|
||||
{
|
||||
for (RenderNode* renderNode : m_renderTargetRenderNodes)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
}
|
||||
|
||||
// if the render targets render OK we don't need to render them every frame. But if a new shader
|
||||
// variant needed to be compiled then they will not have rendered OK. So we check is there are
|
||||
// any shaders still in the process of compiling. Because they are compiled on the render
|
||||
// thread, we may not know until the next frame that a shader needed to be compiled. So we need
|
||||
// the counter.
|
||||
SShaderCacheStatistics stats;
|
||||
gEnv->pRenderer->EF_Query(EFQ_GetShaderCacheInfo, stats);
|
||||
bool waitingOnShadersToCompile = stats.m_nNumShaderAsyncCompiles > 0 ? true : false;
|
||||
if (!waitingOnShadersToCompile)
|
||||
{
|
||||
m_renderToRenderTargetCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_renderToRenderTargetCount = 0;
|
||||
renderNode->Render(uiRenderer, uiRenderer->GetModelViewProjectionMatrix(), dynamicDraw);
|
||||
}
|
||||
m_renderToRenderTargetCount++;
|
||||
}
|
||||
#else
|
||||
for (RenderNode* renderNode : m_renderTargetRenderNodes)
|
||||
else if (m_renderToRenderTargetCount < timesToRenderToRenderTargets + 1)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
// Disable the rtt render passes since they don't need to be rendered to until the graph becomes invalidated again.
|
||||
// This is also necessary to prevent the render targets' contents getting cleared on load by the pass.
|
||||
SetRttPassesEnabled(uiRenderer, false);
|
||||
m_renderToRenderTargetCount++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (UI Editor)
|
||||
// Set2DMode defines the viewport so we set it to canvas viewport here (the render target render nodes
|
||||
// above will have set the viewport as they needed).
|
||||
TransformationMatrices backupMatrices;
|
||||
gEnv->pRenderer->Set2DMode(static_cast<uint32>(viewportSize.GetX()), static_cast<uint32>(viewportSize.GetY()), backupMatrices);
|
||||
#endif
|
||||
for (RenderNode* renderNode : m_renderNodes)
|
||||
{
|
||||
renderNode->Render(uiRenderer);
|
||||
renderNode->Render(uiRenderer, uiRenderer->GetModelViewProjectionMatrix(), dynamicDraw);
|
||||
}
|
||||
#ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (UI Editor)
|
||||
// end the 2D mode
|
||||
gEnv->pRenderer->Unset2DMode(backupMatrices);
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1072,6 +1046,31 @@ namespace LyShine
|
||||
return m_renderNodes.empty();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::GetRenderTargetsAndDependencies(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies)
|
||||
{
|
||||
for (RenderNode* renderNode : m_renderTargetRenderNodes)
|
||||
{
|
||||
const RenderTargetRenderNode* renderTargetRenderNode = static_cast<const RenderTargetRenderNode*>(renderNode);
|
||||
|
||||
if (renderTargetRenderNode->GetNestLevel() == 0)
|
||||
{
|
||||
LyShine::AttachmentImages attachmentImages;
|
||||
const AZStd::vector<RenderNode*>& childNodeList = renderTargetRenderNode->GetChildRenderNodeList();
|
||||
for (auto& childNode : childNodeList)
|
||||
{
|
||||
if (childNode->GetType() == RenderNodeType::RenderTarget)
|
||||
{
|
||||
const RenderTargetRenderNode* childRenderTargetRenderNode = static_cast<const RenderTargetRenderNode*>(childNode);
|
||||
attachmentImages.emplace_back(childRenderTargetRenderNode->GetRenderTarget());
|
||||
}
|
||||
}
|
||||
|
||||
attachmentImagesAndDependencies.emplace_back(AttachmentImageAndDependentsPair(renderTargetRenderNode->GetRenderTarget(), attachmentImages));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _RELEASE
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RenderGraph::ValidateGraph()
|
||||
@@ -1540,4 +1539,19 @@ namespace LyShine
|
||||
return flags;
|
||||
}
|
||||
|
||||
void RenderGraph::SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled)
|
||||
{
|
||||
// Enable or disable the rtt render passes
|
||||
AZ::RPI::SceneId sceneId = uiRenderer->GetViewportContext()->GetRenderScene()->GetId();
|
||||
for (RenderTargetRenderNode* renderTargetRenderNode : m_renderTargetRenderNodes)
|
||||
{
|
||||
// Find the rtt pass to disable
|
||||
AZ::RPI::RasterPass* rttPass = nullptr;
|
||||
LyShinePassRequestBus::EventResult(rttPass, sceneId, &LyShinePassRequestBus::Events::GetRttPass, renderTargetRenderNode->GetRenderTargetName());
|
||||
if (rttPass)
|
||||
{
|
||||
rttPass->SetEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/Math/Color.h>
|
||||
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <Atom/RPI.Reflect/Image/Image.h>
|
||||
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h>
|
||||
#include <AtomCore/Instance/Instance.h>
|
||||
|
||||
#include "UiRenderer.h"
|
||||
#include "LyShinePass.h"
|
||||
#ifndef _RELEASE
|
||||
#include "LyShineDebug.h"
|
||||
#endif
|
||||
@@ -46,7 +49,9 @@ namespace LyShine
|
||||
RenderNode(RenderNodeType type) : m_type(type) {}
|
||||
virtual ~RenderNode() {};
|
||||
|
||||
virtual void Render(UiRenderer* uiRenderer) = 0;
|
||||
virtual void Render(UiRenderer* uiRenderer
|
||||
, const AZ::Matrix4x4& modelViewProjMat
|
||||
, AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw) = 0;
|
||||
|
||||
RenderNodeType GetType() const { return m_type; }
|
||||
|
||||
@@ -70,7 +75,9 @@ namespace LyShine
|
||||
PrimitiveListRenderNode(const AZ::Data::Instance<AZ::RPI::Image>& texture, const AZ::Data::Instance<AZ::RPI::Image>& maskTexture,
|
||||
bool isClampTextureMode, bool isTextureSRGB, bool preMultiplyAlpha, AlphaMaskType alphaMaskType, int blendModeState);
|
||||
~PrimitiveListRenderNode() override;
|
||||
void Render(UiRenderer* uiRenderer) override;
|
||||
void Render(UiRenderer* uiRenderer
|
||||
, const AZ::Matrix4x4& modelViewProjMat
|
||||
, AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw) override;
|
||||
|
||||
void AddPrimitive(IRenderer::DynUiPrimitive* primitive);
|
||||
IRenderer::DynUiPrimitiveList& GetPrimitives() const;
|
||||
@@ -128,7 +135,9 @@ namespace LyShine
|
||||
MaskRenderNode(MaskRenderNode* parentMask, bool isMaskingEnabled, bool useAlphaTest, bool drawBehind, bool drawInFront);
|
||||
~MaskRenderNode() override;
|
||||
|
||||
void Render(UiRenderer* uiRenderer) override;
|
||||
void Render(UiRenderer* uiRenderer
|
||||
, const AZ::Matrix4x4& modelViewProjMat
|
||||
, AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw) override;
|
||||
|
||||
AZStd::vector<RenderNode*>& GetMaskRenderNodeList() { return m_maskRenderNodes; }
|
||||
const AZStd::vector<RenderNode*>& GetMaskRenderNodeList() const { return m_maskRenderNodes; }
|
||||
@@ -152,8 +161,12 @@ namespace LyShine
|
||||
#endif
|
||||
|
||||
private: // functions
|
||||
void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState);
|
||||
void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState);
|
||||
void SetupBeforeRenderingMask(UiRenderer* uiRenderer,
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
|
||||
bool firstPass, UiRenderer::BaseState priorBaseState);
|
||||
void SetupAfterRenderingMask(UiRenderer* uiRenderer,
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw,
|
||||
bool firstPass, UiRenderer::BaseState priorBaseState);
|
||||
|
||||
private: // data
|
||||
AZStd::vector<RenderNode*> m_maskRenderNodes; //!< The render nodes used to render the mask shape
|
||||
@@ -175,15 +188,17 @@ namespace LyShine
|
||||
// We use a pool allocator to keep these allocations fast.
|
||||
AZ_CLASS_ALLOCATOR(RenderTargetRenderNode, AZ::PoolAllocator, 0);
|
||||
|
||||
RenderTargetRenderNode(RenderTargetRenderNode* parentRenderTarget, int renderTargetHandle,
|
||||
SDepthTexture* renderTargetDepthSurface,
|
||||
RenderTargetRenderNode(RenderTargetRenderNode* parentRenderTarget,
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage,
|
||||
const AZ::Vector2& viewportTopLeft,
|
||||
const AZ::Vector2& viewportSize,
|
||||
const AZ::Color& clearColor,
|
||||
int nestLevel);
|
||||
~RenderTargetRenderNode() override;
|
||||
|
||||
void Render(UiRenderer* uiRenderer) override;
|
||||
void Render(UiRenderer* uiRenderer
|
||||
, const AZ::Matrix4x4& modelViewProjMat
|
||||
, AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw) override;
|
||||
|
||||
AZStd::vector<RenderNode*>& GetChildRenderNodeList() { return m_childRenderNodes; }
|
||||
const AZStd::vector<RenderNode*>& GetChildRenderNodeList() const { return m_childRenderNodes; }
|
||||
@@ -197,6 +212,9 @@ namespace LyShine
|
||||
AZ::Color GetClearColor() const { return m_clearColor; }
|
||||
|
||||
const char* GetRenderTargetName() const;
|
||||
int GetNestLevel() const;
|
||||
|
||||
const AZ::Data::Instance<AZ::RPI::AttachmentImage> GetRenderTarget() const;
|
||||
|
||||
#ifndef _RELEASE
|
||||
// A debug-only function useful for debugging
|
||||
@@ -213,13 +231,16 @@ namespace LyShine
|
||||
|
||||
RenderTargetRenderNode* m_parentRenderTarget = nullptr; //! Used while building the render graph.
|
||||
|
||||
int m_renderTargetHandle = -1;
|
||||
SDepthTexture* m_renderTargetDepthSurface = nullptr;
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> m_attachmentImage;
|
||||
|
||||
// Each render target requires a unique dynamic draw context to draw to the raster pass associated with the target
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> m_dynamicDraw;
|
||||
|
||||
float m_viewportX = 0;
|
||||
float m_viewportY = 0;
|
||||
float m_viewportWidth = 0;
|
||||
float m_viewportHeight = 0;
|
||||
AZ::Matrix4x4 m_modelViewProjMat;
|
||||
AZ::Color m_clearColor;
|
||||
int m_nestLevel = 0;
|
||||
};
|
||||
@@ -241,9 +262,10 @@ namespace LyShine
|
||||
void StartChildrenForMask() override;
|
||||
void EndMask() override;
|
||||
|
||||
//! Begin rendering to a texture
|
||||
void BeginRenderToTexture(int renderTargetHandle, SDepthTexture* renderTargetDepthSurface,
|
||||
const AZ::Vector2& viewportTopLeft, const AZ::Vector2& viewportSize,
|
||||
const AZ::Color& clearColor) override;
|
||||
const AZ::Vector2& viewportTopLeft, const AZ::Vector2& viewportSize, const AZ::Color& clearColor) override;
|
||||
|
||||
void EndRenderToTexture() override;
|
||||
|
||||
void AddPrimitive(IRenderer::DynUiPrimitive* primitive, ITexture* texture,
|
||||
@@ -268,6 +290,20 @@ namespace LyShine
|
||||
void AddPrimitiveAtom(IRenderer::DynUiPrimitive* primitive, const AZ::Data::Instance<AZ::RPI::Image>& texture,
|
||||
bool isClampTextureMode, bool isTextureSRGB, bool isTexturePremultipliedAlpha, BlendMode blendMode);
|
||||
|
||||
//! Add an indexed triangle list primitive to the render graph which will use maskTexture as an alpha (gradient) mask
|
||||
void AddAlphaMaskPrimitiveAtom(IRenderer::DynUiPrimitive* primitive,
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> contentAttachmentImage,
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> maskAttachmentImage,
|
||||
bool isClampTextureMode,
|
||||
bool isTextureSRGB,
|
||||
bool isTexturePremultipliedAlpha,
|
||||
BlendMode blendMode);
|
||||
|
||||
void BeginRenderToTexture(AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage,
|
||||
const AZ::Vector2& viewportTopLeft,
|
||||
const AZ::Vector2& viewportSize,
|
||||
const AZ::Color& clearColor);
|
||||
|
||||
//! Render the display graph
|
||||
void Render(UiRenderer* uiRenderer, const AZ::Vector2& viewportSize);
|
||||
|
||||
@@ -283,6 +319,8 @@ namespace LyShine
|
||||
//! Test whether the render graph contains any render nodes
|
||||
bool IsEmpty();
|
||||
|
||||
void GetRenderTargetsAndDependencies(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies);
|
||||
|
||||
#ifndef _RELEASE
|
||||
// A debug-only function useful for debugging, not called but calls can be added during debugging
|
||||
void ValidateGraph();
|
||||
@@ -311,6 +349,8 @@ namespace LyShine
|
||||
//! Given a blend mode and whether the shader will be outputing premultiplied alpha, return state flags
|
||||
int GetBlendModeState(LyShine::BlendMode blendMode, bool isShaderOutputPremultAlpha) const;
|
||||
|
||||
void SetRttPassesEnabled(UiRenderer* uiRenderer, bool enabled);
|
||||
|
||||
protected: // data
|
||||
|
||||
AZStd::vector<RenderNode*> m_renderNodes;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace LyShine
|
||||
{
|
||||
//! Ebus to handle render target requests
|
||||
class RenderToTextureRequests
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
virtual AZ::RHI::AttachmentId UseRenderTarget(const AZ::Name& renderTargetName, AZ::RHI::Size size) = 0;
|
||||
virtual void ReleaseRenderTarget(const AZ::RHI::AttachmentId& attachmentId) = 0;
|
||||
virtual AZ::Data::Instance<AZ::RPI::AttachmentImage> GetRenderTarget(const AZ::RHI::AttachmentId& attachmentId) = 0;
|
||||
};
|
||||
|
||||
using RenderToTextureRequestBus = AZ::EBus<RenderToTextureRequests>;
|
||||
}
|
||||
@@ -49,6 +49,8 @@
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
|
||||
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
|
||||
|
||||
#include "Animation/UiAnimationSystem.h"
|
||||
|
||||
@@ -64,6 +66,8 @@
|
||||
#include <LyShine/Bus/UiFaderBus.h>
|
||||
#endif
|
||||
|
||||
#include "LyShinePassDataBus.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! UiCanvasNotificationBus Behavior context handler class
|
||||
class UiCanvasNotificationBusBehaviorHandler
|
||||
@@ -251,14 +255,22 @@ namespace
|
||||
|
||||
UiRenderer* GetUiRendererForGame()
|
||||
{
|
||||
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
|
||||
return lyShine ? lyShine->GetUiRenderer() : nullptr;
|
||||
if (gEnv && gEnv->pLyShine)
|
||||
{
|
||||
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
|
||||
return lyShine->GetUiRenderer();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UiRenderer* GetUiRendererForEditor()
|
||||
{
|
||||
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
|
||||
return lyShine ? lyShine->GetUiRendererForEditor() : nullptr;
|
||||
if (gEnv && gEnv->pLyShine)
|
||||
{
|
||||
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
|
||||
return lyShine->GetUiRendererForEditor();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsValidInteractable(const AZ::EntityId& entityId)
|
||||
@@ -1829,6 +1841,46 @@ void UiCanvasComponent::MarkRenderGraphDirty()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
AZ::RHI::AttachmentId UiCanvasComponent::UseRenderTarget(const AZ::Name& renderTargetName, AZ::RHI::Size size)
|
||||
{
|
||||
// Create a render target that UI elements will render to
|
||||
AZ::RHI::ImageDescriptor imageDesc;
|
||||
imageDesc.m_bindFlags = AZ::RHI::ImageBindFlags::Color | AZ::RHI::ImageBindFlags::ShaderReadWrite;
|
||||
imageDesc.m_size = size;
|
||||
imageDesc.m_format = AZ::RHI::Format::R8G8B8A8_UNORM;
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImagePool> pool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool();
|
||||
auto attachmentImage = AZ::RPI::AttachmentImage::Create(*pool.get(), imageDesc, renderTargetName);
|
||||
if (!attachmentImage)
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create render target");
|
||||
return AZ::RHI::AttachmentId();
|
||||
}
|
||||
|
||||
m_attachmentImageMap[attachmentImage->GetAttachmentId()] = attachmentImage;
|
||||
|
||||
// Notify LyShine render pass that it needs to rebuild
|
||||
QueueRttPassRebuild();
|
||||
|
||||
return attachmentImage->GetAttachmentId();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasComponent::ReleaseRenderTarget(const AZ::RHI::AttachmentId& attachmentId)
|
||||
{
|
||||
m_attachmentImageMap.erase(attachmentId);
|
||||
|
||||
// Notify LyShine render pass that it needs to rebuild
|
||||
QueueRttPassRebuild();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> UiCanvasComponent::GetRenderTarget(const AZ::RHI::AttachmentId& attachmentId)
|
||||
{
|
||||
return m_attachmentImageMap[attachmentId];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasComponent::UpdateCanvas(float deltaTime, bool isInGame)
|
||||
{
|
||||
@@ -1864,6 +1916,8 @@ void UiCanvasComponent::RenderCanvas(bool isInGame, AZ::Vector2 viewportSize, Ui
|
||||
return;
|
||||
}
|
||||
|
||||
m_renderInEditor = uiRenderer ? true : false;
|
||||
|
||||
if (!uiRenderer)
|
||||
{
|
||||
uiRenderer = GetUiRendererForGame();
|
||||
@@ -1948,6 +2002,12 @@ void UiCanvasComponent::ScheduleElementDestroy(AZ::EntityId entityId)
|
||||
m_elementsScheduledForDestroy.push_back(entityId);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasComponent::GetRenderTargets(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies)
|
||||
{
|
||||
m_renderGraph.GetRenderTargetsAndDependencies(attachmentImagesAndDependencies);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasComponent::DestroyScheduledElements()
|
||||
{
|
||||
@@ -1959,6 +2019,17 @@ void UiCanvasComponent::DestroyScheduledElements()
|
||||
m_elementsScheduledForDestroy.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasComponent::QueueRttPassRebuild()
|
||||
{
|
||||
UiRenderer* uiRenderer = m_renderInEditor ? GetUiRendererForEditor() : GetUiRendererForGame();
|
||||
if (uiRenderer && uiRenderer->GetViewportContext()) // can be null in automated testing
|
||||
{
|
||||
AZ::RPI::SceneId sceneId = uiRenderer->GetViewportContext()->GetRenderScene()->GetId();
|
||||
EBUS_EVENT_ID(sceneId, LyShinePassRequestBus, RebuildRttChildren);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef _RELEASE
|
||||
void UiCanvasComponent::GetDebugInfoInteractables(AZ::EntityId& activeInteractable, AZ::EntityId& hoverInteractable) const
|
||||
@@ -2350,6 +2421,7 @@ void UiCanvasComponent::Activate()
|
||||
UiCanvasComponentImplementationBus::Handler::BusConnect(m_entity->GetId());
|
||||
UiEditorCanvasBus::Handler::BusConnect(m_entity->GetId());
|
||||
UiAnimationBus::Handler::BusConnect(m_entity->GetId());
|
||||
LyShine::RenderToTextureRequestBus::Handler::BusConnect(m_entity->GetId());
|
||||
|
||||
// Reconnect to buses that we connect to intermittently
|
||||
// This will only happen if we have been deactivated and reactivated at runtime
|
||||
@@ -2382,6 +2454,7 @@ void UiCanvasComponent::Deactivate()
|
||||
UiCanvasComponentImplementationBus::Handler::BusDisconnect();
|
||||
UiEditorCanvasBus::Handler::BusDisconnect();
|
||||
UiAnimationBus::Handler::BusDisconnect();
|
||||
LyShine::RenderToTextureRequestBus::Handler::BusDisconnect();
|
||||
|
||||
// disconnect from any other buses we could be connected to
|
||||
if (m_hoverInteractable.IsValid() && AZ::EntityBus::Handler::BusIsConnectedId(m_hoverInteractable))
|
||||
@@ -2400,6 +2473,12 @@ void UiCanvasComponent::Deactivate()
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
|
||||
// Destroy owned render targets
|
||||
m_attachmentImageMap.clear();
|
||||
|
||||
//! Notify LyShine pass that it needs to rebuild
|
||||
QueueRttPassRebuild();
|
||||
|
||||
delete m_layoutManager;
|
||||
m_layoutManager = nullptr;
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "TextureAtlas/TextureAtlasBus.h"
|
||||
#include "TextureAtlas/TextureAtlasNotificationBus.h"
|
||||
|
||||
#include "RenderToTextureBus.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class SerializeContext;
|
||||
@@ -51,6 +53,7 @@ class UiCanvasComponent
|
||||
, public IUiAnimationListener
|
||||
, public UiEditorCanvasBus::Handler
|
||||
, public UiCanvasComponentImplementationBus::Handler
|
||||
, public LyShine::RenderToTextureRequestBus::Handler
|
||||
{
|
||||
public: // constants
|
||||
static const AZ::Vector2 s_defaultCanvasSize;
|
||||
@@ -232,6 +235,12 @@ public: // member functions
|
||||
void MarkRenderGraphDirty() override;
|
||||
// ~UiCanvasComponentImplementationInterface
|
||||
|
||||
// RenderToTextureRequests
|
||||
AZ::RHI::AttachmentId UseRenderTarget(const AZ::Name& renderTargetName, AZ::RHI::Size size) override;
|
||||
void ReleaseRenderTarget(const AZ::RHI::AttachmentId& attachmentId) override;
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> GetRenderTarget(const AZ::RHI::AttachmentId& attachmentId) override;
|
||||
// ~RenderToTextureRequests
|
||||
|
||||
void UpdateCanvas(float deltaTime, bool isInGame);
|
||||
void RenderCanvas(bool isInGame, AZ::Vector2 viewportSize, UiRenderer* uiRenderer = nullptr);
|
||||
|
||||
@@ -257,6 +266,10 @@ public: // member functions
|
||||
//! Queue an element to be destroyed at end of frame
|
||||
void ScheduleElementDestroy(AZ::EntityId entityId);
|
||||
|
||||
bool IsRenderGraphDirty() { return m_renderGraph.GetDirtyFlag(); }
|
||||
|
||||
void GetRenderTargets(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies);
|
||||
|
||||
#ifndef _RELEASE
|
||||
struct DebugInfoNumElements
|
||||
{
|
||||
@@ -427,6 +440,9 @@ private: // member functions
|
||||
|
||||
void DestroyScheduledElements();
|
||||
|
||||
//! Notify LyShine pass that it needs to rebuild its Rtt child passes
|
||||
void QueueRttPassRebuild();
|
||||
|
||||
private: // static member functions
|
||||
|
||||
static AZ::u64 CreateUniqueId();
|
||||
@@ -597,4 +613,8 @@ private: // static data
|
||||
|
||||
LyShine::RenderGraph m_renderGraph; //!< the render graph for rendering the canvas, can be cached between frames
|
||||
bool m_isRendering = false;
|
||||
bool m_renderInEditor = false; //!< indicates whether this canvas will render in the Editor viewport or the Game viewport
|
||||
|
||||
//! Map of attachments used by this canvas's elements
|
||||
AZStd::unordered_map<AZ::RHI::AttachmentId, AZ::Data::Instance<AZ::RPI::AttachmentImage>> m_attachmentImageMap;
|
||||
};
|
||||
|
||||
@@ -301,6 +301,17 @@ void UiCanvasManager::OnFontTextureUpdated([[maybe_unused]] IFFont* font)
|
||||
m_fontTextureHasChanged = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasManager::GetRenderTargets(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies)
|
||||
{
|
||||
for (auto canvas : m_loadedCanvases)
|
||||
{
|
||||
LyShine::AttachmentImagesAndDependencies canvasTargets;
|
||||
canvas->GetRenderTargets(canvasTargets);
|
||||
attachmentImagesAndDependencies.insert(attachmentImagesAndDependencies.end(), canvasTargets.begin(), canvasTargets.end());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasManager::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
|
||||
{
|
||||
@@ -606,13 +617,6 @@ void UiCanvasManager::RenderLoadedCanvases()
|
||||
m_fontTextureHasChanged = false;
|
||||
}
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // render target conversion to Atom
|
||||
// clear the stencil buffer before rendering the loaded canvases - required for masking
|
||||
// NOTE: We want to use ClearTargetsImmediately instead of ClearTargetsLater since we will not be setting the render target
|
||||
ColorF viewportBackgroundColor(0, 0, 0, 0); // if clearing color we want to set alpha to zero also
|
||||
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR_STENCIL, viewportBackgroundColor);
|
||||
#endif
|
||||
|
||||
for (auto canvas : m_loadedCanvases)
|
||||
{
|
||||
if (!canvas->GetIsRenderToTexture())
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <LyShine/Bus/UiCanvasManagerBus.h>
|
||||
#include <LyShine/Bus/UiCanvasBus.h>
|
||||
#include <LyShine/UiEntityContext.h>
|
||||
#include "LyShinePassDataBus.h"
|
||||
#include <IFont.h>
|
||||
|
||||
class UiCanvasComponent;
|
||||
@@ -92,6 +93,9 @@ public: // member functions
|
||||
bool HandleInputEventForLoadedCanvases(const AzFramework::InputChannel& inputChannel);
|
||||
bool HandleTextEventForLoadedCanvases(const AZStd::string& textUTF8);
|
||||
|
||||
// Get the render targets used by all currently loaded UI Canvases
|
||||
void GetRenderTargets(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies);
|
||||
|
||||
#ifndef _RELEASE
|
||||
void DebugDisplayCanvasData(int setting) const;
|
||||
void DebugDisplayDrawCallData() const;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
#include "UiFaderComponent.h"
|
||||
#include "RenderGraph.h"
|
||||
#include <LyShine/Draw2d.h>
|
||||
|
||||
#include <AzCore/Math/Crc.h>
|
||||
@@ -14,6 +15,9 @@
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <AtomCore/Instance/Instance.h>
|
||||
|
||||
#include <LyShine/Bus/UiElementBus.h>
|
||||
#include <LyShine/Bus/UiRenderBus.h>
|
||||
#include <LyShine/Bus/UiCanvasBus.h>
|
||||
@@ -22,6 +26,7 @@
|
||||
#include <ITimer.h>
|
||||
|
||||
#include "UiSerialize.h"
|
||||
#include "RenderToTextureBus.h"
|
||||
|
||||
// BehaviorContext UiFaderNotificationBus forwarder
|
||||
class BehaviorUiFaderNotificationBusHandler
|
||||
@@ -120,7 +125,7 @@ void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInter
|
||||
AZ::Vector2 renderTargetSize = pixelAlignedBottomRight - pixelAlignedTopLeft;
|
||||
|
||||
bool needsResize = static_cast<int>(renderTargetSize.GetX()) != m_renderTargetWidth || static_cast<int>(renderTargetSize.GetY()) != m_renderTargetHeight;
|
||||
if (m_renderTargetHandle == -1 || needsResize)
|
||||
if (m_attachmentImageId.IsEmpty() || needsResize)
|
||||
{
|
||||
// We delay first creation of the render target until render time since size is not known in Activate
|
||||
// We also call this if the size has changed
|
||||
@@ -128,7 +133,7 @@ void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInter
|
||||
}
|
||||
|
||||
// if the render target failed to be created (zero size for example) we don't render the element at all
|
||||
if (m_renderTargetHandle == -1)
|
||||
if (m_attachmentImageId.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +144,7 @@ void UiFaderComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInter
|
||||
else
|
||||
{
|
||||
// destroy previous render target, if exists
|
||||
if (m_renderTargetHandle != -1)
|
||||
if (!m_attachmentImageId.IsEmpty())
|
||||
{
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
@@ -452,54 +457,22 @@ void UiFaderComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAligne
|
||||
m_viewportTopLeft = pixelAlignedTopLeft;
|
||||
m_viewportSize = renderTargetSize;
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // [LYN-3359] Support RTT using Atom
|
||||
// Check if the render target already exists
|
||||
if (m_renderTargetHandle != -1)
|
||||
{
|
||||
// Render target exists, resize it to the given size
|
||||
if (!gEnv->pRenderer->ResizeRenderTarget(m_renderTargetHandle, static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY())))
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to resize render target for UiFaderComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a render target that this element and its children will be rendered to.
|
||||
m_renderTargetHandle = gEnv->pRenderer->CreateRenderTarget(m_renderTargetName.c_str(),
|
||||
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()), Clr_Transparent, eTF_R8G8B8A8);
|
||||
// LYSHINE_ATOM_TODO: optimize by reusing/resizing targets
|
||||
DestroyRenderTarget();
|
||||
|
||||
if (m_renderTargetHandle == -1)
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create render target for UiFaderComponent");
|
||||
}
|
||||
}
|
||||
|
||||
// if depth surface already exists then destroy it
|
||||
if (m_renderTargetDepthSurface)
|
||||
// Create a render target that this element and its children will be rendered to
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
AZ::RHI::Size imageSize(renderTargetSize.GetX(), renderTargetSize.GetY(), 1);
|
||||
EBUS_EVENT_ID_RESULT(m_attachmentImageId, canvasEntityId, LyShine::RenderToTextureRequestBus, UseRenderTarget, AZ::Name(m_renderTargetName.c_str()), imageSize);
|
||||
if (m_attachmentImageId.IsEmpty())
|
||||
{
|
||||
gEnv->pRenderer->DestroyDepthSurface(m_renderTargetDepthSurface);
|
||||
m_renderTargetDepthSurface = nullptr;
|
||||
AZ_Warning("UI", false, "Failed to create render target for UiFaderComponent");
|
||||
}
|
||||
|
||||
if (m_renderTargetHandle != -1)
|
||||
{
|
||||
// Also create a depth surface to render the canvas to, we need depth for masking
|
||||
// since that uses the stencil buffer. We support any combination of nesting faders and masks
|
||||
m_renderTargetDepthSurface = gEnv->pRenderer->CreateDepthSurface(
|
||||
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()));
|
||||
|
||||
if (!m_renderTargetDepthSurface)
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create depth surface for UiFaderComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// at this point either all render targets and depth surfaces are created or none are.
|
||||
// If all succeeded then update the render target size
|
||||
if (m_renderTargetHandle != -1)
|
||||
if (!m_attachmentImageId.IsEmpty())
|
||||
{
|
||||
m_renderTargetWidth = static_cast<int>(renderTargetSize.GetX());
|
||||
m_renderTargetHeight = static_cast<int>(renderTargetSize.GetY());
|
||||
@@ -511,16 +484,12 @@ void UiFaderComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAligne
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiFaderComponent::DestroyRenderTarget()
|
||||
{
|
||||
if (m_renderTargetHandle != -1)
|
||||
if (!m_attachmentImageId.IsEmpty())
|
||||
{
|
||||
gEnv->pRenderer->DestroyRenderTarget(m_renderTargetHandle);
|
||||
m_renderTargetHandle = -1;
|
||||
}
|
||||
|
||||
if (m_renderTargetDepthSurface)
|
||||
{
|
||||
gEnv->pRenderer->DestroyDepthSurface(m_renderTargetDepthSurface);
|
||||
m_renderTargetDepthSurface = nullptr;
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
EBUS_EVENT_ID(canvasEntityId, LyShine::RenderToTextureRequestBus, ReleaseRenderTarget, m_attachmentImageId);
|
||||
m_attachmentImageId = AZ::RHI::AttachmentId{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,14 +563,20 @@ void UiFaderComponent::RenderStandardFader(LyShine::IRenderGraph* renderGraph, U
|
||||
void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElementInterface* elementInterface,
|
||||
UiRenderInterface* renderInterface, int numChildren, bool isInGame)
|
||||
{
|
||||
// Get the render target
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage;
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
EBUS_EVENT_ID_RESULT(attachmentImage, canvasEntityId, LyShine::RenderToTextureRequestBus, GetRenderTarget, m_attachmentImageId);
|
||||
|
||||
// Render the element and its children to a render target
|
||||
{
|
||||
// we always clear to transparent black - the accumulation of alpha in the render target requires it
|
||||
AZ::Color clearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Start building the render to texture node in the render graph
|
||||
renderGraph->BeginRenderToTexture(m_renderTargetHandle, m_renderTargetDepthSurface,
|
||||
m_viewportTopLeft, m_viewportSize, clearColor);
|
||||
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
|
||||
lyRenderGraph->BeginRenderToTexture(attachmentImage, m_viewportTopLeft, m_viewportSize, clearColor);
|
||||
|
||||
// We don't want this fader or parent faders to affect what is rendered to the render target since we will
|
||||
// apply those fades when we render from the render target.
|
||||
@@ -624,14 +599,13 @@ void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElem
|
||||
float desiredAlpha = renderGraph->GetAlphaFade() * m_fade;
|
||||
uint8 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
|
||||
|
||||
UCol desiredPackedColor;
|
||||
// This is a special case. We have an input texture that already has premultiplied alpha.
|
||||
// So we tell the shader not to premultiply the output colors and we premultiply the alpha
|
||||
// into the vertex colors so that they are premultiplied too.
|
||||
desiredPackedColor.r = desiredPackedColor.g = desiredPackedColor.b = desiredPackedColor.a = desiredPackedAlpha;
|
||||
if (m_cachedPrimitive.m_vertices[0].color.dcolor != desiredPackedColor.dcolor)
|
||||
// If the fade value has changed we need to update the alpha values in the vertex colors but we do
|
||||
// not want to touch or recompute the RGB values
|
||||
if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
|
||||
{
|
||||
// go through the cached vertices and update the color values
|
||||
// go through all the cached vertices and update the alpha values
|
||||
UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
|
||||
desiredPackedColor.a = desiredPackedAlpha;
|
||||
for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
|
||||
{
|
||||
m_cachedPrimitive.m_vertices[i].color = desiredPackedColor;
|
||||
@@ -639,21 +613,20 @@ void UiFaderComponent::RenderRttFader(LyShine::IRenderGraph* renderGraph, UiElem
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // [LYN-3359] Support RTT using Atom
|
||||
// Add a primitive to render a quad using the render target we have created
|
||||
{
|
||||
// Set the texture and other render state required
|
||||
ITexture* texture = gEnv->pRenderer->EF_GetTextureByID(m_renderTargetHandle);
|
||||
bool isClampTextureMode = true;
|
||||
bool isTextureSRGB = true;
|
||||
bool isTexturePremultipliedAlpha = true;
|
||||
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
|
||||
|
||||
// add a render node to render from the render target texture to the current target
|
||||
renderGraph->AddPrimitive(&m_cachedPrimitive, texture,
|
||||
isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
|
||||
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
|
||||
if (lyRenderGraph)
|
||||
{
|
||||
// Set the texture and other render state required
|
||||
AZ::Data::Instance<AZ::RPI::Image> image = attachmentImage;
|
||||
bool isClampTextureMode = true;
|
||||
bool isTextureSRGB = true;
|
||||
bool isTexturePremultipliedAlpha = true;
|
||||
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
|
||||
lyRenderGraph->AddPrimitiveAtom(&m_cachedPrimitive, image, isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Math/Color.h>
|
||||
#include <Atom/RHI.Reflect/AttachmentId.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class UiFaderComponent
|
||||
@@ -156,11 +157,8 @@ private: // data
|
||||
//! This is generated from the entity ID and cached
|
||||
AZStd::string m_renderTargetName;
|
||||
|
||||
//! When rendering to a texture this is the texture ID of the render target
|
||||
int m_renderTargetHandle = -1;
|
||||
|
||||
//! When rendering to a texture this is our depth surface
|
||||
SDepthTexture* m_renderTargetDepthSurface = nullptr;
|
||||
//! When rendering to a texture this is the attachment image for the render target
|
||||
AZ::RHI::AttachmentId m_attachmentImageId;
|
||||
|
||||
//! The positions used for the render to texture viewport and to render the render target to the screen
|
||||
AZ::Vector2 m_viewportTopLeft;
|
||||
|
||||
@@ -14,12 +14,17 @@
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
#include "IRenderer.h"
|
||||
#include "RenderToTextureBus.h"
|
||||
#include "RenderGraph.h"
|
||||
#include <LyShine/Bus/UiTransformBus.h>
|
||||
#include <LyShine/Bus/UiElementBus.h>
|
||||
#include <LyShine/Bus/UiRenderBus.h>
|
||||
#include <LyShine/Bus/UiVisualBus.h>
|
||||
#include <LyShine/Bus/UiCanvasBus.h>
|
||||
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <AtomCore/Instance/Instance.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC MEMBER FUNCTIONS
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -79,7 +84,7 @@ void UiMaskComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInterf
|
||||
AZ::Vector2 renderTargetSize = pixelAlignedBottomRight - pixelAlignedTopLeft;
|
||||
|
||||
bool needsResize = static_cast<int>(renderTargetSize.GetX()) != m_renderTargetWidth || static_cast<int>(renderTargetSize.GetY()) != m_renderTargetHeight;
|
||||
if (m_contentRenderTargetHandle == -1 || needsResize)
|
||||
if (m_contentAttachmentImageId.IsEmpty() || needsResize)
|
||||
{
|
||||
// Need to create or resize the render target
|
||||
CreateOrResizeRenderTarget(pixelAlignedTopLeft, pixelAlignedBottomRight);
|
||||
@@ -89,7 +94,7 @@ void UiMaskComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInterf
|
||||
// in theory the child mask element could still be non-zero size and could reveal things. But the way gradient masks
|
||||
// currently work is that the size of the render target is defined by the size of this element, therefore nothing would
|
||||
// be revealed by the mask if it is zero sized.
|
||||
if (m_contentRenderTargetHandle == -1)
|
||||
if (m_contentAttachmentImageId.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -101,7 +106,7 @@ void UiMaskComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInterf
|
||||
else
|
||||
{
|
||||
// using stencil mask, not going to use render targets, destroy previous render target, if exists
|
||||
if (m_contentRenderTargetHandle != -1)
|
||||
if (!m_contentAttachmentImageId.IsEmpty())
|
||||
{
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
@@ -113,7 +118,7 @@ void UiMaskComponent::Render(LyShine::IRenderGraph* renderGraph, UiElementInterf
|
||||
else
|
||||
{
|
||||
// masking disabled, not going to use render targets, destroy previous render target, if exists
|
||||
if (m_contentRenderTargetHandle != -1)
|
||||
if (!m_contentAttachmentImageId.IsEmpty())
|
||||
{
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
@@ -553,77 +558,30 @@ void UiMaskComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAligned
|
||||
m_viewportTopLeft = pixelAlignedTopLeft;
|
||||
m_viewportSize = renderTargetSize;
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // [LYN-3359] Support RTT using Atom
|
||||
// Check if the render target already exists
|
||||
if (m_contentRenderTargetHandle != -1)
|
||||
{
|
||||
// Render target exists, resize it to the given size
|
||||
if (!gEnv->pRenderer->ResizeRenderTarget(m_contentRenderTargetHandle, static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY())))
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to resize content render target for UiMaskComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a render target that this element and its children will be rendered to.
|
||||
m_contentRenderTargetHandle = gEnv->pRenderer->CreateRenderTarget(m_renderTargetName.c_str(),
|
||||
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()), Clr_Transparent, eTF_R8G8B8A8);
|
||||
// LYSHINE_ATOM_TODO: optimize by reusing/resizing targets
|
||||
DestroyRenderTarget();
|
||||
|
||||
if (m_contentRenderTargetHandle == -1)
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create content render target for UiMaskComponent");
|
||||
}
|
||||
// Create a render target that this element and its children will be rendered to
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
AZ::RHI::Size imageSize(renderTargetSize.GetX(), renderTargetSize.GetY(), 1);
|
||||
EBUS_EVENT_ID_RESULT(m_contentAttachmentImageId, canvasEntityId, LyShine::RenderToTextureRequestBus, UseRenderTarget, AZ::Name(m_renderTargetName.c_str()), imageSize);
|
||||
if (m_contentAttachmentImageId.IsEmpty())
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create content render target for UiMaskComponent");
|
||||
}
|
||||
|
||||
// if depth surface already exists then destroy it
|
||||
if (m_renderTargetDepthSurface)
|
||||
// Create separate render target for the mask texture
|
||||
EBUS_EVENT_ID_RESULT(m_maskAttachmentImageId, canvasEntityId, LyShine::RenderToTextureRequestBus, UseRenderTarget, AZ::Name(m_maskRenderTargetName.c_str()), imageSize);
|
||||
if (m_maskAttachmentImageId.IsEmpty())
|
||||
{
|
||||
gEnv->pRenderer->DestroyDepthSurface(m_renderTargetDepthSurface);
|
||||
m_renderTargetDepthSurface = nullptr;
|
||||
AZ_Warning("UI", false, "Failed to create mask render target for UiMaskComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
|
||||
if (m_contentRenderTargetHandle != -1)
|
||||
{
|
||||
// Also create a depth surface to render the canvas to, we need depth for masking
|
||||
// since that uses the stencil buffer. We support any combination of nesting faders and masks
|
||||
m_renderTargetDepthSurface = gEnv->pRenderer->CreateDepthSurface(
|
||||
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()));
|
||||
|
||||
if (!m_renderTargetDepthSurface)
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create depth surface for UiMaskComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the mask render target already exists
|
||||
if (m_maskRenderTargetHandle != -1)
|
||||
{
|
||||
// Render target exists, resize it to the given size
|
||||
if (!gEnv->pRenderer->ResizeRenderTarget(m_maskRenderTargetHandle, static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY())))
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to resize mask render target for UiMaskComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create separate render target for the mask texture
|
||||
m_maskRenderTargetHandle = gEnv->pRenderer->CreateRenderTarget(m_maskRenderTargetName.c_str(),
|
||||
static_cast<int>(renderTargetSize.GetX()), static_cast<int>(renderTargetSize.GetY()), Clr_Transparent, eTF_R8G8B8A8);
|
||||
|
||||
if (m_maskRenderTargetHandle == -1)
|
||||
{
|
||||
AZ_Warning("UI", false, "Failed to create mask render target for UiMaskComponent");
|
||||
DestroyRenderTarget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// at this point either all render targets and depth surfaces are created or none are.
|
||||
// If all succeeded then update the render target size
|
||||
if (m_contentRenderTargetHandle != -1)
|
||||
if (!m_contentAttachmentImageId.IsEmpty())
|
||||
{
|
||||
m_renderTargetWidth = static_cast<int>(renderTargetSize.GetX());
|
||||
m_renderTargetHeight = static_cast<int>(renderTargetSize.GetY());
|
||||
@@ -635,22 +593,22 @@ void UiMaskComponent::CreateOrResizeRenderTarget(const AZ::Vector2& pixelAligned
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiMaskComponent::DestroyRenderTarget()
|
||||
{
|
||||
if (m_contentRenderTargetHandle != -1)
|
||||
if (!m_contentAttachmentImageId.IsEmpty())
|
||||
{
|
||||
gEnv->pRenderer->DestroyRenderTarget(m_contentRenderTargetHandle);
|
||||
m_contentRenderTargetHandle = -1;
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
EBUS_EVENT_ID(canvasEntityId, LyShine::RenderToTextureRequestBus, ReleaseRenderTarget, m_contentAttachmentImageId);
|
||||
|
||||
m_contentAttachmentImageId = AZ::RHI::AttachmentId{};
|
||||
}
|
||||
|
||||
if (m_renderTargetDepthSurface)
|
||||
if (!m_maskAttachmentImageId.IsEmpty())
|
||||
{
|
||||
gEnv->pRenderer->DestroyDepthSurface(m_renderTargetDepthSurface);
|
||||
m_renderTargetDepthSurface = nullptr;
|
||||
}
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
EBUS_EVENT_ID(canvasEntityId, LyShine::RenderToTextureRequestBus, ReleaseRenderTarget, m_maskAttachmentImageId);
|
||||
|
||||
if (m_maskRenderTargetHandle != -1)
|
||||
{
|
||||
gEnv->pRenderer->DestroyRenderTarget(m_maskRenderTargetHandle);
|
||||
m_maskRenderTargetHandle = -1;
|
||||
m_maskAttachmentImageId = AZ::RHI::AttachmentId{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,6 +705,14 @@ void UiMaskComponent::RenderUsingGradientMask(LyShine::IRenderGraph* renderGraph
|
||||
// we always clear to transparent black - the accumulation of alpha in the render target requires it
|
||||
AZ::Color clearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Get the render targets
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> contentAttachmentImage;
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> maskAttachmentImage;
|
||||
AZ::EntityId canvasEntityId;
|
||||
EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId);
|
||||
EBUS_EVENT_ID_RESULT(contentAttachmentImage, canvasEntityId, LyShine::RenderToTextureRequestBus, GetRenderTarget, m_contentAttachmentImageId);
|
||||
EBUS_EVENT_ID_RESULT(maskAttachmentImage, canvasEntityId, LyShine::RenderToTextureRequestBus, GetRenderTarget, m_maskAttachmentImageId);
|
||||
|
||||
// We don't want parent faders to affect what is rendered to the render target since we will
|
||||
// apply those fades when we render from the render target.
|
||||
// Note that this means that, if there are parent (no render to texture) faders, we get a "free"
|
||||
@@ -756,8 +722,8 @@ void UiMaskComponent::RenderUsingGradientMask(LyShine::IRenderGraph* renderGraph
|
||||
// mask render target
|
||||
{
|
||||
// Start building the render to texture node in the render graph
|
||||
renderGraph->BeginRenderToTexture(m_maskRenderTargetHandle, m_renderTargetDepthSurface,
|
||||
m_viewportTopLeft, m_viewportSize, clearColor);
|
||||
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
|
||||
lyRenderGraph->BeginRenderToTexture(maskAttachmentImage, m_viewportTopLeft, m_viewportSize, clearColor);
|
||||
|
||||
// Render the visual component for this element (if there is one) plus the child mask element (if there is one)
|
||||
RenderMaskPrimitives(renderGraph, renderInterface, childMaskElementInterface, isInGame);
|
||||
@@ -769,8 +735,8 @@ void UiMaskComponent::RenderUsingGradientMask(LyShine::IRenderGraph* renderGraph
|
||||
// content render target
|
||||
{
|
||||
// Start building the render to texture node for the content render target in the render graph
|
||||
renderGraph->BeginRenderToTexture(m_contentRenderTargetHandle, m_renderTargetDepthSurface,
|
||||
m_viewportTopLeft, m_viewportSize, clearColor);
|
||||
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
|
||||
lyRenderGraph->BeginRenderToTexture(contentAttachmentImage, m_viewportTopLeft, m_viewportSize, clearColor);
|
||||
|
||||
// Render the "content" - the child elements excluding the child mask element (if any)
|
||||
RenderContentPrimitives(renderGraph, elementInterface, childMaskElementInterface, numChildren, isInGame);
|
||||
@@ -790,14 +756,13 @@ void UiMaskComponent::RenderUsingGradientMask(LyShine::IRenderGraph* renderGraph
|
||||
float desiredAlpha = renderGraph->GetAlphaFade();
|
||||
uint32 desiredPackedAlpha = static_cast<uint8>(desiredAlpha * 255.0f);
|
||||
|
||||
UCol desiredPackedColor;
|
||||
// This is a special case. We have an input texture that already has premultiplied alpha.
|
||||
// So we tell the shader not to premultiply the output colors and we premultiply the alpha
|
||||
// into the vertex colors so that they are premultiplied too.
|
||||
desiredPackedColor.r = desiredPackedColor.g = desiredPackedColor.b = desiredPackedColor.a = desiredPackedAlpha;
|
||||
if (m_cachedPrimitive.m_vertices[0].color.dcolor != desiredPackedColor.dcolor)
|
||||
// If the fade value has changed we need to update the alpha values in the vertex colors but we do
|
||||
// not want to touch or recompute the RGB values
|
||||
if (m_cachedPrimitive.m_vertices[0].color.a != desiredPackedAlpha)
|
||||
{
|
||||
// go through the cached vertices and update the color values
|
||||
// go through all the cached vertices and update the alpha values
|
||||
UCol desiredPackedColor = m_cachedPrimitive.m_vertices[0].color;
|
||||
desiredPackedColor.a = desiredPackedAlpha;
|
||||
for (int i = 0; i < m_cachedPrimitive.m_numVertices; ++i)
|
||||
{
|
||||
m_cachedPrimitive.m_vertices[i].color = desiredPackedColor;
|
||||
@@ -805,22 +770,29 @@ void UiMaskComponent::RenderUsingGradientMask(LyShine::IRenderGraph* renderGraph
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LYSHINE_ATOM_TODO // [LYN-3359] Support RTT using Atom
|
||||
// Add a primitive to do the alpha mask
|
||||
{
|
||||
// Set the texture and other render state required
|
||||
ITexture* texture = gEnv->pRenderer->EF_GetTextureByID(m_contentRenderTargetHandle);
|
||||
ITexture* maskTexture = gEnv->pRenderer->EF_GetTextureByID(m_maskRenderTargetHandle);
|
||||
bool isClampTextureMode = true;
|
||||
bool isTextureSRGB = true;
|
||||
bool isTexturePremultipliedAlpha = true;
|
||||
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
|
||||
LyShine::RenderGraph* lyRenderGraph = dynamic_cast<LyShine::RenderGraph*>(renderGraph);
|
||||
if (lyRenderGraph)
|
||||
{
|
||||
// Set the texture and other render state required
|
||||
AZ::Data::Instance<AZ::RPI::Image> contentImage = contentAttachmentImage;
|
||||
AZ::Data::Instance<AZ::RPI::Image> maskImage = maskAttachmentImage;
|
||||
bool isClampTextureMode = true;
|
||||
bool isTextureSRGB = true;
|
||||
bool isTexturePremultipliedAlpha = false;
|
||||
LyShine::BlendMode blendMode = LyShine::BlendMode::Normal;
|
||||
|
||||
// add a render node to render using the two render targets, one as an alpha mask of the other
|
||||
renderGraph->AddAlphaMaskPrimitive(&m_cachedPrimitive, texture, maskTexture,
|
||||
isClampTextureMode, isTextureSRGB, isTexturePremultipliedAlpha, blendMode);
|
||||
// add a render node to render using the two render targets, one as an alpha mask of the other
|
||||
lyRenderGraph->AddAlphaMaskPrimitiveAtom(&m_cachedPrimitive,
|
||||
contentAttachmentImage,
|
||||
maskAttachmentImage,
|
||||
isClampTextureMode,
|
||||
isTextureSRGB,
|
||||
isTexturePremultipliedAlpha,
|
||||
blendMode);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <LyShine/IRenderGraph.h>
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <Atom/RHI.Reflect/AttachmentId.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class UiMaskComponent
|
||||
@@ -184,15 +185,16 @@ private: // data
|
||||
//! This is generated from the entity ID and cached
|
||||
AZStd::string m_maskRenderTargetName;
|
||||
|
||||
//! When rendering to a texture this is the texture ID of the render target
|
||||
int m_contentRenderTargetHandle = -1;
|
||||
//! When rendering to a texture this is the attachment image for the render target
|
||||
AZ::RHI::AttachmentId m_contentAttachmentImageId;
|
||||
|
||||
//! When rendering to a texture this is our depth surface, we use the same one for rendering the mask elements
|
||||
//! and the content elements - it is cleared in between.
|
||||
SDepthTexture* m_renderTargetDepthSurface = nullptr;
|
||||
|
||||
//! When rendering to a texture this is the texture ID of the render target
|
||||
int m_maskRenderTargetHandle = -1;
|
||||
//! When rendering to a texture this is the attachment image for the render target
|
||||
AZ::RHI::AttachmentId m_maskAttachmentImageId;
|
||||
|
||||
//! The positions used for the render to texture viewport and to render the render target to the screen
|
||||
AZ::Vector2 m_viewportTopLeft = AZ::Vector2::CreateZero();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
#include "UiRenderer.h"
|
||||
#include "LyShinePassDataBus.h"
|
||||
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
|
||||
@@ -60,25 +61,32 @@ void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstra
|
||||
AZ::Data::Instance<AZ::RPI::Shader> uiShader = AZ::RPI::LoadShader(uiShaderFilepath);
|
||||
|
||||
// Create scene to be used by the dynamic draw context
|
||||
AZ::RPI::ScenePtr scene;
|
||||
if (m_viewportContext)
|
||||
{
|
||||
// Create a new scene based on the user specified viewport context
|
||||
scene = CreateScene(m_viewportContext);
|
||||
m_scene = CreateScene(m_viewportContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No viewport context specified, use default scene
|
||||
scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
m_scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
}
|
||||
|
||||
// Create a dynamic draw context for UI Canvas drawing for the scene
|
||||
CreateDynamicDrawContext(scene, uiShader);
|
||||
m_dynamicDraw = CreateDynamicDrawContext(m_scene, uiShader);
|
||||
|
||||
// Cache shader data such as input indices for later use
|
||||
CacheShaderData(m_dynamicDraw);
|
||||
if (m_dynamicDraw)
|
||||
{
|
||||
// Cache shader data such as input indices for later use
|
||||
CacheShaderData(m_dynamicDraw);
|
||||
|
||||
m_isRPIReady = true;
|
||||
m_isRPIReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(LogName, false, "Failed to create a dynamic draw context for LyShine. \
|
||||
This can happen if the LyShine pass hasn't been added to the main render pipeline.");
|
||||
}
|
||||
}
|
||||
|
||||
AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr<AZ::RPI::ViewportContext> viewportContext)
|
||||
@@ -107,22 +115,40 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr<AZ::RPI::ViewportCon
|
||||
return atomScene;
|
||||
}
|
||||
|
||||
void UiRenderer::CreateDynamicDrawContext(AZ::RPI::ScenePtr scene, AZ::Data::Instance<AZ::RPI::Shader> uiShader)
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> UiRenderer::CreateDynamicDrawContext(
|
||||
AZ::RPI::ScenePtr scene,
|
||||
AZ::Data::Instance<AZ::RPI::Shader> uiShader)
|
||||
{
|
||||
m_dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
|
||||
// Find the pass that renders the UI canvases after the rtt passes
|
||||
AZ::RPI::RasterPass* uiCanvasPass = nullptr;
|
||||
AZ::RPI::SceneId sceneId = m_scene->GetId();
|
||||
LyShinePassRequestBus::EventResult(uiCanvasPass, sceneId, &LyShinePassRequestBus::Events::GetUiCanvasPass);
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
|
||||
|
||||
// Initialize the dynamic draw context
|
||||
m_dynamicDraw->InitShader(uiShader);
|
||||
m_dynamicDraw->InitVertexFormat(
|
||||
dynamicDraw->InitShader(uiShader);
|
||||
dynamicDraw->InitVertexFormat(
|
||||
{ { "POSITION", AZ::RHI::Format::R32G32_FLOAT },
|
||||
{ "COLOR", AZ::RHI::Format::B8G8R8A8_UNORM },
|
||||
{ "TEXCOORD", AZ::RHI::Format::R32G32_FLOAT },
|
||||
{ "BLENDINDICES", AZ::RHI::Format::R16G16_UINT } }
|
||||
);
|
||||
m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState
|
||||
dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState
|
||||
| AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode);
|
||||
m_dynamicDraw->SetOutputScope(scene.get());
|
||||
m_dynamicDraw->EndInit();
|
||||
|
||||
if (uiCanvasPass)
|
||||
{
|
||||
dynamicDraw->SetOutputScope(uiCanvasPass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Render target support is disabled
|
||||
dynamicDraw->SetOutputScope(m_scene.get());
|
||||
}
|
||||
dynamicDraw->EndInit();
|
||||
|
||||
return dynamicDraw;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AZ::RPI::ViewportContext> UiRenderer::GetViewportContext()
|
||||
@@ -158,19 +184,26 @@ void UiRenderer::CacheShaderData(const AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext>
|
||||
isClampIndexName);
|
||||
|
||||
// Cache shader variants that will be used
|
||||
// LYSHINE_ATOM_TODO - more variants will be used in future phase (masks/render target support)
|
||||
AZ::RPI::ShaderOptionList shaderOptionsDefault;
|
||||
shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false")));
|
||||
shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("false")));
|
||||
shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true")));
|
||||
shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
|
||||
m_uiShaderData.m_shaderVariantDefault = dynamicDraw->UseShaderVariant(shaderOptionsDefault);
|
||||
AZ::RPI::ShaderOptionList shaderOptionsAlphaTest;
|
||||
shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false")));
|
||||
shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("true")));
|
||||
shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true")));
|
||||
shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
|
||||
m_uiShaderData.m_shaderVariantAlphaTest = dynamicDraw->UseShaderVariant(shaderOptionsAlphaTest);
|
||||
AZ::RPI::ShaderOptionList shaderOptionsTextureLinear;
|
||||
shaderOptionsTextureLinear.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("false")));
|
||||
shaderOptionsTextureLinear.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true")));
|
||||
shaderOptionsTextureLinear.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
|
||||
m_uiShaderData.m_shaderVariantTextureLinear = dynamicDraw->UseShaderVariant(shaderOptionsTextureLinear);
|
||||
AZ::RPI::ShaderOptionList shaderOptionsTextureSrgb;
|
||||
shaderOptionsTextureSrgb.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("false")));
|
||||
shaderOptionsTextureSrgb.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("false")));
|
||||
shaderOptionsTextureSrgb.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
|
||||
m_uiShaderData.m_shaderVariantTextureSrgb = dynamicDraw->UseShaderVariant(shaderOptionsTextureSrgb);
|
||||
AZ::RPI::ShaderOptionList shaderVariantAlphaTestMask;
|
||||
shaderVariantAlphaTestMask.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("true")));
|
||||
shaderVariantAlphaTestMask.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("false")));
|
||||
shaderVariantAlphaTestMask.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None")));
|
||||
m_uiShaderData.m_shaderVariantAlphaTestMask = dynamicDraw->UseShaderVariant(shaderVariantAlphaTestMask);
|
||||
AZ::RPI::ShaderOptionList shaderVariantGradientMask;
|
||||
shaderVariantGradientMask.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("false")));
|
||||
shaderVariantGradientMask.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("false")));
|
||||
shaderVariantGradientMask.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::Alpha")));
|
||||
m_uiShaderData.m_shaderVariantGradientMask = dynamicDraw->UseShaderVariant(shaderVariantGradientMask);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -215,6 +248,38 @@ AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> UiRenderer::GetDynamicDrawContext()
|
||||
return m_dynamicDraw;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> UiRenderer::CreateDynamicDrawContextForRTT(const AZStd::string& rttName)
|
||||
{
|
||||
// find the rtt pass with the specified name
|
||||
AZ::RPI::RasterPass* rttPass = nullptr;
|
||||
AZ::RPI::SceneId sceneId = m_scene->GetId();
|
||||
LyShinePassRequestBus::EventResult(rttPass, sceneId, &LyShinePassRequestBus::Events::GetRttPass, rttName);
|
||||
if (!rttPass)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = AZ::RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext();
|
||||
|
||||
// Initialize the dynamic draw context
|
||||
dynamicDraw->InitShader(m_dynamicDraw->GetShader());
|
||||
dynamicDraw->InitVertexFormat(
|
||||
{ { "POSITION", AZ::RHI::Format::R32G32_FLOAT },
|
||||
{ "COLOR", AZ::RHI::Format::B8G8R8A8_UNORM },
|
||||
{ "TEXCOORD", AZ::RHI::Format::R32G32_FLOAT },
|
||||
{ "BLENDINDICES", AZ::RHI::Format::R16G16_UINT } }
|
||||
);
|
||||
dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState
|
||||
| AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode);
|
||||
|
||||
dynamicDraw->SetOutputScope(rttPass);
|
||||
|
||||
dynamicDraw->EndInit();
|
||||
|
||||
return dynamicDraw;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const UiRenderer::UiShaderData& UiRenderer::GetUiShaderData()
|
||||
{
|
||||
@@ -270,11 +335,27 @@ void UiRenderer::SetBaseState(BaseState state)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
AZ::RPI::ShaderVariantId UiRenderer::GetCurrentShaderVariant()
|
||||
{
|
||||
AZ::RPI::ShaderVariantId variantId = m_uiShaderData.m_shaderVariantDefault;
|
||||
AZ::RPI::ShaderVariantId variantId = m_uiShaderData.m_shaderVariantTextureLinear;
|
||||
|
||||
if (m_baseState.m_useAlphaTest)
|
||||
{
|
||||
variantId = m_uiShaderData.m_shaderVariantAlphaTest;
|
||||
variantId = m_uiShaderData.m_shaderVariantAlphaTestMask;
|
||||
}
|
||||
else if (m_baseState.m_modulateAlpha)
|
||||
{
|
||||
variantId = m_uiShaderData.m_shaderVariantGradientMask;
|
||||
}
|
||||
else if (!m_baseState.m_useAlphaTest && m_baseState.m_srgbWrite)
|
||||
{
|
||||
variantId = m_uiShaderData.m_shaderVariantTextureLinear;
|
||||
}
|
||||
else if (!m_baseState.m_useAlphaTest && !m_baseState.m_srgbWrite)
|
||||
{
|
||||
variantId = m_uiShaderData.m_shaderVariantTextureSrgb;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(LogName, 0, "Unsupported shader variant.");
|
||||
}
|
||||
|
||||
return variantId;
|
||||
|
||||
@@ -36,8 +36,10 @@ public: // types
|
||||
AZ::RHI::ShaderInputConstantIndex m_viewProjInputIndex;
|
||||
AZ::RHI::ShaderInputConstantIndex m_isClampInputIndex;
|
||||
|
||||
AZ::RPI::ShaderVariantId m_shaderVariantDefault;
|
||||
AZ::RPI::ShaderVariantId m_shaderVariantAlphaTest;
|
||||
AZ::RPI::ShaderVariantId m_shaderVariantTextureLinear;
|
||||
AZ::RPI::ShaderVariantId m_shaderVariantTextureSrgb;
|
||||
AZ::RPI::ShaderVariantId m_shaderVariantAlphaTestMask;
|
||||
AZ::RPI::ShaderVariantId m_shaderVariantGradientMask;
|
||||
};
|
||||
|
||||
// Base state
|
||||
@@ -56,17 +58,23 @@ public: // types
|
||||
m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource;
|
||||
m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse;
|
||||
m_blendState.m_blendOp = AZ::RHI::BlendOp::Add;
|
||||
m_blendState.m_blendAlphaSource = AZ::RHI::BlendFactor::One;
|
||||
m_blendState.m_blendAlphaDest = AZ::RHI::BlendFactor::Zero;
|
||||
m_blendState.m_blendAlphaOp = AZ::RHI::BlendOp::Add;
|
||||
|
||||
// Disable stencil
|
||||
m_stencilState = AZ::RHI::StencilState();
|
||||
m_stencilState.m_enable = 0;
|
||||
|
||||
m_useAlphaTest = false;
|
||||
m_modulateAlpha = false;
|
||||
}
|
||||
|
||||
AZ::RHI::TargetBlendState m_blendState;
|
||||
AZ::RHI::StencilState m_stencilState;
|
||||
bool m_useAlphaTest = false;
|
||||
bool m_modulateAlpha = false;
|
||||
bool m_srgbWrite = true;
|
||||
};
|
||||
|
||||
public: // member functions
|
||||
@@ -93,6 +101,8 @@ public: // member functions
|
||||
//! Return the dynamic draw context associated with this UI renderer
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> GetDynamicDrawContext();
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> CreateDynamicDrawContextForRTT(const AZStd::string& rttName);
|
||||
|
||||
//! Return the shader data for the ui shader
|
||||
const UiShaderData& GetUiShaderData();
|
||||
|
||||
@@ -123,6 +133,9 @@ public: // member functions
|
||||
//! Decrement the current stencil reference value
|
||||
void DecrementStencilRef();
|
||||
|
||||
//! Return the viewport context set by the user, or the default if not set
|
||||
AZStd::shared_ptr<AZ::RPI::ViewportContext> GetViewportContext();
|
||||
|
||||
#ifndef _RELEASE
|
||||
//! Setup to record debug texture data before rendering
|
||||
void DebugSetRecordingOptionForTextureData(int recordingOption);
|
||||
@@ -143,10 +156,9 @@ private: // member functions
|
||||
AZ::RPI::ScenePtr CreateScene(AZStd::shared_ptr<AZ::RPI::ViewportContext> viewportContext);
|
||||
|
||||
//! Create a dynamic draw context for this renderer
|
||||
void CreateDynamicDrawContext(AZ::RPI::ScenePtr scene, AZ::Data::Instance<AZ::RPI::Shader>);
|
||||
|
||||
//! Return the viewport context set by the user, or the default if not set
|
||||
AZStd::shared_ptr<AZ::RPI::ViewportContext> GetViewportContext();
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> CreateDynamicDrawContext(
|
||||
AZ::RPI::ScenePtr scene,
|
||||
AZ::Data::Instance<AZ::RPI::Shader> uiShader);
|
||||
|
||||
//! Bind the global white texture for all the texture units we use
|
||||
void BindNullTexture();
|
||||
@@ -168,6 +180,8 @@ protected: // attributes
|
||||
// Set by user when viewport context is not the main/default viewport
|
||||
AZStd::shared_ptr<AZ::RPI::ViewportContext> m_viewportContext;
|
||||
|
||||
AZ::RPI::ScenePtr m_scene;
|
||||
|
||||
#ifndef _RELEASE
|
||||
int m_debugTextureDataRecordLevel = 0;
|
||||
AZStd::unordered_set<ITexture*> m_texturesUsedInFrame; // LYSHINE_ATOM_TODO - convert to RPI::Image
|
||||
|
||||
@@ -163,11 +163,9 @@ namespace UnitTest
|
||||
SSystemGlobalEnvironment* prevEnv = gEnv;
|
||||
gEnv = &env;
|
||||
gEnv->pTimer = &m_timer;
|
||||
gEnv->pLyShine = nullptr;
|
||||
|
||||
UiCanvasComponent* uiCanvasComponent;
|
||||
UiTooltipDisplayComponent* uiTooltipDisplayComponent;
|
||||
UiTooltipComponent* uiTooltipComponent;
|
||||
std::tie(uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent) = CreateUiCanvasWithTooltip();
|
||||
auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip();
|
||||
uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnHover);
|
||||
AZ::Entity* uiTooltipEntity = uiTooltipComponent->GetEntity();
|
||||
|
||||
@@ -193,11 +191,9 @@ namespace UnitTest
|
||||
SSystemGlobalEnvironment* prevEnv = gEnv;
|
||||
gEnv = &env;
|
||||
gEnv->pTimer = &m_timer;
|
||||
gEnv->pLyShine = nullptr;
|
||||
|
||||
UiCanvasComponent* uiCanvasComponent;
|
||||
UiTooltipDisplayComponent* uiTooltipDisplayComponent;
|
||||
UiTooltipComponent* uiTooltipComponent;
|
||||
std::tie(uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent) = CreateUiCanvasWithTooltip();
|
||||
auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip();
|
||||
uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnHover);
|
||||
AZ::Entity* uiTooltipEntity = uiTooltipComponent->GetEntity();
|
||||
|
||||
@@ -221,11 +217,9 @@ namespace UnitTest
|
||||
SSystemGlobalEnvironment* prevEnv = gEnv;
|
||||
gEnv = &env;
|
||||
gEnv->pTimer = &m_timer;
|
||||
gEnv->pLyShine = nullptr;
|
||||
|
||||
UiCanvasComponent* uiCanvasComponent;
|
||||
UiTooltipDisplayComponent* uiTooltipDisplayComponent;
|
||||
UiTooltipComponent* uiTooltipComponent;
|
||||
std::tie(uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent) = CreateUiCanvasWithTooltip();
|
||||
auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip();
|
||||
uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnPress);
|
||||
AZ::Entity* uiTooltipEntity = uiTooltipComponent->GetEntity();
|
||||
|
||||
@@ -249,11 +243,9 @@ namespace UnitTest
|
||||
SSystemGlobalEnvironment* prevEnv = gEnv;
|
||||
gEnv = &env;
|
||||
gEnv->pTimer = &m_timer;
|
||||
gEnv->pLyShine = nullptr;
|
||||
|
||||
UiCanvasComponent* uiCanvasComponent;
|
||||
UiTooltipDisplayComponent* uiTooltipDisplayComponent;
|
||||
UiTooltipComponent* uiTooltipComponent;
|
||||
std::tie(uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent) = CreateUiCanvasWithTooltip();
|
||||
auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip();
|
||||
uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnPress);
|
||||
AZ::Entity* uiTooltipEntity = uiTooltipComponent->GetEntity();
|
||||
|
||||
@@ -277,11 +269,9 @@ namespace UnitTest
|
||||
SSystemGlobalEnvironment* prevEnv = gEnv;
|
||||
gEnv = &env;
|
||||
gEnv->pTimer = &m_timer;
|
||||
gEnv->pLyShine = nullptr;
|
||||
|
||||
UiCanvasComponent* uiCanvasComponent;
|
||||
UiTooltipDisplayComponent* uiTooltipDisplayComponent;
|
||||
UiTooltipComponent* uiTooltipComponent;
|
||||
std::tie(uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent) = CreateUiCanvasWithTooltip();
|
||||
auto [uiCanvasComponent, uiTooltipDisplayComponent, uiTooltipComponent] = CreateUiCanvasWithTooltip();
|
||||
uiTooltipDisplayComponent->SetTriggerMode(UiTooltipDisplayInterface::TriggerMode::OnClick);
|
||||
AZ::Entity* uiTooltipEntity = uiTooltipComponent->GetEntity();
|
||||
|
||||
|
||||
@@ -11,8 +11,11 @@ set(FILES
|
||||
Include/LyShine/Draw2d.h
|
||||
Source/LyShine.cpp
|
||||
Source/LyShine.h
|
||||
Source/LyShinePassDataBus.h
|
||||
Source/LyShineDebug.cpp
|
||||
Source/LyShineDebug.h
|
||||
Source/LyShinePass.cpp
|
||||
Source/LyShinePass.h
|
||||
Source/StringUtfUtils.h
|
||||
Source/UiImageComponent.cpp
|
||||
Source/UiImageComponent.h
|
||||
@@ -28,6 +31,7 @@ set(FILES
|
||||
Source/LyShineLoadScreen.h
|
||||
Source/RenderGraph.cpp
|
||||
Source/RenderGraph.h
|
||||
Source/RenderToTextureBus.h
|
||||
Source/TextMarkup.cpp
|
||||
Source/TextMarkup.h
|
||||
Source/UiButtonComponent.cpp
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"Name": "LyShinePass",
|
||||
"TemplateName": "LyShineParentTemplate",
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "ColorInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "DebugOverlayPass",
|
||||
"Attachment": "InputOutput"
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "DepthInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "DepthPrePass",
|
||||
"Attachment": "Depth"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
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
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Parse arguments
|
||||
if len(sys.argv) != 3:
|
||||
print('Incorrect number of args')
|
||||
exit()
|
||||
|
||||
engine_path = sys.argv[1]
|
||||
if not os.path.exists(engine_path):
|
||||
print(f'Given path {engine_path} does not exist')
|
||||
exit()
|
||||
|
||||
project_path = sys.argv[2]
|
||||
if not os.path.exists(project_path):
|
||||
print(f'Given path {project_path} does not exist')
|
||||
exit()
|
||||
|
||||
sys.path.insert(0, os.path.join(engine_path, 'Gems/Atom/RPI/Tools/'))
|
||||
|
||||
from atom_rpi_tools.pass_data import PassTemplate
|
||||
import atom_rpi_tools.utils as utils
|
||||
|
||||
# Folder of this py file
|
||||
dir_name = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Patch render pipeline to insert a custom LyShine parent pass
|
||||
|
||||
# Gem::Atom_Feature_Common gem's path since default render pipeline is comming from this gem
|
||||
gem_assets_path = os.path.join(engine_path,'Gems/Atom/feature/Common/Assets/')
|
||||
|
||||
pipeline_relatvie_path = 'Passes/MainPipeline.pass'
|
||||
srcRenderPipeline = os.path.join(gem_assets_path, pipeline_relatvie_path)
|
||||
destRenderPipeline = os.path.join(project_path, pipeline_relatvie_path)
|
||||
# If the project doesn't have a customized main pipeline
|
||||
# copy the default render pipeline from Atom_Common_Feature gem to same path in project folder
|
||||
utils.find_or_copy_file(destRenderPipeline, srcRenderPipeline)
|
||||
|
||||
# Load project render pipeline
|
||||
renderPipeline = PassTemplate(destRenderPipeline)
|
||||
|
||||
# Skip if LyShinePass already exist
|
||||
newPassName = 'LyShinePass'
|
||||
if renderPipeline.find_pass(newPassName)>-1:
|
||||
print('Skip merging. LyShinePass already exists')
|
||||
exit()
|
||||
|
||||
# Insert LyShinePass between DebugOverlayPass and UIPass
|
||||
refPass = 'DebugOverlayPass'
|
||||
# The data file for new pass request is in the same folder of the py file
|
||||
newPassRequestFilePath = os.path.join(dir_name, 'LyShinePass.data')
|
||||
newPassRequestData = utils.load_json_file(newPassRequestFilePath)
|
||||
insertIndex = renderPipeline.find_pass(refPass) + 1
|
||||
if insertIndex>-1:
|
||||
renderPipeline.insert_pass_request(insertIndex, newPassRequestData)
|
||||
else:
|
||||
print('Failed to find ', refPass)
|
||||
exit()
|
||||
|
||||
# Update attachment references for the passes following LyShinePass
|
||||
renderPipeline.replace_references_after(newPassName, 'DebugOverlayPass', 'InputOutput', 'LyShinePass', 'ColorInputOutput')
|
||||
|
||||
# Save the updated render pipeline
|
||||
renderPipeline.save()
|
||||
@@ -32,6 +32,7 @@ namespace Multiplayer
|
||||
{
|
||||
m_networkEditorInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(
|
||||
AZ::Name(MPEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
|
||||
m_networkEditorInterface->SetTimeoutEnabled(false);
|
||||
if (editorsv_isDedicated)
|
||||
{
|
||||
uint16_t editorServerPort = DefaultServerEditorPort;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user