using System.Drawing; using System.Numerics; using System.Runtime.InteropServices; using Dashboard.Drawing; using Dashboard.Drawing.OpenGL.Pal; using Dashboard.Pal; using OpenTK.Graphics.OpenGL; namespace Dashboard.OpenGL.Drawing { public class ImmediateMode : IImmediateMode { public string DriverName => "Dashboard OpenGL Immediate Mode"; public string DriverVendor => "Dashboard"; public Version DriverVersion { get; } = new Version(1, 0); public DeviceContext Context { get; private set; } = null!; private int _program; private uint _program_apos; private uint _program_atexcoord; private uint _program_acolor; private int _program_transforms; private int _program_image; private int _vao; private int _white; public void Dispose() { } public void Require(DeviceContext context) { Context = context; _program = GL.CreateProgram(); int vs = GL.CreateShader(ShaderType.VertexShader); using (StreamReader reader = new StreamReader(GetType().Assembly .GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.vert")!)) { GL.ShaderSource(vs, reader.ReadToEnd()); } GL.CompileShader(vs); GL.AttachShader(_program, vs); int fs = GL.CreateShader(ShaderType.FragmentShader); using (StreamReader reader = new StreamReader(GetType().Assembly .GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.frag")!)) { GL.ShaderSource(fs, reader.ReadToEnd()); } GL.CompileShader(fs); GL.AttachShader(_program, fs); GL.LinkProgram(_program); GL.DeleteShader(vs); GL.DeleteShader(fs); _program_apos = (uint)GL.GetAttribLocation(_program, "aPos"); _program_atexcoord = (uint)GL.GetAttribLocation(_program, "aTexCoords"); _program_acolor = (uint)GL.GetAttribLocation(_program, "aColor"); _program_transforms = GL.GetUniformLocation(_program, "transforms"); _program_image = GL.GetUniformLocation(_program, "image"); GL.GenTexture(out _white); GL.BindTexture(TextureTarget.Texture2d, _white); GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgb, 1, 1, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgb, PixelType.Byte, IntPtr.Zero); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleA, (int)All.One); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleR, (int)All.One); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleG, (int)All.One); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleB, (int)All.One); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); GL.GenVertexArray(out _vao); } public void ClearColor(Color color) { GL.ClearColor(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); GL.Clear(ClearBufferMask.ColorBufferBit); } public void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color) { Vector2 normal = Vector2.Normalize(b - a); Vector2 tangent = new Vector2(-normal.Y, normal.X) * width; Span vertices = [ new ImmediateVertex(new Vector3(a-tangent, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(b-tangent, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(b+tangent, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(a-tangent, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(b+tangent, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(a+tangent, depth), Vector2.Zero, color), ]; int buffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, buffer); GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * ImmediateVertex.Size, ref vertices[0], BufferUsage.StreamDraw); GL.BindVertexArray(_vao); GL.VertexAttribPointer(_program_apos, 3, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.PosOffset); GL.EnableVertexAttribArray(_program_apos); GL.VertexAttribPointer(_program_atexcoord, 2, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.TexCoordsOffset); GL.EnableVertexAttribArray(_program_atexcoord); GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset); GL.EnableVertexAttribArray(_program_acolor); Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize; Matrix4x4 view = Matrix4x4.CreateOrthographicOffCenter(0, size.Width, 0, size.Height, 1, -1); GL.UseProgram(_program); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2d, _white); GL.UniformMatrix4f(_program_transforms, 1, true, ref view); GL.Uniform1i(_program_image, 0); GL.DrawArrays(PrimitiveType.Triangles, 0, 6); GL.DeleteBuffer(buffer); } public void Rectangle(Box2d rectangle, float depth, Vector4 color) { Span vertices = [ new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Min.Y, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), Vector2.Zero, color), ]; int buffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, buffer); GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * ImmediateVertex.Size, ref vertices[0], BufferUsage.StreamDraw); GL.BindVertexArray(_vao); GL.VertexAttribPointer(_program_apos, 3, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.PosOffset); GL.EnableVertexAttribArray(_program_apos); GL.VertexAttribPointer(_program_atexcoord, 2, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.TexCoordsOffset); GL.EnableVertexAttribArray(_program_atexcoord); GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset); GL.EnableVertexAttribArray(_program_acolor); Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize; Matrix4x4 view = Matrix4x4.CreateOrthographicOffCenter(0, size.Width, 0, size.Height, 1, -1); GL.UseProgram(_program); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2d, _white); GL.UniformMatrix4f(_program_transforms, 1, true, ref view); GL.Uniform1i(_program_image, 0); GL.DrawArrays(PrimitiveType.Triangles, 0, 6); GL.DeleteBuffer(buffer); } public void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture) { Span vertices = [ new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), new Vector2(uv.Min.X, uv.Min.Y), Vector4.One), new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Min.Y, depth), new Vector2(uv.Max.X, uv.Min.Y), Vector4.One), new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), new Vector2(uv.Max.X, uv.Max.Y), Vector4.One), new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), new Vector2(uv.Min.X, uv.Min.Y), Vector4.One), new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), new Vector2(uv.Max.X, uv.Max.Y), Vector4.One), new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), new Vector2(uv.Min.X, uv.Max.Y), Vector4.One), ]; int buffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, buffer); GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * ImmediateVertex.Size, ref vertices[0], BufferUsage.StreamDraw); GL.BindVertexArray(_vao); GL.VertexAttribPointer(_program_apos, 3, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.PosOffset); GL.EnableVertexAttribArray(_program_apos); GL.VertexAttribPointer(_program_atexcoord, 2, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.TexCoordsOffset); GL.EnableVertexAttribArray(_program_atexcoord); GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset); GL.EnableVertexAttribArray(_program_acolor); Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize; Matrix4x4 view = Matrix4x4.CreateOrthographicOffCenter(0, size.Width, 0, size.Height, 1, -1); GL.UseProgram(_program); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2d, ((GLTexture)texture).Handle); GL.UniformMatrix4f(_program_transforms, 1, true, ref view); GL.Uniform1i(_program_image, 0); GL.DrawArrays(PrimitiveType.Triangles, 0, 6); GL.DeleteBuffer(buffer); } IContextBase IContextExtensionBase.Context => Context; void IContextExtensionBase.Require(IContextBase context) { Require((DeviceContext)context); } [StructLayout(LayoutKind.Explicit, Pack = sizeof(float) * 4, Size = Size)] private struct ImmediateVertex(Vector3 position, Vector2 texCoords, Vector4 color) { [FieldOffset(PosOffset)] public Vector3 Position = position; [FieldOffset(TexCoordsOffset)] public Vector2 TexCoords = texCoords; [FieldOffset(ColorOffset)] public Vector4 Color = color; public const int Size = 16 * sizeof(float); public const int PosOffset = 0 * sizeof(float); public const int TexCoordsOffset = 4 * sizeof(float); public const int ColorOffset = 8 * sizeof(float); } } }