using System; using System.IO; using System.Numerics; using gaemstone.ECS; using Silk.NET.OpenGL; using static gaemstone.Client.Components.RenderingComponents; using static gaemstone.Client.Components.ResourceComponents; using static gaemstone.Client.Systems.Windowing; using ModelRoot = SharpGLTF.Schema2.ModelRoot; namespace gaemstone.Client.Systems; [Module] [DependsOn] [DependsOn] [DependsOn] public class MeshManager { private const uint PositionAttribIndex = 0; private const uint NormalAttribIndex = 1; private const uint UvAttribIndex = 2; [System] public static void LoadMeshWhenDefined( [Game] Canvas canvas, EntityRef entity, Mesh _1, [Not] MeshHandle _2) { var path = entity.GetFullPath(); using var stream = Resources.GetStream(path); var handle = CreateFromStream(canvas.GL, stream); entity.Set(handle); } private static MeshHandle CreateFromStream(GL GL, Stream stream) { var root = ModelRoot.ReadGLB(stream); var primitive = root.LogicalMeshes[0].Primitives[0]; var indices = primitive.IndexAccessor; var vertices = primitive.VertexAccessors["POSITION"]; var normals = primitive.VertexAccessors["NORMAL"]; var vao = GL.GenVertexArray(); GL.BindVertexArray(vao); GL.CreateBufferFromData(indices.SourceBufferView.Content, BufferTargetARB.ElementArrayBuffer); GL.CreateBufferFromData(vertices.SourceBufferView.Content); GL.EnableVertexAttribArray(PositionAttribIndex); unsafe { GL.VertexAttribPointer(PositionAttribIndex, 3, (VertexAttribPointerType)vertices.Encoding, vertices.Normalized, (uint)vertices.SourceBufferView.ByteStride, (void*)vertices.ByteOffset); } GL.CreateBufferFromData(normals.SourceBufferView.Content); GL.EnableVertexAttribArray(NormalAttribIndex); unsafe { GL.VertexAttribPointer(NormalAttribIndex, 3, (VertexAttribPointerType)vertices.Encoding, vertices.Normalized, (uint)vertices.SourceBufferView.ByteStride, (void*)vertices.ByteOffset); } var numVertices = primitive.IndexAccessor.Count; return new(vao, numVertices); } public static MeshHandle Create(GL GL, ReadOnlySpan indices, ReadOnlySpan vertices, ReadOnlySpan normals, ReadOnlySpan uvs) { var vao = GL.GenVertexArray(); GL.BindVertexArray(vao); GL.CreateBufferFromData(indices, BufferTargetARB.ElementArrayBuffer); GL.CreateBufferFromData(vertices); GL.EnableVertexAttribArray(PositionAttribIndex); unsafe { GL.VertexAttribPointer(PositionAttribIndex, 3, VertexAttribPointerType.Float, false, 0, (void*)0); } if (!normals.IsEmpty) { GL.CreateBufferFromData(normals); GL.EnableVertexAttribArray(NormalAttribIndex); unsafe { GL.VertexAttribPointer(NormalAttribIndex, 3, VertexAttribPointerType.Float, false, 0, (void*)0); } } if (!uvs.IsEmpty) { GL.CreateBufferFromData(uvs); GL.EnableVertexAttribArray(UvAttribIndex); unsafe { GL.VertexAttribPointer(UvAttribIndex, 2, VertexAttribPointerType.Float, false, 0, (void*)0); } } return new(vao, indices.Length); } public static MeshHandle Create(GL GL, ReadOnlySpan vertices, ReadOnlySpan normals, ReadOnlySpan uvs) { var vao = GL.GenVertexArray(); GL.BindVertexArray(vao); GL.CreateBufferFromData(vertices); GL.EnableVertexAttribArray(PositionAttribIndex); unsafe { GL.VertexAttribPointer(PositionAttribIndex, 3, VertexAttribPointerType.Float, false, 0, (void*)0); } if (!normals.IsEmpty) { GL.CreateBufferFromData(normals); GL.EnableVertexAttribArray(NormalAttribIndex); unsafe { GL.VertexAttribPointer(NormalAttribIndex, 3, VertexAttribPointerType.Float, false, 0, (void*)0); } } if (!uvs.IsEmpty) { GL.CreateBufferFromData(uvs); GL.EnableVertexAttribArray(UvAttribIndex); unsafe { GL.VertexAttribPointer(UvAttribIndex, 2, VertexAttribPointerType.Float, false, 0, (void*)0); } } return new(vao, vertices.Length, false); } }