COLLADA for XNA

Since I noticed some interest here and there, I spent a few days learning C# and XNA express, and as a result I am ready to contribute a COLLADA loader for the XNA pipeline.

This zip contains two C# projects:

  • COLLADA document, which is a pure C# fine (no XNA involved) that can be used in any C# application to load a COLLADA document in memory. It is not complete, since I only implemented what I needed so far, but a good start !
  • COLLADA pipeline, which is using COLLADA document, and implement a XNA pipeline for XNA.

XNA pipeline is in 2 stages. The Importer and the processor.

In the Importer, this will load the COLLADA document in memory, and then apply a conditioner (a simple convex mesh triangulation is included), and then can save it as a binary file (serialization).
In the processor, this will convert COLLADA document (loaded back from binary) into a NodeContent using Basic Material.

In order to load COLLADA documents in your XNA application, simply add the COLLADAPipeline.dll in the ‘Content Pipeline Assemblies’ properties of the ‘Content Pipeline’. double-click on the properties in your solution explorer to get this option.


IE users: view entire image

Then you will have to make sure the .dae files are added to your content in the project, and that you have associated the right content pipeline tools for it. Then you can use the regular content.Load<Model>() call to get your COLLADA models in your XNA application, just like you do with the embedded .fbx and .x loaders


IE users: view entire image

For your convenience, here is a sample XNA viewer application, including some models that I grabbed from the COLLADA file repository.

What’s next?

Well, I am not sure. I am waiting for feedback to see what we should do next. But the one think I am not sure of, unless I am missing something, is that the XNA pipeline forces you into using XNA sealed classes, that I do not see how the pipeline can be extended to understand what is a Bezier spline, a line primitive, a morph or skinned character, or a physics scene.
Say, if I wanted to do a complete COLLADA viewer, it seems that it would have to be done without using the XNA pipeline ?

Hi,

thanks for doing this, I have an interest in both COLLADA and XNA and its great to have a chance to use them together, if only to explore where they overlap.

Regarding the two-stage pipeline you mention - The XNA Content Pipeline will not use your content reader/writer classes. The dae document will be imported (dae -> Document) and then processed (Document -> ModelContent), the ModelContent will then be serialized either to XNA intermediate XML (used like compiler .obj files) or to XNA runtime binary. Your content reader/writer classes would enable the Document to be stored in the XNA runtime binary and then loaded into a XNA program (like your SimpleXNAViewer). Perhaps that is what you meant and I just mis-read your post.

I’m curious as to what lead you to think you were being forced into using XNA sealed classes - many of the classes are open and designed to be extended with many virtual methods so that you can easily customize the processing. Adding new types such as the bezier spline you mention only requires that you create a ‘content’ type to represent the bezier spline; then importers can import to it and processors can process to or from it. There are already types for representing skinned characters (BoneContent, BoneWeight) though they may not be correct for representing COLLADA skinned characters.

It’s interesting that you decided to represent the COLLADA DOM as a single type (Document). If I was to write a COLLADA importer I might instead have imported the contents of the COLLADA document into the XNA Content Pipeline ‘DOM’, using the current *Content types and/or adding new types to represent COLLADA types (camera, light, material, effect, geometry, etc.). Then there could be different processors to convert parts of the ‘DOM’ to various runtime representations.

Cheers,
Leaf.

Yes, that was I meant by 1st phase and 2nd phase.

I thought there was a serialization phase between the 1st and the second phase. So I provided the COLLADAReader and COLLADAWriter… but thanks to you just realized that those are not used …

I see. It’s easy actually, just adding the following code, and selecting ‘COLLADA out’ processor for one of the .dae files, I can load the COLLADA Document direcly in the viewer:



COLLADAPipeliner.cs

 [ContentProcessor(DisplayName = "COLLADA out")]
    public class COLLADAOUTProcessor : ContentProcessor<Document, Document>
    {
        public override Document Process(Document doc, ContentProcessorContext context)
        {

            return doc;
        }
    }

viewer.cs

Document doc = content.Load<Document>("Content\\Models\\cube");


Although that most probably would not be too useful, since the application (Viewer) will then get a COLLADA document, and then will have to do all the conversions (COLLADA -> XNA.graphics) in the application, instead of in the content pipeline.

On the same note, the ‘COLLADAProcessor’ should simply call the batch mode of the Refinery, instead of having to write again all the conditioners ?
XNA pipeline is for going from COLLADA to game specific data, the last stage in the COLLADA content, everything that can be done within the COLLADA document is better done before any conversion.

