ousnius / BodySlide-and-Outfit-Studio

BodySlide and Outfit Studio, a tool to convert, create, and customize outfits and bodies for Bethesda games.
GNU General Public License v3.0
286 stars 63 forks source link

Mirror weights #417

Closed themilkdrinker0 closed 2 years ago

themilkdrinker0 commented 2 years ago

A mirror weights tool, just a button actually, it be useful to fix weights that are not in perfect symmetry, it could also be useful to for the skinning process, so I wouldn't have to pose both sides to paint, I could just work on one side and then mirror the weights to the other side to speed up the process, given the topology is perfectly symmetrical of course.

OS already can pick the mirrored bones, just need a button to mirror the weights from one side to another. Might need to specify from which to which like -X to +X and vice-versa.

sts1skj commented 2 years ago

I agree that a "Mirror weights" command is a desirable feature; I've encountered the situation you mention myself.

Internally, the "Copy Bone Weights" code uses the AutoMorpher. That's a part of Outfit Studio I haven't touched before. So, if I were to implement this feature request, it would likely take me a lot of work. At the moment, I'd prefer that someone else did it.

I wonder if it would be useful to also have a "Mirror coordinates" command that would try to match up mirrored vertices and force them to have mirrored coordinates (and maybe coordinate diffs too).

themilkdrinker0 commented 2 years ago

Like this? image

sts1skj commented 2 years ago

Yes.

sts1skj commented 2 years ago

I've taken a look at the existing code for "Copy Bone Weights" and "Conform Sliders", which are the two Outfit Studio commands that use the automorpher. For Copy Bone Weights, it creates a temporary slider for each bone (with names like "NPC Pelvis [Pelv]WT "), fills the temporary sliders with weight data from the reference shape, does Conform Sliders for these sliders, copies the results to the target's weights, and then deletes the sliders. So the functionality of the two algorithms is intertwined, currently.

I see two different approaches to trying to do a mirror-weights command:

Idea 1: Modify "Copy Bone Weights". A new option could be added to the "Copy Bone Weights" dialog window called "Mirror weights across the x-axis for the current shape instead of copying from the reference". Alternatively, it could have its own command, "Mirror Copy Bone Weights", but still use the same options dialog window and implementation. Internally, this idea could be a lot of work; it might be necessary to untangle the code from the automorpher, which is currently loaded down with options and features for Conform Sliders that aren't used for Copy Bone Weights.

The resulting algorithm would copy weights from a shape to itself, mirrored, in much the same way as Copy Bone Weights copies them from the reference. It would respect settings like "Normalize Weights" and the normalize-bones list. To restrict it to updating only some vertices, you'd mask everything but those vertices. (So, to copy from -x to +x, you'd mask the -x side.)

(The "normalize-bones list" is the bones with a little double-arrow next to them instead of a dot. You click on the dot or double-arrow to add or remove a bone from the list. The weight normalizer will only add or remove weight from bones in the list, unless the list is empty or only contains the selected bone and its mirror.)

Idea 2: Write a general-purpose Symmetrizer. This command would try to match up every unmasked point with a mirror point. It would then compare the data for each matched point and its mirror, listing all the inconsistencies:

The user would then be able to select which asymmetries they wanted to fix. If a point and its mirror were both unmasked, the data would be averaged for the two. If a point's mirror were masked, it would be updated from its mirror. If a point's mirror were itself, the x-coordinate would be zeroed for positions, and the weights for mirrored bone pairs would be averaged.

Would it be useful to list all the points that would be updated by each option? Or would a count of updated points be enough? Would an option to select which coordinates (x, y, or z) were updated be useful?

If more than one bone or bone pair had weight differences, should they be listed? Should the user be able to pick which bones to update and which ones not to?

If there are unmatched points, should there be an option to mask everything but those points, so the user can see which points they are and try to fix them?

Maybe there should be an option for restricting weight updates to only the selected bones. Should the "Normalize Weights" option be respected? Should the normalize-bones list be respected?

A simple way of finding mirror points is to just look for the nearest point with the x-coordinate flipped. But what if two points are closest to the same mirror point? Should the algorithm look for that and refuse to match? Or what if the mirror point's mirror is not the point? Maybe that should be checked for and rejected. Or maybe the algorithm for finding mirror points shouldn't be so simple; maybe it should rely on the mesh topology in some way. (The description of the "Smart Realign Symmetry" algorithm in the earlier message seems to suggest they do some sort of analysis of the mesh topology rather than match up points via their coordinates.) I think I could probably write an algorithm that used mesh topology, but it would almost certainly need the edge connections to be symmetrical, which is rarely the case in nifs I've seen, even when the points themselves are perfectly symmetrical.

And what about finding and fixing asymmetries in edges and triangles? I've seen lots of meshes (like CBBE Body) that have vertices more or less mirrored but a few of the triangles aren't. In every case I've seen, flipping some triangle edges would be sufficient to achieve triangle and edge symmetry. Should we provide an option or command for symmetrizing triangles?

sts1skj commented 2 years ago

@ousnius I like the Symmetrizer idea. I think I'm ready to start work on it. Do you have any comments or suggestions? In particular, I'd like to know if you find any part of this idea silly, pointless, or redundant.

sts1skj commented 2 years ago

Here are the choices I made for the questions I asked in my earlier post: