浏览代码

Asset processor intermediate assets UI (#10545)

* WIP UI

Signed-off-by: AMZN-stankowi <[email protected]>

* Product list fixed

Signed-off-by: AMZN-stankowi <[email protected]>

* dependencies added to tabbed view

Signed-off-by: AMZN-stankowi <[email protected]>

* hide panels for folders

Signed-off-by: AMZN-stankowi <[email protected]>

* hide tabs that have nothing in them. scrollbars on dependencies

Signed-off-by: AMZN-stankowi <[email protected]>

* First set of intermediate asset UI

Signed-off-by: AMZN-stankowi <[email protected]>

* work in progress on intermediate assets UI.
Added intermediate asset tab, added goto button.
Remaining: Fix goto button logic for source versus intermediate. Clean up how the code checks for what is an intermediate asset.

Signed-off-by: AMZN-stankowi <[email protected]>

* work in progress hooking up goto buttons for intermediate assets

Signed-off-by: AMZN-stankowi <[email protected]>

* Updated logic for detecting intermediate assets to be more correct

Signed-off-by: AMZN-stankowi <[email protected]>

* ID -> Id

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed goto for intermediate assets

Signed-off-by: AMZN-stankowi <[email protected]>

* Right click context menus for intermediate assets

Signed-off-by: AMZN-stankowi <[email protected]>

* pull request feedback: limited lambda capture

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed crash on right click -> view job for source assets

Signed-off-by: AMZN-stankowi <[email protected]>

* Caching the intermediate asset folder ID

Signed-off-by: AMZN-stankowi <[email protected]>

* Pull request feedback:
StripAssetPlatformNoCopy -> StripAssetPlatform
Checked product flag for intermediate asset instead of the platform's name
Made a tooltip say "product assets" instead of "product asset"
GetProductPathForIntermediateSourcePath -> GetRelativeProductPathForIntermediateSourcePath
Comment on GetIntermediateAssetScanFolderId explaining why the return is optional

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed bad merge

Signed-off-by: AMZN-stankowi <[email protected]>

* pull request feedback: Cleaned up some spacing in mainwindow.cpp, cleaned up SetDetailsVisible call

Signed-off-by: AMZN-stankowi <[email protected]>

* fixed a typo

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed crash on flipping intermediate asset flag on the panel incorrectly

Signed-off-by: AMZN-stankowi <[email protected]>
AMZN-stankowi 3 年之前
父节点
当前提交
49b229b74e

+ 6 - 0
Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp

@@ -21,6 +21,7 @@
 #include <AzCore/Component/ComponentApplicationBus.h>
 #include <AzCore/Slice/SliceAsset.h> // For slice asset sub ids
 #include <AzCore/RTTI/BehaviorContext.h>
+#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
 //////////////////////////////////////////////////////////////////////////
 
 #include <xxhash/xxhash.h>
@@ -608,6 +609,11 @@ namespace AssetBuilderSDK
 
     }
 
+    bool IsProductOutputFlagSet(const AzToolsFramework::AssetDatabase::ProductDatabaseEntry& product, ProductOutputFlags flag)
+    {
+        return (static_cast<AssetBuilderSDK::ProductOutputFlags>(product.m_flags.to_ullong()) & flag) == flag;
+    }
+
     ProductPathDependency::ProductPathDependency(AZStd::string_view dependencyPath, ProductPathDependencyType dependencyType)
         : m_dependencyPath(dependencyPath),
         m_dependencyType(dependencyType)

+ 10 - 0
Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h

@@ -38,6 +38,14 @@ namespace AZ
     class Entity;
 }
 
+namespace AzToolsFramework
+{
+    namespace AssetDatabase
+    {
+        class ProductDatabaseEntry;
+    }
+}
+
 // This needs to be up here because it needs to be defined before the hash definition, and the hash needs to be defined before the first use (which occurs further down in this file)
 namespace AssetBuilderSDK
 {
@@ -638,6 +646,8 @@ namespace AssetBuilderSDK
         IntermediateAsset = 2 // Indicates this JobProduct is an intermediate asset which should be output to the intermediate asset folder.  Must be used with the Common platform.
     };
 
+    bool IsProductOutputFlagSet(const AzToolsFramework::AssetDatabase::ProductDatabaseEntry& product, ProductOutputFlags flag);
+
     AZ_DEFINE_ENUM_BITWISE_OPERATORS(ProductOutputFlags);
 
     using ProductPathDependencySet = AZStd::unordered_set<AssetBuilderSDK::ProductPathDependency>;

+ 9 - 0
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp

@@ -4659,6 +4659,15 @@ namespace AssetProcessor
         }
     }
 
+    AZStd::optional<AZ::s64> AssetProcessorManager::GetIntermediateAssetScanFolderId() const
+    {
+        if (!m_platformConfig)
+        {
+            return AZStd::nullopt;
+        }
+        return m_platformConfig->GetIntermediateAssetsScanFolderId();
+    }
+
     void AssetProcessorManager::CheckAssetProcessorIdleState()
     {
         Q_EMIT AssetProcessorManagerIdleState(IsIdle());

+ 8 - 0
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h

@@ -224,6 +224,14 @@ namespace AssetProcessor
         //! Request to invalidate and reprocess a source asset or folder containing source assets
         AZ::u64 RequestReprocess(const QString& sourcePath);
         AZ::u64 RequestReprocess(const AZStd::list<AZStd::string>& reprocessList);
+
+        //! Retrieves the scan folder ID for the intermediate asset scan folder, if available.
+        //! Calls GetIntermediateAssetsScanFolderId for the platform config, which returns an optional.
+        //! If the scan folder ID is not available, returns nullopt, otherwise returns the scan folder ID.
+        //! The scan folder ID may not be available if the platform config is not available,
+        //! or the scan folder ID hasn't been set for the platform config.
+        AZStd::optional<AZ::s64> GetIntermediateAssetScanFolderId() const;
+
     Q_SIGNALS:
         void NumRemainingJobsChanged(int newNumJobs);
 

+ 42 - 7
Code/Tools/AssetProcessor/native/ui/AssetDetailsPanel.cpp

@@ -29,6 +29,9 @@ namespace AssetProcessor
         QTreeView* sourceTreeView,
         SourceAssetTreeModel* sourceAssetTreeModel,
         AssetTreeFilterModel* sourceFilterModel,
+        QTreeView* intermediateTreeView,
+        SourceAssetTreeModel* intermediateAssetTreeModel,
+        AssetTreeFilterModel* intermediateFilterModel,
         QTreeView* productTreeView,
         ProductAssetTreeModel* productAssetTreeModel,
         AssetTreeFilterModel* productFilterModel,
@@ -37,6 +40,11 @@ namespace AssetProcessor
         m_sourceTreeView = sourceTreeView;
         m_sourceTreeModel = sourceAssetTreeModel;
         m_sourceFilterModel = sourceFilterModel;
+
+        m_intermediateTreeView = intermediateTreeView;
+        m_intermediateTreeModel = intermediateAssetTreeModel;
+        m_intermediateFilterModel = intermediateFilterModel;
+
         m_productTreeView = productTreeView;
         m_productTreeModel = productAssetTreeModel;
         m_productFilterModel = productFilterModel;
@@ -45,22 +53,49 @@ namespace AssetProcessor
 
     void AssetDetailsPanel::GoToSource(const AZStd::string& source)
     {
-        if (!m_sourceTreeModel || !m_sourceTreeView || !m_assetsTab || !m_sourceFilterModel)
+        if (!m_sourceTreeModel || !m_sourceTreeView || !m_assetsTab || !m_sourceFilterModel ||
+            !m_intermediateTreeView || !m_intermediateTreeModel || !m_intermediateFilterModel)
         {
             return;
         }
-        m_assetsTab->setCurrentIndex(static_cast<int>(MainWindow::AssetTabIndex::Source));
-        QModelIndex goToIndex = m_sourceTreeModel->GetIndexForSource(source);
+        
+        AssetDatabaseConnection assetDatabaseConnection;
+        assetDatabaseConnection.OpenDatabase();
+        
+        AzToolsFramework::AssetDatabase::SourceDatabaseEntry sourceDetails;
+        assetDatabaseConnection.QuerySourceBySourceName(
+            source.c_str(),
+            [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
+            {
+                sourceDetails = sourceEntry;
+                return false;
+            });
+
+        bool isIntermediate = false;
+        if(m_intermediateAssetFolderId.has_value())
+        {
+            isIntermediate = sourceDetails.m_scanFolderPK == m_intermediateAssetFolderId.value();
+        }
+
+        int assetTabIndex = static_cast<int>(
+            isIntermediate ? MainWindow::AssetTabIndex::Intermediate : MainWindow::AssetTabIndex::Source);
+        m_assetsTab->setCurrentIndex(assetTabIndex);
+
+        QTreeView* treeView = isIntermediate ? m_intermediateTreeView : m_sourceTreeView;
+        SourceAssetTreeModel* treeModel = isIntermediate ? m_intermediateTreeModel : m_sourceTreeModel;
+        AssetTreeFilterModel* filterModel = isIntermediate ? m_intermediateFilterModel : m_sourceFilterModel;
+
+        QModelIndex goToIndex = treeModel->GetIndexForSource(source);
         // Make sure this index is visible, even if a search is active.
-        m_sourceFilterModel->ForceModelIndexVisible(goToIndex);
-        QModelIndex filterIndex = m_sourceFilterModel->mapFromSource(goToIndex);
+        filterModel->ForceModelIndexVisible(goToIndex);
+        QModelIndex filterIndex = filterModel->mapFromSource(goToIndex);
         // Some tables, like the source dependencies table, may have wildcards or links to files that don't exist.
         if (!filterIndex.isValid())
         {
             return;
         }
-        m_sourceTreeView->scrollTo(filterIndex, QAbstractItemView::ScrollHint::EnsureVisible);
-        m_sourceTreeView->selectionModel()->setCurrentIndex(filterIndex, AssetTreeModel::GetAssetTreeSelectionFlags());
+        treeView->scrollTo(filterIndex, QAbstractItemView::ScrollHint::EnsureVisible);
+        treeView->selectionModel()->setCurrentIndex(filterIndex, AssetTreeModel::GetAssetTreeSelectionFlags());
     }
 
     void AssetDetailsPanel::GoToProduct(const AZStd::string& product)

+ 15 - 0
Code/Tools/AssetProcessor/native/ui/AssetDetailsPanel.h

@@ -38,6 +38,9 @@ namespace AssetProcessor
             QTreeView* sourceTreeView,
             SourceAssetTreeModel* sourceAssetTreeModel,
             AssetTreeFilterModel* sourceFilterModel,
+            QTreeView* intermediateTreeView,
+            SourceAssetTreeModel* intermediateAssetTreeModel,
+            AssetTreeFilterModel* intermediateFilterModel,
             QTreeView* productTreeView,
             ProductAssetTreeModel* productAssetTreeModel,
             AssetTreeFilterModel* productFilterModel,
@@ -45,14 +48,26 @@ namespace AssetProcessor
 
         void GoToSource(const AZStd::string& source);
         void GoToProduct(const AZStd::string& product);
+        
+        void SetIntermediateAssetFolderId(AZStd::optional<AZ::s64> intermediateAssetFolderId)
+        {
+            m_intermediateAssetFolderId = intermediateAssetFolderId;
+        }
 
     protected:
         QTreeView* m_sourceTreeView = nullptr;
         SourceAssetTreeModel* m_sourceTreeModel = nullptr;
         AssetTreeFilterModel* m_sourceFilterModel = nullptr;
+        
+        QTreeView* m_intermediateTreeView = nullptr;
+        SourceAssetTreeModel* m_intermediateTreeModel = nullptr;
+        AssetTreeFilterModel* m_intermediateFilterModel = nullptr;
+
         QTreeView* m_productTreeView = nullptr;
         ProductAssetTreeModel* m_productTreeModel = nullptr;
         AssetTreeFilterModel* m_productFilterModel = nullptr;
         QTabWidget* m_assetsTab = nullptr;
+        
+        AZStd::optional<AZ::s64> m_intermediateAssetFolderId;
     };
 } // namespace AssetProcessor

