Add single line rendering.

This commit is contained in:
H. Utku Maden 2022-08-04 16:40:58 +03:00
parent a6465730c1
commit 772906bec7
14 changed files with 703 additions and 4 deletions

13
.idea/.idea.Quik/.idea/.gitignore vendored Normal file

@ -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

7
Quik.OpenTK/Class1.cs Normal file

@ -0,0 +1,7 @@
namespace Quik.OpenTK
{
public class Class1
{
}
}

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK" Version="4.7.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Quik\Quik.csproj" />
</ItemGroup>
</Project>

@ -1,8 +1,12 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# #
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik", "Quik\Quik.csproj", "{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik", "Quik\Quik.csproj", "{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|x64.Build.0 = Release|Any CPU
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
EndGlobal EndGlobal

@ -3,7 +3,8 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<LangVersion>7</LangVersion> <LangVersion>7.3</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

@ -1,4 +1,3 @@
namespace Quik namespace Quik
{ {
/// <summary> /// <summary>

@ -9,5 +9,12 @@
/// Draw queue. /// Draw queue.
/// </summary> /// </summary>
public QuikDraw Draw { get; } = new QuikDraw(); 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)
};
} }
} }

@ -11,7 +11,7 @@ namespace Quik
/// <summary> /// <summary>
/// The draw command queue. /// The draw command queue.
/// </summary> /// </summary>
private Queue<QuikCommand> Commands { get; } = new Queue<QuikCommand>(); public Queue<QuikCommand> Commands { get; } = new Queue<QuikCommand>();
public void Mask(QuikRectangle bounds) => Commands.Enqueue(new QuikCommandMask(bounds)); public void Mask(QuikRectangle bounds) => Commands.Enqueue(new QuikCommandMask(bounds));
public void Line(QuikLine line) => Commands.Enqueue(new QuikCommandLine(line)); public void Line(QuikLine line) => Commands.Enqueue(new QuikCommandLine(line));

@ -1,3 +1,5 @@
using System;
namespace Quik namespace Quik
{ {
/// <summary> /// <summary>
@ -7,6 +9,49 @@ namespace Quik
{ {
public float X; public float X;
public float Y; 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;
} }
/// <summary> /// <summary>
@ -30,6 +75,24 @@ namespace Quik
/// Alpha channel. /// Alpha channel.
/// </summary> /// </summary>
public byte A; 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);
}
} }
/// <summary> /// <summary>
@ -56,6 +119,33 @@ namespace Quik
/// Segment end point. /// Segment end point.
/// </summary> /// </summary>
public QuikVec2 End; public QuikVec2 End;
/// <summary>
/// Get a point in the curve segment.
/// </summary>
/// <param name="t">Control parameter (between 0 and 1)</param>
/// <returns>The point on the curve.</returns>
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;
}
/// <summary>
/// Get the tangent on the curve.
/// </summary>
/// <param name="t">Control parameter (between 0 and 1)</param>
/// <returns>The tangent curve.</returns>
public QuikVec2 GetBezierTangent(float t)
{
return
3 * (1 - t) * (1 - t) * (ControlA - Start) +
6 * (1 - t) * (ControlB - ControlA) +
3 * t * t * (End - ControlB);
}
} }
/// <summary> /// <summary>
@ -88,6 +178,12 @@ namespace Quik
/// Rectangle maximum point. /// Rectangle maximum point.
/// </summary> /// </summary>
public QuikVec2 Max; 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};
}
} }
/// <summary> /// <summary>

@ -0,0 +1,30 @@
using System.Runtime.CompilerServices;
namespace Quik.VertexGenerator
{
/// <summary>
/// Represents a GPU vertex.
/// </summary>
public struct QuikVertex
{
/// <summary>
/// Position value.
/// </summary>
public QuikVec2 Position;
/// <summary>
/// Texture Coordinates.
/// </summary>
public QuikVec2 TextureCoordinates;
/// <summary>
/// Per vertex color value.
/// </summary>
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);
}
}

@ -0,0 +1,310 @@
using System;
namespace Quik.VertexGenerator
{
/// <summary>
/// Generates vertices from draw commands for GPU APIs like OpenGL.
/// </summary>
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.
/// <summary>
/// Controls the buffer granularity.
/// </summary>
private const int BufferGranularity = 4096;
/// <summary>
/// List of vertices.
/// </summary>
private QuikVertex[] _vertexBuffer = new QuikVertex[BufferGranularity];
/// <summary>
/// Pointer into the vertex buffer.
/// </summary>
private int _vertexBufferPointer = 0;
private float _vertexBufferUsage = 0;
/// <summary>
/// List of element indices.
/// </summary>
private short[] _elementBuffer = new short[BufferGranularity];
/// <summary>
/// Pointer into the element buffer.
/// </summary>
private int _elementBufferPointer = 0;
private float _elementBufferUsage;
private long _bufferUsageCounter;
/// <summary>
/// Get a reference to the vertex buffer.
/// </summary>
public QuikVertex[] VertexBuffer => _vertexBuffer;
/// <summary>
/// Number of vertices in the vertex buffer.
/// </summary>
public int VertexCount => _vertexBufferPointer;
/// <summary>
/// Get a reference to the element buffer.
/// </summary>
public short[] ElementBuffer => _elementBuffer;
/// <summary>
/// Number of elements in the element buffer.
/// </summary>
public int ElementCount => _elementBufferPointer;
public float CurveGranularity { get; set; } = 0.5f;
public QuikContext Context { get; }
public QuikVertexGenerator(QuikContext context)
{
Context = context;
}
/// <summary>
/// Expands the vertex buffer by the buffer granularity constant.
/// </summary>
private void ExpandVertexBuffer()
{
Array.Resize(ref _vertexBuffer, _vertexBuffer.Length + BufferGranularity);
}
/// <summary>
/// Expands the element buffer by the buffer granularity constant.
/// </summary>
private void ExpandElementBuffer()
{
Array.Resize(ref _elementBuffer, _elementBuffer.Length + BufferGranularity);
}
/// <summary>
/// Add vertices to the list.
/// </summary>
/// <param name="vertices">The list of vertices to add.</param>
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;
}
/// <summary>
/// Add element indices to the list.
/// </summary>
/// <param name="indices">The list of indices to add.</param>
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);
/// <summary>
/// Clear the drawing buffers.
/// </summary>
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;
}
/// <summary>
/// Renders a line.
/// </summary>
/// <param name="call">The draw call to generate.</param>
/// <param name="line">The line to draw.</param>
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;
}
}

@ -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<QuikDrawCall> calls = new List<QuikDrawCall>();
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();
}
}
}
}

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Quik.OpenTK\Quik.OpenTK.csproj" />
</ItemGroup>
</Project>

@ -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# 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 users without having to battle the un/managed barrier. It also comes with the
advantage of not requiring any platform specific native libraries. 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.