-
Notifications
You must be signed in to change notification settings - Fork 0
Tutorial – Drawing Shapes
In this tutorial we will draw shapes.
Ensure you've covered 'Getting Started' before following this guide.
These tutorials are written assuming that the program is running on NET Core 3.0
. The libraries themselves are NET Standard 2.1
compliant. If you don't have .NET Core 3.0 (or newer), then you can download it here.
In Heirloom, there are various shape types. They are Circle
, Triangle
, Rectangle
and Polygon
. In addition to these types, there exist a Mesh
type for rendering complex geometry.
The following example code will act as a basis for this article. It draws a simple white circle on a window.
using Heirloom.Desktop;
using Heirloom;
namespace Tutorial
{
public class Program
{
public static float Time;
private static void Main(string[] args)
{
Application.Run(() =>
{
// Construct a window with 4x multisampling
var window = new Window("Tutorial", (800, 480), MultisampleQuality.Medium);
window.MoveToCenter();
// Begin the update loop
var loop = GameLoop.Create(window.Graphics, OnUpdate);
loop.Start();
});
}
private static void OnUpdate(GraphicsContext gfx, float dt)
{
gfx.Clear(Color.DarkGray);
Time += dt;
// Compute the center of the screen
var center = new Vector
{
X = gfx.Surface.Width / 2F,
Y = gfx.Surface.Height / 2F
};
// Draw the circle
gfx.Color = Color.White;
gfx.DrawCircle(center, 200);
}
}
}
When the code above is ran, it should look like the following:
In addition we can use the circle struct to draw as well, change the code with the following. We will replace DrawCircle
with DrawCircleOutline
and set a line width of 3
.
// We can create a circle struct too
var circle = new Circle(center, 200);
// Draw the circle
gfx.Color = Color.White;
gfx.DrawCircleOutline(circle, 3);
If you run the application now, it should look like the image below.
Each primitive drawing function in Heirloom comes in pairs for the solid and outline version (ie, DrawTriangle
and DrawTriangleOutline
). With that said, let's move onto triangles.
Continuing with the code drawing the circle outline, add the following to your source code.
// Compute an animated triangle
var triangle = new Triangle
{
A = center + Vector.FromAngle(Time + Calc.TwoPi * (0 / 3F)) * 200,
B = center + Vector.FromAngle(Time + Calc.TwoPi * (1 / 3F)) * 200,
C = center + Vector.FromAngle(Time + Calc.TwoPi * (2 / 3F)) * 200,
};
// Draw a triangle
gfx.Color = Color.Violet;
gfx.DrawTriangle(triangle);
gfx.Color = Color.White;
gfx.DrawTriangleOutline(triangle, 3);
Again, when ran it should look like the follow (but you know, actually rotating).
It is difficult for me to come up with clever or fun ways to introduce different primitive shapes supported by Heirloom. The remainder of this article will largely just be code snippets and associated images. While that might not be exciting, hopefully it will be useful.
Here we draw a 5 sided regular polygon (also known as a pentagon).
// Draw a pentagon
gfx.Color = Color.White;
gfx.DrawPolygon(center, 5, 200);
In this snippet, we draw a simple polygon defined by the points. They define a blocky C shape.
var points = new Vector[]
{
center + new Vector(-200, -200),
center + new Vector(+200, -200),
center + new Vector(+200, -100),
center + new Vector(-100, -100),
center + new Vector(-100, +100),
center + new Vector(+200, +100),
center + new Vector(+200, +200),
center + new Vector(-200, +200),
};
// Draw the polygon
gfx.Color = Color.White;
gfx.DrawPolygon(points);
In addition to drawing from a set of points, a Polygon
type exists. This type provides extra information about the polygon such as edge normals, area and centroid. Heirloom also contains the tools to triangulate or decompose simple concave polygons into convex partitions.
To demonstrate the Mesh
object, we will decompose the previous polygon into triangles and map an image onto it.
The first thing to do is add the following static field to our program.
public static Mesh Mesh;
Second, add the following before the code that creates the game loop:
// Construct a new polygon
var polygon = new Polygon(new Vector[]
{
new Vector(-200, -200),
new Vector(+200, -200),
new Vector(+200, -100),
new Vector(-100, -100),
new Vector(-100, +100),
new Vector(+200, +100),
new Vector(+200, +200),
new Vector(-200, +200),
});
Next we need to take advantage of the aformentioned triangulation feature. Add the following immediately after the previous snippet:
// Construct a new mesh
Mesh = new Mesh();
// Triangulate and add the triangles to the mesh instance
foreach (var (a, b, c) in polygon.Triangulate())
{
var i = Mesh.Vertices.Count;
// Add the vertices of this face
Mesh.AddVertex(new Vertex(a, (0F, 0F)));
Mesh.AddVertex(new Vertex(b, (1F, 0F)));
Mesh.AddVertex(new Vertex(c, (1F, 1F)));
// Define a face with these vertices
Mesh.AddTriangle(i + 0, i + 1, i + 2);
}
This piece of code takes the simple polygon, triangulates it and appends triangle faces to a mesh object. If we run the code now, we will see the following purple shape.
Yikes, the image is stretched funny on the triangles. Can we do any better? The answer is yes, we need to compute improved UV coordintes for each vertex. The values in the previous snippet were just a quick and dirty choice to get something on the screen.
Lets compute some improved UV coordinates based on the bounding rectangle of this polygon.
var bounds = polygon.Bounds;
// Triangulate and add the triangles to the mesh instance
foreach (var (a, b, c) in polygon.Triangulate())
{
var i = Mesh.Vertices.Count;
// Add the vertices of this face
Mesh.AddVertex(new Vertex(a, (a - bounds.Min) / (Vector) bounds.Size));
Mesh.AddVertex(new Vertex(b, (b - bounds.Min) / (Vector) bounds.Size));
Mesh.AddVertex(new Vertex(c, (c - bounds.Min) / (Vector) bounds.Size));
// Define a face with these vertices
Mesh.AddTriangle(i + 0, i + 1, i + 2);
}
And voila!
A Mesh
with an image mapped to it. Obviously more sophisticated UV mappings are possible.
Copyright © 2020 Chris Chamberlain
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
-
The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
-
Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-
This notice may not be removed or altered from any source distribution.