Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve bind poses from FBX clusters instead of FBX poses. #91036

Merged
merged 1 commit into from
Apr 26, 2024

Conversation

bqqbarbhg
Copy link
Contributor

@bqqbarbhg bqqbarbhg commented Apr 23, 2024

See #90986 for more context.

Skeletons imported with ufbx sometimes have broken bind poses:

ufbx previous
image

ufbx previous bind pose
image

this PR (both bind pose and initial pose are the same)
image

Turns out this was not a bug, either in ufbx or in the Godot ufbx importer. The issue is broken data in files, the Godot ufbx importer read the bind poses from FBX Pose objects. The rest pose matrices in these were correct for most files, but they seem to be incorrect in a significant minority of files.

FBX2glTF does not attempt to import bind poses, so it avoids this problem. This PR introduces an alternative method of reading the bind pose data, and initial testing shows improved skeletons in all tested files. I'll do more rigorous testing in the background using the same process as in #90986.

One thing to consider is whether we should have an option whether to import the bind pose data or not. This would allow compatibility with FBX2glTF and allow users to hotfix potentially broken bind poses in files, though if testing indicates that the new method is fully robust, it may not be as necessary.

@bqqbarbhg bqqbarbhg requested a review from a team as a code owner April 23, 2024 00:10
modules/fbx/fbx_document.cpp Outdated Show resolved Hide resolved
@fire fire requested review from fire, lyuma and a team April 23, 2024 14:59
Copy link
Member

@fire fire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FBX2GLTF doesn't use bind poses because of invalid data, this alternative approach is cool, and I like how it fetches the bind pose from either two successive skin clusters or a top-level bone.

@lyuma mentioned wanted to review it

@bqqbarbhg
Copy link
Contributor Author

Ran through all the dataset files containing skeletons:

Loaded 2264 files in 598min 50s.
Processed 5.41GB at 0.15MB/s.
Ignored 0 invalid files.

After this fix there were no more obviously broken skeletons, and most seem to match FBX2glTF 100% accurately. Result categories out of 2264 files:

  • 2135 equal: Again some with non-important material or light differences
  • 80 with broken skinned meshes in FBX2glTF, these seem to be caused by blend shapes
  • 25 meshes with relatively significant differences in skeletons, but this time no obvious issues. Mostly the differences are in the roll of the bones, probably caused by the bind pose data in the file.
  • 6 files with various ufbx issues for me to look at later:

Example of the extent of skeleton difference caused by loading the bind pose:

FBX2glTF
W_ufbx_dataset_skeletons_unity_Skeleton-Low_Poly_skeleton-low_poly_gltf

ufbx
W_ufbx_dataset_skeletons_unity_Skeleton-Low_Poly_skeleton-low_poly_ufbx

All in all, the testing indicates that this fix seems to be robust and fixes all the significant issues encountered before, without breaking any of the previously working cases.

The roll issue and the occasional bone distortion is still significant enough, that I'd propose adding an importer option to disable attempting to import the bind pose, treating the initial pose as rest pose, like FBX2glTF does. Any thoughts?

@fire
Copy link
Member

fire commented Apr 23, 2024

We need to debate adding a new option, but that can review that in a separate pr. That means you can stack the two prs together into that pr.

@bqqbarbhg
Copy link
Contributor Author

We need to debate adding a new option, but that can review that in a separate pr. That means you can stack the two prs together into that pr.

Makes sense, in that case this PR is fine to merge by me!

@bqqbarbhg
Copy link
Contributor Author

@lyuma provided some test cases with multiple bind poses per bone.

Added explicit support for this in the form of warning the user if a bone has multiple conflicting bind pose definitions. Note that this may still mix up different bind poses for different parts of the mesh, but at least now bones do not get completely messed up and users should see a warning.

I'd still highly recommend adding a setting for disabling importing bind poses (maybe even not importing them by default), as they seem to have quite a few problems in the wild, and even this solution is a bit hacky.

@fire
Copy link
Member

fire commented Apr 25, 2024

I think this needs a rebase on master.

Copy link
Contributor

@lyuma lyuma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! The added check gives me more confidence it will not tear my armature apart in the rest.
We still need to discuss making an option for this behavior and what the default should be but again we should do this in a separate change.

please rebase as ifire said and squash your commits into one. I kept a copy of the history in my own fork so don't worry about losing history.

}
const real_t max_error = 0.01f;
if (error >= max_error) {
WARN_PRINT(vformat("FBX: Node '%s' has multiple bind poses, using initial pose as rest pose.", node->get_name()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little worried this will logspam but let's keep it and it will help us diagnose issues during the beta test.

Not something we have to fix here, but we should figure out a better approach for import warnings. I want to show warnings and errors per-file in the import dock or in some dedicated place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah was a little concerned about this as well, but it feels like something that should be informed to the user. Of course collecting them would be ideal.

On that note, we should also probably expose ufbx_metadata.warnings[] there, as ufbx can report a bunch of suspicious stuff in the file.

@bqqbarbhg bqqbarbhg force-pushed the ufbx-bind-pose-fix branch from d3ab316 to 8edbf70 Compare April 25, 2024 17:50
@bqqbarbhg
Copy link
Contributor Author

Thanks! I'll squash the commits after seeing that the CI passes etc.

Also ran through all the skeletons in my dataset (+ the entirety of the public dataset) again with the new changes. Seems like the new bind pose resolution method loads files correctly.

29 of 2636 files had multiple bind poses, and on manual review all seemed to be alright. One file had only a 4% error in bone pose, so that might have been a false positive. I just set the 1% relative error threshold arbitrarily, so could be looked into if necessary.

Turns out that the information in FBX Pose objects is relatively often broken.
Using skin cluster bind poses seems more reliable, but cannot capture the bind pose of the root bone.
@bqqbarbhg bqqbarbhg force-pushed the ufbx-bind-pose-fix branch from 8edbf70 to 0955690 Compare April 25, 2024 18:15
@akien-mga akien-mga merged commit 36833c6 into godotengine:master Apr 26, 2024
16 checks passed
@akien-mga
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

5 participants