
Scalable Vector Graphics (SVG) seemed an ideal format for authoring the enemy paths in Retrofit: Overload but it turned out to be much harder to parse than it appeared at first sight. Fortunately the open-source SVG Rendering Engine works very nicely for reading SVG files in our content pipeline. Here’s how we use it:
Although library source code is available, it’s sufficient to add the Svg.dll to the content importer project’s References. This makes the Svg namespace available. We also need references to System.Drawing and System.Xml.
Here’s some fairly standard preamble:
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content.Pipeline; using Svg; using System.Drawing.Drawing2D; // Alias our output type -- A dictionary mapping a path name to a list of // control points. using PathDict = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<Microsoft.Xna.Framework.Vector2>>; // The Svg library uses this matrix type not the XNA one: using Matrix = System.Drawing.Drawing2D.Matrix; namespace ContentPipeline { [ContentImporter(".svg", DisplayName = "SVG Path Importer")] public class SvgPathImporter : ContentImporter<PathDict> { // (I like to stash the filename at the start of my content // importers to make it easier to generate useful error messages in // nested calls.) string filename; public override PathDict Import( string filename, ContentImporterContext context) { this.filename = filename;
Now for the interesting stuff. Opening and parsing the SVG file is as simple as a single function call:
SvgDocument doc = SvgDocument.Open(filename);
Now we scan for path nodes and pull out the data. The collectPaths() function is defined later on.
var paths = new PathDict(); collectPaths(ref paths, doc); return paths; }
We’re only going to consider Bézier curves. Here’s how we test for them:
static bool isBezier(SvgPath path) { // All but the first point must be type '3' const int bezierPointId = 3; return path.Path.PathTypes.Skip(1).All(t => t == bezierPointId); }
SVG nodes may have multiple associated transforms. (This is one of the things that makes it impractical to throw together a quick parser for this stuff — There are many ways of representing transformations in SVG, and unless you know that your editor won’t use them, even the simplest SVG data is affected by them.) Fortunately SVGRE parses these for us and stores them with each node. The parsed points are stored untransformed though, so we need to accumulate the node’s transforms and apply them ourselves. Here’s a function to calculate the overall transform that applies to a node.
static Matrix getTransform(SvgElement node) { var m = new Matrix(); while (node != null) { if (node.Transforms != null && node.Transforms.Count > 0) { var temp = new Matrix(); foreach (var trans in node.Transforms) { temp.Multiply(trans.Matrix, MatrixOrder.Prepend); } m.Multiply(temp, MatrixOrder.Append); } node = node.Parent; } return m; }
We traverse the tree of SvgElement nodes recursively. If a node is of type SvgPath, and is a Bézier curve, then we process it and add it to our dictionary.
void collectPaths(ref PathDict paths, SvgElement e) { SvgPath path = e as SvgPath; if (path != null) { if (!isBezier(path)) { // Complain about any non-Bezier curves // so we can fix them in the editor. throw new InvalidContentException( String.Format("Cubic Beziers only: {0}", path.ID), new ContentIdentity(filename)); } // Transform points in-place. var pts = path.Path.PathPoints; getTransform(e).TransformPoints(pts); // Convert to Vector2. var vpts = pts.Select(p => new Vector2(p.X, p.Y)).ToList(); paths.Add(path.ID, vpts); } foreach (var child in e.Children) { collectPaths(ref paths, child); } } } }
That’s it. To load the path data in-game we use:
var paths = Content.Load<Dictionary<string, List<Vector2>>>("test");
Here’s the code in one handy chunk.
The same approach can be used to extract other SVG data. In fact in the Retrofit:Overload importer we look for a screen rectangle too and offset the paths relative to that.
The main disadvantage of SVG Rendering Engine is the lack of documentation, however it is possible to work out quite a lot by loading a simple file and browsing the object structure in the debugger.
You can find the game on the Xbox Dashboard (Game Marketplace -> Indie Games) or schedule a download via the web.

Pingback: SVG in XNA « Sgt. Conker
Pingback: Creators Club Communiqué 51 « Adibit
Pingback: Tweets that mention SVG in XNA | WAM Games -- Topsy.com
Pingback: GDC: XNA Studio 4.0 & Windows Phone 7 Series, Part II: Demo on 3 Screens! | Cell Phone Choice Blog
#1 by chaishanymn on June 8th, 2010
Quote
great article, very informative
#2 by Success on June 9th, 2010
Quote
Hello from Vyatka River!!! Thank you for information! Itґs a good idea for next full revision…
))
Write more!!!
#3 by Thomas on May 23rd, 2011
Quote
Wow! Exactly what i’ve been looking for the other day! funny how things show up when you least expect them to! haha
#4 by Denise on June 9th, 2011
Quote
I’ve been looking for this exact information and couldn’t seem to find it anywhere online. Glad I came across your site! Great article!
#5 by James on June 12th, 2011
Quote
Nice informative article.
But the Scalable Vector Graphics (SVG) has always been difficult fot me to get while working on Android games.
#6 by Shed on June 14th, 2011
Quote
This sparks my interest. I am not an SVG user but I think this gets me thinking of trying the application soon. I wonder if it has other uses aside from the one you have already noted. Thanks!
#7 by Bradford on June 19th, 2011
Quote
Hi !! Your post on Scalable Vector Graphics is a worthy stuff. From your blog I learn t how to load the path data in-game. You did an excellent work to present this ,really I appreciate this effort, keep on writing.
#8 by Gary Dahl on November 18th, 2011
Quote
Thanks for the helpful article. After poking around with the SVG Rendering Engine for a while, I ultimately decided to load svg documents using generic xml/dom processing tools (using System.Xml.Linq.XElement). This does require stacking your own transformations and interpolating through curves on your own, but otherwise makes it much easier to find and use any svg data you’d like. Best.