Editor/Tunnel/ArchTunnelGen.cs

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.

File AccessNative Interop
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;
	}

}