Files
GhostEngine/Ghost.Graphics/Data/Mesh.cs
Misaki 1284bb17de Refactor graphics architecture and resource management
Added DescriptorAllocator.cs to manage descriptor allocations for Direct3D 12.
Added Texture2D.cs to handle 2D textures and GPU resource creation.
Added DescriptorAllocatorExample.cs to demonstrate the new descriptor allocator interface.

Changed project files to reference Misaki.HighPerformance.LowLevel instead of Misaki.HighPerformance.Unsafe.
Changed _renderView type from IRenderer? to Renderer? in ScenePage.xaml.cs.
Changed EngineCore.cs to remove explicit graphics API specification during initialization.
Changed Logger.cs to enhance the Assert method with a DoesNotReturnIf attribute.
Changed resource types in Mesh.cs from IResource to GraphicsResource.

Removed multiple interfaces including ICommandBuffer, IDebugLayer, IGraphicsDevice, IPipelineResource, IRenderPass, IRenderer, IResource, and IResourceAllocator to simplify the graphics architecture.
Removed D3D12DebugLayer class from DebugLayer.cs to streamline the debug layer implementation.

Updated CommandList.cs and D3D12CommandBuffer.cs to implement a new command list structure for Direct3D 12.
Updated Material.cs to improve handling of constant buffers and textures.
Updated Shader.cs to include new structures for texture and property information.
Updated GraphicsPipeline.cs to support the new graphics device and resource management system.
Updated UnitTestAppWindow.xaml.cs to reflect changes in the renderer type and ensure proper resource management.
Updated BindlessMeshRenderPass.cs and MeshRenderPass.cs to implement modern rendering techniques, including bindless textures and improved shader management.
Updated CBufferCache.cs to align with the new resource management system and improve memory handling.
2025-07-12 14:25:20 +09:00

