Vehicles/FlatQuad.cs

Static helper that builds and caches a one-inch flat quad Model for a given material path, used for tire marks and plate backings. The mesh has both faces so it is visible from either side.

namespace HorizonRP;

/// <summary>
/// Un carré d'un pouce de côté, à plat dans le plan XY de son objet (face +Z, visible des deux
/// côtés), centré, la texture du matériau étirée dessus : U selon X, V selon −Y. On le pose,
/// le tourne et l'étire par l'objet. Sert aux traces de pneus et au fond des plaques.
/// Un modèle par matériau, fabriqué une fois et gardé.
/// </summary>
public static class FlatQuad
{
	const int Version = 1;
	static readonly Dictionary<string, Model> Cache = new();

	public static Model Get( string materialPath )
	{
		var key = $"{Version}|{materialPath}";
		if ( Cache.TryGetValue( key, out var cached ) && cached is not null && !cached.IsError )
			return cached;

		var material = Material.Load( materialPath );
		if ( material is null )
			return null;
		var n = Vector3.Up;
		var t = Vector3.Forward;
		var verts = new List<Vertex>
		{
			new( new Vector3( -0.5f, 0.5f, 0 ), n, t, new Vector4( 0, 0, 0, 0 ) ),
			new( new Vector3( 0.5f, 0.5f, 0 ), n, t, new Vector4( 1, 0, 0, 0 ) ),
			new( new Vector3( 0.5f, -0.5f, 0 ), n, t, new Vector4( 1, 1, 0, 0 ) ),
			new( new Vector3( -0.5f, -0.5f, 0 ), n, t, new Vector4( 0, 1, 0, 0 ) ),
		};
		var mesh = new Mesh( material, MeshPrimitiveType.Triangles );
		mesh.CreateVertexBuffer( verts.Count, Vertex.Layout, verts );
		// Les deux faces : vu d'en haut, l'une des deux était retirée comme face arrière.
		mesh.CreateIndexBuffer( 12, new List<int> { 0, 1, 2, 0, 2, 3, 0, 2, 1, 0, 3, 2 } );
		mesh.Bounds = new BBox( new Vector3( -0.5f, -0.5f, -0.1f ), new Vector3( 0.5f, 0.5f, 0.1f ) );
		var model = Model.Builder.WithName( $"horizon/quad/{System.IO.Path.GetFileNameWithoutExtension( materialPath )}" ).AddMesh( mesh ).Create();
		Cache[key] = model;
		return model;
	}
}