// Copyright Epic Games, Inc. All Rights Reserved. #include "PoseSearchCustomization.h" #include "Animation/AnimSequence.h" #include "Animation/BlendSpace.h" #include "AssetRegistry/AssetData.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" #include "EditorClassUtils.h" #include "Engine/EngineBaseTypes.h" #include "Engine/GameViewportClient.h" #include "IDetailChildrenBuilder.h" #include "IDetailsView.h" #include "IPropertyUtilities.h" #include "ObjectEditorUtils.h" #include "PoseSearch/MultiAnimAsset.h" #include "PoseSearch/PoseSearchDatabase.h" #include "PropertyCustomizationHelpers.h" #include "PropertyHandle.h" #include "ScopedTransaction.h" #include "UObject/UnrealType.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SWrapBox.h" #include "Widgets/SBoxPanel.h" #include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "PoseSearchCustomization" void FPoseSearchDatabaseAnimationAssetCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { FText SequenceNameText; TArray Objects; InStructPropertyHandle->GetOuterObjects(Objects); if (Objects.Num() == 1) { FPoseSearchDatabaseAnimationAsset* DatabaseAnimationAsset = (FPoseSearchDatabaseAnimationAsset*)InStructPropertyHandle->GetValueBaseAddress((uint8*)Objects[0]); check(DatabaseAnimationAsset); if (const UObject* AnimationAsset = DatabaseAnimationAsset->GetAnimationAsset()) { SequenceNameText = FText::FromName(AnimationAsset->GetFName()); } else { SequenceNameText = LOCTEXT("NewDatabaseAnimationAssetLabel", "New Animation Asset"); } } HeaderRow .NameContent() [ SNew(STextBlock) .Text(SequenceNameText) ]; const FSimpleDelegate OnValueChanged = FSimpleDelegate::CreateLambda([&StructCustomizationUtils]() { StructCustomizationUtils.GetPropertyUtilities()->ForceRefresh(); }); InStructPropertyHandle->SetOnChildPropertyValueChanged(OnValueChanged); } void FPoseSearchDatabaseAnimationAssetCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { uint32 NumChildren; InStructPropertyHandle->GetNumChildren(NumChildren); TSharedPtr bEnabledHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, bEnabled)); TSharedPtr bDisableReselectionHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, bDisableReselection)); TSharedPtr MirrorOptionHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, MirrorOption)); TSharedPtr BranchInIdHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, BranchInId)); TSharedPtr AnimAssetHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, AnimAsset)); TSharedPtr bUseSingleSampleHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, bUseSingleSample)); TSharedPtr bUseGridForSamplingHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, bUseGridForSampling)); TSharedPtr NumberOfHorizontalSamplesHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, NumberOfHorizontalSamples)); TSharedPtr NumberOfVerticalSamplesHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, NumberOfVerticalSamples)); TSharedPtr BlendParamXHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, BlendParamX)); TSharedPtr BlendParamYHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, BlendParamY)); TSharedPtr SamplingRangeHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPoseSearchDatabaseAnimationAsset, SamplingRange)); const bool bIsPoseSearchDatabaseAnimationAssetLayoutValid = NumChildren == 12 && bEnabledHandle.IsValid() && bDisableReselectionHandle.IsValid() && MirrorOptionHandle.IsValid() && BranchInIdHandle.IsValid() && AnimAssetHandle.IsValid() && bUseSingleSampleHandle.IsValid() && bUseGridForSamplingHandle.IsValid() && NumberOfHorizontalSamplesHandle.IsValid() && NumberOfVerticalSamplesHandle.IsValid() && BlendParamXHandle.IsValid() && BlendParamYHandle.IsValid() && SamplingRangeHandle.IsValid(); // update FPoseSearchDatabaseAnimationAssetCustomization if FPoseSearchDatabaseAnimationAsset or FPoseSearchDatabaseAnimationAssetBase changed! if (!ensure(bIsPoseSearchDatabaseAnimationAssetLayoutValid)) { for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { StructBuilder.AddProperty(InStructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef()); } return; } StructBuilder.AddProperty(AnimAssetHandle.ToSharedRef()); StructBuilder.AddProperty(bEnabledHandle.ToSharedRef()); StructBuilder.AddProperty(SamplingRangeHandle.ToSharedRef()); StructBuilder.AddProperty(bDisableReselectionHandle.ToSharedRef()); StructBuilder.AddProperty(MirrorOptionHandle.ToSharedRef()); StructBuilder.AddProperty(BranchInIdHandle.ToSharedRef()); UObject* AnimAsset = nullptr; const FPropertyAccess::Result AnimAssetGetValueResult = AnimAssetHandle->GetValue(AnimAsset); if (AnimAssetGetValueResult == FPropertyAccess::Success && AnimAsset) { if (AnimAsset->IsA()) { StructBuilder.AddProperty(bUseSingleSampleHandle.ToSharedRef()); StructBuilder.AddProperty(bUseGridForSamplingHandle.ToSharedRef()); StructBuilder.AddProperty(NumberOfHorizontalSamplesHandle.ToSharedRef()); StructBuilder.AddProperty(NumberOfVerticalSamplesHandle.ToSharedRef()); StructBuilder.AddProperty(BlendParamXHandle.ToSharedRef()); StructBuilder.AddProperty(BlendParamYHandle.ToSharedRef()); } else if (AnimAsset->IsA()) { StructBuilder.AddProperty(NumberOfHorizontalSamplesHandle.ToSharedRef()); StructBuilder.AddProperty(NumberOfVerticalSamplesHandle.ToSharedRef()); } } else if (FPropertyAccess::MultipleValues == AnimAssetGetValueResult) { StructBuilder.AddProperty(bUseSingleSampleHandle.ToSharedRef()); StructBuilder.AddProperty(bUseGridForSamplingHandle.ToSharedRef()); StructBuilder.AddProperty(NumberOfHorizontalSamplesHandle.ToSharedRef()); StructBuilder.AddProperty(NumberOfVerticalSamplesHandle.ToSharedRef()); StructBuilder.AddProperty(BlendParamXHandle.ToSharedRef()); StructBuilder.AddProperty(BlendParamYHandle.ToSharedRef()); } FFloatInterval SamplingRange = FFloatInterval(0.f, 0.f); void* StructData = nullptr; const FPropertyAccess::Result SamplingRangeGetValueResult = SamplingRangeHandle->GetValueData(StructData); if (SamplingRangeGetValueResult == FPropertyAccess::Success && StructData) { SamplingRange = *static_cast(StructData); } // Create and inject a check box to expose the animation looping state const bool bLooping = FPoseSearchDatabaseAnimationAsset::IsLooping(AnimAsset, SamplingRange); StructBuilder.AddProperty(InStructPropertyHandle).CustomWidget() .NameContent() [ InStructPropertyHandle->CreatePropertyNameWidget( LOCTEXT("FPoseSearchDatabaseAnimationAssetCustomizationLoopingLabel", "Looping"), LOCTEXT("FPoseSearchDatabaseAnimationAssetCustomizationLoopingTooltip", "Is this animation set as looping? If you want to change this you need to change it in the animation asset. Changing the sampling range will disable looping") ) ] .ValueContent() [ SNew(SCheckBox) .IsChecked_Lambda([bLooping]() { return bLooping ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .IsEnabled(false) ]; // Create and inject a check box to expose the animation root motion state const bool bHasRootMotion = FPoseSearchDatabaseAnimationAsset::IsRootMotionEnabled(AnimAsset); StructBuilder.AddProperty(InStructPropertyHandle).CustomWidget() .NameContent() [ InStructPropertyHandle->CreatePropertyNameWidget( LOCTEXT("FPoseSearchDatabaseAnimationAssetCustomizationHasRootMotionLabel", "HasRootMotion"), LOCTEXT("FPoseSearchDatabaseAnimationAssetCustomizationHasRootMotionTooltip", "Does this animation have root motion enabled ? If you want to change this you need to change it in the animation asset.") ) ] .ValueContent() [ SNew(SCheckBox) .IsChecked_Lambda([bHasRootMotion]() { return bHasRootMotion ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .IsEnabled(false) ]; } #undef LOCTEXT_NAMESPACE