Showing posts with label content processor. Show all posts
Showing posts with label content processor. Show all posts

Wednesday, April 28, 2010

Imporing blender models to XNA as FBX

This post was meant for release on April 28th. I've been very busy in May and hadn't had much chance to update until now. Sorry.


For Space Combat Sim I've been making game models using Blender. It's powerful, it's free and it's got an active development community churning out new versions regularly. Check it out.


Like most modelling programs Blender uses its own file format for model data. XNA projects can't read these files. You normally can only add .X and .FBX files to games using XNA (though there are ways of adding your own model file importers via content pipeline extensions.) Fortunately Blender provides exporters for both X and FBX. I chose FBX due to the readability of the format and the fact the exporter worked more consistently for me.


Exporting from Blender to XNA created two major issues for me. One was ensuring the right material shaders were used. That was handled by writing a simple custom content processor. I simply named materials using "shader-name" which worked nicely. The second issue was more annoying. Blender uses a different co-ordinate system from XNA.

Huh? All modelling programs express positions using three numbers representing the object's position on three axes called X, Y and Z. Each axis represents one direction an object can move. In XNA, the X axis goes left to right, the Y axis goes from upward and the Z axis goes out from the screen (backward in the game.) In Blender the Z axis goes upward and the Y axis goes into the screen.

The result is that if I export a model and use it in XNA as is, it faces downward. Obviously that's a problem. Fortunately XNA provides a nice simple solution.


Just rotate it!

As you can see here the content processor lets you specify a rotation to be applied to the model before it's used by the game. This takes care of things nicely.

To finish, here's a list of things to watch out for.

  • Scale. If you don't make your models size consistent with each other you'll need to deal with this in the content processor.
  • Shaders. By default the model processor in XNA will assign the model a basic shader to render it. Not much good for fancy lighting effects and such.
  • Orientation. I tended to model my ships facing along the negative Y axis keeping rotation simple. If your models aren't facing straight along an axis importing can be more painful

Saturday, January 16, 2010

Non-Trivial Custom ContentProcessor - Part 3

So today I'm writing another exciting edition on how I built my custom ContentProcessor for loading game objects. If you've just started reading this series you might want to check out the Introduction, Part 1 and Part 2 first.

When I left off in Part 2 I provided a snippet of code to convert an XML document tree into a set of custom class instances which the game can work with easily. These classes describe how to create a group of in-game entities that make up a starship or other object. However the game makes some assumptions about the data these classes contain which can (and must) be verified by the ContentProcessor.

  • The group has a root entity. Its parent entity is undefined
  • There is only one root entity
  • Entities within the group are only related to eachother, not other groups
  • Entity properties are valid (notice valid here isn't defined yet, stay tuned :)

If we don't ensure these assumptions are valid then Space Combat Sim will crash when using the processed content. So, the next step that happens is validation and occurs immidiately after all the repeated process shown in Part 2 is finished.

bool foundParentEntity, foundTopEntity = false;
foreach (Entity entity in Entities)
{
if (entity.ParentName == "")
{
foundTopEntity = true;
continue;
}

foundParentEntity = false;
foreach (Entity parent in Entities)
{
if (parent.Name == entity.ParentName)
{
foundParentEntity = true;
break;
}
}

if (!foundParentEntity)
{
throw new ContentLoadException("Entity " + entity.Name + " references non-existant parent " + entity.ParentName);
}
}

if (!foundTopEntity)
{
throw new ContentLoadException("Group has no top-level (blank parent) entity!");
}

So, what does this mess do? It verifies that two of the four conditions are true. The outer loop's main purpose is to locate and flag that there is one root entity in the group. The inner loop ensures that every entity (besides the root) has a parent. After the loops is a check to verify that the root was indeed found.

Astute readers might notice a bug in the code above. Circular relationships, e.g. entity A is a child of entity B which is a child of entity A, are not prevented by this procedure. There are a couple ways of dealing with this. The simplest, and most evil, would be to recusively search for the root entity from a given entity. This causes a circular relationship to blow the stack and throw an exception. This can be prevented by making the recursive algorithm build a list of each entity visited as we go up toward the root. If an entity appears in the list more than once then a circular relationship exists. There are also non-recursive ways of doing the same thing which I'll leave as an exercize for the reader.

I decided to omit this step from the game since such errors are rare and are caught in the entity creation tool which you can see in my article on collision detection. Currently by crashing horribly, I'll fix that in the future.

Another apparent bug is the lack of testing for non-unique names and for multiple roots. These are both handled earlier. If you remember Part 2 the last part of loading an individual entity block from XML was a call to AddChildEntity.

foreach (Entity entity in Entities)
{
if (entity.ParentName.Length == 0 && parentName.Length == 0)
{
throw new ContentLoadException("Multiple top level (blank parent) entities found!");
}
if (entity.Name == name)
{
throw new ContentLoadException("Multiple instances of " + name + " found!");
}
if (entity.Name.Trim().Length == 0)
{
throw new ContentLoadException("Found nameless/whitespace named entity!");
}
}

The actual adding part has been omitted since it isn't that interesting.

With all of this taken care of we still have one item left to validate. The entity's creation parameters. In fact, I have yet to show you how they're loaded. In fact, this is because the main tradeoff is made for thoroughness of validation versus speed and flexibility of coding is made here.

Entities represent a wide range of object types in the game with wildly different rules. Simple objects include stuff such as hull sections which can do little besides be blown up. Others, such as weapons, are much more complex. Parameters are needed to define stuff like timing, positions, orientations and references to other game data. What's worse is that, at the time I was coding this loader, many properties had yet to be determined, stuff like sound effects and so on.

With all of that in mind I decided to err on the side of flexibility. The loader will accept all parameters and ensure that the XML data is valid for the type that is being used to fill the parameter. It will not check if the parameter is used by the game and also won't check if the parameter's type is what is needed by the game. I'll show you how I implemented this validation without adding an excess of validation code to the game itself in Part 4.

Tuesday, November 24, 2009

Non-Trivial Custom ContentProcessor - Part 2

If you're reading this for the first time you might want to check out the Introduction and Part 1 first.

The next step taken by the XNA Content Pipeline is to process the data produced by the content importer. This is done by the aptly named ContentProcessor classes. These classes are responsible for pretty much anything that happens between the content being loaded (via ContentImporter or ContentReader which I'll discuss an a later article) and the content being used.

ContentProcessors are used to massage data into a format usable by an XNA project and/or to load additional content. For example a model processor will load textures and shaders that the model is rendered with. So processors usually run when importing content into a game project or when loading imported content as part of game execution.

As I mentioned in Part 1, my custom content importer simply converted the raw XML input into a nice tree that can be worked with by the processor (using the .NET System.Xml family of classes.) Now this XML has to be turned into something meaningful to the game. This obviously leaves a lot of work to the content processor.

As I mentioned before this is the structure of the XML:
  • Group (document element)
  • One or more entity elements. Each element defines how it's related to the others. In-game this is used to build an n-tree of entity objects with one entity as the root.
  • One or more property elements for every entity. The meaning/use of these properties is dependent on the type of entity.

What we'll do in the processor is iterate through all "entity" elements in the document, read in the data we care about, and add each to a content data class called EntityGroupTypeContent. The code to do this is relatively straigtforward and uninteresting. I'm sure it can be improved but at the time I wrote it my main intention was for it to work.

outputGroup = new EntityGroupTypeContent();
XmlElement top = input.DocumentElement;
foreach (XmlElement entity in top.GetElementsByTagName("entity"))
{
// Read in entity attributes (convert from string as needed)
string name = entity.GetAttribute("name");
string parentName = entity.GetAttribute("parent");
string entityType = entity.GetAttribute("type");
string renderObjectName = entity.GetAttribute("render-object");
string[] rawPosition = entity.GetAttribute("position").Split(',');
Vector3 position = new Vector3(Convert.ToSingle(rawPosition[0]),
Convert.ToSingle(rawPosition[1]),
Convert.ToSingle(rawPosition[2]));

string[] rawOrientation = entity.GetAttribute("orientation").Split(',');
Quaternion orientation = new Quaternion(Convert.ToSingle(rawOrientation[0]),
Convert.ToSingle(rawOrientation[1]),
Convert.ToSingle(rawOrientation[2]),
Convert.ToSingle(rawOrientation[3]));

// Read entity properties
Dictionary properties = new Dictionary();
foreach (XmlElement property in entity.GetElementsByTagName("property"))
{
string propertyName = property.GetAttribute("name");
EntityGroupPropertyContent propertyData = new EntityGroupPropertyContent(property);
properties.Add(propertyName, propertyData);
}

// Add the entity
outputGroup.AddChildEntity(name, parentName, entityType, renderObjectName, position, orientation, properties);
}

Notice the lack of exception handlers on Convert.ToSingle. If something goes wrong here the exception percolates up to the content builder. When this happens the exception is recorded as a build error in Visual Studio. Also notice the EntityGroupPropertyContent instantiation. It's pretty important but talking about it brings up a lot of discussion irrelevant to this entry. I'll talk more about EntityGroupPropertyContent in Parts 3 and 4.

Next we have to validate the data that was loaded. The game expects the following conditions to be true:

  • The group has a root entity. Its parent entity is undefined
  • There is only one root entity.
  • Entities within the group are only related to eachother, not other groups
  • Entity properties are valid (notice valid here isn't defined yet, stay tuned :)

Since we didn't verify these conditions in the importer they have to be checked now. This way any errors in the content will be caught by the compiler and show up as build errors. Trying to catch these errors later would result in them crashing the game.

I'll get into validation with Part 3.

Sunday, November 15, 2009

Non-Trivial Custom ContentProcessor - Part 1

This is part one on my series on writing a non-trivial custom content processor for XNA Game Studio 3.1. If you want you can read the introduction of this series for useful background information on why I went about writing a content processor.

Besides the tutorial and samples I mentioned in the introduction MSDN also has a very nice tutorial on how to create a complete content processor.

A complete content pipeline extension project consists of the following parts:
  • A content importer
  • A content processor(s)
  • A content data class(es)
  • A content writer
  • A content reader

Each piece is a distinct step in the process of importing and loading content in your project.

The content importer is a very simple object. All it has to do is open the basic content (image, model, game data etc.) and parse it enough that it's usable by the content processor. Since I originally chose to use XML my importer only needs to open the file as an XML document. If you're using XML you might want to test the document against a DTD to save in sanity checking code in the processor.

Here's the importer:

using System;
using System.Xml;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

// Entity group type specs/annotations are in XML format
using TImport = System.Xml.XmlDocument;

namespace EntityGroupProcessor
{
///
/// Basic entity group type importer. Entity group types are always in XML. All this does
/// is load the file.
///

[ContentImporter(".ent", DisplayName = "Entity Group Importer", DefaultProcessor = "EntityGroupTypeProcessor")]
public class EntityGroupTypeImporter : ContentImporter
{
public override TImport Import(string filename, ContentImporterContext context)
{
XmlDocument document = new XmlDocument();
document.Load(filename);

return document;
}
}
}

Notice the line
[ContentImporter(".ent", DisplayName = "Entity Group Importer", DefaultProcessor = "EntityGroupTypeProcessor")]
The first part ".ent" specifies the extension of the file that this importer processes. This allows Visual Studio to automatically use this importer when content with the extension of ".ent" is added to the project. The second part specifies the name of the importer as it appears in Visual Studio. The third, and most important part, names the class used to process the output (in this case an XmlDocument instance) in the next step.

The bulk of the code, at least in my case, ends up in the content processor and the content data classes. Stay tuned for Part 2 where I get into the basics of the ContentProcessor and content data classes.

Saturday, November 14, 2009

Non-Trivial Custom ContentProcessor - Intro

The network component is finally done for this sprint. Yay! And I have a project that compiles for the X-Box 360. Yayy!!

This past week has been interesting so far since I found that my initial solution for batching objects for drawing was impractical when it came to the critical stage of actually doing the batching. So I had to tear it out and start over. The new solution is quite clean and has an added advantage of reducing the amount of data stored for render objects. Only one set of render data is stored no matter how many objects that use it exist. But this change started a cascade of extra changes which has led me to begin developing a custom content processor for XNA.

I'm working on generalizing the code needed to spawn in-game objects. My original method of loading in-game object data was to load a model and create one in-game object for each mesh that makes the model. The properties of the in-game objects were encoded in the names of the meshes. Initially this worked surprisingly well. The biggest advantage is that I didn't have to spend time on tool development and could focus on getting the game working.

There are, however, four problems.
  • No two objects defined in a model can have the same properties (due to mesh names having to be unique)
  • There's no easy way of creating a deep tree of attached objects (e.g. a capital ship with turrets)
  • Using names severely limits the number of properties that can be defines
  • Not all objects need to be models (e.g. beams and bullets)

My solution is to split off loading of graphics and defining in-game objects. Graphics are loaded and tossed into a pool for later use. Each graphical item has a unique name which can be used to reference it. Once the graphics are loaded the game then has to load information on how to create in-game objects using the graphics.

This is where the ContentProcessor comes in. My current plan is to create a custom XML document which defines a tree of in-game objects and their properties. Each file lists a set of in-game objects to be created. Each object is a single block in the XML file defining the following.

  • What appearance the object has (references a loaded bit of graphics)
  • What the object's type is
  • What the object's reference name is
  • What the object's parent's reference name is
  • A set of properties specific to the object's type

In order to keep the code for the game clean this XML data is to be loaded using a custom ContentProcessor. There are at least some resources out there on the internet including this (tutorial 21, PDF Alert!) and this which provide some nice sample code. I like the first since it distills the process down to its simplest form. Useful for learning, but it doesn't cover all the nuances I need for what I'm doing.

Stay tuned as I post more details on the content processor.