Assimp Model Matrix's (The Inverse bind pose matrix and transforms ?)

Alright so i have assimp loading a fbx up out of blender but im having trouble in seeing how assimp expects this offset matrix to work in relation to the other matrices.

I was thinking that this should be a cummulative transform were the local matrix is multiplied to the parent to get a cummulative transform im reading that the offset matrix is supposed to be applied to the transform but im a little lost as to what the assimp offset matrix is really supposed to be doing here.
I mean if i didn’t care about animation data later on then no problem i would just do it my own way.
but i do sooo… im having trouble seeing how this inverse bind pose is supposed to work.

In fact im not even sure if im even remotely on track.

Is it supposed to just work or am i supposed to be recalculating it and if so how ?
What is the order of application for when it is to be applied ect?

A image of what im getting.
With the the initial code to calculate the initial node matrix chain data below.
(animation isn’t even added yet.)

        public static BoneNode Update(BoneNode node, ref Matrix[] globalBoneTransformMatrices, ref Matrix[] globalBoneOffsetMatrices)
        {
            if (node.isThisAhBoneInTheOffsetList)
            {
                if (node.parent != null)
                {
                    node.CummulativeTransform = node.parent.CummulativeTransform * node.LocalTransform * globalBoneOffsetMatrices[node.boneOffsetMatrixIndex]; // not to sure about how to apply the offset.
                }
                else
                {
                    node.CummulativeTransform = node.LocalTransform * globalBoneOffsetMatrices[node.boneOffsetMatrixIndex];
                }
                // set this to the global transform since it is a actuall bone named index.
                globalBoneTransformMatrices[node.boneOffsetMatrixIndex] = node.CummulativeTransform;
            }
            else
            {
                if (node.parent != null)
                {
                    node.CummulativeTransform = node.parent.CummulativeTransform * node.LocalTransform;
                }
                else
                {
                    node.CummulativeTransform = node.LocalTransform;
                }
            }


            Console.WriteLine("\n");
            Console.WriteLine("Name" + node.name);
            Console.WriteLine();
            PrintMatrix(node.LocalTransform, "node.LocalTransform");
            if(node.isThisAhBoneInTheOffsetList)
                PrintMatrix(globalBoneOffsetMatrices[node.boneOffsetMatrixIndex], "globalBoneOffsetMatrices["+node.boneOffsetMatrixIndex+"]");
            PrintMatrix(node.CummulativeTransform, "node.CummulativeTransform");

            // if its a requisite child transform in a bone chain be it a actual bone or not then iterate the child and calculate.
            for (int i =0; i < node.children.Count; i++)
            {
                if (node.children[i].isThisNodeTransformNecessary)
                    Update(node.children[i], ref globalBoneTransformMatrices, ref globalBoneOffsetMatrices);
            }
            return node;
        }

Output…

node.LocalTransform
 Right:          {X:1 Y:0 Z:0}
 Up:             {X:0 Y:1 Z:0}
 Forward:        {X:0 Y:0 Z:-1}
 Translation:    {X:0 Y:0 Z:0}
node.CummulativeTransform
 Right:          {X:1 Y:0 Z:0}
 Up:             {X:0 Y:1 Z:0}
 Forward:        {X:0 Y:0 Z:-1}
 Translation:    {X:0 Y:0 Z:0}


NameArmature

node.LocalTransform
 Right:          {X:600 Y:0 Z:0}
 Up:             {X:0 Y:-9.775241E-05 Z:600}
 Forward:        {X:0 Y:600 Z:9.775241E-05}
 Translation:    {X:0 Y:0 Z:0}
node.CummulativeTransform
 Right:          {X:600 Y:0 Z:0}
 Up:             {X:0 Y:-9.775241E-05 Z:600}
 Forward:        {X:0 Y:600 Z:9.775241E-05}
 Translation:    {X:0 Y:0 Z:0}


NamerootBone

node.LocalTransform
 Right:          {X:1 Y:0 Z:0}
 Up:             {X:0 Y:-1.629207E-07 Z:-1}
 Forward:        {X:0 Y:-1 Z:1.629207E-07}
 Translation:    {X:0 Y:0 Z:0}
