internal unsafe void Launch() { Console.WriteLine("Tessellation..."); Console.WriteLine(" Left click to add a point."); Console.WriteLine(" 'c' to close the current polyline and start a new one."); Console.WriteLine(" 's' stroke the polyline."); Console.WriteLine(" 't' to tessellate the polygon."); Console.WriteLine(" 'r' reset the polygon."); using var windowServer = new WindowServer(); using var renderingContext = new RenderingContext { Settings = new RenderingSettings { Samples = 8 } }; using var window = windowServer.CreateWindow(renderingContext); window.Title = "Tessellation"; window.BorderStyle = WindowBorderStyle.Sizable; window.Size = (800, 800); renderingContext.CurrentWindow = window; var viewportSize = window.ViewportSize; var windowSize = window.Size; var backend = new DrawingBackend(new EntryPointLoader(renderingContext)) { PixelScaling = viewportSize.height / windowSize.height }; var dataLock = new object(); var vertices = new List <(double x, double y)>(); List <List <(double x, double y)> >?polygon = new List <List <(double x, double y)> >(); var contour = default(List <(double x, double y)>); var colors = new RgbaColor[] { new RgbaColor(255, 255, 255, 255), new RgbaColor(110, 100, 70, 200), new RgbaColor(130, 30, 50, 200), new RgbaColor(150, 90, 80, 200), new RgbaColor(170, 40, 40, 200), new RgbaColor(190, 80, 90, 200), new RgbaColor(210, 50, 30, 200), new RgbaColor(230, 70, 100, 200), }; var buffer = new PointCoordinates[6]; var vertexBuffer = backend.CreateVertexBuffer(VertexType.PointCoordinates, 6); var colorBufferHandle = backend.CreateUniformBuffer(UniformType.RgbaColor, colors.Length); var transformBufferHandle = backend.CreateUniformBuffer(UniformType.AffineTransform, 1); var commandBufferInit = backend.CreateCommandBuffer(); backend.BeginRecordCommands(commandBufferInit); backend.AddClearCommand(commandBufferInit, new RgbaColor(55, 55, 55, 255)); backend.AddUseShaderCommand(commandBufferInit, ShaderKind.PlainColor); backend.AddBindUniformCommand(commandBufferInit, Uniform.Color, colorBufferHandle, 0); backend.AddBindUniformCommand(commandBufferInit, Uniform.Transform, transformBufferHandle, 0); backend.AddBindVertexBufferCommand(commandBufferInit, vertexBuffer); backend.EndRecordCommands(commandBufferInit); var bindColorCommandBuffers = new CommandBufferHandle[colors.Length]; for (int i = 1; i < colors.Length; i++) { bindColorCommandBuffers[i - 1] = backend.CreateCommandBuffer(); backend.BeginRecordCommands(bindColorCommandBuffers[i - 1]); backend.AddBindUniformCommand(bindColorCommandBuffers[i - 1], Uniform.Color, colorBufferHandle, i); backend.EndRecordCommands(bindColorCommandBuffers[i - 1]); } var commandBufferDrawTriangle = backend.CreateCommandBuffer(); backend.BeginRecordCommands(commandBufferDrawTriangle); backend.AddDrawCommand(commandBufferDrawTriangle, DrawingPrimitive.Triangles, 0, 3); backend.EndRecordCommands(commandBufferDrawTriangle); var commandBufferDrawTriangles = backend.CreateCommandBuffer(); backend.BeginRecordCommands(commandBufferDrawTriangles); backend.AddDrawCommand(commandBufferDrawTriangles, DrawingPrimitive.Triangles, 0, 6); backend.EndRecordCommands(commandBufferDrawTriangles); var drawLineStripArgsBufferHandle = backend.CreateVertexRangeBuffer(1); var drawLineStripCommandBuffer = backend.CreateCommandBuffer(); backend.BeginRecordCommands(drawLineStripCommandBuffer); backend.AddDrawIndirectCommand(drawLineStripCommandBuffer, DrawingPrimitive.LineStrip, drawLineStripArgsBufferHandle, 0); backend.EndRecordCommands(drawLineStripCommandBuffer); var vertexRanges = new VertexRange[1]; var commandBufferHandles = new CommandBufferHandle[2]; var transforms = new AffineTransform[1]; transforms[0] = new AffineTransform(m11: 1, m22: 1); var drawLock = new object(); var interruptRendering = false; var requireRendering = true; void draw() { while (!interruptRendering) { if (requireRendering) { requireRendering = false; renderingContext.CurrentWindow = window; backend.PixelScaling = viewportSize.height / windowSize.height; backend.WindowSize = windowSize; backend.UpdateUniformBuffer(colorBufferHandle, colors, 0, colors.Length); backend.BeginRenderFrame(); backend.UpdateUniformBuffer(transformBufferHandle, transforms, 0, 1); commandBufferHandles[0] = commandBufferInit; backend.SubmitCommands(commandBufferHandles, 0, 1); lock (dataLock) { if (polygon != null) { for (int j = 0; j < polygon.Count; j++) { var c = polygon[j]; if (c.Count == 1) { commandBufferHandles[0] = commandBufferDrawTriangles; var(x, y) = c[0]; var x0 = x - 2; var y0 = y - 2; var x1 = x + 2; var y1 = y - 2; var x2 = x + 2; var y2 = y + 2; var x3 = x - 2; var y3 = y + 2; buffer[0] = new PointCoordinates(x0, y0); buffer[1] = new PointCoordinates(x1, y1); buffer[2] = new PointCoordinates(x2, y2); buffer[3] = new PointCoordinates(x2, y2); buffer[4] = new PointCoordinates(x3, y3); buffer[5] = new PointCoordinates(x0, y0); backend.UpdateVertexBuffer(vertexBuffer, buffer, 0, 6); backend.SubmitCommands(commandBufferHandles, 0, 1); } else { commandBufferHandles[0] = drawLineStripCommandBuffer; vertexRanges[0] = new VertexRange(0, 6); backend.UpdateVertexRangeBuffer(drawLineStripArgsBufferHandle, vertexRanges, 0, 1); var n = 0; for (int i = 0; i < c.Count; i++) { var(x, y) = c[i]; buffer[n] = new PointCoordinates(x, y); if (++n == 6) { backend.UpdateVertexBuffer(vertexBuffer, buffer, 0, 6); backend.SubmitCommands(commandBufferHandles, 0, 1); buffer[0] = new PointCoordinates(x, y); n = 1; } } if (j < polygon.Count - 1 || contour == null) { var(x, y) = c[0]; buffer[n] = new PointCoordinates(x, y); ++n; } if (n > 1) { backend.UpdateVertexBuffer(vertexBuffer, buffer, 0, n); vertexRanges[0] = new VertexRange(0, n); backend.UpdateVertexRangeBuffer(drawLineStripArgsBufferHandle, vertexRanges, 0, 1); backend.SubmitCommands(commandBufferHandles, 0, 1); } } } } else { commandBufferHandles[1] = commandBufferDrawTriangle; for (int i = 0; i < vertices.Count - 2; i += 3) { commandBufferHandles[0] = bindColorCommandBuffers[((i / 3) % (colors.Length - 1))]; var(x, y) = vertices[i]; buffer[0] = new PointCoordinates(x, y); (x, y) = vertices[i + 1]; buffer[1] = new PointCoordinates(x, y); (x, y) = vertices[i + 2]; buffer[2] = new PointCoordinates(x, y); backend.UpdateVertexBuffer(vertexBuffer, buffer, 0, 3); backend.SubmitCommands(commandBufferHandles, 0, 2); } } } backend.EndRenderFrame(); renderingContext.SwapBuffers(window); renderingContext.CurrentWindow = null; } lock (drawLock) { Monitor.Wait(drawLock, 50); } } } window.TextInput += (sender, e) => { switch (e.CodePoint) { case 'c': lock (dataLock) { if (contour != null && contour.Count > 2) { contour.Add(contour[0]); } contour = null; } requireRendering = true; break; case 'r': lock (dataLock) { polygon = new List <List <(double x, double y)> >(); contour = null; vertices.Clear(); } requireRendering = true; break; case 's': { var p = new List <List <(double x, double y)> >(); var polylineStroker = new PolylineStroker(new StrokeHandler(p)) { StrokeWidth = 20, StrokeLinecap = StrokeLinecap.Round, StrokeLineJoin = StrokeLineJoin.Round }; foreach (var c in polygon) { polylineStroker.Stroke(c); } contour = null; polygon = p; } requireRendering = true; break; case 't': if (vertices.Count == 0) { lock (dataLock) { var tessellator = new Tessellator2D <int, int>(new TessellateHandler(vertices)) { OutputKind = OutputKind.TrianglesOnly, WindingRule = WindingRule.NonZero }; tessellator.BeginPolygon(0); foreach (var c in polygon) { tessellator.BeginContour(); foreach (var(x, y) in c) { tessellator.AddVertex(x, y, 0); } tessellator.EndContour(); } tessellator.EndPolygon(); vertices.Clear(); tessellator.OutputKind = OutputKind.TriangleEnumerator; tessellator.BeginPolygon(0); foreach (var c in polygon) { tessellator.BeginContour(); foreach (var(x, y) in c) { tessellator.AddVertex(x, y, 0); } tessellator.EndContour(); } var sw = new System.Diagnostics.Stopwatch(); var f = System.Diagnostics.Stopwatch.Frequency; sw.Start(); tessellator.EndPolygon(); while (tessellator.Move()) { var(x, y, _) = tessellator.Vertex; vertices.Add((x, y)); } sw.Stop(); Console.WriteLine("Tessellated " + (vertices.Count / 3) + " triangles in " + (sw.ElapsedTicks * 1e3 / f).ToString("0.000") + "ms"); polygon = null; } } requireRendering = true; break; } }; window.MouseDown += (sender, e) => { lock (dataLock) { if (e.ChangedButton == MouseButton.Left && polygon != null) { if (contour == null) { contour = new List <(double x, double y)>(); polygon.Add(contour); } else if (window.CursorPosition.x == contour[contour.Count - 1].x && window.CursorPosition.y == contour[contour.Count - 1].y) { return; } contour.Add(window.CursorPosition); requireRendering = true; } } }; void resize() { viewportSize = window.ViewportSize; windowSize = window.Size; requireRendering = true; lock (drawLock) { Monitor.Pulse(drawLock); } } var runLoop = MainRunLoop.Create(windowServer); var drawThread = new Thread(draw); window.Closed += (sender, e) => { interruptRendering = true; drawThread.Join(); runLoop.Interrupt(); backend.Dispose(); }; window.Resize += (sender, e) => resize(); window.FramebufferResize += (sender, e) => resize(); renderingContext.CurrentWindow = null; drawThread.Start(); window.Visible = true; runLoop.Run(); Console.WriteLine("Tessellation done."); }