Editor utility that generates tunnel geometry parts for an architectural system. It builds lining, invert (bore fill), and walkway meshes by sweeping profiles along frames using brushes from a style and kit parameters.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
// The road runs through on its own strips - a tunnel is never a different width, camber or material.
public static class ArchTunnelGen
{
// The sweep bridges its own two skins round whatever a cut took out, so an opening shows the lining's thickness
// on its own. A cut carrying walls then stands its chamber a bite inside that mouth, exactly as a shaft does.
public static void Lining( ArchMesh canvas, ArchTunnelShape shape, ArchStyle style )
{
var brush = style.Brush( ArchSurface.Lining, shape.Part.Palette, shape.Road.Palette );
ArchMeshSweep.Tube( canvas, shape.Inner, shape.Outer, shape.Frames, brush, brush,
shape.Carved.Count == 0 ? null : ( row, column ) => shape.Carved.Contains( (row, column) ) );
}
// The bore took the ground under the carriageway - without this the road hangs over a hole in the terrain.
public static void Invert( ArchMesh canvas, ArchTunnelShape shape, ArchKit kit, ArchStyle style )
{
var part = shape.Part;
var brush = style.Brush( ArchSurface.Lining, part.Palette, shape.Road.Palette );
var bite = ArchContact.Bite( kit );
// Stopped a bite inside the lining, or its top and the lining's foot plane z-fight down the bore.
var reach = shape.Section.Half + bite;
var rows = new List<Vector3[]>();
foreach ( var frame in shape.Frames )
{
var top = frame.Position.z + shape.Section.Floor + bite;
rows.Add( new[] { frame.Side( -reach ).WithZ( top ), frame.Side( reach ).WithZ( top ) } );
}
ArchMeshSweep.Solid( canvas, rows, shape.Frames, MathF.Max( 1f, part.Invert ) + bite, new[] { brush }, brush, false,
carves: shape.Cuts );
}
// Seats on whatever the verge is doing - a kerbed pavement gets the walkway on top of it.
public static void Walkway( ArchMesh canvas, ArchTunnelShape shape, bool right, ArchKit kit, ArchStyle style )
{
var road = shape.Road;
var part = shape.Part;
if ( !Walked( road, part, right ) )
{
return;
}
var brush = style.Brush( ArchSurface.Pavement, part.Palette, road.Palette );
var kerb = style.Brush( ArchSurface.Kerb, part.Palette, road.Palette );
var bite = ArchContact.Bite( kit );
var width = MathF.Min( MathF.Max( 6f, part.WalkwayWidth ), shape.Section.Half - 24f );
var lift = ArchTunnel.Walkway( road, part, right );
var rows = new List<Vector3[]>();
foreach ( var frame in shape.Frames )
{
// Buried a bite into the lining, or its back face shares the inner face's plane down the bore.
var outer = (right ? 1f : -1f) * (shape.Section.Half + bite);
var inner = right ? outer - width : outer + width;
var top = frame.Position.z + shape.Section.Floor + lift;
rows.Add( new[]
{
frame.Side( MathF.Min( inner, outer ) ).WithZ( top ),
frame.Side( MathF.Max( inner, outer ) ).WithZ( top )
} );
}
ArchMeshSweep.Solid( canvas, rows, shape.Frames, lift + bite, new[] { brush }, kerb, false, carves: shape.Cuts );
}
public static bool Walked( ArchRoadPart road, ArchTunnelPart part, bool right )
{
return road.Carries( part.Walkways, right ) && part.WalkwayWidth >= 6f;
}
}