Jump to content

Integrating Your XNA Engine With XSI ModTool

float3 effect model content material set modtool float4 project
Learn how to use XSI|ModTool's XNA integration features to create a content authoring pipeline for your engine

4: Adsense

For many a hobbyist developer, the XNA Framework has been a gift from above. The combination of a robust game-development framework with MicrosoftÆs venerable Direct3D graphics back-end can be a winning combination for student programmers making their first game, or bedroom coders who donÆt have time to waste on creating tedious boilerplate code. With all of the framework classes and samples dedicated to showing you how easy it is to get a very simple game up and running in 30 minutes, itÆs pretty easy to forget that with XNA itÆs possible to craft complex, professional-level games by making the most of features like the Content Pipeline (even without a professional budget!). In that regard, this article is going to walk you through an implementation a 3D content authoring system that can allow you to seamlessly integrate XSI ModTool with your gaming engine.

This article assumes at least basic familiarity with: C#, the XNA Framework graphics classes, the XNA Framework Content Pipeline, and HLSL. To compile the sample code, you will need to have XNA Game Studio 2.0 installed as well as Visual Studio 2005 (Visual C# Express 2005 can be used). The sample project also references the XSI Crosswalk Importer assembly, which is installed to the ModTool installation directory. Make sure this reference is properly set before compiling (the section titled ôPublishing The Model And Importing It Into The Content Pipelineö details this process).

Why Bother With Content Authoring Integration?
If you scan through the rest of the article and see the amount of work involved, you may be wondering ôwhy should I even bother with this?ö Or you may be thinking ôthis seems like overkill for my small project.ö After all the XNA Framework is rather flexible, and itÆs perfectly feasible to find other ways to get content into your game that doesnÆt just use BasicEffect. For example, the ShipGame starter pack uses a custom NormalMapping effect for all meshes. And it does it in a very simple way: by simply ignoring the effect assigned to the mesh being rendered and using the NormalMapping effect instead. This of course works, but has limitations: What if you donÆt want to use just one effect for everything? What about effect parameters that arenÆt texture-based? Should the artists be messing around with the code to get what they want?

Content authoring integration does not have these disadvantages. Instead it has the following advantages:

  • Artists can handle creation and importing of models without programmers getting involved
  • Artists can get a real-time preview of the model in the modeling application, allowing them to get what they want quicker
  • Every aspect of a modelÆs appearance is defined in the modeling tool, and completely contained in the model fileÆs data. If a consistent material parameter interface is used as well, this allows you to greatly simplify your rendering code as it wonÆt need to handle any special cases ù all models are treated the same (this is a benefit even if youÆre a one-man-band: simple code is always better).
Why XSI ModTool?
When it comes to 3D modeling, 3D Studio Max and Maya are usually the first names to come up. TheyÆre fantastic, full-featured applications and itÆs much more likely that a 3D artist is going to be familiar with one of them. However they pose a huge problem for any hobbyist developer: they cost money ù a lot of money. ModTool, on the other hand, is completely free for non-commercial use. While there are other free tools available (such as Blender), ModTool is conveniently designed to be integrated with XNA Game Studio project. Plus, it supports the ability to use real-time previewing of Direct3D effects with models, which is crucial for our content integration system.


An Effect-Based Material System
3D models have two important attributes weÆre concerned with: geometry and materials. The geometry will determine the shape of the model, while the materials will determine what the surface of that geometry actually looks like. For our content authoring pipeline, weÆre going to use Effects as primary building blocks for materials. Each material effect will determine the basic type of material weÆre working with: some examples can include a basic texture-mapped surface, a normal-mapped surface, a reflective surface that uses an environment map, a surface with a fur shader, a metallic surface, a cel-shaded surface... whatever it is the actual game calls for.

Each material effect will have a set of artist-editable parameters, which can be tweaked in ModTool (in real-time) in order to further customize an effect. In the actual effect these parameters are implemented as textures or as shader constants.

A Consistent Effect Interface
One of the goals we laid out earlier was that we wanted our material effects to be interchangeable as far as our rendering code is concerned. This means we donÆt want to have to treat any of our materials any differently: the code should be able to just set the shader constants it needs to set the same way for every effect. To facilitate this, weÆre going to create a file containing the shader constants common to every effect and #include it in every material. WeÆll call this file ômat_Common.fxhö, and it looks like this:

