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

Allow preserving the initial bone pose in rest fixer #88821

Merged
merged 1 commit into from
Feb 27, 2024

Conversation

lyuma
Copy link
Contributor

@lyuma lyuma commented Feb 25, 2024

Adds a "Preserve Initial Pose" boolean option to the Skeleton3D Rest Fixer settings retarget/rest_fixer/preserve_initial_pose
This option preserves the bone pose as it would have been before silhouette adjustment.

Basically, ufbx calculates the rest pose differently in some models which use skin binds. Rest pose should only used in the retargeting process, but prior to this change, the retargeting flow left the model in rest pose and discarded the actual bone_pose, which now makes scene inheritance dependent on the retargeter's rest pose.

Before:
before_preserve_initial_pose
Show Rest Only (same before and after):
tpose_rest
After:
preserve_initial_pose

@fire
Copy link
Member

fire commented Feb 25, 2024

Is there a reason why this cannot be defaulted on?

@TokageItLab
Copy link
Member

Can you explain what the actual case this solves is?
Do you mean that if we change the pose after importing the Skeleton and then re-import it, we will have a problem?

@lyuma
Copy link
Contributor Author

lyuma commented Feb 25, 2024

A model may be in some pose upon export from the DCC. ufbx has some code to guess the rest pose from skin binds, and the retarget silhouette fixer will also attempt to calculate a new rest pose.

The retarget system operates entirely on bone rest. however, some code at the end of the retarget flow copies the bone rest to the bone pose, losing the initial pose (which may have been important).

It is probably not important for some usecases, but I need the initial pose in Unidot because scene inheritance may depend on knowing the initial pose.
@fire it should not default on for two reasons:
First, many users probably prefer to see their model in t-pose. Second, it would break compatibility of scene inheritance if we changed the default. The behavior prior to this change is to discard the initial pose and replace with t-pose

@AThousandShips AThousandShips added this to the 4.x milestone Feb 25, 2024
@TokageItLab
Copy link
Member

I understand that in cases where the model includes both rests and poses, it is important that the importer be based on rests only.

I am still unclear about the case in question.

Is the problem case where the rest is a T-pose but the pose is not a T-pose? Or opposite?

Is it only the pose that appears after import that is affected? Is the animation also affected? Can animations be shared correctly between multiple models using this workflow?

It would be helpful to have a file for testing to help me understand

@fire fire requested a review from a team February 25, 2024 18:16
@lyuma
Copy link
Contributor Author

lyuma commented Feb 25, 2024

From what I understand, your concern is that a model is imported which perhaps only has a subset of animation tracks (for example, Left and Right arms)

Is it only the pose that appears after import that is affected?

Correct, this is the only thing affected

Is the animation also affected?

It should not be affected by the initial pose. My understanding is the rest fixer guarantees that animation keys are added for all bones in the bone map (unless the user manually disables this)

Basically, the animation keys would be relative to the initial pose. However, during retarget we may adjust the model's pose and it is critical that the animation continue to look correct. Therefore, Godot must already be adding the necessary keys, and animations should play regardless of pose.

My assumption here is that the animation optimizer won't remove tracks that are equal to the bone pose: if so, this would cause t-pose animations to have no tracks in current version of godot.

Can animations be shared correctly between multiple models using this workflow?

I do not see how the initial bone pose would affect animation sharing. The initial bone pose should be lost when animations are played.

This PR is really a matter of "correctness" or a "technicality". If a model is initially posed as doing a handshake without retarget, it should also be initially posed in a handshake with retarget. Retarget should be adjusting the rolls of the bones, but not affect the visual result of the model import.
The goal of this PR is to allow the user to preserve the imported result visually, while performing retargeting correctly..

To be perfectly honest, this PR is not critical. But it feels like the right thing to do, to avoid losing information.

@TokageItLab
Copy link
Member

TokageItLab commented Feb 25, 2024

If I understand correctly, we could change the option name to like reset all bone poses after import with inverting the boolean, since already there is the option to do it in Skeleton3D Viewport Menu.

image

However, I believe this option should be outside of Retargeter. And then, it may be fine to disable it by default, since it is visually independent of the retarget workflow.

@lyuma
Copy link
Contributor Author

lyuma commented Feb 25, 2024

Reset all Bone Poses after Import sounds good!

As for why it's in retargeter, it is because that is the only place I could guarantee correctness.
Outside retargeter, the bone rest will usually match bone pose anyway. The ufbx behavior is new and only affects bone rest.

However, if we blindly reset bone poses outside of rest fixer, then we may need to add additional animation tracks for each bone we changed which means there will be possible side effects of using this option that the user may not expect.

To contrast, in retargeting, it is expected that all animation tracks exist after retargeting is done, so there will be no side effects of adjusting the bone pose.

To summarize:

  1. When silhouette fixer (or Retargeting option to use a template for silhouette. #88824) is not enabled, he bone pose is almost always equal to the bone rest. The case where this option has an effect is rare.
  2. When rest fixer is not enabled, it is not clear that we can safely adjust the initial bone pose without disrupting animations.

@TokageItLab
Copy link
Member

After renaming it to Reset All Bone Poses After Import, I think the default should be False if you place it outside the retarget or True if you place it inside the retarget. For compatibility, it may be fine to place it in the retarget with the default be True.

Adds a "Reset All Bone Poses After Import" option to the Skeleton3D Rest Fixer settings.
Default value of true resets the bone poses to rest (usually a t-pose), matching previous behavior.
If disabled, keeps the bones posed as they would have been before silhouette adjustment.
Copy link
Member

@TokageItLab TokageItLab left a comment

Choose a reason for hiding this comment

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

It makes sense as an optional feature. As far as I can look in the code, rest has not been changed, so I assume this change is reasonably safe.

@@ -603,6 +609,30 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
}
}
}
if (p_options.has("retarget/rest_fixer/reset_all_bone_poses_after_import") && !bool(p_options["retarget/rest_fixer/reset_all_bone_poses_after_import"])) {
Copy link
Member

Choose a reason for hiding this comment

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

If store the bool results of this earlier, you can avoid unnecessary calculations for some pose changes in line 117-120 and 131 as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered this, but the number of calculations are low, and doing them in that part of the code preserves correctness (counteracting scale changes).

@akien-mga akien-mga modified the milestones: 4.x, 4.3 Feb 26, 2024
@akien-mga akien-mga merged commit 04e16a2 into godotengine:master Feb 27, 2024
16 checks passed
@akien-mga
Copy link
Member

Thanks!

@fire fire deleted the preserve_initial_pose branch April 4, 2024 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants