using System.Text.Json.Serialization; namespace Editor.ShaderGraph; /// /// Defines an input for a subgraph with detailed configuration options /// [Title( "Subgraph Input" ), Category( "Subgraph" ), Icon( "input" )] public sealed class SubgraphInput : ShaderNode, IErroringNode { [Hide] public override string Title => string.IsNullOrWhiteSpace( InputName ) ? $"Subgraph Input" : $"{InputName} ({InputType})"; /// /// The name of the input parameter /// [KeyProperty] public string InputName { get; set; } = ""; /// /// Description of what this input does /// [TextArea] public string InputDescription { get; set; } = ""; /// /// The type of the input parameter /// public InputType InputType { get; set; } = InputType.Float; /// /// Default value for float inputs /// [ShowIf( nameof( InputType ), InputType.Float )] public float DefaultFloat { get; set; } = 0.0f; /// /// Default value for float2 inputs /// [ShowIf( nameof( InputType ), InputType.Float2 )] public Vector2 DefaultFloat2 { get; set; } = Vector2.Zero; /// /// Default value for float3 inputs /// [ShowIf( nameof( InputType ), InputType.Float3 )] public Vector3 DefaultFloat3 { get; set; } = Vector3.Zero; /// /// Default value for color inputs /// [ShowIf( nameof( InputType ), InputType.Color )] public Color DefaultColor { get; set; } = Color.White; /// /// Whether this input is required (must have a connection in order to compile) /// public bool IsRequired { get; set; } = false; /// /// The order of this input port on the subgraph node /// [Title( "Order" )] public int PortOrder { get; set; } = 0; /// /// Preview input for testing values in subgraphs /// [Input( typeof( object ) ), Title( "Preview" ), Hide] public NodeInput PreviewInput { get; set; } /// /// Output for the input value /// [Output( typeof( float ) ), Title( "Value" ), Hide] public NodeResult.Func Result => ( GraphCompiler compiler ) => { // In subgraphs, check if preview input is connected if ( compiler.Graph.IsSubgraph && PreviewInput.IsValid ) { return compiler.Result( PreviewInput ); } // Use the appropriate default value based on input type var outputValue = GetOutputValue(); // If we're in a subgraph context, just return the value directly if ( compiler.Graph.IsSubgraph ) { return compiler.ResultValue( outputValue ); } // For normal graphs, use ResultParameter to create a material parameter return compiler.ResultParameter( InputName, outputValue, default, default, false, IsRequired, new() ); }; [JsonIgnore, Hide] public override Color PrimaryColor => Color.Lerp( Theme.Green, Theme.Blue, 0.5f ); public SubgraphInput() { } private object GetOutputValue() { return InputType switch { InputType.Float => DefaultFloat, InputType.Float2 => DefaultFloat2, InputType.Float3 => DefaultFloat3, InputType.Color => DefaultColor, _ => DefaultFloat }; } public object GetValue() { return GetOutputValue(); } public List GetErrors() { var errors = new List(); if ( string.IsNullOrWhiteSpace( InputName ) ) { errors.Add( "Input name cannot be empty" ); } // Check for duplicate names in the same subgraph if ( Graph is ShaderGraph shaderGraph && shaderGraph.IsSubgraph ) { foreach ( var node in Graph.Nodes ) { if ( node == this ) continue; if ( node is SubgraphInput otherInput && otherInput.InputName == InputName ) { errors.Add( $"Duplicate input name \"{InputName}\"" ); break; } } } return errors; } } /// /// Available input types for subgraph inputs /// public enum InputType { [Title( "Float" ), Icon( "looks_one" )] Float, [Title( "Float2" ), Icon( "looks_two" )] Float2, [Title( "Float3" ), Icon( "looks_3" )] Float3, [Title( "Color" ), Icon( "palette" )] Color }