Blender3D and XNA

For the last three weeks I have started modeling the first in-game content for Horizons, the ‘Locust MK I’, using Blender3D and paint.NET. This time round, I’ll go over how I am exporting model and opaque data from blender, texturing the model with UV maps and paint.NET and importing them into XNA.

Well, the time has finally come that I have started to model and texture in-game content. This is the bane of the hobbyist programmer, but time and consideration are definitely in order when modeling content because if it doesn’t look good, nobody is going to want to play your game (oh how I loved the Amiga, when it wasn’t about graphics, it was about gameplay).

For me the decision to go with a particular set of software is usually pretty easy; the less it costs, the more likely I am to give it a chance. Hey, I’m not a professional programmer, nor am I an artist and as Britonia is purely a hobby project, its just not viable to spend ā‚¬1000s on professional software packages. But alas, that doesn’t mean that I have to use a sub-standard set of tools.

Modelling:

Many times when I browse any XNA forums, I run accross the same topics: “Which 3d modelling program is best?”, so this is where I’ll give my 2 cents and also run through some of the features which I have used so far, and how they can be used in conjuction with XNA.

As stated above, my modeling tool of choice is Blender3d (website). The biggest negative point mentioned in the forums for Blender is that the learning curve is too hard, but I honestly don’t think its any harder than learning 3ds max. Whether a beginner chooses to use Blender or Maya/3ds max it is going to be hard and time consuming; but that’s modelling. Its just a question of which product you choose to invest more time in. After a little while, and a run through the tutorials I found the blender interface quite intuitive; and it certainly doesn’t fall short on the features list.

One change I would definitely recommend making though is to modify the FBX export script to include opaque data when exporting to FBX files. Opaque data are values which can be assigned to individual meshes within blender (and then imported with the model in the content pipeline). This is pretty good because it allows the designer to specify different contraints on different meshes. For example, in this video (youtube link), you can see I have put white planes on the top of the ship which are to be turret points; and within the opaque data fields for this mesh I have specified:

Name: Turret
Size: 1
MeshName:

This is then read in in the content processor and used to fill a ship attributes class which is then stored in the Model.Tag. So the content pipeline can see from this example that this is a turret mount point, with size and the name of the mesh. I can then use the AbsoluteBoneTransform for this mesh when drawing a turret model and it should appear on the ship.

Exporting Opaque Data:

Opaque data is not automatically exported with the default FBX export script, so we do have to make a few changes. I found a nice article about modifing the export script to include this data (link here). (thanks to Ronzan for giving me the link, and Kibix for writing it).

You can get a modifed version of the FBX exporter from that website, but I actually had a few problems with that exporter as it is, so I had to copy just the following parts into an unmodified version of the default FBX export script:

# Edit by Kibix
// This is the part which writes the opaque data to the FBX file
properties = ob.getAllProperties()
for property in properties:
    if(property.getType() == "STRING"):
        file.write('\n\t\t\tProperty: "%s", "enum", "A+U", 0, "%s"' % (property.getName(), property.getData()))
    if(property.getType() == "FLOAT"):
        file.write('\n\t\t\tProperty: "%s", "float", "A+U", %s' % (property.getName(), property.getData()))
    if(property.getType() == "INT"):
        file.write('\n\t\t\tProperty: "%s", "int", "A+U", %s' % (property.getName(), property.getData()))
    if(property.getType() == "BOOL"):
        file.write('\n\t\t\tProperty: "%s", "bool", "A+U", %s' % (property.getName(), property.getData()))

End of edit

I’m not sure yet why it didn’t work, but you can see the author has made a few other changes to the exporter. This was a quick fix for me and with this small change, you should be ready to go.

To actually add the opaque data within blender you must go to ‘object mode’ (where selected objects have a pink outline) and open the logic tooltab (the pacman icon), which should look like this:

Photobucket

You can see the options on the left handside of the tab to add strings, ints, floats, bools etc. to the mesh data.

To test this out, you could create a cube object and add some opaque data to the mesh. Then use your new export script to create an ASCII FBX file which you can open in a texteditor. You should then be able to see the following information in the new file like this :

Photobucket

Importing into XNA :

Importing into XNA also requires a little work in the content pipeline, as the default FBX processor will not read the opaque data we just added to the FBX file. Luckily this change is pretty easy. Just create a new content pipeline importer and derive from the ProcessModel function and make the following changes:

ContentProcessor.cs

public override ModelContent Process(NodeContent input, ContentProcessorContext context)
{
    // Uncomment out if you want to debug (JIT)
    // System.Diagnostics.Debugger.Launch();

    SpaceShipAttributes _ShipAttributes = new SpaceShipAttributes();
    ModelContent model = base.Process(input, context);

    for (int i = 0; i < input.Children.Count; i++)
    {
        NodeContent node = input.Children[i];
        ReadShipInformation(ref _ShipAttributes, i, node);
    }

    // Copy the attributes into the model.tag property object.
    model.Tag = (SpaceShipAttributes)_ShipAttributes;
    return model;
}

private void ReadShipInformation(ref SpaceShipAttributes shipAttributes, int index, NodeContent node)
{
    if (node.OpaqueData.ContainsKey("ShipName"))
    {
        shipAttributes.ShipName = (string)node.OpaqueData["ShipName"];
    }
}

