Files
sbox-public/game/editor/ShaderGraph/Code/Nodes/Unary.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

558 lines
13 KiB
C#

namespace Editor.ShaderGraph.Nodes;
public enum ExponentBase
{
BaseE,
Base2,
}
public enum LogBase
{
BaseE,
Base2,
Base10,
}
public enum DerivativePrecision
{
Standard,
Course,
Fine
}
public abstract class Unary : ShaderNode
{
[Input]
[Hide]
public virtual NodeInput Input { get; set; }
protected virtual string Op { get; }
[Hide]
protected virtual int? Components { get; } = null;
public Unary() : base()
{
ExpandSize = new Vector2( -50, 0 );
}
[Output]
[Hide]
public virtual NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var result = compiler.ResultOrDefault( Input, 0.0f );
return result.IsValid ? new NodeResult( Components ?? result.Components, $"{Op}( {result} )" ) : default;
};
}
public abstract class UnaryCurve : Unary
{
[Input, Title( "" ), Hide]
public override NodeInput Input { get => base.Input; set => base.Input = value; }
[Output, Title( "" ), Hide]
public override NodeResult.Func Result => base.Result;
protected virtual float Evaluate( float x ) => 0.0f;
public UnaryCurve() : base()
{
ExpandSize = new Vector2( -12 * 6, 12 );
}
public override void OnPaint( Rect rect )
{
rect = rect.Shrink( 20, 28, 20, 6 );
Paint.SetBrush( Theme.TextControl );
Paint.SetPen( Theme.TextControl, 2 );
var inc = 1f / 16f;
List<Vector2> points = new List<Vector2>();
for ( var i = 0f; i < 1f; i += inc )
{
var x = rect.BottomLeft.x + rect.Width * i;
var y = rect.BottomLeft.y - rect.Height * Evaluate( i );
points.Add( new Vector2( x, y ) );
}
for ( int i = points.Count - 1; i >= 0; i-- )
{
points.Add( points[i] );
}
Paint.DrawPolygon( points.ToArray() );
}
}
[Title( "Cosine" ), Category( "Unary" )]
public sealed class Cosine : UnaryCurve
{
protected override float Evaluate( float x )
{
return MathF.Cos( x * MathF.PI * 2 ) / 2 + 0.5f;
}
[Hide]
protected override string Op => "cos";
}
[Title( "Abs" ), Category( "Unary" )]
public sealed class Abs : Unary
{
[Input, Title( "" ), Hide]
public override NodeInput Input { get => base.Input; set => base.Input = value; }
[Output, Title( "" ), Hide]
public override NodeResult.Func Result => base.Result;
public Abs() : base()
{
ExpandSize = new Vector2( -12 * 8, 12 );
}
public override void OnPaint( Rect rect )
{
rect = rect.Shrink( 0, 20, 0, 0 );
Paint.SetPen( Theme.TextControl );
Paint.SetFont( "Poppins Bold", 12 );
Paint.DrawText( rect, "| x |" );
}
[Hide]
protected override string Op => "abs";
}
[Title( "Dot Product" ), Category( "Unary" )]
public sealed class DotProduct : ShaderNode
{
[Input]
[Hide]
public NodeInput InputA { get; set; }
[Input]
[Hide]
public NodeInput InputB { get; set; }
[Output]
[Hide]
public NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var resultA = compiler.ResultOrDefault( InputA, 0.0f );
var resultB = compiler.ResultOrDefault( InputB, 0.0f ).Cast( resultA.Components );
return new NodeResult( 1, $"dot( {resultA}, {resultB} )" );
};
}
[Title( "DDX" ), Category( "Unary" )]
public sealed class DDX : Unary
{
public DerivativePrecision Precision { get; set; }
[Hide]
protected override string Op
{
get
{
return Precision switch
{
DerivativePrecision.Course => "ddx_course",
DerivativePrecision.Fine => "ddx_fine",
_ => "ddx",
};
}
}
}
[Title( "DDY" ), Category( "Unary" )]
public sealed class DDY : Unary
{
public DerivativePrecision Precision { get; set; }
[Hide]
protected override string Op
{
get
{
return Precision switch
{
DerivativePrecision.Course => "ddy_course",
DerivativePrecision.Fine => "ddy_fine",
_ => "ddy",
};
}
}
}
[Title( "DDXY" ), Category( "Unary" )]
public sealed class DDXY : Unary
{
[Hide]
protected override string Op => "fwidth";
}
[Title( "Exponential" ), Category( "Unary" )]
public sealed class Exponential : Unary
{
public ExponentBase Base { get; set; }
[Input, Title( "" ), Hide]
public override NodeInput Input { get => base.Input; set => base.Input = value; }
[Output, Title( "" ), Hide]
public override NodeResult.Func Result => base.Result;
public Exponential() : base()
{
ExpandSize = new Vector2( -12 * 6, 12 );
}
public override void OnPaint( Rect rect )
{
rect = rect.Shrink( 0, 20, 0, 0 );
Paint.SetPen( Theme.TextControl );
Paint.SetFont( "Poppins Bold", 16 );
Paint.DrawText( rect, (Base == ExponentBase.BaseE) ? "e" : "2" );
Paint.SetFont( "Poppins Bold", 8 );
Paint.DrawText( rect.Shrink( 20, 0, 0, 16 ), "x" );
}
[Hide]
protected override string Op => Base == ExponentBase.BaseE ? "exp" : "exp2";
}
[Title( "Frac" ), Category( "Unary" )]
public sealed class Frac : Unary
{
[Hide]
protected override string Op => "frac";
}
[Title( "Floor" ), Category( "Unary" )]
public sealed class Floor : Unary
{
[Hide]
protected override string Op => "floor";
}
[Title( "Length" ), Category( "Unary" )]
public sealed class Length : Unary
{
[Hide]
protected override int? Components => 1;
[Hide]
protected override string Op => "length";
}
[Title( "Log" ), Category( "Unary" )]
public sealed class BaseLog : Unary
{
public LogBase Base { get; set; }
[Hide]
protected override string Op
{
get
{
return Base switch
{
LogBase.Base2 => "log2",
LogBase.Base10 => "log10",
_ => "log",
};
}
}
}
[Title( "Min" ), Category( "Unary" )]
public sealed class Min : ShaderNode
{
[Input( typeof( float ) )]
[Hide]
public NodeInput InputA { get; set; }
[Input( typeof( float ) )]
[Hide]
public NodeInput InputB { get; set; }
[InputDefault( nameof( InputA ) )]
public float DefaultA { get; set; } = 0.0f;
[InputDefault( nameof( InputB ) )]
public float DefaultB { get; set; } = 0.0f;
[Output]
[Hide]
public NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var a = compiler.ResultOrDefault( InputA, DefaultA );
var b = compiler.ResultOrDefault( InputB, DefaultB );
int maxComponents = Math.Max( a.IsValid ? a.Components : 1, b.IsValid ? b.Components : 1 );
return new NodeResult( maxComponents, $"min( {(a.IsValid ? a : "0.0f")}, {(b.IsValid ? b : "0.0f")} )" );
};
}
[Title( "Max" ), Category( "Unary" )]
public sealed class Max : ShaderNode
{
[Input( typeof( float ) )]
[Hide]
public NodeInput InputA { get; set; }
[Input( typeof( float ) )]
[Hide]
public NodeInput InputB { get; set; }
[InputDefault( nameof( InputA ) )]
public float DefaultA { get; set; } = 0.0f;
[InputDefault( nameof( InputB ) )]
public float DefaultB { get; set; } = 0.0f;
[Output]
[Hide]
public NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var a = compiler.ResultOrDefault( InputA, DefaultA );
var b = compiler.ResultOrDefault( InputB, DefaultB );
int maxComponents = Math.Max( a.IsValid ? a.Components : 1, b.IsValid ? b.Components : 1 );
return new NodeResult( maxComponents, $"max( {(a.IsValid ? a : "0.0f")}, {(b.IsValid ? b : "0.0f")} )" );
};
}
/// <summary>
/// Clamps the specified value within the range of 0 to 1
/// </summary>
[Title( "Saturate" ), Category( "Transform" ), Icon( "opacity" )]
public sealed class Saturate : Unary
{
[Hide]
protected override string Op => "saturate";
}
[Title( "Sine" ), Category( "Unary" )]
public sealed class Sine : UnaryCurve
{
protected override float Evaluate( float x )
{
return MathF.Sin( x * MathF.PI * 2 ) / 2 + 0.5f;
}
[Hide]
protected override string Op => "sin";
}
[Title( "Step" ), Category( "Unary" )]
public sealed class Step : ShaderNode
{
[Input( typeof( float ) )]
[Hide]
public NodeInput Input { get; set; }
[Input( typeof( float ) )]
[Hide]
public NodeInput Edge { get; set; }
[InputDefault( nameof( Input ) )]
public float DefaultInput { get; set; } = 0.0f;
[InputDefault( nameof( Edge ) )]
public float DefaultEdge { get; set; } = 0.0f;
[Output]
[Hide]
public NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var edge = compiler.ResultOrDefault( Edge, DefaultEdge );
var input = compiler.ResultOrDefault( Input, DefaultInput );
int maxComponents = Math.Max( edge.IsValid ? edge.Components : 1, input.IsValid ? input.Components : 1 );
return new NodeResult( maxComponents, $"step( {(edge.IsValid ? edge : "0.0f")}, {(input.IsValid ? input : "0.0f")} )" );
};
}
[Title( "Smooth Step" ), Category( "Unary" )]
public sealed class SmoothStep : ShaderNode
{
[Input( typeof( float ) )]
[Hide]
public NodeInput Input { get; set; }
[Input( typeof( float ) )]
[Hide]
public NodeInput Edge1 { get; set; }
[Input( typeof( float ) )]
[Hide]
public NodeInput Edge2 { get; set; }
[InputDefault( nameof( Input ) )]
public float DefaultInput { get; set; } = 0.0f;
[InputDefault( nameof( Edge1 ) )]
public float DefaultEdge1 { get; set; } = 0.0f;
[InputDefault( nameof( Edge2 ) )]
public float DefaultEdge2 { get; set; } = 0.0f;
[Output]
[Hide]
public NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var edge1 = compiler.Result( Edge1 );
var edge2 = compiler.Result( Edge2 );
var input = compiler.Result( Input );
int maxComponents = Math.Max( edge1.IsValid ? edge1.Components : 1, input.IsValid ? input.Components : 1 );
maxComponents = Math.Max( edge2.IsValid ? edge2.Components : 1, maxComponents );
var edge1String = edge1.IsValid ? edge1.ToString() : compiler.ResultValue( DefaultEdge1 ).ToString();
var edge2String = edge2.IsValid ? edge2.ToString() : compiler.ResultValue( DefaultEdge2 ).ToString();
var inputString = input.IsValid ? input.ToString() : compiler.ResultValue( DefaultInput ).ToString();
return new NodeResult( maxComponents, $"smoothstep( {edge1String}, {edge2String}, {inputString} )" );
};
}
/// <summary>
/// Computes the tangent of a specified angle (in radians).
/// </summary>
[Title( "Tangent" ), Category( "Unary" )]
public sealed class Tan : Unary
{
[Hide]
protected override string Op => "tan";
}
/// <summary>
/// Computes the angle (in radians) whose sine is the specified number.
/// </summary>
[Title( "Arcsin" ), Category( "Unary" )]
public sealed class Arcsin : Unary
{
[Hide]
protected override string Op => "asin";
}
/// <summary>
/// Computes the angle (in radians) whose cosine is the specified number.
/// </summary>
[Title( "Arccos" ), Category( "Unary" )]
public sealed class Arccos : Unary
{
[Hide]
protected override string Op => "acos";
}
/// <summary>
/// Round to the nearest integer.
/// </summary>
[Title( "Round" ), Category( "Unary" )]
public sealed class Round : Unary
{
[Hide]
protected override string Op => "round";
}
/// <summary>
/// Returns the smallest integer value that is greater than or equal to the specified value.
/// </summary>
[Title( "Ceil" ), Category( "Unary" )]
public sealed class Ceil : Unary
{
[Hide]
protected override string Op => "ceil";
}
[Title( "One Minus" ), Category( "Unary" )]
public sealed class OneMinus : ShaderNode
{
[Input( typeof( float ) ), Hide, Title( "" )]
public NodeInput In { get; set; }
[Output, Hide, Title( "" )]
public NodeResult.Func Out => ( GraphCompiler compiler ) =>
{
var result = compiler.ResultOrDefault( In, 0.0f );
return new NodeResult( result.Components, $"1 - {result}" );
};
public OneMinus() : base()
{
ExpandSize = new Vector2( -85, 0 );
}
public override void OnPaint( Rect rect )
{
rect = rect.Shrink( 0, 20, 0, 0 );
Paint.SetPen( Theme.TextControl );
Paint.SetFont( "Poppins Bold", 12 );
Paint.DrawText( rect, "1 - x" );
}
}
[Title( "Sqrt" ), Category( "Unary" )]
public sealed class Sqrt : Unary
{
[Input, Title( "" ), Hide]
public override NodeInput Input { get => base.Input; set => base.Input = value; }
[Output, Title( "" ), Hide]
public override NodeResult.Func Result => base.Result;
public Sqrt() : base()
{
ExpandSize = new Vector2( -12 * 8, 12 );
}
public override void OnPaint( Rect rect )
{
rect = rect.Shrink( 2, 20, 0, 0 );
Paint.SetPen( Theme.TextControl );
Paint.SetFont( "Poppins Bold", 12 );
Paint.DrawText( rect, " x" );
List<Vector2> points = new()
{
rect.TopLeft + new Vector2(10, 20),
rect.TopLeft + new Vector2(14, 20),
rect.TopLeft + new Vector2(18, 30),
rect.TopLeft + new Vector2(22, 12),
rect.TopLeft + new Vector2(40, 12)
};
for ( int i = points.Count - 1; i >= 0; i-- )
{
points.Add( points[i] );
}
Paint.DrawPolygon( points.ToArray() );
}
[Hide]
protected override string Op => "sqrt";
}
/// <summary>
/// Returns a distance scalar between two vectors.
/// </summary>
[Title( "Distance" ), Category( "Unary" )]
public sealed class Distance : ShaderNode
{
[Input]
[Hide]
public NodeInput A { get; set; }
[Input]
[Hide]
public NodeInput B { get; set; }
[Output]
[Hide]
public NodeResult.Func Result => ( GraphCompiler compiler ) =>
{
var resultA = compiler.ResultOrDefault( A, 0.0f );
var resultB = compiler.ResultOrDefault( B, 0.0f ).Cast( resultA.Components );
return new NodeResult( 1, $"distance( {resultA}, {resultB} )" );
};
}