globalBoneOffsetMatrices[0]
 Right:          {X:0.1666667 Y:0 Z:0}
 Up:             {X:0 Y:-2.715345E-08 Z:0.1666667}
 Forward:        {X:0 Y:0.1666667 Z:2.715345E-08}
 Translation:    {X:0 Y:0 Z:0}
node.CummulativeTransform
 Right:          {X:99.99999 Y:0 Z:0}
 Up:             {X:0 Y:-1.629207E-05 Z:99.99999}
 Forward:        {X:0 Y:99.99999 Z:1.629207E-05}
 Translation:    {X:0 Y:0 Z:0}


NametopBone

node.LocalTransform
 Right:          {X:0.005747838 Y:0.9999834 Z:1.343566E-07}
 Up:             {X:-0.9999834 Y:0.005747838 Z:-1.335865E-07}
 Forward:        {X:1.343565E-07 Y:1.335865E-07 Z:-1}
 Translation:    {X:0 Y:0 Z:0}
globalBoneOffsetMatrices[1]
 Right:          {X:0.1666667 Y:0 Z:0}
 Up:             {X:0 Y:-2.715345E-08 Z:0.1666667}
 Forward:        {X:0 Y:0.1666667 Z:2.715345E-08}
 Translation:    {X:0 Y:0 Z:0}
node.CummulativeTransform
 Right:          {X:0.09579729 Y:-4.954575E-06 Z:16.66639}
 Up:             {X:4.760242E-07 Y:-16.66666 Z:-4.957393E-06}
 Forward:        {X:-16.66639 Y:-5.045105E-07 Z:0.09579729}
 Translation:    {X:0 Y:0 Z:0}
Model Loaded

While this is what is in the actual scene nodes and mesh bones when i load the model.


Node.Name: RootNode
Children:  Armature Cube
node.Transform:
: 1 0 0 0
: 0 1 0 0
: 0 0 1 0
: 0 0 0 1
HasMeshes: No

        Node.Name: Armature
        Parent: RootNode
        Children:  rootBone
        node.Transform:
        : 600 0 0 0
        : 0 -9.775241E-05 600 -600
        : 0 -600 -9.775241E-05 0
        : 0 0 0 1
        HasMeshes: No

                Node.Name: rootBone
                Parent: Armature
                Children:  topBone
                node.Transform:
                : 1 0 0 0
                : 0 -1.629207E-07 -1 0
                : 0 1 -1.629207E-07 0
                : 0 0 0 1
                HasMeshes: No

                        Node.Name: topBone
                        Parent: rootBone
                        Children:  topBone_end
                        node.Transform:
                        : 0.005747838 0.9999834 1.343566E-07 0
                        : -0.9999834 0.005747838 -1.335865E-07 1
                        : -1.343565E-07 -1.335865E-07 1 0
                        : 0 0 0 1
                        HasMeshes: No

                                Node.Name: topBone_end
                                Parent: topBone
                                node.Transform:
                                : 1 0 0 0
                                : 0 1 0 1
                                : 0 0 1 0
                                : 0 0 0 1
                                HasMeshes: No

        Node.Name: Cube
        Parent: RootNode
        node.Transform:
        : 100 0 0 0
        : 0 -1.629207E-05 100 0
        : 0 -100 -1.629207E-05 0
        : 0 0 0 1
        HasMeshes: Yes   MeshIndexs  0


 ========= Mesh Bone Data (The offsets are this >>>> bone space to mesh space maybe ?) =========


Mesh #0 name: Cube.001
  bones: 2
   Name: rootBone
     OffsetMatrix:
     : 0.1666667 0 0 0
     : 0 -2.715345E-08 0.1666667 1
     : 0 -0.1666667 -2.715345E-08 0
     : 0 0 0 1
   Name: topBone
     OffsetMatrix:
     : 0.1666667 0 0 0
     : 0 -2.715345E-08 0.1666667 0
     : 0 -0.1666667 -2.715345E-08 0
     : 0 0 0 1

