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;
/// <summary>
/// Rectangle minimum point.
/// </summary>
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.
/// </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);
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
/// <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),
// 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();
}
}
}