Editor utility that scans a scene and builds a tree of constructed architecture pieces. It finds root GameObjects, walks their children, groups built pieces by owner id, records piece name, path, owner, the GameObject, whether it has a mesh, and nested children.
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
// One named piece as it actually came out - the object it became, whether it renders anything of its own, and
// the pieces nested inside it.
public sealed class ArchBuiltPiece
{
public string Name { get; init; }
// The path under its layer: "Gutters", "Gutters/Fascia".
public string Piece { get; init; }
public int Owner { get; init; }
public GameObject Object { get; init; }
public bool Renders { get; init; }
public List<ArchBuiltPiece> Children { get; } = new();
public string Key => ArchParts.Key( Owner, Piece );
}
// WHAT EACH LAYER BUILT, as a tree - read off the scene rather than declared per kind, because a table of
// "a roof has a fascia" is a second opinion the generators are free to drift from. A generated name carrying
// a plan id starts a new layer; anything else is a named piece of the layer above it.
public static class ArchBuiltPieces
{
public static Dictionary<int, List<ArchBuiltPiece>> Index( Scene scene )
{
var index = new Dictionary<int, List<ArchBuiltPiece>>();
foreach ( var root in ArchScene.FindRoots( scene ) )
{
foreach ( var child in root.Children )
{
Walk( index, child, 0, "" );
}
}
return index;
}
static ArchBuiltPiece Walk( Dictionary<int, List<ArchBuiltPiece>> index, GameObject node, int owner, string under )
{
if ( ArchNames.TrySourceId( node.Name, out var id ) )
{
foreach ( var child in node.Children )
{
if ( Walk( index, child, id, "" ) is { } piece )
{
Filed( index, id ).Add( piece );
}
}
return null;
}
var named = ArchParts.Joined( under, node.Name );
var built = new ArchBuiltPiece
{
Name = node.Name,
Piece = named,
Owner = owner,
Object = node,
Renders = node.Components.Get<MeshComponent>( FindMode.EverythingInSelf ) is { Mesh: not null }
};
foreach ( var child in node.Children )
{
if ( Walk( index, child, owner, named ) is { } nested )
{
built.Children.Add( nested );
}
}
// Nothing generated stands outside a building or a road, so a piece with no layer over it is a node
// somebody added by hand and none of the stack's business.
return owner == 0 ? null : built;
}
static List<ArchBuiltPiece> Filed( Dictionary<int, List<ArchBuiltPiece>> index, int owner )
{
if ( index.TryGetValue( owner, out var held ) )
{
return held;
}
return index[owner] = new List<ArchBuiltPiece>();
}
}