KhronosGroup / glTF-Blender-IO

Blender glTF 2.0 importer and exporter
https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html
Apache License 2.0
1.5k stars 320 forks source link

Armature Transform being Applied to Root bone on Export, changing Bindpose #994

Open sketchpunk opened 4 years ago

sketchpunk commented 4 years ago

Describe the bug When I import fbx from Mixamo into blender, it adds a 90 degree rotation on the x axis on the armature plus it scales it down by 0.009ish. Back in 2.8, the parent node would get the transform applied in the parent node but now in 2.82, that transform is being merged with the root bone's (hip) transform. This causes the Bind Pose for the skeleton to change on export. On export the hip bone now has the scale and rotation that the armature object had.

To Reproduce Steps to reproduce the behavior:

  1. Export Blend file to GLF-BIN file

Expected behavior I expect the transform of the armature object to not be applied to the root bone's transform on export. One way to tell is to check if scale on the hip bone isn't all 1s, then its wrong.

.blend file/ .gltf http://www.sketchpunk.com/tmp/walking_test.zip

Version

julienduroure commented 4 years ago

Hello,

I am not sure to understand. Hips node don't have any scale on glb exported file

image

image

And seems to be OK on external viewer :

image

sketchpunk commented 4 years ago

I'm getting the values out of the bin file, skin.inverseBindMatrices is where I'm getting the bind pose.

sketchpunk commented 4 years ago

I can confirm that the values in the Hip node has the correct values, but thats not the values in InverseBindMatrices. Since hips is the root bone, you can just do an inverse of the matrix to get its localspace bindpose transform.

julienduroure commented 4 years ago

What viewer are you using to get a wrong result?

sketchpunk commented 4 years ago

I'm using my own importer

sketchpunk commented 4 years ago

Capture

sketchpunk commented 4 years ago

I tried with 2.81, scale for hip still returns 0.009. Then in 2.80 it works as intended, scale is 1.0 inside the InverseBindMatrices.

julienduroure commented 4 years ago

2.8 export was totally wrong (regarding skinned mesh with scaled armature). This seems now correct in 2.82 Export seems to be correctly displayed in three.js, babylone.js and Godot. So ... are you sure the problem does not come from your importer?

sketchpunk commented 4 years ago

Well, i'm reading the data right out of the bin file in relation the the armature's InverseBindMatrices. If a scale in blender is set on an Armature, does that mean that when you generate the Inverse Bind Matrix for the first bone, you apply that transform to it but in the node text data you do not apply it?

Have you tried to view the Inverse Bind matrices for the skeleton thats exported? I'm sure you'll see that it doesn't match the hip text node when you compare the transforms.

sketchpunk commented 4 years ago

I've tried the files from 2.82 with online GLTF viewers, like https://gltf-viewer.donmccurdy.com/. The character is there and animates, but its completely not scaled at 0.009, its at scale 1.0. I wouldn't be surprised that those apps are using the text nodes to compute the bindpose and not using InverseBindMatrices to do that. If it was using those matrices, then the character would be around 2 meters tall, not a giant how its displayed.

scurest commented 4 years ago

Since hips is the root bone, you can just do an inverse of the matrix to get its localspace bindpose transform.

You can't. The exporter writes inverseBindMatrices relative to the world root, not to the armature (also note that skin.skeleton is not set). glTF doesn't really care where you calculate the inverseBindMatrices relative to. There's not really a real way to get the localspace bind transform in glTF.

sketchpunk commented 4 years ago

Shouldn't the inverse Bind matrices be based off the bone's own transforms and not any object they are parented too when defining a bind pose. Animations are executed based on the bind pose of the skeleton, any changes to the original bind pose can mess up an animation, like what i'm noticing now with scale be applied to a bone on export instead of its parent object. In the nodes in GLTF, thats all true but when it comes to whats in the binary file, its wrong.

Its easy to compute the local space of a skeleton, or at least it was until 2.81. By changing the values on export, everything is a lil out of wack when I try to use the animations. If a bone has a set position , rotation and scale, then thats what I expect to be exported.

The Spine bone is suppose to have a y position of 124, exported its now at 1.14. The transform on the armature should not have been applied to the rest of the skeleton on export.

Worst case senario I can go back to using the nodes to get the correct transform of the bones since now the export does a better job of that, switched to using the inverse matrices to compute local space of each bones because the node data was not reliable in 2.80. But honestly, I think the way the inverse matrices are being computed are wrong.

julienduroure commented 4 years ago

Hello,

Reading from here: https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_020_Skins.md#the-skin

This means that each matrix is the inverse of the global transform of the respective joint, in its initial configuration

@donmccurdy ?

fire commented 4 years ago

Skinning is complicated.

Gltf does not use a single skeleton system.

https://github.com/KhronosGroup/glTF/issues/1665#issuecomment-529529924

Edited:

Godot Engine developers complained, but was forced to also implement a skeleton skin system (definition of skin as defined in this post, not the traditional meaning).

scurest commented 4 years ago

I do think this would be a good idea, even if it's not necessary. I mean giving inverseBindMatrices relative to the armature and, consequently, giving verts in the armature space instead of giving stuff in world space.

@julienduroure Is there a reason for doing it in world space?


I looked at the functions for changing the space verts are in...

https://github.com/KhronosGroup/glTF-Blender-IO/blob/9dde650dc77fd1a1c9552a2773f0c3533f70683c/addons/io_scene_gltf2/blender/exp/gltf2_blender_extract.py#L96-L97

The inverse cancels so this should be equivalent to the simpler

new_loc = blender_object.matrix_world @ Vector(loc)

I also looked at the function for changing normals...

https://github.com/KhronosGroup/glTF-Blender-IO/blob/9dde650dc77fd1a1c9552a2773f0c3533f70683c/addons/io_scene_gltf2/blender/exp/gltf2_blender_extract.py#L77-L79

But I couldn't figure out the logic here or why its different than tangents (normals do transform different that tangents because of scalings).