Editor/Porch/ArchPorchCanopy.cs
using System;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
// Both run across the porch, so cut from the wall edges, not the open perimeter.
public static partial class ArchPorchGen {
static void Canopy( ArchMesh canvas, ArchPorchShape shape, ArchPorchPart porch, ArchBuilding building, ArchKit kit, ArchBrush brush ) {
var cover = building?.Roofs.FirstOrDefault( roof => roof.Id == porch.RoofId );
if ( cover is null ) {
return;
}
var slope = MathF.Tan( MathF.Max( 1f, cover.Pitch ).DegreeToRadian() );
var beam = MathF.Max( 3f, kit.PorchPostSize );
foreach ( var wall in shape.Walls ) {
var reach = wall.Depth - beam;
if ( reach < 12f ) {
continue;
}
if ( porch.Rafters ) {
Rafters( canvas, shape, wall, kit, cover, slope, reach, brush );
}
if ( porch.Braces ) {
Braces( canvas, shape, porch, wall, kit, reach, brush );
}
}
}
// Hangs from the deck's underside - one slab, no framing cavity to sit in.
static void Rafters( ArchMesh canvas, ArchPorchShape shape, ArchPorchShape.ArchPorchWall wall, ArchKit kit, ArchRoofPart cover, float slope, float reach, ArchBrush brush ) {
var bite = ArchLap.Bite( kit );
var depth = MathF.Max( 2f, kit.PorchRafterDepth );
var half = MathF.Max( 0.5f, kit.PorchRafterWidth ) * 0.5f;
var span = (wall.To - wall.From).Length;
if ( span < 1f ) {
return;
}
var along = (wall.To - wall.From) / span;
var inward = -wall.Outward;
var eave = shape.Ceiling + slope * MathF.Max( 0f, cover.Overhang );
foreach ( var station in ArchDivide.AtMost( span, MathF.Max( 12f, kit.PorchRafterSpacing ) ).Nodes ) {
var foot = wall.From + along * station;
var head = foot + inward * (reach + bite);
var high = eave + slope * (reach + bite);
canvas.Rake( foot - inward * bite, head, -half, half, high - depth, eave - depth, high, eave, brush );
}
}
// A prop, not a knee brace - its foot is clamped to the deck.
static void Braces( ArchMesh canvas, ArchPorchShape shape, ArchPorchPart porch, ArchPorchShape.ArchPorchWall wall, ArchKit kit, float reach, ArchBrush brush ) {
var half = MathF.Max( 1f, kit.PorchBraceSize ) * 0.5f;
var span = (wall.To - wall.From).Length;
if ( span < 1f ) {
return;
}
var along = (wall.To - wall.From) / span;
var inward = -wall.Outward;
var drop = reach * MathF.Max( 0.2f, kit.PorchBraceRake );
var foot = MathF.Max( shape.Grade + half * 2f, shape.Head - drop );
foreach ( var station in ArchDivide.AtMost( span, MathF.Max( 24f, porch.PostSpacing ) ).Nodes ) {
var at = wall.From + along * station;
var head = at + inward * reach;
canvas.Strut(
new Vector3( at.x, at.y, foot ),
new Vector3( head.x, head.y, shape.Head ),
half, brush );
}
}
}