Whats really confusing me is that there is nothing in the translational component for any of these bones.
But then that just might be because of how i modeled this or how blender exported my model.

Well any hints even are appreciated.

You have to calculate it yourself. Yes, Assimp’s offset-matrix is supposed to be a model-space transform that transforms 0,0,0 to the bone, but it doesn’t work with FBX outside of very narrow circumstances as the FBX format doesn’t have trivial transforms.

Likely what you’re getting for the transform (thus lacking translation) is just 1 part of the transform chain for a node in FBX. FBX splits transforms into a pre, nat, and post chain of transforms. Assimp is clueless about the chain and you’re banking on whatever exporter you’re using writing the final result into the nat part.

You have to use the Autodesk FBX sdk or you will always have problems.

There is no getting around it, you accept terrible support or you use the proper SDK.

Well i keep coming back and taking pot shots at getting assimp to work.
Though its still messed up when i try to do anything using the matrices from assimp.
Ive made a little progress with it.

Needless to say i can load a model and i have a shader with the blend weights and all that in the tpose. (If i set all the bone matrices to identity fine). The problems are converting primarily the orientations from blender to assimp to monogame as well as using them properly as intended.

Between blenders weird orientations y and z are reversed or rotated if you like. So the corresponding quaternions (seem to need to also be changed pulling them into monogame not 100 on that) the complexity of assimps matrices intended usage and monogames coordinate system it is really hard to get this working.

However im starting to make some progress understanding the whole thing.

Though if anyone has got the proper procedure to converting over the matrices vertices and quaternions that would be great. Anyways… i have started to figure out a couple of things or at least got some clues even if i don’t fully know what they mean.

So it seems like when you pull in the matrices from assimp the first thing to do is convert them over to mg then do a transpose on them then swap either the rows or columns rotating x doesn’t seem to work right.
The root node however seems to already be in identity that corresponds to monogame without any adjustments im not to sure what is up with that or if im supposed to skip using it in the transform chain?
The quaternions seem to need to be passed into a monogame quaternion in a different order then normal. Currently im just changing them in a new assimp quaternion passing them as
// blender goes Z Up, Y Forward i think so.
//var q = new Assimp.Quaternion(aq.W, aq.X, aq.Y, aq.Z); // this is how they are setup in assimp with the w first.
// seems they need to be redone though before even being transposed.
var q = new Assimp.Quaternion(aq.X, aq.Y, aq.Z, aq.W); // almost seems to work dunno about this though.
ConvertMatrix(q.GetMatrix());
// convert transposes and then swaps the y z rows im not sure if that is correct for the quaternions after i swap them but for the offset matrices it appears to work at first glance.

The model i made in blender is a pipe in the up down direction.
Which in blender is actually the z axis and in monogame that would be the y axis.

This models vertices extents are about -6.0 Y to 6,0 Y in blender and in monogame.
if i swap the y z on the vertices otherwise it has the longest vertices extending in the z direction which wouldn’t match what i did in blender.
The first bone is at 0 -6 0 in blender the next bone is at 0 y height.

Which these bones offsets now finally matches the translation of the inverse of the offset matrices with no scaling after all the stuff i did to them.

The root node and armature bone however as shown in the output below is still confusing.
So these conversions at least seem partially right but the rotations are all hosed.
The quaternion rotations still need to be worked out to as well as how to combine everything properly.

Here is the output i currently get

___________Loaded __________
PipeFromCube13VGrpsLCRetexAnimKeyframes.fbx

 ========= DumpAssimpData =========


 ========= PrintAsWeWalkNodes =========



(0) Node.Name: RootNode
Children:  Cube RootNode
node.Transform:
 Right     { x: 1  y: 0  z: 0  w: 0 }
 Up        { x: 0  y: 0  z: 1  w: 0 }
 Forward   { x: 0  y: 1  z: 0  w: 0 }
 Position  { x: 0  y: 0  z: 0  w: 1 }
     // Because this is the RootNode i dont think we use it as it is.
     // However since im already implicitly fliping it maybe it should be left untouched rather.
     Transform As is from assimp:
      : 1 0 0 0
      : 0 1 0 0
      : 0 0 1 0
      : 0 0 0 1

