Editor/EffigyEditor/EffigyWeightBar.cs
using Editor;
using Effigy;
using Sandbox;
using System;
namespace Marionette.EditorTools;
/// <summary>
/// Radius, strength, falloff and brush kind for weight painting. Same shape as the sculpt and
/// paint bars: values about the stroke, floating near the model.
/// </summary>
internal sealed class EffigyWeightBar : Widget
{
public const float BarHeight = 28f;
private readonly EffigyNumericField _radius;
private readonly EffigyNumericField _strength;
private readonly ComboBox _falloff;
private readonly ComboBox _kind;
private readonly Editor.Label _bone;
private WeightPaintSession _session;
private bool _refreshing;
public Action Changed { get; set; }
public EffigyWeightBar( Widget parent ) : base( parent )
{
TranslucentBackground = true;
NoSystemBackground = true;
Visible = false;
FixedHeight = BarHeight;
FixedWidth = 720f;
Layout = Layout.Row();
Layout.Spacing = 8;
Layout.Add( new Editor.Label( "Radius" ) { Color = Theme.TextControl.WithAlpha( 0.6f ) } );
_radius = new EffigyNumericField( this, 0.25f, "u" )
{
Min = 1e-4f,
ValueEdited = v => { if ( _session is not null ) { _session.Radius = MathF.Max( v, 1e-4f ); Changed?.Invoke(); } },
FixedWidth = 90f,
};
Layout.Add( _radius );
Layout.Add( new Editor.Label( "Strength" ) { Color = Theme.TextControl.WithAlpha( 0.6f ) } );
_strength = new EffigyNumericField( this, 0.15f )
{
Min = 0f,
Max = 1f,
ValueEdited = v => { if ( _session is not null ) { _session.Strength = Math.Clamp( v, 0f, 1f ); Changed?.Invoke(); } },
FixedWidth = 90f,
};
Layout.Add( _strength );
Layout.Add( new Editor.Label( "Falloff" ) { Color = Theme.TextControl.WithAlpha( 0.6f ) } );
_falloff = new ComboBox( this ) { MinimumWidth = 90f };
Layout.Add( _falloff );
Layout.Add( new Editor.Label( "Brush" ) { Color = Theme.TextControl.WithAlpha( 0.6f ) } );
_kind = new ComboBox( this ) { MinimumWidth = 90f };
Layout.Add( _kind );
_bone = new Editor.Label( "" ) { Color = Theme.TextControl.WithAlpha( 0.75f ) };
Layout.Add( _bone, 1 );
}
public Color GapColor { get; set; } = Theme.ControlBackground;
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( GapColor );
Paint.DrawRect( LocalRect );
}
public void Bind( WeightPaintSession session, string boneName )
{
_session = session;
Visible = session is not null;
if ( session is not null )
Refresh( boneName );
}
public void Refresh( string boneName = null )
{
if ( _session is null )
return;
_radius.SetValue( _session.Radius );
_strength.SetValue( _session.Strength );
RefreshCombo( _falloff, Enum.GetValues<BrushFalloff>(), _session.Falloff, k => _session.Falloff = k );
RefreshCombo( _kind, Enum.GetValues<WeightBrushKind>(), _session.Brush, k => _session.Brush = k );
_bone.Text = string.IsNullOrEmpty( boneName )
? "Pick a bone in the Rig tree"
: $"Painting {boneName} — ramp is a texture, not vertex colour";
}
private void RefreshCombo<T>( ComboBox box, T[] values, T current, Action<T> set ) where T : struct, Enum
{
if ( box is null )
return;
_refreshing = true;
try
{
box.Clear();
foreach ( var value in values )
{
var kind = value;
box.AddItem( kind.ToString(), onSelected: () =>
{
if ( _refreshing || _session is null )
return;
set( kind );
Changed?.Invoke();
}, selected: kind.Equals( current ) );
}
}
finally
{
_refreshing = false;
}
}
}