That is probably because I am missing something.

What I do not understand is how a NodeContent gets converted in a Model.

while the ouput of the above code is a COLLADA.Document, and the load gets a COLLADA.Document, the output of the processor in the sample code is a ModelContent, that gets magically converted in a Model in the viewer.

Another problem is that Model is a sealed class, so there is no way to extend it.

I am not sure how to extend this, with for example a line primitive, without having to rewrite everything ?

Well, the first thing is to be able to load the COLLADA document, which is what this sample code does. Now that this is accomplished it would be nice to get all the content from it, using the pipeline if possible.

But I do not understand how this works, since I can only associate one [import,process] pipeline to a COLLADA document. Once I have told XNA that I want ‘Model’ out, then I cannot get ‘Animation’ as well. Unless I copy the COLLADA document in many files, and then associate a different content pipeline to each ? That can’t be right ?

I have been looking into this and came some some conclusions:

Model is the object provided by XNA to represent triangle meshes in a hierarchy. It is well suited for articulated 3D content, probably also works for skinning, although it seems not directly supported by XNA yet. Model also contain the Material that is to be applied to the mesh, and has easy to use API to render, and probably provide good performance.

In order to create a Model, one has to create a ModelContent, and a ContentProcessor<Document, ModelContent> Processor, or an content importer and associate manually the document with the right importer/processor in the built pipeline. Then the application can directly use load<Model> and use it. This is what the code above is doing. Note that the split between importer/processor is somewhat arbitrary, but it is probably wise to have the importer output a ModelContent, if the goal is to reuse some other ModelContent processor.

So now that we have a Model, how do we move forward and add al the other data we need, like lights, cameras … or extend it to understand other primitives such as lines, splines…

I could find only 3 ways to do that:

  1. make a as many copy of the file you want to get the content from. Create as many ‘importer/processors’ that matches with the objects you need. One of those processors will be a model content processor so you can get ‘Model’ in the application. Then write has many load<type> in the application with the matching filenames of the copies, and make sure the type in the application matches with the output of the processor on each copy. If the input file contains a library of models, create as many copies as you have models and process those files to only contain one single model.

  2. Extend Model. Well, you cannot since Model is a sealed class. But there is a possible hack. I have seen many samples using the ‘Tag’ element of ModelContent to store the real data, while returning a ModelContent in the processor. Then in the application, a final processing is done, reading the data in the Tag, and doing the binding with the Models. The binding is not possible before since you do not have access to Model in the processor.

  3. Do not use Model, and its associated Modelxxx classes. Basically, do not use the provided XNA DOM, and create your own mesh, material … with their associate rendering API. This is what some of the more advanced samples I have seen.

Well, 1) is just impractical, I would even say crazy. 2) is tempting, since I am all in favor to use what’s provided, but with experience you learn that hacks a re bad, and always hurt you later on. 3) seems a lot of work, so not too appealing !

There is one additional thing to consider. If you want you application to be able to load content from external sources, a http server, a memory stick, then you have to be able to load the content without the build pipeline. Say you want to provide a menu, with ‘load’ in a level editor tool you are doing. So, this pretty much rules out 2), since without the built pipeline, the application wont be able to create a ‘Model’ anyway.

Conclusion, unless I am missing something, the only way to move forward is not to use the XNA DOM.

I really hope someone will prove me wrong.

Hi, apologies for the slow reply I’ve been travelling lots lately.

ModelContent and the runtime types Model are an example of one way of representing triangle meshes. The Model*Content types are not the generic way to represent meshes in the XNA Dom, you could think of them as an example of the end output of the XNA content pipeline.

The more generic types that I would expect an importer to target are types like NodeContent, MeshContent, GeometryContent and other types in the Microsoft.Xna.Framework.Content.Pipeline.Graphics namespace. You might also create new types to support other features.

You might import a COLLADA document into entirely new .Net types that represented the COLLADA types (camera, light, geometry, mesh, etc.). Then you could provide content processors that converted those types into GeometryContent, MeshContent, etc. End users can then build content processors that convert MeshContent etc. into their runtime types (ModelProcessor and Model*Content are an example of that). End users could also use your new COLLADA types to access extra detail.

Looking through your code, this is very close to what you have already done you just seem to have got a little hung up on ModelContent. Instead of having a seperate converter (Processor.Convert()) wrap that as a content processor (ContentProcessor<Document, NodeContent>), then other processors, like ModelProcessor, can be used to convert NodeContent to the end output, like ModelContent.

There a couple of ways of dealing with this. The simplest is if you can arrange for the external content to have already been built by the content pipeline, then you only need to derive a new ContentManager that can access the external sources (this could be as easy as overriding one virtual method). If you can’t arrange for the external content to be pre-built then you can use the content pipeline from your application as it is exposed as MSBuild tasks (this is how Visual Studio uses the content pipeline) and the MSBuild Api and program are included with the .Net Framework. This requires a little more work and currently the content pipeline assemblies are only distributed with XNA GSE but the XNA devs have mentioned that they would consider making them part of the XNA runtime or creating a new redistribution package.

Cheers,
Leaf.

I have been working with XSI and attempting to utilize DirectX as my intermediate file format. I have had very mixed results and was looking at potential alternatives. The solution you are developing could help move me forward if COLLADA turns out to be a better intermediate format coming out of XSI. Just wondering where the project currently stands and where it is going and if I might be able to provide any help.

Happy new year to all of you !

Yes, I’ve been vacationing lately, so here’s my excuse for a slow reply :lol:

Interesting. I was surprised at first, but then I read the same thing from the XNA team on their forum. Surprising to me that a example would be provided as a binary sealed class rather than as a sample code…

I am still missing something:

  • How do I transform a MeshContent into a ModelMesh ? How do I convert a MaterialContent ? The only way seems to use the ModelProcessor, return a ModelContent, and then let XNA do its magic ?

  • How do you extend MeshContent and ModelMesh to understand Lines and TriangleStrip primitives ?

I do not see how. Like I said, I do not know how to put lines into mesh content, nor how to convert MeshContent to ModelMesh without using the ModelContent structure and processor.

Probably, but it would be better if I could provide at least a working example :?

Could you point me to some documentation or/and source code to those ‘other processors’ ?

There a couple of ways of dealing with this. The simplest is if you can arrange for the external content to have already been built by the content pipeline, then you only need to derive a new ContentManager that can access the external sources (this could be as easy as overriding one virtual method). If you can’t arrange for the external content to be pre-built then you can use the content pipeline from your application as it is exposed as MSBuild tasks (this is how Visual Studio uses the content pipeline) and the MSBuild Api and program are included with the .Net Framework. This requires a little more work and currently the content pipeline assemblies are only distributed with XNA GSE but the XNA devs have mentioned that they would consider making them part of the XNA runtime or creating a new redistribution package…[/quote]

Maybe it is just me, but embedding the full MSbuild in order to be able to load a file seems a bit too drastic. Precompiling the files, or embedding the MSBuild wont solve another issue, the capability for the application to build the content on the fly. Unless the application create the content, save it in a file, invoke the MSbuild, load back the binary version…

Moving forward…

So I wrote a COLLADA conditionner that massage the geometry into a vertex buffer. Basically computing a single index list , and a single float array per primitive. It is a COLLADA -> COLLADA conditionner, since COLLADA is flexible enough for all kind of data stream organization.
I wrote a COLLADA.ModelMesh that can take this data, and understand all primitives (lines, triangle strip, triangle fan), change the orientation of triangles and the texture coordinates for directX.

One problem is that in order to create the vertexBuffer, one need to have a graphic device, which does not exist in the content pipeline. So the final stage of creating the ModelMesh needs to be done in the application.
I though it should be done in the DocumentReader, since the load<Model> does it for you, but I do no know how to access the graphic device in this routine.
It seems I have to do a createVertexBuffer after the content.load<COLLADA.ModelMesh>(), which is not elegant … any advice ?[/b]

I’ll keep working on it if there is enough interest.
Did you try the code I already posted ? does it work for you ?

I would gladly accept help. One thing that would be great is if you could provide sample data, and test out the loader. Also, let me know what features are needed despite default geometry and basic material loader.

Something else that might be an option is Collada.Net from mogware.

Hi,
I am researching how to load collada files (.dae) into xna. I’ve come across one titled ‘Bunkerbewohner-ColladaXna-0333e1a’, when i try to execute the solution it throws up an error:

’ A project with an Output type of Class library cannot be started directly.

In order to debug this project, add an executable project to this solution which references the library project.
set the executable project as the startup project.’
I added a new XNA 4.0 project and added references to the included library, then set the start up point to my new project, now I just get the corn flower blue background when i run it.

Has anyone had any luck with this particular content loader, and can anyone point me in the direction of any others?

The XNA Content Pipeline will not use your content reader/writer classes.