Content Processors (XNA Game Studio 4.0 Programming)

First, let’s recreate one of the first examples you did in the topic. Create a new Game project, and add an image to your content project.The code in the downloadable example uses cat.jpg. Then, draw that texture by replacing your Draw method with the following:

tmp14-81_thumb

 

tmp14-82_thumb

Replace the cat asset name in Content.Load with whatever asset you used if you used something else. This does nothing more than render your image to fill the entire game window. However, now let’s mix things up with a different type of content processor.

Note

You might wonder why the content wasn’t stored in a variable and is instead loaded every call to Draw. The content manager caches assets for you, so you do not get a new texture every time you draw this.


To add a new project to your solution to hold your content pipeline extension, right-click the solution, choose Add->New Project, and choose Content Pipeline Extension Library as in Figure 9.1 (you can name it whatever you like).

Adding a new content pipeline project

Figure 9.1 Adding a new content pipeline project

This creates a new content pipeline extension projection in your solution, with a default processor that doesn’t actually do much of anything. Because you will update this though, and you want to change how your content is built, you need to add a reference to the new project to your content project. Right-click the References in your Content project and select Add Reference.Then choose the content pipeline extension project you just created from the Projects tab, as in Figure 9.2.

Adding a reference to your content pipeline project

Figure 9.2 Adding a reference to your content pipeline project

For this example, you take over the processing phase mentioned earlier. Because you modify the texture, you don’t need to import it—you can let the default importer handle that (importers are discussed later in this topic).To create a content processor, you need to create a class and derive from ContentProcessor, so delete the class that was generated for you and replace it with the following:

tmp14-85_thumb

Notice that the ContentProcessor is a generic type with two types required.The first type is the input type of the data that is incoming, and the second is the output type that is returned after processing is complete. Because you modify a texture and return a texture, the input and output types are the same.

Also notice that the types are Texture2DContent, not Texture2D objects themselves. Although many times the runtime version of an object and the build-time version of an object are the same (as you see later in this topic), sometimes you might need (or want) to have different properties/data/methods on your build-time type. This is what Texture2DContent has, and you see how this gets magically transformed to a Texture2D in your game later this topic.

The last thing to mention before moving on is the first thing your class has, namely the ContentProcessor attribute.The DisplayName property of this attribute enables you to specify what name is displayed when you see the processor in Visual Studio.The name here implies how you process the texture.

In order to do some actual processing, override the Process method in your class. Because it is an abstract method, if you do not do so, you get a compile error. Add the following override to your class:

tmp14-86_thumb

Notice that the Process override returns the output type (in this case, Texture2DContent) and takes in the input type (again, Texture2DContent) as a parameter. It also takes in a ContentProcessorContext, which provides helper methods for pro-cessing.When the texture is first imported, you don’t know what format it is, so first convert it to a known type that you can then modify. The TextureContent class (which Texture2DContent derives from) luckily includes a method that does this for you.To modify each pixel’s color individually, convert to a PixelBitmapContent type of Color.

After the conversion (if it was even needed), loop through each MipmapChain in the texture via the Faces property. In the case of a Texture2DContent, it is only a single Face, and a cube texture has six.You then can enumerate through each mip level in the face.After you have the current mip level, get each pixel’s color by using the GetPixel method, create a new color that is an inversion of the original color, and then use the SetPixel method to update with the new inverted color. At the end, return the Texture2DContent you are modifying and you’re done.You now have a content processor that will invert all of the colors in a texture.

Notice that when running the example, nothing at all has changed. The colors certainly aren’t inverted; it’s the same image as it was last time! That’s because you never changed the actual processor your application uses. Select the image you added to the content project and update its Content Processor to the InvertColorsProcessor, as in Figure 9.3.The name shown here is whatever you used for the DisplayName in the attribute before your class previously.

Choosing your processor

Figure 9.3 Choosing your processor

Now when you run the application, notice that your original image is shown with the colors inverted because the processor you wrote is used, as in Figure 9.4.

Now, this is all well and good, but it isn’t customizable. The image simply has its colors inverted. A way to customize the processors would be useful, and luckily, you can do just that. Add a couple properties to control how the colors are inverted (allow them to form blocks):

tmp14-88_thumb

 

 

tmp14-89_thumb

 

The image with the colors inverted

Figure 9.4 The image with the colors inverted

The properties themselves are simple enough—a bool to determine whether you should use the blocky code (that you haven’t written yet), and an int to specify the size of the blocks. Notice that the attributes on the properties enable you to control how the properties are displayed in Visual Studio. If you compile your solution now, and then look at the properties of your image in the content project, you now see two extra properties available, as in Figure 9.5.

Note

If you get compile errors on the attributes, add a using System.ComponentModel clause to the code file.

Notice how the property name in the Visual Studio window is the name specified in the DisplayName attribute, and not the real property name. Set the BlockyInversion property to true.To update the processor to respond to the properties, add the following code before the SetPixel call in your Process method:

tmp14-91

 

 

 

tmp14-92

 

 

 

 

 

Content processor properties

Figure 9.5 Content processor properties

Running the application now causes your image to be partially inverted, and partially not, forming a blocky type pattern, as in Figure 9.6. Remember that all of the inversion is done at compile time. The texture that is loaded into memory is already modified, so none of this happens at runtime.

Debugging Content Pipeline Extensions

Because the content pipeline is executed during compilation time, you can’t just "run the application" to debug the code you’re writing in your content pipeline extension. In reality, the application that is running the code is Visual Studio (or MSBuild, depending on how you’re building), which means that you need to do something else for debugging.

One option is to use a second version of Visual Studio (or whichever debugger you use), and use it’s Attach to Process command to attach to the original Visual Studio instance. This enables you to put break points in your content pipeline extension code and debug. However, this makes Visual Studio run remarkably slow, so it isn’t recommended.

An easier solution is to force the compilation of your content to give you the opportunity to debug, which you can do using the Debugger.Launch method in the System.Diagnostics namespace. When the line of your code is executed, it forces the system to attempt to launch the debugger, and you see a dialog much like Figure 9.7. Using this method, you can then debug your content pipeline extension. Note that if you select No in this dialog, it kills your Visual Studio session.

A blocky image inversion

Figure 9.6 A blocky image inversion

Next post:

Previous post: