Add Easy_Weight
to Addons
#47
@ -1,7 +1,8 @@
|
||||
from typing import List, Tuple
|
||||
|
||||
import bpy
|
||||
import bpy, sys
|
||||
from bpy.types import Operator, Mesh, VertexGroup, MeshVertex, Object
|
||||
import itertools
|
||||
import bmesh
|
||||
from .utils.naming import flip_name
|
||||
|
||||
@ -268,10 +269,7 @@ def build_vert_index_map(mesh) -> dict:
|
||||
|
||||
def find_weight_island_vertices(mesh: Mesh, vert_idx: int, group_index: int, vert_idx_map: dict, island=[]) -> List[int]:
|
||||
"""Recursively find all vertices that are connected to a vertex by edges, and are also in the same vertex group."""
|
||||
# I am sick of bmesh, so you must build a vertex connection map with build_vert_index_map and pass it in here.
|
||||
|
||||
|
||||
mesh.vertices[vert_idx].select = True
|
||||
island.append(vert_idx)
|
||||
for connected_vert_idx in vert_idx_map[vert_idx]: # For each edge connected to the vert
|
||||
if connected_vert_idx in island: # Avoid infinite recursion!
|
||||
@ -281,11 +279,11 @@ def find_weight_island_vertices(mesh: Mesh, vert_idx: int, group_index: int, ver
|
||||
find_weight_island_vertices(mesh, connected_vert_idx, group_index, vert_idx_map, island) # Continue recursion
|
||||
return island
|
||||
|
||||
def find_any_vertex_in_group(mesh: Mesh, vgroup: VertexGroup, is_selected=None) -> MeshVertex:
|
||||
def find_any_vertex_in_group(mesh: Mesh, vgroup: VertexGroup, excluded_indicies=[]) -> MeshVertex:
|
||||
"""Return the index of the first vertex we find which is part of the
|
||||
vertex group and optinally, has a specified selection state."""
|
||||
for v in mesh.vertices:
|
||||
if is_selected != None and v.select != is_selected:
|
||||
if v.index in excluded_indicies:
|
||||
continue
|
||||
for g in v.groups:
|
||||
if vgroup.index == g.group:
|
||||
@ -296,10 +294,13 @@ def build_weight_islands_in_group(mesh: Mesh, vgroup: VertexGroup, vert_index_ma
|
||||
"""Return a list of lists of vertex indicies: Weight islands within this vertex group."""
|
||||
islands = []
|
||||
while True:
|
||||
any_unselected_vertex_in_group = find_any_vertex_in_group(mesh, vgroup, is_selected=False)
|
||||
flat_islands = set(itertools.chain.from_iterable(islands))
|
||||
any_unselected_vertex_in_group = find_any_vertex_in_group(mesh, vgroup, excluded_indicies=flat_islands)
|
||||
if not any_unselected_vertex_in_group:
|
||||
break
|
||||
island = find_weight_island_vertices(mesh, any_unselected_vertex_in_group.index, vgroup.index, vert_index_map, [])
|
||||
sys.setrecursionlimit(len(mesh.vertices)) # TODO: I guess recursion is bad and we should avoid it here?
|
||||
island = find_weight_island_vertices(mesh, any_unselected_vertex_in_group.index, vgroup.index, vert_index_map, island=[])
|
||||
sys.setrecursionlimit(990)
|
||||
islands.append(island)
|
||||
return islands
|
||||
|
||||
@ -309,7 +310,7 @@ def select_vertices(mesh: Mesh, vert_indicies: List[int]):
|
||||
mesh.vertices[vi].select = True
|
||||
|
||||
class FocusRogueDeformingWeights(WeightPaintOperator):
|
||||
"""While in weight paint mode, find and focus a deforming vertex group which consists of several islands. Automatic fixing is dangerous, so this operator does not remove weights automatically. Keep using this until no more rogue weights are found"""
|
||||
"""While in weight paint mode, find and focus a deforming vertex group which consists of several islands. The smallest weight island is selected, but no weights are removed. Keep running this until no more rogue weights are found"""
|
||||
bl_idname = "object.focus_rogue_weights"
|
||||
bl_label = "Focus Rogue Weights"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
@ -326,10 +327,10 @@ class FocusRogueDeformingWeights(WeightPaintOperator):
|
||||
obj['skip_groups'] = l
|
||||
|
||||
@staticmethod
|
||||
def find_vgroup_with_multiple_islands(obj: Object, vert_index_map: dict) -> Tuple[VertexGroup, List[List[int]]]:
|
||||
def find_rogue_deform_weights(obj: Object, vert_index_map: dict) -> Tuple[VertexGroup, List[List[int]]]:
|
||||
"""Return the first vertex group we find that has multiple islands, as well as the islands."""
|
||||
mesh = obj.data
|
||||
for vgroup in obj.vertex_groups:
|
||||
for vgroup in get_deforming_vgroups(obj):
|
||||
if 'skip_groups' in obj and vgroup.name in obj['skip_groups']:
|
||||
continue
|
||||
obj.vertex_groups.active_index = vgroup.index
|
||||
@ -338,6 +339,8 @@ class FocusRogueDeformingWeights(WeightPaintOperator):
|
||||
|
||||
if len(islands) > 1:
|
||||
return vgroup, islands
|
||||
|
||||
return None, None
|
||||
|
||||
def execute(self, context):
|
||||
rig = context.pose_object
|
||||
@ -347,35 +350,35 @@ class FocusRogueDeformingWeights(WeightPaintOperator):
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_mode(type='VERT')
|
||||
bpy.ops.mesh.reveal()
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
mesh = obj.data
|
||||
vert_index_map = build_vert_index_map(mesh)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
vgroup, islands = self.find_vgroup_with_multiple_islands(obj, vert_index_map)
|
||||
vgroup, islands = self.find_rogue_deform_weights(obj, vert_index_map)
|
||||
|
||||
if vgroup:
|
||||
# Select islands except the one with the most verts.
|
||||
largest_island = max(islands, key=len)
|
||||
for island in islands:
|
||||
if island == largest_island: continue
|
||||
select_vertices(mesh, island)
|
||||
# Select the smallest island.
|
||||
select_vertices(mesh, min(islands, key=len))
|
||||
|
||||
# Support the case where the user chooses not to fix the rogue weights: Perhaps they are intentional
|
||||
self.save_skip_group(obj, vgroup)
|
||||
|
||||
self.report({'INFO'}, f'Found Vertex Group "{vgroup.name}" with {len(islands)} islands. Fix or ignore it, then keep running this operator to find the next group with rogue weights.')
|
||||
|
||||
mesh.use_paint_mask_vertex = True
|
||||
if rig:
|
||||
rig.select_set(True)
|
||||
mesh.use_paint_mask_vertex = True
|
||||
bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
|
||||
else:
|
||||
if rig:
|
||||
rig.select_set(True)
|
||||
obj.vertex_groups.active_index = org_vg_idx
|
||||
bpy.ops.object.mode_set(mode=org_mode)
|
||||
if 'skip_groups' in obj and len(obj['skip_groups']) > 0:
|
||||
self.report({'INFO'}, f"No rogue weights found, but {len(obj['skip_groups'])} were skipped. Run the operator again to cycle through groups with rogue weights.")
|
||||
self.report({'INFO'}, f"No rogue weights found, but {len(obj['skip_groups'])} were skipped. Keep running the operator to cycle through groups with multiple weight islands to make sure they are desired.")
|
||||
del obj['skip_groups']
|
||||
else:
|
||||
self.report({'INFO'}, "No rogue weights found!")
|
||||
|
Loading…
Reference in New Issue
Block a user