using Sandbox.MovieMaker;
using Sandbox.Rendering;
namespace Sandbox;
///
/// The base class for all post process effects.
///
public abstract class BasePostProcess : Component, Component.ExecuteInEditor, Component.DontExecuteOnServer
{
PostProcessContext? _currentContext;
internal PostProcessContext context
{
get
{
if ( !_currentContext.HasValue ) throw new System.Exception( "Should only be called during build" );
return _currentContext.Value;
}
}
///
/// The camera we're being applied to. This is only valid during the Render call.
///
protected CameraComponent Camera => context.Camera;
///
/// The default attributes for this post process. This will be used by helper functions like Blit.
///
protected readonly RenderAttributes Attributes = new();
internal void Build( PostProcessContext ctx )
{
_currentContext = ctx;
try
{
// always cleared before build
Attributes.Clear();
Render();
}
finally
{
_currentContext = default;
}
}
///
/// Override in your implementation to do your rendering
///
public abstract void Render();
public ref struct BlitMode
{
///
/// The material to use for the blit.
///
public Material Material;
///
/// We'll use this instead of BasePostProcess.Attributes if set.
///
public RenderAttributes Attributes;
///
/// Where to place this in the render pipeline
///
public Stage RenderStage;
///
/// The order within the stage. Lower numbers get rendered first.
///
public int Order;
///
/// If true, the backbuffer will be copied to a texture called "ColorBuffer" before the blit.
///
public bool WantsBackbuffer;
///
/// If both WantsBackbufferCopy and this is true the backbuffer will be mipped after being copied.
///
public bool WantsBackbufferMips;
///
/// Shortcut to build a simple blit mode
///
public static BlitMode Simple( Material m, Stage stage, int order = 0 ) => new BlitMode { Material = m, RenderStage = stage, Order = order };
///
/// Shortcut to build a blit mode that copies the backbuffer first
///
public static BlitMode WithBackbuffer( Material m, Stage stage, int order = 0, bool mip = false ) => new BlitMode { Material = m, RenderStage = stage, Order = order, WantsBackbuffer = true, WantsBackbufferMips = true };
}
///
/// Helper to do a blit with the current camera's post process
///
protected void Blit( BlitMode blit, string debugName )
{
if ( !blit.Material.IsValid() ) return;
CommandList cl = new CommandList( blit.Material.Name );
if ( blit.WantsBackbuffer )
{
cl.Attributes.GrabFrameTexture( "ColorBuffer", blit.WantsBackbufferMips );
}
cl.Blit( blit.Material, blit.Attributes ?? Attributes );
InsertCommandList( cl, blit.RenderStage, blit.Order, debugName );
}
///
/// Helper to do a blit with the current camera's post process
///
protected void BlitSimple( Material shader, Stage stage, int order, string debugName )
{
Blit( BlitMode.Simple( shader, stage, order ), debugName );
}
///
/// Helper to add a command list to the current camera's post process
///
protected void InsertCommandList( CommandList cl, Sandbox.Rendering.Stage stage, int order, string debugName )
{
var layer = context.Camera.PostProcess.CreateLayer( stage );
layer.Order = order;
layer.CommandList = cl;
layer.Name = debugName;
}
}
///
/// Like BasePostProcess but enables access to helper methods for accessing from multiple instances using GetWeighted.
///
public abstract class BasePostProcess : BasePostProcess where T : BasePostProcess
{
///
/// Helper to get a weighted value from all active post process volumes
///
protected U GetWeighted( System.Func value, U defaultVal = default, bool onlyLerpBetweenVolumes = false )
{
U v = defaultVal;
var lerper = Interpolator.GetDefault();
int i = 0;
foreach ( var e in context.Components )
{
var target = value( (T)e.Effect );
v = lerper.Interpolate( v, target, e.Weight );
if ( onlyLerpBetweenVolumes && i == 0 )
v = target;
i++;
}
return v;
}
}