Files
UnrealEngine/Engine/Source/Editor/DetailCustomizations/Private/MeshDeformerCustomizations.cpp
2025-05-18 13:04:45 +08:00

104 lines
3.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MeshDeformerCustomizations.h"
#include "Animation/MeshDeformer.h"
#include "Components/SkinnedMeshComponent.h"
#include "Engine/SkeletalMesh.h"
#include "DetailWidgetRow.h"
#include "PropertyCustomizationHelpers.h"
#include "PropertyHandle.h"
namespace
{
/** Pull the class type for filtering from the property. */
UClass* GetFilterClassFromProperty(TSharedRef<IPropertyHandle> InPropertyHandle)
{
TArray<UObject*> OwningObjects;
InPropertyHandle->GetOuterObjects(OwningObjects);
if (OwningObjects.Num() == 0)
{
return nullptr;
}
// Use the owner class by default.
UClass* FilterClass = OwningObjects[0]->GetClass();
// Override if we have metadata.
FProperty* DeformerProperty = InPropertyHandle->GetProperty();
static const FName FilterName(TEXT("Filter"));
const FString& FilterClassPath = DeformerProperty->GetMetaData(FilterName);
if (!FilterClassPath.IsEmpty())
{
FSoftClassPath FilterSoftClassPath(FilterClassPath);
FilterClass = FilterSoftClassPath.ResolveClass();
}
// Only UActorComponent classes are valid filters.
if (FilterClass != nullptr && !FilterClass->IsChildOf(UActorComponent::StaticClass()))
{
FilterClass = nullptr;
}
return FilterClass;
}
/** Pull the class type from the PrimaryBindingClass tag in some AssetData. */
UClass* GetPrimaryBindingClassFromAssetData(FAssetData const& AssetData)
{
UClass* BindingClass = nullptr;
FString BindingClassPath;
if (AssetData.GetTagValue<FString>("PrimaryBindingClass", BindingClassPath))
{
FSoftClassPath BindingSoftClassPath(BindingClassPath);
BindingClass = BindingSoftClassPath.ResolveClass();
}
return BindingClass;
}
}
TSharedRef<IPropertyTypeCustomization> FMeshDeformerCustomization::MakeInstance()
{
return MakeShareable(new FMeshDeformerCustomization);
}
void FMeshDeformerCustomization::CustomizeHeader(
TSharedRef<IPropertyHandle> InPropertyHandle,
FDetailWidgetRow& InHeaderRow,
IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
UClass* FilterClass = GetFilterClassFromProperty(InPropertyHandle);
InHeaderRow.NameContent()
[
InPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SObjectPropertyEntryBox)
.PropertyHandle(InPropertyHandle)
.AllowedClass(UMeshDeformer::StaticClass())
.OnShouldFilterAsset_Lambda([FilterClass](FAssetData const& AssetData)
{
// Filter depending on whether the PrimaryBindingClass matches our owning object.
// First try to load the class type from the asset registry.
UClass* BindingClass = GetPrimaryBindingClassFromAssetData(AssetData);
// If we can't find the tag in the registry then load the full object to get the class type (slow).
if (BindingClass == nullptr)
{
if (UObject* Object = AssetData.GetAsset())
{
FAssetData AssetDataWithObjectRegistryTags;
Object->GetAssetRegistryTags(AssetDataWithObjectRegistryTags);
BindingClass = GetPrimaryBindingClassFromAssetData(AssetDataWithObjectRegistryTags);
}
}
const bool bHideEntry = FilterClass != nullptr && BindingClass != nullptr && !FilterClass->IsChildOf(BindingClass);
return bHideEntry;
})
];
}