Code/Rendering/RenderingRoot.razor.cs
using Sandbox;
using Sandbox.UI;
using System;
using static Sandbox.ScreenPanel;
namespace PostprocessPanel;
/// <summary>
/// Handles the rendering of the main panel and storing the result
/// in a texture.
/// This should not be used (I wasn't able to make it internal)!
/// </summary>
public sealed partial class RenderingRoot : RootPanel
{
public float ManualOpacity { get; set; } = 1f;
public float ManualScale { get; set; } = 1f;
public bool AutoScreenScale { get; set; } = true;
public AutoScale ScaleStrategy { get; set; }
public RenderAttributes Attributes;
public Action OnRendering { get; set; }
private Vector2Int _texturePadding;
public Vector2Int TexturePadding
{
get => _texturePadding;
set
{
if ( TexturePadding == value )
return;
_texturePadding = value;
if ( value.x < 0 )
{
Log.Warning( $"Texture padding ({value}) x size is below 0! Setting it to 0." );
_texturePadding.x = 0;
}
if ( value.y < 0 )
{
Log.Warning( $"Texture padding ({value}) y size is below 0! Setting it to 0." );
_texturePadding.y = 0;
}
}
}
public string ProcessedTextureName { get; set; } = "ProcessedTexture";
public Vector2Int TextureSize { get; private set; }
public bool HasBodyPanel => BodyPanel is not null;
internal bool HasRenderingCallback { get; set; } = false;
internal Panel BodyPanel => GetChild( 0 );
internal Vector2 ScaledTexturePadding => (Vector2)TexturePadding / PanelUtils.GetScale( this, Scene.Camera.ScreenRect );
internal SceneCustomObject Renderer;
private RenderFragment _body;
public RenderingRoot( RenderFragment body, Scene scene )
{
_body = body;
Scene = scene;
SetupForRendering();
}
public RenderingRoot( Scene scene )
{
_body = ChildContent;
Scene = scene;
SetupForRendering();
}
// from GameRootPanel
protected override void UpdateScale( Rect screenSize )
{
base.Scale = PanelUtils.GetScale( this, screenSize );
}
public override void OnDeleted()
{
Renderer?.Delete();
Attributes?.Clear();
OnRendering = null;
}
// for some reason, the panel does not get deleted if this is present
//public override int GetHashCode() => HashCode.Combine( TextureSize, HasRenderingCallback );
/// <summary>
/// Applies the pseudo classes to the body panel
/// </summary>
/// <param name="classes"></param>
public void CopyPseudoClasses( PseudoClass classes )
{
if ( HasBodyPanel is false )
{
Log.Error( "No body panel as child!" );
return;
}
BodyPanel.PseudoClass = classes;
}
private void SetupForRendering()
{
Attributes = new();
RenderedManually = true;
Renderer = new( Scene.SceneWorld );
Renderer.Batchable = false;
Renderer.RenderLayer = SceneRenderLayer.OverlayWithoutDepth;
Renderer.RenderOverride = OnRender;
}
private void UpdatePadding()
{
Style.PaddingLeft = ScaledTexturePadding.x * 0.5f;
Style.PaddingTop = ScaledTexturePadding.y * 0.5f;
}
private void OnRender( SceneObject obj )
{
if ( HasBodyPanel is false )
return;
UpdatePadding();
Attributes.Clear();
Vector2 panelSize = BodyPanel.Box.Rect.Size;
TextureSize = new( panelSize.x.FloorToInt(), panelSize.y.FloorToInt() );
TextureSize += TexturePadding;
RenderTarget target = RenderTarget.GetTemporary( TextureSize.x, TextureSize.y, ImageFormat.RGBA8888, ImageFormat.None );
Graphics.RenderTarget = target;
Graphics.Clear( Color.Transparent );
RenderManual( ManualOpacity );
Texture processedTexture = Texture.Create( TextureSize.x, TextureSize.y, ImageFormat.RGBA8888 )
.WithUAVBinding()
.Finish();
Attributes.Set( "RawTexture", target.ColorTarget );
Attributes.Set( ProcessedTextureName, processedTexture );
if ( HasRenderingCallback is false )
Attributes.Set( ProcessedTextureName, target.ColorTarget );
Graphics.RenderTarget = null;
target.Dispose();
OnRendering?.InvokeWithWarning();
}
}