Editor/Roof/ArchVaultGen.cs

Editor utility that generates a vaulted roof mesh. It computes an arch profile across the roof footprint, samples it into 16 segments, and emits quads to an ArchMesh canvas using the supplied roof, kit and style parameters.

File Access
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 );
	}
}