Editor/Roof/ArchVaultGen.cs
using System;
using System.Collections.Generic;
using Sandbox;

namespace Sunless.Architecture;

public static class ArchVaultGen {
	public static void Build( ArchMesh canvas, ArchRoofPart roof, ArchKit kit, ArchStyle style ) {
		var outline = roof.Outline();

		if ( outline.Count < 4 ) {
			return;
		}

		ArchFootprint.Bounds( outline, out var min, out var max );

		var alongX = roof.RidgeAlongX;
		var runFrom = alongX ? min.x : min.y;
		var runTo = alongX ? max.x : max.y;
		var acrossFrom = alongX ? min.y : min.x;
		var acrossTo = alongX ? max.y : max.x;
		var span = acrossTo - acrossFrom;
		var rise = MathF.Min( MathF.Max( 8f, roof.VaultRise ), span * 0.48f );
		var crown = roof.BaseHeight - MathF.Max( 1f, roof.Thickness );
		var spring = crown - rise;
		var brush = style.Brush( ArchSurface.Ceiling, roof.Palette );
		const int segments = 16;

		for ( var index = 0; index < segments; index++ ) {
			var from = acrossFrom + span * index / segments;
			var to = acrossFrom + span * (index + 1) / segments;
			var fromHeight = Height( from, acrossFrom, span, spring, crown );
			var toHeight = Height( to, acrossFrom, span, spring, crown );

			if ( alongX ) {
				canvas.Quad(
					new Vector3( runFrom, from, fromHeight ),
					new Vector3( runFrom, to, toHeight ),
					new Vector3( runTo, to, toHeight ),
					new Vector3( runTo, from, fromHeight ), brush );
			} else {
				canvas.Quad(
					new Vector3( from, runFrom, fromHeight ),
					new Vector3( to, runFrom, toHeight ),
					new Vector3( to, runTo, toHeight ),
					new Vector3( from, runTo, fromHeight ), brush );
			}
		}
	}

	static float Height( float at, float from, float span, float spring, float crown ) {
		var fraction = Math.Clamp( (at - from) / MathF.Max( 1f, span ), 0f, 1f );
		return spring + (crown - spring) * MathF.Sin( fraction * MathF.PI );
	}
}