5
\$\begingroup\$

I'm trying to manually create a ModelContent instance that includes custom Bone data in a custom ContentProcessor in the XNA Content Pipeline. I can't seem to create or assign manually created bone data due to either private constructors or read-only collections (at every turn).

The code I have right now that creates a single triangle ModelContent that I'd like to create a bone for is:

MeshContent mc = new MeshContent();
mc.Positions.Add(new Vector3(-10, 0, 0));
mc.Positions.Add(new Vector3(0, 10, 0));
mc.Positions.Add(new Vector3(10, 0, 0));

GeometryContent gc = new GeometryContent();
gc.Indices.AddRange(new int[] { 0, 1, 2 });
gc.Vertices.AddRange(new int[] { 0, 1, 2 });
mc.Geometry.Add(gc);

// Create normals
MeshHelper.CalculateNormals(mc, true);

// finally, convert it to a model
ModelContent model = context.Convert<MeshContent, ModelContent>(mc, "ModelProcessor");

The documentation on XNA is amazingly sparse. I've been referencing the class diagrams created by DigitalRune and Sean Hargreaves blog, but I haven't found anything on creating bone content.

Once the ModelContent is created, it's not possible to add bones because the Bones collection is read-only. And it seems the only way to create the ModelContent instance is to call the standard ModelProcessor via ContentProcessorContext.Convert. So it's a bit of a catch-22.

The BoneContent class has a constructor but no methods except those inherited from NodeContent... though now (true to form) maybe I've realized the solution by asking the question. Should I create a root NodeContent with two children: one MeshContent and one BoneContent as the root of my skeleton; then pass the root NodeContent to ContentProcessorContext.Convert?

\$\endgroup\$
1
  • \$\begingroup\$ Solved. Answer to be posted after 9pm tonight due to new account reputation. In an nutshell, every design-time NodeContent instance gets a unique ModelBone at run-time. Details to come. \$\endgroup\$
    – cod3monk3y
    Commented Apr 2, 2012 at 20:06

1 Answer 1

4
\$\begingroup\$

Solved!

A bone will be created for each NodeContent whether it is a BoneContent or a MeshContent. In the former case, it appears the intention of the BoneContent class is simply to indicate that the node is a bone only. The bone hierarchy at runtime will match this build time NodeContent hierarchy.

In my simple case, the solution was simply

MeshContent mc = new MeshContent();
... create mesh ...

mc.Transform = Matrix.CreateTranslation(0,10.0f,0);

// NOTE: type parameter has changed from MeshContent to NodeContent
ModelContent model = context.Convert<NodeContent, ModelContent>(mc, "ModelProcessor");

which creates a single bone at runtime. For a more complicated hierarchy I could do something like:

BoneContent turtle = new BoneContent();
MeshContent body = new MeshContent();
MeshContent head = new MeshContent();
MeshContent eye = new MeshContent();

turtle.Children.Add(body)
body.Children.Add(head);
head.Children.Add(eye);

head.Transform = ...;
body.Transform = ...;
eye.Transform = ...;

// create the one-eyed turtle
ModelContent model = context.Convert<NodeContent, ModelContent>(turtle, "ModelProcessor");

which would have 4 bones at runtime arranged in the chain: turtle->body->head->eye.

To assist in debugging, I found it useful to set the Name property (from ContentItem) at build time, then check Model.Root.Index and Model.Root.Name against the Model.Bones list.

\$\endgroup\$
2
  • \$\begingroup\$ Great work. Please mark this answer as correct when you get the chance. \$\endgroup\$ Commented Apr 4, 2012 at 2:01
  • \$\begingroup\$ Will do. Stackexchange is enforcing the 24-hour delay on marking my own answer as correct. One hour to go. \$\endgroup\$
    – cod3monk3y
    Commented Apr 4, 2012 at 17:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .