As you learned in Chapter 1, the content pipeline is used to import game assets like textures, models, shaders, and sound files. Instead of just adding them to your project like any other unsupported files for Visual Studio (or XNA Studio), the content files get processed and then are compiled into binary content files, which can be loaded from your game (see Figure 3-1).
In the past a game programmer had to write his own importer to load game content data or use one of the available formats like the .x file format in DirectX for model files. But often the available formats are insufficient, too slow, or just too inflexible when trying to add new features to a game. This is the reason why almost every commercial game has its own file formats and custom logic programmed behind it. This has the advantage that only the developers of a game know the layout of their internal format and they can extend it or change it as often as they want. However, it is usually a lot of work to get 3D content in your game this way. Loading textures is usually not that complicated because many libraries exist and even when writing your own file format, it basically just contains pixels, which are stored as 24- or 32-bit color values. It can become a little bit more difficult if you try using compression or if you want to use hardware-compressed textures like with the DXT format, but DirectX has a great repertoire of helpful methods to assist you with that.
On the other hand, loading 3D model data is a lot more complicated, especially in XNA, where you have to have not only the geometry data, but also shaders to render a 3D object and then, of course, material data to tell the shaders which colors, textures, and parameters to use. In DirectX most tutorials and samples just use the .x file format, but the .x file format might not be sufficient for many projects. Especially if you use normal napping and require the geometry data to contain tangents, the .x file format will not be very helpful. You would have to generate tangents in your application and work around problems this might introduce. For example, one of my older games, Rocket Commander, had this exact problem and it required a complex model loading process and tangent regeneration process.
Other game data like loading sound files (.wav), shaders (.fx), or custom (for example, .xml) data might be straightforward because your game or the framework you are using provides enough helpful classes to load everything quickly, but then you might run into problems running your game with the same content on another platform. You can, for example, use ACPCM sound files on the Windows platform and use compiled Pixel Shader 1.1 files or just load a couple of .jpg files as textures, but there is no ACPCM support on the Xbox 360; sound is either PCM or in the custom XMA format for the Xbox. Shader code has to be in the format the Xbox 360 accepts, and loading textures might work differently too. This problem would even get more complicated if more platforms are supported in the future.
To simplify loading the game content, XNA allows you now to just drop the raw content files into your XNA Studio project and they will be processed and compiled to the correct output format for the currently selected platform. For example, your sound files can be processed and based on your XACT project settings - you will have different output formats and compressions, but all the raw sound files in the wave bank are the same and only have to be updated in one place. This idea is great, but it would require that any raw content file format is supported, which is not practical because there are so many file formats available and you don’t know which ones are going to be used. For example, one of your graphic artists might use Photoshop and store .psd files, and other teams might use Gimp or Paint-Shop or just the Paint program of Windows. And there are thousands of other graphic tools and programs around. Additionally, you don’t really know which data to extract; many formats can have multiple layers and maybe the artist wants to have each layer available or just have everything merged down.
Instead of just dropping anything, use one of the available processors and supported formats or try to write your own content processors (see Chapter 7) if you think you will need it:
Texture formats: .dds, .png, .jpg, .bmp, .tga - Basically everything you can load with the .NET Framework or DirectX. The input format should usually be uncompressed for the best quality. Highly compressed .jpg files are bad, especially if you compress them again with DXT for your game. Alternatively, you can use the correct output compression in your input files (dds files with DXT compression) and set the same settings for the content properties again to leave them that way (which is the way I process most of the content in all my projects).
Sound and Music formats: .xap (XACT Audio Project) - In XACT you can only import .wav files, but you can set a lot of effects, set parameters, and choose ACPCM compression on the Windows platform or XMA on the Xbox 360 platform. Read more about this in Chapter 9.
3D Model formats: .fbx and .x model files - .x files are known from the DirectX SDK and many samples and tutorials. DirectX provides classes to easily load .x files. Most .x files should work fine with XNA too; the main difference is that DirectX usually doesn’t use shaders for .x files and XNA always uses shaders. To export models from 3D Studio Max, use the Panda DirectX Exporter. You can find the Panda Exporter plugin at http://www.andytather.co.uk/Panda/directxmax.aspx.
.fbx files are a little bit newer and were originally developed by Alias, the makers of Maya, also a 3D modeling tool. Alias was acquired by Autodesk, the makers of 3D Studio Max and many CAT programs. .fbx stands for “Universal 3D Asset Exchange” and is Autodesk’s free format for cross-platform content interchanging. In the new version of 3D Studio Max 9 it is included by default and Maya supports it too. There are also many other 3D content creation programs that support importing and exporting the .fbx format. In XNA it is especially useful for animated models, bones, and skinning. It supports more options for that, but it is especially bad for shaders, because no material or any shader settings can be exported.
Another problem with the .fbx format is the missing format specification, and to access the SDK you have to join the Autodesk Developer Network for an annual membership fee, which really sucks. If you take a look at other interchange formats like Collada, you can see that they are much more open and extensible, and because they are not just developed by one company many new additions and features are added constantly. In the past Collada did not support shader settings, but the current versions are very good for 3D data; you can export tangents, shader settings, and everything else you need for a game. Sadly, Collada is not supported by XNA and I was not able to convince Microsoft to include it. XNARacer initially used Collada files for all models, track, and landscape data, but this was changed later to support the content pipeline.
Other formats - You can import custom file formats, for example xml files, binary files, or even write your own custom processor. This can be useful if you have a bigger project and it is worth the effort or you need a special model format and it is not supported yet by XNA. For example, Quake3/Doom3 uses md3/md5 files and if you just used to import some models for testing and playing around, an md5 importer would be nice.
In case you have more content files or some custom data for your game you can either decide to write a custom processor and then use the imported and compiled data in your project or just do it the old way by loading the content files yourself. For example, the racer game you will write in the last chapters of this book uses a landscape with landscape height values imported from a bitmap file. Processing the bitmap file and outputting the landscape height data for the game would be possible, but it is too much work - just loading the height data is much simpler and only required once.
Another disadvantage of the content pipeline is the fact that your compiled content cannot be changed anymore. Once you start your game or have your game deployed on client computers or the Xbox 360, all the content files have only the compiled data in them. Say you have just written a particle editor with shader support for all particles. If you want to change the textures, shaders, and other particle settings dynamically while the editor is running, you would have to reload the textures, shaders, and so on. But because you need to have the content compiled first inside of XNA Studio you have to stop your application, add all the files to your XNA Studio project, recompile and wait until all the content is rebuilt, and then start again. Especially in the case of just testing and tweaking effects or particles, this can be very annoying and slows down your work process a lot. It would be more useful to just load textures, shaders, and your particle settings dynamically and not use the content pipeline for programs like that.
Last but not least, here is a trick I use in most projects with many content files: compile them all and make sure you don’t change them very often (only every couple of days). Now you can use a dummy project to compile all your game content and copy over all compiled content files to your real project. Especially when using unit testing and the Agile Methodology talked about in the last chapter, you will start the application several hundred times a day and each run should be as fast as possible.
The good thing about the content pipeline is that the compiled data (.xnb files) cannot be read by any programs except from an XNA engine and the loading process is usually also a lot faster because all the data is already in the exact format you need for your game. For example, textures are always stored as DXT files and use mip-maps if you specified that in the content properties. This way the game just has to load the texture data in one quick call and then send it to the graphic card for rendering, again a very fast process. This is even more important for 3D model data. If you take a look at Rocket Commander and profile it a little bit, you can see that loading the 3D models and generating all the extra data and tangents takes most of the initialization time (over 90%), whereas XNA games with 10 times as many models load a lot faster. Loading all data as quickly as possible is also a good thing for the Xbox 360 console; console games usually have a short loading time.
Okay, you learned a lot about the advantages and disadvantages of using the content pipeline; here you focus on the game programming a little bit more and the everyday problems. If you take a look at the content directories for the Rocket Commander game and Racing game (see Figure 3-2) you can see that Rocket Commander has a lot of directories, whereas just two simple directories are used in XNARacer.
From the looks of it you would expect that Rocket Commander has a lot more content, but in reality XNARacer uses almost 10 times as many 3D model files and also has a lot more textures, music, and sound files.
You might ask why Rocket Commander uses so many directories. There is no content pipeline in the game, and to keep everything organized and easy to find directories are used for each part of the game. For example, the Textures folder holds all the 2D textures for the menu and game interface, the Models sub-directory holds the textures for the 3D models, the Effects sub-directory contains the effect textures, and so on.
In XNA you cannot use this kind of directory structure because most content files, especially the 3D models, can require a lot of other content files to be loaded recursively (see Figure 3-3).
As you can see, the Apple model is loaded from the Apple.x file, which recursively loads Apple.dds, AppleNormal.dds, and NormalMapping.fx. The content processor expects all these files in the same directory, which forces you to use one directory for all the 3D models and the textures and shaders you use for them. Additionally, most shaders are used for other 3D data as well and it would be very confusing duplicating the shaders and having them in another directory too. You also sometimes load the textures for custom 3D data too (for example, the guard rail holder model uses the same texture as the generated guard rail object in XNARacer).
Anyway, it is important to remember that each piece of content must have a unique name. You can’t have an Apple model and an Apple texture. As you can see in the Input File line of Figure 3-3 you only add the Apple.x file; all the other files are added automatically through the model processor. Additionally XNA is clever enough to rename all the recursive files because they often use textures with the same names as the model files. Recursive files will end with ~0. You also can’t set the content properties of these recursive files because you don’t add them into your project. For that reason make sure the input files already use the correct format (DXT1 and DXT5 in the preceding example).
You now know enough to import some content and access it from your game. In the previous chapters you already accessed some content files and took a quick look at the content pipeline. Now you will take a closer look at the actual process and how to use content files. In Chapter 7 you learn how to write your own content processors by extending the X Model File Processor with some useful features for your graphics engine.
Back in Chapter 1 you learned how to add textures; just take a texture file (.dds, .jpg, .bmp, or .png) and drop it onto your XNA Studio project. Now you can click the texture and configure the Texture Content Processor settings (see Figure 3-4).
For textures it is important to set the correct Content Processor mode. For 2D data like sprites, text, and all user interface (UI) graphics you have in your game it is usually best to use the 32bpp Sprite format (uncompressed, which means it takes 4 MB for a 1024x1024 texture with 32bbp).
3D texture data will be used a lot more in a 3D game than 2D UI textures, and textures and levels are getting bigger with every game. For this reason it is very important to keep the texture sizes small. Instead of just reducing the resolution for your textures and making your games look very bad, use hardware texture compression instead. You can simply select DXT1 for a 1:6 compression ratio for color textures and DXT5 for a 1:4 compression ratio for textures with alpha information (or compressed normal maps). This means with DXT1 textures you can have six times as many textures in your game consuming the same amount of video memory as uncompressed textures without losing much of its quality. Another trick is to combine or even generate textures inside your shaders; for example, detail textures can improve landscape details for almost no extra video memory cost.
For models you can currently only select the X Model Importer or the FBX Model Importer (see Figure 3-5). Maybe more formats will be available in the future. If you write custom model processors like you will do in Chapter 7, you can select them the same way you select texture processors. For normal mapping you want to select your custom XNARacer Tangent Model Processor from Chapter 7. For the next few chapters just leave the default value.
If you followed the way you loaded textures in the last two chapters you will probably already know how to load content in XNA. Textures are loaded with:
backgroundTexture = content.Load<Texture2D>("CityGroundSmall");
And 3D models can be loaded the same way - just change the generic type of the Load method:
appleModel = content.Load<Model>("apple");
Displaying a model is a little bit more complicated; there is no simple draw method, you have to go through all model meshes and update all shader effects, and then you can render each part. For more details see Chapters 5 and 6. Later in this book in Chapter 7 you will see a new class just for loading and rendering models, which makes it even easier to just show the 3D model in our 3D world with just one single line of code:
You now know all the basics about the content pipeline. You learn more about the content pipeline in the upcoming chapters when adding 3D models with your custom tangent model processor in your graphics engine, and in Chapter 9 when you learn in detail about XACT.