+ 255 - 82
Code/Tools/AssetProcessor/native/ui/MainWindow.cpp

@@ -39,6 +39,7 @@
 #include "native/ui/ui_MainWindow.h"
 #include "native/ui/JobTreeViewItemDelegate.h"
 #include <native/utilities/AssetServerHandler.h>
+#include <native/utilities/assetUtils.h>
 
 #include "../utilities/GUIApplicationManager.h"
 #include "../utilities/ApplicationServer.h"
@@ -63,58 +64,69 @@ static const qint64 AssetTabFilterUpdateIntervalMs = 5000;
 
 static const int MaxVisiblePopoutMenuRows = 20;
 static const QString productMenuTitle(QObject::tr("View product asset..."));
+static const QString intermediateMenuTitle(QObject::tr("View intermediate asset..."));
 
-struct ProductAssetRightClickMenuResult
+struct AssetRightClickMenuResult
 {
     QListWidget* m_listWidget = nullptr;
-    QMenu* m_productMenu = nullptr;
+    QMenu* m_assetMenu = nullptr;
 };
 
-ProductAssetRightClickMenuResult SetupProductAssetRightClickMenu(QMenu* parentMenu)
+AssetRightClickMenuResult SetupAssetRightClickMenu(QMenu* parentMenu, QString title, QString tooltip)
 {
-    ProductAssetRightClickMenuResult result;
+    AssetRightClickMenuResult result;
     if (!parentMenu)
     {
         return result;
     }
 
-    result.m_productMenu = parentMenu->addMenu(productMenuTitle);
-    QWidgetAction* productMenuListAction = new QWidgetAction(result.m_productMenu);
-    productMenuListAction->setToolTip(QObject::tr("Shows this product asset in the Product Assets tab."));
-    result.m_listWidget = new QListWidget(result.m_productMenu);
+    result.m_assetMenu = parentMenu->addMenu(title);
+    QWidgetAction* productMenuListAction = new QWidgetAction(result.m_assetMenu);
+    productMenuListAction->setToolTip(tooltip);
+    result.m_listWidget = new QListWidget(result.m_assetMenu);
     result.m_listWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
     result.m_listWidget->setTextElideMode(Qt::ElideLeft);
     result.m_listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     result.m_listWidget->setSelectionMode(QAbstractItemView::NoSelection);
 
     productMenuListAction->setDefaultWidget(result.m_listWidget);
-    result.m_productMenu->addAction(productMenuListAction);
+    result.m_assetMenu->addAction(productMenuListAction);
     return result;
 }
 
-void CreateDisabledProductAssetRightClickMenu(QMenu* parentMenu, QMenu* existingProductMenu, QString tooltip)
+AssetRightClickMenuResult SetupProductAssetRightClickMenu(QMenu* parentMenu)
 {
-    if (!parentMenu || !existingProductMenu)
+    return SetupAssetRightClickMenu(parentMenu, productMenuTitle, QObject::tr("Shows this product asset in the Product Assets tab."));
+}
+
+AssetRightClickMenuResult SetupIntermediateAssetRightClickMenu(QMenu* parentMenu)
+{
+    return SetupAssetRightClickMenu(parentMenu, intermediateMenuTitle, QObject::tr("Shows this intermediate asset in the Intermediate Assets tab."));
+}
+
+void CreateDisabledAssetRightClickMenu(QMenu* parentMenu, QMenu* existingMenu, QString title, QString tooltip)
+{
+    if (!parentMenu || !existingMenu)
     {
         return;
     }
     // If there were no products, then show a disabled action with a tooltip.
     // Disabled menus don't support tooltips, so remove the menu first.
-    parentMenu->removeAction(existingProductMenu->menuAction());
-    existingProductMenu->deleteLater();
+    parentMenu->removeAction(existingMenu->menuAction());
+    existingMenu->deleteLater();
 
-    QAction* disabledProductTableAction = parentMenu->addAction(productMenuTitle);
+    QAction* disabledProductTableAction = parentMenu->addAction(title);
     disabledProductTableAction->setToolTip(tooltip);
     disabledProductTableAction->setDisabled(true);
 }
 
-void ResizeProductAssetRightClickMenuList(QListWidget* productAssetList, int productCount)
+void ResizeAssetRightClickMenuList(QListWidget* assetList, int assetCount)
 {
-    // Clamp the max products displayed at once. This is a list view, so it will show a scroll bar for anything over this.
-    productCount = AZStd::min(MaxVisiblePopoutMenuRows, productCount);
+    // Clamp the max assets displayed at once. This is a list view, so it will show a scroll bar for anything over this.
+    assetCount = AZStd::min(MaxVisiblePopoutMenuRows, assetCount);
     // Using fixed width and height because the size hints aren't working well within a qmenu popout menu.
-    productAssetList->setFixedHeight(productCount * productAssetList->sizeHintForRow(0));
-    productAssetList->setFixedWidth(productAssetList->sizeHintForColumn(0));
+    assetList->setFixedHeight(assetCount * assetList->sizeHintForRow(0));
+    assetList->setFixedWidth(assetList->sizeHintForColumn(0));
 }
 
 MainWindow::Config MainWindow::loadConfig(QSettings& settings)
@@ -399,6 +411,19 @@ void MainWindow::Activate()
     connect(ui->assetDataFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
         m_sourceAssetTreeFilterModel, static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetTreeFilterModel::FilterChanged));
 
+    m_intermediateAssetTreeFilterModel = new AssetProcessor::AssetTreeFilterModel(this);
+    m_intermediateModel = new AssetProcessor::SourceAssetTreeModel(m_sharedDbConnection, this);
+    m_intermediateModel->SetOnlyShowIntermediateAssets();
+    m_intermediateModel->Reset();
+    m_intermediateAssetTreeFilterModel->setSourceModel(m_intermediateModel);
+    ui->IntermediateAssetsTreeView->setModel(m_intermediateAssetTreeFilterModel);
+    connect(
+        ui->assetDataFilteredSearchWidget,
+        &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
+        m_intermediateAssetTreeFilterModel,
+        static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetTreeFilterModel::FilterChanged));
+
+
     m_productAssetTreeFilterModel = new AssetProcessor::AssetTreeFilterModel(this);
     m_productModel = new AssetProcessor::ProductAssetTreeModel(m_sharedDbConnection, this);
     m_productModel->Reset();
@@ -408,21 +433,48 @@ void MainWindow::Activate()
     connect(ui->assetDataFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
         m_productAssetTreeFilterModel, static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetTreeFilterModel::FilterChanged));
 
+    ui->intermediateAssetDetailsPanel->SetIsIntermediateAsset();
+
+    AZStd::optional<AZ::s64> intermediateAssetFolderId(m_guiApplicationManager->GetAssetProcessorManager()->GetIntermediateAssetScanFolderId());
+    ui->productAssetDetailsPanel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
+    ui->sourceAssetDetailsPanel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
+    ui->intermediateAssetDetailsPanel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
+    m_intermediateModel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
+    m_sourceModel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
+
     AzQtComponents::StyleManager::setStyleSheet(ui->sourceAssetDetailsPanel, QStringLiteral("style:AssetProcessor.qss"));
+    AzQtComponents::StyleManager::setStyleSheet(ui->intermediateAssetDetailsPanel, QStringLiteral("style:AssetProcessor.qss"));
     AzQtComponents::StyleManager::setStyleSheet(ui->productAssetDetailsPanel, QStringLiteral("style:AssetProcessor.qss"));
 
     ui->sourceAssetDetailsPanel->RegisterAssociatedWidgets(
         ui->SourceAssetsTreeView,
         m_sourceModel,
         m_sourceAssetTreeFilterModel,
+        ui->IntermediateAssetsTreeView,
+        m_intermediateModel,
+        m_intermediateAssetTreeFilterModel,
         ui->ProductAssetsTreeView,
         m_productModel,
         m_productAssetTreeFilterModel,
         ui->assetsTabWidget);
+    ui->intermediateAssetDetailsPanel->RegisterAssociatedWidgets(
+        ui->SourceAssetsTreeView,
+        m_sourceModel,
+        m_sourceAssetTreeFilterModel,
+        ui->IntermediateAssetsTreeView,
+        m_intermediateModel,
+        m_intermediateAssetTreeFilterModel,
+        ui->ProductAssetsTreeView,
+        m_productModel,
+        m_productAssetTreeFilterModel,
+        ui->assetsTabWidget);    
     ui->productAssetDetailsPanel->RegisterAssociatedWidgets(
         ui->SourceAssetsTreeView,
         m_sourceModel,
         m_sourceAssetTreeFilterModel,
+        ui->IntermediateAssetsTreeView,
+        m_intermediateModel,
+        m_intermediateAssetTreeFilterModel,
         ui->ProductAssetsTreeView,
         m_productModel,
         m_productAssetTreeFilterModel,
@@ -433,6 +485,7 @@ void MainWindow::Activate()
     ui->productAssetDetailsPanel->SetScanQueueEnabled(false);
 
     connect(ui->SourceAssetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, ui->sourceAssetDetailsPanel, &SourceAssetDetailsPanel::AssetDataSelectionChanged);
+    connect(ui->IntermediateAssetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, ui->intermediateAssetDetailsPanel, &SourceAssetDetailsPanel::AssetDataSelectionChanged);
     connect(ui->ProductAssetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, ui->productAssetDetailsPanel, &ProductAssetDetailsPanel::AssetDataSelectionChanged);
     connect(ui->assetsTabWidget, &QTabWidget::currentChanged, this, &MainWindow::OnAssetTabChange);
 
@@ -442,6 +495,9 @@ void MainWindow::Activate()
     ui->SourceAssetsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
     connect(ui->SourceAssetsTreeView, &QWidget::customContextMenuRequested, this, &MainWindow::ShowSourceAssetContextMenu);
 
+    ui->IntermediateAssetsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(ui->IntermediateAssetsTreeView, &QWidget::customContextMenuRequested, this, &MainWindow::ShowIntermediateAssetContextMenu);
+
     ui->productAssetDetailsPanel->GetOutgoingProductDependenciesTreeView()->setContextMenuPolicy(Qt::CustomContextMenu);
     connect(
         ui->productAssetDetailsPanel->GetOutgoingProductDependenciesTreeView(), &QWidget::customContextMenuRequested, this,
@@ -971,6 +1027,7 @@ void MainWindow::SetupAssetSelectionCaching()
             // ClearSelection says in the Qt docs that the selectionChange signal will be sent, but that wasn't happening,
             // so force the details panel to refresh.
             ui->sourceAssetDetailsPanel->AssetDataSelectionChanged(QItemSelection(), QItemSelection());
+            ui->intermediateAssetDetailsPanel->AssetDataSelectionChanged(QItemSelection(), QItemSelection());
             return;
         }
         m_sourceAssetTreeFilterModel->ForceModelIndexVisible(goToIndex);
@@ -1559,12 +1616,19 @@ void MainWindow::OnAssetTabChange(int index)
     {
     case AssetTabIndex::Source:
         ui->sourceAssetDetailsPanel->setVisible(true);
+        ui->intermediateAssetDetailsPanel->setVisible(false);
         ui->productAssetDetailsPanel->setVisible(false);
         break;
     case AssetTabIndex::Product:
         ui->sourceAssetDetailsPanel->setVisible(false);
+        ui->intermediateAssetDetailsPanel->setVisible(false);
         ui->productAssetDetailsPanel->setVisible(true);
         break;
+    case AssetTabIndex::Intermediate:
+        ui->sourceAssetDetailsPanel->setVisible(false);
+        ui->intermediateAssetDetailsPanel->setVisible(true);
+        ui->productAssetDetailsPanel->setVisible(false);
+        break;
     }
 }
 
@@ -1917,7 +1981,8 @@ void MainWindow::ShowJobViewContextMenu(const QPoint& pos)
     {
         assetTabSourceAction->setToolTip(tr("Show the source asset for this job in the Assets tab."));
 
-        ProductAssetRightClickMenuResult productAssetMenu(SetupProductAssetRightClickMenu(&menu));
+        AssetRightClickMenuResult productAssetMenu(SetupProductAssetRightClickMenu(&menu));
+        AssetRightClickMenuResult intermediateAssetMenu(SetupIntermediateAssetRightClickMenu(&menu));
 
         auto productMenuItemClicked = [this, &menu](QListWidgetItem* item)
         {
@@ -1930,9 +1995,22 @@ void MainWindow::ShowJobViewContextMenu(const QPoint& pos)
                 menu.close();
             }
         };
-
         connect(productAssetMenu.m_listWidget, &QListWidget::itemClicked, this, productMenuItemClicked);
-
+        
+        auto intermediateMenuItemClicked = [this, &menu](QListWidgetItem* item)
+        {
+            if (item)
+            {
+                ui->dialogStack->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
+                ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
+                AZStd::string assetFromQString(item->text().toUtf8().data());
+                ui->sourceAssetDetailsPanel->GoToSource(assetFromQString);
+                menu.close();
+            }
+        };
+        connect(intermediateAssetMenu.m_listWidget, &QListWidget::itemClicked, this, intermediateMenuItemClicked);
+        
+        int intermediateCount = 0;
         int productCount = 0;
         m_sharedDbConnection->QueryJobByJobRunKey(
             item->m_jobRunKey,
@@ -1946,8 +2024,17 @@ void MainWindow::ShowJobViewContextMenu(const QPoint& pos)
                 {
                     return true;
                 }
-                ++productCount;
-                productAssetMenu.m_listWidget->addItem(productEntry.m_productName.c_str());
+
+                if (IsProductOutputFlagSet(productEntry, AssetBuilderSDK::ProductOutputFlags::IntermediateAsset))
+                {
+                    ++intermediateCount;
+                    intermediateAssetMenu.m_listWidget->addItem(AssetUtilities::StripAssetPlatform(productEntry.m_productName));
+                }
+                else
+                {
+                    ++productCount;
+                    productAssetMenu.m_listWidget->addItem(productEntry.m_productName.c_str());
+                }
                 return true; // Keep iterating, add all products.
             });
             return false; // Stop iterating, there should only be one job with this run key.
@@ -1955,12 +2042,22 @@ void MainWindow::ShowJobViewContextMenu(const QPoint& pos)
 
         if (productCount == 0)
         {
-            CreateDisabledProductAssetRightClickMenu(&menu, productAssetMenu.m_productMenu, tr("This job created no products."));
-            productAssetMenu.m_productMenu = nullptr;
+            CreateDisabledAssetRightClickMenu(&menu, productAssetMenu.m_assetMenu, productMenuTitle, tr("This job created no products."));
+            productAssetMenu.m_assetMenu = nullptr;
+        }
+        else
+        {
+            ResizeAssetRightClickMenuList(productAssetMenu.m_listWidget, productCount);
+        }
+        
+        if (intermediateCount == 0)
+        {
+            CreateDisabledAssetRightClickMenu(&menu, intermediateAssetMenu.m_assetMenu, intermediateMenuTitle, tr("This job created no intermediate product assets."));
+            intermediateAssetMenu.m_assetMenu = nullptr;
         }
         else
         {
-            ResizeProductAssetRightClickMenuList(productAssetMenu.m_listWidget, productCount);
+            ResizeAssetRightClickMenuList(intermediateAssetMenu.m_listWidget, intermediateCount);
         }
     }
 
@@ -2037,6 +2134,55 @@ void MainWindow::SelectJobAndMakeVisible(const QModelIndex& index)
     ui->jobTreeView->selectionModel()->setCurrentIndex(proxyIndex, AssetProcessor::AssetTreeModel::GetAssetTreeSelectionFlags());
 }
 
+void MainWindow::ShowIntermediateAssetContextMenu(const QPoint& pos)
+{
+    using namespace AssetProcessor;
+    auto sourceAt = [this](const QPoint& pos)
+    {
+        const QModelIndex proxyIndex = ui->IntermediateAssetsTreeView->indexAt(pos);
+        const QModelIndex sourceIndex = m_intermediateAssetTreeFilterModel->mapToSource(proxyIndex);
+        return static_cast<AssetTreeItem*>(sourceIndex.internalPointer());
+    };
+
+    const AssetTreeItem* cachedAsset = sourceAt(pos);
+
+    if (!cachedAsset)
+    {
+        return;
+    }
+
+    // Intermediate assets are functionally source assets, with an upstream source asset that generated them.
+    QMenu menu(this);
+
+    QAction* sourceAssetAction = menu.addAction(tr("View source asset"), this, [&]()
+    {
+        const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData = AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(cachedAsset->GetData());
+        // Generate the product path for this intermediate asset.
+        AZStd::string productPathForIntermediateAsset =
+            AssetUtilities::GetRelativeProductPathForIntermediateSourcePath(sourceItemData->m_assetDbName);
+
+        // Retrieve the source asset for that product.
+        m_sharedDbConnection->QueryProductByProductName(
+            productPathForIntermediateAsset.c_str(),
+            [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry)
+        {
+                m_sharedDbConnection->QuerySourceByProductID(
+                    productEntry.m_productID,
+                    [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
+                {
+                    ui->sourceAssetDetailsPanel->GoToSource(sourceEntry.m_sourceName);
+                    return false; // Don't keep iterating
+                });
+                return false;
+        });
+    });
+    sourceAssetAction->setToolTip(tr("Show the source asset for this intermediate asset."));
+    
+    BuildSourceAssetTreeContextMenu(menu, *cachedAsset);
+
+    menu.exec(ui->SourceAssetsTreeView->viewport()->mapToGlobal(pos));
+}
+
 void MainWindow::ShowSourceAssetContextMenu(const QPoint& pos)
 {
     using namespace AssetProcessor;
@@ -2054,49 +2200,60 @@ void MainWindow::ShowSourceAssetContextMenu(const QPoint& pos)
         return;
     }
     QMenu menu(this);
+    BuildSourceAssetTreeContextMenu(menu, *cachedAsset);
+
+    menu.exec(ui->SourceAssetsTreeView->viewport()->mapToGlobal(pos));
+}
+
+void MainWindow::BuildSourceAssetTreeContextMenu(QMenu& menu, const AssetProcessor::AssetTreeItem& sourceAssetTreeItem)
+{
+    using namespace AssetProcessor;
     menu.setToolTipsVisible(true);
-    const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData = AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(cachedAsset->GetData());
+    const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData = AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(sourceAssetTreeItem.GetData());
 
 
     QString jobMenuText(tr("View job..."));
-    QString productMenuText(tr("View product asset..."));
-    if (cachedAsset->getChildCount() > 0)
-    {
-        // Tooltips don't appear for disabled menus, so if this is a folder, create it as an action instead.
-        QAction* jobAction = menu.addAction(jobMenuText);
-        jobAction->setDisabled(true);
-        jobAction->setToolTip(tr("Folders do not have associated jobs."));
+    
+    AssetRightClickMenuResult productAssetMenu(SetupProductAssetRightClickMenu(&menu));
+    AssetRightClickMenuResult intermediateAssetMenu(SetupIntermediateAssetRightClickMenu(&menu));
 
-        QAction* productAction = menu.addAction(productMenuText);
-        productAction->setDisabled(true);
-        productAction->setToolTip(tr("Folders do not have associated products."));
-    }
-    else
+    QMenu* jobMenu = menu.addMenu(jobMenuText);
+    jobMenu->setToolTipsVisible(true);
+
+    auto productMenuItemClicked = [this, &menu](QListWidgetItem* item)
     {
-        QMenu* jobMenu = menu.addMenu(jobMenuText);
-        jobMenu->setToolTipsVisible(true);
-        ProductAssetRightClickMenuResult productAssetMenu(SetupProductAssetRightClickMenu(&menu));
-        auto productMenuItemClicked = [this, &menu](QListWidgetItem* item)
+        if (item)
         {
-            if (item)
-            {
-                AZStd::string productFromQString(item->text().toUtf8().data());
-                ui->sourceAssetDetailsPanel->GoToProduct(productFromQString);
-                menu.close();
-            }
-        };
-        connect(productAssetMenu.m_listWidget, &QListWidget::itemClicked, this, productMenuItemClicked);
+            AZStd::string productFromQString(item->text().toUtf8().data());
+            ui->sourceAssetDetailsPanel->GoToProduct(productFromQString);
+            menu.close();
+        }
+    };
+    connect(productAssetMenu.m_listWidget, &QListWidget::itemClicked, this, productMenuItemClicked);
 
-        int productCount = 0;
-        m_sharedDbConnection->QueryJobBySourceID(sourceItemData->m_sourceInfo.m_sourceID,
-            [&](AzToolsFramework::AssetDatabase::JobDatabaseEntry& jobEntry)
+    auto intermediateMenuItemClicked = [this, &menu](QListWidgetItem* item)
+    {
+        if (item)
         {
-            QAction* jobAction = jobMenu->addAction(tr("with key %1 for platform %2").arg(jobEntry.m_jobKey.c_str(), jobEntry.m_platform.c_str()), this, [&, jobEntry]()
-            {
-                QModelIndex jobIndex = m_jobsModel->GetJobFromSourceAndJobInfo(sourceItemData->m_assetDbName, jobEntry.m_platform, jobEntry.m_jobKey);
-                SelectJobAndMakeVisible(jobIndex);
-            });
-            jobAction->setToolTip(tr("Show this job in the Jobs tab."));
+            AZStd::string assetFromQString(item->text().toUtf8().data());
+            ui->sourceAssetDetailsPanel->GoToSource(assetFromQString);
+            menu.close();
+        }
+    };
+    connect(intermediateAssetMenu.m_listWidget, &QListWidget::itemClicked, this, intermediateMenuItemClicked);
+    
+    int intermediateCount = 0;
+    int productCount = 0;
+    AZStd::string sourceName(sourceItemData->m_assetDbName);
+    m_sharedDbConnection->QueryJobBySourceID(sourceItemData->m_sourceInfo.m_sourceID,
+        [&,sourceName](AzToolsFramework::AssetDatabase::JobDatabaseEntry& jobEntry)
+    {
+        QAction* jobAction = jobMenu->addAction(tr("with key %1 for platform %2").arg(jobEntry.m_jobKey.c_str(), jobEntry.m_platform.c_str()), this, [&, jobEntry, sourceName]()
+        {
+            QModelIndex jobIndex = m_jobsModel->GetJobFromSourceAndJobInfo(sourceName, jobEntry.m_platform, jobEntry.m_jobKey);
+            SelectJobAndMakeVisible(jobIndex);
+        });
+        jobAction->setToolTip(tr("Show this job in the Jobs tab."));
 
             m_sharedDbConnection->QueryProductByJobID(
                 jobEntry.m_jobID,
@@ -2106,37 +2263,55 @@ void MainWindow::ShowSourceAssetContextMenu(const QPoint& pos)
                 {
                     return true;
                 }
-                ++productCount;
-                productAssetMenu.m_listWidget->addItem(productEntry.m_productName.c_str());
+                if (IsProductOutputFlagSet(productEntry, AssetBuilderSDK::ProductOutputFlags::IntermediateAsset))
+                {
+                    ++intermediateCount;
+                    intermediateAssetMenu.m_listWidget->addItem(AZStd::string(AssetUtilities::StripAssetPlatformNoCopy(productEntry.m_productName)).c_str());
+                }
+                else
+                {
+                    ++productCount;
+                    productAssetMenu.m_listWidget->addItem(productEntry.m_productName.c_str());
+                }
                 return true; // Keep iterating, add all products.
             });
-            return true; // Stop iterating, there should only be one job with this run key.
-        });
-        if (productCount == 0)
-        {
-            CreateDisabledProductAssetRightClickMenu(&menu, productAssetMenu.m_productMenu, tr("This source asset has no products."));
-            productAssetMenu.m_productMenu = nullptr;
-        }
-        else
-        {
-            ResizeProductAssetRightClickMenuList(productAssetMenu.m_listWidget, productCount);
-        }
+        return true;
+    });
+
+    if (productCount == 0)
+    {
+        CreateDisabledAssetRightClickMenu(&menu, productAssetMenu.m_assetMenu, productMenuTitle, tr("This source asset has no products."));
+        productAssetMenu.m_assetMenu = nullptr;
+    }
+    else
+    {
+        ResizeAssetRightClickMenuList(productAssetMenu.m_listWidget, productCount);
+    }
+
+    if (intermediateCount == 0)
+    {
+        CreateDisabledAssetRightClickMenu(&menu, intermediateAssetMenu.m_assetMenu, intermediateMenuTitle, tr("This job created no intermediate product asset."));
+        intermediateAssetMenu.m_assetMenu = nullptr;
+    }
+    else
+    {
+        ResizeAssetRightClickMenuList(intermediateAssetMenu.m_listWidget, intermediateCount);
     }
 
     QAction* fileBrowserAction = menu.addAction(AzQtComponents::fileBrowserActionName(), this, [&]()
     {
-        AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(*cachedAsset);
+        AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(sourceAssetTreeItem);
         if (pathToSource.IsSuccess())
         {
             AzQtComponents::ShowFileOnDesktop(pathToSource.GetValue());
         }
     });
