| |
MODELING
3D modeling applications allow for rapid development of animations and game objects. 3D modeling applications are definitely the preferred alternative to manually creating and drawing a series of complex vertex variables. There are lots of file formats to choose from. XNA supports .x and .fbx file formats. 3D Modeling Tools An inexpensive but popular light weight 3D modeling program for game developers is MilkShape . It is used here because it is one of the easiest model creation tools to learn and Milkshape's support for XNA format is excellent. Milkshape imports and exports many relevant model file types for games. Even if you decide later that a different modeling tool is preferred, Milkshape is a great application to learn with. Milkshape offers a free 30 trial version. The purchase price is around $25 US. You can download it from: www.milkshape3d.com Installation Warning: When you install it, just make sure that you select the trial edition if you want to test it for 30 days to be able to save your files.
Milkshape Intro Example: Creating a Windmill
The process of creating a 3D model helps to demonstrate how the XNA library works to provide support for loading and manipulating models in code. This following example shows how to create a windmill using Milkshape.
| Create a new project Starting Milkshape will automatically open the designer studio needed for designing and building a 3D model. The window that appears will show four different viewports. A viewport offers a view of the model from a different angle. |
|
| Prepare the tool to add vertices select the "Model" tab. check "Redraw All Viewports". select the "Vertex" button. |
|
Draw the vertices
choose the front viewport.
to find it:
- right click each viewport.
- choose projection in the drop down menu that appears.
- the "view" type will be highlighted.
|
click in the viewport to draw each point. make 3 points to form a triangle. |

|
| Set Milkshape to draw the face A face is the shaded area between the vertices. Only one side of a mesh has a face. The face side is used to display a texture. For this reason a face is also known as a skinning surface since the face side shows the skin or texture of the model. The opposite side of the face will not show the texture so it is important to have the face set on the correct side. Select the "Face" button on the "Model" tab. |
|
Shade the face surface Milkshape automatically shades in the area contained between vertices when a set of three vertices has been selected. The grey surface that appears is the face. After the "Face" button has been selected on the "Model" tab, click on each vertex for the new face in a counter clock-wise direction. Clicking in a counter-clockwise manner sets the face so it appears immediately before the viewer.
|
 |
Be aware of the non-textured side The texture will only show on the face side (the grey side) so make sure the face points in the proper direction. Clicking each vertex in a clock-wise direction sets the skinning face on the opposite side from the viewer. The resulting surface area that would appear before the viewer after a clock-wise selection would be black. A black surface will not show the texture.
|

|
Copy the triangle
find the front viewport.
choose the "Select" button.
highlight all vertices to be duplicated by left clicking the mouse and dragging around all vertices to be copied. The set of vertices to be copied will be highlighted in red.
then from the "Edit" menu choose "Duplicate Selection". This action will create a duplicate triangle that will be drawn over the original triangle.
|
 |
| Move the triangle along the Z axis At this point two identical triangles have been created. One triangle resides on top of the other. On the model tab near the top, choose the "Move" button. Inside the "Move Options" group below add 6 to the Z text box. Then press the "Move" beside it.
|

|
| Ensure that only the duplicate triangle has been translated If the duplicate triangle was translated properly along the Z axis, the triangles should appear side-by-side. If only one triangle appears press ctrl+z to undo the move. Click on the groups tab and select only one of the triangles. Then repeat the move until the two triangles appear separately.
|
 |
| Select the inner triangle On the "Model" tab choose the "Select" button. Left click and drag the mouse around the triangle at Z = 0. To make this step easier, try using the left viewport. |
|
| Rotate the inner triangle 180 degrees about Y axis With the "Rotate" button selected in the "Tools" group, adjust your settings in the "Rotate Options" group so the surface is rotated 180 Degrees when the "Rotate" button is pressed below. Rotating the inner triangle about Y will set both faces so the non-textured black sides face each other. The texture sides will face outwards. |
|
| Ensure both skinning faces point outwards Since the textures are needed on the outside of the model, the grey side should face outwards and the black side should be on the inside. |

|
| Move the selected triangle along the Z axis away from the centre Enter "-6" in the Z coordinate text box under "Move Options" and press the "Move" button. |
|
| Ensure both triangles are 6 units from the centre In the left viewport, both sides should appear at equal distances away from the Z axis. |
|
| Create another vertex (point) at 0 on Z. Select the "Vertex" button. In the front viewport create another vertex at the top left as shown. |
|
| Draw the face In the front viewport, with the "Face" button selected, highlight the three outer left vertices. Remember to select the vertices in a counter-clockwise direction to keep the skinning surface outwards. |

|
| Ensure skinning faces display properly At this point, the face should now extend to include the new vertex at the top left. |
|
| Select the inner side on the Z axis The left projection is being used as a convenient way to isolate the face on the negative side of the Z axis. |
|
| Draw the face Using the "Face" button, connect the vertices of the inner face with the vertex at point at 0 on Z. Keep the skinning surface on the outside. |

