Editor utility for generating the positive geometry of a carved architectural cut. It builds enclosure walls, head (housing, coping or lid) and roof/lid geometry using provided mesh canvas, palette, kit and style, delegating to ArchCarveRing, ArchRun, ArchFloorGen and band/brush helpers.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
// The positive half: hole derived every rebuild, this builds the shaft walls and the housing.
public static class ArchCutGen
{
// Takes the HOST's palette, not the host: a chamber cut off a bore is skinned by the road that holds it, and
// a shaft by its building, through one call.
public static void Build( ArchMesh canvas, ArchCutPart cut, ArchPalette owner, ArchKit kit, ArchStyle style )
{
if ( cut is not { HasContent: true, Builds: true } )
{
return;
}
var chain = new[] { cut.Palette, owner ?? new ArchPalette() };
if ( cut.Enclosure == CutEnclosure.Walls )
{
using ( canvas.Part( ArchPieces.Enclosure ) )
{
Enclosure( canvas, cut, style.Brush( ArchSurface.WallExterior, chain ) );
}
}
if ( cut.Head != CutHead.None )
{
using ( canvas.Part( ArchPieces.Capping ) )
{
Head( canvas, cut, kit, style, chain );
}
}
}
// Per leg, not the chain - the legs climb, so the shaft is a stack following the stair.
static void Enclosure( ArchMesh canvas, ArchCutPart cut, ArchBrush brush )
{
var thickness = MathF.Max( 1f, cut.WallThickness );
foreach ( var leg in cut.Legs() )
{
ArchCarveRing.Walls( canvas, leg.Outline(), thickness, leg.BaseHeight, leg.TopHeight, brush );
}
}
// Last leg's footprint (it reached the roof), standing on that leg's head, not a typed height.
static void Head( ArchMesh canvas, ArchCutPart cut, ArchKit kit, ArchStyle style, ArchPalette[] chain )
{
if ( cut.Legs().LastOrDefault() is not { } top )
{
return;
}
var thickness = MathF.Max( 1f, cut.WallThickness );
var outline = top.Outline();
var foot = top.TopHeight;
if ( cut.Head == CutHead.Housing )
{
var head = foot + MathF.Max( 12f, cut.HeadHeight );
ArchCarveRing.Walls( canvas, outline, thickness, foot, head, style.Brush( ArchSurface.WallExterior, chain ) );
if ( cut.HeadRoof )
{
Lid( canvas, outline, thickness, head, MathF.Max( 1f, kit.FloorThickness ), style.Brush( ArchSurface.Roof, chain ) );
}
return;
}
if ( cut.Head == CutHead.Coping )
{
Coping( canvas, cut, top, thickness, style.Brush( ArchSurface.WallCap, chain ), kit );
return;
}
Lid( canvas, outline, thickness, foot, MathF.Max( 1f, kit.FloorThickness ), style.Brush( ArchSurface.Roof, chain ) );
}
// The mouth's own band, walked round the leg's loop rather than round a grown one, so it stands over the
// enclosure wall and oversails INTO the hole. Same section a platform's outside edge wears, bitten into the
// head it stands on for the same reason: sitting on it puts three faces on every edge.
static void Coping( ArchMesh canvas, ArchCutPart cut, ArchCutSegment top, float thickness, ArchBrush brush, ArchKit kit )
{
var head = top.TopHeight;
var band = ArchBandSection.Between(
-MathF.Max( 0f, cut.CopingOversail ),
thickness,
head - ArchContact.Bite( kit ),
head + MathF.Max( 1f, cut.CopingHeight ) );
ArchRun.Around( ArchFootprint.Wind( top.Outline() ) ).Level( head ).Emit( canvas, band, brush );
}
static void Lid( ArchMesh canvas, IReadOnlyList<Vector2> outline, float thickness, float bottom, float depth, ArchBrush brush )
{
var grown = ArchFootprint.Grow( new List<List<Vector2>> { outline.ToList() }, thickness ).First();
ArchFloorGen.Slab( canvas, grown, null, bottom, bottom + depth, brush );
}
}