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; } }