0
$\begingroup$

Add a bone at each vertex location?

Using the above, I can place a bone at each vertex, however, it doesn't connect the bone to that vertex.

How can I edit the code in the solution to make it so each bone is connected to (only) the vertex it's placed at (with weight 1) ?

If I try to connect the bone-rig and mesh with automatic weights, it leads to un-evenness in the weight distribution.

Specifics: I have a 100 or so bones and want to automate rather than manually connect each bone to it's respective vertex on the mesh.

$\endgroup$
0
$\begingroup$

You can complete the code from the indicated answer by the following:

Keep bone names as they are created.

Loop over these names to create vertex groups and assign the corresponding vertex.

Parent the object to the rig and add the armature modifier.

names = [] #Will keep bone names
bpy.ops.object.editmode_toggle()
for i, l in enumerate(zip(points, normals)):
    bone = amt.edit_bones.new(str(i))
    bone.head = l[0]
    bone.tail = l[1]
    names.append(bone.name) #Add name
bpy.ops.object.editmode_toggle()

for v_index, name in enumerate(names):
    #Get the group
    group = obj.vertex_groups.new(name=name)
    #Link the vertex to it
    group.add([v_index], 1, 'REPLACE')

#Parent and add modifier
obj.parent = rig
modifier = obj.modifiers.new(rig.name, "ARMATURE")
modifier.object = rig

The complete code

import bpy
from mathutils import Vector

def AddBonesAtVertices(obj, length, use_normals):
    if not obj or obj.type != 'MESH':
        return

    points = []
    normals = []
    data = []
    for v in obj.data.vertices:
        p = obj.matrix_world @ v.co
        target = v.normal @ obj.matrix_world
        dir = target - p
        dir.normalize()
        dir = dir * length
        n = p + dir * (-1)
        points.append(p)
        if not use_normals:
            n = Vector((p[0], p[1], p[2] + length))
        normals.append(n)
        data.append([p, n])

    bpy.ops.object.mode_set(mode='OBJECT')
    
    amt = bpy.data.armatures.new(obj.name + "_vBones")
    rig = bpy.data.objects.new(obj.name + '_vRig', amt)
    
    bpy.context.collection.objects.link(rig)
    objects.active = rig
    
    names = [] #Will keep bone names
    bpy.ops.object.editmode_toggle()
    for i, l in enumerate(zip(points, normals)):
        bone = amt.edit_bones.new(str(i))
        bone.head = l[0]
        bone.tail = l[1]
        names.append(bone.name) #Add name
    bpy.ops.object.editmode_toggle()

    for v_index, name in enumerate(names):
        #Get the group
        group = obj.vertex_groups.new(name=name)
        #Link the vertex to it
        group.add([v_index], 1, 'REPLACE')

    #Parent and add modifier
    obj.parent = rig
    modifier = obj.modifiers.new(rig.name, "ARMATURE")
    modifier.object = rig
    

objects = bpy.context.view_layer.objects
obj = objects.active

AddBonesAtVertices(obj, 0.5, False)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.