You can see here that we search each of the model’s mesh nodes to see if they contain any of the tags which we predefined. In this case, I am just searching for the shipname, which will be stored in the ShipAttributes class object. If the node does, then we can read this data and store it in our own class. Finally, this class is then casted in the Model.Tag.

A few things to watch for in Blender:

Keeping an Eye on the current ‘Space’:

For anybody who has used directX or XNA this is perhaps a no-brainer but I thought I would mention about the differences between working in world space and object space. When creating your model, there are two spaces which you work within (at least, there are two which are exported to XNA), the first is called the world space and the second is the object space.

The world space is where the position, rotation and scale of the meshes are stored in world coordinates relative to the origin (0,0,0). The world space is changed when you move the model in ‘object mode’ within blender. You can see the axis of the model in world space below:

Photobucket

Any transforms (translation, rotation and scale) made in object space will be stored in the AbsoluteBoneTransforms matrices in the model.

The object space is where the position, rotation and scale of the individual vertices are stored in object coordinates, that is, coordinates relative to the object origin (0,0,0). The object positions are changed when you are in ‘edit’ mode within blender and you move the individual vertices, as below:

Photobucket

Each vertex is actually already stored in object space, so there really isn’t anything to do there, but you must remember to pass the correct world matrix to your effect when rendering each mesh. As you can see in almost all the 3d samples on the creators club, this is done so:


// Copy the world transform into an arry of matrices.
_ShipModel.CopyAbsoluteBoneTransformsTo(_Transforms);

foreach (ModelMesh mesh in _ShipModel.Meshes)
{
  worldMatrix = _Transforms[mesh.ParentBone.Index];

  foreach (ModelMeshPart part in mesh.MeshParts)
  {
     Effect currentEffect = part.Effect;

     currentEffect.Parameters["xWorld"].SetValue(worldMatrix); // world matrix
     currentEffect.Parameters["xView"].SetValue(camera.View);
     currentEffect.Parameters["xProjection"].SetValue(camera.Projection);
  }

  mesh.Draw();
}

So with this in mind you can see that we can get the correct positions for all meshes (including our place holders) relative to the model.

UV Mapping:
Another big hurdle is how to UV map you models. I guess there are many different ways and you’ll only find which one best suits you by trying it out, but here is how I do it …

Load up your model and centre on it in the 3D view. Now if you right click on the seperator between the toolview and 3d view you can split the view port, then you should have something like this:

Photobucket

I find this view makes it much easier to work with. Now, I like to use the projective UV map button for actually defining the UV coordaintes. Using the numpad, start off with the top (num7), then select the faces which you want to appear as an area within the UV map:

Photobucket

Then press ‘U’ and choose ‘project from this view’. You can see that the same area is displayed in the UV screen, where you will be able to scale and move the selection around the texture reactangle. This process should be repeated for each of the faces in model, until every face has a unique area on the UV map (unless of course they should be sharing the same texture information). This is an example:

Photobucket

When you export this FBX file, the UV coordinates for each triangle will be saved in the vertex data. Then you can use the TEXCOORD symentics in HLSL to properly texture each face.

Scaling:

The last thing to note on the blender side is that models exported with the default FBX exporter need to be scaled down in XNA by 0.01f to get a 1:1 blender / xna unit scale. It is possible to do this scaling in many different places, either in the content file properties in VS, or in the exporter OR manually before rendering the object, like this:

worldMatrix = _Transforms[mesh.ParentBone.Index] * Matrix.CreateScale(0.01f);

// pass world mat to effect when rendering mesh

Well, that’s it for now. If you have any questions about the blender / XNA process then just leave a comment here or in the forums and as always I’ll try and answer.

I have added a new page to the screenshots section on the site where I’ll upload shots of ships as I make them.

-John

Advertisements
  1. #1 by Kibix on March 31, 2010 - 9:59 am

    Nice to see that someone else had use for my article! Good luck with the game šŸ™‚

  2. #2 by Lintfordpickle on March 31, 2010 - 10:06 am

    thanks for the comment. The article was great and straight forward!

  3. #3 by Gahme on March 31, 2010 - 11:48 am

    Great to see some activity on this site again, useful tutorial, thanks^^

  4. #4 by Robert on April 13, 2010 - 3:13 pm

    What puts me off Blender is the viewport, as it’s very rigid and doesn’t define the shape of the object you’re creating in a decent perspective view. Mind the other thing that puts me off Blender is that essentially it still is a game creation package underneath it’s 3D modeling polish.

    Was good for what it was (and price point) when it was released, but next to the big boys like Max, Maya and XSI. It really does feel very restrictive and lacking. Mind so does Milkshape3D and that has quite a big following as well, heck I used to use that for Half-Life/Quake 2 modeling as it was the only really reliable product for doing so.

    Really the best 3D product always tends to fall in to what you personally feel most comfortable using, for me it’s Maya… then again I’ve been using it since the mid-90s for PSX artwork, unfortunately for most it is possibly also the most expensive product out there haha

    I would recommend XSI Mod Tool, just to try out though. It is exceedingly difficult to get used to how it works, especially if coming from another 3D Application. Still it does offer some very powerful tools as well as connecting seemlessly to XNA with it actually linking directly to active projects. Worth trying out if you haven’t already.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: