Files
sbox-public/game/addons/tools/Code/ShaderGraph/Result.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

242 lines
7.8 KiB
C#

using Editor.NodeEditor;
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace Editor.ShaderGraph;
/// <summary>
/// Final result
/// </summary>
[Title( "Material" ), Icon( "tonality" )]
public sealed class Result : BaseResult
{
[Hide]
private bool IsLit => (Graph is ShaderGraph shaderGraph && shaderGraph.ShadingModel == ShadingModel.Lit && shaderGraph.Domain != ShaderDomain.PostProcess);
[Hide]
private bool IsPostProcess => (Graph is ShaderGraph shaderGraph && shaderGraph.Domain == ShaderDomain.PostProcess);
[Hide]
[Input( typeof( Vector3 ) )]
public NodeInput Albedo { get; set; }
[Hide]
[Input( typeof( Vector3 ) )]
[ShowIf( nameof( this.IsLit ), true )]
public NodeInput Emission { get; set; }
[Hide, Editor( nameof( DefaultOpacity ) )]
[Input( typeof( float ) )]
public NodeInput Opacity { get; set; }
[Hide]
[Input( typeof( Vector3 ) )]
[ShowIf( nameof( this.IsLit ), true )]
public NodeInput Normal { get; set; }
[Hide, Editor( nameof( DefaultRoughness ) )]
[Input( typeof( float ) )]
[ShowIf( nameof( this.IsLit ), true )]
public NodeInput Roughness { get; set; }
[Hide, Editor( nameof( DefaultMetalness ) )]
[Input( typeof( float ) )]
[ShowIf( nameof( this.IsLit ), true )]
public NodeInput Metalness { get; set; }
[Hide, Editor( nameof( DefaultAmbientOcclusion ) )]
[Input( typeof( float ) )]
[ShowIf( nameof( this.IsLit ), true )]
public NodeInput AmbientOcclusion { get; set; }
[InputDefault( nameof( Opacity ) )]
public float DefaultOpacity { get; set; } = 1.0f;
[InputDefault( nameof( Roughness ) )]
public float DefaultRoughness { get; set; } = 1.0f;
[InputDefault( nameof( Metalness ) )]
public float DefaultMetalness { get; set; } = 0.0f;
[InputDefault( nameof( AmbientOcclusion ) )]
public float DefaultAmbientOcclusion { get; set; } = 1.0f;
[Hide, JsonIgnore]
int _lastHashCode = 0;
public override void OnFrame()
{
var hashCode = new HashCode();
if ( Graph is ShaderGraph shaderGraph )
{
hashCode.Add( shaderGraph.ShadingModel );
hashCode.Add( shaderGraph.Domain );
}
var hc = hashCode.ToHashCode();
if ( hc != _lastHashCode )
{
_lastHashCode = hc;
CreateInputs();
Update();
}
}
[Hide]
[Input( typeof( Vector3 ) )]
[HideIf( nameof( IsPostProcess ), true )]
public NodeInput PositionOffset { get; set; }
[JsonIgnore, Hide]
public override Color PrimaryColor => Color.Lerp( Theme.Blue, Color.White, 0.25f );
public override NodeInput GetAlbedo() => Albedo;
public override NodeInput GetEmission() => Emission;
public override NodeInput GetOpacity() => Opacity;
public override NodeInput GetNormal() => Normal;
public override NodeInput GetRoughness() => Roughness;
public override NodeInput GetMetalness() => Metalness;
public override NodeInput GetAmbientOcclusion() => AmbientOcclusion;
public override NodeInput GetPositionOffset() => PositionOffset;
public override float GetDefaultOpacity() => DefaultOpacity;
private void CreateInputs()
{
var plugs = new List<IPlugIn>();
var serialized = this.GetSerialized();
foreach ( var property in serialized )
{
if ( property.TryGetAttribute<InputAttribute>( out var inputAttr ) )
{
if ( property.TryGetAttribute<ConditionalVisibilityAttribute>( out var conditionalVisibilityAttr ) )
{
if ( conditionalVisibilityAttr.TestCondition( this.GetSerialized() ) )
{
continue;
}
}
var propertyInfo = typeof( Result ).GetProperty( property.Name );
if ( propertyInfo is null ) continue;
var info = new PlugInfo( propertyInfo );
var displayInfo = info.DisplayInfo;
displayInfo.Name = property.DisplayName;
info.DisplayInfo = displayInfo;
var plug = new BasePlugIn( this, info, info.Type );
var oldPlug = Inputs.FirstOrDefault( x => x is BasePlugIn plugIn && plugIn.Info.DisplayInfo.Name == property.Name ) as BasePlugIn;
if ( oldPlug is not null )
{
oldPlug.Info.Name = info.Name;
oldPlug.Info.Type = info.Type;
oldPlug.Info.DisplayInfo = info.DisplayInfo;
var nodeInput = property.GetValue<NodeInput>();
if ( nodeInput.IsValid && plug is IPlugIn plugIn )
{
var connectedNode = Graph.Nodes.FirstOrDefault( x => x is BaseNode node && node.Identifier == nodeInput.Identifier ) as BaseNode;
plugIn.ConnectedOutput = connectedNode.Outputs.FirstOrDefault( x => x.Identifier == nodeInput.Output );
}
plugs.Add( oldPlug );
}
else
{
var nodeInput = property.GetValue<NodeInput>();
if ( nodeInput.IsValid && plug is IPlugIn plugIn )
{
var connectedNode = Graph.Nodes.FirstOrDefault( x => x is BaseNode node && node.Identifier == nodeInput.Identifier ) as BaseNode;
plugIn.ConnectedOutput = connectedNode.Outputs.FirstOrDefault( x => x.Identifier == nodeInput.Output );
}
plugs.Add( plug );
}
}
}
Inputs = plugs;
}
}
public abstract class BaseResult : ShaderNode
{
[JsonIgnore, Hide, Browsable( false )]
public override bool CanRemove => Graph.Nodes.Count( x => x is BaseResult ) > 1;
public virtual NodeInput GetAlbedo() => new();
public virtual NodeInput GetEmission() => new();
public virtual NodeInput GetOpacity() => new();
public virtual NodeInput GetNormal() => new();
public virtual NodeInput GetRoughness() => new();
public virtual NodeInput GetMetalness() => new();
public virtual NodeInput GetAmbientOcclusion() => new();
public virtual NodeInput GetPositionOffset() => new();
public virtual Color GetDefaultAlbedo() => Color.White;
public virtual Color GetDefaultEmission() => Color.Black;
public virtual float GetDefaultOpacity() => 1.0f;
public virtual Vector3 GetDefaultNormal() => new( 0, 0, 0 );
public virtual float GetDefaultRoughness() => 1.0f;
public virtual float GetDefaultMetalness() => 0.0f;
public virtual float GetDefaultAmbientOcclusion() => 1.0f;
public virtual Vector3 GetDefaultPositionOffset() => new( 0, 0, 0 );
public NodeResult GetAlbedoResult( GraphCompiler compiler )
{
var albedoInput = GetAlbedo();
if ( albedoInput.IsValid )
return compiler.ResultValue( albedoInput );
return compiler.ResultValue( GetDefaultAlbedo() );
}
public NodeResult GetEmissionResult( GraphCompiler compiler )
{
var emissionInput = GetEmission();
if ( emissionInput.IsValid )
return compiler.ResultValue( emissionInput );
return compiler.ResultValue( GetDefaultEmission() );
}
public NodeResult GetOpacityResult( GraphCompiler compiler )
{
var opacityInput = GetOpacity();
if ( opacityInput.IsValid )
return compiler.ResultValue( opacityInput );
return compiler.ResultValue( GetDefaultOpacity() );
}
public NodeResult GetNormalResult( GraphCompiler compiler )
{
var normalInput = GetNormal();
if ( normalInput.IsValid )
return compiler.ResultValue( normalInput );
return compiler.ResultValue( GetDefaultNormal() );
}
public NodeResult GetRoughnessResult( GraphCompiler compiler )
{
var roughnessInput = GetRoughness();
if ( roughnessInput.IsValid )
return compiler.ResultValue( roughnessInput );
return compiler.ResultValue( GetDefaultRoughness() );
}
public NodeResult GetMetalnessResult( GraphCompiler compiler )
{
var metalnessInput = GetMetalness();
if ( metalnessInput.IsValid )
return compiler.ResultValue( metalnessInput );
return compiler.ResultValue( GetDefaultMetalness() );
}
public NodeResult GetAmbientOcclusionResult( GraphCompiler compiler )
{
var ambientOcclusionInput = GetAmbientOcclusion();
if ( ambientOcclusionInput.IsValid )
return compiler.ResultValue( ambientOcclusionInput );
return compiler.ResultValue( GetDefaultAmbientOcclusion() );
}
public NodeResult GetPositionOffsetResult( GraphCompiler compiler )
{
var positionOffsetInput = GetPositionOffset();
if ( positionOffsetInput.IsValid )
return compiler.ResultValue( positionOffsetInput );
return compiler.ResultValue( GetDefaultPositionOffset() );
}
}