XNA: Sophisticated primitives
Published on Thursday, December 30, 2010 12:11:00 AM UTC in Programming
When I was looking into how I can easily draw 2D geometry in XNA on the Windows Phone 7, I came across a Microsoft sample named "Primitives" which provides a PrimitiveBatch class that can be used to draw points, lines and triangles on the screen. This was basically what I wanted, but when I wanted to use it, I instantly ran into an exception.
The specified primitiveType is not supported...
Of course I wanted to use just the feature the sample didn't implement: strips. I liked the overall approach of the sample though, so I decided to put some work into it to support both line and triangle strips.
What are strips?
First of all some theory. To draw a line, you need two points to define that line:
Theoretically, you can use that to draw any kinds of lines. This is how the primitive type "Line List" works. For each line, you provide two points which then are connected by a line. When you want to draw multiple lines that are connected, you can use a different technique though:
Instead of providing a pair of points for each line, it's sufficient to provide one additional point for each new line and have the video card connect the new point to the last one. To draw the above lines, which would require six points using a line list, this only requires four points. That technique is called "Line Strip".
You might wonder why this is important. For small samples it probably doesn't matter which technique you're using, but imagine drawing thousands of lines. It makes a difference if you (or let's better say your video card) has to handle 10,000 points (which are called vertices) or only 5,001 (remember, the first line still needs two vertices).
This also works for triangles?
Yes, and the saving is even better for them. A single triangle needs three vertices to be drawn:
Now imagine you need to draw a rectangle (which is named quad in this context). With conventional methods, in particular the primitive type "Triangle List", you would need six vertices for that, because a quad consists of two triangles. With a triangle strip however, you only need four:
The trick here is to use two vertices from the already existing previous triangle and one new vertex to create a new triangle. This means that you can create as many additional triangles as necessary by adding a single vertex for each (the total number of vertices is reduced from 3N to N + 2).
Please note that the documentation on the PrimitiveType enumeration on MSDN is actually wrong for the triangle strip, as it says new triangles are "described by two new vertices and one vertex from the previous triangle". This is not true; it's the other way round, like I just described.
I won't go into more details, as handling the triangles correctly for rendering is a bit more complex (triangles have an orientation, as in front and back, which requires some more logic). Fortunately, the XNA runtime handles most of this for us.
The reworked PrimitiveBatch class
The "Primitives" sample internally uses a buffer and a flushing mechanism when the buffer runs full, and the comments in the source code suggest that this was the reason the support for strips was left out. The problem here is that once the buffer is flushed, you lose the reference to the previous primitive you need to draw the next one.
Fortunately, adding support for strips wasn't very hard to achieve. I just added some logic that copies over the last vertices to the new buffer iteration and starts over with a new strip. I think I've added enough comments to the source code to make it obvious how it works in detail. I've also added the possibility to feed the class with the built-in vertex structures of XNA directly (instead of separate Vector2D and Color objects).
How about a sample for demonstration?
Originally, the sample drew a star field, a sun and two space ships on the screen to simulate a retro game style. What I wanted the strip support for was to be able to effectively draw trails (some also call this ribbons). This Tron-like effect screams for something like a triangle strip, as it really only is an ever-growing series of triangles. That is why I changed the included sample to draw a 2D ribbon instead. Here's a screenshot of it:
Here is the source code for both the reworked PrimitiveBatch class as well as the sample above. The included solution is for XNA Game Studio 4.0 and targets Windows Phone 7. I hope some people will find this useful too.