XNA. Customizing the content processing (refresh)

In this post, Ill try to explain the basic concepts of content processing inside XNA and the simplest way to customize it.


Introduction

If you don´t already know, in XNA, all the contents are part of the solution, and are built just like if they were code.
In the build process, they first are imported to a common format (depending on the kind of resource it is). In the file properties, you can choose which importer will load the file:


After that, the content is processed by the ContentProcessor (self-explanatory name ;) specified in the file´s properties, and then the results are stored in an XNB file and copied to the project´s output directory. So, the application loads and uses those XNB files, not the original .FBX, .DDS, or whatever.

Processing contents

One of the biggest advantages of working this way with contents, is that you can customize the processing stage, and store the results in the XNB file. For example, let´s say that your programming team work with an American design studio that works in inches, and you want to transform all your models to meters.
Before XNA, this was something tedious. Basically you had two ways of solving it: do that process every time you receive a new model and store the "transformed version", or do it when the models are loaded in the application (with the delay that implies).
Now, with XNA, you can write your own custom ContentProcessor that makes that transformation. So every time you Build your VisualStudio project, all the contents are processed and stored in the output directory as transformed XNB files. In addition to that, the Content Build is intelligent, and will detect if files have changed or not (and consequently if they need to be rebuilt or not).

How to write a Content Processor

Content Processors must be created in a separate assembly. So, first create a new Library Project with all the XNA references (don´t forget Microsoft.Xna.Framework.Content.Pipeline).
Then add an empty class which inherits from the standard content processor for the kind of content you want to process. For example, a ModelProcessor. This class should be marked with the attribute [ContentProcessor] too. Inside the new class, override the Process method, which will be called by the XNA framework for every model using this processor.
A Content Processor example
[ContentProcessor]
public class VertexTaggedMesh : ModelProcessor
{
    public override ModelContent Process(NodeContent input, ContentProcessorContext context)
    {
        // This converts the raw loaded data of your model to a form that can be written to
        // an instance of the model class
        ModelContent model = base.Process(input, context);
        foreach (ModelMeshContent mesh in model.Meshes)
        {
            // Put the data in the tag.
            byte[] rawVertexBufferData = mesh.VertexBuffer.VertexData;
            mesh.Tag = rawVertexBufferData;
        }
        return model;
    }
}

This example simply stores in the "Tag" property of each mesh, a copy of the vertex information of the mesh. This is useful sometimes, because in XNA the vertex buffer information is not accessible if it was created with the ReadOnly flag (what is the default behavior).
In other posts I´ll include more examples of content processors, but now I´m more interested on how to use them.

How to use the Content Processor DLL

Once we have the Content Processor dll, it´s time to integrate it into the Visual C# Express (or Visual Studio 2008) environment. To do so:
  • If using XNA Game Studio 1.0, go to the project in which you want to use it, and select Project -> Properties -> Content Pipeline. The window contains a list of assemblies used as content processors. Use the button "Add" to include the dll created.
  • If using XNA Game Studio 2.0 or higher, just add a new project´s reference, as any other assembly. Visual Studio will detect it´s a content processor.
After that, a new entry in the Content Processors available for contents (in the file´s properties) should appear. Select that one for the files you want, and voila!
Every time you build your project, the content processor will be executed.