// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Async/AsyncWork.h" #include "Materials/MaterialParameters.h" #include "Widgets/SCompoundWidget.h" #include "AnalyzedMaterialNode.h" class ICollectionContainer; class ITableRow; class SDockTab; class SScrollBox; class STableViewBase; class STextBlock; class SThrobber; class UMaterialInterface; template class STreeView; struct FBuildBasicMaterialTreeAsyncTask : FNonAbandonableTask { public: /** File data loaded for the async read */ TArray& MaterialTreeRoot; TArray AssetDataToAnalyze; FString ParameterFilter; /** Initializes the variables needed to load and verify the data */ FBuildBasicMaterialTreeAsyncTask(TArray& InMaterialTreeRoot, const TArray& InAssetDataToAnalyze, const FString& InParameterFilter = TEXT("")) : MaterialTreeRoot(InMaterialTreeRoot) , AssetDataToAnalyze(InAssetDataToAnalyze) , ParameterFilter(InParameterFilter) { } FAnalyzedMaterialNodePtr FindOrMakeBranchNode(FAnalyzedMaterialNodePtr ParentNode, const FAssetData* ChildData); /** * Loads and hashes the file data. Empties the data if the hash check fails */ void DoWork(); FORCEINLINE TStatId GetStatId() const { RETURN_QUICK_DECLARE_CYCLE_STAT(FBuildBasicMaterialTreeAsyncTask, STATGROUP_ThreadPoolAsyncTasks); } }; struct FAnalyzeMaterialTreeAsyncTask { public: FAnalyzedMaterialNodeRef MaterialTreeRoot; const TArray& AssetDataToAnalyze; TArray MaterialQueue; int32 CurrentMaterialQueueIndex; FAnalyzedMaterialNodeRef CurrentMaterialNode; UMaterialInterface* CurrentMaterialInterface; TArray BasePropertyOverrideInfo; TArray StaticSwitchParameterInfo; TArray StaticSwitchGuids; TArray StaticMaskParameterInfo; TArray StaticMaskGuids; FText ParameterFilter; FAnalyzeMaterialTreeAsyncTask(FAnalyzedMaterialNodeRef InMaterialTreeRoot, const TArray& InAssetDataToAnalyze, const FText& InParameterFilter = FText::GetEmpty()): MaterialTreeRoot(InMaterialTreeRoot), AssetDataToAnalyze(InAssetDataToAnalyze), CurrentMaterialQueueIndex(0), CurrentMaterialNode(InMaterialTreeRoot), ParameterFilter(InParameterFilter) { MaterialQueue.Empty(MaterialTreeRoot->TotalNumberOfChildren()); MaterialQueue.Add(MaterialTreeRoot); LoadNextMaterial(); } bool LoadNextMaterial(); void DoWork(); // need to make abandon-able eventually bool CanAbandon() { return false; } void Abandon() { } FORCEINLINE TStatId GetStatId() const { RETURN_QUICK_DECLARE_CYCLE_STAT(FAnalyzeMaterialTreeAsyncTask, STATGROUP_ThreadPoolAsyncTasks); } }; struct FPermutationSuggestionData { FText Header; TArray Materials; FPermutationSuggestionData(const FText& InHeader, TArray InMaterials) : Header(InHeader) , Materials(InMaterials) { } }; struct FPermutationSuggestionView { FText Header; TArray> Children; }; struct FAnalyzeForSuggestions { public: TMultiMap GetSuggestions() { return Suggestions; } protected: FAnalyzeForSuggestions() :Suggestions(TMultiMap()) { } virtual ~FAnalyzeForSuggestions() { } TMultiMap Suggestions; virtual void GatherSuggestions() = 0; }; struct FAnalyzeForIdenticalPermutationsAsyncTask : FAnalyzeForSuggestions { public: virtual ~FAnalyzeForIdenticalPermutationsAsyncTask() { } FAnalyzedMaterialNodeRef MaterialTreeRoot; TArray MaterialQueue; TMap> MaterialPermutationHashToMaterialObjectPath; int32 AssetCount; FAnalyzeForIdenticalPermutationsAsyncTask(FAnalyzedMaterialNodeRef InMaterialTreeRoot) : MaterialTreeRoot(InMaterialTreeRoot) { MaterialQueue.Empty(MaterialTreeRoot->TotalNumberOfChildren()); MaterialQueue.Add(MaterialTreeRoot); } bool CreateMaterialPermutationHashForNode(const FAnalyzedMaterialNodeRef& MaterialNode, uint32& OutHash); void DoWork(); // need to make abandon-able eventually bool CanAbandon() { return false; } void Abandon() { } virtual void GatherSuggestions() override; FORCEINLINE TStatId GetStatId() const { RETURN_QUICK_DECLARE_CYCLE_STAT(FAnalyzeForIdenticalPermutationsAsyncTask, STATGROUP_ThreadPoolAsyncTasks); } }; class SMaterialAnalyzer : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SMaterialAnalyzer) { } SLATE_END_ARGS() typedef STreeView SAnalyzedMaterialTree; public: SMaterialAnalyzer(); virtual ~SMaterialAnalyzer(); /** * Constructs the application. * * @param InArgs The Slate argument list. * @param ConstructUnderMajorTab The major tab which will contain the session front-end. * @param ConstructUnderWindow The window in which this widget is being constructed. * @param InStyleSet The style set to use. */ void Construct(const FArguments& InArgs, const TSharedRef& ConstructUnderMajorTab, const TSharedPtr& ConstructUnderWindow); virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; TSharedRef< ITableRow > OnGenerateSuggestionRow(TSharedPtr Item, const TSharedRef< STableViewBase >& OwnerTable); EVisibility ShouldShowAdvancedRecommendations(TSharedPtr Item) const; void OnAssetAdded(const FAssetData& InAssetData); protected: TArray AssetDataArray; TArray RecentlyAddedAssetData; TArray RecentlyRemovedAssetData; TSharedPtr MaterialTree; TSharedPtr StatusBox; TSharedPtr SuggestionsBox; TSharedPtr StatusThrobber; TArray AllMaterialTreeRoots; TArray MaterialTreeRoot; /** The tree view widget */ TSharedPtr>> SuggestionsTree; TArray> SuggestionDataArray; FAsyncTask* BuildBaseMaterialTreeTask; FAsyncTask* AnalyzeTreeTask; FAsyncTask* AnalyzeForIdenticalPermutationsTask; FAssetData CurrentlySelectedAsset; bool bWaitingForAssetRegistryLoad; void OnAssetSelected(const FAssetData& AssetData); void OnParameterFilterChanged(const FText& Filter, const ETextCommit::Type InTextAction); FReply OnExportAnalyzedMaterialToCSV(); void BuildBasicMaterialTree(); int32 GetTotalNumberOfMaterialNodes(); void SetupAssetRegistryCallbacks(); void OnGetSuggestionChildren(TSharedPtr InParent, TArray< TSharedPtr >& OutChildren); FReply CreateLocalSuggestionCollectionClicked(TSharedPtr InSuggestion); void CreateLocalSuggestionCollection(ICollectionContainer& InCollectionContainer, const FPermutationSuggestionView& InSuggestion); void StartAsyncWork(const FText& WorkText); void AsyncWorkFinished(const FText& CompleteText); FString GetCurrentAssetPath() const; private: /** Callback for generating a row in the reflector tree view. */ TSharedRef HandleReflectorTreeGenerateRow(FAnalyzedMaterialNodeRef InReflectorNode, const TSharedRef& OwnerTable); /** Callback for getting the child items of the given reflector tree node. */ void HandleReflectorTreeGetChildren(FAnalyzedMaterialNodeRef InReflectorNode, TArray& OutChildren); /** * Called to recursively expand/collapse the children of the given item * * @param InTreeNode The node that was expanded or collapsed * @param bIsItemExpanded True if the item is expanded, false if it is collapsed */ void HandleReflectorTreeRecursiveExpansion(FAnalyzedMaterialNodeRef InTreeNode, bool bIsItemExpanded); void UpdateViewForSelectedAsset(); bool IsMaterialSelectionAllowed() const { // Don't allow to change selected material while async work is operating on currently selected asset. return !bIsAsyncWorkInProgress; } private: bool bIsAsyncWorkInProgress = false; FText ParameterFilter; bool bHasParameterFilterChanged = false; };