Files
UnrealEngineUWP/Engine/Source/Editor/DetailCustomizations/Private/MeshDeformerCustomizations.cpp
Jeremy Moore b1fb2db1a0 Enable filtering of mesh deformers to match primary component binding.
Was disabled because it can trigger mesh deformer asset sync load, which would trigger a slow graph compile.
The graph compilation is no longer done on PostLoad() so performance is now good.
Added specific filter metadata to the property in USkeletalMesh. This is needed for filtering with UObjects that aren't themselves primary binding types.
#preflight 63c0410702024f93d89c727b

[CL 23665339 by Jeremy Moore in ue5-main branch]
2023-01-12 12:33:27 -05: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;
})
];
}