3DS Max style weight tables

So I’m looking for an add-on for 3DS Max style weight tables. They look like this if you’re not familiar;

So you get basically an Excel type spread sheet, where you can see every vertex, every bone, and the weight assigned to both…or if there is no weight.

If such an add on doesn’t exist, I’ll need some guidance on how to make an add on for it. I have some light C# knowledge, and I did create one add-on for myself, granted it’s a really simple add on that basically flips triangles around.

I don’t necessarily need to edit weights from the table, I just want to see them, and preferably be able to add some sorting functionality to the table.

Why do I want to do this? I’m trying to solve a problem. I need to find any vertices in my model that have more than 4 bones skinned to them, as upon export to a game engine, if there are any, it breaks the rig. So it’s a diagnostic tool to help me find any over skinned areas without just running around and clicking on each vertex

2 Likes

This simple script, when you run it from Blender’s text editor, will select all vertices that belong to more than the specified number of groups.

import bpy
import bmesh

def do_stuff(context, max_groups):
    if context.mode != 'EDIT_MESH':
        return
    
    bpy.ops.mesh.select_all(action='DESELECT')
    context.tool_settings.mesh_select_mode = ((True, False, False))

    obj = context.object
    mesh = obj.data
    bm = bmesh.from_edit_mesh(mesh)

    layer = bm.verts.layers.deform.active

    for vert in bm.verts:
        dvert = vert[layer]
        count = sum(i in dvert for i in range(len(obj.vertex_groups)))
        if count > max_groups:
            vert.select = True
            
    bm.select_flush_mode()
            
do_stuff(bpy.context, 4)
2 Likes

OMG freaking awesome. That works perfectly, thanks.

You’re welcome :slight_smile:

I hate to bug you for more, but maybe you can help me stitch some code together? I’d like to make this an add-on so I can access it from the right click context menu. I have this code from a script I made earlier that accomplishes that.

bl_info = {
    "name" : "ReTriangulate",
    "author" : "Hard Rooster Labs",
    "blender" : (2, 80, 0),
    "category" : "Editing"
    }

import bpy

class ReTriangulateOperator(bpy.types.Operator):
    bl_idname = "wm.retriangulate"
    bl_label = "ReTriangulate Operator"


    def execute(self, context):
        bpy.ops.mesh.tris_convert_to_quads()
        bpy.ops.mesh.quads_convert_to_tris(quad_method='FIXED_ALTERNATE', ngon_method='BEAUTY')
        return {"FINISHED"}

def draw_menu(self, context):
    layout = self.layout
    layout.separator()
    layout.operator("wm.retriangulate", text="ReTriangulate")

def register():
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.append(draw_menu)
    
    bpy.utils.register_class(ReTriangulateOperator)

def unregister():
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.remove(draw_menu)
    
    bpy.utils.unregister_class(ReTriangulateOperator)

I’ve tried weaving that script you provided into that, but the way I’ve done it causes errors when trying to install it

bl_info = {
    "name" : "OverWeight",
    "author" : "Hard Rooster Labs",
    "blender" : (2, 80, 0),
    "category" : "Editing"
    }

import bpy
import bmesh

class OverWeightOperator(bpy.types.Operator):
    bl_idname = "wm.overweight"
    bl_label = "OverWeight Operator"

    def do_stuff(context, max_groups):
        if context.mode != 'EDIT_MESH':
            return
        
        bpy.ops.mesh.select_all(action='DESELECT')
        context.tool_settings.mesh_select_mode = ((True, False, False))

        obj = context.object
        mesh = obj.data
        bm = bmesh.from_edit_mesh(mesh)

        layer = bm.verts.layers.deform.active

        for vert in bm.verts:
            dvert = vert[layer]
            count = sum(i in dvert for i in range(len(obj.vertex_groups)))
            if count > max_groups:
                vert.select = True
                
        bm.select_flush_mode()
                
    do_stuff(bpy.context, 4)

def draw_menu(self, context):
    layout = self.layout
    layout.separator()
    layout.operator("wm.overweight", text="OverWeight")

def register():
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.append(draw_menu)
    
    bpy.utils.register_class(OverWeightOperator)

def unregister():
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.remove(draw_menu)
    
    bpy.utils.unregister_class(OverWeightOperator)

That would be:

bl_info = {
    "name" : "OverWeight",
    "author" : "Hard Rooster Labs",
    "blender" : (2, 80, 0),
    "category" : "Editing"
    }

import bpy
import bmesh

class OverWeightOperator(bpy.types.Operator):
    """Selects all vertices belonging to more than specified number of vertex groups"""
    bl_idname = "wm.overweight"
    bl_label = "OverWeight Operator"
    bl_options = {'REGISTER', 'UNDO'}

    max_groups: bpy.props.IntProperty(
        name = "Max Groups",
        description = "Maximum number of vertex groups",
        min = 0,
        default = 4)

    @classmethod
    def poll(cls, context):
        return context.mode == 'EDIT_MESH'

    def execute(self, context):
        
        bpy.ops.mesh.select_all(action='DESELECT')
        context.tool_settings.mesh_select_mode = ((True, False, False))

        obj = context.object
        mesh = obj.data
        bm = bmesh.from_edit_mesh(mesh)

        layer = bm.verts.layers.deform.active
        total_groups = len(obj.vertex_groups)

        for vert in bm.verts:
            dvert = vert[layer]
            count = sum(i in dvert for i in range(total_groups))
            if count > self.max_groups:
                vert.select = True
                
        bm.select_flush_mode()
        return {'FINISHED'}

def draw_menu(self, context):
    layout = self.layout
    layout.separator()
    layout.operator("wm.overweight", text="OverWeight")

def register():
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.append(draw_menu)
    
    bpy.utils.register_class(OverWeightOperator)

def unregister():
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.remove(draw_menu)
    
    bpy.utils.unregister_class(OverWeightOperator)

With max groups becoming an operator property which you can change after running in the operator panel.

And that would be $100 :wink:
(joking, joking, silly disclaimer, but Internet is mad today)

1 Like

Thank You again…this might actually be worth $100 to me

There is also this addon i saw last week here ;

There is also a paid one but if it work as shown it’s well worth the 6$ the developer ask for ;

2 Likes

Small performance improvement (I should never code late at night):

Find this section of code:

        layer = bm.verts.layers.deform.active
        total_groups = len(obj.vertex_groups)

        for vert in bm.verts:
            dvert = vert[layer]
            count = sum(i in dvert for i in range(total_groups))
            if count > self.max_groups:
                vert.select = True

and replace with this:

        layer = bm.verts.layers.deform.active

        for vert in bm.verts:
            dvert = vert[layer]
            if len(dvert) > self.max_groups:
                vert.select = True

Not sure WTH I was thinking…

1 Like