I had some hours to kill and time to think about cool little problem

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('.')[0]

	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()

4 thoughts on “I had some hours to kill and time to think about cool little problem

  1. Hi Niels,
    You think your code could be easily tricked to copy the weights from a selection of vertices from the outer side of a jacket to another selection of closest vertices on the inner side of the same jacket (same mesh)? I can’t find any solution for this problem on the web 😐 and I don’t have coding skill yet.

    Thanks

    • Hey man,

      Yeah normally that should work, you can give it a shot and see what happens. If you’re having any troubles, I’d be more than happy to take a look at your scene and see how it can be fixed.

  2. Hi there !

    I have been looking around on net for this problem, I am self taught about maya as little i have learned, I have been trying to copy weights from a single sided geometry ( no inside faces ) to a double sided geometry ( made by duplicating and flipping the faces inside) I Mirror skin weights on both but there are a few vertices that get left behind. Would this tool of your be helpful in that?. Also i do see a lot of mel scripts online but i have no idea how to use them and or this tool of your, Any help would be appreciated TY .

    • Man, I really have to get some notifications when people react to a post 🙂

      I have since worked this out a bit more and have made an actual script, with UI and everything :), to do just that. A lot of characters I’m working on at the moment have capes or hoods where obviously the inside needs to follow the outside. I’ll take a look at cleaning it up and putting it online.

Comments are closed.