Page MenuHome

Rigify: Don't fail generation just because the target rig is hidden
AbandonedPublic

Authored by Demeter Dzadik (Mets) on May 22 2021, 4:31 AM.

Details

Summary

This is something that was mentioned by @Daniel Salazar (zanqdo) in the #animation-module chat, and I already had the code for it in CloudRig, and the mood struck to contribute, so here goes!

Normally when using Rigify, the generation target rig must be completely visible, or the generation will fail.
This patch adds a utility class which can be instanced to make sure that an object is visible no matter what.

Still, if for any reason we fail to make the rig visible, throw a useful error message, and abort generation early, before the rig is destroyed.

Diff Detail

Repository
rBA Blender Add-ons
Branch
rigify_dont_fail_on_hidden
Build Status
Buildable 14707
Build 14707: arc lint + arc unit

Event Timeline

Demeter Dzadik (Mets) requested review of this revision.May 22 2021, 4:31 AM
Demeter Dzadik (Mets) created this revision.

forgot to restore visibility when rig generation does not fail

This should be considered a bugfix since the generation goes awry if not done this way

Demeter Dzadik (Mets) planned changes to this revision.May 27 2021, 12:15 PM
In blender.chat, @Ivan Cappiello (icappiello) wrote:

i think it address a limitation and it's very welcome. I am always a bit worried about general design of the tool. Here at MAD Rigify it's used also (and mostly) by non riggers but by expert riggers as well. There's always a great struggle between the idea of a magic button and a clear statement of what exactly happened when you clicked the button. We always try to design the tool to keep both (casual rigger and rigging TD) experiences in count.
I have some concerns about rigify dealing with more than just the armature itself.
For example, here we have random crashes exiting local view mode. It happens very often but randomly, so i can't open a report. But it happens. If rigify triggers a local view, and the local view crashes blender, it would be really tricky to find the issue, and moreover if the culprit is rigify itself or the local view code.
I use this case as just an example of why this seems to me an hack way of doing it. It's probably a needed upgrade, but would be better to restrict its interactions to just the two involved object visibility (and even there, there could be problems dealing with users customs like driver on visibility states and so on).
so what we came up with previous rigify design is to have this potentially useful, but more edgy cases, features like toggle options (see "advanced options" menu). This way the user is aware of the fact that rigify is doing something different than elementary rig generation and we can restrict the case if that feature raise troubles because the basic rig generation process stays untouched

Your feedback is very welcome, I wouldn't want to make changes to Rigify without your approval, and I think you make great points.

But I would like to take a stab at re-defining the issue you're raising:

  1. It might actually be confusing for a user who doesn't entirely know what they are doing, if rig generation succeeded even though the target rig is hidden. After all, they won't see any change on their screen.
  2. Calling local view operator can be crash-prone.

So I would make the following changes:

  1. Don't re-hide the target rig after generation. Simply, if it was hidden, unhide it, and leave it un-hidden. No longer support the case where the rig is in a disabled collection: This would still fail, but with a useful error message: "Target rig is in a disabled collection, could not generate".
  2. Avoid exiting local view. Instead, the target rig can be brought into local view safely, without bpy.ops. I already do this in some other code I have which I know to be stable, I just need to check out how I was doing it.

Not having to mess around with collections would also avoid accessing the global context, as pointed out by Sybren via chat.

Demeter Dzadik (Mets) updated this revision to Diff 37711.EditedJun 2 2021, 2:10 AM