-    QString fileOrFolder(cachedAsset->getChildCount() > 0 ? tr("folder") : tr("file"));
+    QString fileOrFolder(sourceAssetTreeItem.getChildCount() > 0 ? tr("folder") : tr("file"));
     fileBrowserAction->setToolTip(tr("Opens a window in your operating system's file explorer to view this %1.").arg(fileOrFolder));
 
     QAction* copyFullPathAction = menu.addAction(tr("Copy full path"), this, [&]()
     {
-        AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(*cachedAsset);
+        AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(sourceAssetTreeItem);
         if (pathToSource.IsSuccess())
         {
             QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(pathToSource.GetValue()));
@@ -2148,18 +2323,16 @@ void MainWindow::ShowSourceAssetContextMenu(const QPoint& pos)
     QString reprocessFolder{ tr("Reprocess Folder") };
     QString reprocessFile{ tr("Reprocess File") };
 
-    QAction* reprocessAssetAction = menu.addAction(cachedAsset->getChildCount() ? reprocessFolder : reprocessFile, this, [&]()
+    QAction* reprocessAssetAction = menu.addAction(sourceAssetTreeItem.getChildCount() ? reprocessFolder : reprocessFile, this, [&]()
     {
-        AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(*cachedAsset);
+        AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(sourceAssetTreeItem);
         m_guiApplicationManager->GetAssetProcessorManager()->RequestReprocess(pathToSource.GetValue());
     });
 
     QString reprocessFolderTip{ tr("Put the source assets in the selected folder back in the processing queue") };
     QString reprocessFileTip{ tr("Put the source asset back in the processing queue") };
 
-    reprocessAssetAction->setToolTip(cachedAsset->getChildCount() ? reprocessFolderTip : reprocessFileTip);
-
-    menu.exec(ui->SourceAssetsTreeView->viewport()->mapToGlobal(pos));
+    reprocessAssetAction->setToolTip(sourceAssetTreeItem.getChildCount() ? reprocessFolderTip : reprocessFileTip);
 }
 
 void MainWindow::ShowProductAssetContextMenu(const QPoint& pos)

+ 9 - 1
Code/Tools/AssetProcessor/native/ui/MainWindow.h

@@ -43,6 +43,7 @@ namespace AssetProcessor
     class AssetTreeFilterModel;
     class JobSortFilterProxyModel;
     class JobsModel;
+    class AssetTreeItem;
     class ProductAssetTreeModel;
     class SourceAssetTreeModel;
     class ProductDependencyTreeItem;
@@ -64,7 +65,8 @@ public:
     enum class AssetTabIndex
     {
         Source = 0,
-        Product = 1
+        Intermediate = 1,
+        Product = 2
     };
 
     // This order is actually driven by the layout in the UI file.
@@ -147,8 +149,10 @@ private:
     LogSortFilterProxy* m_logSortFilterProxy;
     AssetProcessor::JobsModel* m_jobsModel;
     AssetProcessor::SourceAssetTreeModel* m_sourceModel = nullptr;
+    AssetProcessor::SourceAssetTreeModel* m_intermediateModel = nullptr;
     AssetProcessor::ProductAssetTreeModel* m_productModel = nullptr;
     AssetProcessor::AssetTreeFilterModel* m_sourceAssetTreeFilterModel = nullptr;
+    AssetProcessor::AssetTreeFilterModel* m_intermediateAssetTreeFilterModel = nullptr;
     AssetProcessor::AssetTreeFilterModel* m_productAssetTreeFilterModel = nullptr;
     QPointer<AssetProcessor::LogPanel> m_loggingPanel;
     int m_processJobsCount = 0;
@@ -205,6 +209,10 @@ private:
 
     void ShowProductAssetContextMenu(const QPoint& pos);
     void ShowSourceAssetContextMenu(const QPoint& pos);
+    void ShowIntermediateAssetContextMenu(const QPoint& pos);
+
+    void BuildSourceAssetTreeContextMenu(QMenu& menu, const AssetProcessor::AssetTreeItem& sourceAssetTreeItem);
+
     // Helper function that retrieves the item selected in outgoing/incoming dependency TreeView
     AssetProcessor::ProductDependencyTreeItem* GetProductAssetFromDependencyTreeView(bool isOutgoing, const QPoint& pos);
     void ShowOutgoingProductDependenciesContextMenu(const QPoint& pos);

+ 28 - 1
Code/Tools/AssetProcessor/native/ui/MainWindow.ui

@@ -613,7 +613,27 @@
                </item>
               </layout>
              </widget>
-             <widget class="QWidget" name="ProductAssetsTab">
+              <widget class="QWidget" name="IntermediateAssetsTab">
+                <attribute name="title">
+                  <string>Intermediate Assets</string>
+                </attribute>
+                <layout class="QVBoxLayout" name="IntermediateAssetsVerticalLayout" stretch="0">
+                  <item>
+                    <widget class="QTreeView" name="IntermediateAssetsTreeView">
+                      <property name="editTriggers">
+                        <set>QAbstractItemView::NoEditTriggers</set>
+                      </property>
+                      <property name="sortingEnabled">
+                        <bool>true</bool>
+                      </property>
+                      <attribute name="headerDefaultSectionSize">
+                        <number>300</number>
+                      </attribute>
+                    </widget>
+                  </item>
+                </layout>
+              </widget>
+              <widget class="QWidget" name="ProductAssetsTab">
               <attribute name="title">
                <string>Product Assets</string>
               </attribute>
@@ -639,6 +659,13 @@
            <item>
             <widget class="AssetProcessor::SourceAssetDetailsPanel" name="sourceAssetDetailsPanel"/>
            </item>
+            <item>
+              <widget class="AssetProcessor::SourceAssetDetailsPanel" name="intermediateAssetDetailsPanel">
+                <property name="visible">
+                  <bool>false</bool>
+                </property>
+              </widget>
+            </item>
            <item>
             <widget class="AssetProcessor::ProductAssetDetailsPanel" name="productAssetDetailsPanel">
              <property name="visible">

+ 18 - 0
Code/Tools/AssetProcessor/native/ui/ProductAssetTreeModel.cpp

@@ -9,11 +9,13 @@
 #include "ProductAssetTreeModel.h"
 #include "ProductAssetTreeItemData.h"
 
+#include <AssetBuilderSDK/AssetBuilderSDK.h>
 #include <AzCore/Component/TickBus.h>
 #include <AzCore/IO/Path/Path.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzCore/Console/IConsole.h>
 
+
 namespace AssetProcessor
 {
     AZ_CVAR_EXTERNED(bool, ap_disableAssetTreeView);
@@ -169,6 +171,22 @@ namespace AssetProcessor
         const AzToolsFramework::AssetDatabase::ProductDatabaseEntry& product,
         bool modelIsResetting)
     {
+        AZStd::string platform;
+        m_sharedDbConnection->QueryJobByProductID(
+            product.m_productID,
+            [&](AzToolsFramework::AssetDatabase::JobDatabaseEntry& jobEntry)
+            {
+                platform = jobEntry.m_platform;
+                return true;
+            });
+
+        // Intermediate assets are functionally source assets, output as products from other source assets.
+        // Don't display them in the product assets tab.
+        if (product.m_flags.test(static_cast<int>(AssetBuilderSDK::ProductOutputFlags::IntermediateAsset)))
+        {
+            return;
+        }
+
         const auto& existingEntry = m_productIdToTreeItem.find(product.m_productID);
         if (existingEntry != m_productIdToTreeItem.end())
         {

+ 186 - 117
Code/Tools/AssetProcessor/native/ui/SourceAssetDetailsPanel.cpp

@@ -12,15 +12,17 @@
 #include "GoToButton.h"
 #include "SourceAssetTreeItemData.h"
 #include "SourceAssetTreeModel.h"
-
+#include <AssetBuilderSDK/AssetBuilderSDK.h>
 
 #include <native/ui/ui_GoToButton.h>
 #include <native/ui/ui_SourceAssetDetailsPanel.h>
+#include <native/utilities/assetUtils.h>
 
 namespace AssetProcessor
 {
-
-    SourceAssetDetailsPanel::SourceAssetDetailsPanel(QWidget* parent) : AssetDetailsPanel(parent), m_ui(new Ui::SourceAssetDetailsPanel)
+    SourceAssetDetailsPanel::SourceAssetDetailsPanel(QWidget* parent)
+        : AssetDetailsPanel(parent)
+        , m_ui(new Ui::SourceAssetDetailsPanel)
     {
         m_ui->setupUi(this);
         m_ui->scrollAreaWidgetContents->setLayout(m_ui->scrollableVerticalLayout);
@@ -29,12 +31,12 @@ namespace AssetProcessor
 
     SourceAssetDetailsPanel::~SourceAssetDetailsPanel()
     {
-
     }
 
     void SourceAssetDetailsPanel::AssetDataSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/)
     {
-        QItemSelection sourceSelection = m_sourceFilterModel->mapSelectionToSource(selected);
+        AssetTreeFilterModel* filterModel = m_isIntermediateAsset ? m_intermediateFilterModel : m_sourceFilterModel;
+        QItemSelection sourceSelection = filterModel->mapSelectionToSource(selected);
         // Even if multi-select is enabled, only display the first selected item.
         if (sourceSelection.indexes().count() == 0 || !sourceSelection.indexes()[0].isValid())
         {
@@ -50,7 +52,8 @@ namespace AssetProcessor
         }
         AssetTreeItem* childItem = static_cast<AssetTreeItem*>(sourceModelIndex.internalPointer());
 
-        const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData = AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(childItem->GetData());
+        const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData =
+            AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(childItem->GetData());
 
         m_ui->assetNameLabel->setText(childItem->GetData()->m_name);
 
@@ -60,65 +63,141 @@ namespace AssetProcessor
             SetDetailsVisible(false);
             return;
         }
+
         SetDetailsVisible(true);
-        m_ui->scanFolderValueLabel->setText(sourceItemData->m_scanFolderInfo.m_scanFolder.c_str());
-        m_ui->sourceGuidValueLabel->setText(sourceItemData->m_sourceInfo.m_sourceGuid.ToString<AZStd::string>().c_str());
 
         AssetDatabaseConnection assetDatabaseConnection;
         assetDatabaseConnection.OpenDatabase();
 
+        if (m_isIntermediateAsset)
+        {
+            // First, find the product version of this intermediate asset.
+            AZStd::string intermediateProductPath = AssetUtilities::GetIntermediateAssetDatabaseName(sourceItemData->m_assetDbName.c_str());
+            AzToolsFramework::AssetDatabase::SourceDatabaseEntry upstreamSource;
+
+            assetDatabaseConnection.QueryProductByProductName(
+                intermediateProductPath.c_str(),
+                [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry)
+                {
+                    // Second, get the source that created this product.
+                    assetDatabaseConnection.QuerySourceByProductID(
+                        productEntry.m_productID,
+                        [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
+                        {
+                            upstreamSource = sourceEntry;
+                            return true;
+                        });
+                    return true;
+                });
+
+            // Finally, populate the UI with the information on the source that output this.
+            m_ui->gotoAssetButton->m_ui->goToPushButton->disconnect();
+
+            connect(
+                m_ui->gotoAssetButton->m_ui->goToPushButton,
+                &QPushButton::clicked,
+                [=]
+                {
+                    GoToSource(upstreamSource.m_sourceName);
+                });
+
+            m_ui->sourceAssetValueLabel->setText(upstreamSource.m_sourceName.c_str());
+        }
+
+        m_ui->scanFolderValueLabel->setText(sourceItemData->m_scanFolderInfo.m_scanFolder.c_str());
+        m_ui->sourceGuidValueLabel->setText(sourceItemData->m_sourceInfo.m_sourceGuid.ToString<AZStd::string>().c_str());
+
+
         BuildProducts(assetDatabaseConnection, sourceItemData);
         BuildOutgoingSourceDependencies(assetDatabaseConnection, sourceItemData);
         BuildIncomingSourceDependencies(assetDatabaseConnection, sourceItemData);
     }
 
     void SourceAssetDetailsPanel::BuildProducts(
-        AssetDatabaseConnection& assetDatabaseConnection,
-        const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData)
+        AssetDatabaseConnection& assetDatabaseConnection, const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData)
     {
         // Clear & ClearContents leave the table dimensions the same, so set rowCount to zero to reset it.
         m_ui->productTable->setRowCount(0);
+        m_ui->IntermediateAssetsTable->setRowCount(0);
 
+        int intermediateAssetCount = 0;
         int productCount = 0;
         assetDatabaseConnection.QueryProductBySourceID(
             sourceItemData->m_sourceInfo.m_sourceID,
             [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry)
-        {
-            m_ui->productTable->insertRow(productCount);
-
-            // Qt handles cleanup automatically, setting this as the parent means
-            // when this panel is torn down, these widgets will be destroyed.
-            GoToButton* rowGoToButton = new GoToButton(this);
-            connect(rowGoToButton->m_ui->goToPushButton, &QPushButton::clicked, [=] {
-                GoToProduct(productEntry.m_productName);
+            {
+                AZ::s64 sourcePK;
+                assetDatabaseConnection.QueryJobByProductID(
+                    productEntry.m_productID,
+                    [&sourcePK](AzToolsFramework::AssetDatabase::JobDatabaseEntry& jobEntry)
+                    {
+                        sourcePK = jobEntry.m_sourcePK;
+                        return true;
+                    });
+
+                if (IsProductOutputFlagSet(productEntry, AssetBuilderSDK::ProductOutputFlags::IntermediateAsset))
+                {
+                    m_ui->IntermediateAssetsTable->insertRow(intermediateAssetCount);
+
+                    AZStd::string intermediateAssetSourcePath;
+                    assetDatabaseConnection.QuerySourceBySourceID(
+                        sourcePK,
+                        [&intermediateAssetSourcePath](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
+                        {
+                            intermediateAssetSourcePath = sourceEntry.m_sourceName;
+                            return true;
+                        });
+                    
+                    AZStd::string sourceIntermediateAssetPath = AssetUtilities::StripAssetPlatformNoCopy(productEntry.m_productName);
+
+                    // Qt handles cleanup automatically, setting this as the parent means
+                    // when this panel is torn down, these widgets will be destroyed.
+                    GoToButton* rowGoToButton = new GoToButton(this);
+                    connect(
+                        rowGoToButton->m_ui->goToPushButton,
+                        &QPushButton::clicked,
+                        [=]
+                        {
+                            GoToSource(sourceIntermediateAssetPath);
+                        });
+                    m_ui->IntermediateAssetsTable->setCellWidget(intermediateAssetCount, 0, rowGoToButton);
+
+                    QTableWidgetItem* rowName = new QTableWidgetItem(productEntry.m_productName.c_str());
+                    m_ui->IntermediateAssetsTable->setItem(intermediateAssetCount, 1, rowName);
+                    ++intermediateAssetCount;
+                }
+                else
+                {
+                    m_ui->productTable->insertRow(productCount);
+
+                    // Qt handles cleanup automatically, setting this as the parent means
+                    // when this panel is torn down, these widgets will be destroyed.
+                    GoToButton* rowGoToButton = new GoToButton(this);
+                    connect(
+                        rowGoToButton->m_ui->goToPushButton,
+                        &QPushButton::clicked,
+                        [=]
+                        {
+                            GoToProduct(productEntry.m_productName);
+                        });
+                    m_ui->productTable->setCellWidget(productCount, 0, rowGoToButton);
+
+                    QTableWidgetItem* rowName = new QTableWidgetItem(productEntry.m_productName.c_str());
+                    m_ui->productTable->setItem(productCount, 1, rowName);
+                    ++productCount;
+                }
+                return true;
             });
-            m_ui->productTable->setCellWidget(productCount, 0, rowGoToButton);
-
-            QTableWidgetItem* rowName = new QTableWidgetItem(productEntry.m_productName.c_str());
-            m_ui->productTable->setItem(productCount, 1, rowName);
-            ++productCount;
-            return true;
-        });
-
-        m_ui->productsValueLabel->setText(QString::number(productCount));
-        if (productCount == 0)
-        {
-            m_ui->productTable->insertRow(productCount);
-            QTableWidgetItem* rowName = new QTableWidgetItem(tr("No products"));
-            m_ui->productTable->setItem(productCount, 1, rowName);
-            ++productCount;
-        }
 
-        // The default list behavior is to maintain size and let you scroll within.
-        // The entire frame is scrollable here, so the list should adjust to fit the contents.
-        m_ui->productTable->setMinimumHeight(m_ui->productTable->rowHeight(0) * productCount + 2 * m_ui->productTable->frameWidth());
-        m_ui->productTable->adjustSize();
+        m_ui->SourceAssetDetailTabs->setTabText(0, tr("Products (%1)").arg(productCount));
+        m_ui->SourceAssetDetailTabs->setTabVisible(0, productCount > 0);
 
+        m_ui->SourceAssetDetailTabs->setTabText(3, tr("Intermediate Assets (%1)").arg(intermediateAssetCount));
+        m_ui->SourceAssetDetailTabs->setTabVisible(3, intermediateAssetCount > 0);
     }
 
     void SourceAssetDetailsPanel::BuildOutgoingSourceDependencies(
-        AssetDatabaseConnection& assetDatabaseConnection,
-        const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData)
+        AssetDatabaseConnection& assetDatabaseConnection, const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData)
     {
         m_ui->outgoingSourceDependenciesTable->setRowCount(0);
         int sourceDependencyCount = 0;
@@ -127,47 +206,51 @@ namespace AssetProcessor
             nullptr,
             AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any,
             [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry)
-        {
-            m_ui->outgoingSourceDependenciesTable->insertRow(sourceDependencyCount);
-
-            // Some outgoing source dependencies are wildcard, or unresolved paths.
-            // Only add a button to link to rows that actually exist.
-            QModelIndex goToIndex = m_sourceTreeModel->GetIndexForSource(sourceFileDependencyEntry.m_dependsOnSource);
-            if (goToIndex.isValid())
             {
-                // Qt handles cleanup automatically, setting this as the parent means
-                // when this panel is torn down, these widgets will be destroyed.
-                GoToButton* rowGoToButton = new GoToButton(this);
-                connect(rowGoToButton->m_ui->goToPushButton, &QPushButton::clicked, [=] {
-                    GoToSource(sourceFileDependencyEntry.m_dependsOnSource);
-                });
-                m_ui->outgoingSourceDependenciesTable->setCellWidget(sourceDependencyCount, 0, rowGoToButton);
-            }
-
-            QTableWidgetItem* rowName = new QTableWidgetItem(sourceFileDependencyEntry.m_dependsOnSource.c_str());
-            m_ui->outgoingSourceDependenciesTable->setItem(sourceDependencyCount, 1, rowName);
-            ++sourceDependencyCount;
-            return true;
-        });
-
-        m_ui->outgoingSourceDependenciesValueLabel->setText(QString::number(sourceDependencyCount));
-        if (sourceDependencyCount == 0)
-        {
-            m_ui->outgoingSourceDependenciesTable->insertRow(sourceDependencyCount);
-            QTableWidgetItem* rowName = new QTableWidgetItem(tr("No source dependencies"));
-            m_ui->outgoingSourceDependenciesTable->setItem(sourceDependencyCount, 1, rowName);
-            ++sourceDependencyCount;
-        }
+                m_ui->outgoingSourceDependenciesTable->insertRow(sourceDependencyCount);
+
+                // Some outgoing source dependencies are wildcard, or unresolved paths.
+                // Only add a button to link to rows that actually exist.
+                
+                AzToolsFramework::AssetDatabase::SourceDatabaseEntry dependencyDetails;
+                assetDatabaseConnection.QuerySourceBySourceName(
+                    sourceFileDependencyEntry.m_dependsOnSource.c_str(),
+                    [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
+                    {
+                        dependencyDetails = sourceEntry;
+                        return false;
+                    });
+
+
+                SourceAssetTreeModel* treeModel = dependencyDetails.m_scanFolderPK == 1 ? m_intermediateTreeModel : m_sourceTreeModel;
+                QModelIndex goToIndex = treeModel->GetIndexForSource(sourceFileDependencyEntry.m_dependsOnSource);
+                if (goToIndex.isValid())
+                {
+                    // Qt handles cleanup automatically, setting this as the parent means
+                    // when this panel is torn down, these widgets will be destroyed.
+                    GoToButton* rowGoToButton = new GoToButton(this);
+                    connect(
+                        rowGoToButton->m_ui->goToPushButton,
+                        &QPushButton::clicked,
+                        [=]
+                        {
+                            GoToSource(sourceFileDependencyEntry.m_dependsOnSource);
+                        });
+                    m_ui->outgoingSourceDependenciesTable->setCellWidget(sourceDependencyCount, 0, rowGoToButton);
+                }
+
+                QTableWidgetItem* rowName = new QTableWidgetItem(sourceFileDependencyEntry.m_dependsOnSource.c_str());
+                m_ui->outgoingSourceDependenciesTable->setItem(sourceDependencyCount, 1, rowName);
+                ++sourceDependencyCount;
+                return true;
+            });
 
-        // The default list behavior is to maintain size and let you scroll within.
-        // The entire frame is scrollable here, so the list should adjust to fit the contents.
-        m_ui->outgoingSourceDependenciesTable->setMinimumHeight(m_ui->outgoingSourceDependenciesTable->rowHeight(0) * sourceDependencyCount + 2 * m_ui->outgoingSourceDependenciesTable->frameWidth());
-        m_ui->outgoingSourceDependenciesTable->adjustSize();
+        m_ui->SourceAssetDetailTabs->setTabText(1, tr("Dependencies - Out (%1)").arg(sourceDependencyCount));
+        m_ui->SourceAssetDetailTabs->setTabVisible(1, sourceDependencyCount > 0);
     }
 
     void SourceAssetDetailsPanel::BuildIncomingSourceDependencies(
-        AssetDatabaseConnection& assetDatabaseConnection,
-        const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData)
+        AssetDatabaseConnection& assetDatabaseConnection, const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData)
     {
         m_ui->incomingSourceDependenciesTable->setRowCount(0);
         int sourceDependencyCount = 0;
@@ -176,38 +259,30 @@ namespace AssetProcessor
             nullptr,
             AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any,
             [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry)
-        {
-            m_ui->incomingSourceDependenciesTable->insertRow(sourceDependencyCount);
+            {
+                m_ui->incomingSourceDependenciesTable->insertRow(sourceDependencyCount);
 
-            // Qt handles cleanup automatically, setting this as the parent means
-            // when this panel is torn down, these widgets will be destroyed.
-            GoToButton* rowGoToButton = new GoToButton(this);
-            connect(rowGoToButton->m_ui->goToPushButton, &QPushButton::clicked, [=] {
-                GoToSource(sourceFileDependencyEntry.m_source);
+                // Qt handles cleanup automatically, setting this as the parent means
+                // when this panel is torn down, these widgets will be destroyed.
+                GoToButton* rowGoToButton = new GoToButton(this);
+                connect(
+                    rowGoToButton->m_ui->goToPushButton,
+                    &QPushButton::clicked,
+                    [=]
+                    {
+                        GoToSource(sourceFileDependencyEntry.m_source);
+                    });
+                m_ui->incomingSourceDependenciesTable->setCellWidget(sourceDependencyCount, 0, rowGoToButton);
+
+                QTableWidgetItem* rowName = new QTableWidgetItem(sourceFileDependencyEntry.m_source.c_str());
+                m_ui->incomingSourceDependenciesTable->setItem(sourceDependencyCount, 1, rowName);
+
+                ++sourceDependencyCount;
+                return true;
             });
-            m_ui->incomingSourceDependenciesTable->setCellWidget(sourceDependencyCount, 0, rowGoToButton);
-
-            QTableWidgetItem* rowName = new QTableWidgetItem(sourceFileDependencyEntry.m_source.c_str());
-            m_ui->incomingSourceDependenciesTable->setItem(sourceDependencyCount, 1, rowName);
-
-            ++sourceDependencyCount;
-            return true;
-        });
-
-        m_ui->incomingSourceDependenciesValueLabel->setText(QString::number(sourceDependencyCount));
-        if (sourceDependencyCount == 0)
-        {
-            m_ui->incomingSourceDependenciesTable->insertRow(sourceDependencyCount);
-            QTableWidgetItem* rowName = new QTableWidgetItem(tr("No source dependencies"));
-            m_ui->incomingSourceDependenciesTable->setItem(sourceDependencyCount, 1, rowName);
-            ++sourceDependencyCount;
-        }
-
-        // The default list behavior is to maintain size and let you scroll within.
-        // The entire frame is scrollable here, so the list should adjust to fit the contents.
-        m_ui->incomingSourceDependenciesTable->setMinimumHeight(m_ui->incomingSourceDependenciesTable->rowHeight(0) * sourceDependencyCount + 2 * m_ui->incomingSourceDependenciesTable->frameWidth());
-        m_ui->incomingSourceDependenciesTable->adjustSize();
 
+        m_ui->SourceAssetDetailTabs->setTabText(2, tr("Dependencies - In (%1)").arg(sourceDependencyCount));
+        m_ui->SourceAssetDetailTabs->setTabVisible(2, sourceDependencyCount > 0);
     }
 
     void SourceAssetDetailsPanel::ResetText()
@@ -227,18 +302,12 @@ namespace AssetProcessor
         m_ui->sourceGuidTitleLabel->setVisible(visible);
         m_ui->sourceGuidValueLabel->setVisible(visible);
 
-        m_ui->productsTitleLabel->setVisible(visible);
-        m_ui->productsValueLabel->setVisible(visible);
-        m_ui->productTable->setVisible(visible);
-
-        m_ui->outgoingSourceDependenciesTitleLabel->setVisible(visible);
-        m_ui->outgoingSourceDependenciesValueLabel->setVisible(visible);
-        m_ui->outgoingSourceDependenciesTable->setVisible(visible);
+        m_ui->sourceAssetTitleLabel->setVisible(visible && m_isIntermediateAsset);
+        m_ui->gotoAssetButton->setVisible(visible && m_isIntermediateAsset);
+        m_ui->sourceAssetValueLabel->setVisible(visible && m_isIntermediateAsset);
 
-        m_ui->incomingSourceDependenciesTitleLabel->setVisible(visible);
-        m_ui->incomingSourceDependenciesValueLabel->setVisible(visible);
-        m_ui->incomingSourceDependenciesTable->setVisible(visible);
+        m_ui->AssetInfoSeparatorLine->setVisible(visible);
 
-        m_ui->DependencySeparatorLine->setVisible(visible);
+        m_ui->SourceAssetDetailTabs->setVisible(visible);
     }
-}
+} // namespace AssetProcessor

+ 4 - 0
Code/Tools/AssetProcessor/native/ui/SourceAssetDetailsPanel.h

@@ -34,6 +34,8 @@ namespace AssetProcessor
         explicit SourceAssetDetailsPanel(QWidget* parent = nullptr);
         ~SourceAssetDetailsPanel() override;
 
+        void SetIsIntermediateAsset() { m_isIntermediateAsset = true; }
+
     public Q_SLOTS:
         void AssetDataSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
 
@@ -52,5 +54,7 @@ namespace AssetProcessor
             const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData);
 
         QScopedPointer<Ui::SourceAssetDetailsPanel> m_ui;
+        AZStd::optional<AZ::s64> m_intermediateAssetFolderID;
+        bool m_isIntermediateAsset = false;
     };
 } // namespace AssetProcessor

+ 297 - 402
Code/Tools/AssetProcessor/native/ui/SourceAssetDetailsPanel.ui

@@ -281,437 +281,322 @@
               </property>
              </widget>
             </item>
+             <item row="2" column="0">
+               <widget class="QLabel" name="sourceAssetTitleLabel">
+                 <property name="styleSheet">
+                   <string notr="true">font: bold;</string>
+                 </property>
+                 <property name="text">
+                   <string>Source Asset</string>
+                 </property>
+                 <property name="alignment">
+                   <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+                 </property>
+               </widget>
+             </item>
+             <item row="2" column="1">
+               <layout class="QHBoxLayout" name="sourceAsset">
+                 <item>
+                   <widget class="AssetProcessor::GoToButton" name="gotoAssetButton">
+                     <property name="sizePolicy">
+                       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+                         <horstretch>0</horstretch>
+                         <verstretch>0</verstretch>
+                       </sizepolicy>
+                     </property>
+                     <property name="minimumSize">
+                       <size>
+                         <width>24</width>
+                         <height>24</height>
+                       </size>
+                     </property>
+                   </widget>
+                 </item>
+                 <item>
+                   <widget class="QLabel" name="sourceAssetValueLabel">
+                     <property name="text">
+                       <string>sourceAsset Here</string>
+                     </property>
+                     <property name="textInteractionFlags">
+                       <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+                     </property>
+                   </widget>
+                 </item>
+               </layout>
+             </item>
            </layout>
           </item>
           <item>
