As currently designed and implemented, the override system stores and handles overrides purely per datablock, without any internal support for systems that are composed of multiple inter-related datablocks, like character rigs.
This means that the inter-related nature of the character is only handled by an operator that is run once when the character is initially overriden, and this intent information is then discarded, because there is no way to store it.
This results in issues dealing with the character override going forward:
Since the override system is not aware that the set of datablock overrides comprising the character override forms a consistent inter-related system, the operator has to decompose this intent into many individual property overrides for links between datablocks. This produces a lot of data, which can't be good for performance. It also freezes the state of the links, so changing them in the original object will no longer affect the property values in the overrides.
Failure to handle new links
Another consequence of discarding the inter-related system intent is that new links appearing between datablocks (new drivers, etc) will not be overridden either. This is a huge issue, as it can lead to very confusing 'bugs' in the overridden rigs, and is not as immediately obvious as the case of a completely new object added to the rig.
Proposal: Override Groups
As described above, the root of the problem is that there is no way to represent the intent that a set of datablock overrides in fact forms an integrated inter-related system to the low-level override engine in blender. Solving this requires adding a new low-level concept to the override data.
This proposes introducing a concept of an Override Group, which represents an instance of a character or other inter-related system, and follows these rules:
One group per override
Every datablock override can belong to at most one group, which is set at the time of creation and cannot be changed.
A datablock that doesn't belong to a real group should be considered to be in a group with itself when processing override rules. I.e. a datablock is always in group with itself, whether in a real group or not.
Links within the group are implicitly overridden
Every group represents an implicit mapping to the group member overrides from their originals. This mapping should be applied to all datablock pointer properties of the member overrides before evaluating the rules stored in the datablock itself. The remapped value should also be considered to be the 'original' when recomputing the rules from the values of the datablock, so the implicit group rules aren't redundantly duplicated as per-datablock rules.
Thus, datablock pointer properties can now have three override states: fully original, group override (new), local override. Other properties still have only two states. The local override rules have priority over the group override mapping.
Since every override is always in a group with itself, internal links are always in the group override state, even if the property owner doesn't belong to a real group.
UI: Override into group
When a datablock override belongs to an override group, the UI should allow overriding outgoing references into the same group, in addition to overriding just one reference, or all references, as is currently possible. Arguably, overriding into group should be the default in this case too.
Tie to collections?
To avoid the need to create a new datablock type to represent the groups, it seems to make sense to use one of the members as the representative of the group, and simply store datablock pointers in the override data. It makes most sense to use the root collection of the character for this.
Introducing override groups that behave as described above will solve following issues:
- The override system will be aware of the intent that a set of overrides forms an integrated system, and will be able to automatically handle new links between originals within the group.
- Keeping the intra-group link overrides implicit means that the link targets can be changed in the original file, and the overrides will update to follow, unless explicitly changed by the user.
- UI support for adding overrides for outgoing links to the group makes it very easy to manually fix new objects added to the character, or extra datablocks that weren't handled by the operator, like in T70319.
From very cursory understanding of the override code, implementing groups should be as simple as doing an extra scan of overrides to build up the mappings for the groups, and then using those in a pointer replace pass, and in the rule generator. It seems to me that supporting the new override state in the UI might be the more difficult part.
Self references already seem to effectively be handled 'implicitly' by the existing copy datablock code, but the rule generator is not aware of that and creates new rules afterwards. I think that's a design bug, which I adressed in the above rule via self-grouping.
It might make sense to support a hierarchy of groups, implemented by including the mapping of the 'parent' into the final mapping of the group, with group members having priority. However it's not clear what the use may be. One idea may be to have a 'root' group which is the final parent of all groups, including the imaginary one-member groups for non-grouped overrides. That group can be used to truly override 'all' references to an original object.
Deep Collection Overrides
To fully automate updating of overrides to changes in a character it is necessary to also automatically handle addition of new objects. This can be done by supporting some kind of 'deep' overrides for collections, which will automatically spawn a group, and then fill it with overrides created for anything contained in the collection and its child collections. Basically, converting the create character override operator into a built-in feature.
However, after addition of groups, handling new objects should become quite easy and foolproof (click on it and choose to 'override into group'), so this part is not that critical compared to the current state of affairs.