The idea: a Python script that looks at the left side, then looks at the right side group with same root name but ending with ".R", then process all vertices and copy the weights to the vertex closest to the ideal mirrored position.
My attempt: a somewhat ugly loop that assumes the model axis of symmetry is at [0, ?, ?] and then just multiply x by -1. This is assumed to be the ideal mirrored position for that vertex. Then iterate all vertices in the group ending with ".R" and apply weights to the one closest to the ideal position.
The problem: when I finally try to get the weight using vertex_groups[GROUP_NAME].weight(VERTEX_INDEX)
I get RuntimeError: Error: Vertex not in group
This should not happen, as VERTEX_INDEX is taken from an array collected from a selection of the source group. I know the group is not externally changed while the script is running. There is no way a wrong VERTEX_INDEX is passed to the weight() function.
I already checked for bad indentation, that may results in statements running outside the intended blocks of code.
More info: before the expected "apply your weights before applying the deformation to the right side of the model". I'm not the one doing the model and I cannot edit it in a destructive way. The mesh is already provided, I have to weight it. I know that the numbers of vertices in both sides are the same, it's just that some parts of the model aren't perfectly symmetrical (like the artist did everything without the mirror modifier).
The script, a lot of hardcoded stuff, look for # conflictive line
:
import bpy
import mathutils
# ensure we are in edit mode
bpy.ops.object.mode_set(mode="EDIT")
# configurable parameters
# We take weights from vertices in groups ending with src_group_part
# and transfer them to vertices in the group with the same root name
# but ending with dst_group_part.
# The closest possible vertex will be the one receiving the weights
src_group_part = ".L"
dst_group_part = ".R"
obj = bpy.context.edit_object
mesh = obj.data
#bpy.ops.object.mode_set(mode="OBJECT")
print("Working")
for v_group in obj.vertex_groups:
# TODO: .L may not be at end of string, write more robust code
is_src = v_group.name.find(src_group_part)
if is_src == -1:
continue
mirrored_name = v_group.name.split(src_group_part)[0] + dst_group_part
if not mirrored_name in obj.vertex_groups:
continue
target_group = obj.vertex_groups[mirrored_name]
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.edit_object.vertex_groups.active_index = v_group.index
bpy.ops.object.vertex_group_select()
src_sel_verts_ig = [[v.index, [g.group for g in v.groups], v.co] for v in mesh.vertices if v.select]
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.edit_object.vertex_groups.active_index = target_group.index
bpy.ops.object.vertex_group_select()
target_sel_verts_ig = [[v.index, [g.group for g in v.groups], v.co] for v in mesh.vertices if v.select]
for src_v in src_sel_verts_ig:
min_dist_v_index = -1
min_dist = 1000000
for dst_v in target_sel_verts_ig:
mirrored_v = dst_v[2].copy()
mirrored_v.x *= -1
dist = (mirrored_v - dst_v[2]).length
if min_dist > dist:
min_dist_v_index = dst_v[0]
min_dist = dist
if min_dist_v_index > -1:
new_weight = obj.vertex_groups[v_group.name].weight(src_v[0]) # conflictive line
obj.vertex_groups[mirrored_name].add([min_dist_v_index], new_weight, 'REPLACE')
print("Finished")
Any alternative to get this script to work is also appreciated. Like, has Blender a tool for these cases?