[Maya / Python] Weighting Tools. Query vert influence and mesh

Hey hey!

I’m trying to create a weighting tool that lets you select 2 verts, hit a button, select another vert, hit a button and it will apply the averaged out influences from the first 2 verts to the last one. But Im running into some problems. Definitely the most advanced tool I’ve tried to write…

First off, I don’t know how to get the mesh data from a selected vert. I need it so I can soft code the skinCluster.

As far as I know, I have to query the cluster (bone of the influence) and the influence percentage seperately and stitch them together since there’s not command that will query all the data at once.

Here’s what Ive been workin on trying to figure it out.

import maya.cmds as cmds

selection = cmds.ls(sl=True, fl=True)

#goes through the verts selected
for sel in selection:
	
	i=0
	
	clusters = cmds.skinPercent('skinCluster1', q=1, t=None)
	influencePercentage = cmds.skinPercent('skinCluster1', q=True, v=True)
	
	#Goes through clusters
	for cluster in clusters:
		
		influence = influencePercentage[i]
		
		if influence > 0:
			infStr = str(influence)
			print cluster + '[' +infStr + ']'
		i=i+1

I have 3 bones in my scene right now (just testing). I have 1 vert that has influence from 2 of the 3 bones, and when I select the vert and run the script, it prints this out

bone1[0.750080004334]
bone2[0.249919995666]

The data is correct, and it doesnt show the unused influences. But the problem comes when I select 2 verts at a time. I get this error.

Error: RuntimeError: Query point weights only for a single point.

Even though I have it going through my selection in a for loop, which should do it one at a time, no?

So I guess I need help with 3 things.