-           <widget class="Line" name="DependencySeparatorLine">
+           <widget class="Line" name="AssetInfoSeparatorLine">
             <property name="orientation">
              <enum>Qt::Horizontal</enum>
             </property>
            </widget>
           </item>
           <item>
-           <spacer name="verticalSpacer_2">
-            <property name="orientation">
-             <enum>Qt::Vertical</enum>
-            </property>
-            <property name="sizeType">
-             <enum>QSizePolicy::Fixed</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>0</width>
-              <height>8</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
-          <item>
-           <layout class="QVBoxLayout" name="productVerticalLayout">
-            <property name="spacing">
-             <number>1</number>
-            </property>
-            <property name="sizeConstraint">
-             <enum>QLayout::SetDefaultConstraint</enum>
-            </property>
-            <item>
-             <layout class="QHBoxLayout" name="ProductTitleLayout">
+           <widget class="QTabWidget" name="SourceAssetDetailTabs">
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <widget class="QWidget" name="OutputTab">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <attribute name="title">
+              <string>Products</string>
+             </attribute>
+             <layout class="QVBoxLayout" name="ProductTabVerticalLayout" stretch="0">
               <item>
-               <widget class="QLabel" name="productsTitleLabel">
-                <property name="font">
-                 <font>
-                  <family>12pt</family>
-                  <weight>75</weight>
-                  <italic>false</italic>
-                  <bold>true</bold>
-                 </font>
+               <widget class="QTableWidget" name="productTable">
+                <property name="verticalScrollBarPolicy">
+                 <enum>Qt::ScrollBarAsNeeded</enum>
                 </property>
-                <property name="styleSheet">
-                 <string notr="true">font: bold, 12pt;</string>
+                <property name="horizontalScrollBarPolicy">
+                 <enum>Qt::ScrollBarAsNeeded</enum>
                 </property>
-                <property name="text">
-                 <string>Products:</string>
+                <property name="sizeAdjustPolicy">
+                 <enum>QAbstractScrollArea::AdjustIgnored</enum>
                 </property>
-                <property name="alignment">
-                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+                <property name="editTriggers">
+                 <set>QAbstractItemView::NoEditTriggers</set>
                 </property>
-                <property name="margin">
-                 <number>0</number>
+                <property name="showDropIndicator" stdset="0">
+                 <bool>false</bool>
                 </property>
+                <property name="dragDropOverwriteMode">
+                 <bool>false</bool>
+                </property>
+                <property name="alternatingRowColors">
+                 <bool>true</bool>
+                </property>
+                <property name="selectionMode">
+                 <enum>QAbstractItemView::SingleSelection</enum>
+                </property>
+                <property name="textElideMode">
+                 <enum>Qt::ElideNone</enum>
+                </property>
+                <property name="horizontalScrollMode">
+                 <enum>QAbstractItemView::ScrollPerPixel</enum>
+                </property>
+                <property name="showGrid">
+                 <bool>false</bool>
+                </property>
+                <property name="columnCount">
+                 <number>2</number>
+                </property>
+                <attribute name="horizontalHeaderVisible">
+                 <bool>false</bool>
+                </attribute>
+                <attribute name="horizontalHeaderMinimumSectionSize">
+                 <number>24</number>
+                </attribute>
+                <attribute name="horizontalHeaderDefaultSectionSize">
+                 <number>24</number>
+                </attribute>
+                <attribute name="horizontalHeaderStretchLastSection">
+                 <bool>true</bool>
+                </attribute>
+                <attribute name="verticalHeaderVisible">
+                 <bool>false</bool>
+                </attribute>
+                <attribute name="verticalHeaderStretchLastSection">
+                 <bool>false</bool>
+                </attribute>
+                <column/>
+                <column/>
                </widget>
               </item>
+             </layout>
+            </widget>
+            <widget class="QWidget" name="OutgoingSourceDependenciesTab">
+             <attribute name="title">
+              <string>Outgoing Source Dependencies</string>
+             </attribute>
+             <layout class="QVBoxLayout" name="OutgoingSourceDependenciesLayout" stretch="0">
               <item>
-               <widget class="QLabel" name="productsValueLabel">
-                <property name="text">
-                 <string>Number of products here</string>
+               <widget class="QTableWidget" name="outgoingSourceDependenciesTable">
+                <property name="verticalScrollBarPolicy">
+                 <enum>Qt::ScrollBarAsNeeded</enum>
+                </property>
+                <property name="horizontalScrollBarPolicy">
+                 <enum>Qt::ScrollBarAsNeeded</enum>
                 </property>
-                <property name="textInteractionFlags">
-                 <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+                <property name="sizeAdjustPolicy">
+                 <enum>QAbstractScrollArea::AdjustIgnored</enum>
                 </property>
+                <property name="editTriggers">
+                 <set>QAbstractItemView::NoEditTriggers</set>
+                </property>
+                <property name="showDropIndicator" stdset="0">
+                 <bool>false</bool>
+                </property>
+                <property name="dragDropOverwriteMode">
+                 <bool>false</bool>
+                </property>
+                <property name="alternatingRowColors">
+                 <bool>true</bool>
+                </property>
+                <property name="selectionMode">
+                 <enum>QAbstractItemView::SingleSelection</enum>
+                </property>
+                <property name="showGrid">
+                 <bool>false</bool>
+                </property>
+                <property name="columnCount">
+                 <number>2</number>
+                </property>
+                <attribute name="horizontalHeaderVisible">
+                 <bool>false</bool>
+                </attribute>
+                <attribute name="horizontalHeaderMinimumSectionSize">
+                 <number>24</number>
+                </attribute>
+                <attribute name="horizontalHeaderDefaultSectionSize">
+                 <number>24</number>
+                </attribute>
+                <attribute name="horizontalHeaderStretchLastSection">
+                 <bool>true</bool>
+                </attribute>
+                <attribute name="verticalHeaderVisible">
+                 <bool>false</bool>
+                </attribute>
+                <attribute name="verticalHeaderStretchLastSection">
+                 <bool>false</bool>
+                </attribute>
+                <column/>
+                <column/>
                </widget>
               </item>
