Bind Poses, Joint Transforms in Collada
Asked Answered
J

1

47

I'm trying to export a custom 3D model format to Collada. I've built the Collada data classes through XSD and now problems come when I try to fill them with data, especially for what concerns matrices.

My Skeleton class is basically an array of Joint classes that I read from the binary file and every Joint looks like this (values of traslation and rotation are relative to the parent Joint, or Root if there is no parent, always):

class Joint
{
    List<Joint> Children;
    Quaternion Rotation;
    Joint Parent;
    String Name;
    UInt32 Id;
    Vector3 Traslation;
}

The first thing I do is to build the JOINT nodes in the "library_visual_scenes" section of the file. Which is quite simple and I get correct results:

foreach (Joint joint in hierarchicalJoints)
    WriteJointNodes(joint);

private void WriteJointNodes(Joint joint)
{
    Vector3 rotationEulers = Quaternion.ToEulers(joint.Rotation, EulersOrder.ZYX);

    WriteStartElement("node");
    WriteAttributeString("id", String.Concat(joint.Name, "_id"));
    WriteAttributeString("type", "JOINT");
    WriteAttributeString("name", joint.Name);
    {
        WriteElementString("translate", bone.Traslation);
        WriteElementStringAttributes("rotate", String.Concat("0.0 0.0 1.0 ", rotation.Z.ToDegrees()), { "sid", "rotateZ" });
        WriteElementStringAttributes("rotate", String.Concat("0.0 1.0 0.0 ", rotation.Y.ToDegrees()), { "sid", "rotateY" });
        WriteElementStringAttributes("rotate", String.Concat("1.0 0.0 0.0 ", rotation.X.ToDegrees()), { "sid", "rotateX" });
        WriteElementString("scale", "1.0 1.0 1.0");

        Joint[] children = joint.GetChildren();

        for (Int32 i = 0; i < children.Length; ++i)
            WriteJointNodes(children[i]);
    }
    WriteEndElement();
}

Here is an example of the output:

<node id="bn_head_id" type="JOINT" name="bn_head">
    <translate>0.0732510 0.0000000 0.0000000</translate>
    <rotate sid="rotateZ">1.0 0.0 1.0 0.0</rotate>
    <rotate sid="rotateY">0.0 1.0 0.0 9.0</rotate>
    <rotate sid="rotateX">1.0 0.0 0.0 0.0</rotate>
    <scale>1.0 1.0 1.0</scale>

Now comes the tricky part, since I also have to export weights (skinning data) into the "library_controllers" section which looks like this:

<skin source="#geometry1_id">
    <bind_shape_matrix>1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</bind_shape_matrix>
    <source id="skinU3D1_id-joints">
        <Name_array id="skinU3D1_id-joints-array" count="4">bn_head_id bn_jaw_id bn_lefteye_id bn_righteye_id</Name_array>
        <technique_common>
            <accessor source="#skinU3D1_id-joints-array" count="4" stride="1">
                <param name="JOINT" type="Name"/>
            </accessor>
        </technique_common>
    </source>
    <source id="skinU3D1_id-bind_poses">
        <float_array id="skinU3D1_id-bind_poses-array" count="64">0 0.999831 0.018391 -1.58086 -1 0 0 -0.000000 0 -0.018391 0.999831 0.041763 0 0 0 1 -0.00011 -0.374834 0.927092 0.564468 -1 -0.000506 -0.000323 0.000808 0.00059 -0.927092 -0.374834 1.45633 0 0 0 1 0 0.000036 1 -0.074606 1 0 0 0.032523 0 1 -0.000036 -1.638 0 0 0 1 -0.00004 0.000036 1 -0.074607 1 -0.000302 0.00004 -0.032021 0.000302 1 -0.000036 -1.63774 0 0 0 1</float_array>
        <technique_common>
            <accessor source="#skinU3D1_id-bind_poses-array" count="4" stride="16">
                <param name="TRANSFORM" type="float4x4"/>
            </accessor>
        </technique_common>
        </source>
        // <<Weights Source Here>>
        <joints>
            <input semantic="JOINT" source="#skinU3D1_id-joints"/>
            <input semantic="INV_BIND_MATRIX" source="#skinU3D1_id-bind_poses"/>
        </joints>
        // <<Vertex Weights Here>>
</skin>

Here, the first 16 values of skinU3D1_id-bind_poses-array should represent the inverse bind pose of bn_head of my example.

I can build the joints array properly and I can handle vertices weights without problems but I really don't understand how to obtain the matrices being used inside the skin controller. I need to output a Collada model with Y UP orientation and all I know is that Collada matrices are column-major.

Starting from the data I have, my questions basically are:

  1. How do I calculate the bind_shape_matrix (in this example it's an identity matrix but I've also seen other Collada files in which it's different)?
  2. How do I calculate inverse bind matrices for every joint?
Johathan answered 1/7, 2015 at 19:47 Comment(6)
You shouldn't need to calculate your bind-shape matrix, you either have one or you don't. This value is basically an offset to the bound mesh. If you don't have one already (your source file format doesn't specify it) then just leave it as identity.Xylina
And your inverse bind matrices are specified in the float_array id="skinU3D1_id-bind_poses-array? You seem to have a valid export already - I'm not sure if I'm understanding your question?Xylina
In fact I need to understand how to calculate inverse bind poses and local transform matrices. As far as I know a few converters use 4x3 matrices to calculate those.Johathan
You cannot 'calculate' these transforms. The way skinning works is to get the current xform of the bone, multiply by the inverse of the xform when the skin was applied, and that offset is then applied to the vertices.Xylina
If your JOINT structure defines the data from when the skin was bound (ie - when it was applied), then that information can be used to generate your inv bind matrices. However, it looks like you are setting that info on the transform nodes - your example output.Xylina
My guess would be that the JOINT structure in your file format defines bind-state information, and that your animation data for the joints should be coming from somewhere else. If thats the case, then convert your Quat to a rotation part of a matrix (the 3x3 part), add the translation as either the 4th row or column, and you should have your joints initial transform. Depending on your file format, you may or may not need to invert that transform to get the inverse bind shape transform, but most file formats store inverse transforms as it's faster and the invert is always applied.Xylina
B
1

I know what file format you are referring to. The file format you use (.anim) has one more skeletal file with (.inv_bind_mats) extension and same name, you don't need to calculate anything but just read .inv_bind_mats file.

Batey answered 6/11, 2016 at 7:46 Comment(1)
@zarathos also could you answer a little question, how do you read rotation quaternion? 2 bytes as half precision and then to float or what?Batey

© 2022 - 2024 — McMap. All rights reserved.