HasMeshes: No

        (1) Node.Name: Cube
        Parent: RootNode
        node.Transform:
        Right     { x: 100  y: 0  z: 0  w: 0 }
        Up        { x: 0  y: 100  z: 0  w: 0 }
        Forward   { x: 0  y: 0  z: -100  w: 0 }
        Position  { x: 0  y: 0  z: 0  w: 1 }
        HasMeshes: Yes   MeshIndexs  0

        (1) Node.Name: RootNode
        Parent: RootNode
        Children:  Armature
        node.Transform:
        Right     { x: 1  y: 0  z: 0  w: 0 }
        Up        { x: 0  y: 0  z: 1  w: 0 }
        Forward   { x: 0  y: 1  z: 0  w: 0 }
        Position  { x: 0  y: 0  z: 0  w: 1 }
     // Because this is the RootNode i dont think we use it as it is.
     // However since im already implicitly fliping it maybe it should be left untouched rather.
     Transform As is from assimp:
      : 1 0 0 0
      : 0 1 0 0
      : 0 0 1 0
      : 0 0 0 1

        HasMeshes: No

                (2) Node.Name: Armature
                Parent: RootNode
                Children:  rootBone
                node.Transform:
                Right     { x: 600  y: 0  z: 0  w: 0 }
                Up        { x: 0  y: 600  z: 0  w: 0 }
                Forward   { x: 0  y: 0  z: -600  w: 0 }
                Position  { x: 0  y: -600  z: 0  w: 1 }
                HasMeshes: No

                        (3) Node.Name: rootBone
                        Parent: Armature
                        Children:  topBone
                        node.Transform:
                        Right     { x: 1  y: 0  z: 0  w: 0 }
                        Up        { x: 0  y: -1  z: 0  w: 0 }
                        Forward   { x: 0  y: 0  z: 1  w: 0 }
                        Position  { x: 0  y: 0  z: 0  w: 1 }
                        HasMeshes: No

                                (4) Node.Name: topBone
                                Parent: rootBone
                                node.Transform:
                                Right     { x: 0.006  y: -1  z: 0  w: 0 }
                                Up        { x: 0  y: 0  z: 1  w: 0 }
                                Forward   { x: 1  y: 0.006  z: 0  w: 0 }
                                Position  { x: 0  y: 1  z: 0  w: 1 }
                                HasMeshes: No


 ========= Mesh Bone Data (The offsets are this >>>> bone space to mesh space) =========


Mesh #0 name: Cube.001
  bones: 2
   Name: rootBone
     OffsetMatrix:
      Right     { x: 0.167  y: 0  z: 0  w: 0 }
      Up        { x: 0  y: 0.167  z: 0  w: 0 }
      Forward   { x: 0  y: 0  z: -0.167  w: 0 }
      Position  { x: 0  y: 1  z: 0  w: 1 }
     inverseOffsetMatrix:
      Right     { x: 6  y: 0  z: 0  w: 0 }
      Up        { x: 0  y: 6  z: 0  w: 0 }
      Forward   { x: 0  y: 0  z: -6  w: 0 }
      Position  { x: 0  y: -6  z: 0  w: 1 }
   Name: topBone
     OffsetMatrix:
      Right     { x: 0.167  y: 0  z: 0  w: 0 }
      Up        { x: 0  y: 0.167  z: 0  w: 0 }
      Forward   { x: 0  y: 0  z: -0.167  w: 0 }
      Position  { x: 0  y: 0  z: 0  w: 1 }
     inverseOffsetMatrix:
      Right     { x: 6  y: 0  z: 0  w: 0 }
      Up        { x: 0  y: 6  z: 0  w: 0 }
      Forward   { x: 0  y: 0  z: -6  w: 0 }
      Position  { x: 0  y: 0  z: 0  w: 1 }

