Files
UnrealEngine/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/NaniteDisplacedMeshCustomization.cpp
2025-05-18 13:04:45 +08:00

168 lines
6.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NaniteDisplacedMeshCustomization.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "NaniteDisplacedMesh.h"
#include "ScopedTransaction.h"
#include "UObject/StructOnScope.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Text/STextBlock.h"
#define LOCTEXT_NAMESPACE "FNaniteDisplacedMeshDetails"
TSharedRef<IDetailCustomization> FNaniteDisplacedMeshDetails::MakeInstance()
{
return MakeShared<FNaniteDisplacedMeshDetails>();
}
void FNaniteDisplacedMeshDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
TArray<TWeakObjectPtr<UObject>> ObjectsCustomized;
DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized);
CustomizedPairs.Reserve(ObjectsCustomized.Num());
for (const TWeakObjectPtr<UObject>& ObjectCustomized : ObjectsCustomized)
{
if (UNaniteDisplacedMesh* DisplacedMesh = Cast<UNaniteDisplacedMesh>(ObjectCustomized.Get()))
{
CustomizedPairs.Emplace(TWeakObjectPtr<UNaniteDisplacedMesh>(DisplacedMesh), DisplacedMesh->Parameters);
}
}
{
IDetailCategoryBuilder& ParametersCategory = DetailBuilder.EditCategory(TEXT("Parameters"));
UStruct* NaniteDisplacedMeshStaticStruct = FNaniteDisplacedMeshParams::StaticStruct();
for (int32 Index = 0; Index < CustomizedPairs.Num(); ++Index)
{
FNaniteDisplacedMeshParams& DisplacedMeshParams = CustomizedPairs[Index].Value;
// The custom struct work well with minimum code added, but it doesn't support transaction like the existing stuff for the static meshes
TArray<IDetailPropertyRow*> PropertyRows;
ParametersCategory.AddAllExternalStructureProperties(MakeShared<FStructOnScope>(NaniteDisplacedMeshStaticStruct, static_cast<uint8*>(static_cast<void*>(&DisplacedMeshParams))), EPropertyLocation::Default, &PropertyRows);
for (IDetailPropertyRow* PropertyRow : PropertyRows)
{
// Turned off the custom reset to default for now so that we can submit the rest until this is in a working state.
/*
const bool bPropagateToChildren = true;
PropertyRow->OverrideResetToDefault(FResetToDefaultOverride::Create(
FIsResetToDefaultVisible::CreateSP(this, &FNaniteDisplacedMeshDetails::DoesParamDifferFromOriginalValue, Index),
FResetToDefaultHandler::CreateSP(this, &FNaniteDisplacedMeshDetails::ResetParamToOriginalValue, Index),
bPropagateToChildren
));
*/
PropertyRow->EditCondition(GetCanEditAttribute(Index), FOnBooleanValueChanged());
}
ParametersCategory.AddCustomRow(LOCTEXT("ApplyChanges", "Apply Changes"))
.ValueContent()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SButton)
.OnClicked(this, &FNaniteDisplacedMeshDetails::ApplyNaniteDisplacedMeshParams)
.IsEnabled(this, &FNaniteDisplacedMeshDetails::IsApplyNaniteDisplacedMeshParamsNeeded)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("ApplyChanges", "Apply Changes"))
.Font(DetailBuilder.GetDetailFont())
]
];
}
}
TSharedRef<IPropertyHandle> ParametersPropertyHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UNaniteDisplacedMesh, Parameters));
ParametersPropertyHandle->MarkHiddenByCustomization();
}
FReply FNaniteDisplacedMeshDetails::ApplyNaniteDisplacedMeshParams()
{
FScopedTransaction Transaction(LOCTEXT("ApplyDisplacementParamaterToMesh", "Apply Parameters to Nanite Displaced Mesh"));
FProperty* ParametersProperty = UNaniteDisplacedMesh::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UNaniteDisplacedMesh, Parameters));
for (TPair<TWeakObjectPtr<UNaniteDisplacedMesh>, FNaniteDisplacedMeshParams>& Pair : CustomizedPairs)
{
if (UNaniteDisplacedMesh* DisplacedMesh = Pair.Key.Get())
{
DisplacedMesh->PreEditChange(ParametersProperty);
DisplacedMesh->Parameters = Pair.Value;
FPropertyChangedEvent ChangedEvent(ParametersProperty, EPropertyChangeType::ValueSet);
DisplacedMesh->PostEditChangeProperty(ChangedEvent);
}
}
return FReply::Handled();
}
bool FNaniteDisplacedMeshDetails::IsApplyNaniteDisplacedMeshParamsNeeded() const
{
for (const TPair<TWeakObjectPtr<UNaniteDisplacedMesh>, FNaniteDisplacedMeshParams>& Pair : CustomizedPairs)
{
if (UNaniteDisplacedMesh* DisplacedMesh = Pair.Key.Get())
{
if (DisplacedMesh->Parameters != Pair.Value)
{
return true;
}
}
}
return false;
}
bool FNaniteDisplacedMeshDetails::DoesParamDifferFromOriginalValue(TSharedPtr<IPropertyHandle> Handle, int32 ObjectIndex)
{
/*
TPair<TWeakObjectPtr<UNaniteDisplacedMesh>, FNaniteDisplacedMeshParams>& Pair = CustomizedPairs[ObjectIndex];
if (UNaniteDisplacedMesh* DisplacedMesh = Pair.Key.Get())
{
// Check if the property value exist in the NaniteDisplacedMesh
if (uint8* DestAddress = Handle->GetValueBaseAddress(static_cast<uint8*>(static_cast<void*>(&(DisplacedMesh->Parameters)))))
{
uint8* SourceAddress = Handle->GetValueBaseAddress(static_cast<uint8*>(static_cast<void*>(&(Pair.Value))));
return !Handle->GetProperty()->Identical(DestAddress, SourceAddress);
}
}
*/
return false;
}
void FNaniteDisplacedMeshDetails::ResetParamToOriginalValue(TSharedPtr<IPropertyHandle> Handle, int32 ObjectIndex)
{
/*
TPair<TWeakObjectPtr<UNaniteDisplacedMesh>, FNaniteDisplacedMeshParams>& Pair = CustomizedPairs[ObjectIndex];
if (UNaniteDisplacedMesh* DisplacedMesh = Pair.Key.Get())
{
// Check if the property value exist in the NaniteDisplacedMesh
if (uint8* SourceAddress = Handle->GetValueBaseAddress(static_cast<uint8*>(static_cast<void*>(&(DisplacedMesh->Parameters)))))
{
uint8* DestAddress = Handle->GetValueBaseAddress(static_cast<uint8*>(static_cast<void*>(&(Pair.Value))));
Handle->GetProperty()->CopyCompleteValue(DestAddress, SourceAddress);
}
}
*/
}
TAttribute<bool> FNaniteDisplacedMeshDetails::GetCanEditAttribute(int32 ObjectIndex)
{
TPair<TWeakObjectPtr<UNaniteDisplacedMesh>, FNaniteDisplacedMeshParams>& Pair = CustomizedPairs[ObjectIndex];
return TAttribute<bool>::Create([WeakMesh = Pair.Key]
{
if (UNaniteDisplacedMesh* DisplacedMesh = WeakMesh.Get())
{
return DisplacedMesh->bIsEditable;
}
return false;
});
}
#undef LOCTEXT_NAMESPACE