2023-02-24 15:02:45 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SkinWeightDetailCustomization.h"
# include "DetailCategoryBuilder.h"
# include "DetailLayoutBuilder.h"
# include "DetailWidgetRow.h"
# include "Widgets/Input/SSegmentedControl.h"
2023-06-06 12:28:59 -04:00
# include "SkeletalMesh/SkinWeightsPaintTool.h"
2023-02-24 15:02:45 -05:00
# include "SSkinWeightProfileImportOptions.h"
2023-08-23 19:29:44 -04:00
# include "Selection/PolygonSelectionMechanic.h"
2023-05-11 12:53:37 -04:00
# include "UObject/UnrealTypePrivate.h"
# include "Widgets/Input/SSpinBox.h"
# include "Widgets/Input/SButton.h"
2023-08-21 22:12:01 -04:00
# include "Widgets/Input/SNumericEntryBox.h"
# include "Widgets/Input/SSlider.h"
2023-02-24 15:02:45 -05:00
# define LOCTEXT_NAMESPACE "SkinWeightToolSettingsEditor"
2023-06-20 19:18:49 -04:00
// layout constants
float FSkinWeightDetailCustomization : : WeightSliderWidths = 150.0f ;
float FSkinWeightDetailCustomization : : WeightEditingLabelsPercent = 0.40f ;
float FSkinWeightDetailCustomization : : WeightEditVerticalPadding = 4.0f ;
float FSkinWeightDetailCustomization : : WeightEditHorizontalPadding = 2.0f ;
2023-02-24 15:02:45 -05:00
void FSkinWeightDetailCustomization : : CustomizeDetails ( IDetailLayoutBuilder & DetailBuilder )
{
2023-06-20 19:18:49 -04:00
CurrentDetailBuilder = & DetailBuilder ;
2023-02-24 15:02:45 -05:00
TArray < TWeakObjectPtr < UObject > > DetailObjects ;
DetailBuilder . GetObjectsBeingCustomized ( DetailObjects ) ;
// should be impossible to get multiple settings objects for a single tool
ensure ( DetailObjects . Num ( ) = = 1 ) ;
SkinToolSettings = Cast < USkinWeightsPaintToolProperties > ( DetailObjects [ 0 ] ) ;
2023-05-11 12:53:37 -04:00
// custom display of falloff mode as segmented toggle buttons
IDetailCategoryBuilder & EditModeCategory = DetailBuilder . EditCategory ( " Weight Editing Mode " , FText : : GetEmpty ( ) , ECategoryPriority : : Important ) ;
2023-06-20 19:18:49 -04:00
// add segmented control toggle for editing modes ("Brush" or "Selection")
2023-05-11 12:53:37 -04:00
EditModeCategory . AddCustomRow ( LOCTEXT ( " EditModeCategory " , " Weight Editing Mode " ) , false )
. WholeRowContent ( )
[
SNew ( SBox )
. Padding ( 2.0f )
[
SNew ( SSegmentedControl < EWeightEditMode > )
. ToolTipText ( LOCTEXT ( " EditingModeTooltip " ,
2023-08-22 14:51:34 -04:00
" Brush: edit weights by painting directly on mesh. \n "
" Vertices: select vertices and edit weights directly. \n "
" Bones: select and manipulate bones to preview deformations. \n " ) )
2023-05-11 12:53:37 -04:00
. Value_Lambda ( [ this ] ( )
{
return SkinToolSettings - > EditingMode ;
} )
. OnValueChanged_Lambda ( [ this ] ( EWeightEditMode Mode )
{
SkinToolSettings - > EditingMode = Mode ;
2023-05-15 19:40:24 -04:00
SkinToolSettings - > WeightTool - > ToggleEditingMode ( ) ;
2023-06-20 19:18:49 -04:00
if ( CurrentDetailBuilder )
{
CurrentDetailBuilder - > ForceRefreshDetails ( ) ;
}
2023-05-11 12:53:37 -04:00
} )
+ SSegmentedControl < EWeightEditMode > : : Slot ( EWeightEditMode : : Brush )
2023-05-13 13:09:30 -04:00
. Text ( LOCTEXT ( " BrushEditMode " , " Brush " ) )
2023-06-20 19:18:49 -04:00
+ SSegmentedControl < EWeightEditMode > : : Slot ( EWeightEditMode : : Vertices )
. Text ( LOCTEXT ( " VertexEditMode " , " Vertices " ) )
2023-08-22 14:51:34 -04:00
+ SSegmentedControl < EWeightEditMode > : : Slot ( EWeightEditMode : : Bones )
. Text ( LOCTEXT ( " BoneEditMode " , " Bones " ) )
2023-05-11 12:53:37 -04:00
]
] ;
2023-06-20 19:18:49 -04:00
// BRUSH editing mode UI
if ( SkinToolSettings - > EditingMode = = EWeightEditMode : : Brush )
{
AddBrushUI ( DetailBuilder ) ;
}
// VERTEX editing mode UI
if ( SkinToolSettings - > EditingMode = = EWeightEditMode : : Vertices )
{
AddSelectionUI ( DetailBuilder ) ;
}
2023-02-24 15:02:45 -05:00
2023-06-20 19:18:49 -04:00
// COLOR MODE category
IDetailCategoryBuilder & WeightColorsCategory = DetailBuilder . EditCategory ( " WeightColors " , FText : : GetEmpty ( ) , ECategoryPriority : : Important ) ;
WeightColorsCategory . InitiallyCollapsed ( true ) ;
WeightColorsCategory . AddCustomRow ( LOCTEXT ( " ColorModeCategory " , " Color Mode " ) , false )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " ColorModeLabel " , " Color Mode " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " ColorModeTooltip " , " Determines how the weight colors are displayed. " ) )
]
. ValueContent ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
[
SNew ( SBox )
. Padding ( 2.0f )
. HAlign ( HAlign_Center )
[
SNew ( SSegmentedControl < EWeightColorMode > )
. Value_Lambda ( [ this ] ( )
{
return SkinToolSettings - > ColorMode ;
} )
. OnValueChanged_Lambda ( [ this ] ( EWeightColorMode Mode )
{
SkinToolSettings - > ColorMode = Mode ;
SkinToolSettings - > bColorModeChanged = true ;
} )
+ SSegmentedControl < EWeightColorMode > : : Slot ( EWeightColorMode : : MinMax )
. Text ( LOCTEXT ( " MinMaxMode " , " Min / Max " ) )
+ SSegmentedControl < EWeightColorMode > : : Slot ( EWeightColorMode : : Ramp )
2024-04-22 20:29:25 -04:00
. Text ( LOCTEXT ( " RampMode " , " Ramp " ) )
+ SSegmentedControl < EWeightColorMode > : : Slot ( EWeightColorMode : : MultiColor )
. Text ( LOCTEXT ( " MultiColorMode " , " Multi Color " ) )
2023-06-20 19:18:49 -04:00
]
]
] ;
// hide all base brush properties that have been customized
const TSharedRef < IPropertyHandle > BrushModeHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( USkinWeightsPaintToolProperties , BrushMode ) ) ;
DetailBuilder . HideProperty ( BrushModeHandle ) ;
const TSharedRef < IPropertyHandle > BrushSizeHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushSize ) , UBrushBaseProperties : : StaticClass ( ) ) ;
DetailBuilder . HideProperty ( BrushSizeHandle ) ;
const TSharedRef < IPropertyHandle > BrushStrengthHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushStrength ) , UBrushBaseProperties : : StaticClass ( ) ) ;
DetailBuilder . HideProperty ( BrushStrengthHandle ) ;
const TSharedRef < IPropertyHandle > BrushFalloffHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushFalloffAmount ) , UBrushBaseProperties : : StaticClass ( ) ) ;
DetailBuilder . HideProperty ( BrushFalloffHandle ) ;
const TSharedRef < IPropertyHandle > BrushRadiusHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushRadius ) , UBrushBaseProperties : : StaticClass ( ) ) ;
DetailBuilder . HideProperty ( BrushRadiusHandle ) ;
const TSharedRef < IPropertyHandle > SpecifyRadiusHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , bSpecifyRadius ) , UBrushBaseProperties : : StaticClass ( ) ) ;
DetailBuilder . HideProperty ( SpecifyRadiusHandle ) ;
const TSharedRef < IPropertyHandle > EditModePropHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( USkinWeightsPaintToolProperties , EditingMode ) ) ;
DetailBuilder . HideProperty ( EditModePropHandle ) ;
const TSharedRef < IPropertyHandle > ColorModePropHandle = DetailBuilder . GetProperty ( GET_MEMBER_NAME_CHECKED ( USkinWeightsPaintToolProperties , ColorMode ) ) ;
DetailBuilder . HideProperty ( ColorModePropHandle ) ;
}
void FSkinWeightDetailCustomization : : AddBrushUI ( IDetailLayoutBuilder & DetailBuilder )
{
2023-02-24 15:02:45 -05:00
// custom display of falloff mode as segmented toggle buttons
IDetailCategoryBuilder & BrushCategory = DetailBuilder . EditCategory ( " Brush " , FText : : GetEmpty ( ) , ECategoryPriority : : Important ) ;
// add segmented control toggle for brush behavior modes ("Add", "Replace", etc..)
BrushCategory . AddCustomRow ( LOCTEXT ( " BrushModeCategory " , " Brush Mode " ) , false )
2023-05-11 12:53:37 -04:00
. WholeRowContent ( )
[
SNew ( SBox )
. Padding ( 2.0f )
2023-02-24 15:02:45 -05:00
[
2023-05-11 12:53:37 -04:00
SNew ( SSegmentedControl < EWeightEditOperation > )
2023-06-20 19:18:49 -04:00
. ToolTipText ( LOCTEXT ( " FloodTooltip " ,
" Add: applies the current weight plus the flood value to the new weight. \n "
" Replace: applies the current weight minus the strength value to the new weight. \n "
" Multiply: applies the current weight multiplied by the strength value to the new weight. \n "
" Relax: applies the average of the connected (by edge) vertex weights to the new vertex weight, blended by the strength. \n "
" This command operates on the selected bone(s) and selected vertices. \n "
" If no bones are selected, ALL bones are considered. \n "
" If no vertices are selected, ALL vertices are considered. " ) )
2023-05-11 12:53:37 -04:00
. Value_Lambda ( [ this ] ( )
{
return SkinToolSettings - > BrushMode ;
} )
. OnValueChanged_Lambda ( [ this ] ( EWeightEditOperation Mode )
{
2024-04-30 01:02:40 -04:00
SkinToolSettings - > SetBrushMode ( Mode ) ;
2023-05-11 12:53:37 -04:00
} )
+ SSegmentedControl < EWeightEditOperation > : : Slot ( EWeightEditOperation : : Add )
2023-05-13 13:09:30 -04:00
. Text ( LOCTEXT ( " BrushAddMode " , " Add " ) )
2023-05-11 12:53:37 -04:00
+ SSegmentedControl < EWeightEditOperation > : : Slot ( EWeightEditOperation : : Replace )
2023-05-13 13:09:30 -04:00
. Text ( LOCTEXT ( " BrushReplaceMode " , " Replace " ) )
2023-05-11 12:53:37 -04:00
+ SSegmentedControl < EWeightEditOperation > : : Slot ( EWeightEditOperation : : Multiply )
2023-05-13 13:09:30 -04:00
. Text ( LOCTEXT ( " BrushMultiplyMode " , " Multiply " ) )
2023-05-11 12:53:37 -04:00
+ SSegmentedControl < EWeightEditOperation > : : Slot ( EWeightEditOperation : : Relax )
2023-05-13 13:09:30 -04:00
. Text ( LOCTEXT ( " BrushRelaxMode " , " Relax " ) )
2023-02-24 15:02:45 -05:00
]
2023-05-11 12:53:37 -04:00
] ;
2023-02-24 15:02:45 -05:00
// add segmented control toggle for brush falloff modes ("Surface" or "Volume")
2023-05-11 12:53:37 -04:00
BrushCategory . AddCustomRow ( LOCTEXT ( " BrushFalloffModeCategory " , " Brush Falloff Mode " ) , false )
. WholeRowContent ( )
[
SNew ( SBox )
. Padding ( 2.0f )
2023-02-24 15:02:45 -05:00
[
2023-05-11 12:53:37 -04:00
SNew ( SSegmentedControl < EWeightBrushFalloffMode > )
. ToolTipText ( LOCTEXT ( " BrushFalloffModeTooltip " ,
" Surface: falloff is based on the distance along the surface from the brush center to nearby connected vertices. \n "
" Volume: falloff is based on the straight-line distance from the brush center to surrounding vertices. \n " ) )
. Value_Lambda ( [ this ] ( )
{
2023-06-20 19:18:49 -04:00
return SkinToolSettings - > GetBrushConfig ( ) . FalloffMode ;
2023-05-11 12:53:37 -04:00
} )
. OnValueChanged_Lambda ( [ this ] ( EWeightBrushFalloffMode Mode )
{
SkinToolSettings - > bColorModeChanged = true ;
2023-06-20 19:18:49 -04:00
SkinToolSettings - > GetBrushConfig ( ) . FalloffMode = Mode ;
SkinToolSettings - > SaveConfig ( ) ;
2023-05-11 12:53:37 -04:00
} )
+ SSegmentedControl < EWeightBrushFalloffMode > : : Slot ( EWeightBrushFalloffMode : : Surface )
. Text ( LOCTEXT ( " SurfaceMode " , " Surface " ) )
+ SSegmentedControl < EWeightBrushFalloffMode > : : Slot ( EWeightBrushFalloffMode : : Volume )
. Text ( LOCTEXT ( " VolumeMode " , " Volume " ) )
2023-02-24 15:02:45 -05:00
]
2023-05-11 12:53:37 -04:00
] ;
BrushCategory . AddCustomRow ( LOCTEXT ( " BrushSizeCategory " , " Brush Radius " ) , false )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " BrushRadiusLabel " , " Radius " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " BrushRadiusTooltip " , " The radius of the brush in scene units. " ) )
]
. ValueContent ( )
[
SNew ( SSpinBox < float > )
. MinValue ( 0.01f )
. MaxSliderValue ( 20.f )
. Value ( 10.0f )
. SupportDynamicSliderMaxValue ( true )
. Value_Lambda ( [ this ] ( )
{
2023-06-20 19:18:49 -04:00
return SkinToolSettings - > GetBrushConfig ( ) . Radius ;
2023-05-11 12:53:37 -04:00
} )
. OnValueChanged_Lambda ( [ this ] ( float NewValue )
{
SkinToolSettings - > BrushRadius = NewValue ;
2023-06-20 19:18:49 -04:00
SkinToolSettings - > GetBrushConfig ( ) . Radius = NewValue ;
2023-05-11 12:53:37 -04:00
FPropertyChangedEvent PropertyChangedEvent ( UBrushBaseProperties : : StaticClass ( ) - > FindPropertyByName ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushRadius ) ) ) ;
SkinToolSettings - > PostEditChangeProperty ( PropertyChangedEvent ) ;
} )
2023-06-20 19:18:49 -04:00
. OnValueCommitted_Lambda ( [ this ] ( float NewValue , ETextCommit : : Type CommitType )
{
SkinToolSettings - > SaveConfig ( ) ;
} )
2023-05-11 12:53:37 -04:00
] ;
BrushCategory . AddCustomRow ( LOCTEXT ( " BrushStrengthCategory " , " Brush Strength " ) , false )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " BrushStrengthLabel " , " Strength " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " BrushStrengthTooltip " , " The strength of the effect on the weights. Exact effect depends on the Brush mode. " ) )
]
. ValueContent ( )
[
SNew ( SSpinBox < float > )
. MinValue ( 0.f )
2023-05-16 18:09:44 -04:00
. MaxValue ( 2.0f )
. MaxSliderValue ( 1.f )
. Value ( 1.0f )
. SupportDynamicSliderMaxValue ( true )
2023-05-11 12:53:37 -04:00
. Value_Lambda ( [ this ] ( )
{
2023-06-20 19:18:49 -04:00
return SkinToolSettings - > GetBrushConfig ( ) . Strength ;
2023-05-11 12:53:37 -04:00
} )
. OnValueChanged_Lambda ( [ this ] ( float NewValue )
{
SkinToolSettings - > BrushStrength = NewValue ;
2023-06-20 19:18:49 -04:00
SkinToolSettings - > GetBrushConfig ( ) . Strength = NewValue ;
2023-05-11 12:53:37 -04:00
FPropertyChangedEvent PropertyChangedEvent ( UBrushBaseProperties : : StaticClass ( ) - > FindPropertyByName ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushStrength ) ) ) ;
SkinToolSettings - > PostEditChangeProperty ( PropertyChangedEvent ) ;
} )
2023-06-20 19:18:49 -04:00
. OnValueCommitted_Lambda ( [ this ] ( float NewValue , ETextCommit : : Type CommitType )
{
SkinToolSettings - > SaveConfig ( ) ;
} )
2023-05-11 12:53:37 -04:00
] ;
BrushCategory . AddCustomRow ( LOCTEXT ( " BrushFalloffCategory " , " Brush Falloff " ) , false )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " BrushFalloffLabel " , " Falloff " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " BrushFalloffTooltip " , " At 0, the brush has no falloff. At 1 it has exponential falloff. " ) )
]
. ValueContent ( )
[
SNew ( SSpinBox < float > )
. MinValue ( 0.f )
. MaxValue ( 1.f )
. Value_Lambda ( [ this ] ( )
{
2023-06-20 19:18:49 -04:00
return SkinToolSettings - > GetBrushConfig ( ) . Falloff ;
2023-05-11 12:53:37 -04:00
} )
. OnValueChanged_Lambda ( [ this ] ( float NewValue )
{
SkinToolSettings - > BrushFalloffAmount = NewValue ;
2023-06-20 19:18:49 -04:00
SkinToolSettings - > GetBrushConfig ( ) . Falloff = NewValue ;
2023-05-11 12:53:37 -04:00
FPropertyChangedEvent PropertyChangedEvent ( UBrushBaseProperties : : StaticClass ( ) - > FindPropertyByName ( GET_MEMBER_NAME_CHECKED ( UBrushBaseProperties , BrushFalloffAmount ) ) ) ;
SkinToolSettings - > PostEditChangeProperty ( PropertyChangedEvent ) ;
} )
2023-06-20 19:18:49 -04:00
. OnValueCommitted_Lambda ( [ this ] ( float NewValue , ETextCommit : : Type CommitType )
{
SkinToolSettings - > SaveConfig ( ) ;
} )
2023-05-11 12:53:37 -04:00
] ;
2023-06-20 19:18:49 -04:00
}
2023-05-11 12:53:37 -04:00
2023-06-20 19:18:49 -04:00
void FSkinWeightDetailCustomization : : AddSelectionUI ( IDetailLayoutBuilder & DetailBuilder )
{
2023-05-11 12:53:37 -04:00
// custom display of weight editing tools
2023-08-23 19:29:44 -04:00
IDetailCategoryBuilder & EditSelectionCategory = DetailBuilder . EditCategory ( " Edit Selection " , FText : : GetEmpty ( ) , ECategoryPriority : : Important ) ;
EditSelectionCategory . InitiallyCollapsed ( true ) ;
// GROW/SHRINK/FLOOD Selection category
EditSelectionCategory . AddCustomRow ( LOCTEXT ( " EditSelectionRow " , " Edit Selection " ) , false )
. WholeRowContent ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f , WeightEditVerticalPadding )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
. Text ( LOCTEXT ( " GrowSelectionButtonLabel " , " Grow " ) )
. ToolTipText ( LOCTEXT ( " GrowSelectionTooltip " ,
" Grow the current selection by adding connected neighbors to current selection. \n " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > GetSelectionMechanic ( ) - > GrowSelection ( ) ;
return FReply : : Handled ( ) ;
} )
]
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f , WeightEditVerticalPadding )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
. Text ( LOCTEXT ( " ShrinkSelectionButtonLabel " , " Shrink " ) )
. ToolTipText ( LOCTEXT ( " ShrinkSelectionTooltip " ,
" Shrink the current selection by removing vertices on the border of the current selection. \n " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > GetSelectionMechanic ( ) - > ShrinkSelection ( ) ;
return FReply : : Handled ( ) ;
} )
]
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f , WeightEditVerticalPadding )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
. Text ( LOCTEXT ( " FloodSelectionButtonLabel " , " Flood " ) )
. ToolTipText ( LOCTEXT ( " FloodSelectionTooltip " ,
" Flood the current selection by adding all connected vertices to the current selection. \n " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > GetSelectionMechanic ( ) - > FloodSelection ( ) ;
return FReply : : Handled ( ) ;
} )
]
] ;
// custom display of weight editing tools
IDetailCategoryBuilder & EditWeightsCategory = DetailBuilder . EditCategory ( " Edit Weights " , FText : : GetEmpty ( ) , ECategoryPriority : : Important ) ;
2023-05-11 12:53:37 -04:00
EditWeightsCategory . InitiallyCollapsed ( true ) ;
// AVERAGE/RELAX/NORMALIZE WEIGHTS category
EditWeightsCategory . AddCustomRow ( LOCTEXT ( " NormalizeWeightsRow " , " Normalize " ) , false )
. WholeRowContent ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f , WeightEditVerticalPadding )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
. Text ( LOCTEXT ( " AverageWeightsButtonLabel " , " Average " ) )
. ToolTipText ( LOCTEXT ( " AverageWeightsTooltip " ,
" Takes the average of vertex weights and applies it to the selected vertices. \n "
" This command operates on the selected bone(s) and selected vertices. \n "
" If no bones are selected, ALL bone weights are considered. \n "
" If no vertices are selected, ALL vertices are considered. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > AverageWeights ( ) ;
return FReply : : Handled ( ) ;
} )
]
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f , WeightEditVerticalPadding )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
. Text ( LOCTEXT ( " NormalizeWeightsButtonLabel " , " Normalize " ) )
. ToolTipText ( LOCTEXT ( " NormalizeWeightsTooltip " ,
" Forces the weights on the selected vertices to sum to 1. \n "
" This command operates on the selected vertices. \n "
" If no vertices are selected, ALL vertices are considered. " ) )
. IsEnabled_Lambda ( [ this ] ( )
{
2023-06-20 19:18:49 -04:00
return SkinToolSettings - > EditingMode = = EWeightEditMode : : Vertices ;
2023-05-11 12:53:37 -04:00
} )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > NormalizeWeights ( ) ;
return FReply : : Handled ( ) ;
} )
]
] ;
// MIRROR WEIGHTS category
EditWeightsCategory . AddCustomRow ( LOCTEXT ( " MirrorWeightsRow " , " Mirror " ) , false )
. WholeRowContent ( )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
2023-06-20 19:18:49 -04:00
. Padding ( WeightEditHorizontalPadding , WeightEditVerticalPadding )
2023-02-24 15:02:45 -05:00
[
SNew ( SHorizontalBox )
2023-05-11 12:53:37 -04:00
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
. FillWidth ( WeightEditingLabelsPercent )
2023-02-24 15:02:45 -05:00
[
2023-05-11 12:53:37 -04:00
SNew ( STextBlock )
. Text ( LOCTEXT ( " MirrorPlaneLabel " , " Mirror Plane " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " MirrorPlaneTooltip " , " The plane to copy weights across. " ) )
]
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1.f )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
2023-02-24 15:02:45 -05:00
[
2023-05-11 12:53:37 -04:00
SNew ( SSegmentedControl < EAxis : : Type > )
. ToolTipText ( LOCTEXT ( " MirrorAxisTooltip " ,
" X: copies weights across the YZ plane. \n "
" Y: copies weights across the XZ plane. \n "
" Z: copies weights across the XY plane. " ) )
2023-02-24 15:02:45 -05:00
. Value_Lambda ( [ this ] ( )
{
2023-05-11 12:53:37 -04:00
return SkinToolSettings - > MirrorAxis ;
2023-02-24 15:02:45 -05:00
} )
2023-05-11 12:53:37 -04:00
. OnValueChanged_Lambda ( [ this ] ( EAxis : : Type Mode )
2023-02-24 15:02:45 -05:00
{
2023-05-11 12:53:37 -04:00
SkinToolSettings - > MirrorAxis = Mode ;
2023-02-24 15:02:45 -05:00
} )
2023-05-11 12:53:37 -04:00
+ SSegmentedControl < EAxis : : Type > : : Slot ( EAxis : : X )
. Text ( LOCTEXT ( " MirrorXLabel " , " X " ) )
+ SSegmentedControl < EAxis : : Type > : : Slot ( EAxis : : Y )
. Text ( LOCTEXT ( " MirrorYLabel " , " Y " ) )
+ SSegmentedControl < EAxis : : Type > : : Slot ( EAxis : : Z )
. Text ( LOCTEXT ( " MirrorZLabel " , " Z " ) )
]
+ SHorizontalBox : : Slot ( )
[
SNew ( SSegmentedControl < EMirrorDirection > )
. ToolTipText ( LOCTEXT ( " MirrorDirectionTooltip " , " The direction that determines what side of the plane to copy weights from. " ) )
. Value_Lambda ( [ this ] ( )
{
return SkinToolSettings - > MirrorDirection ;
} )
. OnValueChanged_Lambda ( [ this ] ( EMirrorDirection Mode )
{
SkinToolSettings - > MirrorDirection = Mode ;
} )
+ SSegmentedControl < EMirrorDirection > : : Slot ( EMirrorDirection : : PositiveToNegative )
. Text ( LOCTEXT ( " MirrorPosToNegLabel " , " + to - " ) )
+ SSegmentedControl < EMirrorDirection > : : Slot ( EMirrorDirection : : NegativeToPositive )
. Text ( LOCTEXT ( " MirrorNegToPosLabel " , " - to + " ) )
2023-02-24 15:02:45 -05:00
]
]
2023-05-11 12:53:37 -04:00
]
2023-02-24 15:02:45 -05:00
2023-05-11 12:53:37 -04:00
+ SVerticalBox : : Slot ( )
. Padding ( 0.f , WeightEditVerticalPadding )
[
SNew ( SBox )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. Text ( LOCTEXT ( " MirrorWeightsButtonLabel " , " Mirror " ) )
. ToolTipText ( LOCTEXT ( " MirrorButtonTooltip " ,
" Weights are copied across the given plane in the given direction. \n "
" This command operates on the selected bone(s) and selected vertices. \n "
" If no bones are selected, ALL bone weights are considered. \n "
" If no vertices are selected, ALL vertices are considered. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > MirrorWeights ( SkinToolSettings - > MirrorAxis , SkinToolSettings - > MirrorDirection ) ;
return FReply : : Handled ( ) ;
} )
]
]
] ;
// FLOOD WEIGHTS category
EditWeightsCategory . AddCustomRow ( LOCTEXT ( " FloodWeightsRow " , " Flood " ) , false )
. WholeRowContent ( )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. Padding ( 0.f , WeightEditVerticalPadding )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
. FillWidth ( WeightEditingLabelsPercent )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " FloodAmountLabel " , " Flood Amount " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " FloodAmountTooltip " , " The amount of weight to apply in Flood operation. " ) )
]
+ SHorizontalBox : : Slot ( )
. MaxWidth ( WeightSliderWidths )
[
SNew ( SSpinBox < float > )
. MinValue ( 0.f )
2023-05-16 18:09:44 -04:00
. MaxValue ( 2.0f )
. MaxSliderValue ( 1.f )
. Value ( 1.0f )
. SupportDynamicSliderMaxValue ( true )
2023-05-11 12:53:37 -04:00
. Value_Lambda ( [ this ] ( )
{
return SkinToolSettings - > FloodValue ;
} )
. OnValueChanged_Lambda ( [ this ] ( float NewValue )
{
SkinToolSettings - > FloodValue = NewValue ;
} )
2023-06-20 19:18:49 -04:00
. OnValueCommitted_Lambda ( [ this ] ( float NewValue , ETextCommit : : Type CommitType )
{
SkinToolSettings - > SaveConfig ( ) ;
} )
2023-05-11 12:53:37 -04:00
]
]
2023-06-20 19:18:49 -04:00
+ SVerticalBox : : Slot ( )
2023-06-26 20:17:57 -04:00
. Padding ( 0.f , WeightEditVerticalPadding )
2023-05-11 12:53:37 -04:00
[
SNew ( SHorizontalBox )
2023-06-26 20:17:57 -04:00
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
. FillWidth ( WeightEditingLabelsPercent )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " FloodOperationLabel " , " Flood Operation " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " FloodOperationTooltip " , " The various flood weight operations. " ) )
]
2023-05-11 12:53:37 -04:00
2023-06-26 20:17:57 -04:00
+ SHorizontalBox : : Slot ( )
2023-05-11 12:53:37 -04:00
[
2023-06-26 20:17:57 -04:00
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. Text ( LOCTEXT ( " AddWeightsButtonLabel " , " Add " ) )
. ToolTipText ( LOCTEXT ( " AddOpTooltip " , " Add: applies the current weight plus the flood amount to the new weight. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > FloodWeights ( SkinToolSettings - > FloodValue , EWeightEditOperation : : Add ) ;
return FReply : : Handled ( ) ;
} )
]
+ SHorizontalBox : : Slot ( )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. Text ( LOCTEXT ( " ReplaceWeightsButtonLabel " , " Replace " ) )
. ToolTipText ( LOCTEXT ( " ReplaceOpTooltip " , " Replace: applies the current weight minus the flood amount to the new weight. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > FloodWeights ( SkinToolSettings - > FloodValue , EWeightEditOperation : : Replace ) ;
return FReply : : Handled ( ) ;
} )
]
+ SHorizontalBox : : Slot ( )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. Text ( LOCTEXT ( " MultiplyeightsButtonLabel " , " Multiply " ) )
. ToolTipText ( LOCTEXT ( " MultiplyOpTooltip " , " Multiply: applies the current weight multiplied by the flood amount to the new weight. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > FloodWeights ( SkinToolSettings - > FloodValue , EWeightEditOperation : : Multiply ) ;
return FReply : : Handled ( ) ;
} )
]
+ SHorizontalBox : : Slot ( )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. Text ( LOCTEXT ( " RelaxWeightsButtonLabel " , " Relax " ) )
. ToolTipText ( LOCTEXT ( " RelaxeOpTooltip " , " Relax: applies the average of the connected (by edge) vertex weights to the new vertex weight, scaled by the flood amount. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > FloodWeights ( SkinToolSettings - > FloodValue , EWeightEditOperation : : Relax ) ;
return FReply : : Handled ( ) ;
} )
]
2023-05-11 12:53:37 -04:00
]
]
] ;
// PRUNE WEIGHTS category
EditWeightsCategory . AddCustomRow ( LOCTEXT ( " PruneWeightsRow " , " Prune " ) , false )
. WholeRowContent ( )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
2023-06-20 19:18:49 -04:00
. Padding ( WeightEditHorizontalPadding , WeightEditVerticalPadding )
2023-05-11 12:53:37 -04:00
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
. FillWidth ( WeightEditingLabelsPercent )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " PruneThresholdLabel " , " Prune Threshold " ) )
. Font ( FAppStyle : : GetFontStyle ( TEXT ( " PropertyWindow.NormalFont " ) ) )
. ToolTipText ( LOCTEXT ( " PruneThresholdTooltip " , " The threshold weight value to use with Prune operation. " ) )
]
+ SHorizontalBox : : Slot ( )
. MaxWidth ( WeightSliderWidths )
[
SNew ( SSpinBox < float > )
. MinValue ( 0.f )
. MaxValue ( 1.f )
. Value_Lambda ( [ this ] ( )
{
return SkinToolSettings - > PruneValue ;
} )
. OnValueChanged_Lambda ( [ this ] ( float NewValue )
{
SkinToolSettings - > PruneValue = NewValue ;
} )
2023-06-20 19:18:49 -04:00
. OnValueCommitted_Lambda ( [ this ] ( float NewValue , ETextCommit : : Type CommitType )
{
SkinToolSettings - > SaveConfig ( ) ;
} )
2023-05-11 12:53:37 -04:00
]
]
+ SVerticalBox : : Slot ( )
2023-06-20 19:18:49 -04:00
. Padding ( WeightEditHorizontalPadding , WeightEditVerticalPadding )
2023-05-11 12:53:37 -04:00
[
SNew ( SBox )
. MinDesiredWidth ( WeightSliderWidths )
[
SNew ( SButton )
. HAlign ( HAlign_Center )
. Text ( LOCTEXT ( " PruneWeightsButtonLabel " , " Prune " ) )
. ToolTipText ( LOCTEXT ( " PruneButtonTooltip " ,
" Weights below the given threshold value are removed. \n "
" This command operates on the selected bone(s) and selected vertices. \n "
" If no bones are selected, ALL bone weights are considered. \n "
" If no vertices are selected, ALL vertices are considered. " ) )
. OnClicked_Lambda ( [ this ] ( )
{
SkinToolSettings - > WeightTool - > PruneWeights ( SkinToolSettings - > PruneValue ) ;
return FReply : : Handled ( ) ;
} )
]
]
] ;
2023-08-21 22:12:01 -04:00
// VERTEX EDITOR category
EditWeightsCategory . AddCustomRow ( LOCTEXT ( " VertexEditorRow " , " Vertex Editor " ) , false )
. WholeRowContent ( )
[
SNew ( SVertexWeightEditor , SkinToolSettings - > WeightTool )
] ;
}
void SVertexWeightItem : : Construct ( const FArguments & InArgs , const TSharedRef < STableViewBase > & OwnerTableView )
{
Element = InArgs . _Element ;
ParentTable = InArgs . _ParentTable ;
SMultiColumnTableRow < TSharedPtr < FWeightEditorElement > > : : Construct ( FSuperRowType : : FArguments ( ) , OwnerTableView ) ;
}
TSharedRef < SWidget > SVertexWeightItem : : GenerateWidgetForColumn ( const FName & ColumnName )
{
if ( ColumnName = = " Bone " )
{
const FName BoneName = ParentTable - > Tool - > GetBoneNameFromIndex ( Element - > BoneIndex ) ;
return SNew ( STextBlock ) . Text ( FText : : FromName ( BoneName ) ) ;
}
if ( ColumnName = = " Weight " )
{
// add a buffer to the slider to prevent ever fully getting a value to 1 or 0 using the slider alone
// doing so will remove other influences and cause the slider to no longer function as all other influences
// will be culled by normalization thus making the slider "stuck" at full value.
constexpr float SliderBuffer = 0.001f ;
return SNew ( SNumericEntryBox < float > )
. AllowSpin ( true )
. MinSliderValue ( SliderBuffer )
. MinValue ( 0.f )
. MaxSliderValue ( 1.0f - SliderBuffer )
. MaxValue ( 1.f )
. Value_Lambda ( [ this ] ( )
{
return ParentTable - > Tool - > GetAverageWeightOnBone ( Element - > BoneIndex , ParentTable - > SelectedVertices ) ;
} )
. OnValueChanged_Lambda ( [ this ] ( float NewValue )
{
TArray < VertexIndex > VerticesToEdit ;
ParentTable - > Tool - > GetSelectedVertices ( VerticesToEdit ) ;
ParentTable - > Tool - > SetBoneWeightOnVertices ( Element - > BoneIndex , NewValue , VerticesToEdit , ! bInTransaction ) ;
} )
. OnValueCommitted_Lambda ( [ this ] ( float NewValue , ETextCommit : : Type CommitType )
{
bInTransaction = false ;
} )
. OnBeginSliderMovement_Lambda ( [ this ] ( )
{
ParentTable - > Tool - > BeginChange ( ) ;
bInTransaction = true ;
} )
. OnEndSliderMovement_Lambda ( [ this ] ( float )
{
const FText TransactionLabel = LOCTEXT ( " DirectWeightChange " , " Set weights on vertices. " ) ;
ParentTable - > Tool - > EndChange ( TransactionLabel ) ;
bInTransaction = false ;
} )
. ToolTipText ( LOCTEXT ( " WeightSliderToolTip " , " Set the weight on this bone for the selected vertices. " ) ) ;
}
checkNoEntry ( ) ;
return SNullWidget : : NullWidget ;
}
2023-11-16 13:03:39 -05:00
SVertexWeightEditor : : ~ SVertexWeightEditor ( )
{
if ( Tool . IsValid ( ) )
{
Tool - > OnSelectionChanged . RemoveAll ( this ) ;
Tool - > OnWeightsChanged . RemoveAll ( this ) ;
Tool . Reset ( ) ;
}
}
2023-08-21 22:12:01 -04:00
void SVertexWeightEditor : : Construct ( const FArguments & InArgs , USkinWeightsPaintTool * InSkinTool )
{
Tool = InSkinTool ;
2023-11-16 13:03:39 -05:00
2023-08-21 22:12:01 -04:00
ChildSlot
[
SNew ( SBox )
[
SAssignNew ( ListView , SWeightEditorListViewType )
. SelectionMode ( ESelectionMode : : Single )
. ListItemsSource ( & ListViewItems )
. OnGenerateRow_Lambda ( [ this ] ( TSharedPtr < FWeightEditorElement > Element , const TSharedRef < STableViewBase > & OwnerTableView )
{
return SNew ( SVertexWeightItem , OwnerTableView ) . Element ( Element ) . ParentTable ( SharedThis ( this ) ) ;
} )
. HeaderRow
(
SNew ( SHeaderRow )
+ SHeaderRow : : Column ( " Bone " ) . DefaultLabel ( NSLOCTEXT ( " WeightEditorBoneColumn " , " Bone " , " Bone " ) )
+ SHeaderRow : : Column ( " Weight " ) . DefaultLabel ( NSLOCTEXT ( " WeightEditorWeightColumn " , " Weight (Average) " , " Weight (Average) " ) )
)
]
] ;
RefreshView ( ) ;
2023-11-16 13:03:39 -05:00
Tool - > OnSelectionChanged . AddSP ( this , & SVertexWeightEditor : : RefreshView ) ;
Tool - > OnWeightsChanged . AddSP ( this , & SVertexWeightEditor : : RefreshView ) ;
2023-08-21 22:12:01 -04:00
}
void SVertexWeightEditor : : RefreshView ( )
{
if ( ! Tool . IsValid ( ) )
{
return ;
}
// get list of selected vertex indices
SelectedVertices . Reset ( ) ;
Tool - > GetSelectedVertices ( SelectedVertices ) ;
// get all bones affecting the selected vertices
TArray < int32 > Influences ;
Tool - > GetInfluences ( SelectedVertices , Influences ) ;
// generate list view items
ListViewItems . Reset ( ) ;
for ( const int32 InfluenceIndex : Influences )
{
ListViewItems . Add ( MakeShareable ( new FWeightEditorElement ( InfluenceIndex ) ) ) ;
}
ListView - > RequestListRefresh ( ) ;
2023-02-24 15:02:45 -05:00
}
2023-06-20 19:18:49 -04:00
# undef LOCTEXT_NAMESPACE