Merge branch 'development' into animation/rhhong/RenderUtilOptions

Signed-off-by: rhhong <rhhong@amazon.com>

# Conflicts:
#	Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp
#	Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h
This commit is contained in:
rhhong
2021-10-26 08:19:01 -07:00
120 changed files with 6088 additions and 1042 deletions
+2 -2
View File
@@ -185,7 +185,7 @@
{
"id": {
"materialAssetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
@@ -197,7 +197,7 @@
"id": {
"lodIndex": 0,
"materialAssetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
+116
View File
@@ -0,0 +1,116 @@
#
# 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 script shows basic usage of LuaSymbolsReporterBus,
# Which can be used to report all symbols available for
# game scripting with Lua.
import sys
import os
import azlmbr.bus as azbus
import azlmbr.script as azscript
import azlmbr.legacy.general as azgeneral
def _dump_class_symbol(class_symbol: azlmbr.script.LuaClassSymbol):
print(f"** {class_symbol}")
print("Properties:")
for property_symbol in class_symbol.properties:
print(f" - {property_symbol}")
print("Methods:")
for method_symbol in class_symbol.methods:
print(f" - {method_symbol}")
def _dump_lua_classes():
class_symbols = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
"GetListOfClasses")
print("======== Classes ==========")
sorted_classes_by_named = sorted(class_symbols, key=lambda class_symbol: class_symbol.name)
for class_symbol in sorted_classes_by_named:
_dump_class_symbol(class_symbol)
print("\n\n")
def _dump_lua_globals():
global_properties = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
"GetListOfGlobalProperties")
print("======== Global Properties ==========")
sorted_properties_by_name = sorted(global_properties, key=lambda symbol: symbol.name)
for property_symbol in sorted_properties_by_name:
print(f"- {property_symbol}")
print("\n\n")
global_functions = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
"GetListOfGlobalFunctions")
print("======== Global Functions ==========")
sorted_functions_by_name = sorted(global_functions, key=lambda symbol: symbol.name)
for function_symbol in sorted_functions_by_name:
print(f"- {function_symbol}")
print("\n\n")
def _dump_lua_ebus(ebus_symbol: azlmbr.script.LuaEBusSymbol):
print(f">> {ebus_symbol}")
sorted_senders = sorted(ebus_symbol.senders, key=lambda symbol: symbol.name)
for sender in sorted_senders:
print(f" - {sender}")
print("\n")
def _dump_lua_ebuses():
ebuses = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
"GetListOfEBuses")
print("======== Ebus List ==========")
sorted_ebuses_by_name = sorted(ebuses, key=lambda symbol: symbol.name)
for ebus_symbol in sorted_ebuses_by_name:
_dump_lua_ebus(ebus_symbol)
print("\n\n")
class WhatToDo:
DumpClasses = "c"
DumpGlobals = "g"
DumpEBuses = "e"
if __name__ == "__main__":
redirecting_stdout = False
orig_stdout = sys.stdout
if len(sys.argv) > 1:
output_file_name = sys.argv[1]
if not os.path.isabs(output_file_name):
game_root_path = os.path.normpath(azgeneral.get_game_folder())
output_file_name = os.path.join(game_root_path, output_file_name)
try:
file_obj = open(output_file_name, 'wt')
sys.stdout = file_obj
redirecting_stdout = True
except Exception as e:
print(f"Failed to open {output_file_name}: {e}")
sys.exit(-1)
what_to_do = [action.lower() for action in sys.argv[2:]]
# If the user did not specify what to do, then let's dump
# all the symbols.
if len(what_to_do) < 1:
what_to_do = [WhatToDo.DumpClasses, WhatToDo.DumpGlobals, WhatToDo.DumpEBuses]
for action in what_to_do:
if action == WhatToDo.DumpClasses:
_dump_lua_classes()
elif action == WhatToDo.DumpGlobals:
_dump_lua_globals()
elif action == WhatToDo.DumpEBuses:
_dump_lua_ebuses()
if redirecting_stdout:
sys.stdout.close()
sys.stdout = orig_stdout
print(f" Lua Symbols Are available in: {output_file_name}")
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:93a7e033d9fb0fcac221647322bde03716643d789390f79078c4fcc37ecfd005
size 68327
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6e63a55a35c749a16a03e10a1f53a48bd426c61db80151de080235b14cf6b70d
size 2479344
@@ -21,8 +21,8 @@ from ly_test_tools.o3de.asset_processor import ASSET_PROCESSOR_PLATFORM_MAP
from ..ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
from ..ap_fixtures.ap_setup_fixture import ap_setup_fixture as ap_setup_fixture
from ..ap_fixtures.ap_config_backup_fixture import ap_config_backup_fixture as ap_config_backup_fixture
from ..ap_fixtures.ap_config_default_platform_fixture import ap_config_default_platform_fixture as ap_config_default_platform_fixture
from ..ap_fixtures.ap_config_default_platform_fixture \
import ap_config_default_platform_fixture as ap_config_default_platform_fixture
# Import LyShared
import ly_test_tools.o3de.pipeline_utils as utils
@@ -33,6 +33,7 @@ logger = logging.getLogger(__name__)
# Helper: variables we will use for parameter values in the test:
targetProjects = ["AutomatedTesting"]
@pytest.fixture
@pytest.mark.SUITE_sandbox
def local_resources(request, workspace, ap_setup_fixture):
@@ -54,21 +55,21 @@ class BlackboxAssetTest:
blackbox_fbx_tests = [
pytest.param(
BlackboxAssetTest(
test_name= "OneMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
asset_folder= "OneMeshOneMaterial",
test_name="OneMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
asset_folder="OneMeshOneMaterial",
scene_debug_file="onemeshonematerial.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "OneMeshOneMaterial.fbx",
uuid = b"8a9164adb84859be893e18aa819438e1",
jobs = [
source_file_name="OneMeshOneMaterial.fbx",
uuid=b"8a9164adb84859be893e18aa819438e1",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=1,
products = [
products=[
asset_db_utils.DBProduct(
product_name='onemeshonematerial/onemeshonematerial.dbgsg',
sub_id=1918494907,
@@ -86,21 +87,21 @@ blackbox_fbx_tests = [
BlackboxAssetTest(
# Verifies that the soft naming convention feature with level of detail meshes works.
# https://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer-soft-naming.html
test_name= "SoftNamingLOD_RunAP_SuccessWithMatchingProducts",
asset_folder= "SoftNamingLOD",
test_name="SoftNamingLOD_RunAP_SuccessWithMatchingProducts",
asset_folder="SoftNamingLOD",
scene_debug_file="lodtest.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "lodtest.fbx",
uuid = b"44c8627fe2c25aae91fe3ff9547be3b9",
jobs = [
source_file_name="lodtest.fbx",
uuid=b"44c8627fe2c25aae91fe3ff9547be3b9",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=22,
products = [
products=[
asset_db_utils.DBProduct(
product_name='softnaminglod/lodtest.dbgsg',
sub_id=-632012261,
@@ -118,21 +119,21 @@ blackbox_fbx_tests = [
BlackboxAssetTest(
# Verifies that the soft naming convention feature with physics proxies works.
# https://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer-soft-naming.html
test_name= "SoftNamingPhysics_RunAP_SuccessWithMatchingProducts",
asset_folder= "SoftNamingPhysics",
test_name="SoftNamingPhysics_RunAP_SuccessWithMatchingProducts",
asset_folder="SoftNamingPhysics",
scene_debug_file="physicstest.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "physicstest.fbx",
uuid = b"df957b7918cf5b029806c73f630fa1c8",
jobs = [
source_file_name="physicstest.fbx",
uuid=b"df957b7918cf5b029806c73f630fa1c8",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=14,
products = [
products=[
asset_db_utils.DBProduct(
product_name='softnamingphysics/physicstest.dbgsg',
sub_id=-740411732,
@@ -152,21 +153,21 @@ blackbox_fbx_tests = [
),
pytest.param(
BlackboxAssetTest(
test_name= "MultipleMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
asset_folder= "TwoMeshOneMaterial",
test_name="MultipleMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
asset_folder="TwoMeshOneMaterial",
scene_debug_file="multiple_mesh_one_material.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "multiple_mesh_one_material.fbx",
uuid = b"597618fd497659a1b197a015fe47aa95",
jobs = [
source_file_name="multiple_mesh_one_material.fbx",
uuid=b"597618fd497659a1b197a015fe47aa95",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
products = [
products=[
asset_db_utils.DBProduct(
product_name='twomeshonematerial/multiple_mesh_one_material.dbgsg',
sub_id=2077268018,
@@ -183,22 +184,22 @@ blackbox_fbx_tests = [
pytest.param(
BlackboxAssetTest(
# Verifies whether multiple meshes can share linked materials
test_name= "MultipleMeshLinkedMaterials_RunAP_SuccessWithMatchingProducts",
asset_folder= "TwoMeshLinkedMaterials",
scene_debug_file= "multiple_mesh_linked_materials.dbgsg",
assets = [
test_name="MultipleMeshLinkedMaterials_RunAP_SuccessWithMatchingProducts",
asset_folder="TwoMeshLinkedMaterials",
scene_debug_file="multiple_mesh_linked_materials.dbgsg",
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "multiple_mesh_linked_materials.fbx",
uuid = b"25d8301c2eef5dc7bded310db8ea608d",
jobs = [
source_file_name="multiple_mesh_linked_materials.fbx",
uuid=b"25d8301c2eef5dc7bded310db8ea608d",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
platform= "pc",
job_key="Scene compilation",
platform="pc",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
products= [
products=[
asset_db_utils.DBProduct(
product_name='twomeshlinkedmaterials/multiple_mesh_linked_materials.dbgsg',
sub_id=-1898461950,
@@ -216,22 +217,22 @@ blackbox_fbx_tests = [
pytest.param(
BlackboxAssetTest(
# Verifies a mesh with multiple materials
test_name= "SingleMeshMultipleMaterials_RunAP_SuccessWithMatchingProducts",
asset_folder= "OneMeshMultipleMaterials",
test_name="SingleMeshMultipleMaterials_RunAP_SuccessWithMatchingProducts",
asset_folder="OneMeshMultipleMaterials",
scene_debug_file="single_mesh_multiple_materials.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "single_mesh_multiple_materials.fbx",
uuid = b"f08fd585dfa35881b4bf86637da5e858",
jobs = [
source_file_name="single_mesh_multiple_materials.fbx",
uuid=b"f08fd585dfa35881b4bf86637da5e858",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
platform= "pc",
job_key="Scene compilation",
platform="pc",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=1,
products = [
products=[
asset_db_utils.DBProduct(
product_name='onemeshmultiplematerials/single_mesh_multiple_materials.dbgsg',
sub_id=-262822238,
@@ -277,21 +278,21 @@ blackbox_fbx_tests = [
),
pytest.param(
BlackboxAssetTest(
test_name= "MotionTest_RunAP_SuccessWithMatchingProducts",
asset_folder= "Motion",
test_name="MotionTest_RunAP_SuccessWithMatchingProducts",
asset_folder="Motion",
scene_debug_file="Jack_Idle_Aim_ZUp.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "Jack_Idle_Aim_ZUp.fbx",
uuid = b"eda904ae0e145f8b973d57fc5809918b",
jobs = [
source_file_name="Jack_Idle_Aim_ZUp.fbx",
uuid=b"eda904ae0e145f8b973d57fc5809918b",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=0,
products = [
products=[
asset_db_utils.DBProduct(
product_name='motion/jack_idle_aim_zup.dbgsg',
sub_id=-517610290,
@@ -307,29 +308,56 @@ blackbox_fbx_tests = [
]
),
),
pytest.param(
BlackboxAssetTest(
test_name="ShaderBall_RunAP_SuccessWithMatchingProducts",
asset_folder="ShaderBall",
scene_debug_file="shaderball.dbgsg",
assets=[
asset_db_utils.DBSourceAsset(
source_file_name="shaderball.fbx",
uuid=b"48181ba8038e5193997540fc8dffb06d",
jobs=[
asset_db_utils.DBJob(
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=30,
products=[
asset_db_utils.DBProduct(
product_name='shaderball/shaderball.dbgsg',
sub_id=-1607815784,
asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
]
),
]
)
]
),
),
]
blackbox_fbx_special_tests = [
pytest.param(
BlackboxAssetTest(
test_name= "MultipleMeshMultipleMaterial_MultipleAssetInfo_RunAP_SuccessWithMatchingProducts",
asset_folder= "TwoMeshTwoMaterial",
override_asset_folder = "OverrideAssetInfoForTwoMeshTwoMaterial",
test_name="MultipleMeshMultipleMaterial_MultipleAssetInfo_RunAP_SuccessWithMatchingProducts",
asset_folder="TwoMeshTwoMaterial",
override_asset_folder="OverrideAssetInfoForTwoMeshTwoMaterial",
scene_debug_file="multiple_mesh_multiple_material.dbgsg",
override_scene_debug_file="multiple_mesh_multiple_material_override.dbgsg",
assets = [
assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "multiple_mesh_multiple_material.fbx",
uuid = b"b5915fb874af5c8a866ccabbddb57595",
jobs = [
source_file_name="multiple_mesh_multiple_material.fbx",
uuid=b"b5915fb874af5c8a866ccabbddb57595",
jobs=[
asset_db_utils.DBJob(
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
products = [
products=[
asset_db_utils.DBProduct(
product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg',
sub_id=896980093,
@@ -341,16 +369,16 @@ blackbox_fbx_special_tests = [
],
override_assets=[
asset_db_utils.DBSourceAsset(
source_file_name = "multiple_mesh_multiple_material.fbx",
uuid = b"b5915fb874af5c8a866ccabbddb57595",
jobs = [
source_file_name="multiple_mesh_multiple_material.fbx",
uuid=b"b5915fb874af5c8a866ccabbddb57595",
jobs=[
asset_db_utils.DBJob(
job_key= "Scene compilation",
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
products = [
products=[
asset_db_utils.DBProduct(
product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg',
sub_id=896980093,
@@ -378,29 +406,26 @@ class TestsFBX_AllPlatforms(object):
@pytest.mark.BAT
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests)
def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace,
ap_setup_fixture, asset_processor, project,
blackbox_param):
def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, ap_setup_fixture,
asset_processor, project, blackbox_param):
"""
Please see run_fbx_test(...) for details
Please see run_fbx_test(...) for details
Test Steps:
1. Determine if blackbox is set to none
2. Run FBX Test
"""
if blackbox_param == None:
return
self.run_fbx_test(workspace, ap_setup_fixture,
asset_processor, project, blackbox_param)
self.run_fbx_test(workspace, ap_setup_fixture, asset_processor, project, blackbox_param)
@pytest.mark.BAT
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests)
def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self,
workspace, ap_setup_fixture,
asset_processor, project,
blackbox_param):
def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(
self, workspace, ap_setup_fixture, asset_processor, project, blackbox_param):
"""
Please see run_fbx_test(...) for details
@@ -430,7 +455,7 @@ class TestsFBX_AllPlatforms(object):
+ product.product_name
def run_fbx_test(self, workspace, ap_setup_fixture, asset_processor,
project, blackbox_params: BlackboxAssetTest, overrideAsset = False):
project, blackbox_params: BlackboxAssetTest, overrideAsset=False):
"""
These tests work by having the test case ingest the test data and determine the run pattern.
Tests will process scene settings files and will additionally do a verification against a provided debug file
@@ -469,21 +494,23 @@ class TestsFBX_AllPlatforms(object):
expected_product_list.append(expected_product.product_name)
missing_assets, _ = utils.compare_assets_with_cache(expected_product_list,
asset_processor.project_test_cache_folder())
asset_processor.project_test_cache_folder())
assert not missing_assets, f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
assert not missing_assets, \
f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
# Load the asset database.
db_path = os.path.join(asset_processor.temp_asset_root(), "Cache",
"assetdb.sqlite")
cache_root = os.path.dirname(os.path.join(asset_processor.temp_asset_root(), "Cache",
ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform]))
ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform]))
if blackbox_params.scene_debug_file:
scene_debug_file = blackbox_params.override_scene_debug_file if overrideAsset\
scene_debug_file = blackbox_params.override_scene_debug_file if overrideAsset \
else blackbox_params.scene_debug_file
debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(), blackbox_params.scene_debug_file)
debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(),
blackbox_params.scene_debug_file)
expected_debug_graph_path = os.path.join(asset_processor.project_test_source_folder(), scene_debug_file)
logger.info(f"Parsing scene graph: {debug_graph_path}")
@@ -96,10 +96,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::UpdateTableModelAfterFilter);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
&AzAssetBrowserWindow::SelectionChangedSlot);
@@ -251,24 +247,6 @@ void AzAssetBrowserWindow::SetExpandedAssetBrowserMode()
m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::ExpandedMode;
disconnect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::UpdateTableModelAfterFilter);
disconnect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
disconnect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
&AzAssetBrowserWindow::SelectionChangedSlot);
disconnect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
disconnect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearStringFilter);
disconnect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
if (m_ui->m_assetBrowserTableViewWidget->isVisible())
{
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
@@ -281,37 +259,9 @@ void AzAssetBrowserWindow::SetDefaultAssetBrowserMode()
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::DefaultMode;
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::UpdateTableModelAfterFilter);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
&AzAssetBrowserWindow::SelectionChangedSlot);
connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearStringFilter);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
//If the filter is not empty we want to switch views and Update the model
UpdateTableModelAfterFilter();
SetTableViewVisibleAfterFilter();
}
void AzAssetBrowserWindow::UpdateTableModelAfterFilter()
{
if (!m_ui->m_searchWidget->GetFilterString().isEmpty())
{
m_tableModel->UpdateTableModelMaps();
}
}
void AzAssetBrowserWindow::SetTableViewVisibleAfterFilter()
{
@@ -389,8 +339,8 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected
UpdatePreview();
}
// while its tempting to use Activated here, we dont actually want it to count as activation
// just becuase on some OS clicking once is activation.
// while its tempting to use Activated here, we don't actually want it to count as activation
// just because on some OS clicking once is activation.
void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element)
{
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
@@ -68,7 +68,6 @@ protected slots:
void CreateSwitchViewMenu();
void SetExpandedAssetBrowserMode();
void SetDefaultAssetBrowserMode();
void UpdateTableModelAfterFilter();
void SetTableViewVisibleAfterFilter();
private:
@@ -70,6 +70,7 @@
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <Entity/EntityUtilityComponent.h>
#include <AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h>
#include <QtWidgets/QMessageBox>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer<QFileInfoPrivate>' needs to have dll-interface to be used by clients of class 'QFileInfo'
@@ -273,7 +274,8 @@ namespace AzToolsFramework
azrtti_typeid<Components::EditorEntitySearchComponent>(),
azrtti_typeid<Components::EditorIntersectorComponent>(),
azrtti_typeid<AzToolsFramework::SliceRequestComponent>(),
azrtti_typeid<AzToolsFramework::EntityUtilityComponent>()
azrtti_typeid<AzToolsFramework::EntityUtilityComponent>(),
azrtti_typeid<AzToolsFramework::Script::LuaSymbolsReporterSystemComponent>(),
});
return components;
@@ -24,24 +24,13 @@ namespace AzToolsFramework
AZ_Assert(
m_filterModel,
"Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel.");
connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &AssetBrowserTableModel::UpdateTableModelMaps);
connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &AssetBrowserTableModel::UpdateTableModelMaps);
connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &AssetBrowserTableModel::beginResetModel);
connect(
sourceModel, &QAbstractItemModel::modelReset, this,
[this]()
{
{
QSignalBlocker sb(this);
UpdateTableModelMaps();
}
endResetModel();
});
connect(sourceModel, &QAbstractItemModel::layoutChanged, this, &AssetBrowserTableModel::UpdateTableModelMaps);
connect(sourceModel, &QAbstractItemModel::dataChanged, this, &AssetBrowserTableModel::SourceDataChanged);
QSortFilterProxyModel::setSourceModel(sourceModel);
connect(m_filterModel, &QAbstractItemModel::rowsInserted, this, &AssetBrowserTableModel::UpdateTableModelMaps);
connect(m_filterModel, &QAbstractItemModel::rowsRemoved, this, &AssetBrowserTableModel::UpdateTableModelMaps);
connect(m_filterModel, &QAbstractItemModel::layoutChanged, this, &AssetBrowserTableModel::UpdateTableModelMaps);
connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, &AssetBrowserTableModel::beginResetModel);
connect(m_filterModel, &QAbstractItemModel::dataChanged, this, &AssetBrowserTableModel::SourceDataChanged);
}
QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const
@@ -112,7 +101,7 @@ namespace AzToolsFramework
int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const
{
return !parent.isValid() ? m_indexMap.size() : sourceModel()->rowCount(parent);
return !parent.isValid() ? m_indexMap.size() : 0;
}
int AssetBrowserTableModel::BuildTableModelMap(
@@ -162,28 +151,29 @@ namespace AzToolsFramework
AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const
{
if (index.isValid())
{
return static_cast<AssetBrowserEntry*>(index.internalPointer());
}
else
if (!index.isValid())
{
AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry.");
return nullptr;
}
return static_cast<AssetBrowserEntry*>(index.internalPointer());
}
void AssetBrowserTableModel::UpdateTableModelMaps()
{
beginResetModel();
emit layoutAboutToBeChanged();
m_indexMap.clear();
m_rowMap.clear();
if (!m_indexMap.isEmpty() || !m_rowMap.isEmpty())
{
m_indexMap.clear();
m_rowMap.clear();
}
AzToolsFramework::EditorSettingsAPIBus::BroadcastResult(
m_numberOfItemsDisplayed, &AzToolsFramework::EditorSettingsAPIBus::Handler::GetMaxNumberOfItemsShownInSearchView);
BuildTableModelMap(sourceModel());
emit layoutChanged();
endResetModel();
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -21,7 +21,9 @@ namespace AzToolsFramework
class AssetBrowserFilterModel;
class AssetBrowserEntry;
class AssetBrowserTableModel : public QSortFilterProxyModel
class AssetBrowserTableModel
: public QSortFilterProxyModel
, public AssetBrowserComponentNotificationBus::Handler
{
Q_OBJECT
@@ -43,7 +45,7 @@ namespace AzToolsFramework
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override;
////////////////////////////////////////////////////////////////////
private:
AssetBrowserEntry* GetAssetEntry(QModelIndex index) const;
int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0);
@@ -225,29 +225,36 @@ namespace AzToolsFramework
const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column());
const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column());
auto aboveEntry = qvariant_cast<const AssetBrowserEntry*>(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
auto belowEntry = qvariant_cast<const AssetBrowserEntry*>(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
auto belowEntry = qvariant_cast<const AssetBrowserEntry*>(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
auto aboveEntry = qvariant_cast<const AssetBrowserEntry*>(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
auto aboveSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(aboveEntry);
auto belowSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(belowEntry);
auto aboveSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(aboveEntry);
// if current index is the last entry in the view
// or the index above it is a Source Entry and
// the index below is invalid or is valid but it is also a source entry
// then the current index is the only child.
if (index.row() == viewModel->rowCount() - 1 ||
(indexBelow.isValid() && aboveSourceEntry &&
(!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry))))
// Last item and the above entry is a source entry
// or indexBelow is a source entry and the index above is not
if (viewModel->rowCount() > 0 && index.row() == viewModel->rowCount() - 1)
{
if (aboveSourceEntry)
{
DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize);
}
else
{
DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize);
}
}
else if (belowSourceEntry && aboveSourceEntry)
{
DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon
}
else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry
else if (belowSourceEntry && !aboveSourceEntry)
{
DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon
DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize);
}
else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry
else if (aboveSourceEntry) // The index above is a source entry
{
DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon
DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw First Child Icon
}
else //the index above and below are also child entries
{
@@ -286,7 +293,6 @@ namespace AzToolsFramework
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast;
break;
case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild:
default:
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild;
break;
}
@@ -311,5 +317,4 @@ namespace AzToolsFramework
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Views/moc_EntryDelegate.cpp"
@@ -54,6 +54,7 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
#include <AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h>
AZ_DEFINE_BUDGET(AzToolsFramework);
@@ -106,6 +107,7 @@ namespace AzToolsFramework
AzToolsFramework::Components::EditorIntersectorComponent::CreateDescriptor(),
AzToolsFramework::AzToolsFrameworkConfigurationSystemComponent::CreateDescriptor(),
AzToolsFramework::Components::EditorEntityUiSystemComponent::CreateDescriptor(),
AzToolsFramework::Script::LuaSymbolsReporterSystemComponent::CreateDescriptor(),
});
}
}
@@ -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 <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
namespace AzToolsFramework
{
namespace Script
{
struct LuaPropertySymbol
{
AZ_TYPE_INFO(LuaPropertySymbol, "{5AFB147F-50A4-4F00-9F82-D8D5BBC970D6}");
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
bool m_canRead;
bool m_canWrite;
AZStd::string ToString() const;
};
struct LuaMethodSymbol
{
AZ_TYPE_INFO(LuaMethodSymbol, "{7B074A36-C81D-46A0-8D2F-62E426EBE38A}");
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
AZStd::string m_debugArgumentInfo;
AZStd::string ToString() const;
};
struct LuaClassSymbol
{
AZ_TYPE_INFO(LuaClassSymbol, "{5FBE5841-A8E1-44B6-BEDA-22302CF8DF5F}");
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
AZ::Uuid m_typeId;
AZStd::vector<LuaPropertySymbol> m_properties;
AZStd::vector<LuaMethodSymbol> m_methods;
AZStd::string ToString() const;
};
struct LuaEBusSender
{
AZ_TYPE_INFO(LuaEBusSender, "{23EE4188-0924-49DB-BF3F-EB7AAB6D5E5C}");
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
AZStd::string m_debugArgumentInfo;
AZStd::string m_category;
AZStd::string ToString() const;
};
struct LuaEBusSymbol
{
AZ_TYPE_INFO(LuaEBusSymbol, "{381C5639-A916-4D2E-B825-50A3F2D93137}");
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
bool m_canBroadcast;
bool m_canQueue;
bool m_hasHandler;
AZStd::vector<LuaEBusSender> m_senders;
AZStd::string ToString() const;
};
// This is an EBus useful to scrape classes, globals and EBuses exposed to game scripting
// e.g: Lua.
class LuaSymbolsReporterRequests
{
public:
AZ_RTTI(LuaSymbolsReporterRequests, "{3FF9A105-3159-49FF-8DC6-4948AE7B4AB8}");
virtual ~LuaSymbolsReporterRequests() = default;
// Put your public methods here
virtual const AZStd::vector<LuaClassSymbol>& GetListOfClasses() = 0;
virtual const AZStd::vector<LuaPropertySymbol>& GetListOfGlobalProperties() = 0;
virtual const AZStd::vector<LuaMethodSymbol>& GetListOfGlobalFunctions() = 0;
virtual const AZStd::vector<LuaEBusSymbol>& GetListOfEBuses() = 0;
};
class LuaSymbolsReporterBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LuaSymbolsReporterRequestBus = AZ::EBus<LuaSymbolsReporterRequests, LuaSymbolsReporterBusTraits>;
} // namespace Script
} // namespace AzToolsFramework
@@ -0,0 +1,475 @@
/*
* 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/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Script/ScriptSystemBus.h>
#include <AzCore/Script/ScriptContextDebug.h>
#include "LuaSymbolsReporterSystemComponent.h"
namespace AzToolsFramework
{
namespace Script
{
AZStd::string LuaPropertySymbol::ToString() const
{
return AZStd::string::format("%s [%s/%s]",
m_name.c_str(),
m_canRead ? "R" : "_",
m_canWrite ? "W" : "_");
}
void LuaPropertySymbol::Reflect(AZ::ReflectContext* context)
{
auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<LuaPropertySymbol>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "script")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Property("name", BehaviorValueProperty(&LuaPropertySymbol::m_name))
->Property("canRead", BehaviorValueProperty(&LuaPropertySymbol::m_canRead))
->Property("canWrite", BehaviorValueProperty(&LuaPropertySymbol::m_canWrite))
->Method("ToString", &LuaPropertySymbol::ToString)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
}
}
AZStd::string LuaMethodSymbol::ToString() const
{
return AZStd::string::format("%s(%s)", m_name.c_str(), m_debugArgumentInfo.c_str());
}
void LuaMethodSymbol::Reflect(AZ::ReflectContext* context)
{
auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<LuaMethodSymbol>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "script")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Property("name", BehaviorValueProperty(&LuaMethodSymbol::m_name))
->Property("debugArgumentInfo", BehaviorValueProperty(&LuaMethodSymbol::m_debugArgumentInfo))
->Method("ToString", &LuaMethodSymbol::ToString)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
}
}
AZStd::string LuaClassSymbol::ToString() const
{
return AZStd::string::format("%s [%s]", m_name.c_str(), m_typeId.ToString<AZStd::string>().c_str());
}
void LuaClassSymbol::Reflect(AZ::ReflectContext* context)
{
auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<LuaClassSymbol>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "script")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Property("name", BehaviorValueProperty(&LuaClassSymbol::m_name))
->Property("typeId", BehaviorValueProperty(&LuaClassSymbol::m_typeId))
->Property("properties", BehaviorValueProperty(&LuaClassSymbol::m_properties))
->Property("methods", BehaviorValueProperty(&LuaClassSymbol::m_methods))
->Method("ToString", &LuaClassSymbol::ToString)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
}
}
AZStd::string LuaEBusSender::ToString() const
{
return AZStd::string::format("%s(%s) - [%s]", m_name.c_str(), m_debugArgumentInfo.c_str(), m_category.c_str());
}
void LuaEBusSender::Reflect(AZ::ReflectContext* context)
{
auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<LuaEBusSender>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "script")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Property("name", BehaviorValueProperty(&LuaEBusSender::m_name))
->Property("debugArgumentInfo", BehaviorValueProperty(&LuaEBusSender::m_debugArgumentInfo))
->Property("category", BehaviorValueProperty(&LuaEBusSender::m_category))
->Method("ToString", &LuaEBusSender::ToString)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
}
}
AZStd::string LuaEBusSymbol::ToString() const
{
auto boolToStr = +[](bool val) { return val ? "true" : "false"; };
return AZStd::string::format("%s: canBroadcast(%s), canQueue(%s), hasHandler(%s)",
m_name.c_str(),
boolToStr(m_canBroadcast), boolToStr(m_canQueue), boolToStr(m_hasHandler));
}
void LuaEBusSymbol::Reflect(AZ::ReflectContext* context)
{
auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<LuaEBusSymbol>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "script")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Property("name", BehaviorValueProperty(&LuaEBusSymbol::m_name))
->Property("canBroadcast", BehaviorValueProperty(&LuaEBusSymbol::m_canBroadcast))
->Property("canQueue", BehaviorValueProperty(&LuaEBusSymbol::m_canQueue))
->Property("hasHandler", BehaviorValueProperty(&LuaEBusSymbol::m_hasHandler))
->Property("senders", BehaviorValueProperty(&LuaEBusSymbol::m_senders))
->Method("ToString", &LuaEBusSymbol::ToString)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
}
}
//! This local class helps us keeping private the sensitive data in LuaSymbolsReporterSystemComponent
//! Used inside the function pointers for several AZ::SciptContextDebug::Enumerate* functions.
class IntrusiveHelper
{
public:
static AZStd::vector<LuaClassSymbol>& GetClassSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedClassSymbols; }
static AZStd::unordered_map<AZ::Uuid, size_t>& GetClassUuidToIndexMap(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_classUuidToIndexMap; }
static AZStd::vector<LuaPropertySymbol>& GetGlobalPropertySymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedGlobalPropertySymbols; }
static AZStd::vector<LuaMethodSymbol>& GetGlobalFunctionSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedGlobalFunctionSymbols; }
static AZStd::vector<LuaEBusSymbol>& GetEBusSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedEbusSymbols; }
static AZStd::unordered_map<AZStd::string, size_t>& GetEBusNameToIndexMap(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_ebusNameToIndexMap; }
};
void LuaSymbolsReporterSystemComponent::Reflect(AZ::ReflectContext* context)
{
LuaPropertySymbol::Reflect(context);
LuaMethodSymbol::Reflect(context);
LuaClassSymbol::Reflect(context);
LuaEBusSender::Reflect(context);
LuaEBusSymbol::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<LuaSymbolsReporterSystemComponent, AZ::Component>()
->Version(0);
serializeContext->RegisterGenericType<AZStd::vector<LuaPropertySymbol>>();
serializeContext->RegisterGenericType<AZStd::vector<LuaMethodSymbol>>();
serializeContext->RegisterGenericType<AZStd::vector<LuaClassSymbol>>();
serializeContext->RegisterGenericType<AZStd::vector<LuaEBusSender>>();
serializeContext->RegisterGenericType<AZStd::vector<LuaEBusSymbol>>();
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<LuaSymbolsReporterRequestBus>("LuaSymbolsReporterBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "script")
->Event("GetListOfClasses", &LuaSymbolsReporterRequests::GetListOfClasses)
->Event("GetListOfGlobalProperties", &LuaSymbolsReporterRequests::GetListOfGlobalProperties)
->Event("GetListOfGlobalFunctions", &LuaSymbolsReporterRequests::GetListOfGlobalFunctions)
->Event("GetListOfEBuses", &LuaSymbolsReporterRequests::GetListOfEBuses)
;
}
}
void LuaSymbolsReporterSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("LuaSymbolsReporterSystemService"));
}
void LuaSymbolsReporterSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("LuaSymbolsReporterSystemService"));
}
void LuaSymbolsReporterSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("ScriptService"));
}
void LuaSymbolsReporterSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
// No dependent services.
}
void LuaSymbolsReporterSystemComponent::Activate()
{
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
LuaSymbolsReporterRequestBus::Handler::BusConnect();
}
void LuaSymbolsReporterSystemComponent::Deactivate()
{
LuaSymbolsReporterRequestBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
}
AZ::ScriptContext* LuaSymbolsReporterSystemComponent::InitScriptContext()
{
if (m_scriptContext)
{
return m_scriptContext;
}
AZ::ScriptSystemRequestBus::BroadcastResult(m_scriptContext, &AZ::ScriptSystemRequests::GetContext, AZ::ScriptContextIds::DefaultScriptContextId);
return m_scriptContext;
}
void LuaSymbolsReporterSystemComponent::LoadGlobalSymbols()
{
auto scriptContext = InitScriptContext();
if (!scriptContext)
{
AZ_Error(LogName, false, "Invalid scriptContext");
return;
}
scriptContext->EnableDebug();
auto debugContext = scriptContext->GetDebugContext();
if (!debugContext)
{
AZ_Error(LogName, false, "Invalid debugContext from scriptContext");
return;
}
auto enumMethodFunc = +[]([[maybe_unused]] const AZ::Uuid* classTypeId, const char* methodName, const char* debugArgumentInfo, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& methodSymbols = IntrusiveHelper::GetGlobalFunctionSymbols(mySelf);
methodSymbols.push_back({});
auto& methodSymbol = methodSymbols.back();
methodSymbol.m_name = methodName;
if (debugArgumentInfo)
{
methodSymbol.m_debugArgumentInfo = debugArgumentInfo;
}
return true;
};
auto enumPropertyFunc = +[]([[maybe_unused]] const AZ::Uuid* classTypeId, const char* propertyName, bool canRead, bool canWrite, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& propertySymbols = IntrusiveHelper::GetGlobalPropertySymbols(mySelf);
propertySymbols.push_back({});
auto& propertySymbol = propertySymbols.back();
propertySymbol.m_name = propertyName;
propertySymbol.m_canRead = canRead;
propertySymbol.m_canWrite = canWrite;
return true;
};
debugContext->EnumRegisteredGlobals(enumMethodFunc, enumPropertyFunc, this);
scriptContext->DisableDebug();
}
///////////////////////////////////////////////////////////////////////////
/// LuaSymbolsReporterRequestBus::Handler
const AZStd::vector<LuaClassSymbol>& LuaSymbolsReporterSystemComponent::GetListOfClasses()
{
if (!m_cachedClassSymbols.empty())
{
return m_cachedClassSymbols;
}
auto scriptContext = InitScriptContext();
if (!scriptContext)
{
return m_cachedClassSymbols;
}
scriptContext->EnableDebug();
auto debugContext = scriptContext->GetDebugContext();
if (!debugContext)
{
return m_cachedClassSymbols;
}
auto enumClassFunc = +[](const char* className, const AZ::Uuid& classTypeId, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& classSymbols = IntrusiveHelper::GetClassSymbols(mySelf);
classSymbols.push_back({});
auto& classSymbol = classSymbols.back();
classSymbol.m_name = className;
classSymbol.m_typeId = classTypeId;
auto& uuidToClassMap = IntrusiveHelper::GetClassUuidToIndexMap(mySelf);
uuidToClassMap.emplace(classTypeId, classSymbols.size() - 1);
return true;
};
auto enumMethodFunc = +[](const AZ::Uuid* classTypeId, const char* methodName, const char* debugArgumentInfo, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& classUuidToIndexMap = IntrusiveHelper::GetClassUuidToIndexMap(mySelf);
auto itor = classUuidToIndexMap.find(*classTypeId);
if (itor == classUuidToIndexMap.end())
{
AZ_Error(LogName, false, "Can not add method [%s] because class uuid [%s] is not registered", methodName, classTypeId->ToString<AZStd::string>().c_str());
return false;
}
auto classIndex = itor->second;
auto& classSymbols = IntrusiveHelper::GetClassSymbols(mySelf);
auto& classSymbol = classSymbols[classIndex];
classSymbol.m_methods.push_back({});
auto& methodSymbol = classSymbol.m_methods.back();
methodSymbol.m_name = methodName;
if (debugArgumentInfo)
{
methodSymbol.m_debugArgumentInfo = debugArgumentInfo;
}
return true;
};
auto enumPropertyFunc = +[](const AZ::Uuid* classTypeId, const char* propertyName, bool canRead, bool canWrite, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& classUuidToIndexMap = IntrusiveHelper::GetClassUuidToIndexMap(mySelf);
auto itor = classUuidToIndexMap.find(*classTypeId);
if (itor == classUuidToIndexMap.end())
{
AZ_Error(LogName, false, "Can not add property [%s] because class uuid [%s] is not registered", propertyName, classTypeId->ToString<AZStd::string>().c_str());
return false;
}
auto classIndex = itor->second;
auto& classSymbols = IntrusiveHelper::GetClassSymbols(mySelf);
auto& classSymbol = classSymbols[classIndex];
classSymbol.m_properties.push_back({});
auto& propertySymbol = classSymbol.m_properties.back();
propertySymbol.m_name = propertyName;
propertySymbol.m_canRead = canRead;
propertySymbol.m_canWrite = canWrite;
return true;
};
debugContext->EnumRegisteredClasses(enumClassFunc, enumMethodFunc, enumPropertyFunc, this);
scriptContext->DisableDebug();
return m_cachedClassSymbols;
}
const AZStd::vector<LuaPropertySymbol>& LuaSymbolsReporterSystemComponent::GetListOfGlobalProperties()
{
if (!m_cachedGlobalPropertySymbols.empty())
{
return m_cachedGlobalPropertySymbols;
}
LoadGlobalSymbols();
return m_cachedGlobalPropertySymbols;
}
const AZStd::vector<LuaMethodSymbol>& LuaSymbolsReporterSystemComponent::GetListOfGlobalFunctions()
{
if (!m_cachedGlobalFunctionSymbols.empty())
{
return m_cachedGlobalFunctionSymbols;
}
LoadGlobalSymbols();
return m_cachedGlobalFunctionSymbols;
}
const AZStd::vector<LuaEBusSymbol>& LuaSymbolsReporterSystemComponent::GetListOfEBuses()
{
if (!m_cachedEbusSymbols.empty())
{
return m_cachedEbusSymbols;
}
auto scriptContext = InitScriptContext();
if (!scriptContext)
{
return m_cachedEbusSymbols;
}
scriptContext->EnableDebug();
auto debugContext = scriptContext->GetDebugContext();
if (!debugContext)
{
return m_cachedEbusSymbols;
}
auto enumEBusFunc = +[](const AZStd::string& ebusName, bool canBroadcast, bool canQueue, bool hasHandler, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& ebusSymbols = IntrusiveHelper::GetEBusSymbols(mySelf);
ebusSymbols.push_back({});
auto& ebusSymbol = ebusSymbols.back();
ebusSymbol.m_name = ebusName;
ebusSymbol.m_canBroadcast = canBroadcast;
ebusSymbol.m_canQueue = canQueue;
ebusSymbol.m_hasHandler = hasHandler;
auto& nameToIndexMap = IntrusiveHelper::GetEBusNameToIndexMap(mySelf);
nameToIndexMap.emplace(ebusName, ebusSymbols.size() - 1);
return true;
};
auto enumEBusSenderFunc = +[](const AZStd::string& ebusName, const AZStd::string& senderName, const AZStd::string& debugArgumentInfo, const AZStd::string& category, void* userData) -> bool
{
auto& mySelf = *reinterpret_cast<LuaSymbolsReporterSystemComponent*>(userData);
auto& nameToIndexMap = IntrusiveHelper::GetEBusNameToIndexMap(mySelf);
auto itor = nameToIndexMap.find(ebusName);
if (itor == nameToIndexMap.end())
{
AZ_Error(LogName, false, "Can not add ebus sender [%s] because ebus [%s] is not registered", senderName.c_str(), ebusName.c_str());
return false;
}
auto ebusIndex = itor->second;
auto& ebusSymbols = IntrusiveHelper::GetEBusSymbols(mySelf);
auto& ebusSymbol = ebusSymbols[ebusIndex];
ebusSymbol.m_senders.push_back({});
auto& ebusSender = ebusSymbol.m_senders.back();
ebusSender.m_name = senderName;
ebusSender.m_debugArgumentInfo = debugArgumentInfo;
ebusSender.m_category = category;
return true;
};
debugContext->EnumRegisteredEBuses(enumEBusFunc, enumEBusSenderFunc, this);
scriptContext->DisableDebug();
return m_cachedEbusSymbols;
}
///////////////////////////////////////////////////////////////////////////
} //namespace Script
} // namespace AzToolsFramework
@@ -0,0 +1,73 @@
/*
* 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/Script/ScriptContext.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Script/LuaSymbolsReporterBus.h>
namespace AzToolsFramework
{
namespace Script
{
/// System component for LuaSymbolsReporterRequestBus
class LuaSymbolsReporterSystemComponent
: public AZ::Component
, public LuaSymbolsReporterRequestBus::Handler
, private AzToolsFramework::EditorEvents::Bus::Handler
{
public:
AZ_COMPONENT(LuaSymbolsReporterSystemComponent, "{DB8D95BA-FECF-4D81-A45C-8C05E706E2AC}");
static void Reflect(AZ::ReflectContext* context);
static constexpr char LogName[] = "LuaSymbolsReporter";
LuaSymbolsReporterSystemComponent() = default;
~LuaSymbolsReporterSystemComponent() = default;
///////////////////////////////////////////////////////////////////////////
/// LuaSymbolsReporterRequestBus::Handler
const AZStd::vector<LuaClassSymbol>& GetListOfClasses() override;
const AZStd::vector<LuaPropertySymbol>& GetListOfGlobalProperties() override;
const AZStd::vector<LuaMethodSymbol>& GetListOfGlobalFunctions() override;
const AZStd::vector<LuaEBusSymbol>& GetListOfEBuses() override;
///////////////////////////////////////////////////////////////////////////
private:
friend class IntrusiveHelper;
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
// AZ::Component
void Activate() override;
void Deactivate() override;
AZ::ScriptContext* InitScriptContext();
void LoadGlobalSymbols();
AZ::ScriptContext* m_scriptContext = nullptr;
AZStd::vector<LuaClassSymbol> m_cachedClassSymbols;
// The key is a class uuid, the value is the index in @m_cachedClassSymbols
AZStd::unordered_map<AZ::Uuid, size_t> m_classUuidToIndexMap;
AZStd::vector<LuaPropertySymbol> m_cachedGlobalPropertySymbols;
AZStd::vector<LuaMethodSymbol> m_cachedGlobalFunctionSymbols;
AZStd::vector<LuaEBusSymbol> m_cachedEbusSymbols;
// The key is the ebus name, the value is the index in @m_cachedEbusSymbols
AZStd::unordered_map<AZStd::string, size_t> m_ebusNameToIndexMap;
};
} // namespace Script
} // namespace AzToolsFramework
@@ -943,13 +943,15 @@ namespace AzToolsFramework
{
return false;
}
const int count = rowCount(parent);
AZ::EntityId newParentId = GetEntityFromIndex(parent);
AZ::EntityId beforeEntityId = GetEntityFromIndex(index(row, 0, parent));
AZ::EntityId beforeEntityId = (row >= 0 && row < count) ? GetEntityFromIndex(index(row, 0, parent)) : AZ::EntityId();
EntityIdList topLevelEntityIds;
topLevelEntityIds.reserve(entityIdListContainer.m_entityIds.size());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::FindTopLevelEntityIdsInactive, entityIdListContainer.m_entityIds, topLevelEntityIds);
if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId))
const auto appendActionForInvalid = newParentId.IsValid() && (row >= count) ? AppendEnd : AppendBeginning;
if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId, appendActionForInvalid))
{
return false;
}
@@ -1046,7 +1048,7 @@ namespace AzToolsFramework
return true;
}
bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId)
bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId, ReparentForInvalid forInvalid)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (!CanReparentEntities(newParentId, selectedEntityIds))
@@ -1056,10 +1058,18 @@ namespace AzToolsFramework
m_isFilterDirty = true;
ScopedUndoBatch undo("Reparent Entities");
//capture child entity order before re-parent operation, which will automatically add order info if not present
EntityOrderArray entityOrderArray = GetEntityChildOrder(newParentId);
//search for the insertion entity in the order array
const auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId);
const bool hasInvalidIndex = beforeEntityItr == entityOrderArray.end();
if (hasInvalidIndex && forInvalid == None)
{
return false;
}
ScopedUndoBatch undo("Reparent Entities");
// The new parent is dirty due to sort change(s)
undo.MarkEntityDirty(GetEntityIdForSortInfo(newParentId));
@@ -1088,9 +1098,7 @@ namespace AzToolsFramework
}
}
//search for the insertion entity in the order array
auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId);
//replace order info matching selection with bad values rather than remove to preserve layout
for (auto& id : entityOrderArray)
{
@@ -1100,17 +1108,25 @@ namespace AzToolsFramework
}
}
if (newParentId.IsValid())
//if adding to a valid parent entity, insert at the found entity location or at the head/tail depending on placeAtTail flag
if (hasInvalidIndex)
{
//if adding to a valid parent entity, insert at the found entity location or at the head of the container
auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.begin();
entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end());
}
else
switch(forInvalid)
{
case AppendEnd:
entityOrderArray.insert(entityOrderArray.end(), processedEntityIds.begin(), processedEntityIds.end());
break;
case AppendBeginning:
entityOrderArray.insert(entityOrderArray.begin(), processedEntityIds.begin(), processedEntityIds.end());
break;
default:
AZ_Assert(false, "Unexpected type for ReparentForInvalid");
break;
}
}
else
{
//if adding to an invalid parent entity (the root), insert at the found entity location or at the tail of the container
auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.end();
entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end());
entityOrderArray.insert(beforeEntityItr, processedEntityIds.begin(), processedEntityIds.end());
}
//remove placeholder entity ids
@@ -72,6 +72,13 @@ namespace AzToolsFramework
ColumnCount //!< Total number of columns
};
enum ReparentForInvalid
{
None, //!< For an invalid location the entity does not change location
AppendEnd, //!< Append Item to end of target parent list
AppendBeginning, //!< Append Item to the beginning of target parent list
};
// Note: the ColumnSortIndex column isn't shown, hence the -1 and the need for a separate counter.
// A wrong column count number causes refresh issues and hover mismatch on model update.
static const int VisibleColumnCount = ColumnCount - 1;
@@ -162,7 +169,7 @@ namespace AzToolsFramework
// Buffer Processing Slots - These are called using single-shot events when the buffers begin to fill.
bool CanReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds) const;
bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId());
bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId(), ReparentForInvalid forInvalid = None);
//! Use the current filter setting and re-evaluate the filter.
void InvalidateFilter();
@@ -770,6 +770,9 @@ set(FILES
PythonTerminal/ScriptTermDialog.ui
Input/QtEventToAzInputManager.h
Input/QtEventToAzInputManager.cpp
Script/LuaSymbolsReporterBus.h
Script/LuaSymbolsReporterSystemComponent.h
Script/LuaSymbolsReporterSystemComponent.cpp
)
# Prevent the following files from being grouped in UNITY builds
+1
View File
@@ -34,6 +34,7 @@ ly_add_target(
INCLUDE_DIRECTORIES
PRIVATE
Source
Platform/${PAL_PLATFORM_NAME}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_linux.cpp
ProjectUtils_linux.cpp
ProjectManagerDefs_linux.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Linux.h
)
@@ -0,0 +1,11 @@
/*
* 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
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
@@ -0,0 +1,11 @@
/*
* 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 <ProjectManager_Traits_Linux.h>
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_mac.cpp
ProjectUtils_mac.cpp
ProjectManagerDefs_mac.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Mac.h
)
@@ -0,0 +1,11 @@
/*
* 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
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
@@ -0,0 +1,11 @@
/*
* 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 <ProjectManager_Traits_Mac.h>
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_windows.cpp
ProjectUtils_windows.cpp
ProjectManagerDefs_windows.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Windows.h
)
@@ -0,0 +1,11 @@
/*
* 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 <ProjectManager_Traits_Windows.h>
@@ -0,0 +1,11 @@
/*
* 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
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR true
@@ -9,7 +9,7 @@ QMainWindow {
#ScreensCtrl {
min-width:1200px;
min-height:800px;
min-height:700px;
}
QPushButton:focus {
@@ -16,6 +16,7 @@
#include <AzQtComponents/Utilities/HandleDpiAwareness.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <ProjectManager_Traits_Platform.h>
#include <QApplication>
#include <QDir>
@@ -194,8 +195,12 @@ namespace O3DE::ProjectManager
// set stylesheet after creating the main window or their styles won't get updated
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss"));
// the decoration wrapper is intended to remember window positioning and sizing
// the decoration wrapper is intended to remember window positioning and sizing
#if AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR
auto wrapper = new AzQtComponents::WindowDecorationWrapper();
#else
auto wrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled);
#endif
wrapper->setGuest(m_mainWindow.data());
// show the main window here to apply the stylesheet before restoring geometry or we
@@ -50,12 +50,12 @@ namespace AZ
return scope;
}
//! Sets the active context based on the provided PassHierarchyFilter. If the filter doesn't match exactly one pass, then do nothing.
static ImGuiActiveContextScope FromPass(const RPI::PassHierarchyFilter& passHierarchyFilter)
//! Sets the active context based on the provided pass hierarchy filter. If the filter doesn't match exactly one pass, then do nothing.
static ImGuiActiveContextScope FromPass(const AZStd::vector<AZStd::string>& passHierarchy)
{
ImGuiActiveContextScope scope;
scope.ConnectToImguiNotificationBus();
ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchyFilter);
ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchy);
return scope;
}
@@ -15,11 +15,6 @@
namespace AZ
{
namespace RPI
{
class PassHierarchyFilter;
}
namespace Render
{
class ImGuiPass;
@@ -51,7 +46,7 @@ namespace AZ
//! Pushes whichever ImGui pass is default on the top of the active context stack. Returns true/false for success/fail.
virtual bool PushActiveContextFromDefaultPass() = 0;
//! Pushes whichever ImGui pass is provided in passHierarchy on the top of the active context stack. Returns true/false for success/fail.
virtual bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) = 0;
virtual bool PushActiveContextFromPass(const AZStd::vector<AZStd::string>& passHierarchy) = 0;
//! Pops the active context off the top of the active context stack. Returns true if there's a context to pop.
virtual bool PopActiveContext() = 0;
//! Gets the context at the top of the active context stack. Returns nullptr if the stack is emtpy.
@@ -50,7 +50,7 @@ namespace AZ
virtual bool CaptureScreenshotWithPreview(const AZStd::string& outputFilePath) = 0;
//! Save a buffer attachment or a image attachment binded to a pass's slot to a data file.
//! @param passHierarchy For finding the pass by using PassHierarchyFilter
//! @param passHierarchy For finding the pass by using a pass hierarchy filter. Check PassFilter::CreateWithPassHierarchy() function for detail
//! @param slotName Name of the pass's slot. The attachment bound to this slot will be captured.
//! @param option Only valid for an InputOutput attachment. Use PassAttachmentReadbackOption::Input to capture the input state
//! and use PassAttachmentReadbackOption::Output to capture the output state
@@ -647,54 +647,46 @@ namespace AZ
UpdateViewsOfCascadeSegments();
}
void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() {
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("CascadedShadowmapsTemplate"));
void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass()
{
m_cascadedShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline())
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("CascadedShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
RPI::RenderPipeline* pipeline = pass->GetRenderPipeline();
const RPI::RenderPipelineId pipelineId = pipeline->GetId();
// This function can be called when the pipeline is not attached to the scene.
// So we check it is attached to the scene.
if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline)
CascadedShadowmapsPass* shadowPass = azrtti_cast<CascadedShadowmapsPass*>(pass);
AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass.");
if (pipeline->GetDefaultView())
{
CascadedShadowmapsPass* shadowPass = azrtti_cast<CascadedShadowmapsPass*>(pass);
AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass.");
if (pipeline->GetDefaultView())
{
m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass);
}
m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void DirectionalLightFeatureProcessor::CacheEsmShadowmapsPass()
{
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("EsmShadowmapsTemplate"));
m_esmShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline())
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
const RPI::RenderPipelineId pipelineId = pipeline->GetId();
// checking the render pipeline is just removed from the scene.
if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline)
const RPI::RenderPipelineId pipelineId = pass->GetRenderPipeline()->GetId();
if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end())
{
if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end())
EsmShadowmapsPass* esmPass = azrtti_cast<EsmShadowmapsPass*>(pass);
AZ_Assert(esmPass, "It is not an EsmShadowmapPass.");
if (esmPass->GetLightTypeName() == m_lightTypeName)
{
EsmShadowmapsPass* esmPass = azrtti_cast<EsmShadowmapsPass*>(pass);
AZ_Assert(esmPass, "It is not an EsmShadowmapPass.");
if (m_cascadedShadowmapsPasses.find(esmPass->GetRenderPipeline()->GetId()) != m_cascadedShadowmapsPasses.end() &&
esmPass->GetLightTypeName() == m_lightTypeName)
{
m_esmShadowmapsPasses[pipelineId].push_back(esmPass);
}
m_esmShadowmapsPasses[pipelineId].push_back(esmPass);
}
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void DirectionalLightFeatureProcessor::PrepareCameraViews()
@@ -1063,12 +1055,13 @@ namespace AZ
// if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view,
// to filter out shadows from objects that are excluded from the cubemap
RPI::PassClassFilter<RPI::EnvironmentCubeMapPass> passFilter;
AZStd::vector<AZ::RPI::Pass*> cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (!cubeMapPasses.empty())
{
usageFlags |= RPI::View::UsageReflectiveCubeMap;
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<RPI::EnvironmentCubeMapPass>();
passFilter.SetOwenrScene(GetParentScene()); // only handles passes for this scene
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&usageFlags]([[maybe_unused]] RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
usageFlags |= RPI::View::UsageReflectiveCubeMap;
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
segment.m_view = RPI::View::CreateView(viewName, usageFlags);
}
@@ -80,35 +80,48 @@ namespace AZ
}
// update the size multiplier on the DiffuseProbeGridDownsamplePass output
AZStd::vector<Name> downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") };
RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy);
const AZStd::vector<RPI::Pass*>& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter);
for (RPI::Pass* pass : downsamplePasses)
// NOTE: The ownerScene wasn't added to both filters. This is because the passes from the non-owner scene may have invalid SRG values which could lead to
// GPU error if the scene doesn't have this feature processor enabled.
// For example, the ASV MultiScene sample may have TDR.
{
for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex)
{
RPI::Ptr<RPI::PassAttachment> outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment;
RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers;
AZStd::vector<Name> downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") };
RPI::PassFilter downsamplePassFilter = RPI::PassFilter::CreateWithPassHierarchy(downsamplePassHierarchy);
RPI::PassSystemInterface::Get()->ForEachPass(
downsamplePassFilter,
[sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex)
{
RPI::Ptr<RPI::PassAttachment> outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment;
RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers;
sizeMultipliers.m_widthMultiplier = sizeMultiplier;
sizeMultipliers.m_heightMultiplier = sizeMultiplier;
}
sizeMultipliers.m_widthMultiplier = sizeMultiplier;
sizeMultipliers.m_heightMultiplier = sizeMultiplier;
}
// set the output scale on the PassSrg
RPI::FullscreenTrianglePass* downsamplePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale"));
downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
// set the output scale on the PassSrg
RPI::FullscreenTrianglePass* downsamplePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
RHI::ShaderInputNameIndex outputImageScaleShaderInput = "m_outputImageScale";
downsamplePass->GetShaderResourceGroup()->SetConstant(
outputImageScaleShaderInput, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
// handle all downsample passes
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
// update the image scale on the DiffuseComposite pass
AZStd::vector<Name> compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") };
RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy);
const AZStd::vector<RPI::Pass*>& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter);
for (RPI::Pass* pass : compositePasses)
{
RPI::FullscreenTrianglePass* compositePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale"));
compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
AZStd::vector<Name> compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") };
RPI::PassFilter compositePassFilter = RPI::PassFilter::CreateWithPassHierarchy(compositePassHierarchy);
RPI::PassSystemInterface::Get()->ForEachPass(compositePassFilter, [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
RPI::FullscreenTrianglePass* compositePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
RHI::ShaderInputNameIndex imageScaleShaderInput = "m_imageScale";
compositePass->GetShaderResourceGroup()->SetConstant(imageScaleShaderInput, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
}
} // namespace Render
@@ -603,12 +603,12 @@ namespace AZ
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
if (device->GetFeatures().m_rayTracing == false)
{
RPI::PassHierarchyFilter updatePassFilter(AZ::Name("DiffuseProbeGridUpdatePass"));
const AZStd::vector<RPI::Pass*>& updatePasses = RPI::PassSystemInterface::Get()->FindPasses(updatePassFilter);
for (RPI::Pass* pass : updatePasses)
{
pass->SetEnabled(false);
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("DiffuseProbeGridUpdatePass"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(false);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
}
@@ -10,9 +10,10 @@
#include <ACES/Aces.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassFactory.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/RPIUtils.h>
@@ -66,22 +67,14 @@ namespace AZ
{
// Need to invalidate the CopyToSwapChain pass so that it updates the pipeline state in the event that
// the swapchain format changed (for example, moving from LDR to HDR display)
auto* passSystem = RPI::PassSystemInterface::Get();
const Name fullscreenCopyTemplateName("FullscreenCopyTemplate");
if (passSystem->HasPassesForTemplateName(fullscreenCopyTemplateName))
{
const AZStd::vector<RPI::Pass*>& passes = passSystem->GetPassesForTemplateName(fullscreenCopyTemplateName);
for (RPI::Pass* pass : passes)
const Name copyToSwapChainPassName("CopyToSwapChain");
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(copyToSwapChainPassName, GetRenderPipeline());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
RPI::FullscreenTrianglePass* fullscreenTrianglePass = azrtti_cast<RPI::FullscreenTrianglePass*>(pass);
const Name& passName = fullscreenTrianglePass->GetName();
if (passName.GetStringView() == "CopyToSwapChain")
{
fullscreenTrianglePass->QueueForInitialization();
}
}
}
pass->QueueForInitialization();
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
ConfigureDisplayParameters();
}
@@ -372,29 +372,25 @@ namespace AZ
}
m_latestCaptureInfo.clear();
// Find the pass first
RPI::PassClassFilter<RPI::ImageAttachmentPreviewPass> passFilter;
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (foundPasses.size() == 0)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<RPI::ImageAttachmentPreviewPass>();
AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast<AZ::RPI::ImageAttachmentPreviewPass*>(RPI::PassSystemInterface::Get()->FindFirstPass(passFilter));
if (!previewPass)
{
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass pass ");
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass");
return false;
}
AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast<AZ::RPI::ImageAttachmentPreviewPass*>(foundPasses[0]);
bool result = previewPass->ReadbackOutput(m_readback);
if (result)
{
m_state = State::Pending;
m_result = FrameCaptureResult::None;
SystemTickBus::Handler::BusConnect();
return true;
}
else
{
AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");;
}
return result;
AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");
return false;
}
bool FrameCaptureSystemComponent::CapturePassAttachment(const AZStd::vector<AZStd::string>& passHierarchy, const AZStd::string& slot,
@@ -405,6 +401,12 @@ namespace AZ
return false;
}
if (passHierarchy.size() == 0)
{
AZ_Warning("FrameCaptureSystemComponent", false, "Empty data in passHierarchy");
return false;
}
InitReadback();
if (m_state != State::Idle)
@@ -426,17 +428,15 @@ namespace AZ
}
m_latestCaptureInfo.clear();
// Find the pass first
AZ::RPI::PassHierarchyFilter passFilter(passHierarchy);
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchy);
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (foundPasses.size() == 0)
if (!pass)
{
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str());
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passHierarchy[0].c_str());
return false;
}
AZ::RPI::Pass* pass = foundPasses[0];
if (pass->ReadbackAttachment(m_readback, Name(slot), option))
{
m_state = State::Pending;
@@ -444,6 +444,7 @@ namespace AZ
SystemTickBus::Handler::BusConnect();
return true;
}
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to readback the attachment bound to pass [%s] slot [%s]", pass->GetName().GetCStr(), slot.c_str());
return false;
}
@@ -109,15 +109,15 @@ namespace AZ
void ImGuiSystemComponent::ForAllImGuiPasses(PassFunction func)
{
ImGuiContext* contextToRestore = ImGui::GetCurrentContext();
RPI::PassClassFilter<ImGuiPass> filter;
auto imguiPasses = RPI::PassSystemInterface::Get()->FindPasses(filter);
for (RPI::Pass* pass : imguiPasses)
{
ImGuiPass* imguiPass = azrtti_cast<ImGuiPass*>(pass);
ImGui::SetCurrentContext(imguiPass->GetContext());
func(imguiPass);
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<ImGuiPass>();
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [func](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
ImGuiPass* imguiPass = azrtti_cast<ImGuiPass*>(pass);
ImGui::SetCurrentContext(imguiPass->GetContext());
func(imguiPass);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
ImGui::SetCurrentContext(contextToRestore);
}
@@ -169,29 +169,37 @@ namespace AZ
return false;
}
bool ImGuiSystemComponent::PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchyFilter)
bool ImGuiSystemComponent::PushActiveContextFromPass(const AZStd::vector<AZStd::string>& passHierarchyFilter)
{
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passHierarchyFilter);
if (passHierarchyFilter.size() == 0)
{
AZ_Warning("ImGuiSystemComponent", false, "passHierarchyFilter is empty");
return false;
}
AZStd::vector<ImGuiPass*> foundImGuiPasses;
for (RPI::Pass* pass : foundPasses)
{
ImGuiPass* imGuiPass = azrtti_cast<ImGuiPass*>(pass);
if (imGuiPass)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchyFilter);
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&foundImGuiPasses](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
foundImGuiPasses.push_back(imGuiPass);
}
}
ImGuiPass* imGuiPass = azrtti_cast<ImGuiPass*>(pass);
if (imGuiPass)
{
foundImGuiPasses.push_back(imGuiPass);
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
if (foundImGuiPasses.size() == 0)
{
AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter.ToString().c_str());
AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter[0].c_str());
return false;
}
if (foundImGuiPasses.size() > 1)
{
AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter.ToString().c_str());
AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter[0].c_str());
}
ImGuiContext* context = foundImGuiPasses.at(0)->GetContext();
@@ -56,7 +56,7 @@ namespace AZ
ImGuiPass* GetDefaultImGuiPass() override;
bool PushActiveContextFromDefaultPass() override;
bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) override;
bool PushActiveContextFromPass(const AZStd::vector<AZStd::string>& passHierarchy) override;
bool PopActiveContext() override;
ImGuiContext* GetActiveContext() override;
@@ -15,6 +15,7 @@
#include <AzFramework/Asset/AssetSystemBus.h>
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/RPIUtils.h>
@@ -259,24 +260,19 @@ namespace AZ
// [GFX TODO][ATOM-3035]This function is temporary and will change with improvement to the draw list tag system
void DepthOfFieldSettings::UpdateAutoFocusDepth(bool enabled)
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
{
const Name TemplateNameReadBackFocusDepth = Name("DepthOfFieldReadBackFocusDepthTemplate");
if (passSystem->HasPassesForTemplateName(TemplateNameReadBackFocusDepth))
{
const AZStd::vector<RPI::Pass*>& dofPasses = passSystem->GetPassesForTemplateName(TemplateNameReadBackFocusDepth);
for (RPI::Pass* pass : dofPasses)
// [GFX TODO][ATOM-4908] multiple camera should be distingushed.
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(TemplateNameReadBackFocusDepth, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, enabled](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* dofPass = azrtti_cast<AZ::Render::DepthOfFieldReadBackFocusDepthPass*>(pass);
// Check this pass belongs to a render pipeline of the scene.
// [GFX TODO][ATOM-4908] multiple camera should be distingushed.
const RPI::RenderPipelineId pipelineId = dofPass->GetRenderPipeline()->GetId();
if (enabled && GetParentScene()->GetRenderPipeline(pipelineId))
if (enabled)
{
m_normalizedFocusDistanceForAutoFocus = dofPass->GetNormalizedFocusDistanceForAutoFocus();
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void DepthOfFieldSettings::SetCameraEntityId(EntityId cameraEntityId)
@@ -10,6 +10,7 @@
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
@@ -188,21 +189,21 @@ namespace AZ
void ExposureControlSettings::UpdateLuminanceHeatmap()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
// [GFX-TODO][ATOM-13194] Support multiple views for the luminance heatmap
// [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass
const RPI::Ptr<RPI::Pass> luminanceHeatmap = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHeatmapNameId);
if (luminanceHeatmap)
{
luminanceHeatmap->SetEnabled(m_heatmapEnabled);
}
// [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass
RPI::PassFilter heatmapPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHeatmapNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(heatmapPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(m_heatmapEnabled);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
const RPI::Ptr<RPI::Pass> histogramGenerator = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHistogramGeneratorNameId);
if (histogramGenerator)
{
histogramGenerator->SetEnabled(m_heatmapEnabled);
}
RPI::PassFilter histogramPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHistogramGeneratorNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(histogramPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(m_heatmapEnabled);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void ExposureControlSettings::UpdateBuffer()
@@ -31,12 +31,14 @@ namespace AZ
0,
[](const uint8_t& value)
{
auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter<LookModificationCompositePass>());
for (auto* pass : passes)
{
LookModificationCompositePass* lookModPass = azrtti_cast<LookModificationCompositePass*>(pass);
lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value));
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<LookModificationCompositePass>();
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [value](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
LookModificationCompositePass* lookModPass = azrtti_cast<LookModificationCompositePass*>(pass);
lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value));
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
},
ConsoleFunctorFlags::Null,
"This can be increased to deal with particularly tricky luts. Range (0-2). 0 (default) - Standard linear sampling. 1 - 7 tap b-spline sampling. 2 - 19 tap b-spline sampling."
@@ -16,6 +16,7 @@
#include <Atom/RHI/Factory.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
@@ -71,26 +72,18 @@ namespace AZ
void SMAAFeatureProcessor::UpdateConvertToPerceptualPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& convertToPerceptualColorPasses = passSystem->GetPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId);
for (RPI::Pass* pass : convertToPerceptualColorPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_convertToPerceptualColorPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(m_data.m_enable);
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::UpdateEdgeDetectionPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_edgeDetectioPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& edgeDetectionPasses = passSystem->GetPassesForTemplateName(m_edgeDetectioPassTemplateNameId);
for (RPI::Pass* pass : edgeDetectionPasses)
{
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_edgeDetectioPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* edgeDetectionPass = azrtti_cast<AZ::Render::SMAAEdgeDetectionPass*>(pass);
@@ -106,18 +99,14 @@ namespace AZ
edgeDetectionPass->SetPredicationScale(m_data.m_predicationScale);
edgeDetectionPass->SetPredicationStrength(m_data.m_predicationStrength);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::UpdateBlendingWeightCalculationPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& blendingWeightCalculationPasses = passSystem->GetPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId);
for (RPI::Pass* pass : blendingWeightCalculationPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_blendingWeightCalculationPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* blendingWeightCalculationPass = azrtti_cast<AZ::Render::SMAABlendingWeightCalculationPass*>(pass);
@@ -130,18 +119,14 @@ namespace AZ
blendingWeightCalculationPass->SetDiagonalDetectionEnable(m_data.m_enableDiagonalDetection);
blendingWeightCalculationPass->SetCornerDetectionEnable(m_data.m_enableCornerDetection);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::UpdateNeighborhoodBlendingPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& neighborhoodBlendingPasses = passSystem->GetPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId);
for (RPI::Pass* pass : neighborhoodBlendingPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_neighborhoodBlendingPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* neighborhoodBlendingPass = azrtti_cast<AZ::Render::SMAANeighborhoodBlendingPass*>(pass);
@@ -153,8 +138,8 @@ namespace AZ
{
neighborhoodBlendingPass->SetOutputMode(SMAAOutputMode::PassThrough);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet)
@@ -377,14 +377,7 @@ namespace AZ
bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath)
{
// Find the root pass.
AZStd::vector<RPI::Pass*> passes = FindPasses({ "Root" });
if (passes.empty())
{
return false;
}
RPI::Pass* root = passes[0];
RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get();
// Enable all the Timestamp queries in passes.
root->SetTimestampQueryEnabled(true);
@@ -465,14 +458,7 @@ namespace AZ
bool ProfilingCaptureSystemComponent::CapturePassPipelineStatistics(const AZStd::string& outputFilePath)
{
// Find the root pass.
AZStd::vector<RPI::Pass*> passes = FindPasses({ "Root" });
if (passes.empty())
{
return false;
}
RPI::Pass* root = passes[0];
RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get();
// Enable all the PipelineStatistics queries in passes.
root->SetPipelineStatisticsQueryEnabled(true);
@@ -572,19 +558,6 @@ namespace AZ
return passes;
}
AZStd::vector<RPI::Pass*> ProfilingCaptureSystemComponent::FindPasses(AZStd::vector<AZStd::string>&& passHierarchy) const
{
// Find the pass first.
RPI::PassHierarchyFilter passFilter(passHierarchy);
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (foundPasses.size() == 0)
{
AZ_Warning("ProfilingCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str());
}
return foundPasses;
}
void ProfilingCaptureSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{
// Update the delayed captures
@@ -78,8 +78,6 @@ namespace AZ
// Recursively collect all the passes from the root pass.
AZStd::vector<const RPI::Pass*> CollectPassesRecursively(const RPI::Pass* root) const;
AZStd::vector<AZ::RPI::Pass*> FindPasses(AZStd::vector<AZStd::string>&& passHierarchy) const;
DelayedQueryCaptureHelper m_timestampCapture;
DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture;
DelayedQueryCaptureHelper m_pipelineStatisticsCapture;
@@ -28,16 +28,17 @@ namespace AZ
void ReflectionCopyFrameBufferPass::BuildInternal()
{
RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass"));
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (!passes.empty())
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(passes.front());
Data::Instance<RPI::AttachmentImage>& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment();
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(pass);
Data::Instance<RPI::AttachmentImage>& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment();
RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0);
AttachImageToSlot(outputBinding.m_name, frameBufferAttachment);
}
RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0);
AttachImageToSlot(outputBinding.m_name, frameBufferAttachment);
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
FullscreenTrianglePass::BuildInternal();
}
@@ -150,7 +150,7 @@ namespace AZ
auto transientImageDesc = RHI::ImageDescriptor::Create2D(imageBindFlags, mipSize.m_width, mipSize.m_height, RHI::Format::R16G16B16A16_FLOAT);
RPI::PassAttachment* transientPassAttachment = aznew RPI::PassAttachment();
AZStd::string transientAttachmentName = AZStd::string::format("ReflectionScreenSpace_BlurImage%d", mip);
AZStd::string transientAttachmentName = AZStd::string::format("%s.ReflectionScreenSpace_BlurImage%d", GetPathName().GetCStr(), mip);
transientPassAttachment->m_name = transientAttachmentName;
transientPassAttachment->m_path = transientAttachmentName;
transientPassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient;
@@ -33,20 +33,22 @@ namespace AZ
return;
}
RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass"));
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (!passes.empty())
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(passes.front());
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline());
// compute the max mip level based on the available mips in the previous frame image, and capping it
// to stay within a range that has reasonable data
const uint32_t MaxNumRoughnessMips = 8;
uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1;
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(pass);
auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel"));
m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel);
}
// compute the max mip level based on the available mips in the previous frame image, and capping it
// to stay within a range that has reasonable data
const uint32_t MaxNumRoughnessMips = 8;
uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1;
auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel"));
m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel);
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
FullscreenTrianglePass::CompileResources(context);
}
@@ -313,52 +313,38 @@ namespace AZ::Render
void ProjectedShadowFeatureProcessor::CachePasses()
{
const AZStd::vector<RPI::RenderPipelineId> validPipelineIds = CacheProjectedShadowmapsPass();
CacheEsmShadowmapsPass(validPipelineIds);
CacheProjectedShadowmapsPass();
CacheEsmShadowmapsPass();
m_shadowmapPassNeedsUpdate = true;
}
AZStd::vector<RPI::RenderPipelineId> ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass()
void ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass()
{
const AZStd::vector<RPI::RenderPipelinePtr>& renderPipelines = GetParentScene()->GetRenderPipelines();
const auto* passSystem = RPI::PassSystemInterface::Get();;
const AZStd::vector<RPI::Pass*>& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate"));
AZStd::vector<RPI::RenderPipelineId> validPipelineIds;
m_projectedShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
ProjectedShadowmapsPass* shadowPass = static_cast<ProjectedShadowmapsPass*>(pass);
for (const RPI::RenderPipelinePtr& pipeline : renderPipelines)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("ProjectedShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
if (pipeline.get() == shadowPass->GetRenderPipeline())
{
m_projectedShadowmapsPasses.emplace_back(shadowPass);
validPipelineIds.push_back(shadowPass->GetRenderPipeline()->GetId());
}
}
}
return validPipelineIds;
ProjectedShadowmapsPass* shadowPass = static_cast<ProjectedShadowmapsPass*>(pass);
m_projectedShadowmapsPasses.emplace_back(shadowPass);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector<RPI::RenderPipelineId>& validPipelineIds)
void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass()
{
const Name LightTypeName = Name("projected");
const auto* passSystem = RPI::PassSystemInterface::Get();
const AZStd::vector<RPI::Pass*> passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate"));
m_esmShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
EsmShadowmapsPass* esmPass = static_cast<EsmShadowmapsPass*>(pass);
if (esmPass->GetRenderPipeline() &&
AZStd::find(validPipelineIds.begin(), validPipelineIds.end(), esmPass->GetRenderPipeline()->GetId()) != validPipelineIds.end() &&
esmPass->GetLightTypeName() == LightTypeName)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, LightTypeName](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
m_esmShadowmapsPasses.emplace_back(esmPass);
}
}
EsmShadowmapsPass* esmPass = static_cast<EsmShadowmapsPass*>(pass);
if (esmPass->GetLightTypeName() == LightTypeName)
{
m_esmShadowmapsPasses.emplace_back(esmPass);
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void ProjectedShadowFeatureProcessor::UpdateFilterParameters()
@@ -97,8 +97,8 @@ namespace AZ::Render
// Functions for caching the ProjectedShadowmapsPass and EsmShadowmapsPass.
void CachePasses();
AZStd::vector<RPI::RenderPipelineId> CacheProjectedShadowmapsPass();
void CacheEsmShadowmapsPass(const AZStd::vector<RPI::RenderPipelineId>& validPipelineIds);
void CacheProjectedShadowmapsPass();
void CacheEsmShadowmapsPass();
//! Functions to update the parameter of Gaussian filter used in ESM.
void UpdateFilterParameters();
@@ -17,6 +17,7 @@
#include <MorphTargets/MorphTargetDispatchItem.h>
#include <Atom/RPI.Public/Model/ModelLodUtils.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@@ -241,12 +242,12 @@ namespace AZ
void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline)
{
InitSkinningAndMorphPass(pipeline->GetRootPass());
InitSkinningAndMorphPass(pipeline.get());
}
void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
{
InitSkinningAndMorphPass(renderPipeline->GetRootPass());
InitSkinningAndMorphPass(renderPipeline);
}
void SkinnedMeshFeatureProcessor::OnBeginPrepareRender()
@@ -289,9 +290,10 @@ namespace AZ
return false;
}
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass)
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline)
{
RPI::Ptr<RPI::Pass> skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" });
RPI::PassFilter skinPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "SkinningPass" }, renderPipeline);
RPI::Ptr<RPI::Pass> skinningPass = RPI::PassSystemInterface::Get()->FindFirstPass(skinPassFilter);
if (skinningPass)
{
SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast<SkinnedMeshComputePass*>(skinningPass.get());
@@ -310,7 +312,8 @@ namespace AZ
}
}
RPI::Ptr<RPI::Pass> morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" });
RPI::PassFilter morphPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "MorphTargetPass" }, renderPipeline);
RPI::Ptr<RPI::Pass> morphTargetPass = RPI::PassSystemInterface::Get()->FindFirstPass(morphPassFilter);
if (morphTargetPass)
{
MorphTargetComputePass* morphTargetComputePass = azdynamic_cast<MorphTargetComputePass*>(morphTargetPass.get());
@@ -66,7 +66,7 @@ namespace AZ
private:
AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor);
void InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass);
void InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline);
SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override;
bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override;
@@ -7,10 +7,10 @@
#
set(FILES
Shader/DecomposeMsImage.azsl
Shader/DecomposeMsImage.shader
Shader/ImagePreview.azsl
Shader/ImagePreview.shader
Shaders/DecomposeMsImage.azsl
Shaders/DecomposeMsImage.shader
Shaders/ImagePreview.azsl
Shaders/ImagePreview.shader
ShaderLib/Atom/RPI/Math.azsli
ShaderLib/Atom/RPI/TangentSpace.azsli
ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli
@@ -68,9 +68,6 @@ namespace AZ
template<typename PassType>
Ptr<PassType> FindChildPass() const;
//! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found.
Ptr<Pass> FindPassByNameRecursive(const Name& passName) const;
//! Gets the list of children. Useful for validating hierarchies
AZStd::array_view<Ptr<Pass>> GetChildren() const;
@@ -139,6 +139,10 @@ namespace AZ
//! Returns the number of output attachment bindings
uint32_t GetOutputCount() const;
//! Returns the pass template which was used for create this pass.
//! It may return nullptr if the pass wasn't create from a template
const PassTemplate* GetPassTemplate() const;
//! Enable/disable this pass
//! If the pass is disabled, it (and any children if it's a ParentPass) won't be rendered.
void SetEnabled(bool enabled);
@@ -16,95 +16,85 @@ namespace AZ
{
namespace RPI
{
// A base class for a filter which can be used to filter passes
class Scene;
class RenderPipeline;
class PassFilter
{
public:
//! Whether the input pass matches with the filter
virtual bool Matches(const Pass* pass) const = 0;
static PassFilter CreateWithPassName(Name passName, const Scene* scene);
static PassFilter CreateWithPassName(Name passName, const RenderPipeline* renderPipeline);
//! Return the pass' name if a pass name is used for the filter.
//! Return nullptr if the filter doesn't have pass name used for matching
virtual const Name* GetPassName() const = 0;
//! Create a PassFilter with pass hierarchy information
//! Filter for passes which have a matching name and also with ordered parents.
//! For example, if the filter is initialized with
//! pass name: "ShadowPass1"
//! pass parents names: "MainPipeline", "Shadow"
//! Passes with these names match the filter:
//! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1"
//! or "Root.MainPipeline.Shadow.ShadowPass1"
//! or "MainPipeline.Shadow.Group1.ShadowPass1"
//!
//! Passes with these names wont match:
//! "MainPipeline.ShadowPass1"
//! or "Shadow.MainPipeline.ShadowPass1"
static PassFilter CreateWithPassHierarchy(const AZStd::vector<Name>& passHierarchy);
static PassFilter CreateWithPassHierarchy(const AZStd::vector<AZStd::string>& passHierarchy);
static PassFilter CreateWithTemplateName(Name templateName, const Scene* scene);
static PassFilter CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline);
template <typename PassClass>
static PassFilter CreateWithPassClass();
//! Return this filter's info as a string
virtual AZStd::string ToString() const = 0;
};
enum FilterOptions : uint32_t
{
Empty = 0,
PassName = AZ_BIT(0),
PassTemplateName = AZ_BIT(1),
PassClass = AZ_BIT(2),
PassHierarchy = AZ_BIT(3),
OwnerScene = AZ_BIT(4),
OwnerRenderPipeline = AZ_BIT(5)
};
//! Filter for passes which have a matching name and also with ordered parents.
//! For example, if the filter is initialized with
//! pass name: "ShadowPass1"
//! pass parents names: "MainPipeline", "Shadow"
//! Passes with these names match the filter:
//! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1"
//! or "Root.MainPipeline.Shadow.ShadowPass1"
//! or "MainPipeline.Shadow.Group1.ShadowPass1"
//!
//! Passes with these names wont match:
//! "MainPipeline.ShadowPass1"
//! or "Shadow.MainPipeline.ShadowPass1"
class PassHierarchyFilter
: public PassFilter
{
public:
AZ_RTTI(PassHierarchyFilter, "{478F169F-BA97-4321-AC34-EDE823997159}", PassFilter);
AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0);
void SetOwenrScene(const Scene* scene);
void SetOwenrRenderPipeline(const RenderPipeline* renderPipeline);
void SetPassName(Name passName);
void SetTemplateName(Name passTemplateName);
void SetPassClass(TypeId passClassTypeId);
//! Construct filter with only pass name.
PassHierarchyFilter(const Name& passName);
const Name& GetPassName() const;
const Name& GetPassTemplateName() const;
virtual ~PassHierarchyFilter() = default;
uint32_t GetEnabledFilterOptions() const;
//! Construct filter with pass name and its parents' names in the order of the hierarchy
//! This means k-th element is always an ancestor of the (k-1)-th element.
//! And the last element is the pass name.
PassHierarchyFilter(const AZStd::vector<Name>& passHierarchy);
PassHierarchyFilter(const AZStd::vector<AZStd::string>& passHierarchy);
//! Return true if the input pass matches the filter
bool Matches(const Pass* pass) const;
// PassFilter overrides...
bool Matches(const Pass* pass) const override;
const Name* GetPassName() const override;
AZStd::string ToString() const override;
//! Return true if the input pass matches the filter with selected filter options
//! The input filter options should be a subset of options returned by GetEnabledFilterOptions()
//! This function is used to avoid extra checks for passes which was already filtered.
//! Check PassLibrary::ForEachPass() function's implementation for more details
bool Matches(const Pass* pass, uint32_t options) const;
private:
PassHierarchyFilter() = delete;
void UpdateFilterOptions();
AZStd::vector<Name> m_parentNames;
Name m_passName;
Name m_templateName;
TypeId m_passClassTypeId = TypeId::CreateNull();
AZStd::vector<Name> m_parentNames;
const RenderPipeline* m_ownerRenderPipeline = nullptr;
const Scene* m_ownerScene = nullptr;
uint32_t m_filterOptions = 0;
};
//! Filter for passes based on their class.
template<typename PassClass>
class PassClassFilter
: public PassFilter
{
public:
AZ_RTTI(PassClassFilter, "{AF6E3AD5-433A-462A-997A-F36D8A551D02}", PassFilter);
AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0);
PassClassFilter() = default;
// PassFilter overrides...
bool Matches(const Pass* pass) const override;
const Name* GetPassName() const override;
AZStd::string ToString() const override;
};
template<typename PassClass>
bool PassClassFilter<PassClass>::Matches(const Pass* pass) const
{
return pass->RTTI_IsTypeOf(PassClass::RTTI_Type());
}
template<typename PassClass>
const Name* PassClassFilter<PassClass>::GetPassName() const
{
return nullptr;
}
template<typename PassClass>
AZStd::string PassClassFilter<PassClass>::ToString() const
{
return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName());
template <typename PassClass>
PassFilter PassFilter::CreateWithPassClass()
{
PassFilter filter;
filter.m_passClassTypeId = PassClass::RTTI_Type();
filter.UpdateFilterOptions();
return filter;
}
} // namespace RPI
} // namespace AZ
@@ -84,8 +84,8 @@ namespace AZ
bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath);
bool LoadPassTemplateMappings(Data::Asset<AnyAsset> mappingAsset);
//! Returns a list of passes found in the pass name mapping using the provided pass filter
AZStd::vector<Pass*> FindPasses(const PassFilter& passFilter) const;
//! Visit each pass which matches the filter
void ForEachPass(const PassFilter& passFilter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction);
private:
@@ -92,13 +92,13 @@ namespace AZ
// PassSystemInterface library related functions...
bool HasPassesForTemplateName(const Name& templateName) const override;
const AZStd::vector<Pass*>& GetPassesForTemplateName(const Name& templateName) const override;
bool AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate) override;
const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const override;
void RemovePassFromLibrary(Pass* pass) override;
void RegisterPass(Pass* pass) override;
void UnregisterPass(Pass* pass) override;
AZStd::vector<Pass*> FindPasses(const PassFilter& passFilter) const override;
void ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction) override;
Pass* FindFirstPass(const PassFilter& filter) override;
private:
// Returns the root of the pass tree hierarchy
@@ -75,6 +75,13 @@ namespace AZ
u32 m_maxDrawItemsRenderedInAPass = 0;
};
enum PassFilterExecutionFlow : uint8_t
{
StopVisitingPasses,
ContinueVisitingPasses,
};
class PassSystemInterface
{
friend class Pass;
@@ -186,9 +193,6 @@ namespace AZ
//! Returns true if the pass factory contains passes created with the given template name
virtual bool HasPassesForTemplateName(const Name& templateName) const = 0;
//! Get the passes created with the given template name.
virtual const AZStd::vector<Pass*>& GetPassesForTemplateName(const Name& templateName) const = 0;
//! Adds a PassTemplate to the library
virtual bool AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate) = 0;
@@ -197,9 +201,16 @@ namespace AZ
//! Removes all references to the given pass from the pass library
virtual void RemovePassFromLibrary(Pass* pass) = 0;
//! Visit the matching passes from registered passes with specified filter
//! The return value of the passFunction decides if the search continues or not
//! Note: this function will find all the passes which match the pass filter even they are for render pipelines which are not added to a scene
//! This function is fast if a pass name or a pass template name is specified.
virtual void ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction) = 0;
//! Find matching passes from registered passes with specified filter
virtual AZStd::vector<Pass*> FindPasses(const PassFilter& passFilter) const = 0;
//! Find the first matching pass from registered passes with specified filter
//! Note: this function SHOULD ONLY be used when you are certain you only need to handle the first pass found
virtual Pass* FindFirstPass(const PassFilter& filter) = 0;
private:
// These functions are only meant to be used by the Pass class
@@ -32,7 +32,7 @@ namespace AZ
//! The asset cache relative path of the only common shader asset for the RPI system that is used
//! as means to load the layout for scene srg and view srg. This is used to create any RPI::Scene.
AZStd::string m_commonSrgsShaderAssetPath = "shader/sceneandviewsrgs.azshader";
AZStd::string m_commonSrgsShaderAssetPath = "shaders/sceneandviewsrgs.azshader";
ImageSystemDescriptor m_imageSystemDescriptor;
GpuQuerySystemDescriptor m_gpuQuerySystemDescriptor;
@@ -121,7 +121,7 @@ namespace AZ
m_fence->Init(*device, RHI::FenceState::Reset);
// Load shader and srg
const char* ShaderPath = "shader/decomposemsimage.azshader";
const char* ShaderPath = "shaders/decomposemsimage.azshader";
m_decomposeShader = LoadCriticalShader(ShaderPath);
if (m_decomposeShader == nullptr)
@@ -149,29 +149,6 @@ namespace AZ
return index.IsValid() ? m_children[index.GetIndex()] : Ptr<Pass>(nullptr);
}
Ptr<Pass> ParentPass::FindPassByNameRecursive(const Name& passName) const
{
for (const Ptr<Pass>& child : m_children)
{
if (child->GetName() == passName)
{
return child.get();
}
ParentPass* asParent = child->AsParent();
if (asParent)
{
auto pass = asParent->FindPassByNameRecursive(passName);
if (pass)
{
return pass;
}
}
}
return nullptr;
}
const Pass* ParentPass::FindPass(RHI::DrawListTag drawListTag) const
{
if (HasDrawListTag() && GetDrawListTag() == drawListTag)
@@ -238,6 +238,11 @@ namespace AZ
return m_attachmentBindings[bindingIndex];
}
const PassTemplate* Pass::GetPassTemplate() const
{
return m_template.get();
}
void Pass::AddAttachmentBinding(PassAttachmentBinding attachmentBinding)
{
// Add the index of the binding to the input, output or input/output list based on the slot type
@@ -8,101 +8,264 @@
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/ParentPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
namespace AZ
{
namespace RPI
{
PassHierarchyFilter::PassHierarchyFilter(const Name& passName)
PassFilter PassFilter::CreateWithPassName(Name passName, const Scene* scene)
{
PassFilter filter;
filter.m_passName = passName;
filter.m_ownerScene = scene;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithPassName(Name passName, const RenderPipeline* renderPipeline)
{
PassFilter filter;
filter.m_passName = passName;
filter.m_ownerRenderPipeline = renderPipeline;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithTemplateName(Name templateName, const Scene* scene)
{
PassFilter filter;
filter.m_templateName = templateName;
filter.m_ownerScene = scene;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline)
{
PassFilter filter;
filter.m_templateName = templateName;
filter.m_ownerRenderPipeline = renderPipeline;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector<Name>& passHierarchy)
{
PassFilter filter;
if (passHierarchy.size() == 0)
{
AZ_Assert(false, "passHierarchy should have at least one element");
return filter;
}
filter.m_passName = passHierarchy.back();
filter.m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < filter.m_parentNames.size(); index++)
{
filter.m_parentNames[index] = passHierarchy[index];
}
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector<AZStd::string>& passHierarchy)
{
PassFilter filter;
if (passHierarchy.size() == 0)
{
AZ_Assert(false, "passHierarchy should have at least one element");
return filter;
}
filter.m_passName = Name(passHierarchy.back());
filter.m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < filter.m_parentNames.size(); index++)
{
filter.m_parentNames[index] = Name(passHierarchy[index]);
}
filter.UpdateFilterOptions();
return filter;
}
void PassFilter::SetOwenrScene(const Scene* scene)
{
m_ownerScene = scene;
UpdateFilterOptions();
}
void PassFilter::SetOwenrRenderPipeline(const RenderPipeline* renderPipeline)
{
m_ownerRenderPipeline = renderPipeline;
UpdateFilterOptions();
}
void PassFilter::SetPassName(Name passName)
{
m_passName = passName;
UpdateFilterOptions();
}
PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector<AZStd::string>& passHierarchy)
void PassFilter::SetTemplateName(Name passTemplateName)
{
if (passHierarchy.size() == 0)
{
AZ_Assert(false, "passHierarchy should have at least one element");
return;
}
m_passName = Name(passHierarchy.back());
m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < m_parentNames.size(); index++)
{
m_parentNames[index] = Name(passHierarchy[index]);
}
m_templateName = passTemplateName;
UpdateFilterOptions();
}
PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector<Name>& passHierarchy)
void PassFilter::SetPassClass(TypeId passClassTypeId)
{
if (passHierarchy.size() == 0)
{
AZ_Assert(false, "passHierarchy should have at least one element");
return;
}
m_passName = passHierarchy.back();
m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < m_parentNames.size(); index++)
{
m_parentNames[index] = passHierarchy[index];
}
m_passClassTypeId = passClassTypeId;
UpdateFilterOptions();
}
bool PassHierarchyFilter::Matches(const Pass* pass) const
const Name& PassFilter::GetPassName() const
{
if (pass->GetName() != m_passName)
return m_passName;
}
const Name& PassFilter::GetPassTemplateName() const
{
return m_templateName;
}
uint32_t PassFilter::GetEnabledFilterOptions() const
{
return m_filterOptions;
}
bool PassFilter::Matches(const Pass* pass) const
{
return Matches(pass, m_filterOptions);
}
bool PassFilter::Matches(const Pass* pass, uint32_t options) const
{
AZ_Assert( (options&m_filterOptions) == options, "options should be a subset of m_filterOptions");
// return false if the pass doesn't have a pass template or the template's name is not matching
if (options & FilterOptions::PassTemplateName && (!pass->GetPassTemplate() || pass->GetPassTemplate()->m_name != m_templateName))
{
return false;
}
ParentPass* parent = pass->GetParent();
// search from the back of the array with the most close parent
for (int32_t index = static_cast<int32_t>(m_parentNames.size() - 1); index >= 0; index--)
if ((options & FilterOptions::PassName) && pass->GetName() != m_passName)
{
const Name& parentName = m_parentNames[index];
while (parent)
{
if (parent->GetName() == parentName)
{
break;
}
parent = parent->GetParent();
}
return false;
}
// if parent is nullptr the it didn't find a parent has matching current parentName
if (!parent)
if ((options & FilterOptions::PassClass) && pass->RTTI_GetType() != m_passClassTypeId)
{
return false;
}
if ((options & FilterOptions::OwnerRenderPipeline) && m_ownerRenderPipeline != pass->GetRenderPipeline())
{
return false;
}
// If the owner render pipeline was checked, the owner scene check can be skipped
if (options & FilterOptions::OwnerScene)
{
if (pass->GetRenderPipeline())
{
// return false if the owner scene doesn't match
if (m_ownerScene != pass->GetRenderPipeline()->GetScene())
{
return false;
}
}
else
{
// return false if the pass doesn't have an owner scene
return false;
}
}
// move to next parent
parent = parent->GetParent();
if ((options & FilterOptions::PassHierarchy))
{
// Filter for passes which have a matching name and also with ordered parents.
// For example, if the filter is initialized with
// pass name: "ShadowPass1"
// pass parents names: "MainPipeline", "Shadow"
// Passes with these names match the filter:
// "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1"
// or "Root.MainPipeline.Shadow.ShadowPass1"
// or "MainPipeline.Shadow.Group1.ShadowPass1"
//
// Passes with these names wont match:
// "MainPipeline.ShadowPass1"
// or "Shadow.MainPipeline.ShadowPass1"
ParentPass* parent = pass->GetParent();
// search from the back of the array with the most close parent
for (int32_t index = static_cast<int32_t>(m_parentNames.size() - 1); index >= 0; index--)
{
const Name& parentName = m_parentNames[index];
while (parent)
{
if (parent->GetName() == parentName)
{
break;
}
parent = parent->GetParent();
}
// if parent is nullptr the it didn't find a parent has matching current parentName
if (!parent)
{
return false;
}
// move to next parent
parent = parent->GetParent();
}
}
return true;
}
const Name* PassHierarchyFilter::GetPassName() const
void PassFilter::UpdateFilterOptions()
{
return &m_passName;
}
AZStd::string PassHierarchyFilter::ToString() const
{
AZStd::string result = "PassHierarchyFilter";
for (uint32_t index = 0; index < m_parentNames.size(); index++)
m_filterOptions = FilterOptions::Empty;
if (!m_passName.IsEmpty())
{
result += AZStd::string::format(" [%s]", m_parentNames[index].GetCStr());
m_filterOptions |= FilterOptions::PassName;
}
if (!m_templateName.IsEmpty())
{
m_filterOptions |= FilterOptions::PassTemplateName;
}
if (m_parentNames.size() > 0)
{
m_filterOptions |= FilterOptions::PassHierarchy;
}
if (m_ownerRenderPipeline)
{
m_filterOptions |= FilterOptions::OwnerRenderPipeline;
}
if (m_ownerScene)
{
// If the OwnerRenderPipeline exists, we shouldn't need to filter owner scene
// Validate the owner render pipeline belongs to the owner scene
if (m_filterOptions & FilterOptions::OwnerRenderPipeline)
{
if (m_ownerRenderPipeline->GetScene() != m_ownerScene)
{
AZ_Warning("RPI", false, "The owner scene filter doesn't match owner render pipeline. It will be skipped.");
}
}
else
{
m_filterOptions |= FilterOptions::OwnerScene;
}
}
if (!m_passClassTypeId.IsNull())
{
m_filterOptions |= FilterOptions::PassClass;
}
result += AZStd::string::format(" [%s]", m_passName.GetCStr());
return result;
}
} // namespace RPI
} // namespace AZ
@@ -85,47 +85,80 @@ namespace AZ
return (GetPassesForTemplate(templateName).size() > 0);
}
AZStd::vector<Pass*> PassLibrary::FindPasses(const PassFilter& passFilter) const
void PassLibrary::ForEachPass(const PassFilter& passFilter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction)
{
const Name* passName = passFilter.GetPassName();
uint32_t filterOptions = passFilter.GetEnabledFilterOptions();
AZStd::vector<Pass*> result;
if (passName)
// A lambda function which visits each pass in a pass list, if the pass matches the pass filter, then call the pass function
auto visitList = [passFilter, passFunction](const AZStd::vector<Pass*>& passList, uint32_t options) -> PassFilterExecutionFlow
{
// If the pass' name is known, find passes with matching names first
const auto constItr = m_passNameMapping.find(*passName);
if (constItr == m_passNameMapping.end())
if (passList.size() == 0)
{
return result;
return PassFilterExecutionFlow::ContinueVisitingPasses;
}
const AZStd::vector<Pass*>& passes = constItr->second;
for (Pass* pass : passes)
// if there is not other filter options enabled, skip the filter and call pass functions directly
if (options == PassFilter::FilterOptions::Empty)
{
if (passFilter.Matches(pass))
for (Pass* pass : passList)
{
result.push_back(pass);
}
}
}
else
{
// If the filter doesn't know matching pass' name, need to go through all registered passes
for (auto& namePasses : m_passNameMapping)
{
for (Pass* pass : namePasses.second)
{
if (passFilter.Matches(pass))
// If user want to skip processing, return directly.
if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses)
{
result.push_back(pass);
return PassFilterExecutionFlow::StopVisitingPasses;
}
}
return PassFilterExecutionFlow::ContinueVisitingPasses;
}
// Check with the pass filter and call pass functions
for (Pass* pass : passList)
{
if (passFilter.Matches(pass, options))
{
if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses)
{
return PassFilterExecutionFlow::StopVisitingPasses;
}
}
}
return PassFilterExecutionFlow::ContinueVisitingPasses;
};
// Check pass template name first
if (filterOptions & PassFilter::FilterOptions::PassTemplateName)
{
auto entry = GetEntry(passFilter.GetPassTemplateName());
if (!entry)
{
return;
}
filterOptions &= ~(PassFilter::FilterOptions::PassTemplateName);
visitList(entry->m_passes, filterOptions);
return;
}
else if (filterOptions & PassFilter::FilterOptions::PassName)
{
const auto constItr = m_passNameMapping.find(passFilter.GetPassName());
if (constItr == m_passNameMapping.end())
{
return;
}
filterOptions &= ~(PassFilter::FilterOptions::PassName);
visitList(constItr->second, filterOptions);
return;
}
return result;
// check againest every passes. This might be slow
AZ_PROFILE_SCOPE(RPI, "PassLibrary::ForEachPass");
for (auto& namePasses : m_passNameMapping)
{
if (visitList(namePasses.second, filterOptions) == PassFilterExecutionFlow::StopVisitingPasses)
{
return;
}
}
}
// Add Functions...
@@ -419,3 +452,4 @@ namespace AZ
} // namespace RPI
} // namespace AZ
@@ -456,11 +456,6 @@ namespace AZ
return m_passLibrary.HasPassesForTemplate(templateName);
}
const AZStd::vector<Pass*>& PassSystem::GetPassesForTemplateName(const Name& templateName) const
{
return m_passLibrary.GetPassesForTemplate(templateName);
}
bool PassSystem::AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate)
{
return m_passLibrary.AddPassTemplate(name, passTemplate);
@@ -487,10 +482,21 @@ namespace AZ
RemovePassFromLibrary(pass);
--m_passCounter;
}
AZStd::vector<Pass*> PassSystem::FindPasses(const PassFilter& passFilter) const
void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction)
{
return m_passLibrary.FindPasses(passFilter);
return m_passLibrary.ForEachPass(filter, passFunction);
}
Pass* PassSystem::FindFirstPass(const PassFilter& filter)
{
Pass* foundPass = nullptr;
m_passLibrary.ForEachPass(filter, [&foundPass](RPI::Pass* pass) ->PassFilterExecutionFlow
{
foundPass = pass;
return PassFilterExecutionFlow::StopVisitingPasses;
});
return foundPass;
}
SwapChainPass* PassSystem::FindSwapChainPass(AzFramework::NativeWindowHandle windowHandle) const
@@ -244,7 +244,7 @@ namespace AZ
m_needsShaderLoad = false;
// Load Shader
const char* ShaderPath = "shader/imagepreview.azshader";
const char* ShaderPath = "shaders/imagepreview.azshader";
Data::Asset<ShaderAsset> shaderAsset = RPI::FindShaderAsset(ShaderPath);
m_shader = Shader::FindOrCreate(shaderAsset);
if (m_shader == nullptr)
+137 -22
View File
@@ -19,6 +19,8 @@
#include <Atom/RPI.Public/Pass/PassSystem.h>
#include <Atom/RPI.Public/Pass/RasterPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <Common/RPITestFixture.h>
@@ -573,7 +575,7 @@ namespace UnitTest
EXPECT_TRUE(pass != nullptr);
}
TEST_F(PassTests, PassHierarchyFilter)
TEST_F(PassTests, PassFilter_PassHierarchy)
{
m_data->AddPassTemplatesToLibrary();
@@ -587,62 +589,55 @@ namespace UnitTest
parent2->AsParent()->AddChild(parent1);
parent1->AsParent()->AddChild(pass);
{
// Filter with only pass name
PassHierarchyFilter filter(Name("pass1"));
EXPECT_TRUE(filter.Matches(pass.get()));
}
{
// Filter with pass hierarchy which has only one element
PassHierarchyFilter filter({ Name("pass1") });
PassFilter filter = PassFilter::CreateWithPassHierarchy({Name("pass1")});
EXPECT_TRUE(filter.Matches(pass.get()));
}
{
// Filter with empty pass hierarchy. Result one assert
// Filter with empty pass hierarchy, triggers one assert
AZ_TEST_START_TRACE_SUPPRESSION;
PassHierarchyFilter filter(AZStd::vector<Name>{});
PassFilter filter = PassFilter::CreateWithPassHierarchy(AZStd::vector<Name>{});
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(filter.Matches(pass.get()));
}
{
// Filters with partial hierarchy by using string vector
AZStd::vector<AZStd::string> passHierarchy1 = { "parent1", "pass1" };
PassHierarchyFilter filter1(passHierarchy1);
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1);
EXPECT_TRUE(filter1.Matches(pass.get()));
AZStd::vector<AZStd::string> passHierarchy2 = { "parent2", "pass1" };
PassHierarchyFilter filter2(passHierarchy2);
PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2);
EXPECT_TRUE(filter2.Matches(pass.get()));
AZStd::vector<AZStd::string> passHierarchy3 = { "parent3", "parent2", "pass1" };
PassHierarchyFilter filter3(passHierarchy3);
PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3);
EXPECT_TRUE(filter3.Matches(pass.get()));
}
{
// Filters with partial hierarchy by using Name vector
AZStd::vector<Name> passHierarchy1 = { Name("parent1"), Name("pass1") };
PassHierarchyFilter filter1(passHierarchy1);
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1);
EXPECT_TRUE(filter1.Matches(pass.get()));
AZStd::vector<Name> passHierarchy2 = { Name("parent2"), Name("pass1")};
PassHierarchyFilter filter2(passHierarchy2);
PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2);
EXPECT_TRUE(filter2.Matches(pass.get()));
AZStd::vector<Name> passHierarchy3 = { Name("parent3"), Name("parent2"), Name("pass1") };
PassHierarchyFilter filter3(passHierarchy3);
PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3);
EXPECT_TRUE(filter3.Matches(pass.get()));
}
{
// Find non-leaf pass
PassHierarchyFilter filter1(AZStd::vector<AZStd::string>{"parent3", "parent1"});
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector<AZStd::string>{"parent3", "parent1"});
EXPECT_TRUE(filter1.Matches(parent1.get()));
PassHierarchyFilter filter2(Name("parent1"));
PassFilter filter2 = PassFilter::CreateWithPassHierarchy({ Name("parent1") });
EXPECT_TRUE(filter2.Matches(parent1.get()));
EXPECT_FALSE(filter2.Matches(pass.get()));
}
@@ -650,11 +645,131 @@ namespace UnitTest
{
// Failed to find pass
// Mis-matching hierarchy
PassHierarchyFilter filter1(AZStd::vector<AZStd::string>{"Parent1", "Parent3", "pass1"});
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector<AZStd::string>{"Parent1", "Parent3", "pass1"});
EXPECT_FALSE(filter1.Matches(pass.get()));
// Mis-matching name
PassHierarchyFilter filter2(AZStd::vector<AZStd::string>{"Parent1", "pass1"});
PassFilter filter2 = PassFilter::CreateWithPassHierarchy(AZStd::vector<AZStd::string>{"Parent1", "pass1"});
EXPECT_FALSE(filter2.Matches(parent1.get()));
}
}
TEST_F(PassTests, PassFilter_Empty_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
Ptr<Pass> parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2"));
Ptr<Pass> parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3"));
parent3->AsParent()->AddChild(parent2);
parent2->AsParent()->AddChild(parent1);
parent1->AsParent()->AddChild(pass);
PassFilter filter;
// Any pass can match an empty filter
EXPECT_TRUE(filter.Matches(pass.get()));
EXPECT_TRUE(filter.Matches(parent1.get()));
EXPECT_TRUE(filter.Matches(parent2.get()));
EXPECT_TRUE(filter.Matches(parent3.get()));
}
TEST_F(PassTests, PassFilter_PassClass_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> depthPass = m_passSystem->CreatePassFromTemplate(Name("DepthPrePass"), Name("depthPass"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
parent1->AsParent()->AddChild(pass);
parent1->AsParent()->AddChild(depthPass);
PassFilter filter1 = PassFilter::CreateWithPassClass<Pass>();
EXPECT_TRUE(filter1.Matches(pass.get()));
EXPECT_FALSE(filter1.Matches(parent1.get()));
PassFilter filter2 = PassFilter::CreateWithPassClass<ParentPass>();
EXPECT_FALSE(filter2.Matches(pass.get()));
EXPECT_TRUE(filter2.Matches(parent1.get()));
}
TEST_F(PassTests, PassFilter_PassTemplate_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> childPass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
PassFilter filter1 = PassFilter::CreateWithTemplateName(Name("Pass"), (Scene*) nullptr);
// childPass doesn't have a template
EXPECT_FALSE(filter1.Matches(childPass.get()));
PassFilter filter2 = PassFilter::CreateWithTemplateName(Name("ParentPass"), (Scene*) nullptr);
EXPECT_TRUE(filter2.Matches(parent1.get()));
}
TEST_F(PassTests, ForEachPass_PassTemplateFilter_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
Ptr<Pass> parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2"));
Ptr<Pass> parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3"));
parent3->AsParent()->AddChild(parent2);
parent2->AsParent()->AddChild(parent1);
parent1->AsParent()->AddChild(pass);
// Create render pipeline
const RPI::PipelineViewTag viewTag{ "viewTag1" };
RPI::RenderPipelineDescriptor desc;
desc.m_mainViewTagName = viewTag.GetStringView();
desc.m_name = "TestPipeline";
RPI::RenderPipelinePtr pipeline = RPI::RenderPipeline::CreateRenderPipeline(desc);
Ptr<Pass> parent4 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent4"));
pipeline->GetRootPass()->AddChild(parent4);
Name templateName = Name("ParentPass");
PassFilter filter1 = PassFilter::CreateWithTemplateName(templateName, (RenderPipeline*)nullptr);
int count = 0;
m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow
{
EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName);
count++;
return PassFilterExecutionFlow::ContinueVisitingPasses;
});
// three from CreatePassFromTemplate() calls and one from Render Pipeline.
EXPECT_TRUE(count == 4);
count = 0;
m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow
{
EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName);
count++;
return PassFilterExecutionFlow::StopVisitingPasses;
});
EXPECT_TRUE(count == 1);
PassFilter filter2 = PassFilter::CreateWithTemplateName(templateName, pipeline.get());
count = 0;
m_passSystem->ForEachPass(filter2, [&count]([[maybe_unused]] RPI::Pass* pass) -> PassFilterExecutionFlow
{
count++;
return PassFilterExecutionFlow::ContinueVisitingPasses;
});
// only the ParentPass in the render pipeline was found
EXPECT_TRUE(count == 1);
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
"Atom": {
"RPI": {
"Initialization": {
"CommonSrgsShaderAssetPath": "shader/sceneandviewsrgs.azshader",
"CommonSrgsShaderAssetPath": "shaders/sceneandviewsrgs.azshader",
"ImageSystemDescriptor": {
"AssetStreamingImagePoolSize": 2147483648, // 2 * 1024 * 1024 * 1024
"SystemStreamingImagePoolSize": 134217728, // 128 * 1024 * 1024
@@ -581,7 +581,7 @@
{
"id": {
"materialAssetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
@@ -593,7 +593,7 @@
"id": {
"lodIndex": 0,
"materialAssetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
@@ -608,10 +608,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"subId": 277333723
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 277889906
},
"assetHint": "objects/groudplane/groundplane_521x521m.azmodel"
"assetHint": "objects/groudplane/groundplane_512x512m.azmodel"
}
}
}
@@ -836,7 +836,7 @@
</Class>
<Class name="AZ::Render::MeshComponentController" field="Controller" type="{D0F35FAC-4194-4C89-9487-D000DDB8B272}">
<Class name="AZ::Render::MeshComponentConfig" field="Configuration" version="1" type="{63737345-51B1-472B-9355-98F99993909B}">
<Class name="Asset" field="ModelAsset" value="id={935F694A-8639-515B-8133-81CDC7948E5B}:1087c6db,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/groudplane/groundplane_521x521m.azmodel},loadBehavior=1" version="2" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
<Class name="Asset" field="ModelAsset" value="id={0CD745C0-6AA8-569A-A68A-73A3270986C4}:10904372,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/groudplane/groundplane_512x512m.azmodel},loadBehavior=1" version="2" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
<Class name="AZ::s64" field="SortKey" value="0" type="{70D8A282-A1EA-462D-9D04-51EDE81FAC2F}"/>
<Class name="unsigned char" field="LodOverride" value="255" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="bool" field="ExcludeFromReflectionCubeMaps" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
@@ -19,6 +19,7 @@
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@@ -142,12 +143,13 @@ namespace AZ
EnablePasses(true);
}
void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable)
void HairFeatureProcessor::EnablePasses(bool enable)
{
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName);
if (desiredPass)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene());
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (pass)
{
desiredPass->SetEnabled(enable);
pass->SetEnabled(enable);
}
}
@@ -309,10 +311,17 @@ namespace AZ
m_forceClearRenderData = true;
}
bool HairFeatureProcessor::HasHairParentPass()
{
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene());
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
return pass;
}
void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
if (!renderPipeline.get()->GetRootPass()->FindPassByNameRecursive(HairParentPassName))
if (!HasHairParentPass())
{
return;
}
@@ -323,10 +332,10 @@ namespace AZ
m_forceRebuildRenderData = true;
}
void HairFeatureProcessor::OnRenderPipelineRemoved(RPI::RenderPipeline* renderPipeline)
void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName))
if (!HasHairParentPass())
{
return;
}
@@ -338,7 +347,7 @@ namespace AZ
void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName))
if (!HasHairParentPass())
{
return;
}
@@ -457,7 +466,8 @@ namespace AZ
{
m_computePasses[passName] = nullptr;
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(passName, m_renderPipeline);
RPI::Ptr<RPI::Pass> desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (desiredPass)
{
m_computePasses[passName] = static_cast<HairSkinningComputePass*>(desiredPass.get());
@@ -478,8 +488,9 @@ namespace AZ
bool HairFeatureProcessor::InitPPLLFillPass()
{
m_hairPPLLRasterPass = nullptr; // reset it to null, just in case it fails to load the assets properly
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLRasterPassName, m_renderPipeline);
RPI::Ptr<RPI::Pass> desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (desiredPass)
{
m_hairPPLLRasterPass = static_cast<HairPPLLRasterPass*>(desiredPass.get());
@@ -497,7 +508,8 @@ namespace AZ
{
m_hairPPLLResolvePass = nullptr; // reset it to null, just in case it fails to load the assets properly
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLResolvePassName, m_renderPipeline);
RPI::Ptr<RPI::Pass> desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (desiredPass)
{
m_hairPPLLResolvePass = static_cast<HairPPLLResolvePass*>(desiredPass.get());
@@ -518,8 +530,8 @@ namespace AZ
m_hairShortCutGeometryDepthAlphaPass = nullptr;
m_hairShortCutGeometryShadingPass = nullptr;
m_hairShortCutGeometryDepthAlphaPass = static_cast<HairShortCutGeometryDepthAlphaPass*>(
m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get());
RPI::PassFilter depthAlphaPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryDepthAlphaPassName, m_renderPipeline);
m_hairShortCutGeometryDepthAlphaPass = static_cast<HairShortCutGeometryDepthAlphaPass*>(RPI::PassSystemInterface::Get()->FindFirstPass(depthAlphaPassFilter));
if (m_hairShortCutGeometryDepthAlphaPass)
{
m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this);
@@ -530,8 +542,8 @@ namespace AZ
return false;
}
m_hairShortCutGeometryShadingPass = static_cast<HairShortCutGeometryShadingPass*>(
m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get());
RPI::PassFilter shaderingPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryShadingPassName, m_renderPipeline);
m_hairShortCutGeometryShadingPass = static_cast<HairShortCutGeometryShadingPass*>(RPI::PassSystemInterface::Get()->FindFirstPass(shaderingPassFilter));
if (m_hairShortCutGeometryShadingPass)
{
m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this);
@@ -165,6 +165,8 @@ namespace AZ
void EnablePasses(bool enable);
bool HasHairParentPass();
//! The following will serve to register the FP in the Thumbnail system
AZStd::vector<AZStd::string> m_hairFeatureProcessorRegistryName;
@@ -1,8 +0,0 @@
{
"description": "",
"materialType": "TerrainMacroMaterial.materialtype",
"parentMaterial": "",
"propertyLayoutVersion": 1,
"properties": {
}
}
@@ -1,63 +0,0 @@
{
"description": "A material for providing terrain with low-fidelity color and normals. This material will get blended with surface detail materials.",
"version": 1,
"propertyLayout": {
"groups": [
{
"name": "baseColor",
"displayName": "Base Color",
"description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals."
},
{
"name": "normal",
"displayName": "Normal",
"description": "Properties related to configuring surface normal."
}
],
"properties": {
"baseColor": [
{
"name": "textureMap",
"displayName": "Texture",
"description": "Base color of the macro material",
"type": "Image"
}
],
"normal": [
{
"name": "textureMap",
"displayName": "Texture",
"description": "Texture for defining surface normal direction. These will override normals generated from the geometry.",
"type": "Image"
},
{
"name": "flipX",
"displayName": "Flip X Channel",
"description": "Flip tangent direction for this normal map.",
"type": "Bool",
"defaultValue": false
},
{
"name": "flipY",
"displayName": "Flip Y Channel",
"description": "Flip bitangent direction for this normal map.",
"type": "Bool",
"defaultValue": false
},
{
"name": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the values",
"type": "Float",
"defaultValue": 1.0,
"min": 0.0,
"softMax": 2.0
}
]
}
},
"shaders": [
],
"functors": [
]
}
@@ -16,87 +16,65 @@
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
namespace Terrain
{
AZ::Data::AssetId TerrainMacroMaterialConfig::s_macroMaterialTypeAssetId{};
bool TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly() const
{
return !m_macroNormalAsset.GetId().IsValid();
}
void TerrainMacroMaterialConfig::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
if (auto* serialize = azrtti_cast<AZ::SerializeContext*>(context); serialize)
{
serialize->Class<TerrainMacroMaterialConfig, AZ::ComponentConfig>()
->Version(1)
->Field("MacroMaterial", &TerrainMacroMaterialConfig::m_materialAsset)
;
->Field("MacroColor", &TerrainMacroMaterialConfig::m_macroColorAsset)
->Field("MacroNormal", &TerrainMacroMaterialConfig::m_macroNormalAsset)
->Field("NormalFlipX", &TerrainMacroMaterialConfig::m_normalFlipX)
->Field("NormalFlipY", &TerrainMacroMaterialConfig::m_normalFlipY)
->Field("NormalFactor", &TerrainMacroMaterialConfig::m_normalFactor)
;
// The edit context for this appears in EditorTerrainMacroMaterialComponent.cpp.
}
}
AZ::Data::AssetId TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId()
{
// Get the Asset ID for the TerrainMacroMaterial material type and store it in a class static so that we don't have to look it
// up again.
if (!s_macroMaterialTypeAssetId.IsValid())
{
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
s_macroMaterialTypeAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, TerrainMacroMaterialTypeAsset,
azrtti_typeid<AZ::RPI::MaterialTypeAsset>(), false);
AZ_Assert(s_macroMaterialTypeAssetId.IsValid(), "The asset '%s' couldn't be found.", TerrainMacroMaterialTypeAsset);
}
return s_macroMaterialTypeAssetId;
}
bool TerrainMacroMaterialConfig::IsMaterialTypeCorrect(const AZ::Data::AssetId& assetId)
{
// We'll verify that whatever material we try to load has this material type as a dependency, as a way to implicitly detect
// that we're only trying to use terrain macro materials even before we load the asset.
auto macroMaterialTypeAssetId = GetTerrainMacroMaterialTypeAssetId();
// Get the dependencies for the requested asset.
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> result;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
result, &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, assetId);
// If any of the dependencies match the TerrainMacroMaterial materialtype asset, then this should be the correct type of material.
if (result)
{
for (auto& dependency : result.GetValue())
if (auto* editContext = serialize->GetEditContext(); editContext)
{
if (dependency.m_assetId == macroMaterialTypeAssetId)
{
return true;
}
editContext
->Class<TerrainMacroMaterialConfig>(
"Terrain Macro Material Component", "Provide a terrain macro material for a region of the world")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_macroColorAsset, "Color Texture",
"Terrain macro color texture for use by any terrain inside the bounding box on this entity.")
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_macroNormalAsset, "Normal Texture",
"Texture for defining surface normal direction. These will override normals generated from the geometry.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_normalFlipX, "Normal Flip X",
"Flip tangent direction for this normal map.")
->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly)
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_normalFlipY, "Normal Flip Y",
"Flip bitangent direction for this normal map.")
->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly)
->DataElement(
AZ::Edit::UIHandlers::Slider, &TerrainMacroMaterialConfig::m_normalFactor, "Normal Factor",
"Strength factor for scaling the normal map values.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Max, 10.0f)
->Attribute(AZ::Edit::Attributes::SoftMin, 0.0f)
->Attribute(AZ::Edit::Attributes::SoftMax, 2.0f)
->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly)
;
}
}
// Didn't have the expected dependency, so it must not be the right material type.
return false;
}
AZ::Outcome<void, AZStd::string> TerrainMacroMaterialConfig::ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType)
{
if (azrtti_typeid<AZ::Data::Asset<AZ::RPI::MaterialAsset>>() != valueType)
{
AZ_Assert(false, "Unexpected value type");
return AZ::Failure(AZStd::string("Unexpectedly received something other than a material asset for the MacroMaterial!"));
}
auto newMaterialAsset = *static_cast<AZ::Data::Asset<AZ::RPI::MaterialAsset>*>(newValue);
if (!IsMaterialTypeCorrect(newMaterialAsset.GetId()))
{
return AZ::Failure(AZStd::string::format(
"The selected MacroMaterial ('%s') needs to use the TerrainMacroMaterial material type.",
newMaterialAsset.GetHint().c_str()));
}
return AZ::Success();
}
void TerrainMacroMaterialComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC_CE("TerrainMacroMaterialProviderService"));
@@ -133,25 +111,29 @@ namespace Terrain
void TerrainMacroMaterialComponent::Activate()
{
// Clear out our shape bounds and make sure the material is queued to load.
// Clear out our shape bounds and make sure the texture assets are queued to load.
m_cachedShapeBounds = AZ::Aabb::CreateNull();
m_configuration.m_materialAsset.QueueLoad();
m_configuration.m_macroColorAsset.QueueLoad();
m_configuration.m_macroNormalAsset.QueueLoad();
// Don't mark our material as active until it's finished loading and is valid.
m_macroMaterialActive = false;
// Listen for the material asset to complete loading.
AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_materialAsset.GetId());
// Listen for the texture assets to complete loading.
AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_macroColorAsset.GetId());
AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_macroNormalAsset.GetId());
}
void TerrainMacroMaterialComponent::Deactivate()
{
TerrainMacroMaterialRequestBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
m_configuration.m_materialAsset.Release();
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
m_configuration.m_macroColorAsset.Release();
m_configuration.m_macroNormalAsset.Release();
m_macroMaterialInstance.reset();
m_colorImage.reset();
m_normalImage.reset();
// Send out any notifications as appropriate based on the macro material destruction.
HandleMaterialStateChange();
@@ -195,12 +177,18 @@ namespace Terrain
void TerrainMacroMaterialComponent::HandleMaterialStateChange()
{
// We only want our component to appear active during the time that the macro material is loaded and valid. The logic below
// We only want our component to appear active during the time that the macro material is fully loaded and valid. The logic below
// will handle all transition possibilities to notify if we've become active, inactive, or just changed. We'll also only
// keep a valid up-to-date copy of the shape bounds while the material is valid, since we don't need it any other time.
// Color and normal data is considered ready if it's finished loading or if we don't have a texture specified
bool colorReady = m_colorImage || (!m_configuration.m_macroColorAsset.GetId().IsValid());
bool normalReady = m_normalImage || (!m_configuration.m_macroNormalAsset.GetId().IsValid());
// If we don't have color or normal data, then we don't have *any* useful data, so don't activate the macro material.
bool hasAnyData = m_configuration.m_macroColorAsset.GetId().IsValid() || m_configuration.m_macroNormalAsset.GetId().IsValid();
bool wasPreviouslyActive = m_macroMaterialActive;
bool isNowActive = (m_macroMaterialInstance != nullptr);
bool isNowActive = colorReady && normalReady && hasAnyData;
// Set our state to active or inactive, based on whether or not the macro material instance is now valid.
m_macroMaterialActive = isNowActive;
@@ -226,9 +214,10 @@ namespace Terrain
// Start listening for shape changes.
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId());
MacroMaterialData material = GetTerrainMacroMaterialData();
TerrainMacroMaterialNotificationBus::Broadcast(
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), m_macroMaterialInstance,
m_cachedShapeBounds);
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), material);
}
else if (wasPreviouslyActive && !isNowActive)
{
@@ -246,30 +235,35 @@ namespace Terrain
else
{
// We were active both before and after, so just send out a material changed event.
MacroMaterialData material = GetTerrainMacroMaterialData();
TerrainMacroMaterialNotificationBus::Broadcast(
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), m_macroMaterialInstance);
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), material);
}
}
void TerrainMacroMaterialComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
m_configuration.m_materialAsset = asset;
if (m_configuration.m_materialAsset.Get()->GetMaterialTypeAsset().GetId() ==
TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId())
if (asset.GetId() == m_configuration.m_macroColorAsset.GetId())
{
m_macroMaterialInstance = AZ::RPI::Material::FindOrCreate(m_configuration.m_materialAsset);
m_configuration.m_macroColorAsset = asset;
m_colorImage = AZ::RPI::StreamingImage::FindOrCreate(m_configuration.m_macroColorAsset);
// Clear the texture asset reference to make sure we don't prevent hot-reloading.
m_configuration.m_macroColorAsset.Release();
}
else if (asset.GetId() == m_configuration.m_macroNormalAsset.GetId())
{
m_configuration.m_macroNormalAsset = asset;
m_normalImage = AZ::RPI::StreamingImage::FindOrCreate(m_configuration.m_macroNormalAsset);
// Clear the texture asset reference to make sure we don't prevent hot-reloading.
m_configuration.m_macroColorAsset.Release();
}
else
{
AZ_Error("Terrain", false, "Material '%s' has the wrong material type.", m_configuration.m_materialAsset.GetHint().c_str());
m_macroMaterialInstance.reset();
}
// Clear the material asset reference to make sure we don't prevent hot-reloading.
m_configuration.m_materialAsset.Release();
HandleMaterialStateChange();
}
@@ -278,10 +272,18 @@ namespace Terrain
OnAssetReady(asset);
}
void TerrainMacroMaterialComponent::GetTerrainMacroMaterialData(
AZ::Data::Instance<AZ::RPI::Material>& macroMaterial, AZ::Aabb& macroMaterialRegion)
MacroMaterialData TerrainMacroMaterialComponent::GetTerrainMacroMaterialData()
{
macroMaterial = m_macroMaterialInstance;
macroMaterialRegion = m_cachedShapeBounds;
MacroMaterialData macroMaterial;
macroMaterial.m_entityId = GetEntityId();
macroMaterial.m_bounds = m_cachedShapeBounds;
macroMaterial.m_colorImage = m_colorImage;
macroMaterial.m_normalImage = m_normalImage;
macroMaterial.m_normalFactor = m_configuration.m_normalFactor;
macroMaterial.m_normalFlipX = m_configuration.m_normalFlipX;
macroMaterial.m_normalFlipY = m_configuration.m_normalFlipY;
return macroMaterial;
}
}
@@ -11,10 +11,9 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Math/Aabb.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <TerrainRenderer/TerrainMacroMaterialBus.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
namespace LmbrCentral
{
@@ -32,23 +31,20 @@ namespace Terrain
AZ_RTTI(TerrainMacroMaterialConfig, "{9DBAFFF0-FD20-4594-8884-E3266D8CCAC8}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
AZ::Data::Asset<AZ::RPI::MaterialAsset> m_materialAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
static AZ::Data::AssetId GetTerrainMacroMaterialTypeAssetId();
static bool IsMaterialTypeCorrect(const AZ::Data::AssetId&);
AZ::Outcome<void, AZStd::string> ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType);
private:
static inline constexpr const char* TerrainMacroMaterialTypeAsset = "materials/terrain/terrainmacromaterial.azmaterialtype";
static AZ::Data::AssetId s_macroMaterialTypeAssetId;
bool NormalMapAttributesAreReadOnly() const;
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_macroColorAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_macroNormalAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
bool m_normalFlipX = false;
bool m_normalFlipY = false;
float m_normalFactor = 1.0f;
};
class TerrainMacroMaterialComponent
: public AZ::Component
, public TerrainMacroMaterialRequestBus::Handler
, private LmbrCentral::ShapeComponentNotificationsBus::Handler
, private AZ::Data::AssetBus::Handler
, private AZ::Data::AssetBus::MultiHandler
{
public:
template<typename, typename>
@@ -70,7 +66,7 @@ namespace Terrain
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
void GetTerrainMacroMaterialData(AZ::Data::Instance<AZ::RPI::Material>& macroMaterial, AZ::Aabb& macroMaterialRegion) override;
MacroMaterialData GetTerrainMacroMaterialData() override;
private:
////////////////////////////////////////////////////////////////////////
@@ -86,7 +82,8 @@ namespace Terrain
TerrainMacroMaterialConfig m_configuration;
AZ::Aabb m_cachedShapeBounds;
AZ::Data::Instance<AZ::RPI::Material> m_macroMaterialInstance;
bool m_macroMaterialActive{ false };
AZ::Data::Instance<AZ::RPI::Image> m_colorImage;
AZ::Data::Instance<AZ::RPI::Image> m_normalImage;
};
}
@@ -17,36 +17,7 @@ namespace Terrain
{
BaseClassType::ReflectSubClass<EditorTerrainMacroMaterialComponent, BaseClassType>(
context, 1,
&LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType,
typename BaseClassType::WrappedConfigType, 1>
);
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
AZ::EditContext* editContext = serializeContext->GetEditContext();
// The edit context for TerrainMacroMaterialConfig is specified here to make it easier to add custom filtering to the
// asset picker for the material asset so that we can eventually only display materials that inherit from the proper
// material type.
if (editContext)
{
editContext
->Class<TerrainMacroMaterialConfig>(
"Terrain Macro Material Component", "Provide a terrain macro material for a region of the world")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_materialAsset, "Macro Material",
"Terrain macro material for use by any terrain inside the bounding box on this entity.")
// This is disabled until ChangeValidate can support the Asset<T> type. :(
//->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainMacroMaterialConfig::ValidateMaterialAsset)
;
}
}
&LmbrCentral::EditorWrappedComponentBaseVersionConverter<
typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType, 1>);
}
}
@@ -51,13 +51,6 @@ namespace Terrain
{
// Terrain material
static const char* const HeightmapImage("settings.heightmapImage");
// Macro material
static const char* const MacroColorTextureMap("baseColor.textureMap");
static const char* const MacroNormalTextureMap("normal.textureMap");
static const char* const MacroNormalFlipX("normal.flipX");
static const char* const MacroNormalFlipY("normal.flipY");
static const char* const MacroNormalFactor("normal.factor");
}
namespace ShaderInputs
@@ -185,12 +178,11 @@ namespace Terrain
m_areaData.m_heightmapUpdated = true;
}
void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region)
void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& newMaterialData)
{
MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId);
materialData.m_bounds = region;
UpdateMacroMaterialData(materialData, material);
UpdateMacroMaterialData(materialData, newMaterialData);
// Update all sectors in region.
ForOverlappingSectors(materialData.m_bounds,
@@ -203,20 +195,14 @@ namespace Terrain
);
}
void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance macroMaterial)
void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& newMaterialData)
{
if (macroMaterial)
{
MacroMaterialData& data = FindOrCreateMacroMaterial(entityId);
UpdateMacroMaterialData(data, macroMaterial);
}
else
{
RemoveMacroMaterial(entityId);
}
MacroMaterialData& data = FindOrCreateMacroMaterial(entityId);
UpdateMacroMaterialData(data, newMaterialData);
}
void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion)
void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(
AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion)
{
MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId);
for (SectorData& sectorData : m_sectorData)
@@ -269,6 +255,7 @@ namespace Terrain
}
m_areaData.m_macroMaterialsUpdated = true;
RemoveMacroMaterial(entityId);
}
void TerrainFeatureProcessor::UpdateTerrainData()
@@ -382,42 +369,18 @@ namespace Terrain
TerrainMacroMaterialRequestBus::EnumerateHandlers(
[&](TerrainMacroMaterialRequests* handler)
{
MaterialInstance macroMaterial;
AZ::Aabb bounds;
handler->GetTerrainMacroMaterialData(macroMaterial, bounds);
MacroMaterialData macroMaterial = handler->GetTerrainMacroMaterialData();
AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId());
OnTerrainMacroMaterialCreated(entityId, macroMaterial, bounds);
OnTerrainMacroMaterialCreated(entityId, macroMaterial);
return true;
}
);
TerrainMacroMaterialNotificationBus::Handler::BusConnect();
}
void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material)
void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData)
{
// Since we're using an actual macro material instance for now, get the values from it that we care about.
const auto materialLayout = material->GetMaterialPropertiesLayout();
const AZ::RPI::MaterialPropertyIndex macroColorTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroColorTextureMap));
AZ_Error(TerrainFPName, macroColorTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroColorTextureMap);
const AZ::RPI::MaterialPropertyIndex macroNormalTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalTextureMap));
AZ_Error(TerrainFPName, macroNormalTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalTextureMap);
const AZ::RPI::MaterialPropertyIndex macroNormalFlipXIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipX));
AZ_Error(TerrainFPName, macroNormalFlipXIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipX);
const AZ::RPI::MaterialPropertyIndex macroNormalFlipYIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipY));
AZ_Error(TerrainFPName, macroNormalFlipYIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipY);
const AZ::RPI::MaterialPropertyIndex macroNormalFactorIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFactor));
AZ_Error(TerrainFPName, macroNormalFactorIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFactor);
macroMaterialData.m_colorImage = material->GetPropertyValue(macroColorTextureMapIndex).GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
macroMaterialData.m_normalImage = material->GetPropertyValue(macroNormalTextureMapIndex).GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
macroMaterialData.m_normalFlipX = material->GetPropertyValue(macroNormalFlipXIndex).GetValue<bool>();
macroMaterialData.m_normalFlipY = material->GetPropertyValue(macroNormalFlipYIndex).GetValue<bool>();
macroMaterialData.m_normalFactor = material->GetPropertyValue(macroNormalFactorIndex).GetValue<float>();
macroMaterialData = newMaterialData;
if (macroMaterialData.m_bounds.IsValid())
{
@@ -783,7 +746,7 @@ namespace Terrain
// larger but this will limit how much is rendered.
}
TerrainFeatureProcessor::MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId)
MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId)
{
for (MacroMaterialData& data : m_macroMaterials.GetDataVector())
{
@@ -795,7 +758,7 @@ namespace Terrain
return nullptr;
}
TerrainFeatureProcessor::MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId)
MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId)
{
MacroMaterialData* dataPtr = FindMacroMaterial(entityId);
if (dataPtr != nullptr)
@@ -112,18 +112,6 @@ namespace Terrain
AZStd::fixed_vector<uint16_t, MaxMaterialsPerSector> m_macroMaterials;
};
struct MacroMaterialData
{
AZ::EntityId m_entityId;
AZ::Aabb m_bounds = AZ::Aabb::CreateNull();
AZ::Data::Instance<AZ::RPI::Image> m_colorImage;
AZ::Data::Instance<AZ::RPI::Image> m_normalImage;
bool m_normalFlipX{ false };
bool m_normalFlipY{ false };
float m_normalFactor{ 0.0f };
};
// AZ::RPI::MaterialReloadNotificationBus::Handler overrides...
void OnMaterialReinitialized(const MaterialInstance& material) override;
@@ -132,8 +120,8 @@ namespace Terrain
void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override;
// TerrainMacroMaterialNotificationBus overrides...
void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) override;
void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance material) override;
void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& material) override;
void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& material) override;
void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override;
void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override;
@@ -143,7 +131,7 @@ namespace Terrain
void UpdateTerrainData();
void PrepareMaterialData();
void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material);
void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData);
void ProcessSurfaces(const FeatureProcessor::RenderPacket& process);
@@ -12,11 +12,22 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Aabb.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Reflect/Image/Image.h>
namespace Terrain
{
struct MacroMaterialData
{
AZ::EntityId m_entityId;
AZ::Aabb m_bounds = AZ::Aabb::CreateNull();
AZ::Data::Instance<AZ::RPI::Image> m_colorImage;
AZ::Data::Instance<AZ::RPI::Image> m_normalImage;
bool m_normalFlipX{ false };
bool m_normalFlipY{ false };
float m_normalFactor{ 0.0f };
};
/**
* Request terrain macro material data.
*/
@@ -32,7 +43,7 @@ namespace Terrain
virtual ~TerrainMacroMaterialRequests() = default;
// Get the terrain macro material and the region that it covers.
virtual void GetTerrainMacroMaterialData(AZ::Data::Instance<AZ::RPI::Material>& macroMaterial, AZ::Aabb& macroMaterialRegion) = 0;
virtual MacroMaterialData GetTerrainMacroMaterialData() = 0;
};
using TerrainMacroMaterialRequestBus = AZ::EBus<TerrainMacroMaterialRequests>;
@@ -51,14 +62,12 @@ namespace Terrain
virtual void OnTerrainMacroMaterialCreated(
[[maybe_unused]] AZ::EntityId macroMaterialEntity,
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> macroMaterial,
[[maybe_unused]] const AZ::Aabb& macroMaterialRegion)
[[maybe_unused]] const MacroMaterialData& macroMaterial)
{
}
virtual void OnTerrainMacroMaterialChanged(
[[maybe_unused]] AZ::EntityId macroMaterialEntity,
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> macroMaterial)
[[maybe_unused]] AZ::EntityId macroMaterialEntity, [[maybe_unused]] const MacroMaterialData& macroMaterial)
{
}
+2
View File
@@ -9,6 +9,8 @@
ly_install_directory(
DIRECTORIES
AssetGem
CustomTool
PythonGem
DefaultGem
DefaultProject
MinimalProject
@@ -0,0 +1,14 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR})
set(o3de_gem_json ${o3de_gem_path}/gem.json)
o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name")
o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path)
add_subdirectory(Code)
@@ -0,0 +1,14 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
set(FILES
Include/${Name}/${Name}Bus.h
Source/${Name}ModuleInterface.h
Source/${Name}EditorSystemComponent.cpp
Source/${Name}EditorSystemComponent.h
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
set(FILES
Source/${Name}EditorModule.cpp
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
set(FILES
Tests/${Name}EditorTest.cpp
)
@@ -0,0 +1,76 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR}
# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}
# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform
# in which case it will see if that platform is present here or in the restricted folder.
# i.e. It could here in our gem : Gems/${Name}/Code/Platform/<platorm_name> or
# <restricted_folder>/<platform_name>/Gems/${Name}/Code
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name})
# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the
# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem
# is supported by this platform.
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which
# will also depend on ${Name}.Static
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME ${Name}.Editor.Static STATIC
NAMESPACE Gem
FILES_CMAKE
${NameLower}_editor_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PUBLIC
AZ::AzToolsFramework
)
ly_add_target(
NAME ${Name}.Editor GEM_MODULE
NAMESPACE Gem
AUTOMOC
FILES_CMAKE
${NameLower}_editor_shared_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PUBLIC
Gem::${Name}.Editor.Static
)
# By default, we will specify that the above target ${Name} would be used by
# Tool and Builder type targets when this gem is enabled. If you don't want it
# active in Tools or Builders by default, delete one of both of the following lines:
ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor)
ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor)
endif()
################################################################################
# Tests
################################################################################
# See if globally, tests are supported
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
# We globally support tests, see if we support tests on this platform for ${Name}.Static
# If we are a host platform we want to add tools test like editor tests here
if(PAL_TRAIT_BUILD_HOST_TOOLS)
endif()
endif()
@@ -0,0 +1,40 @@
// {BEGIN_LICENSE}
/*
* 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
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
namespace ${SanitizedCppName}
{
class ${SanitizedCppName}Requests
{
public:
AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}");
virtual ~${SanitizedCppName}Requests() = default;
// Put your public methods here
};
class ${SanitizedCppName}BusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>;
using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>;
} // namespace ${SanitizedCppName}
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
# Platform specific files for Linux
# i.e. ../Source/Linux/${Name}Linux.cpp
# ../Source/Linux/${Name}Linux.h
# ../Include/Linux/${Name}Linux.h
set(FILES
)
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
# Platform specific files for Linux
# i.e. ../Source/Linux/${Name}Linux.cpp
# ../Source/Linux/${Name}Linux.h
# ../Include/Linux/${Name}Linux.h
set(FILES
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# 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
#
# {END_LICENSE}
set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE)

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