using System.Numerics; using ProjectUnknown.Engine.Components; using ProjectUnknown.Engine.Rendering; using ProjectUnknown.Engine.Resources; using Serilog; using Silk.NET.OpenGL; using Shader = ProjectUnknown.Engine.Resources.Shader; using Texture = ProjectUnknown.Engine.Resources.Texture; using static ProjectUnknown.Engine.MathHelper; namespace ProjectUnknown.Engine.Systems; public class SpriteRenderSystem : ISystem, IDisposable { private readonly IEntityManager _entityManager; private readonly IEntityQuery _cameraQuery; private readonly IEntityQuery _spriteQuery; private readonly IResourceManager _resources; private readonly Shader _shader; private readonly GL _gl; private readonly int _uTextureLocation; private readonly int _uViewLocation; private readonly int _uProjectionLocation; private readonly int _uModelLocation; private readonly BufferObject _vbo; private readonly BufferObject _ebo; private readonly VertexArrayObject _vao; private const float Min = -.5f; private const float Max = .5f; //Vertex data, uploaded to the VBO. private static readonly float[] Vertices = { //X Y Min, Min, 0, 1, Max, Min, 1, 1, Min, Max, 0, 0, Max, Max, 1, 0 }; //Index data, uploaded to the EBO. private static readonly uint[] Indices = { 0, 2, 1, 2, 3, 1 }; public SpriteRenderSystem(IEntityManager entityManager, IResourceManager resources, GL gl) { _entityManager = entityManager; _cameraQuery = _entityManager.CreateQuery(_entityManager.HasComponent); _spriteQuery = _entityManager.CreateQuery(_entityManager.HasComponent); _resources = resources; _gl = gl; _ebo = new (_gl, Indices, BufferTargetARB.ElementArrayBuffer); _vbo = new (_gl, Vertices, BufferTargetARB.ArrayBuffer); _vao = new (_gl, _vbo, _ebo); _vao.VertexAttributePointer(0, 2, VertexAttribPointerType.Float, 4, 0); _vao.VertexAttributePointer(1, 2, VertexAttribPointerType.Float, 4, 2); _shader = _resources.Load("ProjectUnknown.Engine.Resources.BuiltIn.SpriteDefault", "sprite-default"); _uTextureLocation = GetLocation("uTexture0"); _uViewLocation = GetLocation("uView"); _uProjectionLocation = GetLocation("uProjection"); _uModelLocation = GetLocation("uModel"); } private int GetLocation(string name) { var location = _gl.GetUniformLocation(_shader.Handle, name); if (location == -1) { throw new($"{name} uniform not found on shader."); } return location; } public unsafe void Execute(float deltaTime) { _gl.UseProgram(_shader.Handle); _vao.Bind(); _cameraQuery.ForEach(cam => { var camMatrix = _entityManager.GetComponent(cam); _spriteQuery .OrderBy(entity => _entityManager.HasComponent(entity) ? _entityManager.GetComponent(entity).layer : -1) .ThenBy(entity => _entityManager.HasComponent(entity) ? _entityManager.GetComponent(entity).sortOrder : -1) .ForEach(entity => { var sprite = _entityManager.GetComponent(entity); var texture = _resources.Get(sprite.Texture); Vector2 size = _entityManager.HasComponent(entity) ? _entityManager.GetComponent(entity) : Vector2.One; var rotation = _entityManager.HasComponent(entity) ? _entityManager.GetComponent(entity) : 0; Vector2 position = _entityManager.HasComponent(entity) ? _entityManager.GetComponent(entity) : Vector2.Zero; _gl.ActiveTexture(TextureUnit.Texture0); _gl.BindTexture(TextureTarget.Texture2D, texture.Handle); _gl.Uniform1(_uTextureLocation, 0); SetMatrix(_uViewLocation, camMatrix.View); SetMatrix(_uProjectionLocation, camMatrix.Projection); var model = Matrix4x4.Identity * Matrix4x4.CreateScale(size.X, size.Y, 1) * Matrix4x4.CreateRotationZ(DegreesToRadians(rotation)) * Matrix4x4.CreateTranslation(position.X, position.Y, 0); SetMatrix(_uModelLocation, model); _gl.DrawElements(PrimitiveType.Triangles, (uint) Indices.Length, DrawElementsType.UnsignedInt, null); void SetMatrix(int location, Matrix4x4 value) => _gl.UniformMatrix4(location, 1, false, (float*)&value); }); }); } public void Dispose() { _vao.Dispose(); _vbo.Dispose(); _ebo.Dispose(); _resources.Unload("sprite-default"); } }