float4x4 g_matWorld; float4x4 g_matWorldInverse; float4x4 g_matView; float4x4 g_matProj; float3 g_vCameraPositionWS; float3 g_vLightAmbient; float3 g_vLightDirectionWS; float3 g_vDirectionalLightColor; We have a few basic constants here: transform matrices used to transform vertices to the various coordinate spaces, the camera position in world-space, and ambient lighting color, the direction of a directional light in world-space, and the color of the directional light. For now weÆll keep things simple and leave it at one directional light.
A Normal-Mapping Shader
As our first material type, weÆre going to implement a basic normal-mapping shader. If youÆre not familiar with normal-mapping, it works by sampling a per-pixel normal value from a texture and using that value for lighting calculations. This allows an otherwise flat surface to have the appearance of having much more geometry. These normal values we sample from the texture are in tangent-space, which means in the vertex shader we transform the light direction and the view direction to tangent-space so that we can perform the lighting calculations.

Before we write our vertex shader and pixel shader, letÆs set up some parameters and textures. For parameters weÆre going to need a specular color and power (glossiness), and for textures weÆre going to need a diffuse map and a texture map.

float3 g_vSpecularAlbedo; float g_fSpecularPower; texture2D DiffuseMap; sampler2D DiffuseSampler = sampler_state { Texture = ; MinFilter = anisotropic; MagFilter = linear; MipFilter = linear; MaxAnisotropy = 16; }; texture2D NormalMap; sampler2D NormalSampler = sampler_state { Texture = ; MinFilter = anisotropic; MagFilter = linear; MipFilter = linear; MaxAnisotropy = 16; }; For our vertex shader, we first need to set up our vertex inputs. Models that are exported from XSI ModTool have a particular vertex format, which actually encodes the binormal and tangent in order to save space. The inputs for our vertex shader look like this: in float4 in_vPositionOS : POSITION0, in float3 in_vNormalOS : NORMAL0, in float4 in_vColor0 : COLOR0, in float4 in_vColor1 : COLOR1, in float2 in_vTexCoord : TEXCOORD0, in float4 in_vTexCoord1 : TEXCOORD1, in float4 in_vTexCoord2 : TEXCOORD2, in float4 in_vTexCoord3 : TEXCOORD3, in float4 in_vTexCoord4 : TEXCOORD4, in float4 in_vTexCoord5 : TEXCOORD5, in float4 in_vTexCoord6 : TEXCOORD6, in float4 in_vTexCoord7 : TEXCOORD7 Now like I mentioned, we need to do some unpacking of our binormal and tangent. The code for that looks like this: // Calculate the tangent and binormal float3 vTangentOS = (in_vColor0 * 2) - 1; float fSign = (in_vColor0.a * 2) - 1; fSign = (fSign > 0) ? 1 : -1; float3 vBinormalOS = in_vNormalOS.yzx * vTangentOS.zxy; Okay now wereÆ all set up and ready to code our shaders. HereÆs the final mat_NormalMapping.fx file: float3 g_vSpecularAlbedo; float g_fSpecularPower; texture2D DiffuseMap; sampler2D DiffuseSampler = sampler_state { Texture = ; MinFilter = anisotropic; MagFilter = linear; MipFilter = linear; MaxAnisotropy = 16; }; texture2D NormalMap; sampler2D NormalSampler = sampler_state { Texture = ; MinFilter = anisotropic; MagFilter = linear; MipFilter = linear; MaxAnisotropy = 16; }; void NormalMappingVS( in float4 in_vPositionOS : POSITION0, in float3 in_vNormalOS : NORMAL0, in float4 in_vColor0 : COLOR0, in float4 in_vColor1 : COLOR1, in float2 in_vTexCoord : TEXCOORD0, in float4 in_vTexCoord1 : TEXCOORD1, in float4 in_vTexCoord2 : TEXCOORD2, in float4 in_vTexCoord3 : TEXCOORD3, in float4 in_vTexCoord4 : TEXCOORD4, in float4 in_vTexCoord5 : TEXCOORD5, in float4 in_vTexCoord6 : TEXCOORD6, in float4 in_vTexCoord7 : TEXCOORD7, out float4 out_vPositionCS : POSITION0, out float2 out_vTexCoord : TEXCOORD0, out float3 out_vLightDirTS : TEXCOORD1, out float3 out_vViewDirTS : TEXCOORD2, out float3 out_vPositionWS : TEXCOORD3 ) { // Figure out the position of the vertex in clip space out_vPositionWS = mul(in_vPositionOS, g_matWorld); float4x4 matViewProj = mul(g_matView, g_matProj); float4x4 matWorldViewProj = mul(g_matWorld, matViewProj); out_vPositionCS = mul(in_vPositionOS, matWorldViewProj); out_vTexCoord = in_vTexCoord; // We need these in object space before converting to tangent space float3 vLightDirectionOS = mul(-g_vLightDirectionWS, g_matWorldInverse); float3 vCameraPosOS = mul(float4(g_vCameraPositionWS, 1.0f), g_matWorldInverse); // Calculate the tangent and binormal float3 vTangentOS = (in_vColor0 * 2) - 1; float fSign = (in_vColor0.a * 2) - 1; fSign = (fSign > 0) ? 1 : -1; float3 vBinormalOS = in_vNormalOS.yzx * vTangentOS.zxy; vBinormalOS = (-vTangentOS.yzx * in_vNormalOS.zxy) + vBinormalOS; vBinormalOS = (vBinormalOS * fSign); // Build the TBN matrix float3x3 matTBN = float3x3(vTangentOS, vBinormalOS, in_vNormalOS); // Convert to tangent space out_vLightDirTS = mul(matTBN, vLightDirectionOS); out_vViewDirTS = mul(matTBN, vCameraPosOS - in_vPositionOS.xyz); } float3 CalcLighting ( float3 vDiffuseAlbedo, float3 vSpecularAlbedo, float fSpecularPower, float3 vLightColor, float3 vNormal, float3 vLightDir, float3 vViewDir ) { float3 R = normalize(reflect(-vLightDir, vNormal)); // Calculate the raw lighting terms float fDiffuseReflectance = saturate(dot(vNormal, vLightDir)); float fSpecularReflectance = saturate(dot(R, vViewDir)); if (fDiffuseReflectance == 0) fSpecularReflectance = 0; // Modulate the lighting terms based on the material colors, and the attenuation factor float3 vSpecular = vSpecularAlbedo * vLightColor; pow(fSpecularReflectance, fSpecularPower); float3 vDiffuse = vDiffuseAlbedo * vLightColor * fDiffuseReflectance; // Lighting contribution is the sum of ambient, diffuse and specular terms return vDiffuse + vSpecular; } float4 NormalMappingPS( in float2 in_vTexCoord : TEXCOORD0, in float3 in_vLightDirTS : TEXCOORD1, in float3 in_vViewDirTS : TEXCOORD2, in float3 in_vPositionWS : TEXCOORD3 ) : COLOR0 { // Sample the texture maps float3 vDiffuseAlbedo = tex2D(DiffuseSampler, in_vTexCoord).rgb; float3 vNormalTS = tex2D(NormalSampler, in_vTexCoord).rgb; // Normalize after interpolation vNormalTS = vNormalTS = 2.0f * (vNormalTS.xyz - 0.5f); in_vLightDirTS = normalize(in_vLightDirTS); in_vViewDirTS = normalize(in_vViewDirTS); // Calculate the lighting term for the directional light float3 vColor = CalcLighting( vDiffuseAlbedo, g_vSpecularAlbedo, g_fSpecularPower, g_vDirectionalLightColor, vNormalTS, in_vLightDirTS, in_vViewDirTS); // Add in ambient term vColor += vDiffuseAlbedo * g_vLightAmbient; return float4(vColor, 1.0f); } Technique Render { Pass { VertexShader = compile vs_2_0 NormalMappingVS(); PixelShader = compile ps_2_0 NormalMappingPS(); ZEnable = true; ZWriteEnable = true; AlphaBlendEnable = false; } }
Setting Up SAS Annotations
Okay so weÆve got our fancy normal-mapping shader now, and if we want we could use it to render some stuff in our XNA application. But what about in ModTool? If we used it as-is, ModTool would have no idea what to do without effect. What parameters should be set by the user? Which ones should be set automatically? And to what values? To make sure ModTool can make heads or tails of everything, we need to add some SAS (ôStandard Annotations and Semanticsö) annotations.

