383 lines
12 KiB
C++
383 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SCreateClothingSettingsPanel.h"
|
|
|
|
#include "PropertyEditorModule.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "IStructureDetailsView.h"
|
|
#include "IDetailsView.h"
|
|
#include "Widgets/Layout/SUniformGridPanel.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "DetailCategoryBuilder.h"
|
|
#include "DetailWidgetRow.h"
|
|
#include "PropertyCustomizationHelpers.h"
|
|
#include "ClothingAssetBase.h"
|
|
#include "Widgets/Input/SComboButton.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "PhysicsEngine/PhysicsAsset.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "CreateClothSettings"
|
|
|
|
void SCreateClothingSettingsPanel::Construct(const FArguments& InArgs)
|
|
{
|
|
check(InArgs._LodIndex != INDEX_NONE && InArgs._SectionIndex != INDEX_NONE);
|
|
|
|
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
|
|
TSharedPtr<IStructureDetailsView> StructureDetailsView;
|
|
|
|
OnCreateDelegate = InArgs._OnCreateRequested;
|
|
|
|
bIsSubImport = InArgs._bIsSubImport;
|
|
|
|
BuildParams.LodIndex = InArgs._LodIndex;
|
|
BuildParams.SourceSection = InArgs._SectionIndex;
|
|
|
|
FDetailsViewArgs DetailsViewArgs;
|
|
{
|
|
DetailsViewArgs.bAllowSearch = false;
|
|
DetailsViewArgs.bHideSelectionTip = true;
|
|
DetailsViewArgs.bLockable = false;
|
|
DetailsViewArgs.bSearchInitialKeyFocus = true;
|
|
DetailsViewArgs.bUpdatesFromSelection = false;
|
|
DetailsViewArgs.NotifyHook = nullptr;
|
|
DetailsViewArgs.bShowOptions = true;
|
|
DetailsViewArgs.bShowModifiedPropertiesOption = false;
|
|
DetailsViewArgs.bShowScrollBar = false;
|
|
}
|
|
|
|
FStructureDetailsViewArgs StructureViewArgs;
|
|
{
|
|
StructureViewArgs.bShowObjects = true;
|
|
StructureViewArgs.bShowAssets = true;
|
|
StructureViewArgs.bShowClasses = true;
|
|
StructureViewArgs.bShowInterfaces = true;
|
|
}
|
|
|
|
StructureDetailsView = PropertyEditorModule.CreateStructureDetailView(DetailsViewArgs, StructureViewArgs, nullptr);
|
|
|
|
StructureDetailsView->GetDetailsView()->SetGenericLayoutDetailsDelegate(FOnGetDetailCustomizationInstance::CreateStatic(&FClothCreateSettingsCustomization::MakeInstance, InArgs._Mesh, InArgs._bIsSubImport));
|
|
|
|
BuildParams.AssetName = InArgs._MeshName + TEXT("_Clothing");
|
|
BuildParams.PhysicsAsset = InArgs._Mesh->GetPhysicsAsset();
|
|
|
|
FStructOnScope* Struct = new FStructOnScope(FSkeletalMeshClothBuildParams::StaticStruct(), (uint8*)&BuildParams);
|
|
StructureDetailsView->SetStructureData(MakeShareable(Struct));
|
|
|
|
this->ChildSlot
|
|
[
|
|
SNew(SBox)
|
|
.MinDesiredWidth(300.0f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+SVerticalBox::Slot()
|
|
.MaxHeight(500.0f)
|
|
.Padding(2.f)
|
|
[
|
|
StructureDetailsView->GetWidget()->AsShared()
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(2.f)
|
|
.HAlign(HAlign_Right)
|
|
[
|
|
SNew(SUniformGridPanel)
|
|
.SlotPadding(2.f)
|
|
+SUniformGridPanel::Slot(0, 0)
|
|
[
|
|
SNew(SButton)
|
|
.Text(LOCTEXT("Label_Create", "Create"))
|
|
.OnClicked(this, &SCreateClothingSettingsPanel::OnCreateClicked)
|
|
.ToolTipText(this, &SCreateClothingSettingsPanel::GetCreateButtonTooltip)
|
|
.IsEnabled(this, &SCreateClothingSettingsPanel::CanCreateClothing)
|
|
]
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
FText SCreateClothingSettingsPanel::GetCreateButtonTooltip() const
|
|
{
|
|
if(CanCreateClothing())
|
|
{
|
|
if(bIsSubImport)
|
|
{
|
|
return LOCTEXT("CreateTooltip_NewLod", "Create new simulation mesh for the specified asset and LOD.");
|
|
}
|
|
else
|
|
{
|
|
return LOCTEXT("CreateTooltip_NewAsset", "Create new clothing asset from selected section.");
|
|
}
|
|
}
|
|
else if(bIsSubImport)
|
|
{
|
|
return LOCTEXT("CreateTooltip_NewLodInvalid", "Select an asset and LOD level to create a simulation mesh.");
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
bool SCreateClothingSettingsPanel::CanCreateClothing() const
|
|
{
|
|
// If we're importing a LOD, make sure a target has been selected
|
|
if(bIsSubImport)
|
|
{
|
|
return BuildParams.TargetAsset.IsValid() && BuildParams.TargetLod != INDEX_NONE;
|
|
}
|
|
|
|
// No restriction for new assets
|
|
return true;
|
|
}
|
|
|
|
FReply SCreateClothingSettingsPanel::OnCreateClicked()
|
|
{
|
|
OnCreateDelegate.ExecuteIfBound(BuildParams);
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
TSharedRef<SWidget> FClothCreateSettingsCustomization::OnGetTargetAssetMenu()
|
|
{
|
|
FMenuBuilder Builder(true, nullptr, TSharedPtr<FExtender>(), true);
|
|
|
|
Builder.BeginSection(TEXT("TargetAssetDropdown"), LOCTEXT("TargetAssetMenuHeader", "Available Assets"));
|
|
{
|
|
if(USkeletalMesh* Mesh = MeshPtr.Get())
|
|
{
|
|
const int32 NumCloths = Mesh->GetMeshClothingAssets().Num();
|
|
for(int32 ClothIndex = 0 ; ClothIndex < NumCloths ; ++ClothIndex)
|
|
{
|
|
if (UClothingAssetBase* ClothingAsset = Mesh->GetMeshClothingAssets()[ClothIndex])
|
|
{
|
|
FUIAction Action;
|
|
Action.ExecuteAction = FExecuteAction::CreateSP(this, &FClothCreateSettingsCustomization::OnAssetSelected, ClothIndex);
|
|
Builder.AddMenuEntry(FText::FromString(ClothingAsset->GetName()), LOCTEXT("TargetAsset_Tooltip", "Select this clothing as the target to import to."), FSlateIcon(), Action);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Builder.EndSection();
|
|
|
|
return Builder.MakeWidget();
|
|
}
|
|
|
|
FText FClothCreateSettingsCustomization::GetTargetAssetText() const
|
|
{
|
|
check(ParamsStruct);
|
|
|
|
if(UClothingAssetBase* Target = ParamsStruct->TargetAsset.Get())
|
|
{
|
|
return FText::FromString(Target->GetName());
|
|
}
|
|
else
|
|
{
|
|
return LOCTEXT("SelectAssetPrompt", "Select target clothing...");
|
|
}
|
|
}
|
|
|
|
void FClothCreateSettingsCustomization::OnAssetSelected(int32 InMeshClothingIndex)
|
|
{
|
|
check(ParamsStruct);
|
|
|
|
if(USkeletalMesh* Mesh = MeshPtr.Get())
|
|
{
|
|
if(Mesh->GetMeshClothingAssets().IsValidIndex(InMeshClothingIndex) &&
|
|
Mesh->GetMeshClothingAssets()[InMeshClothingIndex])
|
|
{
|
|
if(ParamsStruct->TargetAsset != Mesh->GetMeshClothingAssets()[InMeshClothingIndex])
|
|
{
|
|
ParamsStruct->TargetAsset = Mesh->GetMeshClothingAssets()[InMeshClothingIndex];
|
|
ParamsStruct->TargetLod = INDEX_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> FClothCreateSettingsCustomization::OnGetTargetLodMenu()
|
|
{
|
|
check(ParamsStruct);
|
|
|
|
USkeletalMesh* Mesh = MeshPtr.Get();
|
|
|
|
FMenuBuilder Builder(true, nullptr, TSharedPtr<FExtender>(), true);
|
|
|
|
Builder.BeginSection(TEXT("TargetLodDropdown"), LOCTEXT("TargetLodMenuHeader", "Available LODs"));
|
|
{
|
|
if(UClothingAssetBase* ClothingAsset = ParamsStruct->TargetAsset.Get())
|
|
{
|
|
const int32 NumLodEntries = ClothingAsset->GetNumLods();
|
|
for(int32 LodEntryIndex = 0; LodEntryIndex < NumLodEntries + 1; ++LodEntryIndex)
|
|
{
|
|
FText EntryText;
|
|
FText EntryTooltip;
|
|
|
|
if(LodEntryIndex < NumLodEntries)
|
|
{
|
|
EntryText = FText::Format(LOCTEXT("LodEntryTextReplace", "Replace LOD {0}"), FText::AsNumber(LodEntryIndex));
|
|
EntryTooltip = FText::Format(LOCTEXT("ImportLodReplaceTooltip", "Replace the simulation mesh in LOD {0} of {1} with this section."), FText::AsNumber(LodEntryIndex), FText::FromString(ClothingAsset->GetName()));
|
|
}
|
|
else
|
|
{
|
|
EntryText = FText::Format(LOCTEXT("LodEntryTextAdd", "Add LOD {0}"), FText::AsNumber(LodEntryIndex));
|
|
EntryTooltip = LOCTEXT("ImportLodNewTooltip", "Use the selected section to add a as a new LOD");
|
|
}
|
|
|
|
FUIAction Action;
|
|
Action.ExecuteAction = FExecuteAction::CreateSP(this, &FClothCreateSettingsCustomization::OnLodSelected, LodEntryIndex);
|
|
Builder.AddMenuEntry(EntryText, EntryTooltip, FSlateIcon(), Action);
|
|
}
|
|
}
|
|
}
|
|
Builder.EndSection();
|
|
|
|
return Builder.MakeWidget();
|
|
}
|
|
|
|
FText FClothCreateSettingsCustomization::GetTargetLodText() const
|
|
{
|
|
check(ParamsStruct);
|
|
|
|
if(!CanSelectLod())
|
|
{
|
|
return LOCTEXT("LodMenuSelectAsset", "Select an Asset");
|
|
}
|
|
|
|
if(ParamsStruct->TargetLod == INDEX_NONE)
|
|
{
|
|
return LOCTEXT("SelectALod", "Select a LOD");
|
|
}
|
|
|
|
return FText::Format(LOCTEXT("LodDropdownEntry", "LOD {0}"), FText::AsNumber(ParamsStruct->TargetLod));
|
|
}
|
|
|
|
void FClothCreateSettingsCustomization::OnLodSelected(int32 InLodIndex)
|
|
{
|
|
check(ParamsStruct);
|
|
|
|
ParamsStruct->TargetLod = InLodIndex;
|
|
}
|
|
|
|
bool FClothCreateSettingsCustomization::CanSelectLod() const
|
|
{
|
|
check(ParamsStruct);
|
|
|
|
return ParamsStruct->TargetAsset.IsValid();
|
|
}
|
|
|
|
void FClothCreateSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
// Make sure we actually get a valid struct before continuing
|
|
TArray<TSharedPtr<FStructOnScope>> Structs;
|
|
DetailBuilder.GetStructsBeingCustomized(Structs);
|
|
|
|
if(Structs.Num() == 0)
|
|
{
|
|
// Nothing being customized
|
|
return;
|
|
}
|
|
|
|
const UStruct* Struct = Structs[0]->GetStruct();
|
|
if(!Struct || Struct != FSkeletalMeshClothBuildParams::StaticStruct())
|
|
{
|
|
// Invalid struct
|
|
return;
|
|
}
|
|
|
|
// Get ptr to our actual type
|
|
ParamsStruct = (FSkeletalMeshClothBuildParams*)Structs[0]->GetStructMemory();
|
|
|
|
TSharedPtr<IPropertyHandle> LodIndexProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, LodIndex), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
TSharedPtr<IPropertyHandle> SectionIndexProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, SourceSection), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
|
|
check(LodIndexProperty.IsValid() && SectionIndexProperty.IsValid());
|
|
|
|
LodIndexProperty->MarkHiddenByCustomization();
|
|
SectionIndexProperty->MarkHiddenByCustomization();
|
|
|
|
TSharedPtr<IPropertyHandle> TargetAssetProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, TargetAsset), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
TSharedPtr<IPropertyHandle> TargetLodProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, TargetLod), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
|
|
check(TargetAssetProperty.IsValid() && TargetLodProperty.IsValid());
|
|
TargetAssetProperty->MarkHiddenByCustomization();
|
|
TargetLodProperty->MarkHiddenByCustomization();
|
|
|
|
if(bIsSubImport)
|
|
{
|
|
// Asset name makes no sense for LODs, so hide it
|
|
TSharedPtr<IPropertyHandle> AssetNameProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, AssetName), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
if(AssetNameProperty.IsValid())
|
|
{
|
|
AssetNameProperty->MarkHiddenByCustomization();
|
|
}
|
|
|
|
// Physics mesh doesn't make sense for LODs
|
|
TSharedPtr<IPropertyHandle> PhysicsAssetProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, PhysicsAsset), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
if(AssetNameProperty.IsValid())
|
|
{
|
|
PhysicsAssetProperty->MarkHiddenByCustomization();
|
|
}
|
|
|
|
IDetailCategoryBuilder& TargetCategory = DetailBuilder.EditCategory(TEXT("Target"));
|
|
|
|
FDetailWidgetRow& AssetRow = TargetCategory.AddCustomRow(LOCTEXT("Asset_FilterString", "Target Asset"));
|
|
|
|
AssetRow.NameContent()
|
|
[
|
|
TargetAssetProperty->CreatePropertyNameWidget()
|
|
];
|
|
AssetRow.ValueContent()
|
|
[
|
|
SNew(SComboButton)
|
|
.OnGetMenuContent(this, &FClothCreateSettingsCustomization::OnGetTargetAssetMenu)
|
|
.ContentPadding(2.f)
|
|
.ButtonStyle(FAppStyle::Get(), "PropertyEditor.AssetComboStyle")
|
|
.ForegroundColor(FAppStyle::GetColor("PropertyEditor.AssetName.ColorAndOpacity"))
|
|
.CollapseMenuOnParentFocus(true)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FClothCreateSettingsCustomization::GetTargetAssetText)
|
|
.TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass")
|
|
.Font(DetailBuilder.GetDetailFont())
|
|
]
|
|
];
|
|
|
|
FDetailWidgetRow& LodRow = TargetCategory.AddCustomRow(LOCTEXT("Lod_FilterString", "Target Lod"));
|
|
|
|
LodRow.NameContent()
|
|
[
|
|
LodIndexProperty->CreatePropertyNameWidget()
|
|
];
|
|
LodRow.ValueContent()
|
|
[
|
|
SNew(SComboButton)
|
|
.IsEnabled(this, &FClothCreateSettingsCustomization::CanSelectLod)
|
|
.OnGetMenuContent(this, &FClothCreateSettingsCustomization::OnGetTargetLodMenu)
|
|
.ContentPadding(2.f)
|
|
.CollapseMenuOnParentFocus(true)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FClothCreateSettingsCustomization::GetTargetLodText)
|
|
.Font(DetailBuilder.GetDetailFont())
|
|
]
|
|
];
|
|
}
|
|
else
|
|
{
|
|
// Specifics for non sub-import
|
|
TSharedPtr<IPropertyHandle> RemapProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(FSkeletalMeshClothBuildParams, bRemapParameters), (UClass*)FSkeletalMeshClothBuildParams::StaticStruct());
|
|
if(RemapProperty.IsValid())
|
|
{
|
|
RemapProperty->MarkHiddenByCustomization();
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|