Add single line rendering.
This commit is contained in:
parent
a6465730c1
commit
772906bec7
13
.idea/.idea.Quik/.idea/.gitignore
vendored
Normal file
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
7
Quik.OpenTK/Class1.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Quik.OpenTK
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
17
Quik.OpenTK/Quik.OpenTK.csproj
Normal file
17
Quik.OpenTK/Quik.OpenTK.csproj
Normal file
@ -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>
|
30
Quik.sln
30
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
|
||||
|
@ -3,7 +3,8 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<LangVersion>7</LangVersion>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
namespace Quik
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -9,5 +9,12 @@
|
||||
/// Draw queue.
|
||||
/// </summary>
|
||||
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>
|
||||
/// The draw command queue.
|
||||
/// </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 Line(QuikLine line) => Commands.Enqueue(new QuikCommandLine(line));
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Quik
|
||||
{
|
||||
/// <summary>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -30,6 +75,24 @@ namespace Quik
|
||||
/// Alpha channel.
|
||||
/// </summary>
|
||||
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>
|
||||
@ -56,6 +119,33 @@ namespace Quik
|
||||
/// Segment end point.
|
||||
/// </summary>
|
||||
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>
|
||||
@ -88,6 +178,12 @@ namespace Quik
|
||||
/// Rectangle maximum point.
|
||||
/// </summary>
|
||||
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>
|
||||
|
30
Quik/VertexGenerator/QuikVertex.cs
Normal file
30
Quik/VertexGenerator/QuikVertex.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
310
Quik/VertexGenerator/QuikVertexGenerator.cs
Normal file
310
Quik/VertexGenerator/QuikVertexGenerator.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
174
QuikTestApplication/Program.cs
Normal file
174
QuikTestApplication/Program.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
QuikTestApplication/QuikTestApplication.csproj
Normal file
12
QuikTestApplication/QuikTestApplication.csproj
Normal file
@ -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#
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user