+             </layout>
+            </widget>
+            <widget class="QWidget" name="IncomingSourceDependenciesTab">
+             <attribute name="title">
+              <string>Incoming Source Dependencies</string>
+             </attribute>
+             <layout class="QVBoxLayout" name="IncomingSourceDependenciesLayout" stretch="0">
               <item>
-               <spacer name="horizontalSpacer_5">
-                <property name="orientation">
-                 <enum>Qt::Horizontal</enum>
+               <widget class="QTableWidget" name="incomingSourceDependenciesTable">
+                <property name="verticalScrollBarPolicy">
+                 <enum>Qt::ScrollBarAsNeeded</enum>
+                </property>
+                <property name="horizontalScrollBarPolicy">
+                 <enum>Qt::ScrollBarAsNeeded</enum>
                 </property>
-                <property name="sizeHint" stdset="0">
-                 <size>
-                  <width>40</width>
-                  <height>20</height>
-                 </size>
+                <property name="sizeAdjustPolicy">
+                 <enum>QAbstractScrollArea::AdjustIgnored</enum>
                 </property>
-               </spacer>
+                <property name="editTriggers">
+                 <set>QAbstractItemView::NoEditTriggers</set>
+                </property>
+                <property name="showDropIndicator" stdset="0">
+                 <bool>false</bool>
+                </property>
+                <property name="dragDropOverwriteMode">
+                 <bool>false</bool>
+                </property>
+                <property name="alternatingRowColors">
+                 <bool>true</bool>
+                </property>
+                <property name="selectionMode">
+                 <enum>QAbstractItemView::SingleSelection</enum>
+                </property>
+                <property name="showGrid">
+                 <bool>false</bool>
+                </property>
+                <property name="columnCount">
+                 <number>2</number>
+                </property>
+                <attribute name="horizontalHeaderVisible">
+                 <bool>false</bool>
+                </attribute>
+                <attribute name="horizontalHeaderMinimumSectionSize">
+                 <number>24</number>
+                </attribute>
+                <attribute name="horizontalHeaderDefaultSectionSize">
+                 <number>24</number>
+                </attribute>
+                <attribute name="horizontalHeaderStretchLastSection">
+                 <bool>true</bool>
+                </attribute>
+                <attribute name="verticalHeaderVisible">
+                 <bool>false</bool>
+                </attribute>
+                <attribute name="verticalHeaderStretchLastSection">
+                 <bool>false</bool>
+                </attribute>
+                <column/>
+                <column/>
+               </widget>
               </item>
              </layout>
-            </item>
-            <item>
-             <widget class="QTableWidget" name="productTable">
-              <property name="sizePolicy">
-               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-                <horstretch>0</horstretch>
-                <verstretch>0</verstretch>
-               </sizepolicy>
-              </property>
-              <property name="minimumSize">
-               <size>
-                <width>0</width>
-                <height>32</height>
-               </size>
-              </property>
-              <property name="verticalScrollBarPolicy">
-               <enum>Qt::ScrollBarAlwaysOff</enum>
-              </property>
-              <property name="horizontalScrollBarPolicy">
-               <enum>Qt::ScrollBarAlwaysOff</enum>
-              </property>
-              <property name="sizeAdjustPolicy">
-               <enum>QAbstractScrollArea::AdjustToContents</enum>
-              </property>
-              <property name="editTriggers">
-               <set>QAbstractItemView::NoEditTriggers</set>
-              </property>
-              <property name="showDropIndicator" stdset="0">
-               <bool>false</bool>
-              </property>
-              <property name="dragDropOverwriteMode">
-               <bool>false</bool>
-              </property>
-              <property name="alternatingRowColors">
-               <bool>true</bool>
-              </property>
-              <property name="selectionMode">
-               <enum>QAbstractItemView::SingleSelection</enum>
-              </property>
-              <property name="showGrid">
-               <bool>false</bool>
-              </property>
-              <property name="columnCount">
-               <number>2</number>
-              </property>
-              <attribute name="horizontalHeaderVisible">
-               <bool>false</bool>
-              </attribute>
-              <attribute name="horizontalHeaderMinimumSectionSize">
-               <number>24</number>
-              </attribute>
-              <attribute name="horizontalHeaderDefaultSectionSize">
-               <number>24</number>
-              </attribute>
-              <attribute name="horizontalHeaderStretchLastSection">
-               <bool>true</bool>
-              </attribute>
-              <attribute name="verticalHeaderVisible">
-               <bool>false</bool>
-              </attribute>
-              <attribute name="verticalHeaderStretchLastSection">
-               <bool>false</bool>
-              </attribute>
-              <column/>
-              <column/>
-             </widget>
-            </item>
-           </layout>
-          </item>
-          <item>
-           <spacer name="verticalSpacer_3">
-            <property name="orientation">
-             <enum>Qt::Vertical</enum>
-            </property>
-            <property name="sizeType">
-             <enum>QSizePolicy::Fixed</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>0</width>
-              <height>8</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
-          <item>
-           <layout class="QHBoxLayout" name="OutgoingDependenciesLayout">
-            <item>
-             <widget class="QLabel" name="outgoingSourceDependenciesTitleLabel">
-              <property name="font">
-               <font>
-                <family>12pt</family>
-                <weight>75</weight>
-                <italic>false</italic>
-                <bold>true</bold>
-               </font>
-              </property>
-              <property name="styleSheet">
-               <string notr="true">font: bold, 12pt;</string>
-              </property>
-              <property name="text">
-               <string>Outgoing Source Dependencies:</string>
-              </property>
-              <property name="alignment">
-               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-              </property>
-              <property name="margin">
-               <number>0</number>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QLabel" name="outgoingSourceDependenciesValueLabel">
-              <property name="text">
-               <string>Number of outgoing source dependencies here</string>
-              </property>
-              <property name="textInteractionFlags">
-               <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-              </property>
+            </widget>
+
+             <widget class="QWidget" name="IntermediateAssetsTab">
+               <attribute name="title">
+                 <string>Intermediate Assets</string>
+               </attribute>
+               <layout class="QVBoxLayout" name="IntermediateAssetsLayout" stretch="0">
+                 <item>
+                   <widget class="QTableWidget" name="IntermediateAssetsTable">
+                     <property name="verticalScrollBarPolicy">
+                       <enum>Qt::ScrollBarAsNeeded</enum>
+                     </property>
+                     <property name="horizontalScrollBarPolicy">
+                       <enum>Qt::ScrollBarAsNeeded</enum>
+                     </property>
+                     <property name="sizeAdjustPolicy">
+                       <enum>QAbstractScrollArea::AdjustIgnored</enum>
+                     </property>
+                     <property name="editTriggers">
+                       <set>QAbstractItemView::NoEditTriggers</set>
+                     </property>
+                     <property name="showDropIndicator" stdset="0">
+                       <bool>false</bool>
+                     </property>
+                     <property name="dragDropOverwriteMode">
+                       <bool>false</bool>
+                     </property>
+                     <property name="alternatingRowColors">
+                       <bool>true</bool>
+                     </property>
+                     <property name="selectionMode">
+                       <enum>QAbstractItemView::SingleSelection</enum>
+                     </property>
+                     <property name="showGrid">
+                       <bool>false</bool>
+                     </property>
+                     <property name="columnCount">
+                       <number>2</number>
+                     </property>
+                     <attribute name="horizontalHeaderVisible">
+                       <bool>false</bool>
+                     </attribute>
+                     <attribute name="horizontalHeaderMinimumSectionSize">
+                       <number>24</number>
+                     </attribute>
+                     <attribute name="horizontalHeaderDefaultSectionSize">
+                       <number>24</number>
+                     </attribute>
+                     <attribute name="horizontalHeaderStretchLastSection">
+                       <bool>true</bool>
+                     </attribute>
+                     <attribute name="verticalHeaderVisible">
+                       <bool>false</bool>
+                     </attribute>
+                     <attribute name="verticalHeaderStretchLastSection">
+                       <bool>false</bool>
+                     </attribute>
+                     <column/>
+                     <column/>
+                   </widget>
+                 </item>
+               </layout>
              </widget>
-            </item>
-            <item>
-             <spacer name="horizontalSpacer_2">
-              <property name="orientation">
-               <enum>Qt::Horizontal</enum>
-              </property>
-              <property name="sizeHint" stdset="0">
-               <size>
-                <width>40</width>
-                <height>20</height>
-               </size>
-              </property>
-             </spacer>
-            </item>
-           </layout>
-          </item>
-          <item>
-           <widget class="QTableWidget" name="outgoingSourceDependenciesTable">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-              <horstretch>0</horstretch>
-              <verstretch>0</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="minimumSize">
-             <size>
-              <width>0</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="verticalScrollBarPolicy">
-             <enum>Qt::ScrollBarAlwaysOff</enum>
-            </property>
-            <property name="horizontalScrollBarPolicy">
-             <enum>Qt::ScrollBarAlwaysOff</enum>
-            </property>
-            <property name="sizeAdjustPolicy">
-             <enum>QAbstractScrollArea::AdjustToContents</enum>
-            </property>
-            <property name="editTriggers">
-             <set>QAbstractItemView::NoEditTriggers</set>
-            </property>
-            <property name="showDropIndicator" stdset="0">
-             <bool>false</bool>
-            </property>
-            <property name="dragDropOverwriteMode">
-             <bool>false</bool>
-            </property>
-            <property name="alternatingRowColors">
-             <bool>true</bool>
-            </property>
-            <property name="selectionMode">
-             <enum>QAbstractItemView::SingleSelection</enum>
-            </property>
-            <property name="showGrid">
-             <bool>false</bool>
-            </property>
-            <property name="columnCount">
-             <number>2</number>
-            </property>
-            <attribute name="horizontalHeaderVisible">
-             <bool>false</bool>
-            </attribute>
-            <attribute name="horizontalHeaderMinimumSectionSize">
-             <number>24</number>
-            </attribute>
-            <attribute name="horizontalHeaderDefaultSectionSize">
-             <number>24</number>
-            </attribute>
-            <attribute name="horizontalHeaderStretchLastSection">
-             <bool>true</bool>
-            </attribute>
-            <attribute name="verticalHeaderVisible">
-             <bool>false</bool>
-            </attribute>
-            <attribute name="verticalHeaderStretchLastSection">
-             <bool>false</bool>
-            </attribute>
-            <column/>
-            <column/>
            </widget>
           </item>
