Is there a possibility to overlay the weights of the active Vertex Group as numbers in the viewport? It would make for a much more precise readout than the color gradients, especially when fiddling with minor adjustments.
Here is the code that I was able to come up with hikariztw's help, the only thing missing is to make it work in Weight Paint Mode. It works in Edit mode, for now.
I was thinking of adding this to the Overlays Panel, since it is basically a variation on the Mesh Analysis Overlays, like Edge Length, Edge Angle or Face Area. Alas, it seems you cannot touch those, as they are written in C.
import bpy
import bgl
import blf
import mathutils
import bmesh
bl_info = {
'name': 'Index Visualiser (BMesh)',
'author': 'Bartius Crouch, CoDEmanX, hikariztw',
'version': (2, 6, 15),
'blender': (2, 80, 0),
'location': 'View3D > Properties panel > Mesh Display tab (edit-mode)',
'warning': '', # used for warning icon and text in addons panel
'description': 'Display the indices of vertices, edges and faces '
'in the 3d-view',
'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/'
'Scripts/3D_interaction/Index_Visualiser',
'tracker_url': 'http://projects.blender.org/tracker/index.php?'
'func=detail&aid=21557',
'category': '3D View'}
# calculate locations and store them as ID property in the mesh
def draw_callback_px(self, context):
# polling
if context.mode != "EDIT_MESH" and context.mode != "PAINT_WEIGHT":
return
# get screen information
region = context.region
mid_x = region.width / 2
mid_y = region.height / 2
width = region.width
height = region.height
# get matrices
obj = context.active_object
view_mat = context.space_data.region_3d.perspective_matrix
ob_mat = obj.matrix_world
total_mat = view_mat @ ob_mat
blf.size(0, 13, 72)
def draw_index(r, g, b, index, center):
vec = total_mat @ center # order is important
# dehomogenise
vec = mathutils.Vector(
(vec[0] / vec[3], vec[1] / vec[3], vec[2] / vec[3]))
x = int(mid_x + vec[0] * width / 2)
y = int(mid_y + vec[1] * height / 2)
# bgl.glColorMask(1,1,1,1)
blf.position(0, x, y, 0)
if isinstance(index, float):
blf.draw(0, '{:.2f}'.format(index))
else:
blf.draw(0, str(index))
scene = context.scene
me = obj.data
if context.mode == "EDIT_MESH":
bm = bmesh.from_edit_mesh(me)
else:
bm = type('null', (), {'verts': [], 'edges': [], 'faces': []})
if scene.live_mode:
me.update()
if scene.display_vert_index:
for v in bm.verts:
if not v.hide and \
(v.select or not scene.display_sel_only):
# CoDEmanx: bm.verts.index_update()?
draw_index(1.0, 1.0, 1.0, v.index, v.co.to_4d())
if scene.display_weight:
vg = context.object.vertex_groups[0]
for v in bm.verts:
try:
weight = vg.weight(v.index)
draw_index(1.0, 1.0, 1.0, weight, v.co.to_4d())
except Exception as e:
continue
if scene.display_edge_index:
for e in bm.edges:
if not e.hide and \
(e.select or not scene.display_sel_only):
v1 = e.verts[0].co
v2 = e.verts[1].co
loc = v1 + ((v2 - v1) / 2)
draw_index(1.0, 1.0, 0.0, e.index, loc.to_4d())
if scene.display_face_index:
for f in bm.faces:
if not f.hide and \
(f.select or not scene.display_sel_only):
draw_index(1.0, 0.0, 0.5, f.index,
f.calc_center_median().to_4d())
# operator
class IndexVisualiser(bpy.types.Operator):
bl_idname = "view3d.index_visualiser"
bl_label = "Index Visualiser"
bl_description = "Toggle the visualisation of indices"
_handle = None
@classmethod
def poll(cls, context):
return context.mode == "EDIT_MESH" or context.mode == "PAINT_WEIGHT"
def modal(self, context, event):
if context.area:
context.area.tag_redraw()
# removal of callbacks when operator is called again
if context.scene.display_indices == -1:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
context.scene.display_indices = 0
return {"CANCELLED"}
return {"PASS_THROUGH"}
def invoke(self, context, event):
if context.area.type == "VIEW_3D":
if context.scene.display_indices < 1:
# operator is called for the first time, start everything
context.scene.display_indices = 1
self._handle = bpy.types.SpaceView3D.draw_handler_add(
draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self)
return {"RUNNING_MODAL"}
else:
# operator is called again, stop displaying
context.scene.display_indices = -1
return {'RUNNING_MODAL'}
else:
self.report({"WARNING"}, "View3D not found, can't run operator")
return {"CANCELLED"}
# defining the panel
def menu_func(self, context):
self.layout.separator()
col = self.layout.column(align=True)
col.operator(IndexVisualiser.bl_idname, text="Visualize indices")
row = col.row(align=True)
row.active = ((context.mode == "EDIT_MESH" or context.mode == "PAINT_WEIGHT") and
context.scene.display_indices == 1)
row.prop(context.scene, "display_vert_index", toggle=True)
row.prop(context.scene, "display_weight", toggle=True)
row.prop(context.scene, "display_edge_index", toggle=True)
row.prop(context.scene, "display_face_index", toggle=True)
row = col.row(align=True)
row.active = context.mode == "EDIT_MESH" and \
context.scene.display_indices == 1
row.prop(context.scene, "display_sel_only")
# row.prop(context.scene, "live_mode")
def register_properties():
bpy.types.Scene.display_indices = bpy.props.IntProperty(
name="Display indices",
default=0)
# context.scene.display_indices = 0
bpy.types.Scene.display_sel_only = bpy.props.BoolProperty(
name="Selected only",
description="Only display indices of selected vertices/edges/faces",
default=True)
bpy.types.Scene.display_vert_index = bpy.props.BoolProperty(
name="Vertices",
description="Display vertex indices", default=False)
bpy.types.Scene.display_weight = bpy.props.BoolProperty(
name="Weight",
description="Display vertex weight", default=False)
bpy.types.Scene.display_edge_index = bpy.props.BoolProperty(
name="Edges",
description="Display edge indices")
bpy.types.Scene.display_face_index = bpy.props.BoolProperty(
name="Faces",
description="Display face indices")
bpy.types.Scene.live_mode = bpy.props.BoolProperty(
name="Live",
description="Toggle live update of the selection, can be slow",
default=False)
def unregister_properties():
del bpy.types.Scene.display_indices
del bpy.types.Scene.display_sel_only
del bpy.types.Scene.display_vert_index
del bpy.types.Scene.display_weight
del bpy.types.Scene.display_edge_index
del bpy.types.Scene.display_face_index
del bpy.types.Scene.live_mode
def register():
register_properties()
bpy.utils.register_class(IndexVisualiser)
bpy.types.VIEW3D_PT_view3d_properties.append(menu_func)
def unregister():
bpy.utils.unregister_class(IndexVisualiser)
unregister_properties()
bpy.types.VIEW3D_PT_view3d_properties.remove(menu_func)
if __name__ == "__main__":
register()