FunStuff/JumperHighlight.cs
using Sandbox;
using System;
using System.Linq;
/// <summary>
/// This should be added to a camera that you want to outline stuff
/// </summary>
[Title( "Jumper - Highlight" )]
[Category( "Post Processing" )]
[Icon( "lightbulb_outline" )]
public sealed class JumperHighlight : Component, Component.ExecuteInEditor
{
IDisposable renderHook;
protected override void OnEnabled()
{
renderHook?.Dispose();
var cc = GameObject.Components.Get<CameraComponent>(FindMode.InSelf);
// FIXME
// renderHook = cc.AddHookAfterTransparent( "Highlight", 1000, RenderEffect );
}
protected override void OnDisabled()
{
renderHook?.Dispose();
renderHook = null;
}
enum OutlinePass
{
Inside,
Outside,
}
RenderAttributes attributes = new RenderAttributes();
public void RenderEffect( SceneCamera camera )
{
var outlines = Scene.Components.GetAll<JumperHighlightOutline>();
if ( !outlines.Any() ) return;
// Copy the depth buffer once
Graphics.GrabFrameTexture( "ColorTexture" );
Graphics.GrabDepthTexture( "DepthTexture" );
// Generate a temporary render target to draw the stencil to, also so we don't clash with the main depth buffer
using var rt = RenderTarget.GetTemporary( 1, ImageFormat.None, ImageFormat.D24S8 );
Graphics.RenderTarget = rt;
Graphics.Clear( Color.Black, false, true, true );
// Draw the stencil first before drawing the outside outline
foreach ( var glow in outlines ) { DrawGlow( glow, OutlinePass.Inside ); }
foreach ( var glow in outlines ) { DrawGlow( glow, OutlinePass.Outside ); }
Graphics.RenderTarget = null;
}
private static void DrawGlow( JumperHighlightOutline glow, OutlinePass pass )
{
foreach ( var model in glow.Components.GetAll<SkinnedModelRenderer>( FindMode.EnabledInSelfAndDescendants ) )
{
var so = model.SceneObject;
if ( so is null ) continue;
var shapeMat = glow.Material ?? Material.FromShader( "postprocess/objecthighlight/objecthighlight.shader" );
// Inside glow and stencil
if ( pass == OutlinePass.Inside )
{
Graphics.Attributes.Set( "D_OUTLINE_PASS", (int)OutlinePass.Inside );
Graphics.Attributes.Set( "Color", glow.InsideColor );
Graphics.Attributes.Set( "ObscuredColor", glow.InsideObscuredColor );
Graphics.Render( so, material: shapeMat );
}
// Outside glow
if ( glow.Width > 0.0f && pass == OutlinePass.Outside && (glow.Color != Color.Transparent || glow.ObscuredColor != Color.Transparent) )
{
Graphics.Attributes.Set( "D_OUTLINE_PASS", (int)OutlinePass.Outside );
Graphics.Attributes.Set( "Color", glow.Color );
Graphics.Attributes.Set( "ObscuredColor", glow.ObscuredColor );
Graphics.Attributes.Set( "LineWidth", glow.Width );
Graphics.Render( so, material: shapeMat );
}
}
}
}
/// <summary>
/// This component should be added to stuff you want to be outlined
/// </summary>
[Title( "Jumper Highlight Outline" )]
[Category( "Renderering" )]
[Icon( "lightbulb_outline" )]
public class JumperHighlightOutline : Component
{
/// <summary>
/// If defined, the glow will use this material rather than a generated one.
/// </summary>
[Property] public Material Material { get; set; }
/// <summary>
/// The colour of the glow outline
/// </summary>
[Property] public Color Color { get; set; } = Color.White;
/// <summary>
/// The colour of the glow when the mesh is obscured by something closer.
/// </summary>
[Property] public Color ObscuredColor { get; set; } = Color.Black * 0.4f;
/// <summary>
/// Color of the inside of the glow
/// </summary>
[Property] public Color InsideColor { get; set; } = Color.Transparent;
/// <summary>
/// Color of the inside of the glow when the mesh is obscured by something closer.
/// </summary>
[Property] public Color InsideObscuredColor { get; set; } = Color.Transparent;
/// <summary>
/// The width of the line of the glow
/// </summary>
[Property] public float Width { get; set; } = 0.25f;
}