guillaumeblanc / ozz-animation

Open source c++ skeletal animation library and toolset
http://guillaumeblanc.github.io/ozz-animation/
Other
2.34k stars 294 forks source link

3 bones with first 2 bones not animated producing discrepancy between playback sample and Khronos GLTF reference #178

Open tsmckelvey opened 1 month ago

tsmckelvey commented 1 month ago

Hello, I am encountering a small but curious issue while implementing hardware skinning with GLTF.

I am using 0.15.0.

Screenshot from within animation software: in-app

Using gltf2ozz.exe, I have generated an animation and a skeleton with this JSON configuration (in attached ZIP):

{
    "skeleton": {
        "filename": "limited_skeleton.ozz"
    },
    "animations": [
        {
            "clip": "*",
            "filename": "limited_animation.ozz"
        }
    ]
}

As you can see, nothing too weird.

A GLTF+bin is attached, here is a screenshot of the animated mesh from Khronos GLTF Sample Viewer: gltf sample viewer This is correct.

Here is a screenshot using the generated skeleton and animation in the playback sample, this is the reason I have opened an issue: playback sample I believe this is incorrect.

By adding a very small translation to the root joint, I can visualize the expected output (I do not believe adding a translation is a valid solution): playback good

The skeleton has 3 bones, and the first 2 joints have no animation data. In the sample playback tool I expect to see all 3 bones offset as in the reference screenshots.

I am not very experienced in this area, apologies if my terminology is inaccurate.

Thanks for your attention.

Relevant files attached here: limited.zip

matt328 commented 1 month ago

I think I have a similar issue, but my example only has 2 bones. When I use gltf2ozz to extract the skeleton, it's treating all 4 nodes in the scene as joints, but the animation only generates matrices for 2 bones at positions 2 and 3, and since the 'real' bones are at positions 0 and 1, nothing gets animated.

Right after I load the skeleton, I log all the joint names:

auto skeleton = ozz::animation::Skeleton{};
archive >> skeleton;

for (const auto& name : skeleton.joint_names()) {
   Log::debug << "skeleton joint name: " << name << std::endl;
}

And like I said this logs the names of every node in the file, whether they're referenced by the skin or not. I haven't had time to trace through ozz' code to see what sends it down this path, but like you said, I also suspect it has to do with when a transform is not applied to the root node. Technically, this might be an invalid gltf file since validation does produce NODE_SKINNED_MESH_NON_ROOT, but plenty of test files say that and work fine.

matt328 commented 1 month ago

I think I've narrowed this down. Working gltf files all call out a skeleton node in the skins section, but when exported from blender, this skeleton key is missing, and that seems to be the differentiator for gltf2ozz extracting skeletons properly.

From the log of a file that works correctly, and has a skeleton key under skins:

Importing from default scene #0 with name "scene_0".
Bone t: 0, -1.36e-07, -4.18 r: 0, 0, 0, 1 s: 1, 1, 1
.Bone.001 t: 1.215e-11, 0.02798, 4.187 r: -0, 0.0002899, -0, -1 s: 1, 1, 1

Imported this gltf into blender, then exported it again, and it no longer contains the skeleton key under skins, and its gltf2ozz log contains:

Importing from default scene #0 with name "Scene".
Z_UP t: 0, 0, 0 r: -0.7071, 0, 0, 0.7071 s: 1, 1, 1
.Armature t: 0, 0, 0 r: 0, 0, -0.7071, 0.7071 s: 1, 1, 1
..Cylinder t: 0, 0, 0 r: 0, 0, 0, 1 s: 1, 1, 1
..Bone t: -1.36e-07, -5.082e-21, -4.18 r: -6.884e-08, -6.884e-08, -0.7071, 0.7071 s: 1, 1, 1
...Bone.001 t: 3.162e-07, 0.02798, 4.187 r: -5.694e-08, -0.0002899, 1.65e-11, 1 s: 1, 1, 1
matt328 commented 1 month ago

So adding the skeleton key to the skins section in the gltf file fixed this issue for me. It seems like it's sort of optional for the gltf format, and most other validators and renderers will determine the structure of the skeleton by looking at the joints array.