Editor-side utility for locating architectural scene objects. It computes a focused context of rooms, walls, roofs and collects matching GameObject nodes from the scene based on an ArchTarget, level and toolkit, then assembles a resulting ArchTarget view.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
public static partial class ArchLocate
{
static ArchTarget Focus( ArchTool tool, ArchTarget target, int? requestedLevel )
{
if ( target.Kind == "none" )
{
return target;
}
var level = requestedLevel ?? target.Level ?? tool.Level;
var rooms = tool.Plan.Buildings
.SelectMany( building => building.Rooms )
.Where( room => room.Floor == level )
.ToList();
var context = ContextRooms( target, rooms, tool.Kit );
var objects = target.Objects.ToList();
var planned = new ArchExtent();
if ( target.Planned.Size.Length > 0.01f )
{
planned.Add( target.Planned.Mins );
planned.Add( target.Planned.Maxs );
}
var focusedRooms = target.Room is null
? context.Where( ArchAsks.IsWalkway ).ToList()
: new List<ArchRoom> { target.Room };
foreach ( var room in focusedRooms )
{
Collect( tool, ArchNames.Room( room ), objects );
planned.Add( RoomExtent( room, tool.Kit ) );
}
foreach ( var room in context.Where( room => !focusedRooms.Contains( room ) ) )
{
foreach ( var wall in room.Walls.Where( wall => focusedRooms.Any( focused => Near( WallExtent( wall, room, tool.Kit ).Box, RoomExtent( focused, tool.Kit ).Box, ContextMargin( tool.Kit ) ) ) ) )
{
Collect( tool, ArchNames.Wall( wall ), objects );
planned.Add( WallExtent( wall, room, tool.Kit ) );
}
}
var style = new ArchStyle( tool.Kit );
foreach ( var building in tool.Plan.Buildings )
{
foreach ( var storey in ArchAsks.Storeys( building, tool.Kit, style )
.Where( storey => storey.Level == level && storey.Rooms.Any( context.Contains ) ) )
{
Collect( tool, building, storey.Name, objects );
}
}
foreach ( var building in tool.Plan.Buildings )
{
foreach ( var roof in building.Roofs.Where( roof => roof.Level == level ) )
{
if ( (target.Kind is "walkway" or "connection") && !roof.Name.StartsWith( "Walkway", StringComparison.OrdinalIgnoreCase ) )
{
continue;
}
if ( focusedRooms.Any( room => Near( RoomExtent( room, tool.Kit ).Box, RoofExtent( roof ).Box, ContextMargin( tool.Kit ) ) ) )
{
Collect( tool, ArchNames.Roof( roof ), objects );
planned.Add( RoofExtent( roof ) );
}
}
}
return Assemble( target.Describe, target.Kind, target.Item, target.Room, target.Building,
objects, planned, target.Yaw, true, level );
}
static List<ArchRoom> ContextRooms( ArchTarget target, List<ArchRoom> rooms, ArchKit kit )
{
if ( target.Room is null )
{
if ( target.Kind is "walkway" or "connection" )
{
var walkways = rooms.Where( ArchAsks.IsWalkway ).ToList();
return rooms
.Where( room => ArchAsks.IsWalkway( room ) || walkways.Any( walkway => Near( RoomExtent( walkway, kit ).Box, RoomExtent( room, kit ).Box, ContextMargin( kit ) ) ) )
.ToList();
}
return rooms;
}
var anchor = RoomExtent( target.Room, kit ).Box;
var margin = ContextMargin( kit );
return rooms
.Where( room => ReferenceEquals( room, target.Room ) || Near( anchor, RoomExtent( room, kit ).Box, margin ) )
.ToList();
}
static float ContextMargin( ArchKit kit ) => MathF.Max( 24f, kit.WallThickness * 2f );
static bool Near( BBox first, BBox second, float margin )
{
return first.Mins.x <= second.Maxs.x + margin
&& first.Maxs.x + margin >= second.Mins.x
&& first.Mins.y <= second.Maxs.y + margin
&& first.Maxs.y + margin >= second.Mins.y;
}
// By name each time: the scene is rebuilt wholesale, so any cache is stale.
static void Collect( ArchTool tool, string name, List<GameObject> into )
{
foreach ( var root in ArchScene.FindRoots( tool.Scene ) )
{
foreach ( var node in ArchScene.Descendants( root ) )
{
if ( node.Name == name && !into.Contains( node ) )
{
into.Add( node );
}
}
}
}
static void Collect( ArchTool tool, ArchBuilding building, string name, List<GameObject> into )
{
foreach ( var root in ArchScene.FindRoots( tool.Scene ) )
{
var owner = ArchScene.Descendants( root ).FirstOrDefault( node => node.Name == ArchNames.Building( building ) );
if ( owner is null )
{
continue;
}
foreach ( var node in ArchScene.Descendants( owner ) )
{
if ( node.Name == name && !into.Contains( node ) )
{
into.Add( node );
}
}
}
}
}