-How to Query the mesh of a selected vert so that I can soft code the skinCluster, so I can get rid of ‘skinCluster1’. Currently my selection array returns something like this, [u’meshName.vtx[1]']. I need to pull the meshName out and into its own variable.

-Why won’t it work on 2 verts in my current setup?

-What’s the proper output format for influences? Is it bone[.8] or bone.0.8? Im not really sure.

Any help, I would appreciate very very much. Thanks all!

[QUOTE=StefanLipsius;22242]
-How to Query the mesh of a selected vert so that I can soft code the skinCluster, so I can get rid of ‘skinCluster1’.

-Why won’t it work on 2 verts in my current setup?

-What’s the proper output format for influences? Is it bone[.8] or bone.0.8? Im not really sure.
[/QUOTE]

Hey Stefan,

I did rush this a bit so you might want to really test this out. My goal here was to point you in the right direction,
but I’ve ended up getting it to work.

So you can see below “get_skin_cluster()” this is just a quick sloppy way of getting the FIRST skin cluster and might not be that useful.

cmds.skinPercent( skin_cluster, q = True, v = True ) can’t have more than one vert selected. So the code below clears selection then selects each vert. (Again you might be able to pass the vert name I haven’t looked into it)


import maya.cmds as cmds

# Get skin cluster
def get_skin_cluster():
	shape = cmds.ls( objectsOnly = True, sl = True )

	if shape:
		shape = shape[0]
		skin_cluster_set = cmds.listConnections( '{0}.instObjGroups[0].objectGroups[0].objectGrpColor'.format( shape ) )

	if skin_cluster_set:
		skin_cluster_set = skin_cluster_set[0]
		skin_cluster = cmds.listConnections( '{0}.usedBy[0]'.format( skin_cluster_set ) )

		if skin_cluster:
			return skin_cluster[0]

		return False

joint_average = {}
joint_data = {}
skin_cluster = get_skin_cluster()

selected = cmds.ls( sl = True )
verts = cmds.ls( os = True, fl = True )
edit_vert = verts.pop( -1 )

# Get joint influence from vert list
for vert in verts:
	cmds.select( vert )
	i = 0

	joints = cmds.skinPercent( skin_cluster, q = 1, t = None )
	influence_value = cmds.skinPercent( skin_cluster, q = True, v = True )

	for joint, influence  in zip( joints, influence_value ):
		if influence > 0:
			# Save skin data
			joint_data['{0}.{1}'.format( vert, joint )] = [joint, influence]

	i = i + 1

# Get average
for vert, joint_influence in joint_data.iteritems():
	joint = joint_influence[0]
	influence = joint_influence[1]

	if joint in joint_average:
		joint_average[joint] = ( joint_average[joint] + influence )

	else:
		joint_average[joint] = influence
	
for joint, influence in joint_average.iteritems():
	# Set weight average to vert
	cmds.skinPercent( skin_cluster, edit_vert, transformValue = ( joint, influence ) )

cmds.select( selected )

Hope this helps,
-Nick

Wow, thanks so much, Nick! I tried copying and pasting your code just to see what it did, and I got errors. I changed verts = cmds.ls( os = True, fl = True ) to verts = cmds.ls( sl = True, fl = True ), since it told me something about os not being a correct flag. That seemed to “work” but it was acting all weird.

Either way, even without it working, per say, what you wrote helped a lot. I had forgot about listConnections, and I didnt know about ls(objectsOnly), so that helped.

So, the problem I’ve run into now is this.


import maya.cmds as cmds

selection = cmds.ls(sl=True)
mesh = cmds.ls(sl=True, o=True)
myCluster = cmds.listConnections(mesh, type='skinCluster')

#goes through the verts selected
for sel in selection:
	cmds.select(sel)
	i=0
	clusters = cmds.skinPercent('skinCluster1', q=1, t=None)
	yo = cmds.skinPercent('skinCluster1', q=True, v=True)

	#Goes through clusters
	for cluster in clusters:
		
		influence = yo[i]

		if influence > 0:
			infStr = str(influence)
			#print cluster + infStr
		i=i+1

print mesh
print myCluster

the two printing out is

[u’pCubeShape1’]
[u’skinCluster1’]

The problem is that if i change my hard coded ‘skinCluster1’ to my variable skinCluster, it uses u’skinCluster1’ and it doesnt like that at all… So my question is what am I doing wrong that its returning these strings with u’____’ ? I pulled apart your code, and your get cluster returns just skinCluster1.

Admittedly, some of your code is quite over my head right now, but Ive been focusing on the get_Skin_Cluster. But I dont quite understand -why- you wrote some of that stuff, or what it does. like the cmds.listConnections( ‘{0}.usedBy[0]’.format( skin_cluster_set ) ). No idea what the usedBy or the format does, and why you have [0] and {0} all over the place. Still learning, I am.

Replied a little too soon. Figured out how to get rid of the u’____’.


mesh = cmds.ls(sl=True, o=True)[0]
myCluster = cmds.listConnections(mesh, type='skinCluster')[0]

Though, I believe that only selected the first object, if I am not mistaken. In this case it works fine since the tool is meant for 1 mesh, and my rigging is still pretty basic, so I havent really run into different skin clusters. But I guess if it works for what I need it to right now, it cant be that bad. A working tool that is written messily is better than no tool.

HA yes, sorry about that. I have it so you have to select as many verts as needed and the last one selected takes the average. Its a one button MESSY proc.
Let me know if it doesn’t work and I will look into it.

Forgot to say this…
“cmds.listConnections( ‘{0}.usedBy[0]’.format( skin_cluster_set ) )”
If you look at the node editor it shows all your connections between each node. Their shapes, sets, parents every connection.
So this was a rush job so I just looked at it’s connections and walked down the chain.

-Nick

Well, Im making progress. I seem to be able to give verts the values from 1 vert to another. However, i’ve run into a snag. It seems as though my selection list is putting the verts selected in numerical order. So, if I select vtx[0], vtx[7], vtx[5]… in that order, my selection array shows it as vtx[0], vtx[5], vtx[7], which results in my for loops not working as intended, since Im trying to grab the last vert in the select to be the editted vert. Any thoughts? Is there a way around this? There’s gotta be.


import maya.cmds as cmds



selection = cmds.ls(sl=True, r=True, fl=True)
mesh = cmds.ls(sl=True, o=True)[0]
myCluster = cmds.listConnections(mesh, type='skinCluster')[0]
edit_vert = selection[-0]
print selection
print 'edit this vert = ' + edit_vert

#goes through the verts selected
for sel in selection:
	cmds.select(sel)
	i=0
	clusters = cmds.skinPercent(myCluster, q=1, t=None)
	yo = cmds.skinPercent(myCluster, q=True, v=True)
	print 'selected vert =' + sel
	
	#Goes through clusters
	for cluster in clusters:
		
		influence = yo[i]

		if influence > 0:
			strInf = str(influence)
			cmds.skinPercent( myCluster, edit_vert, transformValue = ( cluster, influence ) )
			print 'vert = ' + edit_vert + 'cluster  =' + cluster + ' influence =' + strInf
		i=i+1







Hey Stefan,

My old example that I have above, you will see…

My Example
cmds.ls( os = True, fl = True )

Your Code
cmds.ls(sl=True, r=True, fl=True)

So I use orderedSelection (os) rather than selection (sl).

Hope that works,
-Nick

As an addendum, you have to make sure “Track selection order” is enabled in the preferences (not sure if it is by default). Otherwise the orderedSelection flag will not return the components in the selected order and will behave the same as the sl flag.

Preferences > Selection > Track selection order

[QUOTE=capper;22283]As an addendum, you have to make sure “Track selection order” is enabled in the preferences (not sure if it is by default). Otherwise the orderedSelection flag will not return the components in the selected order and will behave the same as the sl flag.

Preferences > Selection > Track selection order[/QUOTE]

Thanks Capper! I did forget that you have to set that. You can also set it using Maya commands.


cmds.selectPref(trackSelectionOrder=True)

-Nick

Well, that explains that. Here’s the problem. I’m using Maya 2010. I don’t have that preference in the selection menu, nor does python recgonize the os flag. If required, I can probably get the office to upgrade to 2012. But, if there is ANY other way to do this without having to, I would prefer that. Is there any other way? I guess I could separate the two actions so that I don’t need the selection order. make it so you select the verts that you grab the average from, hit the script, then grab the edit vert and hit the script again. That was the original plan anyways. I’ve been trying to make it work like yours, Nick, where you just selection all the verts at once, and it edits the last one selected.