This is an editor script that defines the custom inspector for the FlatpackRenderer component. It provides UI controls to bake models into impostors and displays relevant LOD metadata.
using System;
using System.Linq;
using Editor;
using Sandbox;
namespace Flatpack.EditorTools;
/// <summary>
/// Inspector for <see cref="FlatpackRenderer"/>: the usual properties plus a bake button, so giving one model a
/// Flatpack LOD is "add component, press Bake".
/// </summary>
[CustomEditor( typeof( FlatpackRenderer ) )]
public sealed class FlatpackRendererEditor : ComponentEditorWidget
{
public FlatpackRendererEditor( SerializedObject obj ) : base( obj )
{
SetSizeMode( SizeMode.Default, SizeMode.Default );
Layout = Layout.Column();
var sheet = new ControlSheet();
// Only the component's [Property] members; the default sheet hides transform and internals the same way.
sheet.AddObject( obj, p => p.HasAttribute<PropertyAttribute>() );
Layout.Add( sheet );
var renderers = obj.Targets.OfType<FlatpackRenderer>().ToArray();
var baked = renderers.All( x => x.Impostor is not null );
// What the bake is, how close it holds up, and whether the swap distance is inside that.
if ( renderers.Length == 1 && renderers[0].Impostor is { } impostor )
{
var r = renderers[0];
// Radii are the bake's own bounding radius times this object's scale, so metres follow the object's real size.
var scale = r.WorldScale;
var radius = impostor.Radius * MathF.Max( scale.x, MathF.Max( scale.y, scale.z ) );
var swapRadii = radius > 0 ? r.EffectiveSwapDistance / radius : 0;
var holds = impostor.HoldsUpFrom switch
{
> 0 => $"matches the model from {impostor.HoldsUpFrom:0.#} radii ({FlatpackMath.FormatMetres( impostor.HoldsUpFrom * radius )})",
< 0 => $"never matches the model closely inside 16 radii ({FlatpackMath.FormatMetres( 16.0f * radius )})",
_ => "not measured (re-bake to measure)",
};
var level = string.IsNullOrEmpty( impostor.QualityLevel ) ? "" : $"{impostor.QualityLevel} bake, ";
var info = Layout.Add( new Label( $"{level}{holds}. Swaps at {swapRadii:0.#} radii ({FlatpackMath.FormatMetres( r.EffectiveSwapDistance )})." ) { WordWrap = true } );
info.ContentMargins = new Sandbox.UI.Margin( 8, 4, 8, 0 );
if ( impostor.HoldsUpFrom < 0 )
info.SetStyles( "color: #e0a030;" );
}
var row = Layout.AddRow();
row.Margin = new Sandbox.UI.Margin( 8, 4, 8, 8 );
row.Spacing = 4;
row.Add( new Button( baked ? "Re-bake Impostor" : "Bake Impostor", "view_in_ar" )
{
ToolTip = "Bake this object's model with the Impostor Baker's current settings and assign the result.",
Clicked = () =>
{
foreach ( var renderer in renderers )
{
var source = renderer.Source ?? renderer.GameObject.Components.Get<ModelRenderer>();
if ( source.IsValid() )
FlatpackMenus.BakeAndAttach( source );
}
}
} );
row.Add( new Button( "Bake Settings", "tune" )
{
ToolTip = "Open the Impostor Baker window to change grid, atlas size and layout.",
Clicked = () => FlatpackBakeWindow.Open()
} );
row.AddStretchCell();
}
}