272 lines
8.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Ghost.Core;
using Ghost.Graphics.D3D12;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Helpers;
using System.Numerics;
using System.Runtime.CompilerServices;
using Win32.Graphics.Direct3D12;
using Win32.Graphics.Dxgi.Common;
namespace Ghost.Graphics.Data;
public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable
{
private UnsafeList<Vertex> _vertices = new(initialVertexCapacity, Allocator.Persistent);
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
private Bounds _boundingBox;
private GraphicsResource? _vertexBuffer;
private GraphicsResource? _indexBuffer;
private VertexBufferView _vertexBufferView;
private IndexBufferView _indexBufferView;
public Span<Vertex> Vertices => _vertices.AsSpan();
public Span<int> Indices => _indices.AsSpan();
public Bounds BoundingBox => _boundingBox;
public uint VertexCount => (uint)_vertices.Count;
public uint IndexCount => (uint)_indices.Count;
internal ConstPtr<VertexBufferView> VertexBufferView => (VertexBufferView*)Unsafe.AsPointer(ref _vertexBufferView);
internal ConstPtr<IndexBufferView> IndexBufferView => (IndexBufferView*)Unsafe.AsPointer(ref _indexBufferView);
~Mesh()
{
Dispose();
}
/// <summary>
/// Adds a vertex to the mesh with the specified attributes.
/// </summary>
/// <param name="vertex">The vertex data to add</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddVertex(Vertex vertex)
{
_vertices.Add(vertex);
}
/// <summary>
/// Adds a triangle to the mesh by specifying the indices of its three vertices.
/// </summary>
/// <param name="index0">The index of the first vertex in the triangle. Must be within the range of the current vertex count.</param>
/// <param name="index1">The index of the second vertex in the triangle. Must be within the range of the current vertex count.</param>
/// <param name="index2">The index of the third vertex in the triangle. Must be within the range of the current vertex count.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if any of the specified indices are out of range for the current vertex count.</exception>
public void AddTriangle(int index0, int index1, int index2)
{
if (index0 >= _vertices.Count || index1 >= _vertices.Count || index2 >= _vertices.Count)
{
throw new ArgumentOutOfRangeException("Index out of range for the current vertex count.");
}
_indices.Add(index0);
_indices.Add(index1);
_indices.Add(index2);
}
/// <summary>
/// Reduces the memory usage of the internal collections by resizing them to match their current element count.
/// </summary>
public void TrimExcess()
{
_vertices.Resize(_vertices.Count);
_indices.Resize(_indices.Count);
}
/// <summary>
/// Auto-compute smooth per-vertex normals.
/// </summary>
/// <remarks>
/// Call this method before vertices and indices are valid.
/// </remarks>
public void ComputeNormal()
{
if (!_vertices.IsCreated || _vertices.Count < 3
|| !_indices.IsCreated || _indices.Count < 3)
{
return;
}
for (var i = 0; i < _indices.Count; i += 3)
{
var i0 = _indices[i];
var i1 = _indices[i + 1];
var i2 = _indices[i + 2];
var v0 = _vertices[i0];
var v1 = _vertices[i1];
var v2 = _vertices[i2];
var edge1 = v1.Position - v0.Position;
var edge2 = v2.Position - v0.Position;
var faceNormal = Vector3.Cross(edge1.AsVector3(), edge2.AsVector3());
_vertices[i0].Normal += faceNormal.AsVector4();
_vertices[i1].Normal += faceNormal.AsVector4();
_vertices[i2].Normal += faceNormal.AsVector4();
}
for (var i = 0; i < _vertices.Count; i++)
{
_vertices[i].Normal = Vector4.Normalize(_vertices[i].Normal);
}
}
/// <summary>
/// Auto-compute per-vertex tangents.
/// </summary>
/// <remarks>
/// Call this method before vertices, normals, and UVs are valid.
/// </remarks>
public void ComputeTangents()
{
var bitangents = new Vector4[_vertices.Count];
for (var i = 0; i < _indices.Count; i += 3)
{
var i0 = _indices[i];
var i1 = _indices[i + 1];
var i2 = _indices[i + 2];
var v0 = _vertices[i0];
var v1 = _vertices[i1];
var v2 = _vertices[i2];
var uv0 = _vertices[i0].UV;
var uv1 = _vertices[i1].UV;
var uv2 = _vertices[i2].UV;
var deltaPos1 = v1.Position - v0.Position;
var deltaPos2 = v2.Position - v0.Position;
var deltaUV1 = uv1 - uv0;
var deltaUV2 = uv2 - uv0;
var r = 1.0f / (deltaUV1.X * deltaUV2.Y - deltaUV1.Y * deltaUV2.X);
var tangent = (deltaPos1 * deltaUV2.Y - deltaPos2 * deltaUV1.Y) * r;
var bitangent = (deltaPos2 * deltaUV1.X - deltaPos1 * deltaUV2.X) * r;
for (var j = 0; j < 3; j++)
{
var idx = _indices[i + j];
var t = _vertices[idx].Tangent;
_vertices[idx].Tangent = new Vector4(
t.X + tangent.X,
t.Y + tangent.Y,
t.Z + tangent.Z,
0.0f // well fill w later
);
bitangents[idx] += bitangent;
}
}
for (var i = 0; i < _vertices.Count; i++)
{
var n = _vertices[i].Normal;
var t = _vertices[i].Tangent;
var n3 = n.AsVector3();
var t3 = t.AsVector3();
var proj = n3 * Vector3.Dot(n3, t3);
t3 = Vector3.Normalize(t3 - proj);
var b = bitangents[i];
var w = Vector3.Dot(Vector3.Cross(n3, t3), b.AsVector3()) < 0.0f ? -1.0f : 1.0f;
_vertices[i].Tangent = new Vector4(t3.X, t3.Y, t3.Z, w);
}
}
/// <summary>
/// Computes the bounding box of the mesh based on its vertices.
/// </summary>
public void ComputeBounds()
{
if (_vertices.Count == 0)
{
_boundingBox = Bounds.Zero;
return;
}
var min = new Vector3(float.MaxValue);
var max = new Vector3(float.MinValue);
foreach (var vertex in _vertices)
{
var pos = vertex.Position.AsVector3();
min = Vector3.Min(min, pos);
max = Vector3.Max(max, pos);
}
_boundingBox = new Bounds(min, max);
}
/// <summary>
/// Uploads the mesh data to GPU resources.
/// </summary>
public unsafe void UploadMeshData()
{
if (VertexCount == 0 || IndexCount == 0)
{
return;
}
_vertexBuffer?.Dispose();
_indexBuffer?.Dispose();
var vertexBufferSize = (uint)(VertexCount * sizeof(Vertex));
var indexBufferSize = IndexCount * sizeof(int);
_vertexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(vertexBufferSize);
_indexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(indexBufferSize);
using var uploadBatch = new ResourceUploadBatch();
uploadBatch.Begin();
uploadBatch.Upload(_vertexBuffer, _vertices.AsSpan());
uploadBatch.Upload(_indexBuffer, _indices.AsSpan());
uploadBatch.Transition(_vertexBuffer, ResourceStates.CopyDest, ResourceStates.VertexAndConstantBuffer);
uploadBatch.Transition(_indexBuffer, ResourceStates.CopyDest, ResourceStates.IndexBuffer);
uploadBatch.WaitForCompletion(uploadBatch.End());
_vertexBufferView = new VertexBufferView
{
BufferLocation = _vertexBuffer.GPUAddress,
SizeInBytes = vertexBufferSize,
StrideInBytes = (uint)sizeof(Vertex)
};
_indexBufferView = new IndexBufferView
{
BufferLocation = _indexBuffer.GPUAddress,
SizeInBytes = indexBufferSize,
Format = Format.R32Uint
};
}
/// <summary>
/// Clears all vertex and index data and releases associated GPU resources.
/// </summar>
public void Clear()
{
_vertices.Clear();
_indices.Clear();
DisposeGpuResources();
}
private void DisposeGpuResources()
{
_vertexBuffer?.Dispose();
_vertexBuffer = null;
_indexBuffer?.Dispose();
_indexBuffer = null;
}
public void Dispose()
{
_vertices.Dispose();
_indices.Dispose();
DisposeGpuResources();
GC.SuppressFinalize(this);
}
}