|
| Add the remaining faces With the "Face" button selected on the "Model" tab, shade in the remaining outside surface by clicking on each vertices where shading is absent. Remember to build the face by clicking on vertices in sets of three in a counter clock-wise direction. |
|
| Merge all groups into one select the "Groups" tab. click on each group name and choose the "Select" button so all vertex groups in the viewports are highlighted in red. when all areas are highlighted, click on "Regroup" to combine the entire shape into one group. |
|
| Rename the new group Add the new name "house" to the "Rename" text box and click on the "Rename" button. |
|
| Check the name The entire shape will now be listed at the "house" group. |

|
| Add a sphere select the "Model" tab. press the "Sphere" button. click and drag into one of the viewports to add a sphere. |
|
| Move the sphere into place and scale it On the "Model" tab, select the "Move" button and drag the sphere into place. Then press the "Scale" button. When "Scale" is selected use the mouse to drag, push, and pull the sphere to flatten and shape it. |
|
A two boxes perpendicular to each other for the fan On the "Model" tab, press the "Box" button. Then click into one of the viewports to add it to the model. Select the "Scale" button and use the mouse to drag, push, and mould it into shape. Once the box is flattened and shaped as needed, from the "Edit" menu, choose "Duplicate Selection" to generate a duplicate box. Select only one of the boxes. Press the "Rotate" button and rotate it by 90 Degrees.
|
 |
Merge the boxes and rename the groups select the "Groups" tab. merge the two boxes by selecting them both and choosing "Regroup". rename the sphere to "pin". rename the combined boxes to "fan".
|

|
Create a material You can try experimenting by using more than one texture. Be aware though, it is a common for games programmers to use only one texture. You may get varying results with multiple textures. To achieve the appearance of multiple textures create the bitmap so it contains a cross section of different textures.
|
 |
| Set-up a material select the house group and click on the "Material" tab. click "New". This will generate a grey ball as shown. |
|
| Set the bitmap and rename the material click "<none>". This will prompt an "open dialog" which prompts you to select a bitmap. navigate to the bitmap. only 1 bitmap should be used so modify the image to contain all of your textures inside the material tab, rename your texture to "windmill". Your image pixel height and width dimensions must be a power of 2 or they may not load properly. Ie.2, 4, 8, 16, 32, 64, 128, 256, 512 |
|
| Assign the material to the house go back into the "Groups" tab and ensure that only "house" is selected and therefore is highlighted in red. go to "Materials" and click "Assign". from the "Window" menu choose "Texture Coordinate Editor" move, rotate, and scale the house until you find a section of texture that fits your model. |
|
Assign the material to each remaining piece of the windmill Repeat the process above by selecting the fan and pin groups by themselves and selecting "Assign" in the "Materials" tab. Assign an appropriate region of texture to each. When in the "Texture Coordinate Editor" be sure to select the proper group from the drop down and select "Remap" to set the view and vertex group needed. For better precision in mapping textures you could create your groups one side at a time or select a subgroup of vertices when assigning a material. Having smaller vertex groupings enables better control over texturing. The texturing in this example is less than precise since it wraps the texture around the entire house component of the windmill.
|
|
View the fully textured model When finished assigning textures, right click on the lower right viewport and select "Textured". This will show the different parts of the bitmap applied to each group. When the appearance is satisfactory, merge the pin and fan groups. Rename the new group to fan.
|
|
Save your project and export it Save your Milkshape project. Since we are going to animate this model programmatically, we will export the fan and house separately. When the fan is exported the house should be deleted from the project and vice versa. Note: To ensure the fan and house animate properly, before exporting these separate model objects position these objects so their connection point is at the origin . (See the images to the right which shows the fan and house at the origin where each model piece is connected) Positioning these model pieces so their attachment point is away from the origin would add a translation to the animation. Recall that combining a translation with a rotation causes an orbit. You probably don't want an orbit for a windmill animation because this would cause the fan to rotate about an external point which would look weird and would be difficult to debug. Instead you need a revolutioin where you spin the fan about the Z axis.
|
 |
When exporting each piece choose, from the File menu choose Export and select Alias FBX . Save your models to the same directory where the texture is located. This will help you later when your place these files in your XNA project so the model loader can easily find the path to the texture.
|
|
When the process has been completed, you should be left with three separate files that will be placed in the source folder for the XNA project; fan.fbx , house.fbx , and windmill.bmp . Also, the entire Milkshape project, windmill.ms3d, should be kept for later modifications if needed.
Coding the Model Coding models is simple with XNA's model class. You can use the model class to load the model and light it. Loading and Animating the Windmill in Code This example takes the windmill you made in MilkShape and animates it in code.
 |
