Add Easy_Weight to Addons #47

Merged
Nick Alberelli merged 48 commits from feature/easy_weights into main 2023-05-17 22:13:57 +02:00
Showing only changes of commit 131f43eb21 - Show all commits

View File

@ -17,6 +17,40 @@ All functionality can be found in the Sidebar->EasyWeight->Weight Islands panel.
# TODO: # TODO:
# UIList: Filtering options, explanations as to what the numbers mean. Maybe a warning for Calculate Islands operator when the mesh has a lot of verts or vgroups. # UIList: Filtering options, explanations as to what the numbers mean. Maybe a warning for Calculate Islands operator when the mesh has a lot of verts or vgroups.
class VertIndex(PropertyGroup):
index: IntProperty()
class WeightIsland(PropertyGroup):
vert_indicies: CollectionProperty(type=VertIndex) # TODO: Is this really needed?? Why can't a CollectionProperty(type=IntProperty) be fine??
class IslandGroup(PropertyGroup):
name: StringProperty() # Name of the vertex group this set of island is associated with
islands: CollectionProperty(type=WeightIsland)
num_expected_islands: IntProperty(
name="Expected Islands",
default=1,
min=1,
description="Number of weight islands that have been marked as the expected amount by the user. If the real amount differs from this value, a warning appears"
)
index: IntProperty()
def update_vgroup_islands(mesh, vgroup, vert_index_map, island_groups, island_group=None) -> IslandGroup:
islands = get_islands_of_vgroup(mesh, vgroup, vert_index_map)
if not island_group:
island_group = island_groups.add()
island_group.index = len(island_groups)-1
island_group.name = vgroup.name
else:
island_group.islands.clear()
for island in islands:
island_storage = island_group.islands.add()
for v_idx in island:
v_idx_storage = island_storage.vert_indicies.add()
v_idx_storage.index = v_idx
return island_group
def build_vert_index_map(mesh) -> dict: def build_vert_index_map(mesh) -> dict:
"""Build a dictionary of vertex indicies pointing to a list of other vertex indicies that the vertex is connected to by an edge.""" """Build a dictionary of vertex indicies pointing to a list of other vertex indicies that the vertex is connected to by an edge."""
@ -81,45 +115,11 @@ def get_islands_of_vgroup(mesh: Mesh, vgroup: VertexGroup, vert_index_map: dict)
islands.append(island) islands.append(island)
return islands return islands
def update_vgroup_islands(mesh, vgroup, vert_index_map, island_groups, island_group=None) -> IslandGroup:
islands = get_islands_of_vgroup(mesh, vgroup, vert_index_map)
if not island_group:
island_group = island_groups.add()
island_group.index = len(island_groups)-1
island_group.name = vgroup.name
else:
island_group.islands.clear()
for island in islands:
island_storage = island_group.islands.add()
for v_idx in island:
v_idx_storage = island_storage.vert_indicies.add()
v_idx_storage.index = v_idx
return island_group
def select_vertices(mesh: Mesh, vert_indicies: List[int]): def select_vertices(mesh: Mesh, vert_indicies: List[int]):
assert bpy.context.mode != 'EDIT_MESH', "Object must not be in edit mode, otherwise vertex selection doesn't work!" assert bpy.context.mode != 'EDIT_MESH', "Object must not be in edit mode, otherwise vertex selection doesn't work!"
for vi in vert_indicies: for vi in vert_indicies:
mesh.vertices[vi].select = True mesh.vertices[vi].select = True
class VertIndex(PropertyGroup):
index: IntProperty()
class WeightIsland(PropertyGroup):
vert_indicies: CollectionProperty(type=VertIndex) # TODO: Is this really needed?? Why can't a CollectionProperty(type=IntProperty) be fine??
class IslandGroup(PropertyGroup):
name: StringProperty() # Name of the vertex group this set of island is associated with
islands: CollectionProperty(type=WeightIsland)
num_expected_islands: IntProperty(
name="Expected Islands",
default=1,
min=1,
description="Number of weight islands that have been marked as the expected amount by the user. If the real amount differs from this value, a warning appears"
)
index: IntProperty()
class MarkIslandsAsOkay(Operator): class MarkIslandsAsOkay(Operator):
"""Mark this number of vertex islands to be the intended amount. Vertex group will be hidden from the list until this number changes""" """Mark this number of vertex islands to be the intended amount. Vertex group will be hidden from the list until this number changes"""
bl_idname = "object.set_expected_island_count" bl_idname = "object.set_expected_island_count"
@ -286,6 +286,83 @@ class CalculateWeightIslands(Operator):
bpy.ops.object.mode_set(mode=org_mode) bpy.ops.object.mode_set(mode=org_mode)
return {'FINISHED'} return {'FINISHED'}
class EASYWEIGHT_UL_weight_island_groups(UIList):
@staticmethod
def draw_header(layout):
row = layout.row()
split1 = row.split(factor=0.5)
row1 = split1.row()
row1.label(text="Vertex Group")
row1.alignment = 'RIGHT'
row1.label(text="|")
row2 = split1.row()
row2.label(text="Islands")
def filter_items(self, context, data, propname):
flt_flags = []
flt_neworder = []
list_items = getattr(data, propname)
island_groups = getattr(data, propname)
helper_funcs = bpy.types.UI_UL_list
if self.filter_name:
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, island_groups, "name",
reverse=self.use_filter_sort_reverse)
if not flt_flags:
flt_flags = [self.bitflag_filter_item] * len(island_groups)
if self.use_filter_invert:
for idx, flag in enumerate(flt_flags):
flt_flags[idx] = 0 if flag else self.bitflag_filter_item
for idx, island_group in enumerate(island_groups):
if len(island_group.islands) < 1:
# Filter island groups with only 1 or 0 islands in them
flt_flags[idx] = 0
elif len(island_group.islands) == island_group.num_expected_islands:
# Filter island groups with the expected number of islands in them
flt_flags[idx] = 0
return flt_flags, flt_neworder
def draw_filter(self, context, layout):
# Nothing much to say here, it's usual UI code...
main_row = layout.row()
row = main_row.row(align=True)
row.prop(self, 'filter_name', text="")
row.prop(self, 'use_filter_invert', toggle=True, text="", icon='ARROW_LEFTRIGHT')
row = main_row.row(align=True)
row.use_property_split=True
row.use_property_decorate=False
row.prop(self, 'use_filter_sort_alpha', toggle=True, text="")
row.prop(self, 'use_filter_sort_reverse', toggle=True, text="", icon='SORT_ASC')
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
island_group = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
icon = 'ERROR'
num_islands = len(island_group.islands)
if num_islands == island_group.num_expected_islands:
icon = 'CHECKMARK'
row = layout.row()
split = row.split(factor=0.5)
row1 = split.row()
row1.label(text=island_group.name)
row1.alignment = 'RIGHT'
row1.label(text="|")
row2 = split.row()
row2.label(text=str(num_islands), icon=icon)
op = row2.operator(FocusSmallestIsland.bl_idname, text="", icon='VIEWZOOM').vgroup = island_group.name
row2.operator(MarkIslandsAsOkay.bl_idname, text="", icon='CHECKMARK').vgroup = island_group.name
# TODO: Operator to mark current number of islands as the expected amount
elif self.layout_type in {'GRID'}:
pass
class EASYWEIGHT_PT_WeightIslands(Panel): class EASYWEIGHT_PT_WeightIslands(Panel):
"""Panel with utilities for detecting rogue weights.""" """Panel with utilities for detecting rogue weights."""
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
@ -306,8 +383,9 @@ class EASYWEIGHT_PT_WeightIslands(Panel):
if len(island_groups)==0: return if len(island_groups)==0: return
active_weight_islands = obj.island_groups[obj.active_islands_index] active_weight_islands = obj.island_groups[obj.active_islands_index]
row = layout.row() EASYWEIGHT_UL_weight_island_groups.draw_header(layout)
row = layout.row()
row.template_list( row.template_list(
'EASYWEIGHT_UL_weight_island_groups', 'EASYWEIGHT_UL_weight_island_groups',
'', '',
@ -317,53 +395,6 @@ class EASYWEIGHT_PT_WeightIslands(Panel):
'active_islands_index', 'active_islands_index',
) )
class EASYWEIGHT_UL_weight_island_groups(UIList):
def filter_items(self, context, data, propname):
flt_flags = []
flt_neworder = []
list_items = getattr(data, propname)
island_groups = getattr(data, propname)
helper_funcs = bpy.types.UI_UL_list
if self.filter_name:
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, island_groups, "name",
reverse=self.use_filter_name_reverse)
if not flt_flags:
flt_flags = [self.bitflag_filter_item] * len(island_groups)
if self.use_filter_invert:
for idx, flag in enumerate(flt_flags):
flt_flags[idx] = 0 if flag else self.bitflag_filter_item
for idx, island_group in enumerate(island_groups):
if len(island_group.islands) < 1:
# Filter island groups with only 1 or 0 islands in them
flt_flags[idx] = 0
elif len(island_group.islands) == island_group.num_expected_islands:
# Filter island groups with the expected number of islands in them
flt_flags[idx] = 0
return flt_flags, flt_neworder
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
island_group = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
icon = 'ERROR'
num_islands = len(island_group.islands)
if num_islands == island_group.num_expected_islands:
icon = 'CHECKMARK'
row = layout.row()
row.label(text=island_group.name)
row.label(text=str(num_islands), icon=icon)
op = row.operator(FocusSmallestIsland.bl_idname, text="", icon='VIEWZOOM').vgroup = island_group.name
row.operator(MarkIslandsAsOkay.bl_idname, text="", icon='CHECKMARK').vgroup = island_group.name
# TODO: Operator to mark current number of islands as the expected amount
elif self.layout_type in {'GRID'}:
pass
classes = [ classes = [
VertIndex, VertIndex,
WeightIsland, WeightIsland,