import bpy
import bmesh
from mathutils import Vector
def get_loose_parts(obj):
me = obj.data
bm = bmesh.new()
bm.from_mesh(me)
bm.verts.ensure_lookup_table()
islands = []
visited = set()
for v in bm.verts:
if v.index in visited:
continue
stack = [v]
island = set()
while stack:
cur = stack.pop()
if cur.index in visited:
continue
visited.add(cur.index)
island.add(cur.index)
for e in cur.link_edges:
other = e.other_vert(cur)
if other.index not in visited:
stack.append(other)
islands.append(list(island))
bm.free()
return islands
def island_centroid(mesh, idxs):
c = Vector((0,0,0))
for i in idxs:
c += mesh.vertices[i].co
return c / len(idxs)
def assign_one_to_one(mesh_obj, arm_obj, islands):
mesh = mesh_obj.data
bones = arm_obj.data.bones
# ensure vertex groups exist
for b in bones:
if b.name not in mesh_obj.vertex_groups:
mesh_obj.vertex_groups.new(name=b.name)
# clear all weights
all_vids = list(range(len(mesh.vertices)))
for vg in mesh_obj.vertex_groups:
vg.remove(all_vids)
# compute world positions
bone_pos = {
b.name: arm_obj.matrix_world * b.head_local
for b in bones
}
island_pos = {
i: mesh_obj.matrix_world * island_centroid(mesh, verts)
for i, verts in enumerate(islands)
}
# build and sort (island, bone, distance)
pairs = []
for i, ipos in island_pos.items():
for bname, bpos in bone_pos.items():
dist = (ipos - bpos).length
pairs.append((dist, i, bname))
pairs.sort(key=lambda x: x[0])
assigned_islands = set()
assigned_bones = set()
matches = {}
for dist, i, bname in pairs:
if i in assigned_islands or bname in assigned_bones:
continue
matches[i] = bname
assigned_islands.add(i)
assigned_bones.add(bname)
# stop once all islands or all bones are assigned
if len(assigned_islands) == len(islands) or len(assigned_bones) == len(bones):
break
# do the assignments
for i, bname in matches.items():
verts = islands[i]
mesh_obj.vertex_groups[bname].add(verts, 1.0, 'REPLACE')
print("Assigned %d islands to %d bones." % (len(matches), len(matches)))
def main():
sel = bpy.context.selected_objects
if len(sel) != 2:
print("Select exactly two objects: one mesh, one armature.")
return
mesh_obj = next((o for o in sel if o.type=='MESH'), None)
arm_obj = next((o for o in sel if o.type=='ARMATURE'), None)
if not mesh_obj or not arm_obj:
print("Selection must be one mesh and one armature.")
return
islands = get_loose_parts(mesh_obj)
if not islands:
print("No loose geometry found.")
return
assign_one_to_one(mesh_obj, arm_obj, islands)
main()
Does as the title specified. allows you to select the armature and the mesh and parent by loose geometry groups / geometry islands in blender 2.78a/2.79.