// Copyright Epic Games, Inc. All Rights Reserved. #include "PersonaToolkit.h" #include "Modules/ModuleManager.h" #include "Engine/SkeletalMesh.h" #include "Animation/AnimationAsset.h" #include "PhysicsEngine/PhysicsAsset.h" #include "AnimationEditorPreviewScene.h" #include "ISkeletonEditorModule.h" #include "Animation/AnimBlueprint.h" #include "GameFramework/WorldSettings.h" #include "ScopedTransaction.h" #include "PersonaModule.h" #include "PersonaAssetFamily.h" #include "Interfaces/Interface_PreviewMeshProvider.h" #include "AnimationEditorPreviewActor.h" #include "Subsystems/AssetEditorSubsystem.h" FPersonaToolkit::FPersonaToolkit() : Skeleton(nullptr) , Mesh(nullptr) , AnimBlueprint(nullptr) , AnimationAsset(nullptr) , PhysicsAsset(nullptr) , Asset(nullptr) , InitialAssetClass(nullptr) { } FPersonaToolkit::~FPersonaToolkit() { PreviewScene.Reset(); } void FPersonaToolkit::Initialize(UObject* InAsset, const FPersonaToolkitArgs& PersonaToolkitArgs, USkeleton* InSkeleton) { Asset = InAsset; InitialAssetClass = Asset->GetClass(); if(IInterface_PreviewMeshProvider* PreviewMeshInterface = Cast(Asset)) { Mesh = PreviewMeshInterface->GetPreviewMesh(); } Skeleton = InSkeleton; CommonInitialSetup(PersonaToolkitArgs); } void FPersonaToolkit::Initialize(USkeleton* InSkeleton, const FPersonaToolkitArgs& PersonaToolkitArgs) { check(InSkeleton); Skeleton = InSkeleton; InitialAssetClass = USkeleton::StaticClass(); FPersonaAssetFamily AssetFamily(InSkeleton); Mesh = const_cast(AssetFamily.GetAssetOfType().Get()); CommonInitialSetup(PersonaToolkitArgs); } void FPersonaToolkit::Initialize(UAnimationAsset* InAnimationAsset, const FPersonaToolkitArgs& PersonaToolkitArgs) { check(InAnimationAsset); AnimationAsset = InAnimationAsset; InitialAssetClass = UAnimationAsset::StaticClass(); FPersonaAssetFamily AssetFamily(InAnimationAsset); Skeleton = const_cast(AssetFamily.GetAssetOfType().Get()); Mesh = const_cast(AssetFamily.GetAssetOfType().Get()); CommonInitialSetup(PersonaToolkitArgs); if (AnimationAsset != nullptr) { PreviewScene->SetPreviewAnimationAsset(AnimationAsset.Get()); } } void FPersonaToolkit::Initialize(USkeletalMesh* InSkeletalMesh, const FPersonaToolkitArgs& PersonaToolkitArgs) { check(InSkeletalMesh); Mesh = InSkeletalMesh; InitialAssetClass = USkeletalMesh::StaticClass(); FPersonaAssetFamily AssetFamily(InSkeletalMesh); Skeleton = const_cast(AssetFamily.GetAssetOfType().Get()); CommonInitialSetup(PersonaToolkitArgs); } void FPersonaToolkit::Initialize(UAnimBlueprint* InAnimBlueprint, const FPersonaToolkitArgs& PersonaToolkitArgs) { check(InAnimBlueprint); AnimBlueprint = InAnimBlueprint; InitialAssetClass = UAnimBlueprint::StaticClass(); FPersonaAssetFamily AssetFamily(InAnimBlueprint); Skeleton = const_cast(AssetFamily.GetAssetOfType().Get()); Mesh = const_cast(AssetFamily.GetAssetOfType().Get()); CommonInitialSetup(PersonaToolkitArgs); if (InAnimBlueprint->bIsTemplate) { bPreviewMeshCanUseDifferentSkeleton = true; } } void FPersonaToolkit::Initialize(UPhysicsAsset* InPhysicsAsset, const FPersonaToolkitArgs& PersonaToolkitArgs) { check(InPhysicsAsset); PhysicsAsset = InPhysicsAsset; InitialAssetClass = UPhysicsAsset::StaticClass(); FPersonaAssetFamily AssetFamily(InPhysicsAsset); Skeleton = const_cast(AssetFamily.GetAssetOfType().Get()); Mesh = const_cast(AssetFamily.GetAssetOfType().Get()); CommonInitialSetup(PersonaToolkitArgs); } void FPersonaToolkit::CommonInitialSetup(const FPersonaToolkitArgs& PersonaToolkitArgs) { if (PersonaToolkitArgs.bCreatePreviewScene) { CreatePreviewScene(PersonaToolkitArgs); } OnPreviewSceneSettingsCustomized = PersonaToolkitArgs.OnPreviewSceneSettingsCustomized; bPreviewMeshCanUseDifferentSkeleton = PersonaToolkitArgs.bPreviewMeshCanUseDifferentSkeleton; } void FPersonaToolkit::CreatePreviewScene(const FPersonaToolkitArgs& PersonaToolkitArgs) { if (!PreviewScene.IsValid()) { if (!EditableSkeleton.IsValid() && Skeleton.IsValid()) { ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::LoadModuleChecked("SkeletonEditor"); EditableSkeleton = SkeletonEditorModule.CreateEditableSkeleton(Skeleton.Get()); } PreviewScene = MakeShareable(new FAnimationEditorPreviewScene(FPreviewScene::ConstructionValues().AllowAudioPlayback(true).ShouldSimulatePhysics(true), EditableSkeleton, AsShared())); PreviewScene->SetIsBeingConstructed(true); ON_SCOPE_EXIT { PreviewScene->SetIsBeingConstructed(false); }; //Temporary fix for missing attached assets - MDW PreviewScene->GetWorld()->GetWorldSettings()->SetIsTemporarilyHiddenInEditor(false); if (PersonaToolkitArgs.OnPreviewSceneCreated.IsBound()) { // Custom per-instance scene setup PersonaToolkitArgs.OnPreviewSceneCreated.Execute(PreviewScene.ToSharedRef()); } else { // setup default scene AAnimationEditorPreviewActor* Actor = PreviewScene->GetWorld()->SpawnActor(AAnimationEditorPreviewActor::StaticClass(), FTransform::Identity); PreviewScene->SetActor(Actor); // Create the preview component UDebugSkelMeshComponent* SkeletalMeshComponent = NewObject(Actor); if (GEditor->PreviewPlatform.GetEffectivePreviewFeatureLevel() <= ERHIFeatureLevel::ES3_1) { SkeletalMeshComponent->SetMobility(EComponentMobility::Static); } PreviewScene->AddComponent(SkeletalMeshComponent, FTransform::Identity); PreviewScene->SetPreviewMeshComponent(SkeletalMeshComponent); // set root component, so we can attach to it. Actor->SetRootComponent(SkeletalMeshComponent); } // allow external systems to add components or otherwise manipulate the scene FPersonaModule& PersonaModule = FModuleManager::GetModuleChecked(TEXT("Persona")); PersonaModule.OnPreviewSceneCreated().Broadcast(PreviewScene.ToSharedRef()); // if not mesh editor, we allow it to override mesh const bool bAllowOverrideMesh = GetContext() != USkeletalMesh::StaticClass()->GetFName(); // Force validation of preview attached assets (catch case of never doing it if we dont have a valid preview mesh) PreviewScene->ValidatePreviewAttachedAssets(nullptr); PreviewScene->RefreshAdditionalMeshes(false); PreviewScene->SetAllowAdditionalMeshes(bAllowOverrideMesh); bool bSetMesh = false; // Set the mesh if (Mesh.IsValid()) { PreviewScene->SetPreviewMesh(Mesh.Get(), bAllowOverrideMesh); bSetMesh = true; } if (!bSetMesh && Skeleton.IsValid()) { //If no preview mesh set, just find the first mesh that uses this skeleton USkeletalMesh* PreviewMesh = Skeleton->FindCompatibleMesh(); if (PreviewMesh) { PreviewScene->SetPreviewMesh(PreviewMesh); if(EditableSkeleton.IsValid()) { EditableSkeleton->SetPreviewMesh(PreviewMesh); } } } } } USkeleton* FPersonaToolkit::GetSkeleton() const { return Skeleton.Get(); } TSharedPtr FPersonaToolkit::GetEditableSkeleton() const { return EditableSkeleton; } UDebugSkelMeshComponent* FPersonaToolkit::GetPreviewMeshComponent() const { return PreviewScene.IsValid() ? PreviewScene->GetPreviewMeshComponent() : nullptr; } USkeletalMesh* FPersonaToolkit::GetMesh() const { return Mesh.Get(); } void FPersonaToolkit::SetMesh(class USkeletalMesh* InSkeletalMesh) { if (InSkeletalMesh != nullptr && Skeleton.IsValid()) { check(InSkeletalMesh->GetSkeleton() == Skeleton); } Mesh = InSkeletalMesh; } UAnimBlueprint* FPersonaToolkit::GetAnimBlueprint() const { return AnimBlueprint.Get(); } UAnimationAsset* FPersonaToolkit::GetAnimationAsset() const { return AnimationAsset.Get(); } void FPersonaToolkit::SetAnimationAsset(class UAnimationAsset* InAnimationAsset) { USkeleton* PreviousAnimSkeleton = (AnimationAsset != nullptr) ? AnimationAsset->GetSkeleton() : nullptr; if (InAnimationAsset != nullptr) { ensure(Skeleton->IsCompatibleForEditor(InAnimationAsset->GetSkeleton())); } AnimationAsset = InAnimationAsset; if(AnimationAsset.IsValid()) { check(InitialAssetClass == UAnimationAsset::StaticClass()); USkeletalMesh* NewPreviewMesh = GetPreviewMesh(); const USkeleton* CurrentAnimSkeleton = AnimationAsset->GetSkeleton(); if (NewPreviewMesh == nullptr && PreviousAnimSkeleton != CurrentAnimSkeleton) { NewPreviewMesh = CurrentAnimSkeleton->GetPreviewMesh(); } if (NewPreviewMesh) { GetPreviewScene()->SetPreviewMesh(NewPreviewMesh, false); } } } TSharedRef FPersonaToolkit::GetPreviewScene() const { return PreviewScene.ToSharedRef(); } USkeletalMesh* FPersonaToolkit::GetPreviewMesh() const { if (InitialAssetClass == UAnimationAsset::StaticClass()) { check(AnimationAsset.IsValid()); return AnimationAsset->GetPreviewMesh(); } else if (InitialAssetClass == UAnimBlueprint::StaticClass()) { check(AnimBlueprint.IsValid()); return AnimBlueprint->GetPreviewMesh(); } else if (InitialAssetClass == UPhysicsAsset::StaticClass()) { check(PhysicsAsset.IsValid()); return PhysicsAsset->GetPreviewMesh(); } else if(InitialAssetClass == USkeletalMesh::StaticClass()) { check(Mesh.IsValid()); return Mesh.Get(); } else if(InitialAssetClass == USkeleton::StaticClass()) { check(Skeleton.IsValid()); return Skeleton->GetPreviewMesh(); } else if(IInterface_PreviewMeshProvider* PreviewMeshInterface = Cast(Asset)) { return PreviewMeshInterface->GetPreviewMesh(); } return nullptr; } void FPersonaToolkit::SetPreviewMesh(class USkeletalMesh* InSkeletalMesh, bool bSetPreviewMeshInAsset) { // Cant set preview mesh on a skeletal mesh (makes for a confusing experience!) if (InitialAssetClass != USkeletalMesh::StaticClass()) { // If the skeleton itself is changing, then we need to re-open the asset editor bool bReOpenEditor = false; if(InSkeletalMesh != nullptr && EditableSkeleton.IsValid() && InSkeletalMesh->GetSkeleton() != &EditableSkeleton->GetSkeleton()) { bReOpenEditor = true; bSetPreviewMeshInAsset = true; } if(bSetPreviewMeshInAsset) { if (InitialAssetClass == UAnimationAsset::StaticClass()) { FScopedTransaction Transaction(NSLOCTEXT("PersonaToolkit", "SetAnimationPreviewMesh", "Set Animation Preview Mesh")); check(AnimationAsset.IsValid()); AnimationAsset->SetPreviewMesh(InSkeletalMesh); } else if (InitialAssetClass == UAnimBlueprint::StaticClass()) { FScopedTransaction Transaction(NSLOCTEXT("PersonaToolkit", "SetAnimBlueprintPreviewMesh", "Set Animation Blueprint Preview Mesh")); check(AnimBlueprint.IsValid()); AnimBlueprint->SetPreviewMesh(InSkeletalMesh); } else if (InitialAssetClass == UPhysicsAsset::StaticClass()) { FScopedTransaction Transaction(NSLOCTEXT("PersonaToolkit", "SetPhysicsAssetPreviewMesh", "Set Physics Asset Preview Mesh")); check(PhysicsAsset.IsValid()); PhysicsAsset->SetPreviewMesh(InSkeletalMesh); } else if(IInterface_PreviewMeshProvider* PreviewMeshInterface = Cast(Asset)) { PreviewMeshInterface->SetPreviewMesh(InSkeletalMesh); } else if(EditableSkeleton.IsValid()) { EditableSkeleton->SetPreviewMesh(InSkeletalMesh); } } if(bReOpenEditor) { UObject* AssetToReopen = nullptr; if (InitialAssetClass == UAnimationAsset::StaticClass()) { AssetToReopen = AnimationAsset.Get(); } else if (InitialAssetClass == UAnimBlueprint::StaticClass()) { AssetToReopen = AnimBlueprint.Get(); } else if (InitialAssetClass == UPhysicsAsset::StaticClass()) { AssetToReopen = PhysicsAsset.Get(); } else if (InitialAssetClass == USkeleton::StaticClass()) { AssetToReopen = Skeleton.Get(); } else if(IInterface_PreviewMeshProvider* PreviewMeshInterface = Cast(Asset)) { AssetToReopen = Asset.Get(); } check(AssetToReopen); GEditor->GetEditorSubsystem()->CloseAllEditorsForAsset(AssetToReopen); GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToReopen); return; } } // if it's here, it allows to replace GetPreviewScene()->SetPreviewMesh(InSkeletalMesh, false); } void FPersonaToolkit::SetPreviewAnimationBlueprint(UAnimBlueprint* InAnimBlueprint) { // Only allowed for anim blueprints if (InitialAssetClass == UAnimBlueprint::StaticClass()) { FScopedTransaction Transaction(NSLOCTEXT("PersonaToolkit", "SetAnimBlueprintPreviewBlueprint", "Set Animation Blueprint Preview Blueprint")); check(AnimBlueprint.IsValid()); AnimBlueprint->SetPreviewAnimationBlueprint(InAnimBlueprint); // Note setting the 'edited' blueprint as an overlay here GetPreviewScene()->SetPreviewAnimationBlueprint(InAnimBlueprint, AnimBlueprint.Get()); } } UAnimBlueprint* FPersonaToolkit::GetPreviewAnimationBlueprint() const { if (InitialAssetClass == UAnimBlueprint::StaticClass()) { check(AnimBlueprint.IsValid()); return AnimBlueprint->GetPreviewAnimationBlueprint(); } return nullptr; } int32 FPersonaToolkit::GetCustomData(const int32 Key) const { if (!CustomEditorData.Contains(Key)) { return INDEX_NONE; } return CustomEditorData[Key]; } void FPersonaToolkit::SetCustomData(const int32 Key, const int32 CustomData) { CustomEditorData.FindOrAdd(Key) = CustomData; } void FPersonaToolkit::CustomizeSceneSettings(IDetailLayoutBuilder& DetailBuilder) { OnPreviewSceneSettingsCustomized.ExecuteIfBound(DetailBuilder); } FName FPersonaToolkit::GetContext() const { if (InitialAssetClass != nullptr) { return InitialAssetClass->GetFName(); } return NAME_None; } bool FPersonaToolkit::CanPreviewMeshUseDifferentSkeleton() const { return bPreviewMeshCanUseDifferentSkeleton; }