With the new changes, the current behaviours are (irrespective of whether generating all the bones succeeds or fails):

  • If the target rig is hidden by eye or screen icons: They will be toggled to make the rig visible, and they will be left that way. This should cause the least confusion for new users.
  • If the target rig is in a disabled collection: Move it to the scene root during generation, then remove it if it wasn't there originally. This is unlikely to happen for new users, who probably don't put things in disabled collections, but it allows more experienced users to deliberately put the target rig in a disabled collection and still be able to re-generate without going into the outliner to hunt down the right collection toggle.
  • If we are in local view but target the rig isn't: It turns out no special treatment is needed here. The rig generates successfully even if it is hidden by local view.
  • If the rig has driver on its visibility: This also turns out not to matter because such drivers are removed anyways, along with all other animation data, as an early stage of rig generation.
  • If the rig could not be made visible for any other reason: (I couldn't come up with any) It should throw an error message that says "Could not start generating because the target rig could not be made visible".

Give it a shot and let me know if this feels right to you from a UX perspective, and if yes, then I will nag Alexander to take a look at the code. ^^

Notes on the code:

  • Functionality of generate_rig() was moved to enter and exit. This could also be used for error handling, but I would do that as a separate patch that focuses on general error handling changes.
  • Simplified the code used for determining what collection to assign the target rig to. Also, the target rig is no longer forced to be in the same collection as the metarig - it is, however, still forced into the scene collection, if it isn't in the scene at all.
Demeter Dzadik (Mets) edited the summary of this revision. (Show Details)Jun 2 2021, 2:24 AM
Alexander Gavrilov (angavrilov) requested changes to this revision.Jun 2 2021, 10:50 AM
Alexander Gavrilov (angavrilov) added inline comments.
rigify/generate.py
61

This is totally unacceptable. The with pattern should be used in EnsureVisible.

This revision now requires changes to proceed.Jun 2 2021, 10:50 AM
rigify/generate.py
61

What's unacceptable is that this is yet another communication issue. I proposed this solution in chat, while you were present, and you didn't respond:

Look man, I'm always happy to learn, but I won't spend my time re-writing code just because it rubs you the wrong way. Using with in EnsureVisible would cause the entire generate() function to be indented, which is why I assumed that is not what you meant. I don't see what makes this "totally unacceptable", so please provide an explanation, and then I'll be happy to change it, no problem.

Remove with shennanigans

Demeter Dzadik (Mets) added a comment.EditedJun 3 2021, 9:41 PM

Just so it's here:

In blender.chat, @Alexander Gavrilov (angavrilov) wrote:

To be more precise, it's unacceptable to use a huge major class like Generator to implement a try/finally avoiding hack. On the other hand it is totally fine in a small limited scope utility like the make visible class.

I don't think it's a try/finally avoiding hack because there isn't any error handling in there and exceptions will get passed through as long as we return None. But, I will say that I do find the whole with statement in python a bit strange because it's not clear from the class's implementation that it should always be instantiated with with. I'm still new to this, so I will just go ahead and trust you on this one.

Maybe I should try to code my 'acceptable' alternative - it's easier to get into the details while trying to actually do stuff.

Feel free to, if all my patch does is to motivate you to do it better, that's a win in my book! ;)

Re the 'indentation' problem, the solution is likely splitting the huge generate method into smaller logical parts.

When trying to update this, I realized using EnsureVisible with the with keyword is not even possible after all, since the restore() function that should be called when rig generation succeeds or fails requires the context, and we can't pass it to the __exit__() function.

For now I went back to the previous solution, which might just be fine. Let me know what you think.

Demeter Dzadik (Mets) planned changes to this revision.Jun 24 2021, 10:11 PM

Based on feedback from Bassam and Luciano in today's animation module meeting, I will be making the following changes when I find the time:

  • When the target rig is hidden but can be made visible with the eye or screen icons, do that and do not restore its visbility to original state.
  • When the target rig is hidden and cannot be made visible with the eye or screen icons, fail early with an error explaining that the rig is in a hidden collection, therefore cannot be generated.

Patch is much simpler now, simply always un-hide target rig with the eye and screen icons, and when that's not enough, raise an error that tells the user they must make the target rig visible.

I prefer an assert over raise MetaRigError because it avoids a confusing line at the end of the error ("incorrect armature for type 'generate'").

I committed a somewhat modified version to reduce review back and forth (I mainly didn't like collection related stuff). If it addresses all use cases, please abandon this patch.

P.S. This is not appropriate use of asserts - they are for bugs in the code, not user errors.

Demeter Dzadik (Mets) marked an inline comment as done.

Thanks a lot!