scripts/Lidar.cs

Component that builds a point-cloud style mesh from added points. It stores vertex and index lists, creates quads for each lidar point, and rebuilds a ModelRenderer mesh at a controlled rate.

Native Interop
using System;

public sealed class Lidar : Component
{
	[Property] public float RebuildRate { get; set; } = 0.25f;
	[Property] public float PointSize { get; set; } = 5.0f;
	[Property] public Material DotMaterial { get; set; }

	[Property] public ModelRenderer Model { get; set; }

	private float lastUpdate = 0f;
	private bool isDirty = false;

	private readonly List<Vertex> vertices = new();
	private readonly List<int> indices = new();

	protected override void OnStart()
	{
		DotMaterial ??= Material.Load( "materials/default.vmat" );
		Model ??= Components.GetOrCreate<ModelRenderer>();
	}

	protected override void OnUpdate()
	{
		if ( !isDirty )
			return;

		float nowTime = Time.Now;

		if ( (nowTime - lastUpdate) < RebuildRate )
			return;

		lastUpdate = nowTime;

		RebuildModel();
	}

	public void AddPoint( Vector3 pos, Vector3 normal )
	{
		normal = normal.Normal;

		Vector3 tangent = Vector3.Cross(
			normal,
			MathF.Abs( normal.z ) < 0.99f
				? Vector3.Up
				: Vector3.Forward
		).Normal;

		Vector3 bitangent = Vector3.Cross( normal, tangent ).Normal;

		float half = PointSize * 0.5f;

		int baseIndex = vertices.Count;

		vertices.Add( new Vertex(
			pos + (tangent + bitangent) * half,
			normal,
			new Vector4( tangent, 1 ),
			new Vector2( 0, 0 )
		) );

		vertices.Add( new Vertex(
			pos + (tangent - bitangent) * half,
			normal,
			new Vector4( tangent, 1 ),
			new Vector2( 0, 1 )
		) );

		vertices.Add( new Vertex(
			pos + (-tangent - bitangent) * half,
			normal,
			new Vector4( tangent, 1 ),
			new Vector2( 1, 1 )
		) );

		vertices.Add( new Vertex(
			pos + (-tangent + bitangent) * half,
			normal,
			new Vector4( tangent, 1 ),
			new Vector2( 1, 0 )
		) );

		indices.Add( baseIndex + 0 );
		indices.Add( baseIndex + 1 );
		indices.Add( baseIndex + 2 );

		indices.Add( baseIndex + 0 );
		indices.Add( baseIndex + 2 );
		indices.Add( baseIndex + 3 );

		isDirty = true;
	}

	public void ClearPoints()
	{
		vertices.Clear();
		indices.Clear();

		isDirty = true;
	}

	public void RebuildModel()
	{
		isDirty = false;

		if ( vertices.Count == 0 )
			return;

		var mesh = new Mesh( DotMaterial );

		mesh.CreateVertexBuffer( vertices.Count, vertices );
		mesh.CreateIndexBuffer( indices.Count, indices.ToArray() );

		Model.Model = new ModelBuilder()
			.AddMesh( mesh )
			.Create();
	}
}