0
$\begingroup$

I have imported a pre-existing, fully-rigged model into Blender 2.79b. Unfortunately, the source model contains some bones that Blender doesn't appear to know how to import correctly, and thus drops them entirely. (I can recreate the bones correctly afterward, that part's not a big deal.) As a result, about 10%-15% of the mesh contains vertices with missing vertex weights. (I can't control the original model, I'm stuck with how it is.)

However, I know that all vertices must have exactly 1 or 2 vertex groups, and their weights are correctly set to sum to 1.0. I also know that the missing groups do not overlap. Therefore, I could go through every vertex near the bone in question and check its groups:

  • If the vertex has no groups, it must have a weight of 1.0 in a missing group.
  • If the vertex has 1 group that's less than 1.0, it must belong in a missing group with a corresponding weight (e.g. if it's already in "shoulder_R" with weight 0.35, then it must also be in "arm_R" with weight 0.65).

I could do this manually, but that's a lot of work with lots of potential for messing up.

Is there a way to automatically assign a bunch of vertices to a vertex group that "fills in" their total weights to 1.0?

I tried a few things with weight painting but I didn't find anything that seemed to be what I was looking for. I'm not averse to scripting but have no experience with it.

$\endgroup$

1 Answer 1

1
$\begingroup$

I managed to put together an add-on script that does what I want. It takes some tabbing between object/edit mode to "bake in" what vertices you want it to operate on (didn't look too deeply into it), and you can only access it through the spacebar menu, but that's fine by me.

bl_info = {
    "name":"Fill In Missing Weights",
    "category":"Object",
}

import bpy
from decimal import *

class FillInWeights(bpy.types.Operator):
    """Fill In Missing Weights"""
    bl_idname = "object.fill_in_weights"
    bl_label = "Fill In Missing Weights"
    bl_options = {"REGISTER","UNDO"}

    missing_group_name = bpy.props.StringProperty(
        name="Missing group name",
        description="Name of the group to fill in.",
        default="",
    )
    print_debug_info = bpy.props.BoolProperty(
        name="Print debug info",
        description="Output specific operations to console.",
        default=False,
    )

    def execute(self,context):
        obj = context.active_object
        if not obj.vertex_groups:
            self.report({"ERROR"},'Object has no vertex groups')
            return {"CANCELLED"}
        missing_group = obj.vertex_groups.get(self.missing_group_name)
        if not missing_group: # group must exist already
            self.report({"ERROR"},'Vertex group "'+self.missing_group_name+'" does not exist')
            return {"CANCELLED"}
        if self.print_debug_info:
            print('Begin weight-fill operation on group "'+self.missing_group_name+'"')
        bpy.ops.object.mode_set(mode="OBJECT")
        for v in obj.data.vertices:
            if not v.select: continue
            if len(v.groups) == 0:
                missing_group.add([v.index],1.0,"REPLACE")
                print("Added group-less vertex to group.")
            elif len(v.groups) == 1:
                g = v.groups[0]
                g_index,g_weight = g.group,Decimal(g.weight).quantize(Decimal("0.001")) # all known cases are 0.01, but just to be safe
                if g_weight >= 1.0: continue
                group = obj.vertex_groups[g_index]
                target_weight = Decimal("1.00")-g_weight
                missing_group.add([v.index],target_weight,"REPLACE")
                print('Vertex in group '+group.name+' with weight '+str(g_weight)+': adding to group '+self.missing_group_name+' with weight '+str(target_weight))
            # len(v.groups) >= 2: continue
        bpy.ops.object.mode_set(mode="EDIT")
        if self.print_debug_info:
            print("Weight-fill complete.")
        return {"FINISHED"}

    def invoke(self,context,event):
        return context.window_manager.invoke_props_dialog(self)

def register():
    bpy.utils.register_class(FillInWeights)
def unregister():
    bpy.utils.unregister_class(FillInWeights)

if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .