// 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 "Editor/ContentBrowser/Public/IContentBrowserSingleton.h" #include "Editor/ContentBrowser/Public/ContentBrowserModule.h" #include "Developer/AssetTools/Public/IAssetTools.h" #include "Developer/AssetTools/Public/AssetToolsModule.h" class FMenuBuilder; class UAnimBlueprint; class UAnimBoneCompressionSettings; class UAnimSequence; class UEdGraph; class UPoseWatch; class UEdGraphNode; /** 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 UNREALED_API 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() SAnimationCompressionSelectionDialog(); virtual ~SAnimationCompressionSelectionDialog(); virtual void Construct(const FArguments& InArgs, const FAnimationCompressionSelectionDialogConfig& InConfig); /** Sets the delegate handler for when an open operation is committed */ 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); 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 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); UNREALED_API void SetPoseWatch(UPoseWatch* PoseWatch, UAnimBlueprint* AnimBlueprintIfKnown = nullptr); UNREALED_API UPoseWatch* FindPoseWatchForNode(const UEdGraphNode* Node, UAnimBlueprint* AnimBlueprintIfKnown = nullptr); UNREALED_API void MakePoseWatchForNode(UAnimBlueprint* AnimBlueprint, UEdGraphNode* Node, FColor PoseWatchColour); UNREALED_API void RemovePoseWatch(UPoseWatch* PoseWatch, UAnimBlueprint* AnimBlueprintIfKnown = nullptr); UNREALED_API void UpdatePoseWatchColour(UPoseWatch* PoseWatch, FColor NewPoseWatchColour); ////////////////////////////////////////////////////////////////////////////////////////// template void ExecuteNewAnimAsset(TArray> SkeletonsOrSkeletalMeshes, const FString InSuffix, FAnimAssetCreated AssetCreated, bool bInContentBrowser) { if (bInContentBrowser && SkeletonsOrSkeletalMeshes.Num() == 1) { USkeletalMesh* SkeletalMesh = nullptr; USkeleton* Skeleton = Cast(SkeletonsOrSkeletalMeshes[0].Get()); if (Skeleton == nullptr) { SkeletalMesh = CastChecked(SkeletonsOrSkeletalMeshes[0].Get()); 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->MarkPendingKill(); } CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); } } } } } else { CreateAnimationAssets(SkeletonsOrSkeletalMeshes, T::StaticClass(), InSuffix, AssetCreated); } } } // namespace AnimationEditorUtils