Implement rectangle rendering.
This commit is contained in:
parent
16bb3f41a7
commit
72cad718cb
@ -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();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user