using Ghost.Core; using Ghost.Graphics.RHI; using Ghost.Graphics.Utilities; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics.Geometry; namespace Ghost.Graphics.Core; public struct Mesh : IResourceReleasable { private UnsafeList _vertices; private UnsafeList _indices; internal bool IsMeshDataDirty { get; private set; } /// /// Gets or sets the collection of vertices that define the geometry. /// public UnsafeList Vertices { readonly get => _vertices; set { _vertices = value; VertexCount = value.Count; IsMeshDataDirty = true; } } /// /// Gets or sets the collection of indices that define the order of vertices. /// public UnsafeList Indices { readonly get => _indices; set { _indices = value; IndexCount = value.Count; IsMeshDataDirty = true; } } /// /// Get the number of vertices in the mesh. /// public int VertexCount { get; private set; } /// /// Get the number of indices in the mesh. /// public int IndexCount { get; private set; } /// /// Gets or sets the axis-aligned bounding box (AABB) of the mesh. /// public AABB BoundingBox { get; set; } /// /// Gets the handle to the vertex buffer on the GPU. /// public Handle VertexBuffer { get; internal set; } /// /// Gets the handle to the index buffer on the GPU. /// public Handle IndexBuffer { get; internal set; } /// /// Gets the handle to the mesh data buffer on the GPU. /// public Handle ObjectDataBuffer { get; internal set; } internal Mesh(ReadOnlySpan vertices, ReadOnlySpan indices, Handle vertexBuffer, Handle indexBuffer) { Vertices = new UnsafeList(vertices.Length, Allocator.Persistent); Indices = new UnsafeList(indices.Length, Allocator.Persistent); Vertices.CopyFrom(vertices); Indices.CopyFrom(indices); VertexBuffer = vertexBuffer; IndexBuffer = indexBuffer; this.ComputeBounds(); } public readonly void ReleaseCpuResources() { _vertices.Dispose(); _indices.Dispose(); } readonly void IResourceReleasable.ReleaseResource(IResourceDatabase database) { ReleaseCpuResources(); database.ReleaseResource(VertexBuffer.AsResource()); database.ReleaseResource(IndexBuffer.AsResource()); database.ReleaseResource(ObjectDataBuffer.AsResource()); } } public static class MeshExtension { /// /// Computes the bounding box of the mesh based on its vertices. /// public static void ComputeBounds(ref this Mesh mesh) { if (mesh.Vertices.Count == 0) { return; } var min = new float3(float.MaxValue); var max = new float3(float.MinValue); foreach (var vertex in mesh.Vertices) { var pos = vertex.position.xyz; min = math.min(min, pos); max = math.max(max, pos); } mesh.BoundingBox = new AABB(min, max); } /// /// Auto-compute smooth per-vertex normals. /// /// /// Call this method before vertices and indices are valid. /// public static void ComputeNormal(ref this Mesh mesh) { MeshBuilder.ComputeNormal(mesh.Vertices, mesh.Indices); } /// /// Auto-compute per-vertex tangents. /// /// /// Call this method before vertices, normals, and UVs are valid. /// public static void ComputeTangents(ref this Mesh mesh) { MeshBuilder.ComputeTangents(mesh.Vertices, mesh.Indices); } }