WeÆll start off with the shader constants in mat_Common.fxh. We said earlier that these are going to be the constants set by our rendering code, which means we donÆt want the artist to be messing with these. Instead weÆll use annotations that tell ModTool what values to set there for us. First for the matrices, we can use standard HLSL semantics to bind them to certain transforms:

float4x4 g_matWorld : WORLD; float4x4 g_matWorldInverse : WORLDINVERSE; float4x4 g_matView : VIEW; float4x4 g_matProj : PROJECTION; For our lighting constants, we have to use some SAS annotations to specify what we want. Those annotations look like this: float3 g_vCameraPositionWS < string SasBindAddress = "SAS.CAMERA.POSITION"; >; float3 g_vLightAmbient < string SasBindAddress = "SAS.AMBIENTLIGHT[0].COLOR"; >; float3 g_vLightDirectionWS < string SasBindAddress = "SAS.DIRECTIONALLIGHT[0].DIRECTION"; > = {1, -1, 1}; float3 g_vDirectionalLightColor < string SasBindAddress = "SAS.DIRECTIONALLIGHT[0].COLOR"; >; WeÆre also going to add some SAS annotations to the material parameters to specify that they are artist-editable. We can also specify some other information: the name of the parameter to be displayed, the type of UI control to use, and minimum/maximum values. float3 g_vSpecularAlbedo < string SasUiControl = "ColorPicker"; string SasUiLabel = "Specular Albedo"; > = {1.0f, 1.0f, 1.0f}; float g_fSpecularPower < string SasUiControl = "Slider"; string SasUiLabel = "Specular Power"; float SasUiMin = 1; float SasUiMax = 200; > = 32.0f; texture2D DiffuseMap < string ResourceType = "2D"; >; sampler2D DiffuseSampler = sampler_state { Texture = ; MinFilter = anisotropic; MagFilter = linear; MipFilter = linear; MaxAnisotropy = 16; }; texture2D NormalMap < string ResourceType = "2D"; >; sampler2D NormalSampler = sampler_state { Texture = ; MinFilter = anisotropic; MagFilter = linear; MipFilter = linear; MaxAnisotropy = 16; };
Setting Up Our Rendering Code
Now weÆre ready to set up some code for rendering models in our game. As promised, thanks to our consistent material effect interface, this is easy.

