Update – please download take 2 - patch 1
- hack to load non compliant exports from Blender
- fixed bug reported below in this thread
Although there has been many changes since the first post, those changes are mostly structural, in order to finally be able to create a set of model/mesh/material that can be extended to all what COLLADA has to offer.
As the previous thread discussion mentioned, Model, the 3D model primitive provided by XNA is ‘just a sample’ and should be replaced with something that fits better the application. In our case, we need a COLLADAModel.
The problem is that there is a lot of hidden and non extensible code associated with Model, which makes it really difficult to replace. Once I started adding a COLLADAModel, I realized that ModelMesh cannot be used, and need to be replaces as well. The reason is that in order to create a Mesh, one has to provide a MeshContent, and attach it to a ModelContent, and then use the <Model> load/processor, which will do the conversion to the necessary vertexBuffer, indexBuffer and so forth.
So the task to replace the sample Model to something else is large, since a lot of code needs to be provided to replaceModel Mesh, ModelMeshPart.
Since COLLADA is very flexible, the main effort to create the IndexBuffers and VertexBuffers can be done directly in a COLLADA conditionner. So I created and added an re-indexor condionner that pack all the float arrays in a single array, and create the indexes into this array.
(For the curious)
The resulting COLLADA primitive have <source> without any <float_array> data, their <accessor> is referencing the one <source> that has the single float_array. This was designed like this since 1.0, but I am curious if COLLADA importers can cope with this construct. Would be interesting to add a ‘save document’ feature to find out.
Then I create COLLADAModel, and associated COLLADAModel.ModelMesh, COLLADAModel.ModelMeshParts
I also added COLLADAModel.ModelBone, and COLLADAModel.BasicMaterial since it was easy enough. Converting from COLLADAModel.BasicMaterial to BasicEffect is actually possible, since BasicEffect has been designed to be created at run-time, and not only in the content pipeline.
Those new objects are mimic of the original XNA corresponding ones, except for the following:
The Indices are not packed per mesh, but are an index vector per MeshPart. There is no reason for this expect that I did not do the packing. I also did not try to optimize the index to short, or to test if the number of indexes is larger than what the graphics hardware is supporting… so some more work is needed there.
I discovered that XNA Model does not support instancing. Since the Mesh reference back to the bone (position) and ModelMeshPart to their Effect, it is not possible to reuse the same Model at different position, with a different Effect. It was weird, since this is a very basic feature that I did not expect would be missing. So I added InstanceMesh, which reference a Mesh, a Bone, and has a list of Effect to be used for all the MeshParts
In addition to having extensible objects as a basis for additional developement, I also wanted to add the capability to load content either through the content pipeline, or directly in the application, even on the 360. In game development, it is very important to allow for two data path: the complete path that goes through the full content pipeline, and the fast-path, that enable last minute art to be injected in the game for artist to do in-game tuning, it is also necessary if you want to create a level editor to be able to load the data in the application directly.
So this version enables:
- loading COLLADAModel using the content pipeline
- loading COLLADAModel without using the content pipeline (direct file load)
- loading the COLLADA Document with the content pipeline, and then creating the COLLADAModel in the application
- loading the content as a XNA Model through the content pipeline. This is the ‘old’ code that I kept in there for comparison
One interesting note, is that you’ll notice that content.load<Model> does create index and vertex buffers. To do this, you need to have access to the graphicsDevice, since the graphics driver is necessary to create those objects. But the only ‘plug-in’ for content.load is the ContentTypeReader<>, which does not have graphicsDevice as a parameter. So you have to finalize the creation of your own ‘Model’ after calling content.load<MyModel>… This is weird, since this is really a design where hidden code happens on load<Model>, and this is contradicting the claim that Model is really just a sample designed to be replaced… at least not easily.
Anyway, now that this is done, it is now possible to add COLLADA features to XNA.
Unlike XNA COLLADA animations are not limited to tranforms. One can anything that has an id, including textures, colors and so forth… should it be the next feature ?
XNA drawing loop first select a Mesh, and then loop through all the MeshParts in this mesh. Each MeshPart has its own Effect. Switching Effects is in general the most expensive thing to do in graphics. So the rendering loop is wrong. It should be for each Effect, render all the MeshParts that are using this Effect.
In addition, drawing it is important to draw transparent objects last, and in a back to front order, which is not possible with the current structure. So more changes are necessary to Mesh and MeshParts.
It is not clear to me how skinning needs to be implement. Does BasicEffect has support for skinning, or should the skinning be done in the CPU, or should a full FX, with shader code, be provided to do skinning ?
So, what do you think ?
Should the code be re factored differently ?
What feature should be done next ?
How many bugs did I introduced
Thanks for reading.