diff --git a/Quik/QuikContext.cs b/Quik/QuikContext.cs
index 0adebbd..5fb4117 100644
--- a/Quik/QuikContext.cs
+++ b/Quik/QuikContext.cs
@@ -14,7 +14,7 @@
public QuikFillStyle DefaultFill { get; set; } = new QuikFillStyle()
{
- Color = new QuikColor(0x101010FF)
+ Color = new QuikColor(0xA0A0A0FF)
};
}
}
\ No newline at end of file
diff --git a/Quik/QuikGeometry.cs b/Quik/QuikGeometry.cs
index 1df0546..5d140fe 100644
--- a/Quik/QuikGeometry.cs
+++ b/Quik/QuikGeometry.cs
@@ -1,10 +1,13 @@
using System;
+using System.ComponentModel.DataAnnotations;
+using System.Diagnostics;
namespace Quik
{
///
/// A 2 dimensional Vector.
///
+ [DebuggerDisplay("({X}, {Y})")]
public struct QuikVec2
{
public float X;
@@ -89,6 +92,7 @@ namespace Quik
///
/// A RGBA color value.
///
+ [DebuggerDisplay("({R}, {G}, {B}, {A})")]
public struct QuikColor
{
///
@@ -118,18 +122,20 @@ namespace Quik
public QuikColor(byte r, byte g, byte b) : this(r, g, b, 1) { }
- public QuikColor(int hexCode)
+ public QuikColor(uint hexCode)
{
R = (byte)((hexCode >> 24) & 0xFF);
G = (byte)((hexCode >> 16) & 0xFF);
B = (byte)((hexCode >> 8 ) & 0xFF);
A = (byte)((hexCode >> 0 ) & 0xFF);
}
+ public QuikColor(int hexCode) : this((uint)hexCode) { }
}
-
+
///
/// A bezier curve segment.
///
+ [DebuggerDisplay("{Start} -- {ControlA} -- {ControlB} -- {End}")]
public struct QuikBezier
{
///
@@ -217,6 +223,7 @@ namespace Quik
///
/// A line segment.
///
+ [DebuggerDisplay("{Start} -- {End}")]
public struct QuikLine
{
///
@@ -247,22 +254,59 @@ namespace Quik
///
/// A rectangle.
///
+ [DebuggerDisplay("({Right}, {Top}, {Left}, {Bottom})")]
public struct QuikRectangle
{
- ///
- /// Rectangle minimum point.
- ///
- public QuikVec2 Min;
-
///
/// Rectangle maximum point.
///
public QuikVec2 Max;
+
+ ///
+ /// Rectangle minimum point.
+ ///
+ public QuikVec2 Min;
- public QuikRectangle(float l, float t, float r, float b)
+ public float Left
{
- Min = new QuikVec2() {X = r, Y = b};
- Max = new QuikVec2() {X = l, Y = t};
+ get => Min.X;
+ set => Min.X = value;
+ }
+
+ public float Right
+ {
+ get => Max.X;
+ set => Max.X = value;
+ }
+
+ public float Top
+ {
+ get => Max.Y;
+ set => Max.Y = value;
+ }
+
+ public float Bottom
+ {
+ get => Min.Y;
+ set => Min.Y = value;
+ }
+
+ public QuikVec2 Size
+ {
+ get => Max - Min;
+ set => Max = Min + value;
+ }
+
+ public QuikRectangle(QuikVec2 max, QuikVec2 min)
+ {
+ Max = max;
+ Min = min;
+ }
+
+ public QuikRectangle(float r, float t, float l, float b)
+ {
+ Max = new QuikVec2() {X = r, Y = t};
+ Min = new QuikVec2() {X = l, Y = b};
}
}
@@ -270,6 +314,7 @@ namespace Quik
/// An ellipse.
///
/// It is undefined to have an ellipse with non-orthogonal axes.
+ [DebuggerDisplay("{Center} ellipse {AxisA}; {AxisB}")]
public struct QuikEllipse
{
///
@@ -291,6 +336,7 @@ namespace Quik
///
/// A triangle.
///
+ [DebuggerDisplay("{A} -- {B} -- {C}")]
public struct QuikTriangle
{
///
diff --git a/Quik/VertexGenerator/QuikVertex.cs b/Quik/VertexGenerator/QuikVertex.cs
index 1243488..7688526 100644
--- a/Quik/VertexGenerator/QuikVertex.cs
+++ b/Quik/VertexGenerator/QuikVertex.cs
@@ -1,10 +1,11 @@
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
namespace Quik.VertexGenerator
{
///
/// Represents a GPU vertex.
///
+ [DebuggerDisplay("XY={Position} RGBA={Color}, UV={TextureCoordinates}")]
public struct QuikVertex
{
///
diff --git a/Quik/VertexGenerator/QuikVertexGenerator.cs b/Quik/VertexGenerator/QuikVertexGenerator.cs
index d75e2b4..2e3a196 100644
--- a/Quik/VertexGenerator/QuikVertexGenerator.cs
+++ b/Quik/VertexGenerator/QuikVertexGenerator.cs
@@ -30,7 +30,7 @@ namespace Quik.VertexGenerator
///
private int _vertexBufferPointer = 0;
- private float _vertexBufferUsage = 0;
+ private float _vertexUsage = 0;
///
/// List of element indices.
@@ -42,9 +42,19 @@ namespace Quik.VertexGenerator
///
private int _elementBufferPointer = 0;
- private float _elementBufferUsage;
+ private float _elementUsage;
private long _bufferUsageCounter;
+ ///
+ /// Moving average of the vertex count.
+ ///
+ public float VertexUsage => _vertexUsage;
+
+ ///
+ /// Moving average of the element count.
+ ///
+ public float ElementUsage => _elementUsage;
+
///
/// Get a reference to the vertex buffer.
///
@@ -147,11 +157,11 @@ namespace Quik.VertexGenerator
int newElementSize;
_bufferUsageCounter++;
- MovingAverage(ref _vertexBufferUsage, _bufferUsageCounter, _elementBufferPointer);
- MovingAverage(ref _elementBufferUsage, _bufferUsageCounter, _elementBufferPointer);
+ MovingAverage(ref _vertexUsage, _bufferUsageCounter, _vertexBufferPointer);
+ MovingAverage(ref _elementUsage, _bufferUsageCounter, _elementBufferPointer);
- newVertexSize = (int)(Math.Ceiling(_vertexBufferUsage / BufferGranularity) * BufferGranularity);
- newElementSize = (int)(Math.Ceiling(_elementBufferUsage / BufferGranularity) * BufferGranularity);
+ newVertexSize = (int)(Math.Ceiling(_vertexUsage / BufferGranularity) * BufferGranularity);
+ newElementSize = (int)(Math.Ceiling(_elementUsage / BufferGranularity) * BufferGranularity);
Array.Resize(ref _vertexBuffer, newVertexSize);
Array.Resize(ref _elementBuffer, newElementSize);
@@ -160,38 +170,77 @@ namespace Quik.VertexGenerator
_elementBufferPointer = 0;
}
+ private QuikDrawCall _call;
+
+ public event VertexGeneratorCommandHandler HandleCommand;
+
+ protected virtual void OnHandleCommand(QuikVertexGenerator generator, QuikCommand command, ref QuikDrawCall? call)
+ {
+ HandleCommand?.Invoke(generator, command, ref call);
+ }
+
public QuikDrawCall? ConsumeCommand(QuikCommand command)
{
- QuikDrawCall call = new QuikDrawCall()
- {
- Target = _renderStencilMask ? QuikRenderTarget.Stencil : QuikRenderTarget.Color
- };
+ _call.Target = _renderStencilMask ? QuikRenderTarget.Stencil : QuikRenderTarget.Color;
+
+ QuikDrawCall call = _call;
switch (command.Type)
{
+ case QuikCommandType.Mask:
+ _call.Bounds = ((QuikCommandMask)command).Bounds;
+ break;
case QuikCommandType.StencilMaskClear:
- call.ClearStencil = true;
+ _call.ClearStencil = true;
break;
case QuikCommandType.StencilMaskBegin:
_renderStencilMask = true;
- call.Target = QuikRenderTarget.Stencil;
+ _call.Target = QuikRenderTarget.Stencil;
break;
case QuikCommandType.StencilMaskEnd:
_renderStencilMask = false;
- call.Target = QuikRenderTarget.Color;
+ _call.Target = QuikRenderTarget.Color;
break;
+
case QuikCommandType.Line:
RenderLine(ref call, command as QuikCommandLine);
- return call;
+ goto exit_with_call;
case QuikCommandType.Lines:
RenderLine(ref call, command as QuikCommandLines);
- return call;
+ goto exit_with_call;
case QuikCommandType.Bezier:
- RenderBezier(ref call, command as QuikCommandBezier);
- return call;
+ RenderBezier(ref _call, command as QuikCommandBezier);
+ goto exit_with_call;
+
+ case QuikCommandType.Rectangle:
+ RenderRectangles(ref call, command as QuikCommandRectangle);
+ goto exit_with_call;
+ case QuikCommandType.Rectangles:
+ RenderRectangles(ref call, command as QuikCommandRectangles);
+ goto exit_with_call;
+ default:
+ {
+ QuikDrawCall? subcall = null;
+ OnHandleCommand(this, command, ref subcall);
+ if (subcall.HasValue)
+ {
+ call = subcall.Value;
+ goto exit_with_call;
+ }
+ else
+ {
+ goto exit_without_call;
+ }
+ }
}
+ exit_without_call:
return null;
+ exit_with_call:
+ _call.ClearStencil = false;
+ return call;
}
+
+ #region Lines & Beziers
///
/// Generates a basic line segment.
@@ -237,13 +286,12 @@ namespace Quik.VertexGenerator
///
/// Gets the rounding resolution for a line segment.
///
- /// The width of the line.
+ /// The width of the line.
/// The angle of the cap or joint arc.
/// The rounding resolution.
- private int GetRoundingResolution(float width, float arc)
+ private int GetRoundingResolution(float radius, float arc)
{
- int endCapResolution = (int) Math.Ceiling(arc * width * CurveGranularity);
- return endCapResolution;
+ return (int) Math.Ceiling(arc * radius * CurveGranularity);
}
///
@@ -727,8 +775,622 @@ namespace Quik.VertexGenerator
endNegativeIndex = (short) (_vertexBufferPointer - 1);
endPositiveIndex = (short) (_vertexBufferPointer - 2);
}
+
+ #endregion
+
+ #region Rectangles
+
+ private void RenderRectangles(ref QuikDrawCall call, QuikCommandRectangle rectangle)
+ {
+ QuikStrokeStyle stroke = rectangle.StrokeStyle ?? Context.DefaultStroke;
+ QuikFillStyle fill = rectangle.FillStyle ?? Context.DefaultFill;
+
+ short start = (short) _elementBufferPointer;
+
+ GenerateRectangle(rectangle.Rectangle, stroke, fill, rectangle.CornerRadius);
+
+ call.Offset = (short) (start * 2);
+ call.Count = (short) (_elementBufferPointer - start);
+ }
+
+ private void GenerateRectangle(QuikRectangle rectangle, QuikStrokeStyle stroke, QuikFillStyle fill, float cornerRadius)
+ {
+ float semiStroke = 0.5f * stroke.Width;
+
+ if (cornerRadius == 0)
+ {
+ // If there is no stroke radius, draw a simpler rectangle.
+ if (stroke.Width == 0)
+ {
+ // If there is no border, just 4 points is enough.
+
+ GenerateRectangleSimple(rectangle, fill);
+ }
+ else
+ {
+ // If there is a border, take it into account.
+
+ QuikRectangle innerRectangle = rectangle;
+ innerRectangle.Left += semiStroke;
+ innerRectangle.Top -= semiStroke;
+ innerRectangle.Right -= semiStroke;
+ innerRectangle.Bottom += semiStroke;
+
+ GenerateRectangleSimple(innerRectangle, fill);
+ GenerateRectangleBorderSimple(rectangle, stroke);
+ }
+ }
+ else
+ {
+ float innerRadius = cornerRadius - semiStroke;
+
+ if (innerRadius <= 0)
+ {
+ QuikRectangle innerRectangle = new QuikRectangle(
+ rectangle.Right - semiStroke,
+ rectangle.Top - semiStroke,
+ rectangle.Left + semiStroke,
+ rectangle.Bottom + semiStroke
+ );
+
+ GenerateRectangleSimple(innerRectangle, fill);
+ GenerateRectangleBorderNarrow(rectangle, stroke, cornerRadius);
+ }
+ else
+ {
+ // Generate the inner rectangle.
+ QuikRectangle innerRectangle = new QuikRectangle(
+ rectangle.Right - semiStroke * 0.95f,
+ rectangle.Top - semiStroke * 0.95f,
+ rectangle.Left + semiStroke * 0.95f,
+ rectangle.Bottom + semiStroke * 0.95f
+ );
+ GenerateRectangleRound(innerRectangle, fill, innerRadius);
+
+ if (stroke.Width > 0)
+ {
+ GenerateRectangleBorderWide(rectangle, stroke, cornerRadius);
+ }
+ }
+ }
+ }
+
+ private void GenerateRectangleBorderNarrow(
+ QuikRectangle rectangle,
+ QuikStrokeStyle stroke,
+ float cornerRadius)
+ {
+ float semiStroke = 0.5f * stroke.Width;
+ float inset = cornerRadius - semiStroke;
+ short start = (short) _vertexBufferPointer;
+
+ QuikVertex baseVertex = new QuikVertex() {Color = stroke.Color};
+ QuikVertex a = baseVertex, b = baseVertex, c = baseVertex, d = baseVertex,
+ e = baseVertex, f = baseVertex, g = baseVertex, h = baseVertex,
+ i = baseVertex, j = baseVertex, k = baseVertex, l = baseVertex,
+ q1 = baseVertex, q2 = baseVertex, q3 = baseVertex, q4 = baseVertex;
+
+ a.Position = new QuikVec2(rectangle.Left + semiStroke, rectangle.Bottom + semiStroke);
+ b.Position = new QuikVec2(rectangle.Right - semiStroke, rectangle.Bottom + semiStroke);
+ c.Position = new QuikVec2(rectangle.Right - semiStroke, rectangle.Top - semiStroke);
+ d.Position = new QuikVec2(rectangle.Left + semiStroke, rectangle.Top - semiStroke);
+
+ q1.Position = new QuikVec2(rectangle.Left + inset, rectangle.Bottom + inset);
+ q2.Position = new QuikVec2(rectangle.Right - inset, rectangle.Bottom + inset);
+ q3.Position = new QuikVec2(rectangle.Right - inset, rectangle.Top - inset);
+ q4.Position = new QuikVec2(rectangle.Left + inset, rectangle.Top - inset);
+
+ e.Position = new QuikVec2(q1.Position.X, rectangle.Bottom - semiStroke);
+ f.Position = new QuikVec2(q2.Position.X, rectangle.Bottom - semiStroke);
+ g.Position = new QuikVec2(rectangle.Right + semiStroke, q2.Position.Y);
+ h.Position = new QuikVec2(rectangle.Right + semiStroke, q3.Position.Y);
+ i.Position = new QuikVec2(q3.Position.X, rectangle.Top + semiStroke);
+ j.Position = new QuikVec2(q4.Position.X, rectangle.Top + semiStroke);
+ k.Position = new QuikVec2(rectangle.Left - semiStroke, q4.Position.Y);
+ l.Position = new QuikVec2(rectangle.Left - semiStroke, q1.Position.Y);
+
+ AddVertex(a, b, c, d, e, f, g, h, i, j, k, l, q1, q2, q3, q4);
+ AddElement(
+ (short) (start + 4), (short) (start + 5), (short) (start + 13),
+ (short) (start + 4), (short) (start + 13), (short) (start + 12),
+ (short) (start + 12), (short) (start + 13), (short) (start + 1),
+ (short) (start + 12), (short) (start + 1), (short) (start + 0),
+
+ (short) (start + 13), (short) (start + 6), (short) (start + 7),
+ (short) (start + 13), (short) (start + 7), (short) (start + 14),
+ (short) (start + 13), (short) (start + 14), (short) (start + 2),
+ (short) (start + 13), (short) (start + 2), (short) (start + 1),
+
+ (short) (start + 3), (short) (start + 2), (short) (start + 14),
+ (short) (start + 3), (short) (start + 14), (short) (start + 15),
+ (short) (start + 15), (short) (start + 14), (short) (start + 8),
+ (short) (start + 15), (short) (start + 8), (short) (start + 9),
+
+ (short) (start + 0), (short) (start + 3), (short) (start + 15),
+ (short) (start + 0), (short) (start + 15), (short) (start + 12),
+ (short) (start + 12), (short) (start + 15), (short) (start + 10),
+ (short) (start + 12), (short) (start + 10), (short) (start + 11)
+ );
+
+ int resolution = GetRoundingResolution(cornerRadius, 0.5f * MathF.PI);
+ short last, next;
+
+ last = (short) (start + 7);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVertex vertex = baseVertex;
+ vertex.Position = q3.Position + cornerRadius * new QuikVec2(MathF.Cos(angle), MathF.Sin(angle));
+
+ next = (short) _vertexBufferPointer;
+ AddVertex(vertex);
+ AddElement((short)(start + 14), last, next);
+ last = next;
+ }
+ AddElement((short)(start + 14), last, (short)(start + 8));
+
+ last = (short) (start + 9);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVertex vertex = baseVertex;
+ vertex.Position = q4.Position + cornerRadius * new QuikVec2(-MathF.Sin(angle), MathF.Cos(angle));
+
+ next = (short) _vertexBufferPointer;
+ AddVertex(vertex);
+ AddElement((short)(start + 15), last, next);
+ last = next;
+ }
+ AddElement((short)(start + 15), last, (short)(start + 10));
+
+ last = (short) (start + 11);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVertex vertex = baseVertex;
+ vertex.Position = q1.Position + cornerRadius * new QuikVec2(-MathF.Cos(angle), -MathF.Sin(angle));
+
+ next = (short) _vertexBufferPointer;
+ AddVertex(vertex);
+ AddElement((short)(start + 12), last, next);
+ last = next;
+ }
+ AddElement((short)(start + 12), last, (short)(start + 4));
+
+ last = (short) (start + 5);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVertex vertex = baseVertex;
+ vertex.Position = q2.Position + cornerRadius * new QuikVec2(MathF.Sin(angle), -MathF.Cos(angle));
+
+ next = (short) _vertexBufferPointer;
+ AddVertex(vertex);
+ AddElement((short)(start + 13), last, next);
+ last = next;
+ }
+ AddElement((short)(start + 13), last, (short)(start + 6));
+ }
+
+ private void GenerateRectangleBorderWide(QuikRectangle rectangle, QuikStrokeStyle stroke, float cornerRadius)
+ {
+ float semiStroke = 0.5f * stroke.Width;
+ float outerRadius = cornerRadius + semiStroke;
+ float innerRadius = cornerRadius - semiStroke;
+ short start = (short) _vertexBufferPointer;
+
+ // The corner foci.
+ QuikVec2 q1 = new QuikVec2(rectangle.Min.X + cornerRadius, rectangle.Min.Y + cornerRadius);
+ QuikVec2 q2 = new QuikVec2(rectangle.Max.X - cornerRadius, rectangle.Min.Y + cornerRadius);
+ QuikVec2 q3 = new QuikVec2(rectangle.Max.X - cornerRadius, rectangle.Max.Y - cornerRadius);
+ QuikVec2 q4 = new QuikVec2(rectangle.Min.X + cornerRadius, rectangle.Max.Y - cornerRadius);
+
+ QuikVertex baseVertex = new QuikVertex() {Color = stroke.Color};
+ QuikVertex a = baseVertex, b = baseVertex, c = baseVertex, d = baseVertex,
+ e = baseVertex, f = baseVertex, g = baseVertex, h = baseVertex,
+ i = baseVertex, j = baseVertex, k = baseVertex, l = baseVertex,
+ m = baseVertex, n = baseVertex, o = baseVertex, p = baseVertex;
+
+ a.Position = new QuikVec2(q1.X, q1.Y - innerRadius);
+ b.Position = new QuikVec2(q2.X, q2.Y - innerRadius);
+ c.Position = new QuikVec2(q2.X + innerRadius, q2.Y);
+ d.Position = new QuikVec2(q3.X + innerRadius, q3.Y);
+ e.Position = new QuikVec2(q3.X, q3.Y + innerRadius);
+ f.Position = new QuikVec2(q4.X, q4.Y + innerRadius);
+ g.Position = new QuikVec2(q4.X - innerRadius, q4.Y);
+ h.Position = new QuikVec2(q1.X - innerRadius, q1.Y);
+
+ i.Position = new QuikVec2(q1.X, rectangle.Bottom - semiStroke);
+ j.Position = new QuikVec2(q2.X, rectangle.Bottom - semiStroke);
+ k.Position = new QuikVec2(rectangle.Right + semiStroke, q2.Y);
+ l.Position = new QuikVec2(rectangle.Right + semiStroke, q3.Y);
+ m.Position = new QuikVec2(q3.X, rectangle.Top + semiStroke);
+ n.Position = new QuikVec2(q4.X, rectangle.Top + semiStroke);
+ o.Position = new QuikVec2(rectangle.Left - semiStroke, q4.Y);
+ p.Position = new QuikVec2(rectangle.Left - semiStroke, q1.Y);
+
+ AddVertex(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
+ AddElement(
+ (short)(start + 8), (short)(start + 1), (short)(start + 0),
+ (short)(start + 8), (short)(start + 9), (short)(start + 1),
+ (short)(start + 2), (short)(start + 11), (short)(start + 3),
+ (short)(start + 2), (short)(start + 10), (short)(start + 11),
+ (short)(start + 5), (short)(start + 12), (short)(start + 13),
+ (short)(start + 5), (short)(start + 4), (short)(start + 12),
+ (short)(start + 15), (short)(start + 6), (short)(start + 14),
+ (short)(start + 15), (short)(start + 7), (short)(start + 6)
+ );
+
+ int resolution = GetRoundingResolution(outerRadius, 0.5f * MathF.PI);
+ short last0, last1;
+ short next0, next1;
+
+ last0 = (short) (start + 3);
+ last1 = (short) (start + 11);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVec2 normal = new QuikVec2(MathF.Cos(angle), MathF.Sin(angle));
+ QuikVertex v0 = baseVertex, v1 = baseVertex;
+
+ v0.Position = q3 + innerRadius * normal;
+ v1.Position = q3 + outerRadius * normal;
+
+ next0 = (short) (_vertexBufferPointer + 0);
+ next1 = (short) (_vertexBufferPointer + 1);
+
+ AddVertex(v0, v1);
+ AddElement(
+ last0, next1, next0,
+ last0, last1, next1);
+
+ last0 = next0;
+ last1 = next1;
+ }
+
+ AddElement(
+ last0, (short)(start + 12), (short)(start + 4),
+ last0, last1, (short)(start + 12)
+ );
+
+ last0 = (short) (start + 5);
+ last1 = (short) (start + 13);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVec2 normal = new QuikVec2(-MathF.Sin(angle), MathF.Cos(angle));
+ QuikVertex v0 = baseVertex, v1 = baseVertex;
+
+ v0.Position = q4 + innerRadius * normal;
+ v1.Position = q4 + outerRadius * normal;
+
+ next0 = (short) (_vertexBufferPointer + 0);
+ next1 = (short) (_vertexBufferPointer + 1);
+
+ AddVertex(v0, v1);
+ AddElement(
+ last0, next1, next0,
+ last0, last1, next1);
+
+ last0 = next0;
+ last1 = next1;
+ }
+
+ AddElement(
+ last0, (short)(start + 14), (short)(start + 6),
+ last0, last1, (short)(start + 14)
+ );
+
+ last0 = (short) (start + 7);
+ last1 = (short) (start + 15);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVec2 normal = new QuikVec2(-MathF.Cos(angle), -MathF.Sin(angle));
+ QuikVertex v0 = baseVertex, v1 = baseVertex;
+
+ v0.Position = q1 + innerRadius * normal;
+ v1.Position = q1 + outerRadius * normal;
+
+ next0 = (short) (_vertexBufferPointer + 0);
+ next1 = (short) (_vertexBufferPointer + 1);
+
+ AddVertex(v0, v1);
+ AddElement(
+ last0, next1, next0,
+ last0, last1, next1);
+
+ last0 = next0;
+ last1 = next1;
+ }
+
+ AddElement(
+ last0, (short)(start + 8), (short)(start + 0),
+ last0, last1, (short)(start + 8)
+ );
+
+ last0 = (short) (start + 1);
+ last1 = (short) (start + 9);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ QuikVec2 normal = new QuikVec2(MathF.Sin(angle), -MathF.Cos(angle));
+ QuikVertex v0 = baseVertex, v1 = baseVertex;
+
+ v0.Position = q2 + innerRadius * normal;
+ v1.Position = q2 + outerRadius * normal;
+
+ next0 = (short) (_vertexBufferPointer + 0);
+ next1 = (short) (_vertexBufferPointer + 1);
+
+ AddVertex(v0, v1);
+ AddElement(
+ last0, next1, next0,
+ last0, last1, next1);
+
+ last0 = next0;
+ last1 = next1;
+ }
+
+ AddElement(
+ last0, (short)(start + 10), (short)(start + 2),
+ last0, last1, (short)(start + 10)
+ );
+ }
+
+ private void GenerateRectangleRound(
+ QuikRectangle rectangle,
+ QuikFillStyle fill,
+ float radius)
+ {
+ short baseElement = (short) _vertexBufferPointer;
+ QuikVertex baseVertex = new QuikVertex() {Color = fill.Color};
+ QuikVertex a = baseVertex,
+ b = baseVertex,
+ c = baseVertex,
+ d = baseVertex,
+ e = baseVertex,
+ f = baseVertex,
+ g = baseVertex,
+ h = baseVertex,
+ i = baseVertex,
+ j = baseVertex,
+ k = baseVertex,
+ l = baseVertex;
+
+ // Generate base 5 patches.
+
+ a.Position = new QuikVec2(
+ rectangle.Left + radius,
+ rectangle.Bottom + radius);
+ b.Position = new QuikVec2(
+ rectangle.Right - radius,
+ rectangle.Bottom + radius);
+ c.Position = new QuikVec2(
+ rectangle.Right - radius,
+ rectangle.Top - radius);
+ d.Position = new QuikVec2(
+ rectangle.Left + radius,
+ rectangle.Top - radius);
+ e.Position = new QuikVec2(
+ rectangle.Left + radius,
+ rectangle.Bottom);
+ f.Position = new QuikVec2(
+ rectangle.Right - radius,
+ rectangle.Bottom);
+ g.Position = new QuikVec2(
+ rectangle.Right,
+ rectangle.Bottom + radius);
+ h.Position = new QuikVec2(
+ rectangle.Right,
+ rectangle.Top - radius);
+ i.Position = new QuikVec2(
+ rectangle.Right - radius,
+ rectangle.Top);
+ j.Position = new QuikVec2(
+ rectangle.Left + radius,
+ rectangle.Top);
+ k.Position = new QuikVec2(
+ rectangle.Left,
+ rectangle.Top - radius);
+ l.Position = new QuikVec2(
+ rectangle.Left,
+ rectangle.Bottom + radius);
+
+ AddVertex(a, b, c, d, e, f, g, h, i, j, k, l);
+ AddElement(
+ (short) (baseElement + 0), (short) (baseElement + 1), (short) (baseElement + 2),
+ (short) (baseElement + 0), (short) (baseElement + 2), (short) (baseElement + 3),
+ (short) (baseElement + 4), (short) (baseElement + 5), (short) (baseElement + 1),
+ (short) (baseElement + 4), (short) (baseElement + 1), (short) (baseElement + 0),
+ (short) (baseElement + 1), (short) (baseElement + 6), (short) (baseElement + 7),
+ (short) (baseElement + 1), (short) (baseElement + 7), (short) (baseElement + 2),
+ (short) (baseElement + 3), (short) (baseElement + 2), (short) (baseElement + 8),
+ (short) (baseElement + 3), (short) (baseElement + 8), (short) (baseElement + 9),
+ (short) (baseElement + 11), (short) (baseElement + 0), (short) (baseElement + 3),
+ (short) (baseElement + 11), (short) (baseElement + 3), (short) (baseElement + 10));
+
+ // Now generate corner patches.
+
+ int resolution = GetRoundingResolution(radius, 0.5f * MathF.PI);
+ short focus, last, current;
+
+ focus = (short) (baseElement + 2);
+ last = (short) (baseElement + 7);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ QuikVertex vertex = baseVertex;
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ vertex.Position = c.Position + radius * new QuikVec2(MathF.Cos(angle), MathF.Sin(angle));
+ current = (short) _vertexBufferPointer;
+
+ AddVertex(vertex);
+ AddElement(focus, last, current);
+
+ last = current;
+ }
+
+ AddElement(focus, last, (short) (baseElement + 8));
+
+
+ focus = (short) (baseElement + 3);
+ last = (short) (baseElement + 9);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ QuikVertex vertex = baseVertex;
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ vertex.Position = d.Position + radius * new QuikVec2(-MathF.Sin(angle), MathF.Cos(angle));
+ current = (short) _vertexBufferPointer;
+
+ AddVertex(vertex);
+ AddElement(focus, last, current);
+
+ last = current;
+ }
+
+ AddElement(focus, last, (short) (baseElement + 10));
+
+ focus = (short) (baseElement + 0);
+ last = (short) (baseElement + 11);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ QuikVertex vertex = baseVertex;
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ vertex.Position = a.Position + radius * new QuikVec2(-MathF.Cos(angle), -MathF.Sin(angle));
+ current = (short) _vertexBufferPointer;
+
+ AddVertex(vertex);
+ AddElement(focus, last, current);
+
+ last = current;
+ }
+
+ AddElement(focus, last, (short) (baseElement + 4));
+
+ focus = (short) (baseElement + 1);
+ last = (short) (baseElement + 5);
+ for (int idx = 0; idx < resolution; idx++)
+ {
+ QuikVertex vertex = baseVertex;
+ float angle = 0.5f * MathF.PI * (idx + 1) / (resolution + 1);
+ vertex.Position = b.Position + radius * new QuikVec2(MathF.Sin(angle), -MathF.Cos(angle));
+ current = (short) _vertexBufferPointer;
+
+ AddVertex(vertex);
+ AddElement(focus, last, current);
+
+ last = current;
+ }
+
+ AddElement(focus, last, (short) (baseElement + 6));
+ }
+
+ private void GenerateRectangleBorderSimple(QuikRectangle rectangle, QuikStrokeStyle stroke)
+ {
+ QuikVertex baseStrokeVertex = new QuikVertex() {Color = stroke.Color};
+ float semiStroke = 0.5f * stroke.Width;
+
+ // AB
+ GenerateLineSegment(
+ baseStrokeVertex,
+ new QuikLine(
+ rectangle.Min.X + semiStroke,
+ rectangle.Min.Y,
+ rectangle.Max.X - semiStroke,
+ rectangle.Min.Y
+ ),
+ stroke.Width,
+ out _,
+ out _);
+
+ // BC
+ GenerateLineSegment(
+ baseStrokeVertex,
+ new QuikLine(
+ rectangle.Max.X,
+ rectangle.Min.Y - semiStroke,
+ rectangle.Max.X,
+ rectangle.Max.Y + semiStroke
+ ),
+ stroke.Width,
+ out _,
+ out _);
+
+ // CD
+ GenerateLineSegment(
+ baseStrokeVertex,
+ new QuikLine(
+ rectangle.Max.X - semiStroke,
+ rectangle.Max.Y,
+ rectangle.Min.X + semiStroke,
+ rectangle.Max.Y
+ ),
+ stroke.Width,
+ out _,
+ out _);
+
+ // DA
+ GenerateLineSegment(
+ baseStrokeVertex,
+ new QuikLine(
+ rectangle.Min.X,
+ rectangle.Max.Y + semiStroke,
+ rectangle.Min.X,
+ rectangle.Min.Y - semiStroke
+ ),
+ stroke.Width,
+ out _,
+ out _);
+ }
+
+ private void GenerateRectangleSimple(
+ QuikRectangle rectangle,
+ QuikFillStyle fill)
+ {
+ QuikVertex a, b, c, d;
+ a = b = c = d = new QuikVertex() {Color = fill.Color};
+ a.Position = rectangle.Min;
+ b.Position = new QuikVec2(rectangle.Right, rectangle.Bottom);
+ c.Position = rectangle.Max;
+ d.Position = new QuikVec2(rectangle.Left, rectangle.Top);
+
+ short idxA = (short) _vertexBufferPointer;
+ short idxB = (short) (idxA + 1);
+ short idxC = (short) (idxA + 2);
+ short idxD = (short) (idxA + 3);
+
+ AddVertex(a, b, c, d);
+ AddElement(
+ idxA,
+ idxB,
+ idxC,
+ idxA,
+ idxC,
+ idxD
+ );
+ }
+
+ private void RenderRectangles(ref QuikDrawCall call, QuikCommandRectangles rectangles)
+ {
+ QuikStrokeStyle stroke = rectangles.StrokeStyle ?? Context.DefaultStroke;
+ QuikFillStyle fill = rectangles.FillStyle ?? Context.DefaultFill;
+
+ short start = (short) _elementBufferPointer;
+
+ for (int i = 0; i < rectangles.Rectangles.Length; i++)
+ {
+ GenerateRectangle(rectangles.Rectangles[i], stroke, fill, rectangles.CornerRadius);
+ }
+
+ call.Offset = (short) (start * 2);
+ call.Count = (short) (_elementBufferPointer - start);
+ }
+
+ #endregion
}
+ public delegate void VertexGeneratorCommandHandler(QuikVertexGenerator generator, QuikCommand command, ref QuikDrawCall? call);
+
public enum QuikRenderTarget
{
Color,
diff --git a/QuikTestApplication/Program.cs b/QuikTestApplication/Program.cs
index 2941e52..13b12bb 100644
--- a/QuikTestApplication/Program.cs
+++ b/QuikTestApplication/Program.cs
@@ -115,6 +115,24 @@ void main()
loc = GL.GetUniformLocation(sp, "matrix");
List calls = new List();
+ int i = 0;
+
+ QuikStrokeStyle strokeBorder = new QuikStrokeStyle()
+ {
+ Color = new QuikColor(0xaaaaaaff),
+ Width = 8
+ };
+
+ QuikStrokeStyle strokeNoBorder = new QuikStrokeStyle()
+ {
+ Color = new QuikColor(0xaaaaaaff),
+ Width = 0
+ };
+
+ QuikFillStyle fill = new QuikFillStyle()
+ {
+ Color = new QuikColor(0xeeeeeeff)
+ };
for (;!window.IsExiting;)
{
@@ -134,32 +152,53 @@ void main()
-1);
GL.UniformMatrix4(loc, false, ref matrix);
- context.DefaultStroke.Color = new QuikColor(40, 128, 60, 255);
+ context.Draw.Commands.Enqueue(
+ new QuikCommandRectangle(new QuikRectangle(120, 60, 20, 20))
+ {
+ CornerRadius = 0,
+ FillStyle = fill,
+ StrokeStyle = strokeNoBorder
+ });
- context.Draw.Line(
- // >
- new QuikLine(10,10, 20, 20),
- new QuikLine(20, 20, 10, 30),
-
- // s
- new QuikLine(28, 10, 38, 10),
- new QuikLine(38, 10, 38, 20),
- new QuikLine(38, 20, 28, 20),
- new QuikLine(28, 20, 28, 30),
- new QuikLine(28, 30, 38, 30),
-
- // e
- new QuikLine(64, 20, 74, 20),
- new QuikLine(74, 20, 74, 30),
- new QuikLine(74, 30, 64, 30),
- new QuikLine(64, 30, 64, 10),
- new QuikLine(64, 10, 74, 10)
- );
+ context.Draw.Commands.Enqueue(
+ new QuikCommandRectangle(new QuikRectangle(240, 60, 140, 20))
+ {
+ CornerRadius = 4,
+ FillStyle = fill,
+ StrokeStyle = strokeNoBorder
+ });
- context.Draw.Bezier(
- new QuikBezier(46, 10, 56, 10, 46, 30, 56, 30),
- new QuikBezier(56, 30, 46, 30, 56, 60, 46, 60),
- new QuikBezier(46, 60, 56, 60, 46, 90, 56, 90));
+ context.Draw.Commands.Enqueue(
+ new QuikCommandRectangle(new QuikRectangle(360, 60, 260, 20))
+ {
+ CornerRadius = 15,
+ FillStyle = fill,
+ StrokeStyle = strokeNoBorder
+ });
+
+ context.Draw.Commands.Enqueue(
+ new QuikCommandRectangle(new QuikRectangle(120, 120, 20, 80))
+ {
+ CornerRadius = 0,
+ FillStyle = fill,
+ StrokeStyle = strokeBorder
+ });
+
+ context.Draw.Commands.Enqueue(
+ new QuikCommandRectangle(new QuikRectangle(240, 120, 140, 80))
+ {
+ CornerRadius = 4,
+ FillStyle = fill,
+ StrokeStyle = strokeBorder
+ });
+
+ context.Draw.Commands.Enqueue(
+ new QuikCommandRectangle(new QuikRectangle(360, 120, 260, 80))
+ {
+ CornerRadius = 15,
+ FillStyle = fill,
+ StrokeStyle = strokeBorder
+ });
QuikCommand command;
while (context.Draw.Commands.TryDequeue(out command))
@@ -179,7 +218,14 @@ void main()
gen.Clear();
calls.Clear();
+ if (++i % 60 == 0)
+ {
+ Console.WriteLine("Vertex Usage: {0} ; Element Usage: {1}", gen.VertexUsage, gen.ElementUsage);
+ }
+
window.Context.SwapBuffers();
+
+
}
}
}