1 # ***** BEGIN GPL LICENSE BLOCK *****
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ***** END GPL LICENCE BLOCK *****
21 "name": "Show Vertex Groups/Weights",
22 "author": "Jason van Gumster (Fweeb), Bartius Crouch, CoDEmanX",
24 "blender": (2, 80, 4),
25 "location": "3D View > Properties Region > Show Weights",
26 "description": "Finds the vertex groups of a selected vertex "
27 "and displays the corresponding weights",
29 "doc_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
30 "Scripts/Modeling/Show_Vertex_Group_Weights",
31 "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
35 #TODO - Add button for selecting vertices with no groups
38 import bpy
, bmesh
, bgl
, blf
, mathutils
41 # Borrowed/Modified from Bart Crouch's old Index Visualizer add-on
42 def calc_callback(self
, context
):
44 if context
.mode
!= "EDIT_MESH" or len(context
.active_object
.vertex_groups
) == 0:
47 # get color info from theme
48 acol
= context
.preferences
.themes
[0].view_3d
.editmesh_active
49 tcol
= (acol
[0] * 0.85, acol
[1] * 0.85, acol
[2] * 0.85)
51 # get screen information
52 mid_x
= context
.region
.width
/ 2.0
53 mid_y
= context
.region
.height
/ 2.0
54 width
= context
.region
.width
55 height
= context
.region
.height
58 view_mat
= context
.space_data
.region_3d
.perspective_matrix
59 ob_mat
= context
.active_object
.matrix_world
60 total_mat
= view_mat
@ ob_mat
62 # calculate location info
66 me
= context
.active_object
.data
67 bm
= bmesh
.from_edit_mesh(me
)
68 dvert_lay
= bm
.verts
.layers
.deform
.active
70 verts_max
= context
.scene
.show_vgroups_weights_limit
73 for elem
in reversed(bm
.select_history
):
74 if not isinstance(elem
, bmesh
.types
.BMVert
): #or elem.hide:
76 if bm
.select_history
.active
== elem
:
77 locs
.append([acol
[0], acol
[1], acol
[2], elem
.index
, elem
.co
.to_4d()])
79 locs
.append([tcol
[0], tcol
[1], tcol
[2], elem
.index
, elem
.co
.to_4d()])
80 dvert
= elem
[dvert_lay
]
81 for vgroup
in context
.active_object
.vertex_groups
:
82 if vgroup
.index
in dvert
.keys():
83 weights
+= [elem
.index
, vgroup
.index
, dvert
[vgroup
.index
]]
84 verts_used
.append(elem
)
90 if v
.select
and v
not in verts_used
: #XXX Should check v.hide here, but it doesn't work
91 if isinstance(bm
.select_history
.active
, bmesh
.types
.BMVert
) and bm
.select_history
.active
.index
== v
.index
:
92 locs
.append([acol
[0], acol
[1], acol
[2], v
.index
, v
.co
.to_4d()])
94 locs
.append([tcol
[0], tcol
[1], tcol
[2], v
.index
, v
.co
.to_4d()])
96 for vgroup
in context
.active_object
.vertex_groups
:
97 if vgroup
.index
in dvert
.keys():
98 weights
+= [v
.index
, vgroup
.index
, dvert
[vgroup
.index
]]
105 vec
= total_mat
@ loc
[4] # order is important
107 vec
= mathutils
.Vector((vec
[0] / vec
[3], vec
[1] / vec
[3], vec
[2] / vec
[3]))
108 x
= int(mid_x
+ vec
[0] * width
/ 2.0)
109 y
= int(mid_y
+ vec
[1] * height
/ 2.0)
110 texts
+= [loc
[0], loc
[1], loc
[2], loc
[3], x
, y
, 0]
112 # store as ID property in mesh
113 context
.active_object
.data
["show_vgroup_verts"] = texts
114 context
.active_object
.data
["show_vgroup_weights"] = weights
118 def draw_callback(self
, context
):
120 if context
.mode
!= "EDIT_MESH" or len(context
.active_object
.vertex_groups
) == 0:
122 # retrieving ID property data
124 texts
= context
.active_object
.data
["show_vgroup_verts"]
125 weights
= context
.active_object
.data
["show_vgroup_weights"]
131 bm
= bmesh
.from_edit_mesh(context
.active_object
.data
)
133 if bm
.select_mode
== {'VERT'} and bm
.select_history
.active
is not None:
134 active_vert
= bm
.select_history
.active
140 blf
.enable(0, blf
.SHADOW
)
141 blf
.shadow(0, 3, 0.0, 0.0, 0.0, 1.0)
142 blf
.shadow_offset(0, 2, -2)
143 for i
in range(0, len(texts
), 7):
144 bgl
.glColor3f(texts
[i
], texts
[i
+1], texts
[i
+2])
145 blf
.position(0, texts
[i
+4], texts
[i
+5], texts
[i
+6])
146 blf
.draw(0, "Vertex " + str(int(texts
[i
+3])) + ":")
149 for j
in range(0, len(weights
), 3):
150 if int(weights
[j
]) == int(texts
[i
+3]):
152 blf
.position(0, texts
[i
+4] + 10, font_y
, texts
[i
+6])
153 for group
in context
.active_object
.vertex_groups
:
154 if group
.index
== int(weights
[j
+1]):
155 group_name
= group
.name
157 blf
.draw(0, group_name
+ ": %.3f" % weights
[j
+2])
160 blf
.position(0, texts
[i
+4] + 10, font_y
, texts
[i
+6])
161 blf
.draw(0, "No Groups")
164 blf
.disable(0, blf
.SHADOW
)
168 class ShowVGroupWeights(bpy
.types
.Operator
):
169 bl_idname
= "view3d.show_vgroup_weights"
170 bl_label
= "Show Vertex Group Weights"
171 bl_description
= "Toggle the display of the vertex groups and weights for selected vertices"
177 def handle_add(self
, context
):
178 ShowVGroupWeights
._handle
_calc
= bpy
.types
.SpaceView3D
.draw_handler_add(
179 calc_callback
, (self
, context
), 'WINDOW', 'POST_VIEW')
180 ShowVGroupWeights
._handle
_draw
= bpy
.types
.SpaceView3D
.draw_handler_add(
181 draw_callback
, (self
, context
), 'WINDOW', 'POST_PIXEL')
185 if ShowVGroupWeights
._handle
_calc
is not None:
186 bpy
.types
.SpaceView3D
.draw_handler_remove(ShowVGroupWeights
._handle
_calc
, 'WINDOW')
187 if ShowVGroupWeights
._handle
_draw
is not None:
188 bpy
.types
.SpaceView3D
.draw_handler_remove(ShowVGroupWeights
._handle
_draw
, 'WINDOW')
189 ShowVGroupWeights
._handle
_calc
= None
190 ShowVGroupWeights
._handle
_draw
= None
193 def poll(cls
, context
):
194 return context
.mode
== 'EDIT_MESH'
196 def execute(self
, context
):
197 if context
.area
.type == 'VIEW_3D':
198 if not context
.scene
.show_vgroups_weights
:
199 # operator is called and not running, start everything
200 ShowVGroupWeights
.handle_add(self
, context
)
201 context
.scene
.show_vgroups_weights
= True
203 # operator is called again, stop displaying
204 ShowVGroupWeights
.handle_remove()
205 context
.scene
.show_vgroups_weights
= False
206 clear_properties(full
=False)
207 context
.area
.tag_redraw()
210 self
.report({'WARNING'}, "View3D not found, can't run operator")
213 class VGroupsWeights(bpy
.types
.PropertyGroup
):
214 vgroup
: bpy
.props
.IntProperty()
215 weight
: bpy
.props
.FloatProperty(min=0.0, max=1.0)
217 class AssignVertexWeight(bpy
.types
.Operator
):
218 bl_idname
= "mesh.vertex_group_assign"
219 bl_label
= "Assign Weights"
220 bl_description
= "Assign weights for all of the groups on a specific vertex"
222 index
: bpy
.props
.IntProperty()
224 vgroup_weights
: bpy
.props
.CollectionProperty(
225 description
="Vertex Group Weights",
229 def poll(cls
, context
):
230 return context
.mode
== 'EDIT_MESH'
232 def execute(self
, context
):
233 me
= context
.active_object
.data
234 bm
= bmesh
.from_edit_mesh(me
)
235 dvert_lay
= bm
.verts
.layers
.deform
.active
237 for item
in self
.vgroup_weights
:
238 weights
[item
.vgroup
] = item
.weight
241 if v
.index
== self
.index
:
243 for vgroup
in dvert
.keys():
244 dvert
[vgroup
] = weights
[vgroup
]
246 context
.area
.tag_redraw()
250 class RemoveFromVertexGroup(bpy
.types
.Operator
):
251 bl_idname
= "mesh.vertex_group_remove"
252 bl_label
= "Remove Vertex from Group"
253 bl_description
= "Remove a specific vertex from a specific vertex group"
255 #XXX abusing vector props here a bit; the first element is the vert index and the second is the group index
256 vert_and_group
: bpy
.props
.IntVectorProperty(name
= "Vertex and Group to remove", size
= 2)
259 def poll(cls
, context
):
260 return context
.mode
== 'EDIT_MESH'
262 def execute(self
, context
):
263 ob
= context
.active_object
265 bm
= bmesh
.from_edit_mesh(me
)
267 # Save current selection
271 selected_verts
.append(v
.index
)
272 if v
.index
!= self
.vert_and_group
[0]:
275 ob
.vertex_groups
.active_index
= self
.vert_and_group
[1]
276 bpy
.ops
.object.vertex_group_remove_from()
280 if v
.index
in selected_verts
:
283 #XXX Hacky, but there's no other way to update the UI panels
284 bpy
.ops
.object.editmode_toggle()
285 bpy
.ops
.object.editmode_toggle()
289 class AddToVertexGroup(bpy
.types
.Operator
):
290 bl_idname
= "mesh.vertex_group_add"
291 bl_label
= "Add Vertex to Group"
292 bl_description
= "Add a specific vertex to a specific vertex group"
294 def avail_vgroups(self
, context
):
297 ob
= context
.active_object
298 bm
= bmesh
.from_edit_mesh(ob
.data
)
299 dvert_lay
= bm
.verts
.layers
.deform
.active
301 self
.vertex
= bm
.select_history
.active
.index
303 dvert
= bm
.select_history
.active
[dvert_lay
]
305 #XXX since we need an identifier here, user won't be able to add a vgroup with that name ('-1')
306 #XXX could check against vgroup names and find an unused name, but it's a rare case after all.
307 items
.append(("-1", "New Vertex Group", "Add a new vertex group to the active object", -1))
309 for i
in ob
.vertex_groups
:
310 if i
.index
not in dvert
.keys():
311 items
.append((i
.name
, i
.name
, str(i
.index
), i
.index
))
315 vertex
: bpy
.props
.IntProperty()
316 available_vgroups
: bpy
.props
.EnumProperty(items
=avail_vgroups
, name
="Available Groups")
319 def poll(cls
, context
):
320 return context
.mode
== 'EDIT_MESH'
322 def execute(self
, context
):
323 ob
= context
.active_object
325 bm
= bmesh
.from_edit_mesh(me
)
326 #print(self.available_vgroups)
328 # Save current selection
332 selected_verts
.append(v
.index
)
333 if v
.index
!= self
.vertex
:
336 weight
= context
.tool_settings
.vertex_group_weight
337 context
.tool_settings
.vertex_group_weight
= 1.0
338 if self
.available_vgroups
== "-1":
339 bpy
.ops
.object.vertex_group_assign(new
=True) #XXX Assumes self.vertex is the active vertex
341 bpy
.ops
.object.vertex_group_set_active(group
= self
.available_vgroups
)
342 bpy
.ops
.object.vertex_group_assign() #XXX Assumes self.vertex is the active vertex
343 context
.tool_settings
.vertex_group_weight
= weight
347 if v
.index
in selected_verts
:
350 #XXX Hacky, but there's no other way to update the UI panels
351 bpy
.ops
.object.editmode_toggle()
352 bpy
.ops
.object.editmode_toggle()
356 class MESH_PT_ShowWeights(bpy
.types
.Panel
):
357 bl_label
= "Show Weights"
358 bl_space_type
= "VIEW_3D"
359 bl_region_type
= "UI"
361 bl_options
= {'DEFAULT_CLOSED'}
364 def poll(cls
, context
):
365 return context
.mode
== 'EDIT_MESH'
367 def draw(self
, context
):
369 ob
= context
.active_object
371 bm
= bmesh
.from_edit_mesh(me
)
372 dvert_lay
= bm
.verts
.layers
.deform
.active
374 row
= layout
.row(align
=True)
376 # text = "Show" if not context.scene.show_vgroups_weights else "Hide"
377 # row.operator(ShowVGroupWeights.bl_idname, text=text)
378 # row.prop(context.scene, "show_vgroups_weights_limit")
380 if len(ob
.vertex_groups
) > 0:
382 active_vert
= bm
.select_history
.active
384 col
= sub
.column(align
= True)
385 if bm
.select_mode
== {'VERT'} and active_vert
is not None:
386 col
.label(text
= "Active Vertex")
388 row
.label(text
= "Vertex " + str(active_vert
.index
) + ":")
389 row
.operator_menu_enum("mesh.vertex_group_add", "available_vgroups", text
= "Add Group", icon
= 'GROUP_VERTEX')
393 for i
in me
.vertices
:
394 if i
.index
== active_vert
.index
:
395 vgroup_weights_index
= i
.index
396 for j
in range(len(i
.groups
)):
397 for k
in ob
.vertex_groups
:
398 if k
.index
== i
.groups
[j
].group
:
400 split
= col
.split(factor
= 0.90, align
= True)
401 vgroup_weights
.append((k
.index
, i
.groups
[j
].weight
))
402 row
= split
.row(align
= True)
403 row
.prop(i
.groups
[j
], "weight", text
= k
.name
, slider
= True, emboss
= not k
.lock_weight
)
404 row
= split
.row(align
= True)
405 row
.operator("mesh.vertex_group_remove", text
="", icon
='X').vert_and_group
= (i
.index
, k
.index
)
408 col
.label(text
= " No Groups")
410 props
= col
.operator("mesh.vertex_group_assign")
411 props
.index
= vgroup_weights_index
413 for vgroup
, weight
in vgroup_weights
:
414 item
= props
.vgroup_weights
.add()
420 col
.label(text
= "No Active Vertex")
421 layout
.prop(context
.window_manager
, "show_vgroups_show_all", toggle
= True)
422 # All selected vertices (except for the active vertex)
423 if context
.window_manager
.show_vgroups_show_all
:
426 if active_vert
is not None and v
.index
== active_vert
.index
:
429 col
= sub
.column(align
= True)
430 col
.label(text
= "Vertex " + str(v
.index
) + ":")
433 for i
in me
.vertices
:
434 if i
.index
== v
.index
:
435 vgroup_weights_index
= i
.index
436 for j
in range(len(i
.groups
)):
437 for k
in ob
.vertex_groups
:
438 if k
.index
== i
.groups
[j
].group
:
440 split
= col
.split(factor
= 0.90, align
= True)
441 vgroup_weights
.append((k
.index
, i
.groups
[j
].weight
))
442 row
= split
.row(align
= True)
443 row
.prop(i
.groups
[j
], "weight", text
= k
.name
, slider
= True, emboss
= not k
.lock_weight
)
444 row
= split
.row(align
= True)
445 row
.operator("mesh.vertex_group_remove", text
="", icon
='X').vert_and_group
= (i
.index
, k
.index
)
447 col
.label(text
= " No Groups")
449 props
= col
.operator("mesh.vertex_group_assign")
450 props
.index
= vgroup_weights_index
452 for vgroup
, weight
in vgroup_weights
:
453 item
= props
.vgroup_weights
.add()
458 layout
.label(text
= "No Groups")
461 def create_properties():
462 bpy
.types
.WindowManager
.show_vgroups_show_all
= bpy
.props
.BoolProperty(
463 name
= "Show All Selected Vertices",
464 description
= "Show all vertices with vertex groups assigned to them",
467 bpy
.types
.Mesh
.assign_vgroup
= bpy
.props
.StringProperty()
469 bpy
.types
.Scene
.show_vgroups_weights
= bpy
.props
.BoolProperty(
470 name
="Show Vertex Groups/Weights",
473 bpy
.types
.Scene
.show_vgroups_weights_limit
= bpy
.props
.IntProperty(
475 description
="Maximum number of weight overlays to draw",
481 # removal of ID-properties when script is disabled
482 def clear_properties(full
=True):
484 if bpy
.context
.active_object
is not None:
485 me
= bpy
.context
.active_object
.data
487 if hasattr(me
, "show_vgroup_verts"):
488 del me
["show_vgroup_verts"]
489 if hasattr(me
, "show_vgroup_weights"):
490 del me
["show_vgroup_weights"]
493 del bpy
.types
.WindowManager
.show_vgroups_show_all
494 del bpy
.types
.Mesh
.assign_vgroup
495 del bpy
.types
.Scene
.show_vgroups_weights
496 del bpy
.types
.Scene
.show_vgroups_weights_limit
503 RemoveFromVertexGroup
,
510 bpy
.utils
.register_class(cls
)
515 ShowVGroupWeights
.handle_remove()
519 bpy
.utils
.unregister_class(cls
)
521 if __name__
== "__main__":