// Copyright Epic Games, Inc. All Rights Reserved. #include "AnimAssetFindReplaceCurves.h" #include "ISkeletonEditorModule.h" #include "SAnimAssetFindReplace.h" #include "Animation/PoseAsset.h" #include "Animation/Skeleton.h" #include "Animation/AnimationAsset.h" #include "Animation/AnimSequence.h" #include "AssetRegistry/AssetRegistryModule.h" #include "Engine/SkeletalMesh.h" #include "Engine/SkinnedAssetCommon.h" #include "Materials/Material.h" #include "Materials/MaterialExpressionParameter.h" #include "Materials/MaterialInterface.h" #include "String/ParseTokens.h" #include "ToolMenuSection.h" #define LOCTEXT_NAMESPACE "AnimAssetFindReplaceCurves" namespace UE::AnimAssetFindReplaceCurves::Private { // Helper function used to rename material parameters in skeletal meshes static void RenameMaterialParameter(USkeletalMesh* InSkeletalMesh, FName InOldName, FName InNewName) { for(const FSkeletalMaterial& SkeletalMaterial : InSkeletalMesh->GetMaterials()) { UMaterial* Material = (SkeletalMaterial.MaterialInterface != nullptr) ? SkeletalMaterial.MaterialInterface->GetMaterial() : nullptr; if (Material == nullptr) { continue; } // Search all expressions for matching scalar parameters for(UMaterialExpression* Expression : Material->GetExpressions()) { UMaterialExpressionParameter* Parameter = Cast(Expression); if(Parameter == nullptr) { continue; } FMaterialParameterMetadata ParameterMeta; if (!Parameter->GetParameterValue(ParameterMeta)) { continue; } if(ParameterMeta.Value.Type != EMaterialParameterType::Scalar) { continue; } if(Parameter->GetParameterName() == InOldName) { FProperty* ParameterNameProperty = UMaterialExpressionParameter::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UMaterialExpressionParameter, ParameterName)); check(ParameterNameProperty); Parameter->PreEditChange(ParameterNameProperty); Parameter->ParameterName = InNewName; FPropertyChangedEvent ChangedEvent(ParameterNameProperty); Parameter->PostEditChangeProperty(ChangedEvent); } } } } // Helper function used to remove material parameters in skeletal meshes static void RemoveMaterialParameters(USkeletalMesh* InSkeletalMesh, TConstArrayView InMaterialParameterNames) { for(const FSkeletalMaterial& SkeletalMaterial : InSkeletalMesh->GetMaterials()) { bool bMaterialModified = false; UMaterial* Material = (SkeletalMaterial.MaterialInterface != nullptr) ? SkeletalMaterial.MaterialInterface->GetMaterial() : nullptr; if (Material == nullptr) { continue; } // Search all expressions for matching scalar parameters for(UMaterialExpression* Expression : Material->GetExpressions()) { UMaterialExpressionParameter* Parameter = Cast(Expression); if(Parameter == nullptr) { continue; } FMaterialParameterMetadata ParameterMeta; if (!Parameter->GetParameterValue(ParameterMeta)) { continue; } if(ParameterMeta.Value.Type != EMaterialParameterType::Scalar) { continue; } for(FName NameToRemove : InMaterialParameterNames) { if(Parameter->GetParameterName() == NameToRemove) { Material->PreEditChange(nullptr); Material->GetExpressionCollection().RemoveExpression(Parameter); Material->RemoveExpressionParameter(Parameter); Parameter->MarkAsGarbage(); Material->PostEditChange(); } } } } } } FString UAnimAssetFindReplaceCurves::GetFindResultStringFromAssetData(const FAssetData& InAssetData) const { if(GetFindString().IsEmpty()) { return FString(); } auto GetMatchingCurveNamesForAsset = [this](const FAssetData& InAssetData, TArray& OutCurveNames, FName InTag, const FString& InDelimiter) { const FString TagValue = InAssetData.GetTagValueRef(InTag); if (!TagValue.IsEmpty()) { UE::String::ParseTokens(TagValue, *InDelimiter, [this, &OutCurveNames](FStringView InToken) { if(GetFindWholeWord()) { if(InToken.Compare(GetFindString(), GetSearchCase()) == 0) { OutCurveNames.Add(FString(InToken)); } } else { if(UE::String::FindFirst(InToken, GetFindString(), GetSearchCase()) != INDEX_NONE) { OutCurveNames.Add(FString(InToken)); } } }, UE::String::EParseTokensOptions::SkipEmpty); } }; TStringBuilder<128> Builder; TArray CurveNames; GetMatchingCurveNamesForAsset(InAssetData, CurveNames, USkeleton::CurveNameTag, USkeleton::CurveTagDelimiter); if(CurveNames.Num() > 0) { for(int32 NameIndex = 0; NameIndex < CurveNames.Num(); ++NameIndex) { Builder.Append(CurveNames[NameIndex]); if(NameIndex != CurveNames.Num() - 1) { Builder.Append(TEXT(", ")); } } } if(GetSearchMorphTargets()) { UClass* AssetClass = InAssetData.GetClass(); if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { TArray MorphNames; GetMatchingCurveNamesForAsset(InAssetData, MorphNames, USkeletalMesh::MorphNamesTag, USkeletalMesh::MorphNamesTagDelimiter); if(MorphNames.Num() > 0) { if(Builder.Len() > 0) { Builder.Append(TEXT(" ")); } Builder.Append(LOCTEXT("MorphTargets", "Morph targets: ").ToString()); for(int32 NameIndex = 0; NameIndex < MorphNames.Num(); ++NameIndex) { Builder.Append(MorphNames[NameIndex]); if(NameIndex != MorphNames.Num() - 1) { Builder.Append(TEXT(", ")); } } } } } if(GetSearchMaterials()) { UClass* AssetClass = InAssetData.GetClass(); if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { TArray MaterialNames; GetMatchingCurveNamesForAsset(InAssetData, MaterialNames, USkeletalMesh::MaterialParamNamesTag, USkeletalMesh::MaterialParamNamesTagDelimiter); if(MaterialNames.Num() > 0) { if(Builder.Len() > 0) { Builder.Append(TEXT(" ")); } Builder.Append(LOCTEXT("MaterialParameters", "Material parameters: ").ToString()); for(int32 NameIndex = 0; NameIndex < MaterialNames.Num(); ++NameIndex) { Builder.Append(MaterialNames[NameIndex]); if(NameIndex != MaterialNames.Num() - 1) { Builder.Append(TEXT(", ")); } } } } } return FString(Builder.ToString()); } TConstArrayView UAnimAssetFindReplaceCurves::GetSupportedAssetTypes() const { static UClass* Types[] = { UPoseAsset::StaticClass(), UAnimSequenceBase::StaticClass(), USkeleton::StaticClass(), USkeletalMesh::StaticClass() }; return Types; } bool UAnimAssetFindReplaceCurves::ShouldFilterOutAsset(const FAssetData& InAssetData, bool& bOutIsOldAsset) const { UClass* AssetClass = InAssetData.GetClass(); FString TagValue; if(InAssetData.GetTagValue(USkeleton::CurveNameTag, TagValue)) { bOutIsOldAsset = false; } else { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); if(AssetClass->IsChildOf(UAnimSequenceBase::StaticClass())) { // Check the package object version - the asset was saving empty tags for curves, so the absence of curves is // not the same as an empty value FAssetPackageData PackageData; const UE::AssetRegistry::EExists PackageExists = AssetRegistryModule.Get().TryGetAssetPackageData(InAssetData.PackageName, PackageData); if (PackageExists == UE::AssetRegistry::EExists::Exists) { bOutIsOldAsset = PackageData.FileVersionUE < VER_UE4_SKELETON_ADD_SMARTNAMES; } else { // Does not exist or unknown - treat it as 'old' bOutIsOldAsset = true; } } else if(AssetClass->IsChildOf(UPoseAsset::StaticClass())) { // Check the package custom version - the asset was saving empty tags for curves, so the absence of curves is // not the same as an empty value FAssetPackageData PackageData; const UE::AssetRegistry::EExists PackageExists = AssetRegistryModule.Get().TryGetAssetPackageData(InAssetData.PackageName, PackageData); if (PackageExists == UE::AssetRegistry::EExists::Exists) { for(const UE::AssetRegistry::FPackageCustomVersion& CustomVersion : PackageData.GetCustomVersions()) { if(CustomVersion.Key == FAnimPhysObjectVersion::GUID) { bOutIsOldAsset = CustomVersion.Version < FAnimPhysObjectVersion::SmartNameRefactorForDeterministicCooking; } } // No FAnimPhysObjectVersion, treat as old bOutIsOldAsset = true; } else { // Does not exist or unknown - treat it as 'old' bOutIsOldAsset = true; } } else if(AssetClass->IsChildOf(USkeleton::StaticClass())) { bOutIsOldAsset = true; } else if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { // Skeletal meshes didn't have curves before, so cant be 'old' bOutIsOldAsset = false; } else { // Assume unknown assets are not 'old' bOutIsOldAsset = false; } } if(GetSearchMorphTargets()) { if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { FString MorphTagValue; if(!InAssetData.GetTagValue(USkeletalMesh::MorphNamesTag, MorphTagValue)) { bOutIsOldAsset = true; } } } if(GetSearchMaterials()) { if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { FString MaterialParamTagValue; if(!InAssetData.GetTagValue(USkeletalMesh::MaterialParamNamesTag, MaterialParamTagValue)) { bOutIsOldAsset = true; } } } if(GetFindString().IsEmpty()) { return true; } if(GetSkeletonFilter().IsValid() ) { if(InAssetData.GetClass() != USkeleton::StaticClass()) { FString SkeletonPath; if(InAssetData.GetTagValue(TEXT("Skeleton"), SkeletonPath)) { if(SkeletonPath != GetSkeletonFilter().GetExportTextName()) { return true; } } } else { if(InAssetData.ToSoftObjectPath() != GetSkeletonFilter().ToSoftObjectPath()) { return true; } } } bool bFoundMatch = false; UE::String::ParseTokens(TagValue, *USkeleton::CurveTagDelimiter, [this, &bFoundMatch](FStringView InToken) { if(NameMatches(InToken)) { bFoundMatch = true; } }, UE::String::EParseTokensOptions::SkipEmpty); if(GetSearchMorphTargets()) { if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { FString MorphTagValue; if(InAssetData.GetTagValue(USkeletalMesh::MorphNamesTag, MorphTagValue)) { UE::String::ParseTokens(MorphTagValue, *USkeletalMesh::MorphNamesTagDelimiter, [this, &bFoundMatch](FStringView InToken) { if(NameMatches(InToken)) { bFoundMatch = true; } }, UE::String::EParseTokensOptions::SkipEmpty); } } } if(GetSearchMaterials()) { if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { FString MaterialParamTagValue; if(InAssetData.GetTagValue(USkeletalMesh::MaterialParamNamesTag, MaterialParamTagValue)) { UE::String::ParseTokens(MaterialParamTagValue, *USkeletalMesh::MaterialParamNamesTagDelimiter, [this, &bFoundMatch](FStringView InToken) { if(NameMatches(InToken)) { bFoundMatch = true; } }, UE::String::EParseTokensOptions::SkipEmpty); } } } return !bFoundMatch; } void UAnimAssetFindReplaceCurves::ReplaceInAsset(const FAssetData& InAssetData) const { if(UObject* Asset = InAssetData.GetAsset()) { if(UAnimSequenceBase* AnimSequenceBase = Cast(Asset)) { Asset->MarkPackageDirty(); if(GetFindWholeWord()) { const FAnimationCurveIdentifier FindCurveId(FName(GetFindString()), ERawCurveTrackTypes::RCT_Float); const FAnimationCurveIdentifier ReplaceCurveId(FName(GetReplaceString()), ERawCurveTrackTypes::RCT_Float); IAnimationDataController::FScopedBracket ScopedBracket(AnimSequenceBase->GetController(), LOCTEXT("ReplaceCurves", "Replace Curves")); AnimSequenceBase->GetController().RenameCurve(FindCurveId, ReplaceCurveId); } else { TArray> FindReplacePairs; const TArray& Curves = AnimSequenceBase->GetDataModel()->GetFloatCurves(); for(const FFloatCurve& Curve : Curves) { FString CurveName = Curve.GetName().ToString(); if(NameMatches(CurveName)) { const FAnimationCurveIdentifier FindCurveId(*CurveName, ERawCurveTrackTypes::RCT_Float); const FString NewName = CurveName.Replace(*GetFindStringRef(), *GetReplaceStringRef(), GetSearchCase()); const FAnimationCurveIdentifier ReplaceCurveId(*NewName, ERawCurveTrackTypes::RCT_Float); FindReplacePairs.Emplace(FindCurveId, ReplaceCurveId); } } if(FindReplacePairs.Num() > 0) { IAnimationDataController::FScopedBracket ScopedBracket(AnimSequenceBase->GetController(), LOCTEXT("ReplaceCurves", "Replace Curves")); for(const TPair& FindReplacePair : FindReplacePairs) { AnimSequenceBase->GetController().RenameCurve(FindReplacePair.Key, FindReplacePair.Value); } } } } else if(UPoseAsset* PoseAsset = Cast(Asset)) { if(GetFindWholeWord()) { Asset->Modify(); const FName FindCurveName(GetFindString()); const FName ReplaceCurveName(GetReplaceString()); PoseAsset->RenamePoseOrCurveName(FindCurveName, ReplaceCurveName); } else { TArray> FindReplacePairs; for(const FName& PoseName : PoseAsset->GetPoseFNames()) { FString CurveName = PoseName.ToString(); if(NameMatches(CurveName)) { const FName FindCurveName(*CurveName); const FString NewName = CurveName.Replace(*GetFindStringRef(), *GetReplaceStringRef(), GetSearchCase()); const FName ReplaceCurveName(*NewName); FindReplacePairs.Emplace(FindCurveName, ReplaceCurveName); } } if(FindReplacePairs.Num() > 0) { Asset->Modify(); for(const TPair& FindReplacePair : FindReplacePairs) { PoseAsset->RenamePoseOrCurveName(FindReplacePair.Key, FindReplacePair.Value); } } } } else if(USkeleton* Skeleton = Cast(Asset)) { if(GetFindWholeWord()) { Asset->Modify(); const FName FindCurveName(GetFindString()); const FName ReplaceCurveName(GetReplaceString()); Skeleton->RenameCurveMetaData(FindCurveName, ReplaceCurveName); } else { TArray> FindReplacePairs; Skeleton->ForEachCurveMetaData([this, &FindReplacePairs](FName InCurveName, const FCurveMetaData& InMetaData) { const FString CurveNameString = InCurveName.ToString(); if(NameMatches(CurveNameString)) { const FName FindCurveName(InCurveName); const FString NewName = CurveNameString.Replace(*GetFindStringRef(), *GetReplaceStringRef(), GetSearchCase()); const FName ReplaceCurveName(*NewName); FindReplacePairs.Emplace(FindCurveName, ReplaceCurveName); } }); if(FindReplacePairs.Num() > 0) { Asset->Modify(); for(const TPair& FindReplacePair : FindReplacePairs) { Skeleton->RenameCurveMetaData(FindReplacePair.Key, FindReplacePair.Value); } } } } else if(USkeletalMesh* SkeletalMesh = Cast(Asset)) { if(UAnimCurveMetaData* AnimCurveMetaData = SkeletalMesh->GetAssetUserData()) { if(GetFindWholeWord()) { Asset->Modify(); const FName FindCurveName(GetFindString()); const FName ReplaceCurveName(GetReplaceString()); AnimCurveMetaData->RenameCurveMetaData(FindCurveName, ReplaceCurveName); } else { TArray> FindReplacePairs; AnimCurveMetaData->ForEachCurveMetaData([this, &FindReplacePairs](FName InCurveName, const FCurveMetaData& InMetaData) { const FString CurveNameString = InCurveName.ToString(); if(NameMatches(CurveNameString)) { const FName FindCurveName(InCurveName); const FString NewName = CurveNameString.Replace(*GetFindStringRef(), *GetReplaceStringRef(), GetSearchCase()); const FName ReplaceCurveName(*NewName); FindReplacePairs.Emplace(FindCurveName, ReplaceCurveName); } }); if(FindReplacePairs.Num() > 0) { Asset->Modify(); for(const TPair& FindReplacePair : FindReplacePairs) { AnimCurveMetaData->RenameCurveMetaData(FindReplacePair.Key, FindReplacePair.Value); } } } } if(GetSearchMorphTargets()) { if(GetFindWholeWord()) { Asset->Modify(); const FName FindMorphName(GetFindString()); const FName ReplaceMorphName(GetReplaceString()); SkeletalMesh->RenameMorphTarget(FindMorphName, ReplaceMorphName); } else { TArray> FindReplacePairs; for(UMorphTarget* MorphTarget : SkeletalMesh->GetMorphTargets()) { const FString MorphNameString = MorphTarget->GetName(); if(NameMatches(MorphNameString)) { const FName FindMorphName(MorphTarget->GetFName()); const FString NewName = MorphNameString.Replace(*GetFindStringRef(), *GetReplaceStringRef(), GetSearchCase()); const FName ReplaceMorphName(*NewName); FindReplacePairs.Emplace(FindMorphName, ReplaceMorphName); } } if(FindReplacePairs.Num() > 0) { Asset->Modify(); for(const TPair& FindReplacePair : FindReplacePairs) { SkeletalMesh->RenameMorphTarget(FindReplacePair.Key, FindReplacePair.Value); } } } } if(GetSearchMaterials()) { if(GetFindWholeWord()) { Asset->Modify(); const FName FindMorphName(GetFindString()); const FName ReplaceMorphName(GetReplaceString()); UE::AnimAssetFindReplaceCurves::Private::RenameMaterialParameter(SkeletalMesh, FindMorphName, ReplaceMorphName); } else { TArray> FindReplacePairs; for (const FSkeletalMaterial& SkeletalMaterial : SkeletalMesh->GetMaterials()) { UMaterial* Material = (SkeletalMaterial.MaterialInterface != nullptr) ? SkeletalMaterial.MaterialInterface->GetMaterial() : nullptr; if (Material) { TArray OutParameterInfo; TArray OutParameterIds; SkeletalMaterial.MaterialInterface->GetAllScalarParameterInfo(OutParameterInfo, OutParameterIds); for (const FMaterialParameterInfo& MaterialParameterInfo : OutParameterInfo) { const FString ParameterNameString = MaterialParameterInfo.Name.ToString(); if(NameMatches(ParameterNameString)) { const FName FindMorphName(MaterialParameterInfo.Name); const FString NewName = ParameterNameString.Replace(*GetFindStringRef(), *GetReplaceStringRef(), GetSearchCase()); const FName ReplaceMorphName(*NewName); FindReplacePairs.Emplace(FindMorphName, ReplaceMorphName); } } } } if(FindReplacePairs.Num() > 0) { Asset->Modify(); for(const TPair& FindReplacePair : FindReplacePairs) { UE::AnimAssetFindReplaceCurves::Private::RenameMaterialParameter(SkeletalMesh, FindReplacePair.Key, FindReplacePair.Value); } } } } } } } void UAnimAssetFindReplaceCurves::RemoveInAsset(const FAssetData& InAssetData) const { if(UObject* Asset = InAssetData.GetAsset()) { if(UAnimSequenceBase* AnimSequenceBase = Cast(Asset)) { Asset->MarkPackageDirty(); if(GetFindWholeWord()) { const FAnimationCurveIdentifier CurveId(FName(GetFindString()), ERawCurveTrackTypes::RCT_Float); IAnimationDataController::FScopedBracket ScopedBracket(AnimSequenceBase->GetController(), LOCTEXT("RemoveCurves", "Remove Curves")); AnimSequenceBase->GetController().RemoveCurve(CurveId); } else { TSet CurveIdsToRemove; const TArray& Curves = AnimSequenceBase->GetDataModel()->GetFloatCurves(); for(const FFloatCurve& Curve : Curves) { FString CurveName = Curve.GetName().ToString(); if(NameMatches(CurveName)) { const FAnimationCurveIdentifier CurveId(*CurveName, ERawCurveTrackTypes::RCT_Float); CurveIdsToRemove.Add(CurveId); } } if(CurveIdsToRemove.Num() > 0) { IAnimationDataController::FScopedBracket ScopedBracket(AnimSequenceBase->GetController(), LOCTEXT("RemoveCurves", "Remove Curves")); for(const FAnimationCurveIdentifier& CurveIdToRemove : CurveIdsToRemove) { AnimSequenceBase->GetController().RemoveCurve(CurveIdToRemove); } } } } else if(UPoseAsset* PoseAsset = Cast(Asset)) { if(GetFindWholeWord()) { Asset->Modify(); PoseAsset->RemovePoseOrCurveNames({ FName(GetFindString()) }); } else { TArray CurveIdsToRemove; for(const FName& PoseName : PoseAsset->GetPoseFNames()) { if(NameMatches(PoseName.ToString())) { CurveIdsToRemove.AddUnique(PoseName); } } for(const FName& CurveName : PoseAsset->GetCurveFNames()) { if(NameMatches(CurveName.ToString())) { CurveIdsToRemove.AddUnique(CurveName); } } if(CurveIdsToRemove.Num() > 0) { Asset->Modify(); PoseAsset->RemovePoseOrCurveNames(CurveIdsToRemove); } } } else if(USkeleton* Skeleton = Cast(Asset)) { if(GetFindWholeWord()) { Asset->Modify(); Skeleton->RemoveCurveMetaData(FName(GetFindString())); } else { TArray CurvesToRemove; Skeleton->ForEachCurveMetaData([this, &CurvesToRemove](FName InCurveName, const FCurveMetaData& InCurveMetaData) { if(NameMatches(InCurveName.ToString())) { CurvesToRemove.AddUnique(InCurveName); } }); if(CurvesToRemove.Num() > 0) { Asset->Modify(); Skeleton->RemoveCurveMetaData(CurvesToRemove); } } } else if(USkeletalMesh* SkeletalMesh = Cast(Asset)) { if(UAnimCurveMetaData* AnimCurveMetaData = SkeletalMesh->GetAssetUserData()) { if(GetFindWholeWord()) { Asset->Modify(); AnimCurveMetaData->RemoveCurveMetaData(FName(GetFindString())); } else { TArray CurvesToRemove; AnimCurveMetaData->ForEachCurveMetaData([this, &CurvesToRemove](FName InCurveName, const FCurveMetaData& InCurveMetaData) { if(NameMatches(InCurveName.ToString())) { CurvesToRemove.AddUnique(InCurveName); } }); if(CurvesToRemove.Num() > 0) { Asset->Modify(); AnimCurveMetaData->RemoveCurveMetaData(CurvesToRemove); } } } if(GetSearchMorphTargets()) { if(GetFindWholeWord()) { Asset->Modify(); SkeletalMesh->RemoveMorphTargets({FName(GetFindString())}); } else { TArray MorphsToRemove; for(UMorphTarget* MorphTarget : SkeletalMesh->GetMorphTargets()) { if(NameMatches(MorphTarget->GetFName().ToString())) { MorphsToRemove.AddUnique(MorphTarget->GetFName()); } } if(MorphsToRemove.Num() > 0) { Asset->Modify(); SkeletalMesh->RemoveMorphTargets(MorphsToRemove); } } } if(GetSearchMaterials()) { if(GetFindWholeWord()) { Asset->Modify(); UE::AnimAssetFindReplaceCurves::Private::RemoveMaterialParameters(SkeletalMesh, {FName(GetFindString())}); } else { TArray MaterialParametersToRemove; for(UMorphTarget* MorphTarget : SkeletalMesh->GetMorphTargets()) { if(NameMatches(MorphTarget->GetFName().ToString())) { MaterialParametersToRemove.AddUnique(MorphTarget->GetFName()); } } if(MaterialParametersToRemove.Num() > 0) { Asset->Modify(); UE::AnimAssetFindReplaceCurves::Private::RemoveMaterialParameters(SkeletalMesh, MaterialParametersToRemove); } } } } } } void UAnimAssetFindReplaceCurves::ExtendToolbar(FToolMenuSection& InSection) { Super::ExtendToolbar(InSection); InSection.AddDynamicEntry("CurveSearchOptions", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) { FToolUIAction SearchMorphTargetsCheckbox; SearchMorphTargetsCheckbox.ExecuteAction = FToolMenuExecuteAction::CreateLambda([this](const FToolMenuContext& InContext) { SetSearchMorphTargets(!GetSearchMorphTargets()); }); SearchMorphTargetsCheckbox.GetActionCheckState = FToolMenuGetActionCheckState::CreateLambda([this](const FToolMenuContext& InContext) { return GetSearchMorphTargets() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }); InSection.AddEntry( FToolMenuEntry::InitToolBarButton( "SearchMorphTargets", SearchMorphTargetsCheckbox, LOCTEXT("SearchMorphTargetsCheckboxLabel", "Morph Targets"), LOCTEXT("SearchMorphTargetsCheckboxTooltip", "Whether to search morph targets in skeletal meshes while searching for curves."), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Persona.Tabs.MorphTargetPreviewer"), EUserInterfaceActionType::ToggleButton)); FToolUIAction SearchMaterialsCheckbox; SearchMaterialsCheckbox.ExecuteAction = FToolMenuExecuteAction::CreateLambda([this](const FToolMenuContext& InContext) { SetSearchMaterials(!GetSearchMaterials()); }); SearchMaterialsCheckbox.GetActionCheckState = FToolMenuGetActionCheckState::CreateLambda([this](const FToolMenuContext& InContext) { return GetSearchMaterials() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }); InSection.AddEntry( FToolMenuEntry::InitToolBarButton( "SearchMaterials", SearchMaterialsCheckbox, LOCTEXT("SearchMaterialsCheckboxLabel", "Materials"), LOCTEXT("SearchMaterialsCheckboxTooltip", "Whether to search material parameters in skeletal meshes while searching for curves."), FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.Material"), EUserInterfaceActionType::ToggleButton)); })); } void UAnimAssetFindReplaceCurves::GetAutoCompleteNames(TArrayView InAssetDatas, TSet& OutUniqueNames) const { for (const FAssetData& AssetData : InAssetDatas) { auto GetNamesForAsset = [this, &OutUniqueNames](const FAssetData& InAssetData, FName InTag, const FString& InDelimiter) { const FString TagValue = InAssetData.GetTagValueRef(InTag); if (!TagValue.IsEmpty()) { UE::String::ParseTokens(TagValue, *USkeleton::CurveTagDelimiter, [&OutUniqueNames](FStringView InToken) { OutUniqueNames.Add(FString(InToken)); }, UE::String::EParseTokensOptions::SkipEmpty); } }; GetNamesForAsset(AssetData, USkeleton::CurveNameTag, USkeleton::CurveTagDelimiter); UClass* AssetClass = AssetData.GetClass(); if(AssetClass->IsChildOf(USkeletalMesh::StaticClass())) { GetNamesForAsset(AssetData, USkeletalMesh::MorphNamesTag, USkeletalMesh::MorphNamesTagDelimiter); GetNamesForAsset(AssetData, USkeletalMesh::MaterialParamNamesTag, USkeletalMesh::MaterialParamNamesTagDelimiter); } } } void UAnimAssetFindReplaceCurves::SetSearchMorphTargets(bool bInSearchMorphTargets) { bSearchMorphTargets = bInSearchMorphTargets; RequestRefreshSearchResults(); } void UAnimAssetFindReplaceCurves::SetSearchMaterials(bool bInSearchMaterials) { bSearchMaterials = bInSearchMaterials; RequestRefreshSearchResults(); } #undef LOCTEXT_NAMESPACE