A readonly struct representing a single convex collision or render solid used by the architecture system. It supports three concrete forms (Box, Cylinder, Hull), factory constructors for each form, a usability check to skip tiny solids, and a Fold method to combine the solid into a rolling hash.
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
public enum ArchSolidForm
{
Box,
Cylinder,
Hull
}
// One convex piece of a part, in the part node's own space. A generator emits solids because it KNOWS them - a
// column is a box, a voussoir is a prism, a board is a beam - so nothing here has to be recovered from triangles.
// Every form the engine can take as a single shape and no more: a box, a lathed cylinder, or a point cloud whose
// convex hull is the shape. A rotated anything is Hull, because a hull carries its own orientation in its points
// and a box shape does not.
public readonly struct ArchSolid
{
public ArchSolidForm Form { get; init; }
public Vector3 Centre { get; init; }
public Vector3 Size { get; init; }
public float Radius { get; init; }
public float Height { get; init; }
public int Slices { get; init; }
public IReadOnlyList<Vector3> Points { get; init; }
public static ArchSolid Box( Vector3 mins, Vector3 maxs )
{
var lo = Vector3.Min( mins, maxs );
var hi = Vector3.Max( mins, maxs );
return new ArchSolid
{
Form = ArchSolidForm.Box,
Centre = (lo + hi) * 0.5f,
Size = hi - lo
};
}
public static ArchSolid Cylinder( Vector3 centre, float radius, float height, int slices )
{
return new ArchSolid
{
Form = ArchSolidForm.Cylinder,
Centre = centre,
Radius = radius,
Height = height,
Slices = slices
};
}
public static ArchSolid Hull( IReadOnlyList<Vector3> points )
{
return new ArchSolid { Form = ArchSolidForm.Hull, Points = points };
}
// A shape smaller than this in every direction is not worth a solver body - it is a beading or a lip, and the
// piece it sits on already carries the collision the player meets.
const float Least = 1.5f;
public bool IsUsable
{
get
{
if ( Form == ArchSolidForm.Box )
{
return Size.x > Least && Size.y > Least && Size.z > Least;
}
if ( Form == ArchSolidForm.Cylinder )
{
return Radius > Least * 0.5f && Height > Least;
}
return Points is { Count: >= 4 };
}
}
public ulong Fold( ulong hash )
{
hash = ArchHash.Fold( hash, (int)Form );
if ( Form == ArchSolidForm.Hull )
{
foreach ( var point in Points )
{
hash = ArchHash.Fold( hash, point );
}
return hash;
}
hash = ArchHash.Fold( hash, Centre );
hash = ArchHash.Fold( hash, Size );
hash = ArchHash.Fold( hash, Radius );
hash = ArchHash.Fold( hash, Height );
return ArchHash.Fold( hash, Slices );
}
}