From 772906bec7d1143a96d7d1a25e739421850ab155 Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Thu, 4 Aug 2022 16:40:58 +0300 Subject: [PATCH] Add single line rendering. --- .idea/.idea.Quik/.idea/.gitignore | 13 + Quik.OpenTK/Class1.cs | 7 + Quik.OpenTK/Quik.OpenTK.csproj | 17 + Quik.sln | 30 +- Quik/Quik.csproj | 3 +- Quik/QuikCommand.cs | 1 - Quik/QuikContext.cs | 7 + Quik/QuikDraw.cs | 2 +- Quik/QuikGeometry.cs | 96 ++++++ Quik/VertexGenerator/QuikVertex.cs | 30 ++ Quik/VertexGenerator/QuikVertexGenerator.cs | 310 ++++++++++++++++++ QuikTestApplication/Program.cs | 174 ++++++++++ .../QuikTestApplication.csproj | 12 + README.md | 5 + 14 files changed, 703 insertions(+), 4 deletions(-) create mode 100644 .idea/.idea.Quik/.idea/.gitignore create mode 100644 Quik.OpenTK/Class1.cs create mode 100644 Quik.OpenTK/Quik.OpenTK.csproj create mode 100644 Quik/VertexGenerator/QuikVertex.cs create mode 100644 Quik/VertexGenerator/QuikVertexGenerator.cs create mode 100644 QuikTestApplication/Program.cs create mode 100644 QuikTestApplication/QuikTestApplication.csproj diff --git a/.idea/.idea.Quik/.idea/.gitignore b/.idea/.idea.Quik/.idea/.gitignore new file mode 100644 index 0000000..dbeb0b0 --- /dev/null +++ b/.idea/.idea.Quik/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.Quik.iml +/modules.xml +/projectSettingsUpdater.xml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/Quik.OpenTK/Class1.cs b/Quik.OpenTK/Class1.cs new file mode 100644 index 0000000..4a1662c --- /dev/null +++ b/Quik.OpenTK/Class1.cs @@ -0,0 +1,7 @@ +namespace Quik.OpenTK +{ + public class Class1 + { + + } +} diff --git a/Quik.OpenTK/Quik.OpenTK.csproj b/Quik.OpenTK/Quik.OpenTK.csproj new file mode 100644 index 0000000..0f1462d --- /dev/null +++ b/Quik.OpenTK/Quik.OpenTK.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + disable + 7.3 + + + + + + + + + + + diff --git a/Quik.sln b/Quik.sln index 43dd58b..90061af 100644 --- a/Quik.sln +++ b/Quik.sln @@ -1,8 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# +# Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik", "Quik\Quik.csproj", "{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik.OpenTK", "Quik.OpenTK\Quik.OpenTK.csproj", "{586E5E28-1D07-4CC2-B04F-0BC420564F57}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuikTestApplication", "QuikTestApplication\QuikTestApplication.csproj", "{49AEF502-692A-48A4-8076-EF2228925280}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -25,5 +29,29 @@ Global {B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x64.Build.0 = Release|Any CPU {B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x86.ActiveCfg = Release|Any CPU {B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x86.Build.0 = Release|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|Any CPU.Build.0 = Debug|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x64.ActiveCfg = Debug|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x64.Build.0 = Debug|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x86.ActiveCfg = Debug|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x86.Build.0 = Debug|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|Any CPU.ActiveCfg = Release|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|Any CPU.Build.0 = Release|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x64.ActiveCfg = Release|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x64.Build.0 = Release|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x86.ActiveCfg = Release|Any CPU + {586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x86.Build.0 = Release|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Debug|x64.ActiveCfg = Debug|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Debug|x64.Build.0 = Debug|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Debug|x86.ActiveCfg = Debug|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Debug|x86.Build.0 = Debug|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Release|Any CPU.Build.0 = Release|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Release|x64.ActiveCfg = Release|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Release|x64.Build.0 = Release|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Release|x86.ActiveCfg = Release|Any CPU + {49AEF502-692A-48A4-8076-EF2228925280}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Quik/Quik.csproj b/Quik/Quik.csproj index 2d68f5d..deccd25 100644 --- a/Quik/Quik.csproj +++ b/Quik/Quik.csproj @@ -3,7 +3,8 @@ net6.0 disable - 7 + 7.3 + True diff --git a/Quik/QuikCommand.cs b/Quik/QuikCommand.cs index 7d3b7a3..c235de5 100644 --- a/Quik/QuikCommand.cs +++ b/Quik/QuikCommand.cs @@ -1,4 +1,3 @@ - namespace Quik { /// diff --git a/Quik/QuikContext.cs b/Quik/QuikContext.cs index 27b143c..0adebbd 100644 --- a/Quik/QuikContext.cs +++ b/Quik/QuikContext.cs @@ -9,5 +9,12 @@ /// Draw queue. /// public QuikDraw Draw { get; } = new QuikDraw(); + + public QuikStrokeStyle DefaultStroke { get; set; } = new QuikStrokeStyle(new QuikColor(0x000000FF), 4); + + public QuikFillStyle DefaultFill { get; set; } = new QuikFillStyle() + { + Color = new QuikColor(0x101010FF) + }; } } \ No newline at end of file diff --git a/Quik/QuikDraw.cs b/Quik/QuikDraw.cs index cd1f972..eafb467 100644 --- a/Quik/QuikDraw.cs +++ b/Quik/QuikDraw.cs @@ -11,7 +11,7 @@ namespace Quik /// /// The draw command queue. /// - private Queue Commands { get; } = new Queue(); + public Queue Commands { get; } = new Queue(); public void Mask(QuikRectangle bounds) => Commands.Enqueue(new QuikCommandMask(bounds)); public void Line(QuikLine line) => Commands.Enqueue(new QuikCommandLine(line)); diff --git a/Quik/QuikGeometry.cs b/Quik/QuikGeometry.cs index 75e1981..2cfe0dc 100644 --- a/Quik/QuikGeometry.cs +++ b/Quik/QuikGeometry.cs @@ -1,3 +1,5 @@ +using System; + namespace Quik { /// @@ -7,6 +9,49 @@ namespace Quik { public float X; public float Y; + + public float Length() => MathF.Sqrt(X * X + Y * Y); + + public QuikVec2 Normalize() => this * (1.0f / this.Length()); + + public float Atan2() => MathF.Atan2(Y, X); + public static QuikVec2 operator +(QuikVec2 a, QuikVec2 b) + { + return new QuikVec2() + { + X = a.X + b.X, + Y = a.Y + b.Y + }; + } + + public static QuikVec2 operator -(QuikVec2 a) + { + return new QuikVec2() + { + X = -a.X, + Y = -a.Y + }; + } + + public static QuikVec2 operator -(QuikVec2 a, QuikVec2 b) + { + return new QuikVec2() + { + X = a.X - b.X, + Y = a.Y - b.Y + }; + } + + public static QuikVec2 operator *(float a, QuikVec2 b) + { + return new QuikVec2() + { + X = a * b.X, + Y = a * b.Y + }; + } + + public static QuikVec2 operator *(QuikVec2 a, float b) => b * a; } /// @@ -30,6 +75,24 @@ namespace Quik /// Alpha channel. /// public byte A; + + public QuikColor(byte r, byte g, byte b, byte a) + { + R = r; + G = g; + B = b; + A = a; + } + + public QuikColor(byte r, byte g, byte b) : this(r, g, b, 1) { } + + public QuikColor(int hexCode) + { + R = (byte)((hexCode >> 24) & 0xFF); + G = (byte)((hexCode >> 16) & 0xFF); + B = (byte)((hexCode >> 8 ) & 0xFF); + A = (byte)((hexCode >> 0 ) & 0xFF); + } } /// @@ -56,6 +119,33 @@ namespace Quik /// Segment end point. /// public QuikVec2 End; + + /// + /// Get a point in the curve segment. + /// + /// Control parameter (between 0 and 1) + /// The point on the curve. + public QuikVec2 GetBezierPoint(float t) + { + return + (1 - t) * (1 - t) * (1 - t) * Start + + (1 - t) * (1 - t) * t * ControlA + + (1 - t) * t * t * ControlB + + t * t * t * End; + } + + /// + /// Get the tangent on the curve. + /// + /// Control parameter (between 0 and 1) + /// The tangent curve. + public QuikVec2 GetBezierTangent(float t) + { + return + 3 * (1 - t) * (1 - t) * (ControlA - Start) + + 6 * (1 - t) * (ControlB - ControlA) + + 3 * t * t * (End - ControlB); + } } /// @@ -88,6 +178,12 @@ namespace Quik /// Rectangle maximum point. /// public QuikVec2 Max; + + public QuikRectangle(float l, float t, float r, float b) + { + Min = new QuikVec2() {X = r, Y = b}; + Max = new QuikVec2() {X = l, Y = t}; + } } /// diff --git a/Quik/VertexGenerator/QuikVertex.cs b/Quik/VertexGenerator/QuikVertex.cs new file mode 100644 index 0000000..1243488 --- /dev/null +++ b/Quik/VertexGenerator/QuikVertex.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; + +namespace Quik.VertexGenerator +{ + /// + /// Represents a GPU vertex. + /// + public struct QuikVertex + { + /// + /// Position value. + /// + public QuikVec2 Position; + + /// + /// Texture Coordinates. + /// + public QuikVec2 TextureCoordinates; + + /// + /// Per vertex color value. + /// + public QuikColor Color; + + public static int PositionOffset => 0; + public static unsafe int TextureCoordinatesOffset => sizeof(QuikVec2); + public static unsafe int ColorOffset => 2 * sizeof(QuikVec2); + public static unsafe int Stride => sizeof(QuikVertex); + } +} \ No newline at end of file diff --git a/Quik/VertexGenerator/QuikVertexGenerator.cs b/Quik/VertexGenerator/QuikVertexGenerator.cs new file mode 100644 index 0000000..599f9d0 --- /dev/null +++ b/Quik/VertexGenerator/QuikVertexGenerator.cs @@ -0,0 +1,310 @@ +using System; + +namespace Quik.VertexGenerator +{ + /// + /// Generates vertices from draw commands for GPU APIs like OpenGL. + /// + public class QuikVertexGenerator + { + // There is a very specific reason I am not using lists like a regular + // person would use. It has to do with the fact that there is no way + // to access the internal pointer of a System.Collections.Generic.List<> + // in older versions of .NET. Avoiding a copy of an entire vertex buffer + // would be very much appreciated by many devs. So please don't be + // "smart" around this code. + // - mixed. + + /// + /// Controls the buffer granularity. + /// + private const int BufferGranularity = 4096; + + /// + /// List of vertices. + /// + private QuikVertex[] _vertexBuffer = new QuikVertex[BufferGranularity]; + + /// + /// Pointer into the vertex buffer. + /// + private int _vertexBufferPointer = 0; + + private float _vertexBufferUsage = 0; + + /// + /// List of element indices. + /// + private short[] _elementBuffer = new short[BufferGranularity]; + + /// + /// Pointer into the element buffer. + /// + private int _elementBufferPointer = 0; + + private float _elementBufferUsage; + private long _bufferUsageCounter; + + /// + /// Get a reference to the vertex buffer. + /// + public QuikVertex[] VertexBuffer => _vertexBuffer; + + /// + /// Number of vertices in the vertex buffer. + /// + public int VertexCount => _vertexBufferPointer; + + /// + /// Get a reference to the element buffer. + /// + public short[] ElementBuffer => _elementBuffer; + + /// + /// Number of elements in the element buffer. + /// + public int ElementCount => _elementBufferPointer; + + public float CurveGranularity { get; set; } = 0.5f; + + public QuikContext Context { get; } + + public QuikVertexGenerator(QuikContext context) + { + Context = context; + } + + /// + /// Expands the vertex buffer by the buffer granularity constant. + /// + private void ExpandVertexBuffer() + { + Array.Resize(ref _vertexBuffer, _vertexBuffer.Length + BufferGranularity); + } + + /// + /// Expands the element buffer by the buffer granularity constant. + /// + private void ExpandElementBuffer() + { + Array.Resize(ref _elementBuffer, _elementBuffer.Length + BufferGranularity); + } + + /// + /// Add vertices to the list. + /// + /// The list of vertices to add. + private void AddVertex(params QuikVertex[] vertices) + { + int requiredCapacity = _vertexBufferPointer + vertices.Length; + while (requiredCapacity > _vertexBuffer.Length) + { + ExpandVertexBuffer(); + } + + Array.Copy(vertices, 0, _vertexBuffer, _vertexBufferPointer, vertices.Length); + _vertexBufferPointer += vertices.Length; + } + + /// + /// Add element indices to the list. + /// + /// The list of indices to add. + private void AddElement(params short[] indices) + { + int requiredCapacity = _elementBufferPointer + indices.Length; + while (requiredCapacity > _elementBuffer.Length) + { + ExpandElementBuffer(); + } + + Array.Copy(indices, 0, _elementBuffer, _elementBufferPointer, indices.Length); + _elementBufferPointer += indices.Length; + } + + private void MovingAverage(ref float average, long sampleCounter, int newSample) + { + // Thanks to stackoverflow for a neat formula. + // https://stackoverflow.com/questions/12636613/how-to-calculate-moving-average-without-keeping-the-count-and-data-total + + const float order = 4; + + average = average + (newSample - average) / Math.Min(sampleCounter + 1, order); + } + + private bool _renderStencilMask = false; + + private QuikRectangle _bounds = new QuikRectangle( + float.PositiveInfinity, float.PositiveInfinity, + float.NegativeInfinity, float.NegativeInfinity); + + /// + /// Clear the drawing buffers. + /// + public void Clear() + { + int newVertexSize; + int newElementSize; + + _bufferUsageCounter++; + MovingAverage(ref _vertexBufferUsage, _bufferUsageCounter, _elementBufferPointer); + MovingAverage(ref _elementBufferUsage, _bufferUsageCounter, _elementBufferPointer); + + newVertexSize = (int)(Math.Ceiling(_vertexBufferUsage / BufferGranularity) * BufferGranularity); + newElementSize = (int)(Math.Ceiling(_elementBufferUsage / BufferGranularity) * BufferGranularity); + + Array.Resize(ref _vertexBuffer, newVertexSize); + Array.Resize(ref _elementBuffer, newElementSize); + + _vertexBufferPointer = 0; + _elementBufferPointer = 0; + } + + public QuikDrawCall? ConsumeCommand(QuikCommand command) + { + QuikDrawCall call = new QuikDrawCall() + { + Target = _renderStencilMask ? QuikRenderTarget.Stencil : QuikRenderTarget.Color + }; + + switch (command.Type) + { + case QuikCommandType.StencilMaskClear: + call.ClearStencil = true; + break; + case QuikCommandType.StencilMaskBegin: + _renderStencilMask = true; + call.Target = QuikRenderTarget.Stencil; + break; + case QuikCommandType.StencilMaskEnd: + _renderStencilMask = false; + call.Target = QuikRenderTarget.Color; + break; + case QuikCommandType.Line: + RenderLine(ref call, command as QuikCommandLine); + return call; + case QuikCommandType.Lines: + RenderLine(ref call, command as QuikCommandLines); + return call; + } + return null; + } + + /// + /// Renders a line. + /// + /// The draw call to generate. + /// The line to draw. + private void RenderLine(ref QuikDrawCall call, QuikCommandLine line) + { + // Skip over stipple patterns for now. + QuikStrokeStyle style = line.Style ?? Context.DefaultStroke; + int endCapResolution; // Resolution of the end cap. + short startOffset = (short) _vertexBufferPointer; // Starting index pointer. + QuikVec2 tangent; + QuikVec2 normal; + + tangent = (line.Line.End - line.Line.Start).Normalize(); + normal = new QuikVec2() {X = -tangent.Y, Y = tangent.X}; + + QuikVertex baseVertex = new QuikVertex() {Color = style.Color }; + QuikVertex startA = baseVertex, startB = baseVertex; + QuikVertex endA = baseVertex, endB = baseVertex; + + startA.Position = line.Line.Start + style.Width / 2 * normal; + startB.Position = line.Line.Start - style.Width / 2 * normal; + endA.Position = line.Line.End + style.Width / 2 * normal; + endB.Position = line.Line.End - style.Width / 2 * normal; + + // Add the major line vertices. + AddVertex(startA, startB, endA, endB); + + // Add the line indices. + AddElement( + (short) (startOffset + 1), + (short) (startOffset + 2), + (short) (startOffset + 0), + (short) (startOffset + 1), + (short) (startOffset + 3), + (short) (startOffset + 2) + ); + + // Now calculate the end caps. + endCapResolution = (int)Math.Ceiling(MathF.PI * style.Width * CurveGranularity); + + // Construct start cap. + QuikVertex circlePoint = baseVertex; + short lastIndex = startOffset; + for (int i = 0; i < endCapResolution; i++) + { + float angle = (float) (i + 1) / (endCapResolution + 1) * MathF.PI; + float cosT = MathF.Cos(angle); + float sinT = MathF.Sin(angle); + + QuikVec2 displacement = new QuikVec2() + { + X = normal.X * cosT - normal.Y * sinT, + Y = normal.X * sinT + normal.Y * cosT + } * (style.Width / 2); + + circlePoint.Position = line.Line.Start + displacement; + + AddVertex(circlePoint); + AddElement( + (short)(startOffset + 1), + lastIndex, + (short)(_vertexBufferPointer - 1)); + + lastIndex = (short) (_vertexBufferPointer - 1); + } + + // Construct end cap. + lastIndex = (short)(startOffset + 2); + for (int i = 0; i < endCapResolution; i++) + { + float angle = -(float) (i + 1) / (endCapResolution + 1) * MathF.PI; + float cosT = MathF.Cos(angle); + float sinT = MathF.Sin(angle); + + QuikVec2 displacement = new QuikVec2() + { + X = normal.X * cosT - normal.Y * sinT, + Y = normal.X * sinT + normal.Y * cosT + } * (style.Width / 2); + + circlePoint.Position = line.Line.End + displacement; + + AddVertex(circlePoint); + AddElement( + (short)(startOffset + 3), + lastIndex, + (short)(_vertexBufferPointer - 1)); + + lastIndex = (short) (_vertexBufferPointer - 1); + } + + call.Offset =(short) (startOffset * 2); + call.Count = (short) (_elementBufferPointer - startOffset); + } + + private void RenderLine(ref QuikDrawCall call, QuikCommandLines lines) + { + + } + } + + public enum QuikRenderTarget + { + Color, + Stencil + } + + public struct QuikDrawCall + { + public QuikRenderTarget Target; + public short Offset; + public short Count; + public QuikRectangle Bounds; + public bool ClearStencil; + } +} \ No newline at end of file diff --git a/QuikTestApplication/Program.cs b/QuikTestApplication/Program.cs new file mode 100644 index 0000000..4ce6f62 --- /dev/null +++ b/QuikTestApplication/Program.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using Quik; +using Quik.VertexGenerator; +using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; +using OpenTK.Windowing.Common; +using OpenTK.Windowing.Desktop; +using OpenTK.Windowing.GraphicsLibraryFramework; + +namespace QuikTestApplication +{ + public class Program + { + public const string vertex = + @"#version 140 +uniform mat4 matrix; +in vec2 position; +in vec2 texcoord; +in vec4 color; +out vec2 ftexcoord; +out vec4 fcolor; + +void main() +{ + fcolor = color; + ftexcoord = texcoord; + gl_Position = matrix * vec4(position.xy, 0, 1); +} +"; + + public const string fragment = + @"#version 140 +in vec2 ftexcoord; +in vec4 fcolor; +out vec4 outcolor; + +void main() +{ + outcolor = fcolor; +} +"; + + public static void Main(string[] args) + { + NativeWindowSettings windowSettings = NativeWindowSettings.Default; + windowSettings.NumberOfSamples = 4; + NativeWindow window = new NativeWindow(windowSettings); + + window.Context.MakeCurrent(); + GL.LoadBindings(new GLFWBindingsContext()); + + QuikContext context = new QuikContext(); + QuikVertexGenerator gen = new QuikVertexGenerator(context); + + GL.Enable(EnableCap.Multisample); + + int sp; + { + int vs, fs; + + sp = GL.CreateProgram(); + + vs = GL.CreateShader(ShaderType.VertexShader); + fs = GL.CreateShader(ShaderType.FragmentShader); + + GL.ShaderSource(vs, vertex); + GL.CompileShader(vs); + GL.ShaderSource(fs, fragment); + GL.CompileShader(fs); + + GL.AttachShader(sp, vs); + GL.AttachShader(sp, fs); + GL.LinkProgram(sp); + + GL.UseProgram(sp); + } + + int vbo, ebo, vao; + vbo = GL.GenBuffer(); + ebo = GL.GenBuffer(); + + vao = GL.GenVertexArray(); + GL.BindVertexArray(vao); + + GL.BindBuffer(BufferTarget.ArrayBuffer, vbo); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, ebo); + + int loc; + GL.VertexAttribPointer( + loc = GL.GetAttribLocation(sp, "position"), + 2, + VertexAttribPointerType.Float, + false, + QuikVertex.Stride, + QuikVertex.PositionOffset); + GL.EnableVertexAttribArray(loc); + GL.VertexAttribPointer( + loc = GL.GetAttribLocation(sp, "texcoords"), + 2, + VertexAttribPointerType.Float, + false, + QuikVertex.Stride, + QuikVertex.TextureCoordinatesOffset); + GL.EnableVertexAttribArray(loc); + GL.VertexAttribPointer( + loc = GL.GetAttribLocation(sp, "color"), + 4, + VertexAttribPointerType.UnsignedByte, + true, + QuikVertex.Stride, + QuikVertex.ColorOffset); + GL.EnableVertexAttribArray(loc); + + loc = GL.GetUniformLocation(sp, "matrix"); + + List calls = new List(); + + for (;!window.IsExiting;) + { + NativeWindow.ProcessWindowEvents(false); + + GL.Viewport(0, 0, window.Size.X, window.Size.Y); + + GL.ClearColor(1,1,1,1); + GL.Clear(ClearBufferMask.ColorBufferBit); + + Matrix4 matrix = Matrix4.CreateOrthographicOffCenter( + 0, + window.Size.X, + 0, + window.Size.Y, + 1, + -1); + GL.UniformMatrix4(loc, false, ref matrix); + + context.Draw.Line( + new QuikLine() + { + Start = new QuikVec2() + { + X = 20, + Y = 40 + }, + End = new QuikVec2() + { + X=100, + Y=100 + } + }); + + QuikCommand command; + while (context.Draw.Commands.TryDequeue(out command)) + { + QuikDrawCall? call = gen.ConsumeCommand(command); + if (call.HasValue) calls.Add(call.Value); + } + + GL.BufferData(BufferTarget.ArrayBuffer, gen.VertexCount * QuikVertex.Stride, ref gen.VertexBuffer[0], BufferUsageHint.StreamDraw); + GL.BufferData(BufferTarget.ElementArrayBuffer, gen.ElementCount * 2, ref gen.ElementBuffer[0], BufferUsageHint.StreamDraw); + + foreach (QuikDrawCall call in calls) + { + GL.DrawElements(BeginMode.Triangles, call.Count, DrawElementsType.UnsignedShort, call.Offset); + } + + gen.Clear(); + calls.Clear(); + + window.Context.SwapBuffers(); + } + } + } +} \ No newline at end of file diff --git a/QuikTestApplication/QuikTestApplication.csproj b/QuikTestApplication/QuikTestApplication.csproj new file mode 100644 index 0000000..786da43 --- /dev/null +++ b/QuikTestApplication/QuikTestApplication.csproj @@ -0,0 +1,12 @@ + + + + Exe + net6.0 + + + + + + + diff --git a/README.md b/README.md index ca65ea4..ea590fa 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,8 @@ QUIK is not intended to replace the aforementioned libraries for the C or C++ developer, however it is intended to make a similar library available to C# users without having to battle the un/managed barrier. It also comes with the advantage of not requiring any platform specific native libraries. + +On top of that, QUIK targets not just .NET 6.0, but old-school .NET framework +in mind as well. Whilst I do not promise the golden .NET Framework 2.0 support +I do wish to make the library available for .NET Framework 4.7.2 in the future. +Therefore the language version of the project is strictly limited to 7.3.