3 days spent on Dynamic Content Building Tool
I have spent the past 3 days on creating a tool used to build XNA content dynamically. In fact, this is a extended tool based on my creation of RealKo framework. Back in that time the framework ties to configuration of XNA content (model and xml config files) at compile time, so when users have a new model they need to render in the engine, they have no choice as they need to recompile things up and run it again.
This’s probably not a good approach of flexibility. So it’s time for me to revive things up creating a tool used in this circumstance.
(… and yeah, I just find a time swapping from my Pong Master project to this, and after this post is finished I’ll back on route.)
Dynamic Content Building with XNA
What’s the approach I will use here ?
I will let users select an arbitrary model file (mainly focus on .fbx file) to load, then the tool will render it with Deferred shading technique in the program. In fact, it’s not quite fully deferred shading as although your rendering pipeline supports that but with the simple set up of directional lights coming from all 4 ways in 3d space. So it seems everything seems bright. This is a limit as if a timeframe to develop such a tool is not much, and users want just a visual aspect with no fancy so this set up is a fast way to come up with.
Take a consideration into point lights, or spot light targeting at some areas not all area. Deferred shading would be perfectly beautiful for that. If time to creating a customization and set up of different light source is there, then this will leverage rendering pipeline and visualization even further.
So back to the point. It seems like simple enough to just let users select a model file. Unfortunately as I discover that the basic framework cannot identify and automatic discover diffuse texture mapping to each mesh of the model. Although the artist already attached the diffuse texture for each mesh back in 3d tool, and export it nicely. Yes, the information is there in export file (as I examine it via normal text editor, the export file is in ascii format so it would be great to let artist exports it in this format for your ease in debugging) but the framework only can discover only one and the first diffuse texture assigned to any mesh of the whole model. Please note for the case prior I used BasicEffect to read the texture out. I’m not sure whether there’s some ways to do.
So I solved it with another approach by using configuration file to define which mesh maps to which texture. In this way, you can use ful textureset as they are diffuse texutre, normal texture, and specular texture if they are any and available to use. That would be a great choice of rendering in terms of this technique (of course even better with the customization of light source).
In order to know which mesh is mapping to which texture. There’s a need to also export model’s structure. This method goes inside the model for each mesh, and print out its mesh index, bone index, or even do other calculations such as how big of the model, its radius, and number of polygons. Even more complicate ways to implement this is via ContentImporter or ContentProcessor in which these two I don’t go with it with this project. For the case I used, only load a new model and process it manually from the interfaces provided to access of Model class. It’s quite simple enough not to goes so complex.
With the information of model’s structure, users can take a look at the output file and see targeted mesh for its mesh index then go back to text editor to create an xml file that will be used for loading into a program later on.
The following is the sample of the xml file described a model being load.
<?xml version="1.0" encoding="utf-8" ?>
<Model>
<Name>Ship 1</Name>
<FilePath>ship1.fbx</FilePath>
<Scale>1.0</Scale>
<Meshes>
<Mesh>
<Index>0</Index>
<BoneIndex>-1</BoneIndex>
<Textures>
<Diffuse>ship1_c.tga</Diffuse>
<Normal>null</Normal>
<Specular>null</Specular>
</Textures>
</Mesh>
</Meshes>
</Model>
At this point, we could have a config file being compiled as a XNA content. That is to say both config file along with model file and its texture files are in region of XNA content. If so they are in need to be built at compile time with Visual Studio. This is not an option here. So instead I just normally read an xml config file via normal I/O provided in .net framework. The flexible class I used is XmlDocument. If you take a look at it, you will see how flexible to work with nodes, tags, and its value in xml file rather than messing up with serialization and parsing through the entire xml file. The great thing about XmlDocument class is that, it allows developers to access the target value in chain just like.
node.GetElementsByTagName(“Book”)[“Author”][“Name”][“FirstName”]
And that’s it about the approach I used to create this simple humble tool.
Things to note, erros to note
MSBuilder is a core builder used to compile content into .xnb in runtime. It’s different from ContentManager as appears in the Game1 class of normal XNA game project. MSBuilder is at another region different of ContentManager, so when you call Load() via MSBuilder it actually does on a temporary directory. A little bit more about it is that, the stable and safe version that has worked since XNA version 3.0 is MSBuilder version 2 which consists of the following
- Microsoft.Build.BuildEngine - version 2
- Microsoft.Build.Framework - version 2
It has 2 versions which are 2, and 3.5. Mostly when you add their reference into a project, the default one is version 3.5, and when compile the project and run, things go error and error stating something weird. So we better stick to version 2.0.
There’s also a file called App.Config used as a trick to bind reference conflict for Build.Framework. Its content is as follows.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
If you download your base project from somewhere to further develop on such as from App Hub, and found out that you found an error. This might be one of the cause. Please check if this file is exist in your project.
Another big chunk lies inside ContentBuilder class which provided by Microsoft for convenient in development involving with content builder. The things to note about this file is firstly the version of XNA to build against. If you switch to another version of XNA, make sure to change this line to match XNA version.
// What importers or processors should we load?
const string xnaVersion = ", Version=3.0.0.0, PublicKeyToken=6d5c3888ef60e27d";
If you build your own custom Importer/Processor, and you found an error stating that the system cannot find your custom content class while it’s reading, then you might not include the reference of those content class to your main project just yet.
*From above, there’s a high chance that you will create another library project just to hold your content information and be referenced by other projects in development. For my case, once I tried, I have at least 4 projects to hook.
- XNA game project
- Winform project
- ContentShared (just to hold content information, it’s XNA library project.)
Inside, it will be a data class and a ContentTypeReader class corresponding for each data class.
- ContentPipeline
Inside, it will be ContentTypeWriter class to write data out into .xnb file.
So ContentShared project is a central of data information for your content.
To note, I use XNA game project instead of implement a rendering inside winform application. If I do that, I have to mess up with GameTime, the controls and things will got messy. I found the solution on how to hook up render each frame of XNA project into a picture to draw on PictureBox control on winform. It requires about 10 lines of code, and it works like a charm, please check it here.
So back to reference things. You may need to try to add a reference to make the ContentBuilder knows via code by doing the following.
1.) Add a direct code into pipelineAssemblies at the very first of the code in ContentBuilder class.
static string[] pipelineAssemblies =
{
"Microsoft.Xna.Framework.Content.Pipeline.FBXImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.XImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.TextureImporter" + xnaVersion,
"Microsoft.Xna.Framework.Content.Pipeline.EffectImporter" + xnaVersion,
"E:/somepath_base/somepath_sub/bin/Debug/ContentShared.dll"
};
But note that this is not so elegant as it’s fixed. A better is to determine the current working directory via Path class and get into a correct directory.
Sample Screenshot

…
I guess that’s all about what I would like to say.
Happy coding and hacking !
posted 2 months ago