From e970247fc5b80c8d5bb43e149ee1cbe602d045a7 Mon Sep 17 00:00:00 2001 From: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:35:50 +0200 Subject: [PATCH 01/14] Asset Browser Search View fixes (#4814) * Fixed RowCount method. Signed-off-by: igarri * Fixed Signals Signed-off-by: igarri * Fixed Delegate case Signed-off-by: igarri * Fixed issue when displaying branch icons Signed-off-by: igarri * Fixed AssetBrowser Delegate Signed-off-by: igarri * Removed optimize flags Signed-off-by: igarri * AssetBrowsertableModel cleanup Signed-off-by: igarri * Fixed Typos Signed-off-by: igarri * Fixed Comment Signed-off-by: igarri * Added check for rowCount == 0 Signed-off-by: igarri --- .../AzAssetBrowser/AzAssetBrowserWindow.cpp | 54 +------------------ .../AzAssetBrowser/AzAssetBrowserWindow.h | 1 - .../AssetBrowser/AssetBrowserTableModel.cpp | 42 ++++++--------- .../AssetBrowser/AssetBrowserTableModel.h | 6 ++- .../AssetBrowser/Views/EntryDelegate.cpp | 37 +++++++------ 5 files changed, 43 insertions(+), 97 deletions(-) diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 9fe4cfd0d2..bcaaa02b67 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -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; diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 753f000300..34103316ab 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -68,7 +68,6 @@ protected slots: void CreateSwitchViewMenu(); void SetExpandedAssetBrowserMode(); void SetDefaultAssetBrowserMode(); - void UpdateTableModelAfterFilter(); void SetTableViewVisibleAfterFilter(); private: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index ec709aef8a..14c5e719b4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -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(index.internalPointer()); - } - else + if (!index.isValid()) { AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry."); return nullptr; } + return static_cast(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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index f84e6bd81c..4048156b60 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -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); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index b463381638..8b58791e68 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -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(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); - auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); + auto belowEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); + auto aboveEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); - auto aboveSourceEntry = azrtti_cast(aboveEntry); auto belowSourceEntry = azrtti_cast(belowEntry); + auto aboveSourceEntry = azrtti_cast(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" From 91ca986e2a78f577baec6927d3e69420fc366ae9 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:37:32 -0500 Subject: [PATCH 02/14] Terrain Macro Material component improvements (#4930) * First pass of non-working changes to Terrain Macro Material Component. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Reworked Terrain Macro Material to use properties instead of a material. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed comments. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * PR feedback - disable attributes when no normal map selected Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fix linux compile error - unused variables. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../DefaultTerrainMacroMaterial.material | 8 - .../Terrain/TerrainMacroMaterial.materialtype | 63 ------ .../TerrainMacroMaterialComponent.cpp | 188 +++++++++--------- .../TerrainMacroMaterialComponent.h | 25 +-- .../EditorTerrainMacroMaterialComponent.cpp | 33 +-- .../TerrainFeatureProcessor.cpp | 65 ++---- .../TerrainRenderer/TerrainFeatureProcessor.h | 18 +- .../TerrainRenderer/TerrainMacroMaterialBus.h | 23 ++- 8 files changed, 141 insertions(+), 282 deletions(-) delete mode 100644 Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material delete mode 100644 Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material deleted file mode 100644 index afaf7e1947..0000000000 --- a/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "", - "materialType": "TerrainMacroMaterial.materialtype", - "parentMaterial": "", - "propertyLayoutVersion": 1, - "properties": { - } -} diff --git a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype deleted file mode 100644 index 17769ffb92..0000000000 --- a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype +++ /dev/null @@ -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": [ - ] -} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp index 55653c64fa..0d161b6b2b 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp @@ -16,87 +16,65 @@ #include #include +#include + 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(context); - if (serialize) + if (auto* serialize = azrtti_cast(context); serialize) { serialize->Class() ->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(), 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::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( + "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 TerrainMacroMaterialConfig::ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType) - { - if (azrtti_typeid>() != 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*>(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 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& 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; } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h index b60f6b2a94..cd82316ff6 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h @@ -11,10 +11,9 @@ #include #include #include -#include #include #include - +#include 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 m_materialAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; - - static AZ::Data::AssetId GetTerrainMacroMaterialTypeAssetId(); - static bool IsMaterialTypeCorrect(const AZ::Data::AssetId&); - AZ::Outcome 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 m_macroColorAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; + AZ::Data::Asset 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 @@ -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& 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 m_macroMaterialInstance; bool m_macroMaterialActive{ false }; + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; }; } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp index 07472d1b85..65500af42d 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp @@ -17,36 +17,7 @@ namespace Terrain { BaseClassType::ReflectSubClass( context, 1, - &LmbrCentral::EditorWrappedComponentBaseVersionConverter - ); - - AZ::SerializeContext* serializeContext = azrtti_cast(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( - "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 type. :( - //->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainMacroMaterialConfig::ValidateMaterialAsset) - ; - } - } - + &LmbrCentral::EditorWrappedComponentBaseVersionConverter< + typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType, 1>); } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 8c85e21490..e6aed28897 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -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>(); - macroMaterialData.m_normalImage = material->GetPropertyValue(macroNormalTextureMapIndex).GetValue>(); - macroMaterialData.m_normalFlipX = material->GetPropertyValue(macroNormalFlipXIndex).GetValue(); - macroMaterialData.m_normalFlipY = material->GetPropertyValue(macroNormalFlipYIndex).GetValue(); - macroMaterialData.m_normalFactor = material->GetPropertyValue(macroNormalFactorIndex).GetValue(); + 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) diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index d9f15f2d47..f82fd8ecb0 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -112,18 +112,6 @@ namespace Terrain AZStd::fixed_vector m_macroMaterials; }; - struct MacroMaterialData - { - AZ::EntityId m_entityId; - AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); - - AZ::Data::Instance m_colorImage; - AZ::Data::Instance 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); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h index c0618a7f66..af1e755b32 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h @@ -12,11 +12,22 @@ #include #include #include - -#include +#include namespace Terrain { + struct MacroMaterialData + { + AZ::EntityId m_entityId; + AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); + + AZ::Data::Instance m_colorImage; + AZ::Data::Instance 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& macroMaterial, AZ::Aabb& macroMaterialRegion) = 0; + virtual MacroMaterialData GetTerrainMacroMaterialData() = 0; }; using TerrainMacroMaterialRequestBus = AZ::EBus; @@ -51,14 +62,12 @@ namespace Terrain virtual void OnTerrainMacroMaterialCreated( [[maybe_unused]] AZ::EntityId macroMaterialEntity, - [[maybe_unused]] AZ::Data::Instance 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 macroMaterial) + [[maybe_unused]] AZ::EntityId macroMaterialEntity, [[maybe_unused]] const MacroMaterialData& macroMaterial) { } From 079e684b77940f7e1bde7030512517e0b7f41bb8 Mon Sep 17 00:00:00 2001 From: LesaelR <89800757+LesaelR@users.noreply.github.com> Date: Mon, 25 Oct 2021 10:05:07 -0700 Subject: [PATCH 03/14] Adding Shaderball test and asset files. (#4743) Signed-off-by: Rosario Cox --- .../_dev_shaderball_00_basecolor.png | 3 + .../assets/ShaderBall/shaderball.dbgsg | 3111 +++++++++++++++++ .../assets/ShaderBall/shaderball.fbx | 3 + .../assetpipeline/fbx_tests/fbx_tests.py | 209 +- 4 files changed, 3235 insertions(+), 91 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png create mode 100644 AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg create mode 100644 AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png new file mode 100644 index 0000000000..415ca3e521 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93a7e033d9fb0fcac221647322bde03716643d789390f79078c4fcc37ecfd005 +size 68327 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg new file mode 100644 index 0000000000..7d070fcea0 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg @@ -0,0 +1,3111 @@ +ProductName: shaderball.dbgsg +debugSceneGraphVersion: 1 +shaderball +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: ShaderBall_1m +Node Path: RootNode.ShaderBall_1m +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: transform +Node Path: RootNode.ShaderBall_1m.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerPortion +Node Path: RootNode.ShaderBall_1m.InnerPortion +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: MaterialBase +Node Path: RootNode.ShaderBall_1m.MaterialBase +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InlayRings +Node Path: RootNode.ShaderBall_1m.InlayRings +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: MainSphere +Node Path: RootNode.ShaderBall_1m.MainSphere +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: HubCap_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1 +Node Type: MeshData + Positions: Count 20476. Hash: 5411361036988924549 + Normals: Count 20476. Hash: 5915154682063029054 + FaceList: Count 8676. Hash: 9142863582186575896 + FaceMaterialIds: Count 8676. Hash: 723360536895379791 + +Node Name: HubCap_2 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: HubCap_1_optimized +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized +Node Type: MeshData + Positions: Count 4724. Hash: 448283466401665158 + Normals: Count 4724. Hash: 345267294337234954 + FaceList: Count 8676. Hash: 11155608373229651496 + FaceMaterialIds: Count 8676. Hash: 723360536895379791 + +Node Name: InnerSphere_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1 +Node Type: MeshData + Positions: Count 19800. Hash: 17342106809405761922 + Normals: Count 19800. Hash: 602384960091561079 + FaceList: Count 9800. Hash: 15975352410309879244 + FaceMaterialIds: Count 9800. Hash: 2219364576630417284 + +Node Name: InnerSphere_2 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerSphere_1_optimized +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized +Node Type: MeshData + Positions: Count 5846. Hash: 18229431498904963984 + Normals: Count 5846. Hash: 12980164457801827192 + FaceList: Count 9800. Hash: 2914108932212582430 + FaceMaterialIds: Count 9800. Hash: 2219364576630417284 + +Node Name: InnerCone_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1 +Node Type: MeshData + Positions: Count 192. Hash: 3022774266117638288 + Normals: Count 192. Hash: 12863565187316537150 + FaceList: Count 64. Hash: 1255131899577053537 + FaceMaterialIds: Count 64. Hash: 6312841653578246165 + +Node Name: InnerCone_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerCone_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized +Node Type: MeshData + Positions: Count 65. Hash: 832325255726840940 + Normals: Count 65. Hash: 14121159448916141450 + FaceList: Count 64. Hash: 9660474080562375138 + FaceMaterialIds: Count 64. Hash: 6312841653578246165 + +Node Name: InnerPost_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1 +Node Type: MeshData + Positions: Count 4864. Hash: 7530018531436061848 + Normals: Count 4864. Hash: 534420091635424803 + FaceList: Count 2432. Hash: 4330575616942580147 + FaceMaterialIds: Count 2432. Hash: 12393250111858627709 + +Node Name: InnerPost_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerPost_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized +Node Type: MeshData + Positions: Count 1300. Hash: 15277132209442728123 + Normals: Count 1300. Hash: 1247055552073340932 + FaceList: Count 2432. Hash: 16808521883586752319 + FaceMaterialIds: Count 2432. Hash: 12393250111858627709 + +Node Name: InnerBaseCuff_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1 +Node Type: MeshData + Positions: Count 4096. Hash: 1814004361979755447 + Normals: Count 4096. Hash: 1760582409511017750 + FaceList: Count 2048. Hash: 6642755656211284824 + FaceMaterialIds: Count 2048. Hash: 2795877824940392899 + +Node Name: InnerBaseCuff_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerBaseCuff_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized +Node Type: MeshData + Positions: Count 1122. Hash: 15009434720129565864 + Normals: Count 1122. Hash: 7245598986723209052 + FaceList: Count 2048. Hash: 2887888015166354330 + FaceMaterialIds: Count 2048. Hash: 2795877824940392899 + +Node Name: Inset_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1 +Node Type: MeshData + Positions: Count 1536. Hash: 6247721054948161820 + Normals: Count 1536. Hash: 16362051041722971623 + FaceList: Count 768. Hash: 3142075668387852387 + FaceMaterialIds: Count 768. Hash: 2979441869813298271 + +Node Name: Inset_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Inset_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized +Node Type: MeshData + Positions: Count 448. Hash: 9520190263881589378 + Normals: Count 448. Hash: 8658432920439039136 + FaceList: Count 768. Hash: 8621713142583851338 + FaceMaterialIds: Count 768. Hash: 2979441869813298271 + +Node Name: BottomCap_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1 +Node Type: MeshData + Positions: Count 960. Hash: 13502046855286963834 + Normals: Count 960. Hash: 7165134632415312413 + FaceList: Count 448. Hash: 11458684524699877690 + FaceMaterialIds: Count 448. Hash: 15444662993354423801 + +Node Name: BottomCap_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: BottomCap_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized +Node Type: MeshData + Positions: Count 257. Hash: 6437758550313952689 + Normals: Count 257. Hash: 9382966961287919705 + FaceList: Count 448. Hash: 16093592375171717669 + FaceMaterialIds: Count 448. Hash: 15444662993354423801 + +Node Name: InnerCushion_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1 +Node Type: MeshData + Positions: Count 54208. Hash: 17548679297809443982 + Normals: Count 54208. Hash: 18279651495381460361 + FaceList: Count 27104. Hash: 12714006452420198903 + FaceMaterialIds: Count 27104. Hash: 12674758055018775830 + +Node Name: InnerCushion_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerCushion_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized +Node Type: MeshData + Positions: Count 13860. Hash: 15965614532178328362 + Normals: Count 13860. Hash: 7297376140184766040 + FaceList: Count 27104. Hash: 2523751700832022798 + FaceMaterialIds: Count 27104. Hash: 12674758055018775830 + +Node Name: OuterBaseCuff_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1 +Node Type: MeshData + Positions: Count 20048. Hash: 3861768905934340307 + Normals: Count 20048. Hash: 12292053423401502106 + FaceList: Count 10024. Hash: 4821138438569111218 + FaceMaterialIds: Count 10024. Hash: 9849297365743356453 + +Node Name: OuterBaseCuff_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: OuterBaseCuff_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized +Node Type: MeshData + Positions: Count 5206. Hash: 5201932106801476439 + Normals: Count 5206. Hash: 15696187265416627056 + FaceList: Count 10024. Hash: 10907381836011223214 + FaceMaterialIds: Count 10024. Hash: 9849297365743356453 + +Node Name: RingLeft_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1 +Node Type: MeshData + Positions: Count 2560. Hash: 10944366330558725569 + Normals: Count 2560. Hash: 11471590896496428199 + FaceList: Count 1280. Hash: 2548580276766813978 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RingLeft_2 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: RingLeft_1_optimized +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized +Node Type: MeshData + Positions: Count 720. Hash: 14374869925777029719 + Normals: Count 720. Hash: 2252820527750115179 + FaceList: Count 1280. Hash: 17583744445000895264 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RingRight_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1 +Node Type: MeshData + Positions: Count 2560. Hash: 17639843025153488175 + Normals: Count 2560. Hash: 8564843488923790338 + FaceList: Count 1280. Hash: 2548580276766813978 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RingRight_2 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: RingRight_1_optimized +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized +Node Type: MeshData + Positions: Count 720. Hash: 17033597990859129189 + Normals: Count 720. Hash: 14263134585377771060 + FaceList: Count 1280. Hash: 13824345071081010014 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RightHub_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1 +Node Type: MeshData + Positions: Count 6304. Hash: 11021385291123971383 + Normals: Count 6304. Hash: 10267084099841111837 + FaceList: Count 3152. Hash: 17728162485525521181 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: RightHub_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: RightHub_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized +Node Type: MeshData + Positions: Count 1617. Hash: 9820946434895369083 + Normals: Count 1617. Hash: 16951123277321747448 + FaceList: Count 3152. Hash: 15437295155178226041 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: LeftHub_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1 +Node Type: MeshData + Positions: Count 6304. Hash: 7564731352768958355 + Normals: Count 6304. Hash: 8133714814490222392 + FaceList: Count 3152. Hash: 17728162485525521181 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: LeftHub_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: LeftHub_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized +Node Type: MeshData + Positions: Count 1617. Hash: 8811470420139443913 + Normals: Count 1617. Hash: 11224729257132823435 + FaceList: Count 3152. Hash: 16696390019846851737 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: Inside_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1 +Node Type: MeshData + Positions: Count 3520. Hash: 13476777876937219698 + Normals: Count 3520. Hash: 10561277746451021236 + FaceList: Count 1760. Hash: 1345243954764462275 + FaceMaterialIds: Count 1760. Hash: 3100204266221257056 + +Node Name: Inside_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Inside_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized +Node Type: MeshData + Positions: Count 960. Hash: 1074011803485396393 + Normals: Count 960. Hash: 16318911598614464642 + FaceList: Count 1760. Hash: 3382287220404120115 + FaceMaterialIds: Count 1760. Hash: 3100204266221257056 + +Node Name: MainOuterSphere_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1 +Node Type: MeshData + Positions: Count 18912. Hash: 15956257973657753552 + Normals: Count 18912. Hash: 9328348641512406334 + FaceList: Count 9456. Hash: 9836933646038198686 + FaceMaterialIds: Count 9456. Hash: 13982281543095132650 + +Node Name: MainOuterSphere_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: MainOuterSphere_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized +Node Type: MeshData + Positions: Count 4891. Hash: 9579089515723090907 + Normals: Count 4891. Hash: 10089610259011330329 + FaceList: Count 9456. Hash: 13541126452398082145 + FaceMaterialIds: Count 9456. Hash: 13982281543095132650 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 10688945422788452939 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 1387390223232454000 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 20476. Hash: 4424021631256816544 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 20476. Hash: 9768152532901400557 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 20476. Hash: 4890305528292926235 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 20476. Hash: 309820643999247955 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 10688945422788452939 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 1387390223232454000 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 4724. Hash: 10265340188340999982 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4724. Hash: 6393506322209434620 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4724. Hash: 7072046838448900487 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4724. Hash: 14469479861311642848 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4724. Hash: 14586570136206675867 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4724. Hash: 1080930468361041453 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 9998270082112342253 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 715698668253946311 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 19800. Hash: 9689144294054390217 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 19800. Hash: 13129471596255615133 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 19800. Hash: 12915864712175384367 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 19800. Hash: 704744783983559605 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 9998270082112342253 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 715698668253946311 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 5846. Hash: 2128276120164603588 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 5846. Hash: 954788723450394678 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 5846. Hash: 14309642535096835998 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 5846. Hash: 2996000735336843208 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 5846. Hash: 6423899825309547347 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 5846. Hash: 6861847030641362531 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 10645867109602892598 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 7257961874201179082 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 192. Hash: 12720324392877726426 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 192. Hash: 7937958557505694755 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 192. Hash: 15065829696130008213 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 192. Hash: 14378088137727336097 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 10645867109602892598 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 7257961874201179082 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 65. Hash: 3145658351065228323 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 65. Hash: 13102825703658386866 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 65. Hash: 14651886668877289638 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 65. Hash: 7706988068999921308 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 65. Hash: 1183045102730537867 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 65. Hash: 15200278891890596008 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 5283498994389857134 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 4759806696539235318 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4864. Hash: 1916980755154570809 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4864. Hash: 3641075817419129841 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4864. Hash: 1597884606887295389 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4864. Hash: 12470368568863176335 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 5283498994389857134 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 4759806696539235318 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1300. Hash: 519927422840758162 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1300. Hash: 16690819534142395524 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1300. Hash: 13697492049473980098 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1300. Hash: 16069718749273082363 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1300. Hash: 9503213552298852479 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1300. Hash: 14920629477034919393 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 12344372623177285558 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 3415525735687273566 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4096. Hash: 1937219152553164558 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4096. Hash: 16718347243549159919 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4096. Hash: 7775661729866538946 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4096. Hash: 16465645522600859391 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 12344372623177285558 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 3415525735687273566 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1122. Hash: 10794215007683911939 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1122. Hash: 4030215540982392192 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1122. Hash: 14289595630739500233 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1122. Hash: 13010448485976282215 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1122. Hash: 8318401526835048407 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1122. Hash: 100095329364523248 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 7123339701675171032 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 15827204344457762670 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1536. Hash: 15732245908985477324 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1536. Hash: 865397990859725823 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1536. Hash: 2552281640891423471 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1536. Hash: 17779645716491562667 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 7123339701675171032 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 15827204344457762670 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 448. Hash: 11533511688039530581 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 448. Hash: 6463578982826635244 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 448. Hash: 10590255987412720146 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 448. Hash: 17744412650281038495 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 448. Hash: 8278337182397882868 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 448. Hash: 8818080862566813062 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 12059380739436291361 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 17894062399627363441 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 3025207993250150716 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 909667023812047269 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 9807712812840041710 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 15826838159231044203 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 12059380739436291361 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 17894062399627363441 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 257. Hash: 8823641736072761245 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 257. Hash: 17988941723121644388 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 257. Hash: 8180735114915510878 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 257. Hash: 6608315278879931556 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 257. Hash: 15606348252975307518 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 257. Hash: 9909736394462106525 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 3444203649101035485 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 937532470362399061 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 54208. Hash: 196567085853229565 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 54208. Hash: 1716600532837684655 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 54208. Hash: 2905235470236164097 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 54208. Hash: 2074363216216487237 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 3444203649101035485 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 937532470362399061 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 13860. Hash: 263381874971540959 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 13860. Hash: 15191673020616208011 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 13860. Hash: 7105458185902008309 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 13860. Hash: 16259705089531364237 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 13860. Hash: 16022317673583270139 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 13860. Hash: 7251784463761381809 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 10464397683020008867 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 11067273583889078226 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 20048. Hash: 15901168792190323178 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 20048. Hash: 552570814640404138 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 20048. Hash: 17726428588184726937 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 20048. Hash: 7508591493894188819 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 10464397683020008867 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 11067273583889078226 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 5206. Hash: 1074257132915638034 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 5206. Hash: 2243653809093009497 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 5206. Hash: 11559777087289074275 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 5206. Hash: 15450183130901763011 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 5206. Hash: 15359221084106995089 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 5206. Hash: 8652443944286435468 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 14931201357194905697 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5600314145323623005 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 11738661055172304644 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 320620113692599118 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 8466587534284734762 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 3318206549561696188 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 14931201357194905697 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5600314145323623005 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 75483450873662317 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 3793407172213641704 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 2508676912793167321 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 2013136453053212946 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 9302779689053257196 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 16922723248982534245 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 9568588494434360329 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5573555818259644549 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 12082144485035076372 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 5616878210949329467 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 4057154333260499171 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 1620460765416970456 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 9568588494434360329 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5573555818259644549 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 3542867785718437760 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 5575788853295372734 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 8244955966647049157 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 17763430282605007327 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 5442657821959315522 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 12477592739739533067 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 4609122246850169975 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2696528076485355457 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 6640465288865380105 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 133343330720363387 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 790774688340154169 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 3046294637431015510 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 4609122246850169975 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2696528076485355457 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 16761061667647714654 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 14954885971875692232 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 37061345418264916 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 7368059604555723833 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 6244533771017459078 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 6416015160247779547 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2524875548439506384 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 89810986084680009 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 1044733215619569246 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 15252409165383740719 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 3184716392697283856 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 4969210758291089995 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2524875548439506384 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 89810986084680009 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 8467946356718878053 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 2373603727160338558 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 1521191693628786862 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 7175852234718691900 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 12283058591680528758 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 14516337485055158228 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 7734180808251274182 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 13560118186140352568 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 3520. Hash: 5538036360908204376 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 3520. Hash: 470358662493460341 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 3520. Hash: 13801426056203770982 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 3520. Hash: 8463107387658894201 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 7734180808251274182 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 13560118186140352568 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 2691029117997309291 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 10093724573967674240 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 1221219959437752888 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 1294720383009806722 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 10294793677923893113 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 6108415656799664788 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 4120783891454032649 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 9011003754405408275 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 18912. Hash: 12406712159692783345 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 18912. Hash: 7868083933985729169 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 18912. Hash: 1990603474898794477 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 18912. Hash: 4812378464029296668 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 4120783891454032649 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 9011003754405408275 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 4891. Hash: 15498889522919365505 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4891. Hash: 15832520573612498718 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4891. Hash: 6697133368486369688 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4891. Hash: 18420832496569008358 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4891. Hash: 2267250201584370787 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4891. Hash: 7889928104085840247 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx new file mode 100644 index 0000000000..caf6dcbe8f --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e63a55a35c749a16a03e10a1f53a48bd426c61db80151de080235b14cf6b70d +size 2479344 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py index 71f200074c..5965db57b1 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py @@ -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}") From fe8dac798977a2271a2a5775d947d7172949866e Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Mon, 25 Oct 2021 10:07:55 -0700 Subject: [PATCH 04/14] ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface (#4739) * ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface Introduced new PassSystemInterface::ForEachPass() funtion to replace PassSystemInterface::FindPasses(), PassSystemInterface::GetPassesByTemplateName and ParentPass::FindPassByNameRecursive() functions. Update all the places which were using those three functions. The new pass finding filter support any combination of pass name, pass template name, pass class type, pass hirechary, owner scene, owner render pipeline. Update unit tests. Signed-off-by: Qing Tao --- .../Include/Atom/Feature/ImGui/ImGuiUtils.h | 6 +- .../Include/Atom/Feature/ImGui/SystemBus.h | 7 +- .../Atom/Feature/Utils/FrameCaptureBus.h | 2 +- .../DirectionalLightFeatureProcessor.cpp | 71 ++--- ...fuseGlobalIlluminationFeatureProcessor.cpp | 57 ++-- .../DiffuseProbeGridFeatureProcessor.cpp | 12 +- .../DisplayMapper/DisplayMapperPass.cpp | 25 +- .../Source/FrameCaptureSystemComponent.cpp | 37 +-- .../Source/ImGui/ImGuiSystemComponent.cpp | 48 +-- .../Code/Source/ImGui/ImGuiSystemComponent.h | 2 +- .../DepthOfField/DepthOfFieldSettings.cpp | 20 +- .../ExposureControlSettings.cpp | 27 +- .../LookModificationCompositePass.cpp | 14 +- .../PostProcessing/SMAAFeatureProcessor.cpp | 51 ++-- .../ProfilingCaptureSystemComponent.cpp | 31 +- .../Source/ProfilingCaptureSystemComponent.h | 2 - .../ReflectionCopyFrameBufferPass.cpp | 19 +- .../ReflectionScreenSpaceBlurPass.cpp | 2 +- .../ReflectionScreenSpaceCompositePass.cpp | 26 +- .../ProjectedShadowFeatureProcessor.cpp | 54 ++-- .../Shadows/ProjectedShadowFeatureProcessor.h | 4 +- .../SkinnedMeshFeatureProcessor.cpp | 13 +- .../SkinnedMesh/SkinnedMeshFeatureProcessor.h | 2 +- .../Include/Atom/RPI.Public/Pass/ParentPass.h | 3 - .../Code/Include/Atom/RPI.Public/Pass/Pass.h | 4 + .../Include/Atom/RPI.Public/Pass/PassFilter.h | 136 ++++----- .../Atom/RPI.Public/Pass/PassLibrary.h | 4 +- .../Include/Atom/RPI.Public/Pass/PassSystem.h | 4 +- .../RPI.Public/Pass/PassSystemInterface.h | 21 +- .../Source/RPI.Public/Pass/ParentPass.cpp | 23 -- .../RPI/Code/Source/RPI.Public/Pass/Pass.cpp | 5 + .../Source/RPI.Public/Pass/PassFilter.cpp | 285 ++++++++++++++---- .../Source/RPI.Public/Pass/PassLibrary.cpp | 90 ++++-- .../Source/RPI.Public/Pass/PassSystem.cpp | 22 +- Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp | 159 ++++++++-- .../Code/Rendering/HairFeatureProcessor.cpp | 44 ++- .../Code/Rendering/HairFeatureProcessor.h | 2 + 37 files changed, 800 insertions(+), 534 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h index a200f30c98..52e272a9c4 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h @@ -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& passHierarchy) { ImGuiActiveContextScope scope; scope.ConnectToImguiNotificationBus(); - ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchyFilter); + ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchy); return scope; } diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h index 289cb178d4..78f71b39ba 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h @@ -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& 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. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h index 93600ec08f..c80926700e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h @@ -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 diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 6c24b7d35b..b6c6910fd3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -647,54 +647,46 @@ namespace AZ UpdateViewsOfCascadeSegments(); } - void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() { - const AZStd::vector& 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(pass); + AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); + if (pipeline->GetDefaultView()) { - CascadedShadowmapsPass* shadowPass = azrtti_cast(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& 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(pass); + AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); + if (esmPass->GetLightTypeName() == m_lightTypeName) { - EsmShadowmapsPass* esmPass = azrtti_cast(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 passFilter; - AZStd::vector cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!cubeMapPasses.empty()) - { - usageFlags |= RPI::View::UsageReflectiveCubeMap; - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + 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); } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp index c085b26e31..453dbbc0ab 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -80,35 +80,48 @@ namespace AZ } // update the size multiplier on the DiffuseProbeGridDownsamplePass output - AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; - RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); - const AZStd::vector& 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 outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; - RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + AZStd::vector 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 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(pass); - auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); - downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + RHI::ShaderInputNameIndex outputImageScaleShaderInput = "m_outputImageScale"; + downsamplePass->GetShaderResourceGroup()->SetConstant( + outputImageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + // handle all downsample passes + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } // update the image scale on the DiffuseComposite pass - AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; - RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); - const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); - for (RPI::Pass* pass : compositePasses) { - RPI::FullscreenTrianglePass* compositePass = static_cast(pass); - auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); - compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + AZStd::vector 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(pass); + RHI::ShaderInputNameIndex imageScaleShaderInput = "m_imageScale"; + compositePass->GetShaderResourceGroup()->SetConstant(imageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 4329fd556c..5ea4748fc9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -603,12 +603,12 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); if (device->GetFeatures().m_rayTracing == false) { - RPI::PassHierarchyFilter updatePassFilter(AZ::Name("DiffuseProbeGridUpdatePass")); - const AZStd::vector& 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; + }); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp index 3ec8840a1c..5b5599383e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp @@ -10,9 +10,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -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& 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(pass); - const Name& passName = fullscreenTrianglePass->GetName(); - if (passName.GetStringView() == "CopyToSwapChain") - { - fullscreenTrianglePass->QueueForInitialization(); - } - } - } + pass->QueueForInitialization(); + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); + ConfigureDisplayParameters(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 8c0360dec2..ed2d8a1d9f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -372,29 +372,25 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - RPI::PassClassFilter passFilter; - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - - if (foundPasses.size() == 0) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(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(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& 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 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; } diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp index afca20c5cb..3783752e67 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp @@ -109,15 +109,15 @@ namespace AZ void ImGuiSystemComponent::ForAllImGuiPasses(PassFunction func) { ImGuiContext* contextToRestore = ImGui::GetCurrentContext(); - RPI::PassClassFilter filter; - auto imguiPasses = RPI::PassSystemInterface::Get()->FindPasses(filter); - - for (RPI::Pass* pass : imguiPasses) - { - ImGuiPass* imguiPass = azrtti_cast(pass); - ImGui::SetCurrentContext(imguiPass->GetContext()); - func(imguiPass); - } + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [func](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + ImGuiPass* imguiPass = azrtti_cast(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& passHierarchyFilter) { - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passHierarchyFilter); + if (passHierarchyFilter.size() == 0) + { + AZ_Warning("ImGuiSystemComponent", false, "passHierarchyFilter is empty"); + return false; + } + AZStd::vector foundImGuiPasses; - for (RPI::Pass* pass : foundPasses) - { - ImGuiPass* imGuiPass = azrtti_cast(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(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(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h index 838cf0b3c2..d59124890f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h @@ -56,7 +56,7 @@ namespace AZ ImGuiPass* GetDefaultImGuiPass() override; bool PushActiveContextFromDefaultPass() override; - bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) override; + bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) override; bool PopActiveContext() override; ImGuiContext* GetActiveContext() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp index fe1ce8a281..98b527868d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -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& 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(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) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index 96ca424307..26c2a61d54 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -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 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 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() diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp index 85c45de96b..aac3d1d941 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp @@ -31,12 +31,14 @@ namespace AZ 0, [](const uint8_t& value) { - auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter()); - for (auto* pass : passes) - { - LookModificationCompositePass* lookModPass = azrtti_cast(pass); - lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [value](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + LookModificationCompositePass* lookModPass = azrtti_cast(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." diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index eef0c51e95..ad059fbec0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -71,26 +72,18 @@ namespace AZ void SMAAFeatureProcessor::UpdateConvertToPerceptualPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId)) - { - const AZStd::vector& 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& 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(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& 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(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& 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(pass); @@ -153,8 +138,8 @@ namespace AZ { neighborhoodBlendingPass->SetOutputMode(SMAAOutputMode::PassThrough); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet) diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 66adfe9985..9bfb2b7e9e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -377,14 +377,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector 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 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 ProfilingCaptureSystemComponent::FindPasses(AZStd::vector&& passHierarchy) const - { - // Find the pass first. - RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector 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 diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h index a9bb8c585f..af1d2f5643 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h @@ -78,8 +78,6 @@ namespace AZ // Recursively collect all the passes from the root pass. AZStd::vector CollectPassesRecursively(const RPI::Pass* root) const; - AZStd::vector FindPasses(AZStd::vector&& passHierarchy) const; - DelayedQueryCaptureHelper m_timestampCapture; DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture; DelayedQueryCaptureHelper m_pipelineStatisticsCapture; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp index 37b1885ee3..a1858a7e9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp @@ -28,16 +28,17 @@ namespace AZ void ReflectionCopyFrameBufferPass::BuildInternal() { - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); - Data::Instance& 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(pass); + Data::Instance& 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(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp index 394a6fd406..edd7ad1013 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp @@ -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; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp index 1cf227650c..1362191691 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -33,20 +33,22 @@ namespace AZ return; } - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(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(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); } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 7c0b3563c7..70ccaa5702 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -313,52 +313,38 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::CachePasses() { - const AZStd::vector validPipelineIds = CacheProjectedShadowmapsPass(); - CacheEsmShadowmapsPass(validPipelineIds); + CacheProjectedShadowmapsPass(); + CacheEsmShadowmapsPass(); m_shadowmapPassNeedsUpdate = true; } - AZStd::vector ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() + void ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() { - const AZStd::vector& renderPipelines = GetParentScene()->GetRenderPipelines(); - const auto* passSystem = RPI::PassSystemInterface::Get();; - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate")); - - AZStd::vector validPipelineIds; m_projectedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - ProjectedShadowmapsPass* shadowPass = static_cast(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(pass); + m_projectedShadowmapsPasses.emplace_back(shadowPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } - void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) + void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass() { const Name LightTypeName = Name("projected"); - - const auto* passSystem = RPI::PassSystemInterface::Get(); - const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); - + m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - EsmShadowmapsPass* esmPass = static_cast(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(pass); + if (esmPass->GetLightTypeName() == LightTypeName) + { + m_esmShadowmapsPasses.emplace_back(esmPass); + } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ProjectedShadowFeatureProcessor::UpdateFilterParameters() diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index fafcb25a08..8939f1845d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -97,8 +97,8 @@ namespace AZ::Render // Functions for caching the ProjectedShadowmapsPass and EsmShadowmapsPass. void CachePasses(); - AZStd::vector CacheProjectedShadowmapsPass(); - void CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds); + void CacheProjectedShadowmapsPass(); + void CacheEsmShadowmapsPass(); //! Functions to update the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index e0209702dc..4c379c4239 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -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 pipelineRootPass) + void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline) { - RPI::Ptr skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" }); + RPI::PassFilter skinPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "SkinningPass" }, renderPipeline); + RPI::Ptr skinningPass = RPI::PassSystemInterface::Get()->FindFirstPass(skinPassFilter); if (skinningPass) { SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast(skinningPass.get()); @@ -310,7 +312,8 @@ namespace AZ } } - RPI::Ptr morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" }); + RPI::PassFilter morphPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "MorphTargetPass" }, renderPipeline); + RPI::Ptr morphTargetPass = RPI::PassSystemInterface::Get()->FindFirstPass(morphPassFilter); if (morphTargetPass) { MorphTargetComputePass* morphTargetComputePass = azdynamic_cast(morphTargetPass.get()); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h index 2e93acf2cd..5b7ab943e1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h @@ -66,7 +66,7 @@ namespace AZ private: AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor); - void InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass); + void InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline); SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override; bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 36994ec03b..6523f0a6d8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -68,9 +68,6 @@ namespace AZ template Ptr FindChildPass() const; - //! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found. - Ptr FindPassByNameRecursive(const Name& passName) const; - //! Gets the list of children. Useful for validating hierarchies AZStd::array_view> GetChildren() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 34eaae4495..e7b9825ecb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -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); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h index c31f353adb..c42991725e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h @@ -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& passHierarchy); + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithTemplateName(Name templateName, const Scene* scene); + static PassFilter CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline); + template + 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& passHierarchy); - PassHierarchyFilter(const AZStd::vector& 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 m_parentNames; Name m_passName; + Name m_templateName; + TypeId m_passClassTypeId = TypeId::CreateNull(); + AZStd::vector 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 - 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 - bool PassClassFilter::Matches(const Pass* pass) const - { - return pass->RTTI_IsTypeOf(PassClass::RTTI_Type()); - } - - template - const Name* PassClassFilter::GetPassName() const - { - return nullptr; - } - - template - AZStd::string PassClassFilter::ToString() const - { - return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName()); + template + PassFilter PassFilter::CreateWithPassClass() + { + PassFilter filter; + filter.m_passClassTypeId = PassClass::RTTI_Type(); + filter.UpdateFilterOptions(); + return filter; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h index 0a4b1c4399..66c3c205ab 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h @@ -84,8 +84,8 @@ namespace AZ bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath); bool LoadPassTemplateMappings(Data::Asset mappingAsset); - //! Returns a list of passes found in the pass name mapping using the provided pass filter - AZStd::vector FindPasses(const PassFilter& passFilter) const; + //! Visit each pass which matches the filter + void ForEachPass(const PassFilter& passFilter, AZStd::function passFunction); private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h index 30fa27e64b..8390b0f7e2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h @@ -92,13 +92,13 @@ namespace AZ // PassSystemInterface library related functions... bool HasPassesForTemplateName(const Name& templateName) const override; - const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const override; bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) override; const AZStd::shared_ptr GetPassTemplate(const Name& name) const override; void RemovePassFromLibrary(Pass* pass) override; void RegisterPass(Pass* pass) override; void UnregisterPass(Pass* pass) override; - AZStd::vector FindPasses(const PassFilter& passFilter) const override; + void ForEachPass(const PassFilter& filter, AZStd::function passFunction) override; + Pass* FindFirstPass(const PassFilter& filter) override; private: // Returns the root of the pass tree hierarchy diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h index 0e9386f3bb..7f944df88f 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h @@ -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& GetPassesForTemplateName(const Name& templateName) const = 0; - //! Adds a PassTemplate to the library virtual bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& 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 passFunction) = 0; - //! Find matching passes from registered passes with specified filter - virtual AZStd::vector 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 diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp index 36c28877ea..dccf5dbc2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -149,29 +149,6 @@ namespace AZ return index.IsValid() ? m_children[index.GetIndex()] : Ptr(nullptr); } - Ptr ParentPass::FindPassByNameRecursive(const Name& passName) const - { - for (const Ptr& 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) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index a8d94e9a91..3c1de28d6a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -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 diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp index 7bd1abc0a7..d9e458c615 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp @@ -8,101 +8,264 @@ #include #include +#include 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& 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& 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& 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& 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(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(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 diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp index c43edafd6b..6a6f5f3ff9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp @@ -85,47 +85,80 @@ namespace AZ return (GetPassesForTemplate(templateName).size() > 0); } - AZStd::vector PassLibrary::FindPasses(const PassFilter& passFilter) const + void PassLibrary::ForEachPass(const PassFilter& passFilter, AZStd::function passFunction) { - const Name* passName = passFilter.GetPassName(); + uint32_t filterOptions = passFilter.GetEnabledFilterOptions(); - AZStd::vector 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& 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& 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 + diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index f4f51f97b7..7f2948c13a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -456,11 +456,6 @@ namespace AZ return m_passLibrary.HasPassesForTemplate(templateName); } - const AZStd::vector& PassSystem::GetPassesForTemplateName(const Name& templateName) const - { - return m_passLibrary.GetPassesForTemplate(templateName); - } - bool PassSystem::AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) { return m_passLibrary.AddPassTemplate(name, passTemplate); @@ -487,10 +482,21 @@ namespace AZ RemovePassFromLibrary(pass); --m_passCounter; } - - AZStd::vector PassSystem::FindPasses(const PassFilter& passFilter) const + + void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function 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 diff --git a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp index 420ab1798a..690f212ec7 100644 --- a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -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{}); + PassFilter filter = PassFilter::CreateWithPassHierarchy(AZStd::vector{}); AZ_TEST_STOP_TRACE_SUPPRESSION(1); - EXPECT_FALSE(filter.Matches(pass.get())); } { // Filters with partial hierarchy by using string vector AZStd::vector passHierarchy1 = { "parent1", "pass1" }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { "parent2", "pass1" }; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector 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 passHierarchy1 = { Name("parent1"), Name("pass1") }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { Name("parent2"), Name("pass1")}; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector 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{"parent3", "parent1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"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{"Parent1", "Parent3", "pass1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "Parent3", "pass1"}); EXPECT_FALSE(filter1.Matches(pass.get())); // Mis-matching name - PassHierarchyFilter filter2(AZStd::vector{"Parent1", "pass1"}); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "pass1"}); EXPECT_FALSE(filter2.Matches(parent1.get())); } } + + TEST_F(PassTests, PassFilter_Empty_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr 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 = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr depthPass = m_passSystem->CreatePassFromTemplate(Name("DepthPrePass"), Name("depthPass")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + parent1->AsParent()->AddChild(pass); + parent1->AsParent()->AddChild(depthPass); + + PassFilter filter1 = PassFilter::CreateWithPassClass(); + + EXPECT_TRUE(filter1.Matches(pass.get())); + EXPECT_FALSE(filter1.Matches(parent1.get())); + + PassFilter filter2 = PassFilter::CreateWithPassClass(); + 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 childPass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr 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 = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr 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 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); + + } } diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index a0be18f0e2..161160e16f 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -142,12 +143,13 @@ namespace AZ EnablePasses(true); } - void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable) + void HairFeatureProcessor::EnablePasses(bool enable) { - RPI::Ptr 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 desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(passName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_computePasses[passName] = static_cast(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 desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName); + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLRasterPassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLRasterPass = static_cast(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 desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLResolvePassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLResolvePass = static_cast(desiredPass.get()); @@ -518,8 +530,8 @@ namespace AZ m_hairShortCutGeometryDepthAlphaPass = nullptr; m_hairShortCutGeometryShadingPass = nullptr; - m_hairShortCutGeometryDepthAlphaPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get()); + RPI::PassFilter depthAlphaPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryDepthAlphaPassName, m_renderPipeline); + m_hairShortCutGeometryDepthAlphaPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(depthAlphaPassFilter)); if (m_hairShortCutGeometryDepthAlphaPass) { m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this); @@ -530,8 +542,8 @@ namespace AZ return false; } - m_hairShortCutGeometryShadingPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get()); + RPI::PassFilter shaderingPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryShadingPassName, m_renderPipeline); + m_hairShortCutGeometryShadingPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(shaderingPassFilter)); if (m_hairShortCutGeometryShadingPass) { m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this); diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index 46660a6623..f810967824 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -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 m_hairFeatureProcessorRegistryName; From c44d2a351b3bd202ed5126ff58c8ddf2ebcb5c8b Mon Sep 17 00:00:00 2001 From: AMZN-stankowi <4838196+AMZN-stankowi@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:08:49 -0700 Subject: [PATCH 05/14] Fixed some files missed when groundplane_521 was renamed to 512 (#4958) * Fixed references to 521x521 to reference the correct 512x512 FBX file Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Fixed asset hints Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> --- Assets/Editor/Prefabs/Default_Level.prefab | 4 ++-- .../Prefabs/test_sponza_material_conversion.prefab | 10 +++++----- .../CommonFeatures/Assets/LevelAssets/default.slice | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Assets/Editor/Prefabs/Default_Level.prefab b/Assets/Editor/Prefabs/Default_Level.prefab index d02d669f53..0267131fa0 100644 --- a/Assets/Editor/Prefabs/Default_Level.prefab +++ b/Assets/Editor/Prefabs/Default_Level.prefab @@ -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 } } diff --git a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab index b5aed9d14b..92874574e4 100644 --- a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab +++ b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab @@ -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" } } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice index b4c9eac10f..e0c7c9e456 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice @@ -836,7 +836,7 @@ - + From da2ce2d7f0ac1ce5cbc77d7960e03f7623e38f46 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:23:43 -0500 Subject: [PATCH 06/14] Normalize Shader vs Shaders Folders (#4925) Normalize Shader vs Shaders Folders (#4925) Signed-off-by: garrieta --- .../RPI/Assets/{Shader => Shaders}/DecomposeMsImage.azsl | 0 .../Assets/{Shader => Shaders}/DecomposeMsImage.shader | 0 .../Atom/RPI/Assets/{Shader => Shaders}/ImagePreview.azsl | 0 .../RPI/Assets/{Shader => Shaders}/ImagePreview.shader | 0 .../{Shader => Shaders}/ImagePreview.shadervariantlist | 0 .../RPI/Assets/{Shader => Shaders}/SceneAndViewSrgs.azsl | 0 .../Assets/{Shader => Shaders}/SceneAndViewSrgs.shader | 0 Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake | 8 ++++---- .../Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h | 2 +- .../Code/Source/RPI.Public/Pass/AttachmentReadback.cpp | 2 +- .../Pass/Specific/ImageAttachmentPreviewPass.cpp | 2 +- Gems/Atom/RPI/Registry/atom_rpi.setreg | 2 +- 12 files changed, 8 insertions(+), 8 deletions(-) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/DecomposeMsImage.azsl (100%) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/DecomposeMsImage.shader (100%) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/ImagePreview.azsl (100%) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/ImagePreview.shader (100%) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/ImagePreview.shadervariantlist (100%) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/SceneAndViewSrgs.azsl (100%) rename Gems/Atom/RPI/Assets/{Shader => Shaders}/SceneAndViewSrgs.shader (100%) diff --git a/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.azsl b/Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.azsl similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.azsl rename to Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.azsl diff --git a/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader b/Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.shader similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader rename to Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.shader diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.azsl b/Gems/Atom/RPI/Assets/Shaders/ImagePreview.azsl similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/ImagePreview.azsl rename to Gems/Atom/RPI/Assets/Shaders/ImagePreview.azsl diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.shader b/Gems/Atom/RPI/Assets/Shaders/ImagePreview.shader similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/ImagePreview.shader rename to Gems/Atom/RPI/Assets/Shaders/ImagePreview.shader diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.shadervariantlist b/Gems/Atom/RPI/Assets/Shaders/ImagePreview.shadervariantlist similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/ImagePreview.shadervariantlist rename to Gems/Atom/RPI/Assets/Shaders/ImagePreview.shadervariantlist diff --git a/Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.azsl b/Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.azsl similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.azsl rename to Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.azsl diff --git a/Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.shader b/Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.shader similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.shader rename to Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.shader diff --git a/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake b/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake index 9e89427a70..40478da961 100644 --- a/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake +++ b/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake @@ -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 diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h index 1b7b32c1e9..7f57b30326 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h @@ -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; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp index c19ae565d0..890e87a7a5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp @@ -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) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp index 105936f64d..74d52fd64d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp @@ -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 = RPI::FindShaderAsset(ShaderPath); m_shader = Shader::FindOrCreate(shaderAsset); if (m_shader == nullptr) diff --git a/Gems/Atom/RPI/Registry/atom_rpi.setreg b/Gems/Atom/RPI/Registry/atom_rpi.setreg index bcbade5d38..16ce8b3bd7 100644 --- a/Gems/Atom/RPI/Registry/atom_rpi.setreg +++ b/Gems/Atom/RPI/Registry/atom_rpi.setreg @@ -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 From 4d4deb121190095a1e719480c814a26b83a55dc2 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:26:53 -0500 Subject: [PATCH 07/14] Added Hydra API to extract all the classes, globals and EBuses exposed (#4953) * Added Hydra API to extract all the classes, globals and EBuses exposed to lua: azlmbr.script.LuaSymbolsReporterBus: GetListOfClasses GetListOfGlobalProperties GetListOfGlobalFunctions GetListOfEBuses Also exposed to Hydra the classes that can be used to dump the symbols azlmbr.script.LuaPropertySymbol azlmbr.script.LuaMethodSymbol azlmbr.script.LuaClassSymbol azlmbr.script.LuaEBusSender azlmbr.script.LuaEBusSymbol The python file Assets/Editor/Scripts/lua_symbols.py can be used with "pyRunFile [output.txt]" to create Game/output.txt will all the symbols OR passing up to three additional arguments "c" or "g" or "e" to dump only classes, globals or ebuses or a combination of those. Example: To create an output file with only classes and Ebuses: "pyRunFile [output.txt] c e" Signed-off-by: garrieta --- Assets/Editor/Scripts/lua_symbols.py | 116 +++++ .../Application/ToolsApplication.cpp | 4 +- .../AzToolsFrameworkModule.cpp | 2 + .../Script/LuaSymbolsReporterBus.h | 110 ++++ .../LuaSymbolsReporterSystemComponent.cpp | 475 ++++++++++++++++++ .../LuaSymbolsReporterSystemComponent.h | 73 +++ .../aztoolsframework_files.cmake | 3 + 7 files changed, 782 insertions(+), 1 deletion(-) create mode 100644 Assets/Editor/Scripts/lua_symbols.py create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h diff --git a/Assets/Editor/Scripts/lua_symbols.py b/Assets/Editor/Scripts/lua_symbols.py new file mode 100644 index 0000000000..b21edb41d0 --- /dev/null +++ b/Assets/Editor/Scripts/lua_symbols.py @@ -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}") diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index fbd066ec6e..3f254581dd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -70,6 +70,7 @@ #include #include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer' needs to have dll-interface to be used by clients of class 'QFileInfo' @@ -273,7 +274,8 @@ namespace AzToolsFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), - azrtti_typeid() + azrtti_typeid(), + azrtti_typeid(), }); return components; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index d2a88df544..2cdf409125 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -54,6 +54,7 @@ #include #include #include +#include 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(), }); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h new file mode 100644 index 0000000000..f0bfecef38 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h @@ -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 +#include + +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 m_properties; + AZStd::vector 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 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& GetListOfClasses() = 0; + virtual const AZStd::vector& GetListOfGlobalProperties() = 0; + virtual const AZStd::vector& GetListOfGlobalFunctions() = 0; + virtual const AZStd::vector& 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; + + } // namespace Script +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp new file mode 100644 index 0000000000..81ba8ee398 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp @@ -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 +#include +#include +#include + +#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(context); + if (behaviorContext) + { + behaviorContext->Class() + ->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(context); + if (behaviorContext) + { + behaviorContext->Class() + ->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().c_str()); + } + + void LuaClassSymbol::Reflect(AZ::ReflectContext* context) + { + auto behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->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(context); + if (behaviorContext) + { + behaviorContext->Class() + ->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(context); + if (behaviorContext) + { + behaviorContext->Class() + ->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& GetClassSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedClassSymbols; } + static AZStd::unordered_map& GetClassUuidToIndexMap(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_classUuidToIndexMap; } + static AZStd::vector& GetGlobalPropertySymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedGlobalPropertySymbols; } + static AZStd::vector& GetGlobalFunctionSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedGlobalFunctionSymbols; } + static AZStd::vector& GetEBusSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedEbusSymbols; } + static AZStd::unordered_map& 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(context)) + { + serializeContext->Class() + ->Version(0); + + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("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(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(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& 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(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(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().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(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().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& LuaSymbolsReporterSystemComponent::GetListOfGlobalProperties() + { + if (!m_cachedGlobalPropertySymbols.empty()) + { + return m_cachedGlobalPropertySymbols; + } + + LoadGlobalSymbols(); + + return m_cachedGlobalPropertySymbols; + } + + const AZStd::vector& LuaSymbolsReporterSystemComponent::GetListOfGlobalFunctions() + { + if (!m_cachedGlobalFunctionSymbols.empty()) + { + return m_cachedGlobalFunctionSymbols; + } + + LoadGlobalSymbols(); + + return m_cachedGlobalFunctionSymbols; + } + + const AZStd::vector& 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(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(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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h new file mode 100644 index 0000000000..2467c82ad7 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h @@ -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 +#include + +#include + +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& GetListOfClasses() override; + const AZStd::vector& GetListOfGlobalProperties() override; + const AZStd::vector& GetListOfGlobalFunctions() override; + const AZStd::vector& 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 m_cachedClassSymbols; + // The key is a class uuid, the value is the index in @m_cachedClassSymbols + AZStd::unordered_map m_classUuidToIndexMap; + + AZStd::vector m_cachedGlobalPropertySymbols; + AZStd::vector m_cachedGlobalFunctionSymbols; + + AZStd::vector m_cachedEbusSymbols; + + // The key is the ebus name, the value is the index in @m_cachedEbusSymbols + AZStd::unordered_map m_ebusNameToIndexMap; + + }; + } // namespace Script +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index b57eedcd81..286ef97418 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -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 From 62231aeb4cc415a6c43de8bcfab09e087aeffaab Mon Sep 17 00:00:00 2001 From: Mike Chang Date: Mon, 25 Oct 2021 13:48:17 -0700 Subject: [PATCH 08/14] Add xcb input dev packages (#4962) Signed-off-by: Mike Chang --- .../Platform/Linux/package-list.ubuntu-bionic.txt | 7 ++++++- .../Platform/Linux/package-list.ubuntu-focal.txt | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt index 625909e214..a09d823950 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt @@ -12,7 +12,12 @@ libxcb-xinerama0 # For Qt plugins at runtime libxcb-xinput0 # For Qt plugins at runtime libfontconfig1-dev # For Qt plugins at runtime libcurl4-openssl-dev # For HttpRequestor -libsdl2-dev # for WWise/Audio +libsdl2-dev # For WWise/Audio +libxcb-xkb-dev # For xcb keyboard input +libxkbcommon-x11-dev # For xcb keyboard input +libxkbcommon-dev # For xcb keyboard input +libxcb-xfixes0-dev # For mouse input +libxcb-xinput-dev # For mouse input zlib1g-dev mesa-common-dev diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt index 2cbbe58b38..e0e03cda90 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt @@ -16,5 +16,7 @@ libsdl2-dev # for WWise/Audio libxcb-xkb-dev # For xcb keyboard input libxkbcommon-x11-dev # For xcb keyboard input libxkbcommon-dev # For xcb keyboard input +libxcb-xfixes0-dev # For mouse input +libxcb-xinput-dev # For mouse input zlib1g-dev mesa-common-dev From 29dbb0b08935bfdf0c1c364d3458482b46e369d8 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:57:53 -0700 Subject: [PATCH 09/14] Set Project Manager minimum height 700px (#4970) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- Code/Tools/ProjectManager/Resources/ProjectManager.qss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 6694168f2b..db0ec7e91a 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -9,7 +9,7 @@ QMainWindow { #ScreensCtrl { min-width:1200px; - min-height:800px; + min-height:700px; } QPushButton:focus { From d7045f4c3148229495adc557647541afb249c040 Mon Sep 17 00:00:00 2001 From: Jonny Galloway Date: Mon, 25 Oct 2021 18:18:27 -0500 Subject: [PATCH 10/14] Scaffold PythonGem template (#4888) * Renamed ctest_pytest.ini to pytest.ini so it is used by default, added TestSuite_ as collection file (#4822) * Fixed warnings of unused marks, renamed ctest_pytest.ini to pytest.ini to better consistency on runs * Fixed some test suites to run propertly * Fix missing arguments * Fixed missing cmakelists and renamed missing file * Temp disable editor_testing_tests as timeout in jenkins Signed-off-by: Jonny Gallowy * scaffold PythonGem template Signed-off-by: Jonny Gallowy * CustomTool was not added to CMakeLists Signed-off-by: Jonny Gallowy * Update Templates/PythonGem/Template/Code/CMakeLists.txt makes sense thank you for the help Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Signed-off-by: Jonny Gallowy * Update Templates/PythonGem/Template/Code/CMakeLists.txt Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Signed-off-by: Jonny Gallowy * Delete ${NameLower}_tests_files.cmake Signed-off-by: Jonny Gallowy * suggested changes made Signed-off-by: Jonny Gallowy * cleanup not needed files Signed-off-by: Jonny Gallowy * fix up template.json Signed-off-by: Jonny Gallowy * made a correction Signed-off-by: Jonny Gallowy * made a correction Signed-off-by: Jonny Gallowy * fixes Signed-off-by: Jonny Gallowy * fixes Signed-off-by: Jonny Gallowy * more fixes, fixed a file name Signed-off-by: Jonny Gallowy * Added helper method to az_qt_helpers for retrieving the main window instance. Signed-off-by: Chris Galvan * additional fixes Signed-off-by: Jonny Gallowy * Fix for AP trowing errors at boot (can not get mainwindow) Signed-off-by: Jonny Gallowy * Added missing EBus connect/disconnect calls in the EditorSystemComponent Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * additional fix up Signed-off-by: Jonny Gallowy * adding dialog internals Signed-off-by: Jonny Gallowy Co-authored-by: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Co-authored-by: Chris Galvan --- Templates/CMakeLists.txt | 2 + Templates/PythonGem/Template/CMakeLists.txt | 14 ++ .../Code/${NameLower}_editor_files.cmake | 14 ++ .../${NameLower}_editor_shared_files.cmake | 11 + .../${NameLower}_editor_tests_files.cmake | 11 + .../PythonGem/Template/Code/CMakeLists.txt | 76 ++++++ .../Code/Include/${Name}/${Name}Bus.h | 40 ++++ .../Linux/${NameLower}_linux_files.cmake | 15 ++ .../${NameLower}_shared_linux_files.cmake | 15 ++ .../Code/Platform/Linux/PAL_linux.cmake | 11 + .../Platform/Mac/${NameLower}_mac_files.cmake | 15 ++ .../Mac/${NameLower}_shared_mac_files.cmake | 15 ++ .../Template/Code/Platform/Mac/PAL_mac.cmake | 11 + .../${NameLower}_shared_windows_files.cmake | 15 ++ .../Windows/${NameLower}_windows_files.cmake | 15 ++ .../Code/Platform/Windows/PAL_windows.cmake | 11 + .../Code/Source/${Name}EditorModule.cpp | 47 ++++ .../Source/${Name}EditorSystemComponent.cpp | 70 ++++++ .../Source/${Name}EditorSystemComponent.h | 42 ++++ .../Code/Source/${Name}ModuleInterface.h | 36 +++ .../Template/Code/Tests/${Name}EditorTest.cpp | 13 ++ .../Editor/Scripts/${NameLower}_dialog.py | 46 ++++ .../Template/Editor/Scripts/__init__.py | 9 + .../Template/Editor/Scripts/bootstrap.py | 117 ++++++++++ Templates/PythonGem/Template/gem.json | 16 ++ Templates/PythonGem/Template/preview.png | 3 + Templates/PythonGem/template.json | 216 ++++++++++++++++++ 27 files changed, 906 insertions(+) create mode 100644 Templates/PythonGem/Template/CMakeLists.txt create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake create mode 100644 Templates/PythonGem/Template/Code/CMakeLists.txt create mode 100644 Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h create mode 100644 Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp create mode 100644 Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py create mode 100644 Templates/PythonGem/Template/Editor/Scripts/__init__.py create mode 100644 Templates/PythonGem/Template/Editor/Scripts/bootstrap.py create mode 100644 Templates/PythonGem/Template/gem.json create mode 100644 Templates/PythonGem/Template/preview.png create mode 100644 Templates/PythonGem/template.json diff --git a/Templates/CMakeLists.txt b/Templates/CMakeLists.txt index 1a3a45b5ec..84a708989a 100644 --- a/Templates/CMakeLists.txt +++ b/Templates/CMakeLists.txt @@ -9,6 +9,8 @@ ly_install_directory( DIRECTORIES AssetGem + CustomTool + PythonGem DefaultGem DefaultProject MinimalProject diff --git a/Templates/PythonGem/Template/CMakeLists.txt b/Templates/PythonGem/Template/CMakeLists.txt new file mode 100644 index 0000000000..d61bbd9e7d --- /dev/null +++ b/Templates/PythonGem/Template/CMakeLists.txt @@ -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) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..8362d37f52 --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake @@ -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 +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake @@ -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 +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake @@ -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 +) diff --git a/Templates/PythonGem/Template/Code/CMakeLists.txt b/Templates/PythonGem/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..b7a5ac89a9 --- /dev/null +++ b/Templates/PythonGem/Template/Code/CMakeLists.txt @@ -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/ or +# //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() diff --git a/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h @@ -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 +#include + +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} diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -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 +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -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 +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake @@ -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) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -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 Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -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 Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake @@ -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) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -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 Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -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 Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake @@ -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) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..644c513747 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,47 @@ +// {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} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..1493c98e68 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,70 @@ +// {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} + +#include +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, AZ::Component>(); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..1db8725a9e --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,42 @@ +// {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 +#include <${Name}/${Name}Bus.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}RequestBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler + , public AZ::Component + { + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}"); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + 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(); + void Deactivate(); + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..4ddfc9c007 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,36 @@ +// {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} + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {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} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py new file mode 100644 index 0000000000..39515711ae --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py @@ -0,0 +1,46 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\${SanitizedCppName}_dialog.py +Generated from O3DE PythonGem Template""" + +import azlmbr +from shiboken2 import wrapInstance, getCppPointer +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QVBoxLayout, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton + +# Once PySide2 has been bootstrapped, register our ${SanitizedCppName}Dialog with the Editor + +class ${SanitizedCppName}Dialog(QDialog): + def __init__(self, parent=None): + super(${SanitizedCppName}Dialog, self).__init__(parent) + + self.setObjectName("${SanitizedCppName}Dialog") + + self.setWindowTitle("HelloWorld, ${SanitizedCppName} Dialog") + + self.mainLayout = QVBoxLayout(self) + + self.introLabel = QLabel("Put your cool stuff here!") + + self.mainLayout.addWidget(self.introLabel, 0, Qt.AlignCenter) + + self.helpText = str("For help getting started," + "visit the UI Development documentation
" + "or come ask a question in the sig-ui-ux channel on Discord") + + self.helpLabel = QLabel() + self.helpLabel.setTextFormat(Qt.RichText) + self.helpLabel.setText(self.helpText) + self.helpLabel.setOpenExternalLinks(True) + + self.mainLayout.addWidget(self.helpLabel, 0, Qt.AlignCenter) + + self.setLayout(self.mainLayout) + + return \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/__init__.py b/Templates/PythonGem/Template/Editor/Scripts/__init__.py new file mode 100644 index 0000000000..b5da0c7ff0 --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/__init__.py @@ -0,0 +1,9 @@ +""" +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 +""" +# ------------------------------------------------------------------------- + +__ALL__ = ['bootstrap','${NameLower}_dialog'] \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py new file mode 100644 index 0000000000..060116d36c --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py @@ -0,0 +1,117 @@ +""" +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 +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\boostrap.py +Generated from O3DE PythonGem Template""" + +import azlmbr +import az_qt_helpers +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QMainWindow, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +class SampleUI(QtWidgets.QDialog): + """Lightweight UI Test Class created a button""" + def __init__(self, parent, title='Not Set'): + super(SampleUI, self).__init__(parent) + self.setWindowTitle(title) + self.initUI() + + def initUI(self): + mainLayout = QtWidgets.QHBoxLayout() + testBtn = QtWidgets.QPushButton("I am just a Button man!") + mainLayout.addWidget(testBtn) + self.setLayout(mainLayout) +# ------------------------------------------------------------------------- + +if __name__ == "__main__": + print("${SanitizedCppName}.boostrap, Generated from O3DE PythonGem Template") + + # --------------------------------------------------------------------- + # validate pyside before continuing + try: + azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'IsActive') + params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'GetQtBootstrapParameters') + params is not None and params.mainWindowId is not 0 + from PySide2 import QtWidgets + except Exception as e: + _LOGGER.error(f'Pyside not available, exception: {e}') + raise e + + # keep going, import the other PySide2 bits we will use + from PySide2 import QtGui + from PySide2.QtCore import Slot + from shiboken2 import wrapInstance, getCppPointer + + # Get our Editor main window + _widget_main_window = None + try: + _widget_main_window = az_qt_helpers.get_editor_main_window() + except: + pass # may be booting in the AP? + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # creat a custom menu + _tag_str = '${SanitizedCppName}' + + # create our own menuBar + ${SanitizedCppName}_menu = _widget_main_window.menuBar().addMenu(f"&{_tag_str}") + + # nest a menu for util/tool launching + ${SanitizedCppName}_launch_menu = ${SanitizedCppName}_menu.addMenu("examples") + else: + print('No O3DE MainWindow') + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) add the first SampleUI + action_launch_sample_ui = ${SanitizedCppName}_launch_menu.addAction("O3DE:SampleUI") + + @Slot() + def clicked_sample_ui(): + while 1: # simple PySide2 test, set to 0 to disable + ui = SampleUI(parent=_widget_main_window, title='O3DE:SampleUI') + ui.show() + break + return + # Add click event to menu bar + action_launch_sample_ui.triggered.connect(clicked_sample_ui) + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) and custom external module Qwidget + action_launch_${SanitizedCppName}_dialog = ${SanitizedCppName}_launch_menu.addAction("O3DE:${SanitizedCppName}_dialog") + + @Slot() + def clicked_${SanitizedCppName}_dialog(): + while 1: # simple PySide2 test, set to 0 to disable + try: + import az_qt_helpers + from ${NameLower}_dialog import ${SanitizedCppName}Dialog + az_qt_helpers.register_view_pane('${SanitizedCppName} Popup', ${SanitizedCppName}Dialog) + except Exception as e: + print(f'Error: {e}') + print('Skipping register our ${SanitizedCppName}Dialog with the Editor.') + ${SanitizedCppName}_dialog = ${SanitizedCppName}Dialog(parent=_widget_main_window) + ${SanitizedCppName}_dialog.show() + break + return + # Add click event to menu bar + action_launch_${SanitizedCppName}_dialog.triggered.connect(clicked_${SanitizedCppName}_dialog) + # --------------------------------------------------------------------- + + # end \ No newline at end of file diff --git a/Templates/PythonGem/Template/gem.json b/Templates/PythonGem/Template/gem.json new file mode 100644 index 0000000000..353ad6bf8d --- /dev/null +++ b/Templates/PythonGem/Template/gem.json @@ -0,0 +1,16 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "" +} diff --git a/Templates/PythonGem/Template/preview.png b/Templates/PythonGem/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/PythonGem/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/PythonGem/template.json b/Templates/PythonGem/template.json new file mode 100644 index 0000000000..75be757abb --- /dev/null +++ b/Templates/PythonGem/template.json @@ -0,0 +1,216 @@ +{ + "template_name": "PythonGem", + "restricted_name": "o3de", + "restricted_platform_relative_path": "Templates", + "origin": "The primary repo for PythonGem goes here: i.e. http://www.mydomain.com", + "license": "What license PythonGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "PythonGem", + "summary": "A short description of PythonGem.", + "canonical_tags": [], + "user_tags": [ + "PythonGem" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/__init__.py", + "origin": "Editor/Scripts/__init__.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/bootstrap.py", + "origin": "Editor/Scripts/bootstrap.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/${NameLower}_dialog.py", + "origin": "Editor/Scripts/${NameLower}_dialog.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Editor", + "origin": "Editor" + }, + { + "dir": "Editor/Scripts", + "origin": "Editor/Scripts" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + } + ] +} From c00b99f2d592cbe5141101b8782cfe0f27da33d6 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:33:32 -0700 Subject: [PATCH 11/14] Disable custom titlebar on Mac, Linux, fix resize (#4973) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- Code/Tools/ProjectManager/CMakeLists.txt | 1 + .../Platform/Linux/PAL_linux_files.cmake | 2 ++ .../Platform/Linux/ProjectManager_Traits_Linux.h | 11 +++++++++++ .../Platform/Linux/ProjectManager_Traits_Platform.h | 11 +++++++++++ .../ProjectManager/Platform/Mac/PAL_mac_files.cmake | 2 ++ .../Platform/Mac/ProjectManager_Traits_Mac.h | 11 +++++++++++ .../Platform/Mac/ProjectManager_Traits_Platform.h | 11 +++++++++++ .../Platform/Windows/PAL_windows_files.cmake | 2 ++ .../Platform/Windows/ProjectManager_Traits_Platform.h | 11 +++++++++++ .../Platform/Windows/ProjectManager_Traits_Windows.h | 11 +++++++++++ Code/Tools/ProjectManager/Source/Application.cpp | 7 ++++++- 11 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index d34abcbc6c..a47ccb62c9 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -34,6 +34,7 @@ ly_add_target( INCLUDE_DIRECTORIES PRIVATE Source + Platform/${PAL_PLATFORM_NAME} BUILD_DEPENDENCIES PRIVATE 3rdParty::Qt::Core diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake index 11222602d5..c3acd44f9b 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_linux.cpp ProjectUtils_linux.cpp ProjectManagerDefs_linux.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Linux.h ) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h new file mode 100644 index 0000000000..7c0543361f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/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 diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..97aee25507 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.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 diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake index 54b35f0d3c..6d4d453f21 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_mac.cpp ProjectUtils_mac.cpp ProjectManagerDefs_mac.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Mac.h ) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h new file mode 100644 index 0000000000..7c0543361f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/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 diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..dc77e77fd0 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.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 diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake index d95b0d2502..22b4614ddf 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_windows.cpp ProjectUtils_windows.cpp ProjectManagerDefs_windows.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Windows.h ) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..f5eac50dbc --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.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 diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h new file mode 100644 index 0000000000..e6422b5a77 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/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 diff --git a/Code/Tools/ProjectManager/Source/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp index a7e4805ce9..29e0df3c3a 100644 --- a/Code/Tools/ProjectManager/Source/Application.cpp +++ b/Code/Tools/ProjectManager/Source/Application.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -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 From 7ab0871327e3c51de99331a6ebd8893afb180524 Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Mon, 25 Oct 2021 19:16:29 -0700 Subject: [PATCH 12/14] Upload AP logs when there is an AP eeror (#4896) * Updating LFS config to new endpoint (#1624) Signed-off-by: AMZN-alexpete * Updates licenses to APACHE-2.0 OR MIT (#1685) Not to be committed before 7/6/2021 Signed-off-by: lawsonamzn <70027408+lawsonamzn@users.noreply.github.com> * Updating CONTRIBUTING.md Signed-off-by: Terry Michaels * Fixed typo Signed-off-by: Terry Michaels * Updated text to be more descriptive Signed-off-by: Terry Michaels * Upload AP logs when there is an AP eeror Signed-off-by: shiranj * Add AP log upload step for single step Asset jobs Signed-off-by: shiranj * Address more comments Signed-off-by: shiranj * Only call CreateUploadAPLogsStage() once in try/catch block Signed-off-by: shiranj * Revert acciental update to README.md Signed-off-by: shiranj Co-authored-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Co-authored-by: Chris Galvan Co-authored-by: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Co-authored-by: Terry Michaels --- scripts/build/Jenkins/Jenkinsfile | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 1388366661..f5e5edcf66 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -102,6 +102,10 @@ def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) { } } +def IsAPLogUpload(branchName, jobName) { + return !IsPullRequest(branchName) && jobName.toLowerCase().contains('asset') && env.AP_LOGS_S3_BUCKET +} + def GetRunningPipelineName(JENKINS_JOB_NAME) { // If the job name has an underscore def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_') @@ -431,6 +435,27 @@ def ExportTestScreenshots(Map options, String branchName, String platformName, S } } +def UploadAPLogs(Map options, String branchName, String jobName, String workspace, Map params) { + dir("${workspace}/${ENGINE_REPOSITORY_NAME}") { + projects = params.CMAKE_LY_PROJECTS.split(",") + projects.each{ project -> + def apLogsPath = "${project}/user/log" + def s3UploadScriptPath = "scripts/build/tools/upload_to_s3.py" + if(env.IS_UNIX) { + pythonPath = "${options.PYTHON_DIR}/python.sh" + } + else { + pythonPath = "${options.PYTHON_DIR}/python.cmd" + } + def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " + + "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " + + "--search_subdirectories True --key_prefix ${env.JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName}" + + "--extra-args {\"ACL\": \"bucket-owner-full-control\"}" + palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false) + } + } + } + def PostBuildCommonSteps(String workspace, boolean mount = true) { echo 'Starting post-build common steps...' @@ -494,6 +519,14 @@ def CreateExportTestScreenshotsStage(Map pipelineConfig, String branchName, Stri } } +def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String jobName, String workspace, Map params) { + return { + stage("${jobName}_upload_ap_logs") { + UploadAPLogs(pipelineConfig, branchName, jobName, workspace, params) + } + } +} + def CreateTeardownStage(Map environmentVars) { return { stage('Teardown') { @@ -543,6 +576,9 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node } } + if (IsAPLogUpload(branchName, build_job_name)) { + CreateUploadAPLogsStage(pipelineConfig, branchName, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call() + } // All other errors will be raised outside the retry block currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' currentException = e.toString() From 77d02ea657e4c381640f26931cdd3b83d38ac8fd Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Tue, 26 Oct 2021 01:59:02 -0700 Subject: [PATCH 13/14] Add some debug rendering options. (#4950) * [WIP] Adding rendering options Signed-off-by: rhhong * code review feedback. Also add the renderflag as qsettings. Signed-off-by: rhhong * fix broken test Signed-off-by: rhhong * fix linux build Signed-off-by: rhhong --- .../EMotionFXAtom/Assets/Icons/Resources.qrc | 1 + .../Assets/Icons/Visualization.svg | 9 + .../Code/Source/AtomActorDebugDraw.cpp | 415 ++++++++++++++++++ .../Code/Source/AtomActorDebugDraw.h | 56 +++ .../Code/Source/AtomActorInstance.cpp | 118 +---- .../Code/Source/AtomActorInstance.h | 11 +- .../Tools/EMStudio/AnimViewportRenderer.cpp | 14 + .../Tools/EMStudio/AnimViewportRenderer.h | 6 +- .../Tools/EMStudio/AnimViewportRequestBus.h | 5 +- .../Tools/EMStudio/AnimViewportToolBar.cpp | 126 ++++-- .../Code/Tools/EMStudio/AnimViewportToolBar.h | 11 + .../Tools/EMStudio/AnimViewportWidget.cpp | 46 +- .../Code/Tools/EMStudio/AnimViewportWidget.h | 8 + .../Code/Tools/EMStudio/AtomRenderPlugin.cpp | 10 +- .../Code/emotionfx_atom_files.cmake | 2 + .../Include/Integration/ActorComponentBus.h | 3 - .../Integration/Components/ActorComponent.cpp | 31 +- .../Integration/Components/ActorComponent.h | 5 +- .../Components/EditorActorComponent.cpp | 15 +- .../Editor/Components/EditorActorComponent.h | 3 + .../Rendering/RenderActorInstance.h | 12 +- .../Source/Integration/Rendering/RenderFlag.h | 44 ++ .../Code/Tests/RenderBackendManagerTests.cpp | 3 +- 23 files changed, 759 insertions(+), 195 deletions(-) create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h create mode 100644 Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc index 28ae322d5b..7924ef1c4e 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Resources.qrc @@ -1,5 +1,6 @@ Camera_category.svg + Visualization.svg diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg new file mode 100644 index 0000000000..3d1b40d1b6 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Assets/Icons/Visualization.svg @@ -0,0 +1,9 @@ + + + + Icons / System / View + Created with Sketch. + + + + diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp new file mode 100644 index 0000000000..eaaf04fcf2 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp @@ -0,0 +1,415 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::Render +{ + AtomActorDebugDraw::AtomActorDebugDraw(AZ::EntityId entityId) + { + m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); + } + + void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance) + { + if (!m_auxGeomFeatureProcessor || !instance) + { + return; + } + + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + if (!auxGeom) + { + return; + } + + // Render aabb + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB]) + { + RenderAABB(instance); + } + + // Render skeleton + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_LINESKELETON]) + { + RenderSkeleton(instance); + } + + // Render internal EMFX debug lines. + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_EMFX_DEBUG]) + { + RenderEMFXDebugDraw(instance); + } + + // Render vertex normal, face normal, tagent and wireframe. + const bool renderVertexNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS]; + const bool renderFaceNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS]; + const bool renderTangents = renderFlags[EMotionFX::ActorRenderFlag::RENDER_TANGENTS]; + const bool renderWireframe = renderFlags[EMotionFX::ActorRenderFlag::RENDER_WIREFRAME]; + + if (renderVertexNormals || renderFaceNormals || renderTangents || renderWireframe) + { + // Iterate through all enabled nodes + const EMotionFX::Pose* pose = instance->GetTransformData()->GetCurrentPose(); + const size_t geomLODLevel = instance->GetLODLevel(); + const size_t numEnabled = instance->GetNumEnabledNodes(); + for (size_t i = 0; i < numEnabled; ++i) + { + EMotionFX::Node* node = instance->GetActor()->GetSkeleton()->GetNode(instance->GetEnabledNode(i)); + EMotionFX::Mesh* mesh = instance->GetActor()->GetMesh(geomLODLevel, node->GetNodeIndex()); + const AZ::Transform globalTM = pose->GetWorldSpaceTransform(node->GetNodeIndex()).ToAZTransform(); + + m_currentMesh = nullptr; + + if (!mesh) + { + continue; + } + + RenderNormals(mesh, globalTM, renderVertexNormals, renderFaceNormals); + if (renderTangents) + { + RenderTangents(mesh, globalTM); + } + } + } + } + + void AtomActorDebugDraw::PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM) + { + // Check if we have already prepared for the given mesh + if (m_currentMesh == mesh) + { + return; + } + + // Set our new current mesh + m_currentMesh = mesh; + + // Get the number of vertices and the data + const uint32 numVertices = m_currentMesh->GetNumVertices(); + AZ::Vector3* positions = (AZ::Vector3*)m_currentMesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS); + + // Check if the vertices fits in our buffer + if (m_worldSpacePositions.size() < numVertices) + { + m_worldSpacePositions.resize(numVertices); + } + + // Pre-calculate the world space positions + for (uint32 i = 0; i < numVertices; ++i) + { + m_worldSpacePositions[i] = worldTM.TransformPoint(positions[i]); + } + } + + void AtomActorDebugDraw::RenderAABB(EMotionFX::ActorInstance* instance) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + const AZ::Aabb& aabb = instance->GetAabb(); + auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line); + } + + void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + + const EMotionFX::TransformData* transformData = instance->GetTransformData(); + const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton(); + const EMotionFX::Pose* pose = transformData->GetCurrentPose(); + + const size_t lodLevel = instance->GetLODLevel(); + const size_t numJoints = skeleton->GetNumNodes(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numJoints * 2); + + for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex) + { + const EMotionFX::Node* joint = skeleton->GetNode(jointIndex); + if (!joint->GetSkeletalLODStatus(lodLevel)) + { + continue; + } + + const size_t parentIndex = joint->GetParentIndex(); + if (parentIndex == InvalidIndex) + { + continue; + } + + const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position; + m_auxVertices.emplace_back(parentPos); + + const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position; + m_auxVertices.emplace_back(bonePos); + } + + const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f); + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = &skeletonColor; + lineArgs.m_colorCount = 1; + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + + void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + + EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw(); + debugDraw.Lock(); + EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(instance); + actorInstanceData->Lock(); + const AZStd::vector& lines = actorInstanceData->GetLines(); + if (lines.empty()) + { + actorInstanceData->Unlock(); + debugDraw.Unlock(); + return; + } + + m_auxVertices.clear(); + m_auxVertices.reserve(lines.size() * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines()) + { + m_auxVertices.emplace_back(line.m_start); + m_auxColors.emplace_back(line.m_startColor); + m_auxVertices.emplace_back(line.m_end); + m_auxColors.emplace_back(line.m_endColor); + } + + AZ_Assert(m_auxVertices.size() == m_auxColors.size(), "Number of vertices and number of colors need to match."); + actorInstanceData->Unlock(); + debugDraw.Unlock(); + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + + void AtomActorDebugDraw::RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals) + { + if (!mesh) + { + return; + } + + if (!vertexNormals && !faceNormals) + { + return; + } + + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + if (!auxGeom) + { + return; + } + + // TODO: Move line color to a render setting. + const float faceNormalsScale = 0.01f; + const AZ::Color colorFaceNormals = AZ::Colors::Lime; + const float vertexNormalsScale = 0.01f; + const AZ::Color colorVertexNormals = AZ::Colors::Orange; + + PrepareForMesh(mesh, worldTM); + + AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS); + + // Render face normals + if (faceNormals) + { + const size_t numSubMeshes = mesh->GetNumSubMeshes(); + for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex) + { + EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex); + const uint32 numTriangles = subMesh->GetNumPolygons(); + const uint32 startVertex = subMesh->GetStartVertex(); + const uint32* indices = subMesh->GetIndices(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numTriangles * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex) + { + const uint32 triangleStartIndex = triangleIndex * 3; + const uint32 indexA = indices[triangleStartIndex + 0] + startVertex; + const uint32 indexB = indices[triangleStartIndex + 1] + startVertex; + const uint32 indexC = indices[triangleStartIndex + 2] + startVertex; + + const AZ::Vector3& posA = m_worldSpacePositions[indexA]; + const AZ::Vector3& posB = m_worldSpacePositions[indexB]; + const AZ::Vector3& posC = m_worldSpacePositions[indexC]; + + const AZ::Vector3 normalDir = (posB - posA).Cross(posC - posA).GetNormalized(); + + // Calculate the center pos + const AZ::Vector3 normalPos = (posA + posB + posC) * (1.0f / 3.0f); + + m_auxVertices.emplace_back(normalPos); + m_auxColors.emplace_back(colorFaceNormals); + m_auxVertices.emplace_back(normalPos + (normalDir * faceNormalsScale)); + m_auxColors.emplace_back(colorFaceNormals); + } + } + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + + // render vertex normals + if (vertexNormals) + { + const size_t numSubMeshes = mesh->GetNumSubMeshes(); + for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex) + { + EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex); + const uint32 numVertices = subMesh->GetNumVertices(); + const uint32 startVertex = subMesh->GetStartVertex(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numVertices * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + for (uint32 j = 0; j < numVertices; ++j) + { + const uint32 vertexIndex = j + startVertex; + const AZ::Vector3& position = m_worldSpacePositions[vertexIndex]; + const AZ::Vector3 normal = worldTM.TransformVector(normals[vertexIndex]).GetNormalizedSafe() * vertexNormalsScale; + + m_auxVertices.emplace_back(position); + m_auxColors.emplace_back(colorFaceNormals); + m_auxVertices.emplace_back(position + normal); + m_auxColors.emplace_back(colorFaceNormals); + } + } + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } + } + + void AtomActorDebugDraw::RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM) + { + if (!mesh) + { + return; + } + + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + if (!auxGeom) + { + return; + } + + // TODO: Move line color to a render setting. + const AZ::Color colorTangents = AZ::Colors::Red; + const AZ::Color mirroredBitangentColor = AZ::Colors::Yellow; + const AZ::Color colorBitangents = AZ::Colors::White; + const float scale = 0.01f; + + // Get the tangents and check if this mesh actually has tangents + AZ::Vector4* tangents = static_cast(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_TANGENTS)); + if (!tangents) + { + return; + } + + AZ::Vector3* bitangents = static_cast(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_BITANGENTS)); + + PrepareForMesh(mesh, worldTM); + + AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS); + const uint32 numVertices = mesh->GetNumVertices(); + + m_auxVertices.clear(); + m_auxVertices.reserve(numVertices * 2); + m_auxColors.clear(); + m_auxColors.reserve(m_auxVertices.size()); + + // Render the tangents and bitangents + AZ::Vector3 orgTangent, tangent, bitangent; + for (uint32 i = 0; i < numVertices; ++i) + { + orgTangent.Set(tangents[i].GetX(), tangents[i].GetY(), tangents[i].GetZ()); + tangent = (worldTM.TransformVector(orgTangent)).GetNormalized(); + + if (bitangents) + { + bitangent = bitangents[i]; + } + else + { + bitangent = tangents[i].GetW() * normals[i].Cross(orgTangent); + } + bitangent = (worldTM.TransformVector(bitangent)).GetNormalizedSafe(); + + m_auxVertices.emplace_back(m_worldSpacePositions[i]); + m_auxColors.emplace_back(colorTangents); + m_auxVertices.emplace_back(m_worldSpacePositions[i] + (tangent * scale)); + m_auxColors.emplace_back(colorTangents); + + if (tangents[i].GetW() < 0.0f) + { + m_auxVertices.emplace_back(m_worldSpacePositions[i]); + m_auxColors.emplace_back(mirroredBitangentColor); + m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale)); + m_auxColors.emplace_back(mirroredBitangentColor); + } + else + { + m_auxVertices.emplace_back(m_worldSpacePositions[i]); + m_auxColors.emplace_back(colorBitangents); + m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale)); + m_auxColors.emplace_back(colorBitangents); + } + } + + RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; + lineArgs.m_verts = m_auxVertices.data(); + lineArgs.m_vertCount = static_cast(m_auxVertices.size()); + lineArgs.m_colors = m_auxColors.data(); + lineArgs.m_colorCount = static_cast(m_auxColors.size()); + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + auxGeom->DrawLines(lineArgs); + } +} // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h new file mode 100644 index 0000000000..9f8b137f13 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include +#include + +namespace EMotionFX +{ + class Mesh; + class ActorInstance; +} + +namespace AZ::RPI +{ + class AuxGeomDraw; + class AuxGeomFeatureProcessorInterface; +} + +namespace AZ::Render +{ + // Ultility class for atom debug render on actor + class AtomActorDebugDraw + { + public: + AtomActorDebugDraw(AZ::EntityId entityId); + + void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance); + + private: + + void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); + void RenderAABB(EMotionFX::ActorInstance* instance); + void RenderSkeleton(EMotionFX::ActorInstance* instance); + void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance); + void RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals); + void RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); + + EMotionFX::Mesh* m_currentMesh = nullptr; /**< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer. + NULL in case we haven't pre-calculated any positions yet. */ + AZStd::vector m_worldSpacePositions; /**< The buffer used to store world space positions for rendering normals + tangents and the wireframe. */ + + RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr; + AZStd::vector m_auxVertices; + AZStd::vector m_auxColors; + }; +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index a3161002a0..4d1b42a0eb 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -59,7 +60,7 @@ namespace AZ AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId); } - m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + m_atomActorDebugDraw = AZStd::make_unique(entityId); } AtomActorInstance::~AtomActorInstance() @@ -78,6 +79,11 @@ namespace AZ UpdateBounds(); } + void AtomActorInstance::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) + { + m_atomActorDebugDraw->DebugDraw(renderFlags, m_actorInstance); + } + void AtomActorInstance::UpdateBounds() { // Update RenderActorInstance world bounding box @@ -99,116 +105,6 @@ namespace AZ AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(m_entityId); } - void AtomActorInstance::DebugDraw(const DebugOptions& debugOptions) - { - if (m_auxGeomFeatureProcessor) - { - if (RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue()) - { - if (debugOptions.m_drawAABB) - { - const AZ::Aabb& aabb = m_actorInstance->GetAabb(); - auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line); - } - - if (debugOptions.m_drawSkeleton) - { - RenderSkeleton(auxGeom.get()); - } - - if (debugOptions.m_emfxDebugDraw) - { - RenderEMFXDebugDraw(auxGeom.get()); - } - } - } - } - - void AtomActorInstance::RenderSkeleton(RPI::AuxGeomDraw* auxGeom) - { - AZ_Assert(m_actorInstance, "Valid actor instance required."); - const EMotionFX::TransformData* transformData = m_actorInstance->GetTransformData(); - const EMotionFX::Skeleton* skeleton = m_actorInstance->GetActor()->GetSkeleton(); - const EMotionFX::Pose* pose = transformData->GetCurrentPose(); - - const size_t lodLevel = m_actorInstance->GetLODLevel(); - const size_t numJoints = skeleton->GetNumNodes(); - - m_auxVertices.clear(); - m_auxVertices.reserve(numJoints * 2); - - for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex) - { - const EMotionFX::Node* joint = skeleton->GetNode(jointIndex); - if (!joint->GetSkeletalLODStatus(lodLevel)) - { - continue; - } - - const size_t parentIndex = joint->GetParentIndex(); - if (parentIndex == InvalidIndex) - { - continue; - } - - const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position; - m_auxVertices.emplace_back(parentPos); - - const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position; - m_auxVertices.emplace_back(bonePos); - } - - const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f); - RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; - lineArgs.m_verts = m_auxVertices.data(); - lineArgs.m_vertCount = static_cast(m_auxVertices.size()); - lineArgs.m_colors = &skeletonColor; - lineArgs.m_colorCount = 1; - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; - auxGeom->DrawLines(lineArgs); - } - - void AtomActorInstance::RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom) - { - EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw(); - debugDraw.Lock(); - EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(m_actorInstance); - actorInstanceData->Lock(); - const AZStd::vector& lines = actorInstanceData->GetLines(); - if (lines.empty()) - { - actorInstanceData->Unlock(); - debugDraw.Unlock(); - return; - } - - m_auxVertices.clear(); - m_auxVertices.reserve(lines.size() * 2); - m_auxColors.clear(); - m_auxColors.reserve(m_auxVertices.size()); - - for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines()) - { - m_auxVertices.emplace_back(line.m_start); - m_auxColors.emplace_back(line.m_startColor); - m_auxVertices.emplace_back(line.m_end); - m_auxColors.emplace_back(line.m_endColor); - } - - AZ_Assert(m_auxVertices.size() == m_auxColors.size(), - "Number of vertices and number of colors need to match."); - actorInstanceData->Unlock(); - debugDraw.Unlock(); - - RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; - lineArgs.m_verts = m_auxVertices.data(); - lineArgs.m_vertCount = static_cast(m_auxVertices.size()); - lineArgs.m_colors = m_auxColors.data(); - lineArgs.m_colorCount = static_cast(m_auxColors.size()); - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; - auxGeom->DrawLines(lineArgs); - } - AZ::Aabb AtomActorInstance::GetWorldBounds() { return m_worldAABB; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h index 5ddab8bc61..7f646466a5 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h @@ -53,6 +53,7 @@ namespace AZ class SkinnedMeshInputBuffers; class MeshFeatureProcessorInterface; class AtomActor; + class AtomActorDebugDraw; //! Render node for managing and rendering actor instances. Each Actor Component //! creates an ActorRenderNode. The render node is responsible for drawing meshes and @@ -85,8 +86,8 @@ namespace AZ // RenderActorInstance overrides ... void OnTick(float timeDelta) override; + void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags); void UpdateBounds() override; - void DebugDraw(const DebugOptions& debugOptions) override; void SetMaterials(const EMotionFX::Integration::ActorAsset::MaterialList& materialPerLOD) override { AZ_UNUSED(materialPerLOD); }; void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod) override; SkinningMethod GetAtomSkinningMethod() const; @@ -184,12 +185,8 @@ namespace AZ void InitWrinkleMasks(); void UpdateWrinkleMasks(); - // Helper and debug geometry rendering - void RenderSkeleton(RPI::AuxGeomDraw* auxGeom); - void RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom); - RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr; - AZStd::vector m_auxVertices; - AZStd::vector m_auxColors; + // Debug geometry rendering + AZStd::unique_ptr m_atomActorDebugDraw; AZStd::intrusive_ptr m_skinnedMeshInputBuffers = nullptr; AZStd::intrusive_ptr m_skinnedMeshInstance; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index faa033956a..f6186be235 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -206,6 +206,20 @@ namespace EMStudio return result; } + void AnimViewportRenderer::UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags) + { + for (AZ::Entity* entity : m_actorEntities) + { + EMotionFX::Integration::ActorComponent* actorComponent = entity->FindComponent(); + if (!actorComponent) + { + AZ_Assert(false, "Found entity without actor component in the actor entity list."); + continue; + } + actorComponent->SetRenderFlag(renderFlags); + } + } + void AnimViewportRenderer::ResetEnvironment() { // Reset environment diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h index 1de4cbdb1c..a4f67ddfd1 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h @@ -52,6 +52,8 @@ namespace EMStudio //! Return the center position of the existing objects. AZ::Vector3 GetCharacterCenter() const; + void UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags); + private: // This function resets the light, camera and other environment settings. @@ -79,10 +81,6 @@ namespace EMStudio AZ::Entity* m_postProcessEntity = nullptr; AZ::Entity* m_iblEntity = nullptr; - AZ::Entity* m_cameraEntity = nullptr; - AZ::Component* m_cameraComponent = nullptr; - AZ::Entity* m_modelEntity = nullptr; - AZ::Data::AssetId m_modelAssetId; AZ::Entity* m_gridEntity = nullptr; AZStd::vector m_actorEntities; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h index 03784c2158..da4a054c53 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h @@ -8,7 +8,7 @@ #pragma once #include - +#include namespace EMStudio { @@ -35,6 +35,9 @@ namespace EMStudio //! Set the camera view mode. virtual void SetCameraViewMode(CameraViewMode mode) = 0; + + //! Toggle render option flag + virtual void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) = 0; }; using AnimViewportRequestBus = AZ::EBus; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp index a47a773e10..50cd088f5d 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp @@ -13,7 +13,6 @@ #include #include - namespace EMStudio { AnimViewportToolBar::AnimViewportToolBar(QWidget* parent) @@ -21,40 +20,107 @@ namespace EMStudio { AzQtComponents::ToolBar::addMainToolBarStyle(this); - // Add the camera button - QToolButton* cameraButton = new QToolButton(this); - QMenu* cameraMenu = new QMenu(cameraButton); - - // Add the camera option - const AZStd::vector> cameraOptionNames = { - { CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" }, - { CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" }, - }; - - for (const auto& pair : cameraOptionNames) + // Add the render view options button + QToolButton* renderOptionsButton = new QToolButton(this); { - CameraViewMode mode = pair.first; - cameraMenu->addAction( - pair.second.c_str(), - [mode]() - { - // Send the reset camera event. - AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode); - }); + QMenu* contextMenu = new QMenu(renderOptionsButton); + + renderOptionsButton->setText("Render Options"); + renderOptionsButton->setMenu(contextMenu); + renderOptionsButton->setPopupMode(QToolButton::InstantPopup); + renderOptionsButton->setVisible(true); + renderOptionsButton->setIcon(QIcon(":/EMotionFXAtom/Visualization.svg")); + addWidget(renderOptionsButton); + + CreateViewOptionEntry(contextMenu, "Solid", EMotionFX::ActorRenderFlag::RENDER_SOLID); + CreateViewOptionEntry(contextMenu, "Wireframe", EMotionFX::ActorRenderFlag::RENDER_WIREFRAME); + CreateViewOptionEntry(contextMenu, "Lighting", EMotionFX::ActorRenderFlag::RENDER_LIGHTING); + CreateViewOptionEntry(contextMenu, "Backface Culling", EMotionFX::ActorRenderFlag::RENDER_BACKFACECULLING); + contextMenu->addSeparator(); + CreateViewOptionEntry(contextMenu, "Vertex Normals", EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS); + CreateViewOptionEntry(contextMenu, "Face Normals", EMotionFX::ActorRenderFlag::RENDER_FACENORMALS); + CreateViewOptionEntry(contextMenu, "Tangents", EMotionFX::ActorRenderFlag::RENDER_TANGENTS); + CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", EMotionFX::ActorRenderFlag::RENDER_AABB); + contextMenu->addSeparator(); + CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlag::RENDER_LINESKELETON); + CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlag::RENDER_SKELETON); + CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlag::RENDER_NODENAMES); + CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION); + CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE); + contextMenu->addSeparator(); } - cameraMenu->addSeparator(); - cameraMenu->addAction("Reset Camera", - []() + // Add the camera button + QToolButton* cameraButton = new QToolButton(this); + { + QMenu* cameraMenu = new QMenu(cameraButton); + + // Add the camera option + const AZStd::vector> cameraOptionNames = { + { CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" }, + { CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" }, + }; + + for (const auto& pair : cameraOptionNames) + { + CameraViewMode mode = pair.first; + cameraMenu->addAction( + pair.second.c_str(), + [mode]() + { + // Send the reset camera event. + AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode); + }); + } + + cameraMenu->addSeparator(); + cameraMenu->addAction( + "Reset Camera", + []() + { + // Send the reset camera event. + AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera); + }); + cameraButton->setMenu(cameraMenu); + cameraButton->setText("Camera Option"); + cameraButton->setPopupMode(QToolButton::InstantPopup); + cameraButton->setVisible(true); + cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg")); + addWidget(cameraButton); + } + } + + void AnimViewportToolBar::CreateViewOptionEntry( + QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible, char* iconFileName) + { + QAction* action = menu->addAction( + menuEntryName, + [actionIndex]() { // Send the reset camera event. - AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera); + AnimViewportRequestBus::Broadcast( + &AnimViewportRequestBus::Events::ToggleRenderFlag, (EMotionFX::ActorRenderFlag)actionIndex); }); - cameraButton->setMenu(cameraMenu); - cameraButton->setText("Camera Option"); - cameraButton->setPopupMode(QToolButton::InstantPopup); - cameraButton->setVisible(true); - cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg")); - addWidget(cameraButton); + action->setCheckable(true); + action->setVisible(visible); + + if (iconFileName) + { + action->setIcon(QIcon(iconFileName)); + } + + m_actions[actionIndex] = action; + } + + void AnimViewportToolBar::SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags) + { + for (size_t i = 0; i < renderFlags.size(); ++i) + { + QAction* action = m_actions[i]; + if (action) + { + action->setChecked(renderFlags[i]); + } + } } } // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h index 23ef5fdcd8..57633e5284 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h @@ -11,8 +11,11 @@ #if !defined(Q_MOC_RUN) #include #include +#include #endif +#include + namespace EMStudio { class AnimViewportToolBar : public QToolBar @@ -20,5 +23,13 @@ namespace EMStudio public: AnimViewportToolBar(QWidget* parent = nullptr); ~AnimViewportToolBar() = default; + + void SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags); + + private: + void CreateViewOptionEntry( + QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible = true, char* iconFileName = nullptr); + + QAction* m_actions[EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS] = { nullptr }; }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index 2e05864adc..7af5c1607a 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ namespace EMStudio m_renderer = AZStd::make_unique(GetViewportContext()); + LoadRenderFlags(); SetupCameras(); SetupCameraController(); Reinit(); @@ -41,6 +43,7 @@ namespace EMStudio AnimViewportWidget::~AnimViewportWidget() { + SaveRenderFlags(); AnimViewportRequestBus::Handler::BusDisconnect(); } @@ -50,7 +53,14 @@ namespace EMStudio { ResetCamera(); } + m_renderer->Reinit(); + m_renderer->UpdateActorRenderFlag(m_renderFlags); + } + + EMotionFX::ActorRenderFlagBitset AnimViewportWidget::GetRenderFlags() const + { + return m_renderFlags; } void AnimViewportWidget::SetupCameras() @@ -123,7 +133,7 @@ namespace EMStudio SetCameraViewMode(CameraViewMode::DEFAULT); } - void AnimViewportWidget::SetCameraViewMode([[maybe_unused]]CameraViewMode mode) + void AnimViewportWidget::SetCameraViewMode(CameraViewMode mode) { // Set the camera view mode. const AZ::Vector3 targetPosition = m_renderer->GetCharacterCenter(); @@ -155,4 +165,38 @@ namespace EMStudio } GetViewportContext()->SetCameraTransform(AZ::Transform::CreateLookAt(cameraPosition, targetPosition)); } + + void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) + { + m_renderFlags[flag] = !m_renderFlags[flag]; + m_renderer->UpdateActorRenderFlag(m_renderFlags); + } + + void AnimViewportWidget::LoadRenderFlags() + { + AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder()); + renderFlagsFilename += "AnimViewportRenderFlags.cfg"; + QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this); + + for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i) + { + QString name = QString(i); + const bool isEnabled = settings.value(name).toBool(); + m_renderFlags[i] = isEnabled; + } + m_renderer->UpdateActorRenderFlag(m_renderFlags); + } + + void AnimViewportWidget::SaveRenderFlags() + { + AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder()); + renderFlagsFilename += "AnimViewportRenderFlags.cfg"; + QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this); + + for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i) + { + QString name = QString(i); + settings.setValue(name, (bool)m_renderFlags[i]); + } + } } // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 6d708b0996..8aa316a8ba 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -7,9 +7,11 @@ */ #pragma once +#include #include #include #include +#include namespace EMStudio { @@ -25,14 +27,19 @@ namespace EMStudio AnimViewportRenderer* GetAnimViewportRenderer() { return m_renderer.get(); } void Reinit(bool resetCamera = true); + EMotionFX::ActorRenderFlagBitset GetRenderFlags() const; private: void SetupCameras(); void SetupCameraController(); + void LoadRenderFlags(); + void SaveRenderFlags(); + // AnimViewportRequestBus::Handler overrides void ResetCamera(); void SetCameraViewMode(CameraViewMode mode); + void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag); static constexpr float CameraDistance = 2.0f; @@ -40,5 +47,6 @@ namespace EMStudio AZStd::shared_ptr m_rotateCamera; AZStd::shared_ptr m_translateCamera; AZStd::shared_ptr m_orbitDollyScrollCamera; + EMotionFX::ActorRenderFlagBitset m_renderFlags; }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp index bc74592485..ce2e76a6c7 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp @@ -90,12 +90,14 @@ namespace EMStudio verticalLayout->setSpacing(1); verticalLayout->setMargin(0); - // Add the tool bar - AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget); - verticalLayout->addWidget(toolBar); - // Add the viewport widget m_animViewportWidget = new AnimViewportWidget(m_innerWidget); + + // Add the tool bar + AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget); + toolBar->SetRenderFlags(m_animViewportWidget->GetRenderFlags()); + + verticalLayout->addWidget(toolBar); verticalLayout->addWidget(m_animViewportWidget); // Register command callbacks. diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake index 4ada953caf..413984b96d 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_files.cmake @@ -17,4 +17,6 @@ set(FILES Source/AtomActor.cpp Source/AtomActorInstance.h Source/AtomActorInstance.cpp + Source/AtomActorDebugDraw.h + Source/AtomActorDebugDraw.cpp ) diff --git a/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h b/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h index 4161b3c77d..1fd3e55f12 100644 --- a/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h +++ b/Gems/EMotionFX/Code/Include/Integration/ActorComponentBus.h @@ -85,9 +85,6 @@ namespace EMotionFX /// Detach from parent entity, if attached. virtual void DetachFromEntity() {} - /// Enables debug-drawing of the actor's root. - virtual void DebugDrawRoot(bool /*enable*/) {} - /// Enables rendering of the actor. virtual bool GetRenderCharacter() const = 0; virtual void SetRenderCharacter(bool enable) = 0; diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index 206cae3f59..8520896dd2 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -209,7 +209,6 @@ namespace EMotionFX ->Event("GetJointTransform", &ActorComponentRequestBus::Events::GetJointTransform) ->Event("AttachToEntity", &ActorComponentRequestBus::Events::AttachToEntity) ->Event("DetachFromEntity", &ActorComponentRequestBus::Events::DetachFromEntity) - ->Event("DebugDrawRoot", &ActorComponentRequestBus::Events::DebugDrawRoot) ->Event("GetRenderCharacter", &ActorComponentRequestBus::Events::GetRenderCharacter) ->Event("SetRenderCharacter", &ActorComponentRequestBus::Events::SetRenderCharacter) ->Event("GetRenderActorVisible", &ActorComponentRequestBus::Events::GetRenderActorVisible) @@ -238,8 +237,7 @@ namespace EMotionFX ////////////////////////////////////////////////////////////////////////// ActorComponent::ActorComponent(const Configuration* configuration) - : m_debugDrawRoot(false) - , m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, + : m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, float fixedDeltatime) { if (m_actorInstance) @@ -252,6 +250,8 @@ namespace EMotionFX { m_configuration = *configuration; } + + m_debugRenderFlags[RENDER_SOLID] = true; } ////////////////////////////////////////////////////////////////////////// @@ -341,12 +341,6 @@ namespace EMotionFX } } - ////////////////////////////////////////////////////////////////////////// - void ActorComponent::DebugDrawRoot(bool enable) - { - m_debugDrawRoot = enable; - } - ////////////////////////////////////////////////////////////////////////// bool ActorComponent::GetRenderCharacter() const { @@ -400,6 +394,11 @@ namespace EMotionFX return m_sceneFinishSimHandler.IsConnected(); } + void ActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags) + { + m_debugRenderFlags = renderFlags; + } + void ActorComponent::CheckActorCreation() { // Create actor instance. @@ -573,13 +572,13 @@ namespace EMotionFX m_actorInstance->SetIsVisible(isInCameraFrustum && m_configuration.m_renderCharacter); } - RenderActorInstance::DebugOptions debugOptions; - debugOptions.m_drawAABB = m_configuration.m_renderBounds; - debugOptions.m_drawSkeleton = m_configuration.m_renderSkeleton; - debugOptions.m_drawRootTransform = m_debugDrawRoot; - debugOptions.m_rootWorldTransform = GetEntity()->GetTransform()->GetWorldTM(); - debugOptions.m_emfxDebugDraw = true; - m_renderActorInstance->DebugDraw(debugOptions); + m_renderActorInstance->SetIsVisible(m_debugRenderFlags[RENDER_SOLID]); + + // The configuration stores some debug option. When that is enabled, we override it on top of the render flags. + m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds; + m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton; + m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; + m_renderActorInstance->DebugDraw(m_debugRenderFlags); } } diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 15d9736f34..9eecea2f54 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -116,7 +116,6 @@ namespace EMotionFX ActorInstance* GetActorInstance() override { return m_actorInstance.get(); } void AttachToEntity(AZ::EntityId targetEntityId, AttachmentType attachmentType) override; void DetachFromEntity() override; - void DebugDrawRoot(bool enable) override; bool GetRenderCharacter() const override; void SetRenderCharacter(bool enable) override; bool GetRenderActorVisible() const override; @@ -181,6 +180,8 @@ namespace EMotionFX bool IsPhysicsSceneSimulationFinishEventConnected() const; AZ::Data::Asset GetActorAsset() const { return m_configuration.m_actorAsset; } + void SetRenderFlag(ActorRenderFlagBitset renderFlags); + private: // AZ::TransformNotificationBus::MultiHandler void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; @@ -201,7 +202,7 @@ namespace EMotionFX AZStd::vector m_attachments; AZStd::unique_ptr m_renderActorInstance; - bool m_debugDrawRoot; ///< Enables drawing of actor root and facing. + ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_sceneFinishSimHandler; }; diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 5da9716d15..d0274b3c68 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -180,6 +180,7 @@ namespace EMotionFX , m_lodLevel(0) , m_actorAsset(AZ::Data::AssetLoadBehavior::NoLoad) { + m_debugRenderFlags[RENDER_SOLID] = true; } ////////////////////////////////////////////////////////////////////////// @@ -604,11 +605,10 @@ namespace EMotionFX m_renderActorInstance->OnTick(deltaTime); m_renderActorInstance->UpdateBounds(); - RenderActorInstance::DebugOptions debugOptions; - debugOptions.m_drawAABB = m_renderBounds; - debugOptions.m_drawSkeleton = m_renderSkeleton; - debugOptions.m_emfxDebugDraw = true; - m_renderActorInstance->DebugDraw(debugOptions); + m_debugRenderFlags[RENDER_AABB] = m_renderBounds; + m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton; + m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; + m_renderActorInstance->DebugDraw(m_debugRenderFlags); } } @@ -951,5 +951,10 @@ namespace EMotionFX LmbrCentral::AttachmentComponentRequestBus::Event(attachment, &LmbrCentral::AttachmentComponentRequestBus::Events::Reattach, true); } } + + void EditorActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags) + { + m_debugRenderFlags = renderFlags; + } } //namespace Integration } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h index 1d682b47d4..f4c663a92f 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h @@ -104,6 +104,8 @@ namespace EMotionFX ActorComponent::GetRequiredServices(required); } + void SetRenderFlag(ActorRenderFlagBitset renderFlags); + static void Reflect(AZ::ReflectContext* context); private: @@ -162,6 +164,7 @@ namespace EMotionFX size_t m_lodLevel; ActorComponent::BoundingBoxConfiguration m_bboxConfig; bool m_forceUpdateJointsOOV = false; + ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag // \todo attachmentTarget node nr // Note: LOD work in progress. For now we use one material instead of a list of material, because we don't have the support for LOD with multiple scene files. diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h index 47da0d4cca..8afb2a2f9a 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h @@ -16,6 +16,7 @@ #include #include +#include namespace EMotionFX { @@ -33,16 +34,7 @@ namespace EMotionFX virtual ~RenderActorInstance() = default; virtual void OnTick(float timeDelta) = 0; - - struct DebugOptions - { - bool m_drawAABB = false; - bool m_drawSkeleton = false; - bool m_drawRootTransform = false; - AZ::Transform m_rootWorldTransform = AZ::Transform::CreateIdentity(); - bool m_emfxDebugDraw = false; - }; - virtual void DebugDraw(const DebugOptions& debugOptions) = 0; + virtual void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) = 0; SkinningMethod GetSkinningMethod() const; virtual void SetSkinningMethod(SkinningMethod skinningMethod); diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h new file mode 100644 index 0000000000..e053eae6d5 --- /dev/null +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h @@ -0,0 +1,44 @@ +/* + * 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 + +namespace EMotionFX +{ + enum ActorRenderFlag + { + RENDER_SOLID = 0, + RENDER_WIREFRAME = 1, + RENDER_LIGHTING = 2, + RENDER_SHADOWS = 3, + RENDER_FACENORMALS = 4, + RENDER_VERTEXNORMALS = 5, + RENDER_TANGENTS = 6, + RENDER_AABB = 7, + RENDER_SKELETON = 8, + RENDER_LINESKELETON = 9, + RENDER_NODEORIENTATION = 10, + RENDER_NODENAMES = 11, + RENDER_GRID = 12, + RENDER_BACKFACECULLING = 13, + RENDER_ACTORBINDPOSE = 14, + RENDER_RAGDOLL_COLLIDERS = 15, + RENDER_RAGDOLL_JOINTLIMITS = 16, + RENDER_HITDETECTION_COLLIDERS = 17, + RENDER_USE_GRADIENTBACKGROUND = 18, + RENDER_MOTIONEXTRACTION = 19, + RENDER_CLOTH_COLLIDERS = 20, + RENDER_SIMULATEDOBJECT_COLLIDERS = 21, + RENDER_SIMULATEJOINTS = 22, + RENDER_EMFX_DEBUG = 23, + NUM_RENDERFLAGS = 24 + }; + + using ActorRenderFlagBitset = AZStd::bitset; +} diff --git a/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp b/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp index 89d6fab93b..be990f444c 100644 --- a/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp +++ b/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ namespace EMotionFX } MOCK_METHOD1(OnTick, void(float)); - MOCK_METHOD1(DebugDraw, void(const DebugOptions&)); + MOCK_METHOD1(DebugDraw, void(const EMotionFX::ActorRenderFlagBitset&)); MOCK_CONST_METHOD0(IsVisible, bool()); MOCK_METHOD1(SetIsVisible, void(bool)); MOCK_METHOD1(SetMaterials, void(const ActorAsset::MaterialList&)); From 5374860444e406b798cb8ad7f9f2ce0270dc5438 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 26 Oct 2021 02:29:12 -0700 Subject: [PATCH 14/14] Bug Fix: resolve entity ordering for EntityOutliner (#4798) (#4938) * bugifx: resolve dragging behaviour for EntityOutliner (#4798) Signed-off-by: Michael Pollind * chore: cleanup and rework logic Signed-off-by: Michael Pollind --- .../UI/Outliner/EntityOutlinerListModel.cpp | 50 ++++++++++++------- .../UI/Outliner/EntityOutlinerListModel.hxx | 9 +++- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index a5f1e29942..434a1d8303 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 8176867038..0a46ee4850 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -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();