Editor-side procedural geometry for a wall tool. Holds the plinth and opening-grid settings on ConnectedWallSettings/WallEditor, and AddPlinth builds the base band around each wall section, splitting it into height bands at opening sills and skipping the gaps where openings sit.
namespace AdvancedMappingTools;
public sealed partial class ConnectedWallSettings
{
[Property, Group( "Opening Grid" ), WideMode]
public bool OpeningGrid { get; set; }
[Property, Group( "Opening Grid" ), Range( 1, 16, slider: false ), WideMode]
public int OpeningRows { get; set; } = 2;
[Property, Group( "Opening Grid" ), Range( 8, 1024, slider: false ), WideMode]
public float OpeningRowSpacing { get; set; } = 128;
[Property, Group( "Opening Grid" ), WideMode]
public WallOpeningKind AutomaticOpeningKind { get; set; } = WallOpeningKind.Window;
[Property, Group( "Base" ), WideMode]
public bool Plinth { get; set; }
[Property, Group( "Base" ), Range( 1, 256, slider: false ), WideMode]
public float PlinthHeight { get; set; } = 12;
[Property, Group( "Base" ), Range( 0, 64, slider: false ), WideMode]
public float PlinthProjection { get; set; } = 2;
[Property, Group( "Materials" ), ResourceType( "vmat" ), WideMode]
public string PlinthMaterial { get; set; }
}
public sealed partial class WallEditor
{
[Property, Group( "Connection" ), WideMode]
public bool AdvancedCorners { get; set; }
[Property, Group( "Connection" ), WideMode]
public WallJoinStyle CornerProfile { get; set; } = WallJoinStyle.Miter;
[Property, Group( "Opening Grid" ), WideMode]
public bool OpeningGrid { get; set; }
[Property, Group( "Opening Grid" ), Range( 1, 32, slider: false ), WideMode]
public int OpeningColumns { get; set; } = 3;
[Property, Group( "Opening Grid" ), Range( 1, 16, slider: false ), WideMode]
public int OpeningRows { get; set; } = 2;
[Property, Group( "Opening Grid" ), Range( 8, 1024, slider: false ), WideMode]
public float OpeningRowSpacing { get; set; } = 128;
[Property, Group( "Opening Grid" ), WideMode]
public WallOpeningKind OpeningKind { get; set; } = WallOpeningKind.Window;
[Property, Group( "Opening Grid" ), Range( 4, 1024, slider: false ), WideMode]
public float GridOpeningWidth { get; set; } = 48;
[Property, Group( "Opening Grid" ), Range( 4, 1024, slider: false ), WideMode]
public float GridOpeningHeight { get; set; } = 64;
[Property, Group( "Opening Grid" ), Range( 0, 1024, slider: false ), WideMode]
public float GridSill { get; set; } = 32;
}
public sealed partial class ConnectedWallGenerator
{
void AddPlinth( PartPlan root, ConnectedWallSettings s, GenContext context, PathTrack track, List<WallSection> sections, float z0 )
{
var shape = new WallShape( s, track, Math.Clamp( s.PlinthProjection, 0, 64 ) );
var height = MathF.Min( Math.Clamp( s.PlinthHeight, 1, 256 ), s.Height );
for ( var sectionIndex = 0; sectionIndex < sections.Count; sectionIndex++ )
{
var section = sections[sectionIndex];
var openings = OpeningsIn( s, section ).Where( o => o.Sill < height ).ToArray();
var levels = new[]
{
0f,
height
}.Concat( openings.SelectMany( o => new[] { Math.Clamp( o.Sill, 0, height ), Math.Clamp( o.Sill + o.Height, 0, height ) } ) ).Distinct().OrderBy( v => v ).ToArray();
for ( var band = 0; band + 1 < levels.Length; band++ )
{
var low = levels[band];
var high = levels[band + 1];
var middle = (low + high) * .5f;
var cursor = section.From;
var piece = 0;
void Fill( float from, float to )
{
if ( to - from < .05f )
return;
foreach ( var(a, b)in shape.Pieces( from, to ) )
{
var id = $"plinth{sectionIndex}_{band}_{piece++}";
root.Add( context.Solid( id, "Wall Plinth", "WallPlinth", WallCategory, context.Material( s.PlinthMaterial ?? s.Material ), kit => kit.Prism( shape.Outline( new WallSection { From = section.From, To = section.To }, a, b ), z0 + low, z0 + high ) ) );
}
}
foreach ( var opening in openings.Where( o => o.Sill < middle && o.Sill + o.Height > middle ).OrderBy( o => o.Along ) )
{
// The same gap as the wall leaves, so the wall above the plinth meets it everywhere.
var from = Math.Clamp( MathF.Max( opening.Along - opening.Width * .5f, section.From + PierClearance ), section.From, section.To );
var to = Math.Clamp( MathF.Min( opening.Along + opening.Width * .5f, section.To - PierClearance ), section.From, section.To );
Fill( cursor, from );
cursor = MathF.Max( cursor, to );
}
Fill( cursor, section.To );
}
}
}
}