protected void RenderModel(Model model, Matrix modelTransform) { Matrix[] bones = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(bones); // Get camera matrices Matrix cameraTransform, viewMatrix, projMatrix; camera.GetWorldMatrix(out cameraTransform); camera.GetViewMatrix(out viewMatrix); camera.GetProjectionMatrix(out projMatrix); for (int i = 0; i <: model.Meshes.Count; i++) { ModelMesh mesh = model.Meshes[i]; Matrix worldMatrix = bones[mesh.ParentBone.Index]; Matrix.Multiply(ref worldMatrix, ref modelTransform, out worldMatrix); Matrix worldInverseMatrix; Matrix.Invert(ref worldMatrix, out worldInverseMatrix); for (int j = 0; j < mesh.MeshParts.Count; j++) { ModelMeshPart meshPart = mesh.MeshParts[j]; // If primitives to render if (meshPart.PrimitiveCount > 0) { // Setup vertices and indices GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration; GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride); GraphicsDevice.Indices = mesh.IndexBuffer; // Setup the parameters for the sun Effect effect = meshPart.Effect; effect.Parameters["g_matWorld"].SetValue(worldMatrix); effect.Parameters["g_matWorldInverse"].SetValue(worldInverseMatrix); effect.Parameters["g_matView"].SetValue(viewMatrix); effect.Parameters["g_matProj"].SetValue(projMatrix); effect.Parameters["g_vCameraPositionWS"].SetValue(cameraTransform.Translation); effect.Parameters["g_vLightDirectionWS"].SetValue(sunLightDirection); effect.Parameters["g_vDirectionalLightColor"].SetValue(sunLightColor); effect.Parameters["g_vLightAmbient"].SetValue(ambientLight); // Begin effect effect.Begin(SaveStateMode.SaveState); effect.CurrentTechnique.Passes[0].Begin(); // Draw primitives GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, meshPart.BaseVertex, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount); effect.CurrentTechnique.Passes[0].End(); effect.End(); GraphicsDevice.Vertices[0].SetSource(null, 0, 0); GraphicsDevice.Indices = null; GraphicsDevice.VertexDeclaration = null; } } } }
Working With XSI ModTool

Setting Up ModTool
Make sure youÆve downloaded and installed XSI ModTool, along with both the latest patch as well as the CROSSWALK import/export system. If this has been done, start up ModTool. If youÆve never used ModTool before, you may want to perform some customizations. This article will assume that youÆre operating in XSI Default mode (go to Modes->XSI Default). You may also find it helpful to activate the main toolbar, which you can do by clicking on View->Optional Panels->Main Shelf.

ModTool works by connecting to an existing XNA project and creating an XSI project. This XSI project contains a workspace which contains saved scenes, along with other data used by these scenes. This project is created in a subfolder of the Content folder in your project folder, which means you can include this XSI project in your source control in order to have all artists share a common workspace. To have ModTool connect to your project, click on XNA Game Studio->Connect To XNA Project. In the dialog that appears, browse for the main folder that contains your project. The dialog should say that a project has not been detected at this location, and that one will be created for you. Close this dialog to let ModTool create the project.

Posted Image
Making A Model
Now weÆre ready to make a model that will test out our new content authoring system. First, click on XNA Game Studio->New XNA Scene to create a new scene. Save it in the scenes folder of the new XSI project that was created in your Content folder.

Now that we have a scene, we can add a model to it. We do this by going to XNA Game Studio->Create Model. You should get a cube mesh added to the scene when you do this. You can view this cube in several ways by switching the view mode, which you do by clicking one of the buttons in the bottom left corner of the screen.

Posted Image The default is Four Views, which is the top right button. This gives you four different views at a time. The three views in the top left, bottom left and bottom right are typically configured to give you an orthographic (2D) view from a certain direction. The top right view is typically configured to give a 3D view of the scene. Switch the top-right view to ôExplorerö mode (click on the bluish button that says ôCameraö and select Explorer). This view presents the scene graph for your current scene. The scene graph is a hierarchal representation of everything in the scene: meshes, lights, submeshes, etc. YouÆll see that you have your camera, 3 lights, and your model in the scene. If you expand the model node, youÆll see thereÆs a mesh in there: thatÆs our cube that you see in the views. We always want meshes to be part of a model, since anything thatÆs not part of a model wonÆt be exported when we publish the model to our project. Remember this if you ever add additional polygon meshes (you can add any mesh to the model by dragging it to the Model node).

Posted Image In order to use normal-mapping and other effects that require a tangent basis, we need to add tangents to the mesh. To do this, first select the polygon mesh by expanding the Model node and selecting the ôMeshö node. Now in the toolbar on the left side of the screen, change it to ôRenderö mode by clicking on the purple button on top that says ôModelö and then click on ôRenderö. Then click on the ôPropertyö button, and click on ôTangentö. Just leave the settings at default in the dialog that pops up. After you do this, there should be a new ôTangentö node under the Mesh node.

Now we want to set up the lighting for our scene. We designed our material effect to work with one single directional. This means we canÆt use those point lights; go ahead and delete them from your scene. Now add an Infinite Light to the scene: you can do this by clicking on the ôPrimitiveö button on the left side of the screen and then going to Light->Infinite. In the dialog that pops up, donÆt worry about anything except for the ôRGBö and Intensityö values in the ôColorö area.

Once the light is added, we can position it and rotate it. Position of course doesnÆt matter for an Infinite Light, since theyÆre global light sources. Direction is what matters, and you can see the direction indicated by the arrow that points away from the light when you select it (you can select the light by clicking on it in the scene graph). To position it, go to translate mode by hitting ôVö, or by clicking on it in the main toolbar, or by clicking on the ôTö in the transform toolbar on the right side of the screen. Then you can drag it around in any of the views. To rotate it, go to rotation mode by hitting ôCö, by clicking on it in the main toolbar, or by clicking on the ôRö in the transform toolbar. You can then rotate it by dragging the mouse on one of the circles, which represent the 3 transform axes. If you need to get a better view of things, go to zoom and pan mode by hitting ôZö on the keyboard. You can then drag the mouse to move the view, zoom in by clicking the middle mouse button, or zoom out by clicking the right mouse button. If you have the top-right view set to Camera mode, you can also rotate the view by going to orbit mode (hit ôOö on the keyboard) and dragging the mouse.

Go back to the normal ôSelection cursorö by clicking on the big arrow in the top left corner of the screen. Set it to object mode as well. Now select the cube by dragging and making a box that includes it (donÆt select the light). We can now manipulate it in the same way we manipulated the light previously. However for this tutorial, just leave it positioned and oriented where it is. You can also scale the cube: to do this, either go to scaling mode (hit ôXö on the keyboard, or click on the button in the main toolbar or the transform toolbar) and drag the mouse, or set a value directly next to the ôSö in the transform toolbar.

WeÆre now going to assign a material to the mesh. As we said earlier, a material is comprised of one of our material effects, some material parameters, and some textures. Together these all define what the surface of the mesh looks like, and how it responds to lighting. Make sure the mesh is selected, and then go to the Materials Manager by either clicking on ôMaterialsö in the left-hand toolbar or by going to View->Rendering/Texturing->Materials Manager. In here we have one material already made for us already, but we canÆt use it since it doesnÆt use a DX9 effect. Now weÆll make a new one: to do this, open up an explorer window and go where you saved your mat_NormalMapping effect. With explorer on top, drag the .fx file right into the Material Manager window. You should see a new material appear with the same name as the effect. Double click on it to open up the material properties. On the first tab, we have some properties to play with. The first is the name: name it ôBrickö, since weÆre going to use a brick texture and normal map. In the middle is the .fx file being used; leave that alone, itÆs already set up to use our effect (note that you can click on the ôReloadö button if you ever make changes to the .fx file). At the bottom are the shader properties we defined, complete with the names and controls we specified. You can leave them at the default values for now.

Posted Image Move over to the ôImagesö tab of the dialog. This is where we set textures for the effect. The dialog should be prompting for two textures: the diffuse map and the normal. Click on the ônewö button to browse for a texture for each: set the diffuse map to ôBrick_Diffuse.pngö and the normal map to ôBrick_Normal.pngö. We also need to set the texture space for each texture: next to where it says ôTexture Spaceö click on ôNewö, and then select ôUnique UVs (polymesh)ö.

Now we have to assign this new material to our mesh. To do this, first make sure the mesh is selected. Then, assign the material by pressing the button that has a purple sphere with two arrows pointing at a cone (when you hover over the button, the tooltip should say ôAssign material to selected objects or clusters.ö) After this you can close the Material Manager.

Posted Image In order to see how our material actually looks, we need to turn on the DX9 real-time preview. First make sure the top-right view is set to ôCameraö mode. Then click where it says ôWireframeö and choose Realtime Shaders->DirectX9 from the dropdown. You should see the mesh in all its normal-mapped glory now.

Posted Image
Publishing The Model And Importing It Into The Content Pipeline
To export a model from ModTool, go to XNA Game Studio->Publish Model. In the dialog that pops up, hit the ôàö button and browse to the content folder of your project. Pick a name for your model, then hit OK. Now click on ôConsolidate Images and FX Filesö, and then click OK (Consolidating images and files copies all textures to Content\Textures, and effects to Content\Effects). You should now have an .xsi model in your Content folder.

Before we can add this new model to the Content Pipeline, we need to add a reference to the Crosswalk content importer. To do this, right-click on the Content project and go to the ôReference Pathsö tab. Here you need to add a path to the Addons\XNAGSE\Application\References\XNA 2.0\ subfolder of your ModTool installation directory. Then add a reference to the importer assembly by right-clicking on References and adding the ôXW4XNAö assembly.

Once youÆve added the importer, we can add the model to your project. Right click on the Content project and click on Add->Existing Item.... Set the dialog to browse for ôall files(*.*)ö, and then navigate to the Content folder add your model. Once the model is added, set it to use the Crosswalk Importer by right-clicking and going to Properties, and then selecting the correct importer in the dropdown. Then set it to use the standard Model content processor.

Now that the model is added to the Content project, we can load it in our code through the content pipeline.

protected override void LoadContent() { model = Content.Load("TestModel"); } And finally, weÆre ready to run our game and see our model in action!
By integrating our effects and rendering code with XSI ModTool, weÆve created a Content Authoring pipeline that allows the artists to control their content all the way up to the step of adding it to the gameÆs Content project. This allows both artists and programmers to adopt a streamlined workflow that keeps both groups focused on their main tasks, and also keeps rendering code simplified and data-driven. The techniques used can also be extended to a full material library featuring effect types for a wide variety of rendering scenarios.


Note: GameDev.net moderates article comments.