-          <item>
-           <spacer name="verticalSpacer_4">
-            <property name="orientation">
-             <enum>Qt::Vertical</enum>
-            </property>
-            <property name="sizeType">
-             <enum>QSizePolicy::Fixed</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>0</width>
-              <height>8</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
-          <item>
-           <layout class="QHBoxLayout" name="IncomingDependenciesLayout">
-            <item>
-             <widget class="QLabel" name="incomingSourceDependenciesTitleLabel">
-              <property name="font">
-               <font>
-                <family>12pt</family>
-                <weight>75</weight>
-                <italic>false</italic>
-                <bold>true</bold>
-               </font>
-              </property>
-              <property name="styleSheet">
-               <string notr="true">font: bold, 12pt;</string>
-              </property>
-              <property name="text">
-               <string>Incoming Source Dependencies:</string>
-              </property>
-              <property name="alignment">
-               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-              </property>
-              <property name="margin">
-               <number>0</number>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QLabel" name="incomingSourceDependenciesValueLabel">
-              <property name="text">
-               <string>Number of incoming source dependencies here</string>
-              </property>
-              <property name="textInteractionFlags">
-               <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <spacer name="horizontalSpacer_3">
-              <property name="orientation">
-               <enum>Qt::Horizontal</enum>
-              </property>
-              <property name="sizeHint" stdset="0">
-               <size>
-                <width>40</width>
-                <height>20</height>
-               </size>
-              </property>
-             </spacer>
-            </item>
-           </layout>
-          </item>
-          <item>
-           <widget class="QTableWidget" name="incomingSourceDependenciesTable">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-              <horstretch>0</horstretch>
-              <verstretch>0</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="minimumSize">
-             <size>
-              <width>0</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="verticalScrollBarPolicy">
-             <enum>Qt::ScrollBarAlwaysOff</enum>
-            </property>
-            <property name="horizontalScrollBarPolicy">
-             <enum>Qt::ScrollBarAlwaysOff</enum>
-            </property>
-            <property name="sizeAdjustPolicy">
-             <enum>QAbstractScrollArea::AdjustToContents</enum>
-            </property>
-            <property name="editTriggers">
-             <set>QAbstractItemView::NoEditTriggers</set>
-            </property>
-            <property name="showDropIndicator" stdset="0">
-             <bool>false</bool>
-            </property>
-            <property name="dragDropOverwriteMode">
-             <bool>false</bool>
-            </property>
-            <property name="alternatingRowColors">
-             <bool>true</bool>
-            </property>
-            <property name="selectionMode">
-             <enum>QAbstractItemView::SingleSelection</enum>
-            </property>
-            <property name="showGrid">
-             <bool>false</bool>
-            </property>
-            <property name="columnCount">
-             <number>2</number>
-            </property>
-            <attribute name="horizontalHeaderVisible">
-             <bool>false</bool>
-            </attribute>
-            <attribute name="horizontalHeaderMinimumSectionSize">
-             <number>24</number>
-            </attribute>
-            <attribute name="horizontalHeaderDefaultSectionSize">
-             <number>24</number>
-            </attribute>
-            <attribute name="horizontalHeaderStretchLastSection">
-             <bool>true</bool>
-            </attribute>
-            <attribute name="verticalHeaderVisible">
-             <bool>false</bool>
-            </attribute>
-            <attribute name="verticalHeaderStretchLastSection">
-             <bool>false</bool>
-            </attribute>
-            <column/>
-            <column/>
-           </widget>
-          </item>
-          <item>
-           <spacer name="verticalSpacer">
-            <property name="orientation">
-             <enum>Qt::Vertical</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>20</width>
-              <height>40</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
          </layout>
         </widget>
        </widget>
@@ -721,6 +606,16 @@
    </item>
   </layout>
  </widget>
- <resources/>
+  <customwidgets>
+    <customwidget>
+      <class>AssetProcessor::GoToButton</class>
+      <extends>QPushButton</extends>
+      <header>native/ui/GoToButton.h</header>
+      <container>1</container>
+    </customwidget>
+  </customwidgets>
+  <resources>
+    <include location="style/AssetProcessor.qrc"/>
+  </resources>
  <connections/>
 </ui>

+ 65 - 11
Code/Tools/AssetProcessor/native/ui/SourceAssetTreeModel.cpp

@@ -73,19 +73,66 @@ namespace AssetProcessor
                 }
                 return true;
             });
-        m_sharedDbConnection->QuerySourceAndScanfolder(
-            [&](AzToolsFramework::AssetDatabase::SourceAndScanFolderDatabaseEntry& sourceAndScanFolder)
-            {
-                if (statsTable.count(sourceAndScanFolder.m_sourceName))
+ 
+        if (!m_intermediateAssets)
+        {
+            // AddOrUpdateEntry will remove intermediate assets if they shouldn't be included in this tree.
+            m_sharedDbConnection->QuerySourceAndScanfolder(
+                [&](AzToolsFramework::AssetDatabase::SourceAndScanFolderDatabaseEntry& sourceAndScanFolder)
                 {
-                    AddOrUpdateEntry(sourceAndScanFolder, sourceAndScanFolder, true, statsTable[sourceAndScanFolder.m_sourceName]);
-                }
-                else
+                    if (statsTable.count(sourceAndScanFolder.m_sourceName))
+                    {
+                        AddOrUpdateEntry(sourceAndScanFolder, sourceAndScanFolder, true, statsTable[sourceAndScanFolder.m_sourceName]);
+                    }
+                    else
+                    {
+                        AddOrUpdateEntry(sourceAndScanFolder, sourceAndScanFolder, true);
+                    }
+                    return true; // return true to continue iterating over additional results, we are populating a container
+                });
+        }
+        else
+        {
+            AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolderEntry;
+
+            if(!m_intermediateAssetFolderId.has_value())
+            {
+                // When building the intermediate asset source asset tree, search by the scan folder to save time
+                m_sharedDbConnection->QueryScanFolderByPortableKey(
+                    AssetProcessor::IntermediateAssetsFolderName,
+                    [&](AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry& scanFolder)
+                    {
+                        m_intermediateAssetFolderId = scanFolder.m_scanFolderID;
+                        scanFolderEntry = scanFolder;
+                        return false;
+                    });
+            }
+            else
+            {
+                m_sharedDbConnection->QueryScanFolderByScanFolderID(
+                    m_intermediateAssetFolderId.value(),
+                    [&](AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry& scanFolder)
+                    {
+                        scanFolderEntry = scanFolder;
+                        return false;
+                    });
+            }
+
+            m_sharedDbConnection->QuerySourceByScanFolderID(
+                scanFolderEntry.m_scanFolderID,
+                [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
                 {
-                    AddOrUpdateEntry(sourceAndScanFolder, sourceAndScanFolder, true);
-                }
-                return true; // return true to continue iterating over additional results, we are populating a container
-            });
+                    if (statsTable.count(sourceEntry.m_sourceName))
+                    {
+                        AddOrUpdateEntry(sourceEntry, scanFolderEntry, true, statsTable[sourceEntry.m_sourceName]);
+                    }
+                    else
+                    {
+                        AddOrUpdateEntry(sourceEntry, scanFolderEntry, true);
+                    }
+                    return true; // return true to continue iterating over additional results, we are populating a container
+                });
+        }
     }
 
     void SourceAssetTreeModel::AddOrUpdateEntry(
@@ -113,6 +160,13 @@ namespace AssetProcessor
             return;
         }
 
+        if (m_intermediateAssetFolderId.has_value() &&
+            ((source.m_scanFolderPK == m_intermediateAssetFolderId.value() && !m_intermediateAssets) ||
+             (source.m_scanFolderPK != m_intermediateAssetFolderId.value() && m_intermediateAssets)))
+        {
+            return;
+        }
+
 
         AZ::IO::Path fullPath = AZ::IO::Path(scanFolder.m_scanFolder) / source.m_sourceName;
 

+ 9 - 0
Code/Tools/AssetProcessor/native/ui/SourceAssetTreeModel.h

@@ -30,6 +30,13 @@ namespace AssetProcessor
 
         QModelIndex GetIndexForSource(const AZStd::string& source);
 
+        void SetOnlyShowIntermediateAssets() { m_intermediateAssets = true; }
+
+        void SetIntermediateAssetFolderId(AZStd::optional<AZ::s64> intermediateAssetFolderId)
+        {
+            m_intermediateAssetFolderId = intermediateAssetFolderId;
+        }
+
     public Q_SLOTS:
         void OnCreateJobsDurationChanged(QString sourceName);
 
@@ -48,5 +55,7 @@ namespace AssetProcessor
         AZStd::unordered_map<AZ::s64, AssetTreeItem*> m_sourceIdToTreeItem;
         QDir m_assetRoot;
         bool m_assetRootSet = false;
+        bool m_intermediateAssets = false;
+        AZStd::optional<AZ::s64> m_intermediateAssetFolderId;
     };
 }

+ 9 - 0
Code/Tools/AssetProcessor/native/ui/style/AssetProcessor.qss

@@ -50,6 +50,9 @@ QLabel#portLabel {
 QTreeView#SourceAssetsTreeView,
 QTreeView#SourceAssetsTreeView::item,
 QTreeView#SourceAssetsTreeView::branch,
+QTreeView#IntermediateAssetsTreeView,
+QTreeView#IntermediateAssetsTreeView::item,
+QTreeView#IntermediateAssetsTreeView::branch,
 QTreeView#ProductAssetsTreeView,
 QTreeView#ProductAssetsTreeView::item,
 QTreeView#ProductAssetsTreeView::branch,
@@ -64,6 +67,8 @@ QTreeView#IncomingProductDependenciesTreeView::branch {
 
 QTreeView#SourceAssetsTreeView::item:hover,
 QTreeView#SourceAssetsTreeView::branch:hover,
+QTreeView#IntermediateAssetsTreeView::item:hover,
+QTreeView#IntermediateAssetsTreeView::branch:hover,
 QTreeView#ProductAssetsTreeView::item:hover,
 QTreeView#ProductAssetsTreeView::branch:hover,
 QTreeView#OutgoingProductDependenciesTreeView::item:hover,
@@ -76,6 +81,9 @@ QTreeView#IncomingProductDependenciesTreeView::branch:hover {
 QTreeView#SourceAssetsTreeView::item:selected,
 QTreeView#SourceAssetsTreeView::branch:selected,
 QTreeView#SourceAssetsTreeView::item:selected:active,
+QTreeView#IntermediateAssetsTreeView::item:selected,
+QTreeView#IntermediateAssetsTreeView::branch:selected,
+QTreeView#IntermediateAssetsTreeView::item:selected:active,
 QTreeView#ProductAssetsTreeView::item:selected,
 QTreeView#ProductAssetsTreeView::branch:selected,
 QTreeView#ProductAssetsTreeView::item:selected:active,
@@ -89,6 +97,7 @@ QTreeView#IncomingProductDependenciesTreeView::item:selected:active {
 }
 
 QTreeView#SourceAssetsTreeView::item:selected:!active,
+QTreeView#IntermediateAssetsTreeView::item:selected:!active,
 QTreeView#ProductAssetsTreeView::item:selected:!active,
 QTreeView#OutgoingProductDependenciesTreeView::item:selected:!active,
 QTreeView#IncomingProductDependenciesTreeView::item:selected:!active,

+ 8 - 0
Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp

@@ -1728,6 +1728,14 @@ namespace AssetUtilities
         ++m_warningCount;
     }
 
+    AZStd::string GetRelativeProductPathForIntermediateSourcePath(AZStd::string_view relativeSourcePath)
+    {
+        AZStd::string productPath((AZ::IO::FixedMaxPath(AssetBuilderSDK::CommonPlatformName) / relativeSourcePath).StringAsPosix());
+        // Product paths are always lowercase
+        AZStd::to_lower(productPath.begin(), productPath.end());
+        return productPath;
+    }
+
     ProductPath::ProductPath(AZStd::string scanfolderRelativeProductPath, AZStd::string platformIdentifier)
     {
         AZ_Assert(AZ::IO::PathView(scanfolderRelativeProductPath).IsRelative(), "scanfolderRelativeProductPath is not relative: %s", scanfolderRelativeProductPath.c_str());

+ 4 - 0
Code/Tools/AssetProcessor/native/utilities/assetUtils.h

@@ -273,6 +273,10 @@ namespace AssetUtilities
     //! Finds all the sources (up and down) in an intermediate output chain
     AZStd::vector<AZStd::string> GetAllIntermediateSources(
         AZ::IO::PathView relativeSourcePath, AZStd::shared_ptr<AssetProcessor::AssetDatabaseConnection> db);
+    
+    //! Given a source path for an intermediate asset, constructs the product path.
+    //! This does not verify either exist, it just manipulates the string.
+    AZStd::string GetRelativeProductPathForIntermediateSourcePath(AZStd::string_view relativeSourcePath);
 
     //! Helper class that provides various paths related to a single output asset.
     //! Files are not guaranteed to exist at the given path.