This code example begins with XNAStarter files. Also, your fan.fbx and base.fbx files, along with the windmill texture (windmill.bmp) models, must be added to your project so they can be loaded in the content pipeline. Or, if you don't want to build these models, you can find the fan.fbx , base.fbx , and windmill.bmp files in a Models folder. To create this models folder in your project, right click the project name, select Add , then New Folder and name it Models . |
 |
These three files need to be placed in a Models folder in your project so they can be loaded properly. You will also need to reference the *.fbx files in your project. To do this, right click the Models folder and choose Add . Select Existing Item. Then navigate to your 3D models and select them. When you are finished, you models should be referenced in Solution Explorer as shown here. |
Note: Your windmill.bmp file must also be placed in this folder in your project but do not reference it. It will be loaded automatically when the model files are loaded in your code. Naming conflicts will occur if an identically named model exists in the same folder. To store the fan and base models separately, two separate Model objects are declared at the module level of the game class. Matrices for transforming the meshes in each model are also included with this declaration so they can be set later when the models are loaded.
| Model mModBase; Model mModFan; Matrix[] matFan; Matrix[] matBase; |
The same code will be reused to draw each model, so identifiers are needed to distinguish between the windmill base and fan model. These definitions are used throughout the game class, so they need to be added at the top of the game class.
| const int WINDMILL_BASE = 0; const int WINDMILL_FAN = 1; |
The two models are loaded separately with the ContentManager's Load() method using a <model> identifier. When each model is loaded, the bone matrices are stored in a Matrix object for that model. These examples have only one bone, so any transformations applied to them will apply to the entire model. The CopyAbsoluteBoneTransformsTo() method copies the transformations for all bones in the model into an array that the model object can use.
void initialize_windmill_model(){
mModBase = content.Load<Model>(".\\Models\\house");
matBase = new Matrix[mModBase.Bones.Count];
mModBase.CopyAbsoluteBoneTransformsTo(matBase);
mModFan = content.Load<Model>(".\\Models\\fan");
matFan = new Matrix[mModFan.Bones.Count];
mModFan.CopyAbsoluteBoneTransformsTo(matFan);
}
|
To set the model up when the program begins, call initialize_windmill_model() from the Initialize() method:
| initialize_windmill_model(); |
To create a continuous rotation for the windmill fan, a module-level variable, mfFanRotation , is used. The module-level variable stores the total rotation in radians and is incremented each frame. Adding it at the module level allows you to store the variable and read its updated value each frame:
| private float mfFanRotation = 0.0f; // stores rotation of windmill fan |
Drawing the windmill base is actually very simple. The first three steps used to create the transformation are are similar to the steps taken before drawing a primitive surface. In steps four and five the texture is applied and the model is drawn:
void draw_windmill(Model model, int iModel, GameTime gameTime)
{
foreach (ModelMesh mesh in model.Meshes)
{
// 1: declare matrices
Matrix matWorld, matIdent, matScale, matRotY, matRotZ, matTransl;
// 2: initialize matrices
matIdent = Matrix.Identity;
matScale = Matrix.CreateScale(0.04f, 0.04f, 0.04f);
matTransl = Matrix.CreateTranslation(0.0f, -0.43f, 4.5f);
matRotZ = Matrix.CreateRotationZ(0.0f);
if (iModel == WINDMILL_FAN)
{
// calculate t between frames for system independent speed
mfFanRotation += gameTime.ElapsedRealTime.Ticks / 6000000.0f;
// prevent var overflow store remainder
mfFanRotation = mfFanRotation % (2.0f * (float)Math.PI);
matRotZ = Matrix.CreateRotationZ(mfFanRotation);
}
// 3: build cumulative world matrix using I.S.R.O.T. sequence
// identity, scale, rotate, orbit(translate&rotate), translate
matWorld = matIdent * matScale * matRotZ * matTransl;
foreach (BasicEffect effect in mesh.Effects)
{
// 4: pass wvp to shader
if (iModel == WINDMILL_BASE)
effect.World = matBase[mesh.ParentBone.Index] * matWorld;
if (iModel == WINDMILL_FAN)
effect.World = matFan[mesh.ParentBone.Index] * matWorld;
effect.View = mMatView;
effect.Projection = mMatProj;
// 4b. set lighting
effect.EnableDefaultLighting();
effect.CommitChanges();
}
// 5: draw object
mesh.Draw();
}
}
|
To draw both models, call them from the Draw() method:
draw_windmill(mModBase, WINDMILL_BASE, gameTime);
draw_windmill(mModFan, WINDMILL_FAN, gameTime);
|
When you run this program, you will see how great the windmill looks in your game. The output shows your windmill with the fan rotating about the Z axis. You may find that additional scaling, rotations, or translations are needed to move your own models into place depending on how your windmill was built. In the end, you will find you can create, load, and render 3D models with very little effort. |