Add support for QualityLevelMinLod in SkeletalMesh.

-add clear minlod conversion data
 -add QualityLevelMinLOD tags for static meshes
 -fix qualityLevel property customization OnAddEntry and OnRemoveEntry slate event

#rb [at]jack.porter,[at]jian.ru
#test consoles, pc, mobile (results FNTEST-66031)

#ROBOMERGE-OWNER: serge.bernier
#ROBOMERGE-AUTHOR: serge.bernier
#ROBOMERGE-SOURCE: CL 19129732 via CL 19131639 via CL 19133087 via CL 19134011 via CL 19137049
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v921-19075845)

[CL 19155608 by serge bernier in ue5-main branch]
This commit is contained in:
serge bernier
2022-02-25 15:46:30 -05:00
parent ad549e7af5
commit a558eda684
23 changed files with 585 additions and 81 deletions
@@ -3234,11 +3234,66 @@ void FPersonaMeshDetails::CustomizeLODSettingsCategories(IDetailLayoutBuilder& D
.EntryNames(this, &FPersonaMeshDetails::GetNoRefStreamingLODBiasOverrideNames)
];
TAttribute<bool> IsQualityLevelLodEnabled = TAttribute<bool>::CreateLambda([this]() { return FPersonaMeshDetails::IsQualityLevelMinLodEnable(); });
TAttribute<bool> IsPerPlatformMinLodEnabled = TAttribute<bool>::CreateLambda([this]() { return FPersonaMeshDetails::IsMinLodEnable(); });
TSharedPtr<IPropertyHandle> MinLODPropertyHandle = DetailLayout.GetProperty(USkeletalMesh::GetMinLodMemberName(), USkeletalMesh::StaticClass());
IDetailPropertyRow& MinLODRow = LODSettingsCategory.AddProperty(MinLODPropertyHandle);
MinLODRow.IsEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &FPersonaMeshDetails::IsLODInfoEditingEnabled, -1)));
MinLODRow.EditCondition(IsPerPlatformMinLodEnabled, NULL);
DetailLayout.HideProperty(MinLODPropertyHandle);
TSharedRef<IPropertyHandle> LODInfoProperty = DetailLayout.GetProperty(FName("LODInfo"), USkeletalMesh::StaticClass());
DetailLayout.HideProperty(LODInfoProperty);
TSharedPtr<IPropertyHandle> QualityLevelMinLODPropertyHandle = DetailLayout.GetProperty(USkeletalMesh::GetQualityLevelMinLodMemberName(), USkeletalMesh::StaticClass());
DetailLayout.HideProperty(QualityLevelMinLODPropertyHandle);
LODSettingsCategory.AddCustomRow(LOCTEXT("QualityLevelMinLOD", "Quality Level Min LOD"))
.RowTag("QualityLevelMinLOD")
.IsEnabled(IsQualityLevelLodEnabled)
.EditCondition(IsQualityLevelLodEnabled, NULL)
.NameContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(0.0f, 4.0f)
.HAlign(HAlign_Left)
.AutoWidth()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("QualityLevelMinLOD", "Quality Level Min LOD"))
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.Padding(50.0f, 0.0f)
.AutoWidth()
[
SNew(SButton)
.OnClicked(this, &FPersonaMeshDetails::ResetToDefault)
.ButtonStyle(FEditorStyle::Get(), "HoverHintOnly")
.ToolTipText(LOCTEXT("QualityLevelMinLodToolTip", "Clear MinLOD conversion data"))
.ForegroundColor(FSlateColor::UseForeground())
.IsEnabled(TAttribute<bool>::CreateLambda([this]() { return GetMinLod().PerPlatform.Num() != 0 || GetMinLod().Default != 0; }))
.Content()
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("Icons.Delete"))
]
]
]
.ValueContent()
.MinDesiredWidth((float)(SkelMesh->GetQualityLevelMinLod().PerQuality.Num() + 1) * 125.0f)
.MaxDesiredWidth((float)((int32)QualityLevelProperty::EQualityLevels::Num + 1) * 125.0f)
[
SNew(SPerQualityLevelPropertiesWidget)
.OnGenerateWidget(this, &FPersonaMeshDetails::GetMinQualityLevelLodWidget)
.OnAddEntry(this, &FPersonaMeshDetails::AddMinLodQualityLevelOverride)
.OnRemoveEntry(this, &FPersonaMeshDetails::RemoveMinLodQualityLevelOverride)
.EntryNames(this, &FPersonaMeshDetails::GetMinQualityLevelLodOverrideNames)
];
TSharedPtr<IPropertyHandle> DisableBelowMinLodStrippingPropertyHandle = DetailLayout.GetProperty(USkeletalMesh::GetDisableBelowMinLodStrippingMemberName(), USkeletalMesh::StaticClass());
IDetailPropertyRow& DisableBelowMinLodStrippingRow = LODSettingsCategory.AddProperty(DisableBelowMinLodStrippingPropertyHandle);
DisableBelowMinLodStrippingRow.IsEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &FPersonaMeshDetails::IsLODInfoEditingEnabled, -1)));
@@ -3331,6 +3386,16 @@ void FPersonaMeshDetails::OnLODSettingsSelected(const FAssetData& AssetData)
}
}
bool FPersonaMeshDetails::IsQualityLevelMinLodEnable() const
{
return GEngine->UseSkeletalMeshMinLODPerQualityLevels;
}
bool FPersonaMeshDetails::IsMinLodEnable() const
{
return !GEngine->UseSkeletalMeshMinLODPerQualityLevels;
}
bool FPersonaMeshDetails::IsLODInfoEditingEnabled(int32 LODIndex) const
{
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
@@ -3529,6 +3594,140 @@ FText FPersonaMeshDetails::GetNoRefStreamingLODBiasTooltip() const
return LOCTEXT("NoRefStreamingLODBiasTooltip", "LOD bias for preloading no-ref mesh LODs. To use platform default, set to -1.");
}
FPerPlatformInt FPersonaMeshDetails::GetMinLod()
{
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
return SkelMesh->GetMinLod();
}
int32 FPersonaMeshDetails::GetMinQualityLevelLod(FName QualityLevel) const
{
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
int32 QLKey = QualityLevelProperty::FNameToQualityLevel(QualityLevel);
const int32* ValuePtr = (QualityLevel == NAME_None) ? nullptr : SkelMesh->GetQualityLevelMinLod().PerQuality.Find(QLKey);
return (ValuePtr != nullptr) ? *ValuePtr : SkelMesh->GetQualityLevelMinLod().Default;
}
void FPersonaMeshDetails::OnMinQualityLevelLodChanged(int32 NewValue, FName QualityLevel)
{
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
{
FSkinnedMeshComponentRecreateRenderStateContext ReregisterContext(SkelMesh);
NewValue = FMath::Clamp<int32>(NewValue, 0, MAX_STATIC_MESH_LODS - 1);
FPerQualityLevelInt MinLOD = SkelMesh->GetQualityLevelMinLod();
int32 QLKey = QualityLevelProperty::FNameToQualityLevel(QualityLevel);
if (QualityLevel == NAME_None || QLKey == INDEX_NONE)
{
MinLOD.Default = NewValue;
}
else
{
int32* ValuePtr = MinLOD.PerQuality.Find(QLKey);
if (ValuePtr != nullptr)
{
*ValuePtr = NewValue;
}
}
SkelMesh->SetQualityLevelMinLod(MoveTemp(MinLOD));
SkelMesh->Modify();
}
RefreshMeshDetailLayout();
}
void FPersonaMeshDetails::OnMinQualityLevelLodCommitted(int32 InValue, ETextCommit::Type CommitInfo, FName QualityLevel)
{
OnMinQualityLevelLodChanged(InValue, QualityLevel);
}
TSharedRef<SWidget> FPersonaMeshDetails::GetMinQualityLevelLodWidget(FName QualityLevelName) const
{
return SNew(SSpinBox<int32>)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Value(this, &FPersonaMeshDetails::GetMinQualityLevelLod, QualityLevelName)
.OnValueChanged(const_cast<FPersonaMeshDetails*>(this), &FPersonaMeshDetails::OnMinQualityLevelLodChanged, QualityLevelName)
.OnValueCommitted(const_cast<FPersonaMeshDetails*>(this), &FPersonaMeshDetails::OnMinQualityLevelLodCommitted, QualityLevelName)
.MinValue(0)
.MaxValue(MAX_STATIC_MESH_LODS)
.ToolTipText(LOCTEXT("QualityLevelMinLodTooltip", "The minimum quality level LOD to use for rendering. This can be overridden in components."))
.IsEnabled(FPersonaMeshDetails::GetLODCount() > 1);
}
bool FPersonaMeshDetails::AddMinLodQualityLevelOverride(FName QualityLevelName)
{
FScopedTransaction Transaction(LOCTEXT("AddMinLODQualityLevelOverride", "Add Min LOD Quality Level Override"));
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
SkelMesh->Modify();
int32 QLKey = QualityLevelProperty::FNameToQualityLevel(QualityLevelName);
if (SkelMesh->GetQualityLevelMinLod().PerQuality.Find(QLKey) == nullptr)
{
FPerQualityLevelInt MinLOD = SkelMesh->GetQualityLevelMinLod();
float Value = MinLOD.Default;
MinLOD.PerQuality.Add(QLKey, Value);
SkelMesh->SetQualityLevelMinLod(MoveTemp(MinLOD));
OnMinQualityLevelLodChanged(Value, QualityLevelName);
return true;
}
return false;
}
bool FPersonaMeshDetails::RemoveMinLodQualityLevelOverride(FName QualityLevelName)
{
FScopedTransaction Transaction(LOCTEXT("RemoveMinLODQualityLevelOverride", "Remove Min LOD Quality Level Override"));
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
SkelMesh->Modify();
FPerQualityLevelInt MinLOD = SkelMesh->GetQualityLevelMinLod();
int32 QL = QualityLevelProperty::FNameToQualityLevel(QualityLevelName);
if (QL != INDEX_NONE && MinLOD.PerQuality.Remove(QL) != 0)
{
float Value = MinLOD.Default;
SkelMesh->SetQualityLevelMinLod(MoveTemp(MinLOD));
OnMinQualityLevelLodChanged(Value, QualityLevelName);
return true;
}
return false;
}
TArray<FName> FPersonaMeshDetails::GetMinQualityLevelLodOverrideNames() const
{
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
TArray<FName> OverrideNames;
for (const TPair<int32, int32>& Pair : SkelMesh->GetQualityLevelMinLod().PerQuality)
{
OverrideNames.Add(QualityLevelProperty::QualityLevelToFName(Pair.Key));
}
OverrideNames.Sort(FNameLexicalLess());
return OverrideNames;
}
FReply FPersonaMeshDetails::ResetToDefault()
{
if (FPersonaMeshDetails::IsQualityLevelMinLodEnable())
{
USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh();
check(SkelMesh);
FPerPlatformInt PlatformMinLOD;
SkelMesh->SetMinLod(MoveTemp(PlatformMinLOD));
SkelMesh->Modify();
RefreshMeshDetailLayout();
}
return FReply::Handled();
}
FReply FPersonaMeshDetails::OnApplyChanges()
{
ApplyChanges();
@@ -6384,5 +6583,4 @@ bool FPersonaMeshDetails::FilterOutBakePose(const FAssetData& AssetData, USkelet
{
return !(Skeleton && Skeleton->IsCompatibleSkeletonByAssetData(AssetData));
}
#undef LOCTEXT_NAMESPACE