I recently encountered a very simple problem that took too many mouse clicks to solve. In a scenario where you have a character that needs to have its head separate from its body, it can be a pain in the ass to have the skinning on the edges of the two meshes to match up.
Manually copying the vertex weights from one mesh to the other is the preferred workflow. It means you can guarantee that there won’t be any holes or seams between the two pieces of geometry. Depending on how many edges your character has, you’re wasting valuable minutes selecting one mesh, going to vertex mode, selecting the vertex, copying the vertex weights, selecting the other mesh, going to vertex mode, selecting the other vertex and pasting the vertex weights.
So to get around that problem I wrote a little tool that checks the distance between the vertices on both meshes and copies the vertex weights over if two vertices are close enough. Think the Merge Vertex tools, but then with skin weights. It’s not really one of those things that’s absolutely necessary to have, but I had some time to kill and it seemed like a fun problem to solve.
Have a quick look at the video to see how it works and steal whatever bits of code you find interesting and/or useful. Oh, and the skinning on the model is just what Maya came up with. I didn’t feel like manually adjusting the weights for a small demonstration video 🙂
Grab the code here:
def copy_vertex_weights(): #TODO: make a ui for this so whoever's using it can set the minimum distance between verts that need # to share vertex weights # Any vertices closer together than this distance will share the same vertex weight copyDistance = 0.7 # Make sure that the selection is converted to vertices cmds.select(cmds.polyListComponentConversion(cmds.ls(selection = True, flatten = True), toVertex = True)) selection = cmds.ls(selection = True, flatten = True) # Two lists to store the vertices of both the head and the body edges first_object_vertices =  second_object_vertices =  for i in xrange(0, len(selection) / 2): vertex = Vertex(selection[i]) first_object_vertices.append(vertex) for i in xrange(len(selection) / 2, len(selection)): vertex = Vertex(selection[i]) second_object_vertices.append(vertex) # Iterate over both lists to find vertices that are close enough together to share weights for first_vertex in first_object_vertices: for second_vertex in second_object_vertices: if get_distance_between_vertices(first_vertex, second_vertex) < copyDistance: """ So there's a command called 'skinPercent' which allows you to get the influence value(s) of every joint of a vertex. The problem with that is there is absolutely no good or easy to get something like a key value pair to know how much influence a particular joint has on the vertex. The only way I could figure out was going over every joint and recording how much influence that specific joint has on the vertex. Then setting all those values for the same joints on the other object. At which point I said fuck it and just used the MEL commands that are echo'd out for Copy Vertex weight and Paste Vertex Weight. """ cmds.select(first_vertex.name) mel.eval('artAttrSkinWeightCopy;') cmds.select(second_vertex.name) mel.eval('artAttrSkinWeightPaste;') def get_distance_between_vertices(vertexA, vertexB): # distance between 2 points in 3D is # # squareRoot[ <(point1.x - point2.x)^2> + <(point1.y - point2.y)^2> + <(point1.z - point2.z)^2> ] # vertexAx, vertexAy, vertexAz = vertexA.get_world_position() vertexBx, vertexBy, vertexBz = vertexB.get_world_position() distance = ((vertexAx - vertexBx) ** 2 + (vertexAy - vertexBy) ** 2 + (vertexAz - vertexBz) ** 2) ** 0.5 return distance class Vertex: """ Tiny little class to treat the vertices as objects """ def __init__(self, name): self.name = name def get_object_name(self): return self.name.split('.') def get_world_position(self): return cmds.pointPosition(self.name, world = True) # def get_skin_cluster_name(self): # name = self.get_object_name() # #python doesn't have a findRelatedSkinCluster command weirdly enough... # return mel.eval('findRelatedSkinCluster '+name) # def get_skin_percentage(self): # skinCluster = self.get_skin_cluster_name() # return cmds.skinPercent(skinCluster, self.name, query = True, value = True) def print_debug_info(self): print "Name: " + self.name print "Object Name: " + self.get_object_name() print "World Position: " + str(self.get_world_position()) #print "Skin Cluster Name: " + self.get_skin_cluster_name() #print "Skin Percent: " + str(self.get_skin_percentage()) copy_vertex_weights()