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