Editor/ShaderGraphPlus/Nodes/SceneColor.cs
using Sandbox.Rendering;

namespace ShaderGraphPlus.Nodes;

/// <summary>
/// Color of the scene.
/// </summary>
[Title( "Scene Color" ), Category( "Variables" ), Icon( "palette" )]
public sealed class SceneColorNode : ShaderNodePlus
{
	[JsonIgnore, Hide, Browsable( false )]
	public override Color NodeTitleColor => ShaderGraphPlusTheme.NodeHeaderColors.FunctionNode;

	[Hide]
	public string MapSceneColorCoords => @"
float2 MapSceneColorCoords( float2 vInput, float2 vModes )
{
	float2 result;

	// X
	if ( vModes.x == 1 ) // Mirror
	{
		float xx = abs( vInput.x );
		result.x = ( fmod( floor( xx ), 2.0f ) == 0.0f ) ? frac( xx ) : 1.0f - frac( xx );
	}
	else if ( vModes.x == 2 ) // Clamp
	{
		result.x = clamp( vInput.x, 0.0f, 1.0f );
	}
	else if ( vModes.x == 3 ) // Border
	{
		result.x = ( vInput.x < 0.0f || vInput.x > 1.0f ) ? 0.5f : vInput.x;
	}
	else if ( vModes.x == 4 ) // MirrorOnce
	{
		float xx = abs( vInput.x );
		float floorX = floor( xx );
		
		if ( floorX < 1.0f )
		{
			result.x = frac( xx );
		}
		else if ( floorX < 2.0f )
		{
			result.x = 1.0f - frac( xx );
		}
		else
		{
			result.x = vInput.x;
		}
	}
	else // Wrap by default
	{
		result.x = vInput.x;
	}

	// Y
	if ( vModes.y == 1 ) // Mirror
	{
		float yy = abs( vInput.y );
		result.y = ( fmod( floor( yy ), 2.0f ) == 0.0f ) ? frac( yy ) : 1.0f - frac( yy );
	}
	else if ( vModes.y == 2 ) // Clamp
	{
		result.y = clamp( vInput.y, 0.0f, 1.0f );
	}
	else if ( vModes.y == 3 ) // Border
	{
		result.y = ( vInput.y < 0.0f || vInput.y > 1.0f ) ? 0.5f : vInput.y;
	}
	else if ( vModes.y == 4 ) // MirrorOnce
	{
		float yy = abs( vInput.y );
		float floorY = floor( yy );

		if ( floorY < 1.0f )
		{
			result.y = frac( yy );
		}
		else if ( floorY < 2.0f )
		{
			result.y = 1.0f - frac( yy );
		}
		else
		{
			result.y = vInput.y;
		}
	}
	else // Wrap by default
	{
		result.y = vInput.y;
	}

	return result;
}
";

	[Input( typeof( Vector2 ) )]
	[Hide]
	public NodeInput Coords { get; set; }

	public TextureAddressMode AddressU { get; set; } = TextureAddressMode.Wrap;
	public TextureAddressMode AddressV { get; set; } = TextureAddressMode.Wrap;

	[Output( typeof( Vector3 ) )]
	[Hide]
	public NodeResult.Func SceneColor => ( GraphCompiler compiler ) =>
	{
		var inputCoords = compiler.Result( Coords );
		var graph = compiler.Graph;

		if ( graph.Domain != ShaderDomain.PostProcess && graph.BlendMode != BlendMode.Translucent )
		{
			return NodeResult.Error( $"Graph \"{nameof( BlendMode )}\" must be set to \"{nameof( BlendMode.Translucent )}\" in order to use \"{DisplayInfo.Name}\"" );
		}

		var uvModes = $"float2( {(int)AddressU}, {(int)AddressV} )";
		var func = compiler.RegisterHLSLFunction( MapSceneColorCoords, "MapSceneColorCoords" );

		var coords = inputCoords.IsValid ? compiler.ResultHLSLFunction( func, inputCoords.Cast( 2 ), uvModes ) : $"CalculateViewportUv( {compiler.ResultHLSLFunction( func, "i.vPositionSs.xy", uvModes )} )";

		if ( graph.Domain is ShaderDomain.PostProcess )
		{
			return new NodeResult( ResultType.Vector3, $"g_tColorBuffer.Sample( g_sAniso, {coords} ).rgb" );
		}

		compiler.RegisterGlobal( "bWantsFBCopyTexture", "BoolAttribute( bWantsFBCopyTexture, true );" );
		compiler.RegisterGlobal( "g_tFrameBufferCopyTexture", "Texture2D g_tFrameBufferCopyTexture < Attribute( \"FrameBufferCopyTexture\"); SrgbRead( false ); >;" );

		return new NodeResult( ResultType.Vector3, $"g_tFrameBufferCopyTexture.Sample( g_sAniso, {coords}{(compiler.IsPreview ? " * g_vFrameBufferCopyInvSizeAndUvScale.zw" : "")} ).rgb" );
	};
}

[Title( "Frame Buffer Copy Inv Size And Uv Scale" ), Category( "Variables" )]
public sealed class FrameBufferCopyInvSizeAndUvScaleNode : ShaderNodePlus
{
	[JsonIgnore, Hide, Browsable( false )]
	public override Color NodeTitleColor => Color.Parse( "#803334" )!.Value;

	[Output( typeof( Vector2 ) )]
	[Hide]
	public NodeResult.Func Result => ( GraphCompiler compiler ) =>
	{
		return new NodeResult( ResultType.Vector2, $"g_vFrameBufferCopyInvSizeAndUvScale.zw", true );
	};
}