Implement rectangle rendering.

This commit is contained in:
H. Utku Maden 2022-08-16 21:35:12 +03:00
parent 16bb3f41a7
commit 72cad718cb
5 changed files with 812 additions and 57 deletions

@ -14,7 +14,7 @@
public QuikFillStyle DefaultFill { get; set; } = new QuikFillStyle()
{
Color = new QuikColor(0x101010FF)
Color = new QuikColor(0xA0A0A0FF)
};
}
}

@ -1,10 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
namespace Quik
{
/// <summary>
/// A 2 dimensional Vector.
/// </summary>
[DebuggerDisplay("({X}, {Y})")]
public struct QuikVec2
{
public float X;
@ -89,6 +92,7 @@ namespace Quik
/// <summary>
/// A RGBA color value.
/// </summary>
[DebuggerDisplay("({R}, {G}, {B}, {A})")]
public struct QuikColor
{
/// <summary>
@ -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) { }
}
/// <summary>
/// A bezier curve segment.
/// </summary>
[DebuggerDisplay("{Start} -- {ControlA} -- {ControlB} -- {End}")]
public struct QuikBezier
{
/// <summary>
@ -217,6 +223,7 @@ namespace Quik
/// <summary>
/// A line segment.
/// </summary>
[DebuggerDisplay("{Start} -- {End}")]
public struct QuikLine
{
/// <summary>
@ -247,22 +254,59 @@ namespace Quik
/// <summary>
/// A rectangle.
/// </summary>
[DebuggerDisplay("({Right}, {Top}, {Left}, {Bottom})")]
public struct QuikRectangle
{
/// <summary>
/// Rectangle minimum point.
/// </summary>
public QuikVec2 Min;
/// <summary>
/// Rectangle maximum point.
/// </summary>
public QuikVec2 Max;
public QuikRectangle(float l, float t, float r, float b)
/// <summary>
/// Rectangle minimum point.
/// </summary>
public QuikVec2 Min;
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.
/// </summary>
/// <remarks>It is undefined to have an ellipse with non-orthogonal axes.</remarks>
[DebuggerDisplay("{Center} ellipse {AxisA}; {AxisB}")]
public struct QuikEllipse
{
/// <summary>
@ -291,6 +336,7 @@ namespace Quik
/// <summary>
/// A triangle.
/// </summary>
[DebuggerDisplay("{A} -- {B} -- {C}")]
public struct QuikTriangle
{
/// <summary>

@ -1,10 +1,11 @@
using System.Runtime.CompilerServices;
using System.Diagnostics;
namespace Quik.VertexGenerator
{
/// <summary>
/// Represents a GPU vertex.
/// </summary>
[DebuggerDisplay("XY={Position} RGBA={Color}, UV={TextureCoordinates}")]
public struct QuikVertex
{
/// <summary>

@ -30,7 +30,7 @@ namespace Quik.VertexGenerator
/// </summary>
private int _vertexBufferPointer = 0;
private float _vertexBufferUsage = 0;
private float _vertexUsage = 0;
/// <summary>
/// List of element indices.
@ -42,9 +42,19 @@ namespace Quik.VertexGenerator
/// </summary>
private int _elementBufferPointer = 0;
private float _elementBufferUsage;
private float _elementUsage;
private long _bufferUsageCounter;
/// <summary>
/// Moving average of the vertex count.
/// </summary>
public float VertexUsage => _vertexUsage;
/// <summary>
/// Moving average of the element count.
/// </summary>
public float ElementUsage => _elementUsage;
/// <summary>
/// Get a reference to the vertex buffer.
/// </summary>
@ -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);
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;
}
return null;
}
#region Lines & Beziers
/// <summary>
/// Generates a basic line segment.
@ -237,13 +286,12 @@ namespace Quik.VertexGenerator
/// <summary>
/// Gets the rounding resolution for a line segment.
/// </summary>
/// <param name="width">The width of the line.</param>
/// <param name="radius">The width of the line.</param>
/// <param name="arc">The angle of the cap or joint arc.</param>
/// <returns>The rounding resolution.</returns>
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);
}
/// <summary>
@ -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,

@ -115,6 +115,24 @@ void main()
loc = GL.GetUniformLocation(sp, "matrix");
List<QuikDrawCall> calls = new List<QuikDrawCall>();
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),
context.Draw.Commands.Enqueue(
new QuikCommandRectangle(new QuikRectangle(240, 60, 140, 20))
{
CornerRadius = 4,
FillStyle = fill,
StrokeStyle = strokeNoBorder
});
// 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),
context.Draw.Commands.Enqueue(
new QuikCommandRectangle(new QuikRectangle(360, 60, 260, 20))
{
CornerRadius = 15,
FillStyle = fill,
StrokeStyle = strokeNoBorder
});
// 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(120, 120, 20, 80))
{
CornerRadius = 0,
FillStyle = fill,
StrokeStyle = strokeBorder
});
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(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();
}
}
}