Well if anyone can see or has experience with what needs to be done to get this in line.
especially with the quaternions changes to monogame.

Or even has any helpful observations feel free to chime in.
Id love to get this working.

Hi @willmotil,

I’ve been lately thinking of publishing my code for assimp loading/exporting.

The code is far from pretty and most comments are in my maiden language (although most variable names are english) but works quite well with most animations I’ve tried (although not all, because the FBX is quite tricky depending on the export parameters)

Would you be interested? You could at least do a side by side comparison of my results with yours, or reuse pieces of code if you dare :slight_smile:

Sure absolutely.
The progress im making at the moment is painfully slow.
Mostly just trial and error guesswork.

ok, give me some days to decouple the code from some internal big libraries and some extra time to remove all the swearing of the code comments :joy:

Lol alright im pretty sure i wont have this solved by then.
I been going at it for like over a week off and on for some full days out of those.
With plenty of swearing myself.

spegetti but i do have the bones placed at least for the t pose but how to account for all the transforms from blender to assimp to mg is still eluding me.

There was way less swearing than I thought :slight_smile:

I’ve uploaded two projects, the exporter ( https://github.com/KakCAT/3dexp3 ) and a sample of exporting a file and displaying the result. This sample is linked at the bottom in the exporter readme.

Also, please consider that exporting FBX is usually a dirty job (but somebody’s gottta do it). Specially with the blender exporter. Sometimes I’ve had to export several times a file util I found the correct parameters.

I hope it helps!

1 Like

Thanks kat kat wow yikes lots of information there hard to see were the transforms are reinterpreted which i think is my main problem atm.

Im going to have to take a look at it when i get more time.

Hey kakcat i tried to build that app but the batch file just kept closing out i added a pause command it just keeps saying cannot load model is there a step missing ?

I have been trying to keep going with own project so i took a look at the open mod 3d viewer and copied the values that it shows for my model. I then managed to reproduce the numbers it gives for the entire heirarchy however when i convert the matrices over at the final step and draw the model it is still distorted.

Here is my current function to update the shader transforms it’s pretty straight forward at least it should be.

        public void ConsoleOutRealTimeUpdateOperation()
        {
            Helpers.ExtraBasicMatrixInformation = true;
            Helpers.ExtraAlteredMatrixInfo = false;
            Helpers.ExtraTranspose = false;
            Helpers.ExtraInvert = false;
            Console.WriteLine("");
            IterateUpdateSimulation(rootNodeOfTree);
        }
        public void IterateUpdateSimulation(OnlyAssimpNode node)
        {
            var finalassimp = Matrix4x4.Identity;
            var roottrans = rootNodeOfTree.LocalTransform;
            var mesh = GetBoneMeshTransform(node);
            var local = node.LocalTransform;
            var offset = Matrix4x4.Identity;
            var pac = Matrix4x4.Identity;

            if (node.parent != null)
                pac = node.parent.cummulativeTransform;
            else
                node.cummulativeTransform = local;
            if (node.isThisARealBone)
                offset = node.offsetMatrix;

            // Calculate
            finalassimp = local  * pac;
            node.cummulativeTransform = finalassimp;

            var finalmg = Matrix.Transpose(Helpers.DirectlySwapMatrixAssimpToMg(finalassimp)); //* Matrix.CreateScale(.01f);

            Console.WriteLine("_______________________________________________________________________");
            Console.WriteLine(node.name);
            Console.WriteLine("The final proper Assimp Matrix ");
            Console.WriteLine(Helpers.MatrixToString(finalassimp, "  "));
            Console.WriteLine("Mg Matrix remember it was transposed so signs flip on the diagnal it is correct.");
            Console.WriteLine(Helpers.MatrixToString(finalmg, " "));

            // set to the final shader matrix.
            globalShaderMatrixs[node.boneMeshIndex] = finalmg;

            // call children
            foreach (OnlyAssimpNode n in node.children)
                IterateUpdateSimulation(n);
        }

here is the data for a model in the viewer.

_______________________________
*** the top bone          my 2nd bone defines 

    local transform

translation  x     y 1.000    z
scaling      1.000
rotation     x     y -76.540    z

    global

translation  x     y 618.652    z
scaling      600.000
rotation     x     y -76.540    z

Here is my output…
Only the global is shown and the mg counterpart.
The local is static and i already got that to match.

_______________________________________________________________________
topBone
The final proper Assimp Matrix

      Assimp Not a normalize Matrix *possibly inverted or needs transposed*
      :
      :    139.66    583.52      0.00      0.00
      :   -583.52    139.66      0.00    618.65
      :      0.00      0.00    600.00      0.00
      :      0.00      0.00      0.00      1.00
      :
      As Quaternion: x: 0.0  y: 0.0  z: -0.619  w: 0.785
      :
      Translation:         0.00, 618.65, 0.00
      Scale:               600.00
      Rotation degrees:    0.00, -76.54, 0.00

Mg Matrix remember it was transposed so signs flip on the diagnal it is correct.

     MG not a normalize Matrix
     :
     :    139.66   -583.52      0.00      0.00
     :    583.52    139.66      0.00      0.00
     :      0.00      0.00    600.00      0.00
     :      0.00    618.65      0.00      1.00
     :

It’s baffling because this really should be working im getting really close but im missing something critical.

could you paste the exact error?

my guess is that either the .bat file hasn’t got the right .exe file, or (more probable if the error talks of models) that the first path in the .kcp file is invalid Check the readme.md in https://github.com/KakCAT/3dexp3_monogame_player , at “building models”. It’s better if you put an absolute path (I don’t know if it works with relaitve paths)

I usually run the bat from cmd, it won’t close from there.

Ah i had a space before the path.

You can add the pause command in the batch to stop it from closing it will tell the user to press a key.

@echo off
cls
echo ------------
echo 1 - fix the first path in tester.kcp
echo 2 - fix the path to kakExport3.exe in the following line
echo ------------

...\3dexp3-master\kakExport\bin\Debug\kakExport3.exe project.kcp /all

pause

I actually almost have my own project working ive got the model in the bind pose with all the transforms.
But the translation if off center with either way ive done it.

Im not sure how the mesh transform is supposed to plug into the transformation chain or were.
finalassimp = pretransform * (localTransform * parentCombinedTransform) * offset;

        /// <summary>
        /// Here comes the tricky part if you look at the output its obvious the locals here are basically rotation translations.
        /// But to what ? well to each other it should be, but to what else translation ? as this is really a weird way they have it set up.
        /// ok in one big long chain its not .
        /// </summary>
        /// <param name="node"></param>
        private void IterateUpdateBoneTree(OnlyAssimpNode node)
        {
            var combinedAssimp = Matrix4x4.Identity;
            var finalassimp = Matrix4x4.Identity;
            var roottrans = rootNodeOfTree.LocalTransform;
            var mesh = GetBoneMeshTransform(node); // 
            var invmesh = GetBoneMeshTransform(node); invmesh.Inverse(); // this should put things in the right scale but it seems to eh mess up the rotation i dunno about the scale either.
            var localTransform = node.LocalTransform;
            var offset = Matrix4x4.Identity;
            var parentCombinedTransform = Matrix4x4.Identity;
            var pretransform = GetGlobalPreTransform();
            var scale = 01f;
            //var scale = 0.01f;
            var scaleMat = Matrix.CreateScale(scale);
            var theoldx90 = Matrix4x4.FromRotationX(MathHelper.ToRadians(-90));

            if (node.parent != null)
            {
                if (node.parent.isTheGlobalPreTransformNode == false)
                    parentCombinedTransform = node.parent.combinedTransform;
            }
            if (node.isThisARealBone)
                offset = node.offsetMatrix;

            // Calculate
            combinedAssimp = localTransform * parentCombinedTransform;
            node.combinedTransform = combinedAssimp;

            finalassimp = pretransform * (localTransform * parentCombinedTransform) * offset; // THIS WORKED its 100x scale though translation its off centered.
            //finalassimp = pretransform * localTransform * invmesh * parentCombinedTransform * offset * theoldx90; // THIS WORKED its 1x scale though translation its off centered i had to -90 deg rotation to put it right.

            // hummm

            Console.WriteLine("_______________________________________________________________________");
            Console.WriteLine(Helpers.InfoAboutNode(node, ""));
            Console.WriteLine("");
            Console.WriteLine("The local Assimp Matrix ");
            Console.WriteLine(Helpers.MatrixToString(localTransform, "  "));

            var finalmg = Matrix.Transpose(Helpers.DirectlySwapMatrixAssimpToMg(finalassimp));

            Console.WriteLine("The final proper Assimp Matrix ");
            Console.WriteLine(Helpers.MatrixToString(finalassimp, "  "));
            Console.WriteLine("Mg Matrix remember it was transposed so signs flip on the diagnal it is correct.");
            Console.WriteLine(Helpers.MatrixToString(finalmg, " "));
            Console.WriteLine("Mg Matrix scaled by " + scale);
            Console.WriteLine(Helpers.MatrixToString(finalmg *scaleMat, " "));

            // set to the final shader matrix.
            if (node.isThisARealBone)
                globalShaderMatrixs[node.boneShaderFinalTransformIndex] = finalmg * scaleMat;

            // call children
            foreach (OnlyAssimpNode n in node.children)
                IterateUpdateBoneTree(n);
        }

This is about as close as i am now which is starting to feel pretty close.
The pretransform here is everything before the first bone as its own transform.
the parentCombined is basically the local matrix chain multiplied down the tree and stored in a combined matrix.
However i separated everything before the first user bone which includes the armature to the pretransform because it seemed the like the scale was snowballing by just applying it down the line.

Ill try to take a look at yours soon as i run out of steam my whole class is really small in comparison,.

So i put this all down since the last time i posted and picked it back up a couple days ago,

Turns out i had my shader bone matrices mixed up due to not handling the mesh indexs for multiple mesh bone offset lists properly.

Basically i haven’t changed that since i started this project so the problem had nothing to do with the math at all the entire time :frowning:

Once i fixed that things pretty much just started to fall together. So basically i was racking my brains over the matrices when the problem was covered up else were. It took me adding a bunch of debug stuff to see it,

So here is the dude.fbx the beige lines are the T pose bones Blue are the local pose or animation pose,

I still have to figure out how to generate the inverse bind pose matrices manually if i want to make my own animations on the fly in app but that isn’t really a priority.

The code is actually pretty simple or will be once i clean it all up.
This is the primary order of multiplications.

        // not shown below skeletalNode.LocalTransformMg = animNodeframe.Orientation; 
        // is done prior to this call for all the animation current frames nodes
        // don't mind the naming its hardly only assimp nodes i just was tossing in names for multiple sets of test classes
        // basically the scenes first node is passed in.
        private void IterateAnimatedUpdate(OnlyAssimpNode node)
        {
            if (node.parent != null)
                node.CombinedLocalTransform = node.LocalTransform * node.parent.CombinedLocalTransform;
            else
                node.CombinedLocalTransform = node.LocalTransform;

            if (node.isThisARealBone)
                globalShaderMatrixs[node.boneShaderFinalTransformIndex] = node.OffsetMatrix.ToMgTransposed() * node.CombinedLocalTransform.ToMgTransposed();

            // call children
            foreach (OnlyAssimpNode n in node.children)
                IterateAnimatedUpdate(n);
        }

Eventually here ill get this smoothed out and straightened up and post a link for a how too load a rigged animated fbx model from blender thru the nuget assimp right into monogame but the above is basically the crucial math details.

This works for the dude fbx.

This works so far for the rigged animated pipe bone model i made myself in blender.
That one i made in blender 2.79b and is imported straight into monogame.
Though the scale is pretty small pretty sure the mesh transform has to be applied for that as its a scaling matrix if i remember right which is a trivial addition.

Edit you can find this continued here…

I put up a project on github that demos the work done so far.