// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "Templates/SubclassOf.h" #include "Modules/ModuleManager.h" #include "Misc/PackageName.h" #include "Input/Reply.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SWindow.h" #include "Animation/Skeleton.h" #include "Animation/AnimationAsset.h" #include "IContentBrowserSingleton.h" #include "ContentBrowserModule.h" #include "IAssetTools.h" #include "AssetToolsModule.h" #include "Engine/SkeletalMesh.h" #include "UObject/SoftObjectPtr.h" class FMenuBuilder; class UAnimBlueprint; class UAnimBoneCompressionSettings; class UAnimSequence; class UEdGraph; class UPoseWatch; class UEdGraphNode; class UAnimBlueprintGeneratedClass; class UAnimGraphNode_Base; /** dialog to prompt users to decide an animation asset name */ class SCreateAnimationAssetDlg : public SWindow { public: SLATE_BEGIN_ARGS(SCreateAnimationAssetDlg) { } SLATE_ARGUMENT(FText, DefaultAssetPath) SLATE_END_ARGS() SCreateAnimationAssetDlg() : UserResponse(EAppReturnType::Cancel) { } void Construct(const FArguments& InArgs); public: /** Displays the dialog in a blocking fashion */ EAppReturnType::Type ShowModal(); /** Gets the resulting asset path */ FString GetAssetPath(); /** Gets the resulting asset name */ FString GetAssetName(); /** Gets the resulting full asset path (path+'/'+name) */ FString GetFullAssetPath(); protected: void OnPathChange(const FString& NewPath); void OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo); FReply OnButtonClick(EAppReturnType::Type ButtonID); bool ValidatePackage(); EAppReturnType::Type UserResponse; FText AssetPath; FText AssetName; static FText LastUsedAssetPath; }; /** A struct containing the settings to control the SAnimationCompressionSelectionDialog creation. */ struct FAnimationCompressionSelectionDialogConfig { FText DialogTitleOverride; FVector2D WindowSizeOverride; UAnimBoneCompressionSettings* DefaultSelectedAsset; FAnimationCompressionSelectionDialogConfig() : WindowSizeOverride(ForceInitToZero) , DefaultSelectedAsset(nullptr) {} }; /** Dialog to prompt user to select an animation compression settings asset. */ class SAnimationCompressionSelectionDialog : public SCompoundWidget { public: /** Called from the Dialog when an asset has been selected. */ DECLARE_DELEGATE_OneParam(FOnAssetSelected, const FAssetData& /*SelectedAsset*/); SLATE_BEGIN_ARGS(SAnimationCompressionSelectionDialog) {} SLATE_END_ARGS() UNREALED_API SAnimationCompressionSelectionDialog(); UNREALED_API virtual ~SAnimationCompressionSelectionDialog(); UNREALED_API virtual void Construct(const FArguments& InArgs, const FAnimationCompressionSelectionDialogConfig& InConfig); /** Sets the delegate handler for when an open operation is committed */ UNREALED_API void SetOnAssetSelected(const FOnAssetSelected& InHandler); private: void DoSelectAsset(const FAssetData& SelectedAsset); FReply OnConfirmClicked(); FReply OnCancelClicked(); void CloseDialog(); void OnAssetSelected(const FAssetData& AssetData); void OnAssetsActivated(const TArray& SelectedAssets, EAssetTypeActivationMethod::Type ActivationType); bool IsConfirmButtonEnabled() const; /** Asset Picker used by the dialog */ TSharedPtr AssetPicker; /** The assets that are currently selected in the asset picker */ TArray CurrentlySelectedAssets; /** Used to specify that a valid asset was chosen */ bool bValidAssetChosen; /** Fired when assets are chosen for open. Only fired in open dialogs. */ FOnAssetSelected OnAssetSelectedHandler; /** Used to get the currently selected assets */ FGetCurrentSelectionDelegate GetCurrentSelectionDelegate; }; /** Defines FCanExecuteAction delegate interface. Returns false to force the caller to delete the just created assets*/ DECLARE_DELEGATE_RetVal_OneParam(bool, FAnimAssetCreated, TArray); //Animation editor utility functions namespace AnimationEditorUtils { UNREALED_API FAssetData CreateModalAnimationCompressionSelectionDialog(const FAnimationCompressionSelectionDialogConfig& InConfig); UNREALED_API void CreateAnimationAssets(const TArray>& SkeletonsOrSkeletalMeshes, TSubclassOf AssetClass, const FString& InPrefix, FAnimAssetCreated AssetCreated, UObject* NameBaseObject = nullptr, bool bDoNotShowNameDialog = false, bool bAllowReplaceExisting = false); UNREALED_API void CreateNewAnimBlueprint(TArray> SkeletonsOrSkeletalMeshes, FAnimAssetCreated AssetCreated, bool bInContentBrowser); UNREALED_API void CreateNewAnimBlueprint(TArray> SkeletonsOrSkeletalMeshes, FAnimAssetCreated AssetCreated, bool bInContentBrowser); UNREALED_API void FillCreateAssetMenu(FMenuBuilder& MenuBuilder, const TArray>& SkeletonsOrSkeletalMeshes, FAnimAssetCreated AssetCreated, bool bInContentBrowser=true); UNREALED_API void CreateUniqueAssetName(const FString& InBasePackageName, const FString& InSuffix, FString& OutPackageName, FString& OutAssetName); /** Applies the animation compression codecs to the sequence list with optional override settings */ UNREALED_API bool ApplyCompressionAlgorithm(TArray& AnimSequencePtrs, UAnimBoneCompressionSettings* OverrideSettings); // template version of simple creating animation asset template< class T > T* CreateAnimationAsset(UObject* SkeletonOrSkeletalMesh, const FString& AssetPath, const FString& InPrefix) { USkeletalMesh* SkeletalMesh = nullptr; USkeleton* Skeleton = Cast(SkeletonOrSkeletalMesh); if (Skeleton == nullptr) { SkeletalMesh = CastChecked(SkeletonOrSkeletalMesh); Skeleton = SkeletalMesh->GetSkeleton(); } if (Skeleton) { FString Name; FString PackageName; // Determine an appropriate name CreateUniqueAssetName(AssetPath, InPrefix, PackageName, Name); // Create the asset, and assign its skeleton FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked("AssetTools"); T* NewAsset = Cast(AssetToolsModule.Get().CreateAsset(Name, FPackageName::GetLongPackagePath(PackageName), T::StaticClass(), NULL)); if (NewAsset) { NewAsset->SetSkeleton(Skeleton); if (SkeletalMesh) { NewAsset->SetPreviewMesh(SkeletalMesh); } NewAsset->MarkPackageDirty(); } return NewAsset; } return nullptr; } // The following functions are used to fix subgraph arrays for assets UNREALED_API void RegenerateSubGraphArrays(UAnimBlueprint* Blueprint); void RegenerateGraphSubGraphs(UAnimBlueprint* OwningBlueprint, UEdGraph* GraphToFix); void RemoveDuplicateSubGraphs(UEdGraph* GraphToClean); UNREALED_API void FindChildGraphsFromNodes(UEdGraph* GraphToSearch, TArray& ChildGraphs); // Is the supplied UEdGraph an Animation Graph UNREALED_API bool IsAnimGraph(UEdGraph* Graph); int32 GetPoseWatchNodeLinkID(UPoseWatch* PoseWatch, OUT UAnimBlueprintGeneratedClass*& AnimBPGenClass); UNREALED_API void SetPoseWatch(UPoseWatch* PoseWatch, UAnimBlueprint* AnimBlueprintIfKnown = nullptr); UNREALED_API UPoseWatch* FindPoseWatchForNode(const UEdGraphNode* Node, UAnimBlueprint* AnimBlueprintIfKnown=nullptr); UNREALED_API UPoseWatch* MakePoseWatchForNode(UAnimBlueprint* AnimBlueprint, UEdGraphNode* Node); UNREALED_API void RemovePoseWatch(UPoseWatch* PoseWatch, UAnimBlueprint* AnimBlueprintIfKnown=nullptr); UNREALED_API void RemovePoseWatchFromNode(UEdGraphNode* Node, UAnimBlueprint* AnimBlueprint); UNREALED_API void RemovePoseWatchesFromGraph(UAnimBlueprint* AnimBlueprint, class UEdGraph* Graph); // Delegate fired when a pose watch is added or removed DECLARE_MULTICAST_DELEGATE_TwoParams(FOnPoseWatchesChanged, UAnimBlueprint* /*InAnimBlueprint*/, UEdGraphNode* /*InNode*/); UNREALED_API FOnPoseWatchesChanged& OnPoseWatchesChanged(); UNREALED_API void SetupDebugLinkedAnimInstances(UAnimBlueprint* InAnimBlueprint, UObject* InRootObjectBeingDebugged); ////////////////////////////////////////////////////////////////////////////////////////// template void ExecuteNewAnimAsset(TArray> SkeletonsOrSkeletalMeshes, const FString InSuffix, FAnimAssetCreated AssetCreated, bool bInContentBrowser, bool bAllowReplaceExisting) { if(bInContentBrowser && SkeletonsOrSkeletalMeshes.Num() == 1) { USkeletalMesh* SkeletalMesh = nullptr; USkeleton* Skeleton = Cast(SkeletonsOrSkeletalMeshes[0].LoadSynchronous()); if (Skeleton == nullptr) { SkeletalMesh = CastChecked(SkeletonsOrSkeletalMeshes[0].LoadSynchronous()); Skeleton = SkeletalMesh->GetSkeleton(); } if(Skeleton) { // Determine an appropriate name for inline-rename FString Name; FString PackageName; CreateUniqueAssetName(Skeleton->GetOutermost()->GetName(), InSuffix, PackageName, Name); TFactory* Factory = NewObject(); Factory->TargetSkeleton = Skeleton; Factory->PreviewSkeletalMesh = SkeletalMesh; FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); ContentBrowserModule.Get().CreateNewAsset(Name, FPackageName::GetLongPackagePath(PackageName), T::StaticClass(), Factory); if(AssetCreated.IsBound()) { // @TODO: this doesn't work //FString LongPackagePath = FPackageName::GetLongPackagePath(PackageName); UObject* Parent = FindPackage(NULL, *PackageName); UObject* NewAsset = FindObject(Parent, *Name, false); if(NewAsset) { TArray NewAssets; NewAssets.Add(NewAsset); if (!AssetCreated.Execute(NewAssets)) { //Destroy the assets we just create for (UObject* ObjectToDelete : NewAssets) { ObjectToDelete->ClearFlags(RF_Standalone | RF_Public); ObjectToDelete->RemoveFromRoot(); ObjectToDelete->MarkAsGarbage(); } CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); } } } } } else { UObject* NameBaseObject = nullptr; const bool bDoNotShowNameDialog = false; CreateAnimationAssets(SkeletonsOrSkeletalMeshes, T::StaticClass(), InSuffix, AssetCreated, NameBaseObject, bDoNotShowNameDialog, bAllowReplaceExisting); } } } // namespace AnimationEditorUtils